diff --git a/cmd/admin/v2/commands.go b/cmd/admin/v2/commands.go index 881a7be..6ab7358 100644 --- a/cmd/admin/v2/commands.go +++ b/cmd/admin/v2/commands.go @@ -14,10 +14,11 @@ func AddCmds(cmd *cobra.Command, c *config.Config) { Hidden: true, } + adminCmd.AddCommand(newComponentCmd(c)) adminCmd.AddCommand(newImageCmd(c)) + adminCmd.AddCommand(newProjectCmd(c)) adminCmd.AddCommand(newTenantCmd(c)) adminCmd.AddCommand(newTokenCmd(c)) - adminCmd.AddCommand(newProjectCmd(c)) cmd.AddCommand(adminCmd) } diff --git a/cmd/admin/v2/component.go b/cmd/admin/v2/component.go new file mode 100644 index 0000000..5910dd8 --- /dev/null +++ b/cmd/admin/v2/component.go @@ -0,0 +1,109 @@ +package v2 + +import ( + "fmt" + + "github.com/metal-stack/api/go/enum" + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/metal-stack/cli/cmd/config" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/metal-stack/metal-lib/pkg/pointer" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type component struct { + c *config.Config +} + +func newComponentCmd(c *config.Config) *cobra.Command { + w := &component{ + c: c, + } + gcli := genericcli.NewGenericCLI(w).WithFS(c.Fs) + + cmdsConfig := &genericcli.CmdsConfig[any, any, *apiv2.Component]{ + BinaryName: config.BinaryName, + GenericCLI: gcli, + Singular: "component", + Plural: "components", + Description: "list status of components, e.g. microservices connected to the metal-apiserver", + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + OnlyCmds: genericcli.OnlyCmds(genericcli.DescribeCmd, genericcli.ListCmd, genericcli.DeleteCmd), + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("uuid", "", "lists only component with this uuid") + cmd.Flags().String("type", "", "lists only component of this type") + cmd.Flags().String("identifier", "", "lists only component with this identifier") + }, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *component) Get(id string) (*apiv2.Component, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &adminv2.ComponentServiceGetRequest{Uuid: id} + + resp, err := c.c.Client.Adminv2().Component().Get(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get component: %w", err) + } + + return resp.Component, nil +} + +func (c *component) Create(rq any) (*apiv2.Component, error) { + panic("unimplemented") +} + +func (c *component) Delete(id string) (*apiv2.Component, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &adminv2.ComponentServiceDeleteRequest{Uuid: id} + + resp, err := c.c.Client.Adminv2().Component().Delete(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to delete component: %w", err) + } + + return resp.Component, nil +} +func (c *component) List() ([]*apiv2.Component, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + query := &apiv2.ComponentQuery{ + Uuid: pointer.PointerOrNil(viper.GetString("uuid")), + Identifier: pointer.PointerOrNil(viper.GetString("identifier")), + } + + if viper.IsSet("type") { + t, err := enum.GetEnum[apiv2.ComponentType](viper.GetString("type")) + if err != nil { + return nil, fmt.Errorf("unable to get component type of string %q %w", viper.GetString("type"), err) + } + query.Type = &t + } + + req := &adminv2.ComponentServiceListRequest{Query: query} + + resp, err := c.c.Client.Adminv2().Component().List(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get components: %w", err) + } + + return resp.Components, nil +} +func (c *component) Convert(r *apiv2.Component) (string, any, any, error) { + panic("unimplemented") +} + +func (c *component) Update(rq any) (*apiv2.Component, error) { + panic("unimplemented") +} diff --git a/cmd/tableprinters/common.go b/cmd/tableprinters/common.go index 19c30e7..78fad32 100644 --- a/cmd/tableprinters/common.go +++ b/cmd/tableprinters/common.go @@ -30,6 +30,11 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin case *config.Contexts: return t.ContextTable(d, wide) + case *apiv2.Component: + return t.ComponentTable(pointer.WrapInSlice(d), wide) + case []*apiv2.Component: + return t.ComponentTable(d, wide) + case *apiv2.IP: return t.IPTable(pointer.WrapInSlice(d), wide) case []*apiv2.IP: diff --git a/cmd/tableprinters/component.go b/cmd/tableprinters/component.go new file mode 100644 index 0000000..fddb4c2 --- /dev/null +++ b/cmd/tableprinters/component.go @@ -0,0 +1,46 @@ +package tableprinters + +import ( + "time" + + "github.com/fatih/color" + "github.com/metal-stack/api/go/enum" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" +) + +func (t *TablePrinter) ComponentTable(data []*apiv2.Component, wide bool) ([]string, [][]string, error) { + var ( + rows [][]string + header = []string{"ID", "Type", "Identifier", "Started", "Age", "Version", "Token", "Token Expires In"} + ) + + for _, c := range data { + typeString, err := enum.GetStringValue(c.Type) + if err != nil { + return nil, nil, err + } + + started := humanizeDuration(time.Since(c.StartedAt.AsTime())) + ageAsDuration := time.Since(c.ReportedAt.AsTime()) + age := humanizeDuration(ageAsDuration) + + if c.Interval != nil && c.Interval.AsDuration() < ageAsDuration { + age = color.RedString(age) + } + + tokenExpirationAsDuration := time.Until(c.Token.Expires.AsTime()) + tokenExpiresIn := humanizeDuration(tokenExpirationAsDuration) + + if tokenExpirationAsDuration < time.Hour { + tokenExpiresIn = color.YellowString(tokenExpiresIn) + } else if tokenExpirationAsDuration < 0 { + tokenExpiresIn = color.RedString(tokenExpiresIn) + } + + rows = append(rows, []string{c.Uuid, *typeString, c.Identifier, started, age, c.Version.Version, c.Token.Uuid, tokenExpiresIn}) + } + + t.t.DisableAutoWrap(false) + + return header, rows, nil +}