Skip to content

Commit

Permalink
fix: locked release file caused by interruption (#1219)
Browse files Browse the repository at this point in the history
* fix: locked release file caused by interruption

* fix: a lint error
  • Loading branch information
liu-hm19 authored Jul 16, 2024
1 parent 6b9c2cd commit e037960
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 9 deletions.
71 changes: 64 additions & 7 deletions pkg/cmd/destroy/destroy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package destroy

import (
"errors"
"fmt"
"os"
"strings"
Expand All @@ -36,6 +37,7 @@ import (
"kusionstack.io/kusion/pkg/engine/runtime/terraform"
"kusionstack.io/kusion/pkg/log"
"kusionstack.io/kusion/pkg/util/pretty"
"kusionstack.io/kusion/pkg/util/signal"
"kusionstack.io/kusion/pkg/util/terminal"
)

Expand Down Expand Up @@ -188,12 +190,66 @@ func (o *DestroyOptions) Run() (err error) {
}
releaseCreated = true

errCh := make(chan error, 1)
defer close(errCh)

// wait for the SIGTERM or SIGINT
go func() {
stopCh := signal.SetupSignalHandler()
<-stopCh
errCh <- errors.New("receive SIGTERM or SIGINT, exit cmd")
}()

// run destroy command
go func() {
errCh <- o.run(rel, storage)
}()

if err = <-errCh; err != nil {
rel.Phase = apiv1.ReleasePhaseFailed
release.UpdateDestroyRelease(storage, rel)
} else {
rel.Phase = apiv1.ReleasePhaseSucceeded
release.UpdateDestroyRelease(storage, rel)
}

return err
}

// run executes the delete command after release is created.
func (o *DestroyOptions) run(rel *apiv1.Release, storage release.Storage) (err error) {
// update release to succeeded or failed
defer func() {
if err != nil {
rel.Phase = apiv1.ReleasePhaseFailed
release.UpdateDestroyRelease(storage, rel)
} else {
rel.Phase = apiv1.ReleasePhaseSucceeded
err = release.UpdateDestroyRelease(storage, rel)
}
}()

// set no style
if o.NoStyle {
pterm.DisableStyling()
}

sp := o.UI.SpinnerPrinter
sp, _ = sp.Start(fmt.Sprintf("Computing destroy changes in the Stack %s...", o.RefStack.Name))

// compute changes for preview
changes, err := o.preview(rel.Spec, rel.State, o.RefProject, o.RefStack, storage)
if err != nil {
if sp != nil {
sp.Fail()
}
return
}

if sp != nil {
sp.Success()
}

// preview
changes.Summary(os.Stdout, o.NoStyle)

Expand All @@ -203,16 +259,11 @@ func (o *DestroyOptions) Run() (err error) {
return nil
}

// set no style
if o.NoStyle {
pterm.DisableStyling()
}

// prompt
if !o.Yes {
for {
var input string
input, err = prompt(o.UI)
input, err = prompt(o.UI, rel, storage)
if err != nil {
return
}
Expand Down Expand Up @@ -395,13 +446,19 @@ func (o *DestroyOptions) destroy(rel *apiv1.Release, changes *models.Changes, st
return updatedRel, nil
}

func prompt(ui *terminal.UI) (string, error) {
func prompt(ui *terminal.UI, rel *apiv1.Release, storage release.Storage) (string, error) {
options := []string{"yes", "details", "no"}
input, err := ui.InteractiveSelectPrinter.
WithFilter(false).
WithDefaultText(`Do you want to destroy these diffs?`).
WithOptions(options).
WithDefaultOption("details").
// To gracefully exit if interrupted by SIGINT or SIGTERM.
WithOnInterruptFunc(func() {
rel.Phase = apiv1.ReleasePhaseFailed
release.UpdateDestroyRelease(storage, rel)
os.Exit(1)
}).
Show()
if err != nil {
fmt.Printf("Prompt failed: %v\n", err)
Expand Down
4 changes: 2 additions & 2 deletions pkg/cmd/destroy/destroy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,13 @@ func mockOperationDestroy(res models.OpResult) {
func TestPrompt(t *testing.T) {
mockey.PatchConvey("prompt error", t, func() {
mockey.Mock((*pterm.InteractiveSelectPrinter).Show).Return("", errors.New("mock error")).Build()
_, err := prompt(terminal.DefaultUI())
_, err := prompt(terminal.DefaultUI(), &apiv1.Release{}, &releasestorages.LocalStorage{})
assert.NotNil(t, err)
})

mockey.PatchConvey("prompt yes", t, func() {
mockPromptOutput("yes")
_, err := prompt(terminal.DefaultUI())
_, err := prompt(terminal.DefaultUI(), &apiv1.Release{}, &releasestorages.LocalStorage{})
assert.Nil(t, err)
})
}
8 changes: 8 additions & 0 deletions pkg/engine/operation/models/change.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"io"
"os"
"strings"

"github.com/liu-hm19/pterm"
Expand Down Expand Up @@ -262,6 +263,13 @@ func (o *ChangeOrder) PromptDetails(ui *terminal.UI) (string, error) {
WithDefaultText(`Which diff detail do you want to see?`).
WithOptions(options).
WithDefaultOption("all").
// Fixme: interruption during 'apply' or 'destroy' may result in a locked release file.
WithOnInterruptFunc(func() {
hint := `Interruption during 'apply' or 'destroy' may result in a locked release file.
Please use 'kusion release unlock' before executing the next operation.`
fmt.Printf("\n" + hint + "\n")
os.Exit(1)
}).
Show()
if err != nil {
fmt.Printf("Prompt failed: %v\n", err)
Expand Down

0 comments on commit e037960

Please sign in to comment.