diff --git a/internal/api/types/v1alpha1/types.go b/internal/api/types/v1alpha1/types.go index aea4a7f41..94c89baa0 100644 --- a/internal/api/types/v1alpha1/types.go +++ b/internal/api/types/v1alpha1/types.go @@ -8,6 +8,29 @@ import ( "github.com/akuity/kargo/pkg/api/v1alpha1" ) +func FromStageProto(s *v1alpha1.Stage) *kubev1alpha1.Stage { + if s == nil { + return nil + } + var status kubev1alpha1.StageStatus + if s.GetStatus() != nil { + status = *FromStageStatusProto(s.GetStatus()) + } + var objectMeta metav1.ObjectMeta + if s.GetMetadata() != nil { + objectMeta = *s.GetMetadata() + } + return &kubev1alpha1.Stage{ + TypeMeta: metav1.TypeMeta{ + APIVersion: kubev1alpha1.GroupVersion.String(), + Kind: "Stage", + }, + ObjectMeta: objectMeta, + Spec: FromStageSpecProto(s.GetSpec()), + Status: status, + } +} + func FromStageSpecProto(s *v1alpha1.StageSpec) *kubev1alpha1.StageSpec { return &kubev1alpha1.StageSpec{ Subscriptions: FromSubscriptionsProto(s.GetSubscriptions()), @@ -15,6 +38,96 @@ func FromStageSpecProto(s *v1alpha1.StageSpec) *kubev1alpha1.StageSpec { } } +func FromStageStatusProto(s *v1alpha1.StageStatus) *kubev1alpha1.StageStatus { + if s == nil { + return nil + } + availableStates := make(kubev1alpha1.StageStateStack, len(s.GetAvailableStates())) + for idx, state := range s.GetAvailableStates() { + availableStates[idx] = *FromStageStateProto(state) + } + history := make(kubev1alpha1.StageStateStack, len(s.GetHistory())) + for idx, state := range s.GetHistory() { + history[idx] = *FromStageStateProto(state) + } + return &kubev1alpha1.StageStatus{ + AvailableStates: availableStates, + CurrentState: FromStageStateProto(s.GetCurrentState()), + History: history, + Error: s.GetError(), + } +} + +func FromStageStateProto(s *v1alpha1.StageState) *kubev1alpha1.StageState { + if s == nil { + return nil + } + commits := make([]kubev1alpha1.GitCommit, len(s.GetCommits())) + for idx, commit := range s.GetCommits() { + commits[idx] = *FromGitCommitProto(commit) + } + images := make([]kubev1alpha1.Image, len(s.GetImages())) + for idx, image := range s.GetImages() { + images[idx] = *FromImageProto(image) + } + charts := make([]kubev1alpha1.Chart, len(s.GetCharts())) + for idx, chart := range s.GetCharts() { + charts[idx] = *FromChartProto(chart) + } + return &kubev1alpha1.StageState{ + ID: s.GetId(), + FirstSeen: s.GetFirstSeen(), + Provenance: s.GetProvenance(), + Commits: commits, + Images: images, + Charts: charts, + Health: FromHealthProto(s.GetHealth()), + } +} + +func FromGitCommitProto(g *v1alpha1.GitCommit) *kubev1alpha1.GitCommit { + if g == nil { + return nil + } + return &kubev1alpha1.GitCommit{ + RepoURL: g.GetRepoURL(), + ID: g.GetId(), + Branch: g.GetBranch(), + HealthCheckCommit: g.GetHealthCheckCommit(), + } +} + +func FromImageProto(i *v1alpha1.Image) *kubev1alpha1.Image { + if i == nil { + return nil + } + return &kubev1alpha1.Image{ + RepoURL: i.GetRepoURL(), + Tag: i.GetTag(), + } +} + +func FromChartProto(c *v1alpha1.Chart) *kubev1alpha1.Chart { + if c == nil { + return nil + } + return &kubev1alpha1.Chart{ + RegistryURL: c.GetRegistryURL(), + Name: c.GetName(), + Version: c.GetVersion(), + } +} + +func FromHealthProto(h *v1alpha1.Health) *kubev1alpha1.Health { + if h == nil { + return nil + } + return &kubev1alpha1.Health{ + Status: kubev1alpha1.HealthState(h.GetStatus()), + Issues: h.GetIssues(), + } +} + func FromSubscriptionsProto(s *v1alpha1.Subscriptions) *kubev1alpha1.Subscriptions { if s == nil { return nil diff --git a/internal/cli/stage/delete.go b/internal/cli/stage/delete.go new file mode 100644 index 000000000..1be0280b1 --- /dev/null +++ b/internal/cli/stage/delete.go @@ -0,0 +1,48 @@ +package stage + +import ( + "fmt" + "net/http" + "strings" + + "github.com/bufbuild/connect-go" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/akuity/kargo/internal/cli/option" + v1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1" + "github.com/akuity/kargo/pkg/api/service/v1alpha1/svcv1alpha1connect" +) + +func newDeleteCommand(opt *option.Option) *cobra.Command { + cmd := &cobra.Command{ + Use: "delete", + Args: cobra.ExactArgs(2), + Example: "kargo stage delete (PROJECT) (NAME)", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + project := strings.TrimSpace(args[0]) + if project == "" { + return errors.New("project is required") + } + + name := strings.TrimSpace(args[1]) + if name == "" { + return errors.New("name is required") + } + + client := svcv1alpha1connect.NewKargoServiceClient(http.DefaultClient, opt.ServerURL, opt.ClientOption) + _, err := client.DeleteStage(ctx, connect.NewRequest(&v1alpha1.DeleteStageRequest{ + Project: project, + Name: name, + })) + if err != nil { + return errors.Wrap(err, "delete stage") + } + _, _ = fmt.Fprintf(opt.IOStreams.Out, "Stage Deleted: %q", name) + return nil + }, + } + return cmd +} diff --git a/internal/cli/stage/list.go b/internal/cli/stage/list.go new file mode 100644 index 000000000..377e60584 --- /dev/null +++ b/internal/cli/stage/list.go @@ -0,0 +1,59 @@ +package stage + +import ( + "net/http" + "strings" + + "github.com/bufbuild/connect-go" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "k8s.io/cli-runtime/pkg/printers" + "k8s.io/utils/pointer" + + kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1" + typesv1alpha1 "github.com/akuity/kargo/internal/api/types/v1alpha1" + "github.com/akuity/kargo/internal/cli/option" + v1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1" + "github.com/akuity/kargo/pkg/api/service/v1alpha1/svcv1alpha1connect" +) + +func newListCommand(opt *option.Option) *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Args: cobra.ExactArgs(1), + Example: "kargo stage list (PROJECT)", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + project := strings.TrimSpace(args[0]) + if project == "" { + return errors.New("project is required") + } + + client := svcv1alpha1connect.NewKargoServiceClient(http.DefaultClient, opt.ServerURL, opt.ClientOption) + res, err := client.ListStages(ctx, connect.NewRequest(&v1alpha1.ListStagesRequest{ + Project: project, + })) + if err != nil { + return errors.Wrap(err, "list projects") + } + stages := &kubev1alpha1.StageList{ + Items: make([]kubev1alpha1.Stage, len(res.Msg.GetStages())), + } + for idx, stage := range res.Msg.GetStages() { + stages.Items[idx] = *typesv1alpha1.FromStageProto(stage) + } + if pointer.StringDeref(opt.PrintFlags.OutputFormat, "") == "" { + _ = printers.NewTablePrinter(printers.PrintOptions{}).PrintObj(stages, opt.IOStreams.Out) + return nil + } + printer, err := opt.PrintFlags.ToPrinter() + if err != nil { + return errors.Wrap(err, "new printer") + } + return printer.PrintObj(stages, opt.IOStreams.Out) + }, + } + opt.PrintFlags.AddFlags(cmd) + return cmd +} diff --git a/internal/cli/stage/env.go b/internal/cli/stage/stage.go similarity index 78% rename from internal/cli/stage/env.go rename to internal/cli/stage/stage.go index 282bbf3f1..814e374e2 100644 --- a/internal/cli/stage/env.go +++ b/internal/cli/stage/stage.go @@ -11,6 +11,8 @@ func NewCommand(opt *option.Option) *cobra.Command { Use: "stage", Short: "Manage stages", } + cmd.AddCommand(newDeleteCommand(opt)) + cmd.AddCommand(newListCommand(opt)) cmd.AddCommand(newPromoteCommand(opt)) return cmd }