Skip to content

Commit 4e63425

Browse files
authored
feat: enable force unlock and optimize failure handling in execute api (#1339)
1 parent 7b361d5 commit 4e63425

File tree

4 files changed

+63
-10
lines changed

4 files changed

+63
-10
lines changed

pkg/server/handler/stack/utils.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func requestHelper(r *http.Request) (context.Context, *httplog.Logger, *stackman
101101
dryrunParam, _ := strconv.ParseBool(r.URL.Query().Get("dryrun"))
102102
forceParam, _ := strconv.ParseBool(r.URL.Query().Get("force"))
103103
noCacheParam, _ := strconv.ParseBool(r.URL.Query().Get("noCache"))
104+
unlockParam, _ := strconv.ParseBool(r.URL.Query().Get("unlock"))
104105
importResourcesParam, _ := strconv.ParseBool(r.URL.Query().Get("importResources"))
105106
specIDParam := r.URL.Query().Get("specID")
106107
// TODO: Should match automatically eventually???
@@ -123,6 +124,7 @@ func requestHelper(r *http.Request) (context.Context, *httplog.Logger, *stackman
123124
SpecID: specIDParam,
124125
ImportResources: importResourcesParam,
125126
NoCache: noCacheParam,
127+
Unlock: unlockParam,
126128
}
127129
params := stackmanager.StackRequestParams{
128130
StackID: uint(id),

pkg/server/manager/stack/execute.go

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ func (m *StackManager) PreviewStack(ctx context.Context, params *StackRequestPar
132132
// TODO: This is a temporary solution to prevent multiple requests from previewing the same stack and cause concurrency issues
133133
// To override this, pass in force == true
134134
if stackEntity.StackInOperation() && !params.ExecuteParams.Force {
135-
return nil, ErrStackInOperation
135+
err = ErrStackInOperation
136+
return nil, err
136137
}
137138

138139
// Set stack sync state to previewing
@@ -250,11 +251,12 @@ func (m *StackManager) ApplyStack(ctx context.Context, params *StackRequestParam
250251
releaseCreated := false
251252
// Ensure the state is updated properly
252253
defer func() {
253-
if !releaseCreated {
254-
return
255-
}
256254
if err != nil {
257255
stackEntity.SyncState = constant.StackStateApplyFailed
256+
if !releaseCreated {
257+
m.stackRepo.Update(ctx, stackEntity)
258+
return
259+
}
258260
release.UpdateReleasePhase(rel, apiv1.ReleasePhaseFailed, relLock)
259261
_ = release.UpdateApplyRelease(storage, rel, params.ExecuteParams.Dryrun, relLock)
260262
} else {
@@ -274,7 +276,8 @@ func (m *StackManager) ApplyStack(ctx context.Context, params *StackRequestParam
274276
// TODO: This is a temporary solution to prevent multiple requests from applying the same stack and cause concurrency issues
275277
// To override this, pass in force == true
276278
if stackEntity.StackInOperation() && !params.ExecuteParams.Force {
277-
return ErrStackInOperation
279+
err = ErrStackInOperation
280+
return err
278281
}
279282
// Temporarily commented out
280283
// if stackEntity.LastPreviewedRevision == "" || stackEntity.SyncState != constant.StackStatePreviewed {
@@ -301,13 +304,22 @@ func (m *StackManager) ApplyStack(ctx context.Context, params *StackRequestParam
301304
if err != nil {
302305
return err
303306
}
307+
// Allow force unlock of the release
308+
if params.ExecuteParams.Unlock {
309+
err = unlockRelease(ctx, storage)
310+
if err != nil {
311+
return err
312+
}
313+
}
314+
// Get the latest state from the release
304315
priorState, err := release.GetLatestState(storage)
305316
if err != nil {
306317
return err
307318
}
308319
if priorState == nil {
309320
priorState = &apiv1.State{}
310321
}
322+
// Create new release
311323
rel, err = release.NewApplyRelease(storage, project.Name, stackEntity.Name, ws.Name)
312324
if err != nil {
313325
return err
@@ -468,13 +480,14 @@ func (m *StackManager) DestroyStack(ctx context.Context, params *StackRequestPar
468480
rel := &apiv1.Release{}
469481
releaseCreated := false
470482
defer func() {
471-
if !releaseCreated {
472-
return
473-
}
474483
if err != nil {
484+
stackEntity.SyncState = constant.StackStateDestroyFailed
485+
if !releaseCreated {
486+
m.stackRepo.Update(ctx, stackEntity)
487+
return
488+
}
475489
rel.Phase = apiv1.ReleasePhaseFailed
476490
_ = release.UpdateDestroyRelease(storage, rel)
477-
stackEntity.SyncState = constant.StackStateDestroyFailed
478491
} else {
479492
rel.Phase = apiv1.ReleasePhaseSucceeded
480493
err = release.UpdateDestroyRelease(storage, rel)
@@ -490,7 +503,8 @@ func (m *StackManager) DestroyStack(ctx context.Context, params *StackRequestPar
490503
// TODO: This is a temporary solution to prevent multiple requests from destroying the same stack and cause concurrency issues
491504
// To override this, pass in force == true
492505
if stackEntity.StackInOperation() && !params.ExecuteParams.Force {
493-
return ErrStackInOperation
506+
err = ErrStackInOperation
507+
return err
494508
}
495509

496510
// Set stack sync state to destroying
@@ -514,6 +528,14 @@ func (m *StackManager) DestroyStack(ctx context.Context, params *StackRequestPar
514528
if err != nil {
515529
return err
516530
}
531+
// Allow force unlock of the release
532+
if params.ExecuteParams.Unlock {
533+
err = unlockRelease(ctx, storage)
534+
if err != nil {
535+
return err
536+
}
537+
}
538+
// Create destroy release
517539
rel, err = release.CreateDestroyRelease(storage, project.Name, stack.Name, ws.Name)
518540
if err != nil {
519541
return

pkg/server/manager/stack/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type StackExecuteParams struct {
5959
Force bool
6060
ImportResources bool
6161
NoCache bool
62+
Unlock bool
6263
}
6364

6465
type RunRequestParams struct {

pkg/server/manager/stack/util.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
engineapi "kusionstack.io/kusion/pkg/engine/api"
2323
sourceapi "kusionstack.io/kusion/pkg/engine/api/source"
2424
"kusionstack.io/kusion/pkg/engine/operation/models"
25+
"kusionstack.io/kusion/pkg/engine/release"
2526
"kusionstack.io/kusion/pkg/engine/runtime/terraform/tfops"
2627
workspacemanager "kusionstack.io/kusion/pkg/server/manager/workspace"
2728
logutil "kusionstack.io/kusion/pkg/server/util/logging"
@@ -453,3 +454,30 @@ func logToAll(sysLogger *httplog.Logger, runLogger *httplog.Logger, level string
453454
sysLogger.Error("unknown log level", "level", level)
454455
}
455456
}
457+
458+
func unlockRelease(ctx context.Context, storage release.Storage) error {
459+
logger := logutil.GetLogger(ctx)
460+
logger.Info("Getting workdir from stack source...")
461+
// Get the latest release.
462+
r, err := release.GetLatestRelease(storage)
463+
if err != nil {
464+
return err
465+
}
466+
if r == nil {
467+
logger.Info("No release file found for given stack")
468+
return nil
469+
}
470+
471+
// Update the phase to 'failed', if it was not succeeded or failed.
472+
if r.Phase != v1.ReleasePhaseSucceeded && r.Phase != v1.ReleasePhaseFailed {
473+
r.Phase = v1.ReleasePhaseFailed
474+
if err := storage.Update(r); err != nil {
475+
return err
476+
}
477+
logger.Info("Successfully update release phase!")
478+
return nil
479+
} else {
480+
logger.Info("No need to update the release phase, current phase: ", "phase", r.Phase)
481+
}
482+
return nil
483+
}

0 commit comments

Comments
 (0)