-
Notifications
You must be signed in to change notification settings - Fork 135
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
get promotions
command to CLI (#790)
Signed-off-by: Sunghoon Kang <[email protected]> Co-authored-by: Kent Rancourt <[email protected]>
- Loading branch information
Showing
7 changed files
with
355 additions
and
230 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 |
---|---|---|
@@ -1,162 +1,72 @@ | ||
package get | ||
|
||
import ( | ||
"strings" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/exp/slices" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/util/duration" | ||
"k8s.io/cli-runtime/pkg/printers" | ||
"k8s.io/utils/pointer" | ||
|
||
kargoapi "github.com/akuity/kargo/api/v1alpha1" | ||
"github.com/akuity/kargo/internal/cli/client" | ||
"github.com/akuity/kargo/internal/cli/option" | ||
) | ||
|
||
func NewCommand(opt *option.Option) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "get [--project=project] (RESOURCE) [NAME]...", | ||
Use: "get (RESOURCE) [NAME]...", | ||
Short: "Display one or many resources", | ||
Args: cobra.MinimumNArgs(1), | ||
Example: ` | ||
# List all projects | ||
kargo get projects | ||
# List all stages in the project | ||
kargo get --project= stages | ||
kargo get stages --project=my-project | ||
# List all stages in JSON output format | ||
kargo get stages my-project -o json | ||
# List all promotions for the given stage | ||
kargo get promotions --project=my-project --stage=my-stage | ||
`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
ctx := cmd.Context() | ||
kargoSvcCli, err := client.GetClientFromConfig(ctx, opt) | ||
if err != nil { | ||
return errors.New("get client from config") | ||
} | ||
|
||
resource := strings.ToLower(strings.TrimSpace(args[0])) | ||
if resource == "" { | ||
return errors.New("resource is required") | ||
} | ||
|
||
var resErr error | ||
res := &metav1.List{ | ||
TypeMeta: metav1.TypeMeta{ | ||
APIVersion: metav1.Unversioned.String(), | ||
Kind: "List", | ||
}, | ||
} | ||
switch resource { | ||
case "project", "projects": | ||
names := slices.Compact(args[1:]) | ||
filter, err := filterProjects(ctx, kargoSvcCli) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var projects []runtime.Object | ||
projects, resErr = filter(names...) | ||
res.Items = make([]runtime.RawExtension, 0, len(projects)) | ||
for _, project := range projects { | ||
res.Items = append(res.Items, runtime.RawExtension{Object: project}) | ||
} | ||
if len(names) == 1 { | ||
if len(res.Items) == 1 { | ||
_ = printResult(opt, res.Items[0].Object) | ||
} | ||
return resErr | ||
} | ||
case "stage", "stages": | ||
project := opt.Project.OrElse("") | ||
if project == "" { | ||
return errors.New("project is required") | ||
} | ||
names := slices.Compact(args[1:]) | ||
filter, err := filterStages(ctx, kargoSvcCli, project) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
var stages []runtime.Object | ||
stages, resErr = filter(names...) | ||
res.Items = make([]runtime.RawExtension, 0, len(stages)) | ||
for _, stage := range stages { | ||
res.Items = append(res.Items, runtime.RawExtension{Object: stage}) | ||
} | ||
if len(names) == 1 && len(res.Items) == 1 { | ||
_ = printStageResult(opt, res.Items[0].Object) | ||
} else { | ||
_ = printStageResult(opt, res) | ||
} | ||
return resErr | ||
default: | ||
return errors.Errorf("unknown resource %q", resource) | ||
} | ||
_ = printResult(opt, res) | ||
return resErr | ||
}, | ||
} | ||
option.OptionalProject(opt.Project)(cmd.Flags()) | ||
opt.PrintFlags.AddFlags(cmd) | ||
// Subcommands | ||
cmd.AddCommand(newGetProjectsCommand(opt)) | ||
cmd.AddCommand(newGetStagesCommand(opt)) | ||
cmd.AddCommand(newGetPromotionsCommand(opt)) | ||
return cmd | ||
} | ||
|
||
func printStageResult(opt *option.Option, res runtime.Object) error { | ||
if pointer.StringDeref(opt.PrintFlags.OutputFormat, "") != "" { | ||
return printResult(opt, res) | ||
func printObjects[T runtime.Object](opt *option.Option, objects []T) error { | ||
items := make([]runtime.RawExtension, len(objects)) | ||
for i, obj := range objects { | ||
items[i] = runtime.RawExtension{Object: obj} | ||
} | ||
var items []runtime.RawExtension | ||
if list, ok := res.(*metav1.List); ok { | ||
items = list.Items | ||
} else { | ||
items = []runtime.RawExtension{{Object: res}} | ||
} | ||
table := &metav1.Table{ | ||
ColumnDefinitions: []metav1.TableColumnDefinition{ | ||
{Name: "Name", Type: "string"}, | ||
{Name: "Current Freight", Type: "string"}, | ||
{Name: "Health", Type: "string"}, | ||
{Name: "Age", Type: "string"}, | ||
list := &metav1.List{ | ||
TypeMeta: metav1.TypeMeta{ | ||
APIVersion: metav1.Unversioned.String(), | ||
Kind: "List", | ||
}, | ||
Rows: make([]metav1.TableRow, len(items)), | ||
Items: items, | ||
} | ||
for i, item := range items { | ||
// This func is only ever passed Stages | ||
stage := item.Object.(*kargoapi.Stage) // nolint: forcetypeassert | ||
var currentFreightID string | ||
if stage.Status.CurrentFreight != nil { | ||
currentFreightID = stage.Status.CurrentFreight.ID | ||
} | ||
var health string | ||
if stage.Status.Health != nil { | ||
health = string(stage.Status.Health.Status) | ||
|
||
if pointer.StringDeref(opt.PrintFlags.OutputFormat, "") != "" { | ||
printer, err := opt.PrintFlags.ToPrinter() | ||
if err != nil { | ||
return errors.Wrap(err, "new printer") | ||
} | ||
table.Rows[i] = metav1.TableRow{ | ||
Cells: []any{ | ||
stage.Name, | ||
currentFreightID, | ||
health, | ||
duration.HumanDuration(time.Since(stage.CreationTimestamp.Time)), | ||
}, | ||
Object: item, | ||
if len(list.Items) == 1 { | ||
return printer.PrintObj(list.Items[0].Object, opt.IOStreams.Out) | ||
} | ||
return printer.PrintObj(list, opt.IOStreams.Out) | ||
} | ||
return printers.NewTablePrinter(printers.PrintOptions{}).PrintObj(table, opt.IOStreams.Out) | ||
} | ||
|
||
func printResult(opt *option.Option, res runtime.Object) error { | ||
if pointer.StringDeref(opt.PrintFlags.OutputFormat, "") == "" { | ||
return printers.NewTablePrinter(printers.PrintOptions{}).PrintObj(res, opt.IOStreams.Out) | ||
} | ||
printer, err := opt.PrintFlags.ToPrinter() | ||
if err != nil { | ||
return errors.Wrap(err, "new printer") | ||
var t T | ||
switch any(t).(type) { | ||
case *kargoapi.Stage: | ||
table := newStageTable(list) | ||
return printers.NewTablePrinter(printers.PrintOptions{}).PrintObj(table, opt.IOStreams.Out) | ||
case *kargoapi.Promotion: | ||
table := newPromotionTable(list) | ||
return printers.NewTablePrinter(printers.PrintOptions{}).PrintObj(table, opt.IOStreams.Out) | ||
default: | ||
return printers.NewTablePrinter(printers.PrintOptions{}).PrintObj(list, opt.IOStreams.Out) | ||
} | ||
return printer.PrintObj(res, opt.IOStreams.Out) | ||
} |
This file was deleted.
Oops, something went wrong.
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,70 @@ | ||
package get | ||
|
||
import ( | ||
goerrors "errors" | ||
|
||
"connectrpc.com/connect" | ||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/exp/slices" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
|
||
typesv1alpha1 "github.com/akuity/kargo/internal/api/types/v1alpha1" | ||
"github.com/akuity/kargo/internal/cli/client" | ||
"github.com/akuity/kargo/internal/cli/option" | ||
v1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1" | ||
) | ||
|
||
func newGetProjectsCommand(opt *option.Option) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "projects [NAME...]", | ||
Aliases: []string{"project"}, | ||
Short: "Display one or many projects", | ||
Example: ` | ||
# List all projects | ||
kargo get projects | ||
# List all projects in JSON output format | ||
kargo get projects -o json | ||
`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
ctx := cmd.Context() | ||
|
||
kargoSvcCli, err := client.GetClientFromConfig(ctx, opt) | ||
if err != nil { | ||
return errors.New("get client from config") | ||
} | ||
resp, err := kargoSvcCli.ListProjects(ctx, connect.NewRequest(&v1alpha1.ListProjectsRequest{})) | ||
if err != nil { | ||
return errors.Wrap(err, "list projects") | ||
} | ||
|
||
names := slices.Compact(args) | ||
res := make([]*unstructured.Unstructured, 0, len(resp.Msg.GetProjects())) | ||
var resErr error | ||
if len(names) == 0 { | ||
for _, p := range resp.Msg.GetProjects() { | ||
res = append(res, typesv1alpha1.FromProjectProto(p)) | ||
} | ||
} else { | ||
projectsByName := make(map[string]*unstructured.Unstructured, len(resp.Msg.GetProjects())) | ||
for _, p := range resp.Msg.GetProjects() { | ||
projectsByName[p.GetName()] = typesv1alpha1.FromProjectProto(p) | ||
} | ||
for _, name := range names { | ||
if promo, ok := projectsByName[name]; ok { | ||
res = append(res, promo) | ||
} else { | ||
resErr = goerrors.Join(err, errors.Errorf("project %q not found", name)) | ||
} | ||
} | ||
} | ||
if err := printObjects(opt, res); err != nil { | ||
return err | ||
} | ||
return resErr | ||
}, | ||
} | ||
opt.PrintFlags.AddFlags(cmd) | ||
return cmd | ||
} |
Oops, something went wrong.