Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support release unlock command #1191

Merged
merged 1 commit into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions pkg/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"kusionstack.io/kusion/pkg/cmd/mod"
"kusionstack.io/kusion/pkg/cmd/preview"
"kusionstack.io/kusion/pkg/cmd/project"
rel "kusionstack.io/kusion/pkg/cmd/release"
"kusionstack.io/kusion/pkg/cmd/stack"
"kusionstack.io/kusion/pkg/cmd/version"
"kusionstack.io/kusion/pkg/cmd/workspace"
Expand Down Expand Up @@ -128,6 +129,12 @@ Find more information at: https://www.kusionstack.io`),
mod.NewCmdMod(o.IOStreams),
},
},
{
Message: "Release Management Commands:",
Commands: []*cobra.Command{
rel.NewCmdRel(o.IOStreams),
},
},
}
groups.Add(rootCmd)

Expand Down
29 changes: 29 additions & 0 deletions pkg/cmd/release/release.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package rel

import (
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/kubectl/pkg/util/templates"
cmdutil "kusionstack.io/kusion/pkg/cmd/util"
"kusionstack.io/kusion/pkg/util/i18n"
)

var relLong = i18n.T(`
Commands for managing Kusion release files.
These commands help you manage the lifecycle of Kusion release files. `)

// NewCmdRel returns an initialized Command instance for 'release' sub command.
func NewCmdRel(streams genericiooptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "release",
DisableFlagsInUseLine: true,
Short: "Manage Kusion release files",
Long: templates.LongDesc(relLong),
Run: cmdutil.DefaultSubCommandRun(streams.ErrOut),
}

cmd.AddCommand(NewCmdUnlock(streams))

return cmd
}
17 changes: 17 additions & 0 deletions pkg/cmd/release/release_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package rel

import (
"testing"

"github.com/stretchr/testify/assert"
"k8s.io/cli-runtime/pkg/genericiooptions"
)

func TestNewCmdRel(t *testing.T) {
t.Run("successfully get release help", func(t *testing.T) {
streams, _, _, _ := genericiooptions.NewTestIOStreams()

cmd := NewCmdRel(streams)
assert.NotNil(t, cmd)
})
}
143 changes: 143 additions & 0 deletions pkg/cmd/release/unlock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package rel

import (
"fmt"

"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericiooptions"
"k8s.io/kubectl/pkg/util/templates"
v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
"kusionstack.io/kusion/pkg/cmd/meta"
cmdutil "kusionstack.io/kusion/pkg/cmd/util"
"kusionstack.io/kusion/pkg/engine/release"
"kusionstack.io/kusion/pkg/util/i18n"
)

var (
unlockShort = i18n.T("Unlock the latest release file of the current stack")

unlockLong = i18n.T(`
Unlock the latest release file of the current stack.
The phase of the latest release file of the current stack in the current or a specified workspace
will be set to 'failed' if it was in the stages of 'generating', 'previewing', 'applying' or 'destroying'.
Please note that using the 'kusion release unlock' command may cause unexpected concurrent read-write
issues with release files, so please use it with caution.
`)

unlockExample = i18n.T(`# Unlock the latest release file of the current stack in the current workspace.
kusion release unlock
# Unlock the latest release file of the current stack in a specified workspace.
kusion release unlock --workspace=dev
`)
)

// UnlockFlags reflects the information that CLI is gathering via flags,
// which will be converted into UnlockOptions.
type UnlockFlags struct {
MetaFlags *meta.MetaFlags
}

// UnlockOptions defines the configuration parameters for the `kusion release unlock` command.
type UnlockOptions struct {
*meta.MetaOptions
}

// NewUnlockFlags returns a default UnlockFlags.
func NewUnlockFlags(streams genericiooptions.IOStreams) *UnlockFlags {
return &UnlockFlags{
MetaFlags: meta.NewMetaFlags(),
}
}

// NewCmdUnlock creates the `kusion release unlock` command.
func NewCmdUnlock(streams genericiooptions.IOStreams) *cobra.Command {
flags := NewUnlockFlags(streams)

cmd := &cobra.Command{
Use: "unlock",
Short: unlockShort,
Long: templates.LongDesc(unlockLong),
Example: templates.Examples(unlockExample),
RunE: func(cmd *cobra.Command, args []string) (err error) {
o, err := flags.ToOptions()
defer cmdutil.RecoverErr(&err)
cmdutil.CheckErr(err)
cmdutil.CheckErr(o.Validate(cmd, args))
cmdutil.CheckErr(o.Run())

return
},
}

flags.AddFlags(cmd)

return cmd
}

// AddFlags registers flags for the CLI.
func (f *UnlockFlags) AddFlags(cmd *cobra.Command) {
f.MetaFlags.AddFlags(cmd)
}

// ToOptions converts from CLI inputs to runtime inputs.
func (f *UnlockFlags) ToOptions() (*UnlockOptions, error) {
metaOpts, err := f.MetaFlags.ToOptions()
if err != nil {
return nil, err
}

o := &UnlockOptions{
MetaOptions: metaOpts,
}

return o, nil
}

// Validate verifies if UnlockOptions are valid and without conflicts.
func (o *UnlockOptions) Validate(cmd *cobra.Command, args []string) error {
if len(args) != 0 {
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
}

return nil
}

// Run executes the `kusion release unlock` command.
func (o *UnlockOptions) Run() error {
// Get the storage backend of the release.
storage, err := o.Backend.ReleaseStorage(o.RefProject.Name, o.RefWorkspace.Name)
if err != nil {
return err
}

// Get the latest release.
r, err := release.GetLatestRelease(storage)
if err != nil {
return err
}
if r == nil {
fmt.Printf("No release file found for project: %s, workspace: %s\n",
o.RefProject.Name, o.RefWorkspace.Name)
return nil
}

// Update the phase to 'failed', if it was not succeeded or failed.
if r.Phase != v1.ReleasePhaseSucceeded && r.Phase != v1.ReleasePhaseFailed {
r.Phase = v1.ReleasePhaseFailed

if err := storage.Update(r); err != nil {
return err
}

fmt.Printf("Successfully update release phase to Failed, project: %s, workspace: %s, revision: %d\n",
r.Project, r.Workspace, r.Revision)

return nil
}

fmt.Printf("No need to update the release phase, current phase: %s\n", r.Phase)
return nil
}
Loading
Loading