Skip to content

Commit

Permalink
feat: add a new command kusion release list
Browse files Browse the repository at this point in the history
  • Loading branch information
ekjotsinghmakhija committed Aug 8, 2024
1 parent 106ac08 commit 11d1e51
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 0 deletions.
131 changes: 131 additions & 0 deletions pkg/cmd/release/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package rel

import (
"fmt"

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

var (
listShort = i18n.T("List all releases of the current stack")

listLong = i18n.T(`
List all releases of the current stack.
This command displays information about all releases of the current stack in the current or a specified workspace,
including their revision, phase, and creation time.
`)

listExample = i18n.T(`
# List all releases of the current stack in current workspace
kusion release list
# List all releases of the current stack in a specified workspace
kusion release list --workspace=dev
`)
)

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

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

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

// NewCmdList creates the `kusion release list` command.
func NewCmdList(streams genericiooptions.IOStreams) *cobra.Command {
flags := NewListFlags(streams)

cmd := &cobra.Command{
Use: "list",
Short: listShort,
Long: templates.LongDesc(listLong),
Example: templates.Examples(listExample),
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 *ListFlags) AddFlags(cmd *cobra.Command) {
f.MetaFlags.AddFlags(cmd)
}

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

o := &ListOptions{
MetaOptions: metaOpts,
}

return o, nil
}

// Validate verifies if ListOptions are valid and without conflicts.
func (o *ListOptions) 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 list` command.
func (o *ListOptions) 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 all releases.
releases := storage.GetRevisions()
if len(releases) == 0 {
fmt.Printf("No releases found for project: %s, workspace: %s\n",
o.RefProject.Name, o.RefWorkspace.Name)
return nil
}

// Print the releases
fmt.Printf("Releases for project: %s, workspace: %s\n\n", o.RefProject.Name, o.RefWorkspace.Name)
fmt.Printf("%-10s %-15s %-30s\n", "Revision", "Phase", "Creation Time")
fmt.Println("------------------------------------------------------")
for _, revision := range releases {
r, err := storage.Get(revision)
if err != nil {
return err
}
fmt.Printf("%-10d %-15s %-30s\n", r.Revision, string(r.Phase), r.CreateTime.Format("2006-01-02 15:04:05"))
}

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

import (
"fmt"
"testing"

"github.com/bytedance/mockey"
"github.com/stretchr/testify/assert"
v1 "kusionstack.io/kusion/pkg/apis/api.kusion.io/v1"
"kusionstack.io/kusion/pkg/cmd/meta"
"kusionstack.io/kusion/pkg/engine/release"
"kusionstack.io/kusion/pkg/workspace"
)

// ... (TestListOptions_Validate remains the same)

func TestListOptions_Run(t *testing.T) {
opts := &ListOptions{
MetaOptions: &meta.MetaOptions{
RefProject: &v1.Project{
Name: "mock-project",
},
RefWorkspace: &v1.Workspace{
Name: "mock-workspace",
},
Backend: &fakeBackendForList{},
},
}

t.Run("No Releases Found", func(t *testing.T) {
mockey.PatchConvey("mock release storage", t, func() {
mockStorage := &fakeStorageForList{
revisions: []uint64{},
releases: map[uint64]*v1.Release{},
}
mockey.Mock((*fakeBackendForList).ReleaseStorage).
Return(mockStorage, nil).Build()

err := opts.Run()
assert.NoError(t, err)
})
})

// ... (other test cases remain the same)
}

// Fake implementations for testing
type fakeBackendForList struct{}

func (f *fakeBackendForList) ReleaseStorage(project, workspace string) (release.Storage, error) {
return &fakeStorageForList{}, nil
}

func (f *fakeBackendForList) WorkspaceStorage() (workspace.Storage, error) {
return &fakeWorkspaceStorage{}, nil
}

type fakeWorkspaceStorage struct {
revisions []uint64

Check failure on line 59 in pkg/cmd/release/list_test.go

View workflow job for this annotation

GitHub Actions / Golang Lint

field `revisions` is unused (unused)
releases map[uint64]*v1.Release

Check failure on line 60 in pkg/cmd/release/list_test.go

View workflow job for this annotation

GitHub Actions / Golang Lint

field `releases` is unused (unused)
}

func (f *fakeWorkspaceStorage) Get(name string) (*v1.Workspace, error) {
return &v1.Workspace{Name: name}, nil
}

func (f *fakeWorkspaceStorage) List() ([]*v1.Workspace, error) {
return []*v1.Workspace{}, nil
}

func (f *fakeWorkspaceStorage) Create(ws *v1.Workspace) error {
return nil
}

func (f *fakeWorkspaceStorage) Update(ws *v1.Workspace) error {
return nil
}

func (f *fakeWorkspaceStorage) Delete(name string) error {
return nil
}

func (f *fakeWorkspaceStorage) GetCurrent() (string, error) {
return "current-workspace", nil
}

func (f *fakeWorkspaceStorage) GetNames() ([]string, error) {
return []string{}, nil
}

func (f *fakeWorkspaceStorage) SetCurrent(name string) error {
return nil
}

type fakeStorageForList struct {
revisions []uint64
releases map[uint64]*v1.Release
}

func (f *fakeStorageForList) Get(revision uint64) (*v1.Release, error) {
r, ok := f.releases[revision]
if !ok {
return nil, fmt.Errorf("release not found")
}
return r, nil
}

func (f *fakeStorageForList) GetRevisions() []uint64 {
return f.revisions
}

func (f *fakeStorageForList) GetLatestRevision() uint64 {
if len(f.revisions) == 0 {
return 0
}
return f.revisions[len(f.revisions)-1]
}

func (f *fakeStorageForList) Create(release *v1.Release) error {
return nil
}

func (f *fakeStorageForList) Update(release *v1.Release) error {
return nil
}

func (f *fakeStorageForList) GetStackBoundRevisions(stack string) []uint64 {
return f.revisions
}

0 comments on commit 11d1e51

Please sign in to comment.