-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add a new command kusion release list
- Loading branch information
1 parent
106ac08
commit 11d1e51
Showing
2 changed files
with
260 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
releases map[uint64]*v1.Release | ||
} | ||
|
||
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 | ||
} |