Skip to content

Commit

Permalink
feat: use release to update state usage in preview/apply/destroy (#1130)
Browse files Browse the repository at this point in the history
  • Loading branch information
healthjyk committed May 22, 2024
1 parent 519b8c3 commit 7765bc1
Show file tree
Hide file tree
Showing 41 changed files with 1,771 additions and 1,372 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ require (
sigs.k8s.io/controller-runtime v0.15.1
)

require github.com/kisielk/godepgraph v0.0.0-20240411160502-0f324ca7e282 // indirect

require (
atomicgo.dev/cursor v0.2.0 // indirect
atomicgo.dev/keyboard v0.2.9 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/godepgraph v0.0.0-20240411160502-0f324ca7e282 h1:GJiyKwxPNw3KuY/etrF81fqHKNpWSiGsqGbwKxooWRc=
github.com/kisielk/godepgraph v0.0.0-20240411160502-0f324ca7e282/go.mod h1:Gb5YEgxqiSSVrXKWQxDcKoCM94NO5QAwOwTaVmIUAMI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
Expand Down
12 changes: 6 additions & 6 deletions pkg/apis/api.kusion.io/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -858,22 +858,22 @@ type ReleasePhase string

const (
// ReleasePhaseGenerating indicates the stage of generating Spec.
ReleasePhaseGenerating = "generating"
ReleasePhaseGenerating ReleasePhase = "generating"

// ReleasePhasePreviewing indicated the stage of previewing.
ReleasePhasePreviewing = "previewing"
ReleasePhasePreviewing ReleasePhase = "previewing"

// ReleasePhaseApplying indicates the stage of applying.
ReleasePhaseApplying = "applying"
ReleasePhaseApplying ReleasePhase = "applying"

// ReleasePhaseDestroying indicates the stage of destroying.
ReleasePhaseDestroying = "destroying"
ReleasePhaseDestroying ReleasePhase = "destroying"

// ReleasePhaseSucceeded is a final phase, indicates the Release is successful.
ReleasePhaseSucceeded = "succeeded"
ReleasePhaseSucceeded ReleasePhase = "succeeded"

// ReleasePhaseFailed is a final phase, indicates the Release is failed.
ReleasePhaseFailed = "failed"
ReleasePhaseFailed ReleasePhase = "failed"
)

// Release describes the generation, preview and deployment of a specified Stack. When the operation
Expand Down
163 changes: 88 additions & 75 deletions pkg/cmd/apply/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import (
cmdutil "kusionstack.io/kusion/pkg/cmd/util"
"kusionstack.io/kusion/pkg/engine/operation"
"kusionstack.io/kusion/pkg/engine/operation/models"
"kusionstack.io/kusion/pkg/engine/state"
"kusionstack.io/kusion/pkg/engine/release"
"kusionstack.io/kusion/pkg/log"
"kusionstack.io/kusion/pkg/util/i18n"
"kusionstack.io/kusion/pkg/util/pretty"
Expand Down Expand Up @@ -168,23 +168,56 @@ func (o *ApplyOptions) Validate(cmd *cobra.Command, args []string) error {
}

// Run executes the `apply` command.
func (o *ApplyOptions) Run() error {
func (o *ApplyOptions) Run() (err error) {
// update release to succeeded or failed
var storage release.Storage
var rel *apiv1.Release
releaseCreated := false
defer func() {
if !releaseCreated {
return
}
if err != nil {
rel.Phase = apiv1.ReleasePhaseFailed
_ = release.UpdateApplyRelease(storage, rel, o.DryRun)
} else {
rel.Phase = apiv1.ReleasePhaseSucceeded
err = release.UpdateApplyRelease(storage, rel, o.DryRun)
}
}()

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

// create release
storage, err = o.Backend.ReleaseStorage(o.RefProject.Name, o.RefWorkspace.Name)
if err != nil {
return
}
rel, err = release.NewApplyRelease(storage, o.RefProject.Name, o.RefStack.Name, o.RefWorkspace.Name)
if err != nil {
return
}
if !o.DryRun {
if err = storage.Create(rel); err != nil {
return
}
releaseCreated = true
}

// build parameters
parameters := make(map[string]string)
for _, value := range o.PreviewOptions.Values {
parts := strings.SplitN(value, "=", 2)
parameters[parts[0]] = parts[1]
}

// Generate Spec
// generate Spec
spec, err := generate.GenerateSpecWithSpinner(o.RefProject, o.RefStack, o.RefWorkspace, nil, o.UI, o.NoStyle)
if err != nil {
return err
return
}

// return immediately if no resource found in stack
Expand All @@ -193,11 +226,16 @@ func (o *ApplyOptions) Run() error {
return nil
}

// update release phase to previewing
rel.Spec = spec
rel.Phase = apiv1.ReleasePhasePreviewing
if err = release.UpdateApplyRelease(storage, rel, o.DryRun); err != nil {
return
}
// compute changes for preview
storage := o.StorageBackend.StateStorage(o.RefProject.Name, o.RefWorkspace.Name)
changes, err := preview.Preview(o.PreviewOptions, storage, spec, o.RefProject, o.RefStack)
changes, err := preview.Preview(o.PreviewOptions, storage, rel.Spec, rel.State, o.RefProject, o.RefStack)
if err != nil {
return err
return
}

if allUnChange(changes) {
Expand All @@ -219,14 +257,16 @@ func (o *ApplyOptions) Run() error {
// prompt
if !o.Yes {
for {
input, err := prompt(o.UI)
var input string
input, err = prompt(o.UI)
if err != nil {
return err
}
if input == "yes" {
break
} else if input == "details" {
target, err := changes.PromptDetails(o.UI)
var target string
target, err = changes.PromptDetails(o.UI)
if err != nil {
return err
}
Expand All @@ -238,70 +278,58 @@ func (o *ApplyOptions) Run() error {
}
}

// update release phase to applying
rel.Phase = apiv1.ReleasePhaseApplying
if err = release.UpdateApplyRelease(storage, rel, o.DryRun); err != nil {
return
}
// start applying
fmt.Println("Start applying diffs ...")
if err = Apply(o, storage, spec, changes, o.IOStreams.Out); err != nil {
return err
var updatedRel *apiv1.Release
updatedRel, err = Apply(o, storage, rel, changes, o.IOStreams.Out)
if err != nil {
return
}

// if dry run, print the hint
if o.DryRun {
fmt.Printf("\nNOTE: Currently running in the --dry-run mode, the above configuration does not really take effect\n")
return nil
}
rel = updatedRel

if o.Watch {
fmt.Println("\nStart watching changes ...")
if err = Watch(o, spec, changes); err != nil {
return err
if err = Watch(o, rel.Spec, changes); err != nil {
return
}
}

if o.PortForward > 0 {
fmt.Printf("\nStart port-forwarding ...\n")
if err = PortForward(o, spec); err != nil {
return err
if err = PortForward(o, rel.Spec); err != nil {
return
}
}

return nil
}

// The Apply function will apply the resources changes
// through the execution Kusion Engine, and will save
// the state to specified storage.
//
// You can customize the runtime of engine and the state
// storage through `runtime` and `storage` parameters.
//
// Example:
//
// o := NewApplyOptions()
// stateStorage := &states.FileSystemState{
// Path: filepath.Join(o.WorkDir, states.KusionState)
// }
// kubernetesRuntime, err := runtime.NewKubernetesRuntime()
// if err != nil {
// return err
// }
//
// err = Apply(o, kubernetesRuntime, stateStorage, planResources, changes, os.Stdout)
// if err != nil {
// return err
// }
// The Apply function will apply the resources changes through the execution kusion engine.
// You can customize the runtime of engine and the release storage through `runtime` and `storage` parameters.
func Apply(
o *ApplyOptions,
storage state.Storage,
planResources *apiv1.Spec,
storage release.Storage,
rel *apiv1.Release,
changes *models.Changes,
out io.Writer,
) error {
) (*apiv1.Release, error) {
// construct the apply operation
ac := &operation.ApplyOperation{
Operation: models.Operation{
Stack: changes.Stack(),
StateStorage: storage,
MsgCh: make(chan models.Message),
IgnoreFields: o.IgnoreFields,
Stack: changes.Stack(),
ReleaseStorage: storage,
MsgCh: make(chan models.Message),
IgnoreFields: o.IgnoreFields,
},
}

Expand All @@ -316,7 +344,7 @@ func Apply(
WithRemoveWhenDone().
Start()
if err != nil {
return err
return nil, err
}
// wait msgCh close
var wg sync.WaitGroup
Expand Down Expand Up @@ -377,8 +405,9 @@ func Apply(
}
}()

var updatedRel *apiv1.Release
if o.DryRun {
for _, r := range planResources.Resources {
for _, r := range rel.Spec.Resources {
ac.MsgCh <- models.Message{
ResourceID: r.ResourceKey(),
OpResult: models.Success,
Expand All @@ -388,42 +417,28 @@ func Apply(
close(ac.MsgCh)
} else {
// parse cluster in arguments
_, st := ac.Apply(&operation.ApplyRequest{
rsp, st := ac.Apply(&operation.ApplyRequest{
Request: models.Request{
Project: changes.Project(),
Stack: changes.Stack(),
Operator: o.Operator,
Intent: planResources,
Project: changes.Project(),
Stack: changes.Stack(),
},
Release: rel,
})
if v1.IsErr(st) {
return fmt.Errorf("apply failed, status:\n%v", st)
return nil, fmt.Errorf("apply failed, status:\n%v", st)
}
updatedRel = rsp.Release
}

// wait for msgCh closed
wg.Wait()
// print summary
pterm.Println()
pterm.Fprintln(out, fmt.Sprintf("Apply complete! Resources: %d created, %d updated, %d deleted.", ls.created, ls.updated, ls.deleted))
return nil
return updatedRel, nil
}

// Watch function will observe the changes of each resource
// by the execution engine.
//
// Example:
//
// o := NewApplyOptions()
// kubernetesRuntime, err := runtime.NewKubernetesRuntime()
// if err != nil {
// return err
// }
//
// Watch(o, kubernetesRuntime, planResources, changes, os.Stdout)
// if err != nil {
// return err
// }
// Watch function will observe the changes of each resource by the execution engine.
func Watch(
o *ApplyOptions,
planResources *apiv1.Spec,
Expand All @@ -442,14 +457,14 @@ func Watch(
}
}

// watch operation
// Watch operation
wo := &operation.WatchOperation{}
if err := wo.Watch(&operation.WatchRequest{
Request: models.Request{
Project: changes.Project(),
Stack: changes.Stack(),
Intent: &apiv1.Spec{Resources: toBeWatched},
},
Spec: &apiv1.Spec{Resources: toBeWatched},
}); err != nil {
return err
}
Expand All @@ -462,7 +477,7 @@ func Watch(
//
// Example:
//
// o := NewApplyOptions()
// o := newApplyOptions()
// spec, err := generate.GenerateSpecWithSpinner(o.RefProject, o.RefStack, o.RefWorkspace, nil, o.NoStyle)
//
// if err != nil {
Expand All @@ -486,9 +501,7 @@ func PortForward(
// portforward operation
wo := &operation.PortForwardOperation{}
if err := wo.PortForward(&operation.PortForwardRequest{
Request: models.Request{
Intent: spec,
},
Spec: spec,
Port: o.PortForward,
}); err != nil {
return err
Expand Down
Loading

0 comments on commit 7765bc1

Please sign in to comment.