mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-26 15:25:25 +02:00
Some refactoring
This commit is contained in:
parent
86fa672d1f
commit
b5dd7ea6e1
@ -10,16 +10,15 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"time"
|
||||||
|
|
||||||
packages_model "code.gitea.io/gitea/models/packages"
|
packages_model "code.gitea.io/gitea/models/packages"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
packages_module "code.gitea.io/gitea/modules/packages"
|
packages_module "code.gitea.io/gitea/modules/packages"
|
||||||
"code.gitea.io/gitea/routers/api/packages/helper"
|
|
||||||
"code.gitea.io/gitea/services/context"
|
"code.gitea.io/gitea/services/context"
|
||||||
packages_service "code.gitea.io/gitea/services/packages"
|
"code.gitea.io/gitea/services/packages"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
@ -46,157 +45,105 @@ type InstanceState struct {
|
|||||||
Attributes map[string]any `json:"attributes"`
|
Attributes map[string]any `json:"attributes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type LockInfo struct {
|
||||||
stateStorage = make(map[string]*TFState)
|
ID string `json:"id"`
|
||||||
stateLocks = make(map[string]string)
|
Created string `json:"created"`
|
||||||
storeMutex sync.Mutex
|
}
|
||||||
)
|
|
||||||
|
|
||||||
func apiError(ctx *context.Context, status int, obj any) {
|
var stateLocks = make(map[string]LockInfo)
|
||||||
helper.LogAndProcessError(ctx, status, obj, func(message string) {
|
|
||||||
type Error struct {
|
func apiError(ctx *context.Context, status int, message string) {
|
||||||
Status int `json:"status"`
|
log.Error("Terraform API Error: %d - %s", status, message)
|
||||||
Message string `json:"message"`
|
ctx.JSON(status, map[string]string{"error": message})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLockID(ctx *context.Context) (string, error) {
|
||||||
|
var lock struct {
|
||||||
|
ID string `json:"ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the body of the request and try to parse the JSON
|
||||||
|
body, err := io.ReadAll(ctx.Req.Body)
|
||||||
|
if err == nil && len(body) > 0 {
|
||||||
|
if err := json.Unmarshal(body, &lock); err != nil {
|
||||||
|
log.Error("Failed to unmarshal request body: %v", err)
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
ctx.JSON(status, struct {
|
}
|
||||||
Errors []Error `json:"errors"`
|
|
||||||
}{
|
// We check the presence of lock ID in the request body or request parameters
|
||||||
Errors: []Error{
|
if lock.ID == "" {
|
||||||
{Status: status, Message: message},
|
lock.ID = ctx.Req.URL.Query().Get("ID")
|
||||||
},
|
}
|
||||||
})
|
|
||||||
})
|
if lock.ID == "" {
|
||||||
|
apiError(ctx, http.StatusBadRequest, "Missing lock ID")
|
||||||
|
return "", fmt.Errorf("missing lock ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Extracted lockID: %s", lock.ID)
|
||||||
|
return lock.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetState(ctx *context.Context) {
|
func GetState(ctx *context.Context) {
|
||||||
stateName := ctx.PathParam("statename")
|
stateName := ctx.PathParam("statename")
|
||||||
log.Info("Function GetState called with parameters: stateName=%s", stateName)
|
log.Info("GetState called for: %s", stateName)
|
||||||
|
|
||||||
// Find the package version
|
pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
|
||||||
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
OwnerID: ctx.Package.Owner.ID,
|
||||||
OwnerID: ctx.Package.Owner.ID,
|
Type: packages_model.TypeTerraform,
|
||||||
Type: packages_model.TypeTerraform,
|
Name: packages_model.SearchValue{ExactMatch: true, Value: stateName},
|
||||||
Name: packages_model.SearchValue{
|
|
||||||
ExactMatch: true,
|
|
||||||
Value: stateName,
|
|
||||||
},
|
|
||||||
HasFileWithName: stateName,
|
HasFileWithName: stateName,
|
||||||
IsInternal: optional.Some(false),
|
IsInternal: optional.Some(false),
|
||||||
|
Sort: packages_model.SortCreatedDesc,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to search package versions for state %s: %v", stateName, err)
|
apiError(ctx, http.StatusInternalServerError, "Failed to fetch latest versions")
|
||||||
apiError(ctx, http.StatusInternalServerError, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no version is found, return 204
|
|
||||||
if len(pvs) == 0 {
|
if len(pvs) == 0 {
|
||||||
log.Info("No existing state found for %s, returning 204 No Content", stateName)
|
apiError(ctx, http.StatusNoContent, "No content available")
|
||||||
ctx.Resp.WriteHeader(http.StatusNoContent)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the latest package version
|
stream, _, _, err := packages.GetFileStreamByPackageNameAndVersion(ctx, &packages.PackageInfo{
|
||||||
stateVersion := pvs[0]
|
Owner: ctx.Package.Owner,
|
||||||
if stateVersion == nil {
|
PackageType: packages_model.TypeTerraform,
|
||||||
log.Error("State version is nil for state %s", stateName)
|
Name: stateName,
|
||||||
apiError(ctx, http.StatusInternalServerError, "Invalid state version")
|
Version: pvs[0].Version,
|
||||||
return
|
}, &packages.PackageFileInfo{Filename: stateName})
|
||||||
}
|
|
||||||
log.Info("Fetching file stream for state %s with version %s", stateName, stateVersion.Version)
|
|
||||||
|
|
||||||
// Log the parameters of GetFileStreamByPackageNameAndVersion call
|
|
||||||
log.Info("Fetching file stream with params: Owner=%v, PackageType=%v, Name=%v, Version=%v, Filename=%v",
|
|
||||||
ctx.Package.Owner,
|
|
||||||
packages_model.TypeTerraform,
|
|
||||||
stateName,
|
|
||||||
stateVersion.Version,
|
|
||||||
stateName,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fetch the file stream
|
|
||||||
s, _, _, err := packages_service.GetFileStreamByPackageNameAndVersion(
|
|
||||||
ctx,
|
|
||||||
&packages_service.PackageInfo{
|
|
||||||
Owner: ctx.Package.Owner,
|
|
||||||
PackageType: packages_model.TypeTerraform,
|
|
||||||
Name: stateName,
|
|
||||||
Version: stateVersion.Version,
|
|
||||||
},
|
|
||||||
&packages_service.PackageFileInfo{
|
|
||||||
Filename: stateName,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error fetching file stream for state %s: %v", stateName, err)
|
switch {
|
||||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
case errors.Is(err, packages_model.ErrPackageNotExist):
|
||||||
log.Error("Package does not exist: %v", err)
|
|
||||||
apiError(ctx, http.StatusNotFound, "Package not found")
|
apiError(ctx, http.StatusNotFound, "Package not found")
|
||||||
return
|
case errors.Is(err, packages_model.ErrPackageFileNotExist):
|
||||||
}
|
|
||||||
if errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
|
||||||
log.Error("Package file does not exist: %v", err)
|
|
||||||
apiError(ctx, http.StatusNotFound, "File not found")
|
apiError(ctx, http.StatusNotFound, "File not found")
|
||||||
return
|
default:
|
||||||
|
apiError(ctx, http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
apiError(ctx, http.StatusInternalServerError, "Failed to fetch file stream")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer stream.Close()
|
||||||
|
|
||||||
// Read the file contents
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
if _, err := io.Copy(buf, s); err != nil {
|
|
||||||
log.Error("Failed to read state file for %s: %v", stateName, err)
|
|
||||||
apiError(ctx, http.StatusInternalServerError, "Failed to read state file")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deserialize the state
|
|
||||||
var state TFState
|
var state TFState
|
||||||
if err := json.Unmarshal(buf.Bytes(), &state); err != nil {
|
if err := json.NewDecoder(stream).Decode(&state); err != nil {
|
||||||
log.Error("Failed to unmarshal state file for %s: %v", stateName, err)
|
apiError(ctx, http.StatusInternalServerError, "Failed to parse state file")
|
||||||
apiError(ctx, http.StatusInternalServerError, "Invalid state file format")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure lineage is set
|
|
||||||
if state.Lineage == "" {
|
if state.Lineage == "" {
|
||||||
state.Lineage = uuid.NewString()
|
state.Lineage = uuid.NewString()
|
||||||
log.Info("Generated new lineage for state %s: %s", stateName, state.Lineage)
|
log.Info("Generated new lineage for state: %s", state.Lineage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the state in the response
|
|
||||||
ctx.Resp.Header().Set("Content-Type", "application/json")
|
ctx.Resp.Header().Set("Content-Type", "application/json")
|
||||||
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", stateName))
|
ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", stateName))
|
||||||
ctx.Resp.WriteHeader(http.StatusOK)
|
ctx.JSON(http.StatusOK, state)
|
||||||
if _, writeErr := ctx.Resp.Write(buf.Bytes()); writeErr != nil {
|
|
||||||
log.Error("Failed to write response for state %s: %v", stateName, writeErr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateState updates or creates a new Terraform state and interacts with Gitea packages.
|
|
||||||
func UpdateState(ctx *context.Context) {
|
func UpdateState(ctx *context.Context) {
|
||||||
stateName := ctx.PathParam("statename")
|
stateName := ctx.PathParam("statename")
|
||||||
log.Info("UpdateState called for stateName: %s", stateName)
|
|
||||||
|
|
||||||
storeMutex.Lock()
|
|
||||||
defer storeMutex.Unlock()
|
|
||||||
|
|
||||||
// Check for the presence of a lock ID
|
|
||||||
requestLockID := ctx.Req.URL.Query().Get("ID")
|
|
||||||
if requestLockID == "" {
|
|
||||||
apiError(ctx, http.StatusBadRequest, "Missing ID query parameter")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for blocking state
|
|
||||||
if lockID, locked := stateLocks[stateName]; locked && lockID != requestLockID {
|
|
||||||
apiError(ctx, http.StatusConflict, fmt.Sprintf("State %s is locked", stateName))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the request body
|
|
||||||
body, err := io.ReadAll(ctx.Req.Body)
|
body, err := io.ReadAll(ctx.Req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiError(ctx, http.StatusInternalServerError, "Failed to read request body")
|
apiError(ctx, http.StatusInternalServerError, "Failed to read request body")
|
||||||
@ -209,37 +156,36 @@ func UpdateState(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getting the current serial
|
pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
|
||||||
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
OwnerID: ctx.Package.Owner.ID,
|
||||||
OwnerID: ctx.Package.Owner.ID,
|
Type: packages_model.TypeTerraform,
|
||||||
Type: packages_model.TypeTerraform,
|
Name: packages_model.SearchValue{ExactMatch: true, Value: stateName},
|
||||||
Name: packages_model.SearchValue{ExactMatch: true, Value: stateName},
|
HasFileWithName: stateName,
|
||||||
IsInternal: optional.Some(false),
|
IsInternal: optional.Some(false),
|
||||||
|
Sort: packages_model.SortCreatedDesc,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiError(ctx, http.StatusInternalServerError, "Failed to search package versions")
|
apiError(ctx, http.StatusInternalServerError, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
serial := uint64(0)
|
||||||
serial := uint64(1) // Start from 1
|
|
||||||
if len(pvs) > 0 {
|
if len(pvs) > 0 {
|
||||||
lastSerial, _ := strconv.ParseUint(pvs[0].Version, 10, 64)
|
if lastSerial, err := strconv.ParseUint(pvs[0].Version, 10, 64); err == nil {
|
||||||
serial = lastSerial + 1
|
serial = lastSerial + 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log.Info("State %s updated to serial %d", stateName, serial)
|
|
||||||
|
|
||||||
// Create package information
|
|
||||||
packageVersion := fmt.Sprintf("%d", serial)
|
packageVersion := fmt.Sprintf("%d", serial)
|
||||||
packageInfo := &packages_service.PackageCreationInfo{
|
|
||||||
PackageInfo: packages_service.PackageInfo{
|
packageInfo := &packages.PackageCreationInfo{
|
||||||
|
PackageInfo: packages.PackageInfo{
|
||||||
Owner: ctx.Package.Owner,
|
Owner: ctx.Package.Owner,
|
||||||
PackageType: packages_model.TypeTerraform,
|
PackageType: packages_model.TypeTerraform,
|
||||||
Name: stateName,
|
Name: stateName,
|
||||||
Version: packageVersion,
|
Version: packageVersion,
|
||||||
},
|
},
|
||||||
SemverCompatible: true,
|
Creator: ctx.Doer,
|
||||||
Creator: ctx.Doer,
|
Metadata: newState,
|
||||||
Metadata: newState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(body))
|
buffer, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(body))
|
||||||
@ -247,146 +193,86 @@ func UpdateState(ctx *context.Context) {
|
|||||||
apiError(ctx, http.StatusInternalServerError, "Failed to create buffer")
|
apiError(ctx, http.StatusInternalServerError, "Failed to create buffer")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
_, _, err = packages.CreatePackageOrAddFileToExisting(ctx, packageInfo, &packages.PackageFileCreationInfo{
|
||||||
// Create/update package
|
PackageFileInfo: packages.PackageFileInfo{Filename: stateName},
|
||||||
if _, _, err = packages_service.CreatePackageOrAddFileToExisting(
|
Creator: ctx.Doer,
|
||||||
ctx,
|
Data: buffer,
|
||||||
packageInfo,
|
IsLead: true,
|
||||||
&packages_service.PackageFileCreationInfo{
|
})
|
||||||
PackageFileInfo: packages_service.PackageFileInfo{
|
if err != nil {
|
||||||
Filename: stateName,
|
|
||||||
},
|
|
||||||
Creator: ctx.Doer,
|
|
||||||
Data: buffer,
|
|
||||||
IsLead: true,
|
|
||||||
},
|
|
||||||
); err != nil {
|
|
||||||
apiError(ctx, http.StatusInternalServerError, "Failed to update package")
|
apiError(ctx, http.StatusInternalServerError, "Failed to update package")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("State %s updated successfully with version %s", stateName, packageVersion)
|
ctx.JSON(http.StatusOK, map[string]string{"message": "State updated successfully", "statename": stateName})
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, map[string]string{
|
|
||||||
"message": "State updated successfully",
|
|
||||||
"statename": stateName,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LockState locks a Terraform state to prevent updates.
|
|
||||||
func LockState(ctx *context.Context) {
|
func LockState(ctx *context.Context) {
|
||||||
stateName := ctx.PathParam("statename")
|
stateName := ctx.PathParam("statename")
|
||||||
log.Info("LockState called for state: %s", stateName)
|
lockID, err := getLockID(ctx)
|
||||||
|
|
||||||
// Read the request body
|
|
||||||
body, err := io.ReadAll(ctx.Req.Body)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Failed to read request body: %v", err)
|
apiError(ctx, http.StatusBadRequest, err.Error())
|
||||||
apiError(ctx, http.StatusInternalServerError, "Failed to read request body")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode JSON and check lockID
|
|
||||||
var lockRequest struct {
|
|
||||||
ID string `json:"ID"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(body, &lockRequest); err != nil || lockRequest.ID == "" {
|
|
||||||
log.Error("Invalid lock request body: %v", err)
|
|
||||||
apiError(ctx, http.StatusBadRequest, "Invalid or missing lock ID")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
storeMutex.Lock()
|
|
||||||
defer storeMutex.Unlock()
|
|
||||||
|
|
||||||
// Check if the state is locked
|
// Check if the state is locked
|
||||||
if _, locked := stateLocks[stateName]; locked {
|
if lockInfo, locked := stateLocks[stateName]; locked {
|
||||||
log.Warn("State %s is already locked", stateName)
|
log.Warn("State %s is already locked", stateName)
|
||||||
apiError(ctx, http.StatusConflict, fmt.Sprintf("State %s is already locked", stateName))
|
|
||||||
|
// Generate a response for the conflict with information about the current lock
|
||||||
|
response := lockInfo // Return full information about the lock
|
||||||
|
ctx.JSON(http.StatusConflict, response)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the lock
|
// Set the lock
|
||||||
stateLocks[stateName] = lockRequest.ID
|
stateLocks[stateName] = LockInfo{
|
||||||
log.Info("State %s locked with ID %s", stateName, lockRequest.ID)
|
ID: lockID,
|
||||||
|
Created: time.Now().UTC().Format(time.RFC3339),
|
||||||
|
}
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, map[string]string{
|
log.Info("Locked state: %s with ID: %s", stateName, lockID)
|
||||||
"message": "State locked successfully",
|
ctx.JSON(http.StatusOK, map[string]string{"message": "State locked successfully", "statename": stateName})
|
||||||
"statename": stateName,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnlockState unlocks a Terraform state.
|
|
||||||
func UnlockState(ctx *context.Context) {
|
func UnlockState(ctx *context.Context) {
|
||||||
stateName := ctx.PathParam("statename")
|
stateName := ctx.PathParam("statename")
|
||||||
log.Info("UnlockState called for state: %s", stateName)
|
lockID, err := getLockID(ctx)
|
||||||
|
if err != nil {
|
||||||
// Extract lockID from request body or parameters
|
apiError(ctx, http.StatusBadRequest, err.Error())
|
||||||
var unlockRequest struct {
|
|
||||||
ID string `json:"ID"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trying to read the request body
|
|
||||||
body, _ := io.ReadAll(ctx.Req.Body)
|
|
||||||
if len(body) > 0 {
|
|
||||||
_ = json.Unmarshal(body, &unlockRequest) // The error can be ignored, since the ID can also be in the query
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for ID presence
|
|
||||||
if unlockRequest.ID == "" {
|
|
||||||
log.Error("Missing lock ID in both query and request body")
|
|
||||||
apiError(ctx, http.StatusBadRequest, "Missing lock ID")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Extracted lockID: %s", unlockRequest.ID)
|
|
||||||
|
|
||||||
storeMutex.Lock()
|
|
||||||
defer storeMutex.Unlock()
|
|
||||||
|
|
||||||
// Check the lock status
|
// Check the lock status
|
||||||
currentLockID, locked := stateLocks[stateName]
|
currentLockInfo, locked := stateLocks[stateName]
|
||||||
if !locked || currentLockID != unlockRequest.ID {
|
if !locked || currentLockInfo.ID != lockID {
|
||||||
log.Warn("Unlock attempt failed for state %s with lock ID %s", stateName, unlockRequest.ID)
|
log.Warn("Unlock attempt failed for state %s with lock ID %s", stateName, lockID)
|
||||||
apiError(ctx, http.StatusConflict, fmt.Sprintf("State %s is not locked or lock ID mismatch", stateName))
|
apiError(ctx, http.StatusConflict, fmt.Sprintf("State %s is not locked or lock ID mismatch", stateName))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the lock
|
// Remove the lock
|
||||||
delete(stateLocks, stateName)
|
delete(stateLocks, stateName)
|
||||||
log.Info("State %s unlocked successfully", stateName)
|
log.Info("Unlocked state: %s with ID: %s", stateName, lockID)
|
||||||
|
ctx.JSON(http.StatusOK, map[string]string{"message": "State unlocked successfully"})
|
||||||
ctx.JSON(http.StatusOK, map[string]string{
|
|
||||||
"message": "State unlocked successfully",
|
|
||||||
"statename": stateName,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteState deletes the Terraform state for a given name.
|
|
||||||
func DeleteState(ctx *context.Context) {
|
func DeleteState(ctx *context.Context) {
|
||||||
stateName := ctx.PathParam("statename")
|
stateName := ctx.PathParam("statename")
|
||||||
log.Info("Attempting to delete state: %s", stateName)
|
pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeTerraform, stateName)
|
||||||
|
if err != nil {
|
||||||
storeMutex.Lock()
|
apiError(ctx, http.StatusInternalServerError, "Failed to fetch package versions")
|
||||||
defer storeMutex.Unlock()
|
|
||||||
|
|
||||||
// Check if a state or lock exists
|
|
||||||
_, stateExists := stateStorage[stateName]
|
|
||||||
_, lockExists := stateLocks[stateName]
|
|
||||||
|
|
||||||
if !stateExists && !lockExists {
|
|
||||||
log.Warn("State %s does not exist or is not locked", stateName)
|
|
||||||
apiError(ctx, http.StatusNotFound, "State not found")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(pvs) == 0 {
|
||||||
// Delete the state and lock
|
ctx.Status(http.StatusNoContent)
|
||||||
delete(stateStorage, stateName)
|
return
|
||||||
delete(stateLocks, stateName)
|
}
|
||||||
|
for _, pv := range pvs {
|
||||||
log.Info("State %s deleted successfully", stateName)
|
if err := packages.RemovePackageVersion(ctx, ctx.Doer, pv); err != nil {
|
||||||
ctx.JSON(http.StatusOK, map[string]string{
|
apiError(ctx, http.StatusInternalServerError, "Failed to delete package version")
|
||||||
"message": "State deleted successfully",
|
return
|
||||||
"statename": stateName,
|
}
|
||||||
})
|
}
|
||||||
|
ctx.JSON(http.StatusOK, map[string]string{"message": "State deleted successfully"})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user