From 6fe3d5109229c450a456461069e3860ce645c30b Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Tue, 12 Aug 2025 11:13:44 +0200 Subject: [PATCH 1/4] Initial machine get and list --- cmd/api/v1/commands.go | 1 + cmd/api/v1/machine.go | 106 ++++++++++++++++++ cmd/completion/machine.go | 22 ++++ cmd/printers.go | 1 + cmd/sorters/machine.go | 20 ++++ cmd/tableprinters/common.go | 12 ++- cmd/tableprinters/icons.go | 10 ++ cmd/tableprinters/machine.go | 160 ++++++++++++++++++++++++++++ docs/metalctlv2.md | 1 + docs/metalctlv2_machine.md | 38 +++++++ docs/metalctlv2_machine_apply.md | 46 ++++++++ docs/metalctlv2_machine_create.md | 46 ++++++++ docs/metalctlv2_machine_delete.md | 46 ++++++++ docs/metalctlv2_machine_describe.md | 32 ++++++ docs/metalctlv2_machine_edit.md | 31 ++++++ docs/metalctlv2_machine_list.md | 33 ++++++ docs/metalctlv2_machine_update.md | 46 ++++++++ pkg/helpers/emoji.go | 27 +++++ 18 files changed, 677 insertions(+), 1 deletion(-) create mode 100644 cmd/api/v1/machine.go create mode 100644 cmd/completion/machine.go create mode 100644 cmd/sorters/machine.go create mode 100644 cmd/tableprinters/icons.go create mode 100644 cmd/tableprinters/machine.go create mode 100644 docs/metalctlv2_machine.md create mode 100644 docs/metalctlv2_machine_apply.md create mode 100644 docs/metalctlv2_machine_create.md create mode 100644 docs/metalctlv2_machine_delete.md create mode 100644 docs/metalctlv2_machine_describe.md create mode 100644 docs/metalctlv2_machine_edit.md create mode 100644 docs/metalctlv2_machine_list.md create mode 100644 docs/metalctlv2_machine_update.md create mode 100644 pkg/helpers/emoji.go diff --git a/cmd/api/v1/commands.go b/cmd/api/v1/commands.go index 1e62d60..52d0cc1 100644 --- a/cmd/api/v1/commands.go +++ b/cmd/api/v1/commands.go @@ -13,6 +13,7 @@ func AddCmds(cmd *cobra.Command, c *config.Config) { cmd.AddCommand(newImageCmd(c)) cmd.AddCommand(newProjectCmd(c)) cmd.AddCommand(newTenantCmd(c)) + cmd.AddCommand(newMachineCmd(c)) cmd.AddCommand(newMethodsCmd(c)) cmd.AddCommand(newUserCmd(c)) } diff --git a/cmd/api/v1/machine.go b/cmd/api/v1/machine.go new file mode 100644 index 0000000..e79e7f3 --- /dev/null +++ b/cmd/api/v1/machine.go @@ -0,0 +1,106 @@ +package v1 + +import ( + "connectrpc.com/connect" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/metal-stack/cli/cmd/config" + "github.com/metal-stack/cli/cmd/sorters" + "github.com/metal-stack/cli/pkg/helpers" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/spf13/cobra" +) + +type machine struct { + c *config.Config +} + +func newMachineCmd(c *config.Config) *cobra.Command { + w := &machine{ + c: c, + } + + cmdsConfig := &genericcli.CmdsConfig[*apiv2.MachineServiceCreateRequest, *apiv2.MachineServiceUpdateRequest, *apiv2.Machine]{ + BinaryName: config.BinaryName, + GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), + Singular: "machine", + Plural: "machines", + Description: "an machine of metal-stack.io", + Sorter: sorters.MachineSorter(), + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.ProjectListCompletion)) + }, + DescribeCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().StringP("project", "p", "", "project of the machine") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.ProjectListCompletion)) + }, + ValidArgsFn: c.Completion.MachineListCompletion, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *machine) updateFromCLI(args []string) (*apiv2.MachineServiceUpdateRequest, error) { + panic("unimplemented") +} + +func (c *machine) Create(rq *apiv2.MachineServiceCreateRequest) (*apiv2.Machine, error) { + panic("unimplemented") +} + +func (c *machine) Delete(id string) (*apiv2.Machine, error) { + panic("unimplemented") +} + +func (c *machine) Get(id string) (*apiv2.Machine, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Machine().Get(ctx, connect.NewRequest(&apiv2.MachineServiceGetRequest{ + Project: c.c.GetProject(), + Uuid: id, + })) + if err != nil { + return nil, err + } + + return resp.Msg.Machine, nil +} + +func (c *machine) List() ([]*apiv2.Machine, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Machine().List(ctx, connect.NewRequest(&apiv2.MachineServiceListRequest{ + Project: c.c.GetProject(), + })) + if err != nil { + return nil, err + } + + return resp.Msg.Machines, nil +} + +func (c *machine) Update(rq *apiv2.MachineServiceUpdateRequest) (*apiv2.Machine, error) { + panic("unimplemented") +} + +func (c *machine) Convert(r *apiv2.Machine) (string, *apiv2.MachineServiceCreateRequest, *apiv2.MachineServiceUpdateRequest, error) { + responseToUpdate, err := c.MachineResponseToUpdate(r) + return helpers.EncodeProject(r.Uuid, r.Allocation.Project), c.MachineResponseToCreate(r), responseToUpdate, err +} + +func (c *machine) MachineResponseToCreate(r *apiv2.Machine) *apiv2.MachineServiceCreateRequest { + return &apiv2.MachineServiceCreateRequest{ + // FIXME + } +} + +func (c *machine) MachineResponseToUpdate(desired *apiv2.Machine) (*apiv2.MachineServiceUpdateRequest, error) { + panic("unimplemented") +} diff --git a/cmd/completion/machine.go b/cmd/completion/machine.go new file mode 100644 index 0000000..1f79b12 --- /dev/null +++ b/cmd/completion/machine.go @@ -0,0 +1,22 @@ +package completion + +import ( + "connectrpc.com/connect" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/spf13/cobra" +) + +func (c *Completion) MachineListCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + req := &apiv2.MachineServiceListRequest{ + Project: c.Project, + } + resp, err := c.Client.Apiv2().Machine().List(c.Ctx, connect.NewRequest(req)) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + var names []string + for _, s := range resp.Msg.Machines { + names = append(names, s.Uuid) + } + return names, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/printers.go b/cmd/printers.go index 3668f2c..2e27033 100644 --- a/cmd/printers.go +++ b/cmd/printers.go @@ -32,6 +32,7 @@ func newPrinterFromCLI(out io.Writer) (printers.Printer, error) { } tablePrinter := printers.NewTablePrinter(cfg).WithOut(out) tp.SetPrinter(tablePrinter) + tp.SetLastEventErrorThreshold(viper.GetDuration("last-event-error-threshold")) printer = tablePrinter case "template": printer = printers.NewTemplatePrinter(viper.GetString("template")).WithOut(out) diff --git a/cmd/sorters/machine.go b/cmd/sorters/machine.go new file mode 100644 index 0000000..0a92679 --- /dev/null +++ b/cmd/sorters/machine.go @@ -0,0 +1,20 @@ +package sorters + +import ( + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/metal-stack/metal-lib/pkg/multisort" +) + +func MachineSorter() *multisort.Sorter[*apiv2.Machine] { + return multisort.New(multisort.FieldMap[*apiv2.Machine]{ + "partition": func(a, b *apiv2.Machine, descending bool) multisort.CompareResult { + return multisort.Compare(a.Partition.Id, b.Partition.Id, descending) + }, + "size": func(a, b *apiv2.Machine, descending bool) multisort.CompareResult { + return multisort.Compare(a.Size.Id, b.Size.Id, descending) + }, + "uuid": func(a, b *apiv2.Machine, descending bool) multisort.CompareResult { + return multisort.Compare(a.Uuid, b.Uuid, descending) + }, + }, multisort.Keys{{ID: "uuid"}, {ID: "size"}, {ID: "partition"}}) +} diff --git a/cmd/tableprinters/common.go b/cmd/tableprinters/common.go index 19c30e7..dca46c2 100644 --- a/cmd/tableprinters/common.go +++ b/cmd/tableprinters/common.go @@ -13,7 +13,8 @@ import ( ) type TablePrinter struct { - t *printers.TablePrinter + t *printers.TablePrinter + lastEventErrorThreshold time.Duration } func New() *TablePrinter { @@ -24,6 +25,10 @@ func (t *TablePrinter) SetPrinter(printer *printers.TablePrinter) { t.t = printer } +func (t *TablePrinter) SetLastEventErrorThreshold(threshold time.Duration) { + t.lastEventErrorThreshold = threshold +} + func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]string, error) { switch d := data.(type) { @@ -35,6 +40,11 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin case []*apiv2.IP: return t.IPTable(d, wide) + case *apiv2.Machine: + return t.MachineTable(pointer.WrapInSlice(d), wide) + case []*apiv2.Machine: + return t.MachineTable(d, wide) + case *apiv2.Image: return t.ImageTable(pointer.WrapInSlice(d), wide) case []*apiv2.Image: diff --git a/cmd/tableprinters/icons.go b/cmd/tableprinters/icons.go new file mode 100644 index 0000000..165f63f --- /dev/null +++ b/cmd/tableprinters/icons.go @@ -0,0 +1,10 @@ +package tableprinters + +const ( + dot = "●" + halfpie = "◒" + threequarterpie = "◕" + nbr = " " + poweron = "⏻" + powersleep = "⏾" +) diff --git a/cmd/tableprinters/machine.go b/cmd/tableprinters/machine.go new file mode 100644 index 0000000..4a1ae02 --- /dev/null +++ b/cmd/tableprinters/machine.go @@ -0,0 +1,160 @@ +package tableprinters + +import ( + "fmt" + "strings" + "time" + + "github.com/fatih/color" + "github.com/metal-stack/api/go/enum" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/metal-stack/cli/pkg/helpers" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/pointer" +) + +func (t *TablePrinter) MachineTable(data []*apiv2.Machine, wide bool) ([]string, [][]string, error) { + + var ( + rows [][]string + header = []string{"ID", "", "Last Event", "When", "Age", "Hostname", "Project", "Size", "Image", "Partition", "Rack"} + ) + + if wide { + header = []string{"ID", "Last Event", "When", "Age", "Description", "Name", "Hostname", "Project", "IPs", "Size", "Image", "Partition", "Rack", "Started", "Tags", "Lock/Reserve"} + } + for _, machine := range data { + machineID := machine.Uuid + + if machine.Status != nil && machine.Status.LedState != nil && machine.Status.LedState.Value == "LED-ON" { + blue := color.New(color.FgBlue).SprintFunc() + machineID = blue(machineID) + } + + alloc := pointer.SafeDeref(machine.Allocation) + sizeID := pointer.SafeDeref(machine.Size).Id + partitionID := pointer.SafeDeref(machine.Partition).Id + project := alloc.Project + name := alloc.Name + desc := alloc.Description + hostname := alloc.Hostname + image := pointer.SafeDeref(alloc.Image.Name) + + rack := machine.Rack + + truncatedHostname := genericcli.TruncateEnd(hostname, 30) + + var nwIPs []string + for _, nw := range alloc.Networks { + nwIPs = append(nwIPs, nw.Ips...) + } + ips := strings.Join(nwIPs, "\n") + + started := "" + age := "" + if alloc.Meta.CreatedAt != nil && !alloc.Meta.CreatedAt.AsTime().IsZero() { + started = alloc.Meta.CreatedAt.AsTime().Format(time.RFC3339) + age = humanizeDuration(time.Since(alloc.Meta.CreatedAt.AsTime())) + } + tags := "" + if machine.Meta.Labels != nil && len(machine.Meta.Labels.Labels) > 0 { + var labels []string + for k, v := range machine.Meta.Labels.Labels { + labels = append(labels, k+"="+v) + } + tags = strings.Join(labels, ",") + } + + reserved := "" + if machine.Status.Condition != nil { + stateString, err := enum.GetStringValue(machine.Status.Condition.State) + if err != nil { + return nil, nil, err + } + reserved = fmt.Sprintf("%s:%s", *stateString, machine.Status.Condition.Description) + } + + lastEvent := "" + when := "" + if len(machine.RecentProvisioningEvents.Events) > 0 { + since := time.Since(machine.RecentProvisioningEvents.LastEventTime.AsTime()) + when = humanizeDuration(since) + lastEvent = machine.RecentProvisioningEvents.Events[0].Event + } + + emojis, _ := t.getMachineStatusEmojis(machine.Status.Liveliness, machine.RecentProvisioningEvents, machine.Status.Condition.State, alloc.Vpn) + + if wide { + rows = append(rows, []string{machineID, lastEvent, when, age, desc, name, hostname, project, ips, sizeID, image, partitionID, rack, started, tags, reserved}) + } else { + rows = append(rows, []string{machineID, emojis, lastEvent, when, age, truncatedHostname, project, sizeID, image, partitionID, rack}) + } + } + + t.t.DisableAutoWrap(false) + + return header, rows, nil +} + +func (t *TablePrinter) getMachineStatusEmojis(liveliness apiv2.MachineLiveliness, events *apiv2.MachineRecentProvisioningEvents, state apiv2.MachineState, vpn *apiv2.MachineVPN) (string, string) { + var ( + emojis []string + wide []string + livelinessString *string + err error + ) + livelinessString, err = enum.GetStringValue(liveliness) + if err != nil { + livelinessString = pointer.Pointer("unknown") + } + + switch l := liveliness; l { + case apiv2.MachineLiveliness_MACHINE_LIVELINESS_ALIVE: + // noop + case apiv2.MachineLiveliness_MACHINE_LIVELINESS_DEAD: + emojis = append(emojis, helpers.Skull) + wide = append(wide, *livelinessString) + case apiv2.MachineLiveliness_MACHINE_LIVELINESS_UNKNOWN: + emojis = append(emojis, helpers.Question) + wide = append(wide, *livelinessString) + default: + emojis = append(emojis, helpers.Question) + wide = append(wide, *livelinessString) + } + + switch state { + case apiv2.MachineState_MACHINE_STATE_AVAILABLE: + // noop + case apiv2.MachineState_MACHINE_STATE_LOCKED: + emojis = append(emojis, helpers.Lock) + wide = append(wide, "Locked") + case apiv2.MachineState_MACHINE_STATE_RESERVED: + emojis = append(emojis, helpers.Bark) + wide = append(wide, "Reserved") + } + + if events != nil { + switch events.State { + case apiv2.MachineProvisioningEventState_MACHINE_PROVISIONING_EVENT_STATE_FAILED_RECLAIM: + emojis = append(emojis, helpers.Ambulance) + wide = append(wide, "FailedReclaim") + case apiv2.MachineProvisioningEventState_MACHINE_PROVISIONING_EVENT_STATE_CRASHLOOP: + emojis = append(emojis, helpers.Loop) + wide = append(wide, "CrashLoop") + + } + + if events.LastErrorEvent != nil && time.Since(events.LastErrorEvent.Time.AsTime()) < t.lastEventErrorThreshold { + emojis = append(emojis, helpers.Exclamation) + wide = append(wide, "LastEventErrors") + } + + } + + if vpn != nil && vpn.Connected { + emojis = append(emojis, helpers.VPN) + wide = append(wide, "VPN") + } + + return strings.Join(emojis, nbr), strings.Join(wide, ", ") +} diff --git a/docs/metalctlv2.md b/docs/metalctlv2.md index 4671daf..a32e384 100644 --- a/docs/metalctlv2.md +++ b/docs/metalctlv2.md @@ -26,6 +26,7 @@ cli for managing entities in metal-stack * [metalctlv2 ip](metalctlv2_ip.md) - manage ip entities * [metalctlv2 login](metalctlv2_login.md) - login * [metalctlv2 logout](metalctlv2_logout.md) - logout +* [metalctlv2 machine](metalctlv2_machine.md) - manage machine entities * [metalctlv2 markdown](metalctlv2_markdown.md) - create markdown documentation * [metalctlv2 project](metalctlv2_project.md) - manage project entities * [metalctlv2 tenant](metalctlv2_tenant.md) - manage tenant entities diff --git a/docs/metalctlv2_machine.md b/docs/metalctlv2_machine.md new file mode 100644 index 0000000..cf12864 --- /dev/null +++ b/docs/metalctlv2_machine.md @@ -0,0 +1,38 @@ +## metalctlv2 machine + +manage machine entities + +### Synopsis + +an machine of metal-stack.io + +### Options + +``` + -h, --help help for machine +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2](metalctlv2.md) - cli for managing entities in metal-stack +* [metalctlv2 machine apply](metalctlv2_machine_apply.md) - applies one or more machines from a given file +* [metalctlv2 machine create](metalctlv2_machine_create.md) - creates the machine +* [metalctlv2 machine delete](metalctlv2_machine_delete.md) - deletes the machine +* [metalctlv2 machine describe](metalctlv2_machine_describe.md) - describes the machine +* [metalctlv2 machine edit](metalctlv2_machine_edit.md) - edit the machine through an editor and update +* [metalctlv2 machine list](metalctlv2_machine_list.md) - list all machines +* [metalctlv2 machine update](metalctlv2_machine_update.md) - updates the machine + diff --git a/docs/metalctlv2_machine_apply.md b/docs/metalctlv2_machine_apply.md new file mode 100644 index 0000000..040e94d --- /dev/null +++ b/docs/metalctlv2_machine_apply.md @@ -0,0 +1,46 @@ +## metalctlv2 machine apply + +applies one or more machines from a given file + +``` +metalctlv2 machine apply [flags] +``` + +### Options + +``` + --bulk-output when used with --file (bulk operation): prints results at the end as a list. default is printing results intermediately during the operation, which causes single entities to be printed in a row. + -f, --file string filename of the create or update request in yaml format, or - for stdin. + + Example: + $ metalctlv2 machine describe machine-1 -o yaml > machine.yaml + $ vi machine.yaml + $ # either via stdin + $ cat machine.yaml | metalctlv2 machine apply -f - + $ # or via file + $ metalctlv2 machine apply -f machine.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for apply + --skip-security-prompts skips security prompt for bulk operations + --timestamps when used with --file (bulk operation): prints timestamps in-between the operations +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 machine](metalctlv2_machine.md) - manage machine entities + diff --git a/docs/metalctlv2_machine_create.md b/docs/metalctlv2_machine_create.md new file mode 100644 index 0000000..8381bf4 --- /dev/null +++ b/docs/metalctlv2_machine_create.md @@ -0,0 +1,46 @@ +## metalctlv2 machine create + +creates the machine + +``` +metalctlv2 machine create [flags] +``` + +### Options + +``` + --bulk-output when used with --file (bulk operation): prints results at the end as a list. default is printing results intermediately during the operation, which causes single entities to be printed in a row. + -f, --file string filename of the create or update request in yaml format, or - for stdin. + + Example: + $ metalctlv2 machine describe machine-1 -o yaml > machine.yaml + $ vi machine.yaml + $ # either via stdin + $ cat machine.yaml | metalctlv2 machine create -f - + $ # or via file + $ metalctlv2 machine create -f machine.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for create + --skip-security-prompts skips security prompt for bulk operations + --timestamps when used with --file (bulk operation): prints timestamps in-between the operations +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 machine](metalctlv2_machine.md) - manage machine entities + diff --git a/docs/metalctlv2_machine_delete.md b/docs/metalctlv2_machine_delete.md new file mode 100644 index 0000000..b9aa286 --- /dev/null +++ b/docs/metalctlv2_machine_delete.md @@ -0,0 +1,46 @@ +## metalctlv2 machine delete + +deletes the machine + +``` +metalctlv2 machine delete [flags] +``` + +### Options + +``` + --bulk-output when used with --file (bulk operation): prints results at the end as a list. default is printing results intermediately during the operation, which causes single entities to be printed in a row. + -f, --file string filename of the create or update request in yaml format, or - for stdin. + + Example: + $ metalctlv2 machine describe machine-1 -o yaml > machine.yaml + $ vi machine.yaml + $ # either via stdin + $ cat machine.yaml | metalctlv2 machine delete -f - + $ # or via file + $ metalctlv2 machine delete -f machine.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for delete + --skip-security-prompts skips security prompt for bulk operations + --timestamps when used with --file (bulk operation): prints timestamps in-between the operations +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 machine](metalctlv2_machine.md) - manage machine entities + diff --git a/docs/metalctlv2_machine_describe.md b/docs/metalctlv2_machine_describe.md new file mode 100644 index 0000000..e590112 --- /dev/null +++ b/docs/metalctlv2_machine_describe.md @@ -0,0 +1,32 @@ +## metalctlv2 machine describe + +describes the machine + +``` +metalctlv2 machine describe [flags] +``` + +### Options + +``` + -h, --help help for describe + -p, --project string project of the machine +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 machine](metalctlv2_machine.md) - manage machine entities + diff --git a/docs/metalctlv2_machine_edit.md b/docs/metalctlv2_machine_edit.md new file mode 100644 index 0000000..08d19fe --- /dev/null +++ b/docs/metalctlv2_machine_edit.md @@ -0,0 +1,31 @@ +## metalctlv2 machine edit + +edit the machine through an editor and update + +``` +metalctlv2 machine edit [flags] +``` + +### Options + +``` + -h, --help help for edit +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 machine](metalctlv2_machine.md) - manage machine entities + diff --git a/docs/metalctlv2_machine_list.md b/docs/metalctlv2_machine_list.md new file mode 100644 index 0000000..edb4a90 --- /dev/null +++ b/docs/metalctlv2_machine_list.md @@ -0,0 +1,33 @@ +## metalctlv2 machine list + +list all machines + +``` +metalctlv2 machine list [flags] +``` + +### Options + +``` + -h, --help help for list + -p, --project string project from where machines should be listed + --sort-by strings sort by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: partition|size|uuid +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 machine](metalctlv2_machine.md) - manage machine entities + diff --git a/docs/metalctlv2_machine_update.md b/docs/metalctlv2_machine_update.md new file mode 100644 index 0000000..0d23675 --- /dev/null +++ b/docs/metalctlv2_machine_update.md @@ -0,0 +1,46 @@ +## metalctlv2 machine update + +updates the machine + +``` +metalctlv2 machine update [flags] +``` + +### Options + +``` + --bulk-output when used with --file (bulk operation): prints results at the end as a list. default is printing results intermediately during the operation, which causes single entities to be printed in a row. + -f, --file string filename of the create or update request in yaml format, or - for stdin. + + Example: + $ metalctlv2 machine describe machine-1 -o yaml > machine.yaml + $ vi machine.yaml + $ # either via stdin + $ cat machine.yaml | metalctlv2 machine update -f - + $ # or via file + $ metalctlv2 machine update -f machine.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for update + --skip-security-prompts skips security prompt for bulk operations + --timestamps when used with --file (bulk operation): prints timestamps in-between the operations +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 machine](metalctlv2_machine.md) - manage machine entities + diff --git a/pkg/helpers/emoji.go b/pkg/helpers/emoji.go new file mode 100644 index 0000000..63e9c56 --- /dev/null +++ b/pkg/helpers/emoji.go @@ -0,0 +1,27 @@ +package helpers + +const ( + Ambulance = "🚑" + Exclamation = "❗" + Bark = "🚧" + Loop = "⭕" + Lock = "🔒" + Question = "❓" + Skull = "💀" + VPN = "🛡" +) + +func EmojiHelpText() string { + return ` +Meaning of the emojis: + +🚧 Machine is reserved. Reserved machines are not considered for random allocation until the reservation flag is removed. +🔒 Machine is locked. Locked machines can not be deleted until the lock is removed. +💀 Machine is dead. The metal-api does not receive any events from this machine. +❗ Machine has a last event error. The machine has recently encountered an error during the provisioning lifecycle. +❓ Machine is in unknown condition. The metal-api does not receive phoned home events anymore or has never booted successfully. +⭕ Machine is in a provisioning crash loop. Flag can be reset through an API-triggered reboot or when the machine reaches the phoned home state. +🚑 Machine reclaim has failed. The machine was deleted but it is not going back into the available machine pool. +🛡 Machine is connected to our VPN, ssh access only possible via this VPN. +` +} From ef4e4ecf2567bcd19044110ac1bc0426d918246b Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Mon, 18 Aug 2025 07:58:06 +0200 Subject: [PATCH 2/4] add admin machine get/list --- .github/workflows/docker.yaml | 2 +- cmd/admin/v1/commands.go | 1 + cmd/admin/v1/machine.go | 105 ++++++++++++++++++++++++++++++++++ cmd/api/v1/machine.go | 3 + cmd/tableprinters/machine.go | 6 +- go.mod | 6 +- go.sum | 4 +- 7 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 cmd/admin/v1/machine.go diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index e057bae..04fc351 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -30,7 +30,7 @@ jobs: cache: false - name: Lint - uses: golangci/golangci-lint-action@v7 + uses: golangci/golangci-lint-action@v8 with: args: --build-tags integration -D protogetter --timeout=5m diff --git a/cmd/admin/v1/commands.go b/cmd/admin/v1/commands.go index ddfef8c..3a2b86b 100644 --- a/cmd/admin/v1/commands.go +++ b/cmd/admin/v1/commands.go @@ -16,6 +16,7 @@ func AddCmds(cmd *cobra.Command, c *config.Config) { adminCmd.AddCommand(newTokenCmd(c)) adminCmd.AddCommand(newImageCmd(c)) + adminCmd.AddCommand(newMachineCmd(c)) cmd.AddCommand(adminCmd) } diff --git a/cmd/admin/v1/machine.go b/cmd/admin/v1/machine.go new file mode 100644 index 0000000..9effccd --- /dev/null +++ b/cmd/admin/v1/machine.go @@ -0,0 +1,105 @@ +package v1 + +import ( + "connectrpc.com/connect" + 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/cli/cmd/sorters" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/spf13/cobra" +) + +type machine struct { + c *config.Config +} + +func newMachineCmd(c *config.Config) *cobra.Command { + w := &machine{ + c: c, + } + + cmdsConfig := &genericcli.CmdsConfig[any, any, *apiv2.Machine]{ + BinaryName: config.BinaryName, + GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), + Singular: "machine", + Plural: "machines", + Description: "an machine of metal-stack.io", + Sorter: sorters.MachineSorter(), + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().StringP("project", "p", "", "project from where machines should be listed") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.ProjectListCompletion)) + }, + DescribeCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().StringP("project", "p", "", "project of the machine") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.ProjectListCompletion)) + }, + ValidArgsFn: c.Completion.MachineListCompletion, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *machine) updateFromCLI(args []string) (any, error) { + panic("unimplemented") +} + +func (c *machine) Create(rq any) (*apiv2.Machine, error) { + panic("unimplemented") +} + +func (c *machine) Delete(id string) (*apiv2.Machine, error) { + panic("unimplemented") +} + +func (c *machine) Get(id string) (*apiv2.Machine, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Adminv2().Machine().Get(ctx, connect.NewRequest(&adminv2.MachineServiceGetRequest{ + Uuid: id, + })) + if err != nil { + return nil, err + } + + return resp.Msg.Machine, nil +} + +func (c *machine) List() ([]*apiv2.Machine, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Adminv2().Machine().List(ctx, connect.NewRequest(&adminv2.MachineServiceListRequest{ + Query: &apiv2.MachineQuery{ + // FIXME implement + }, + })) + if err != nil { + return nil, err + } + + return resp.Msg.Machines, nil +} + +func (c *machine) Update(rq any) (*apiv2.Machine, error) { + panic("unimplemented") +} + +func (c *machine) Convert(r *apiv2.Machine) (string, any, any, error) { + panic("unimplemented") + +} + +func (c *machine) MachineResponseToCreate(r *apiv2.Machine) any { + panic("unimplemented") +} + +func (c *machine) MachineResponseToUpdate(desired *apiv2.Machine) (any, error) { + panic("unimplemented") +} diff --git a/cmd/api/v1/machine.go b/cmd/api/v1/machine.go index e79e7f3..bb598ca 100644 --- a/cmd/api/v1/machine.go +++ b/cmd/api/v1/machine.go @@ -78,6 +78,9 @@ func (c *machine) List() ([]*apiv2.Machine, error) { resp, err := c.c.Client.Apiv2().Machine().List(ctx, connect.NewRequest(&apiv2.MachineServiceListRequest{ Project: c.c.GetProject(), + Query: &apiv2.MachineQuery{ + // FIXME implement + }, })) if err != nil { return nil, err diff --git a/cmd/tableprinters/machine.go b/cmd/tableprinters/machine.go index 4a1ae02..1bd4969 100644 --- a/cmd/tableprinters/machine.go +++ b/cmd/tableprinters/machine.go @@ -79,7 +79,11 @@ func (t *TablePrinter) MachineTable(data []*apiv2.Machine, wide bool) ([]string, if len(machine.RecentProvisioningEvents.Events) > 0 { since := time.Since(machine.RecentProvisioningEvents.LastEventTime.AsTime()) when = humanizeDuration(since) - lastEvent = machine.RecentProvisioningEvents.Events[0].Event + lastEventString, err := enum.GetStringValue(machine.RecentProvisioningEvents.Events[0].Event) + if err != nil { + return nil, nil, err + } + lastEvent = *lastEventString } emojis, _ := t.getMachineStatusEmojis(machine.Status.Liveliness, machine.RecentProvisioningEvents, machine.Status.Condition.State, alloc.Vpn) diff --git a/go.mod b/go.mod index 0172e84..81f6214 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/metal-stack/cli -go 1.24.0 +go 1.25 require ( bou.ke/monkey v1.0.2 @@ -8,10 +8,9 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 github.com/google/go-cmp v0.7.0 - github.com/metal-stack/api v0.0.11 + github.com/metal-stack/api v0.0.12-0.20250818052029-1d3c54d0b728 github.com/metal-stack/metal-lib v0.23.3 github.com/metal-stack/v v1.0.3 - github.com/olekukonko/tablewriter v1.0.9 github.com/spf13/afero v1.14.0 github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.7 @@ -45,6 +44,7 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/errors v1.1.0 // indirect github.com/olekukonko/ll v0.0.9 // indirect + github.com/olekukonko/tablewriter v1.0.9 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/go.sum b/go.sum index 9449017..89061d4 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/metal-stack/api v0.0.11 h1:aWMT1WTCIRm+DZFWZGUI08M/9LY2XIdtJZA5zJ/o1zo= -github.com/metal-stack/api v0.0.11/go.mod h1:GZEuAwwZXo1szv+0iLstU9sSra8gOIwSiquuRtGWxWo= +github.com/metal-stack/api v0.0.12-0.20250818052029-1d3c54d0b728 h1:sxpFilvByXHRYOm0ho/3o03f5idi/FN3zHY2/PoPQWE= +github.com/metal-stack/api v0.0.12-0.20250818052029-1d3c54d0b728/go.mod h1:uWv5i8YyjETpq8ioewHI/fFmvWxW8yrzC1e2q3rKbHo= github.com/metal-stack/metal-lib v0.23.3 h1:ZcouyuCXy2bMZn+CB0yx1JdAl8SEkfjmlzNLyrmD/kA= github.com/metal-stack/metal-lib v0.23.3/go.mod h1:29JMehA4KSeDovG9y1xgQkdc5pAk2/MBvOufRNSk5Bc= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= From 066542bda4c3703b10655e93be554010421b2823 Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Tue, 19 Aug 2025 07:50:42 +0200 Subject: [PATCH 3/4] Update api --- cmd/admin/v1/token.go | 6 +++--- cmd/api/v1/project.go | 10 +++++----- cmd/api/v1/tenant.go | 10 +++++----- cmd/sorters/token.go | 2 +- cmd/tableprinters/machine.go | 5 +++-- cmd/tableprinters/token.go | 2 +- go.mod | 4 ++-- go.sum | 8 ++++---- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/cmd/admin/v1/token.go b/cmd/admin/v1/token.go index 4c7291c..8464989 100644 --- a/cmd/admin/v1/token.go +++ b/cmd/admin/v1/token.go @@ -58,7 +58,7 @@ func (c *token) List() ([]*apiv2.Token, error) { req := &adminv2.TokenServiceListRequest{} if viper.IsSet("user") { - req.UserId = pointer.Pointer(viper.GetString("user")) + req.User = pointer.Pointer(viper.GetString("user")) } resp, err := c.c.Client.Adminv2().Token().List(ctx, connect.NewRequest(req)) @@ -82,8 +82,8 @@ func (c *token) Delete(id string) (*apiv2.Token, error) { } req := &adminv2.TokenServiceRevokeRequest{ - Uuid: id, - UserId: viper.GetString("user"), + Uuid: id, + User: viper.GetString("user"), } _, err := c.c.Client.Adminv2().Token().Revoke(ctx, connect.NewRequest(req)) diff --git a/cmd/api/v1/project.go b/cmd/api/v1/project.go index a4fc714..1087c38 100644 --- a/cmd/api/v1/project.go +++ b/cmd/api/v1/project.go @@ -402,8 +402,8 @@ func (c *project) removeMember(args []string) error { defer cancel() _, err = c.c.Client.Apiv2().Project().RemoveMember(ctx, connect.NewRequest(&apiv2.ProjectServiceRemoveMemberRequest{ - Project: c.c.GetProject(), - MemberId: member, + Project: c.c.GetProject(), + Member: member, })) if err != nil { return fmt.Errorf("failed to remove member from project: %w", err) @@ -424,9 +424,9 @@ func (c *project) updateMember(args []string) error { defer cancel() resp, err := c.c.Client.Apiv2().Project().UpdateMember(ctx, connect.NewRequest(&apiv2.ProjectServiceUpdateMemberRequest{ - Project: c.c.GetProject(), - MemberId: member, - Role: apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")]), + Project: c.c.GetProject(), + Member: member, + Role: apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")]), })) if err != nil { return fmt.Errorf("failed to update member: %w", err) diff --git a/cmd/api/v1/tenant.go b/cmd/api/v1/tenant.go index d7bc015..3db8675 100644 --- a/cmd/api/v1/tenant.go +++ b/cmd/api/v1/tenant.go @@ -398,8 +398,8 @@ func (c *tenant) removeMember(args []string) error { defer cancel() _, err = c.c.Client.Apiv2().Tenant().RemoveMember(ctx, connect.NewRequest(&apiv2.TenantServiceRemoveMemberRequest{ - Login: tenant, - MemberId: member, + Login: tenant, + Member: member, })) if err != nil { return fmt.Errorf("failed to remove member from tenant: %w", err) @@ -425,9 +425,9 @@ func (c *tenant) updateMember(args []string) error { defer cancel() resp, err := c.c.Client.Apiv2().Tenant().UpdateMember(ctx, connect.NewRequest(&apiv2.TenantServiceUpdateMemberRequest{ - Login: tenant, - MemberId: member, - Role: apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")]), + Login: tenant, + Member: member, + Role: apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")]), })) if err != nil { return fmt.Errorf("failed to update member: %w", err) diff --git a/cmd/sorters/token.go b/cmd/sorters/token.go index 86e1243..27cc738 100644 --- a/cmd/sorters/token.go +++ b/cmd/sorters/token.go @@ -11,7 +11,7 @@ func TokenSorter() *multisort.Sorter[*apiv2.Token] { return multisort.Compare(a.Uuid, b.Uuid, descending) }, "user": func(a, b *apiv2.Token, descending bool) multisort.CompareResult { - return multisort.Compare(a.UserId, b.UserId, descending) + return multisort.Compare(a.User, b.User, descending) }, "type": func(a, b *apiv2.Token, descending bool) multisort.CompareResult { return multisort.Compare(a.TokenType.String(), b.TokenType.String(), descending) diff --git a/cmd/tableprinters/machine.go b/cmd/tableprinters/machine.go index 1bd4969..569cbf1 100644 --- a/cmd/tableprinters/machine.go +++ b/cmd/tableprinters/machine.go @@ -38,7 +38,7 @@ func (t *TablePrinter) MachineTable(data []*apiv2.Machine, wide bool) ([]string, name := alloc.Name desc := alloc.Description hostname := alloc.Hostname - image := pointer.SafeDeref(alloc.Image.Name) + image := pointer.SafeDeref(pointer.SafeDeref(alloc.Image).Name) rack := machine.Rack @@ -52,7 +52,8 @@ func (t *TablePrinter) MachineTable(data []*apiv2.Machine, wide bool) ([]string, started := "" age := "" - if alloc.Meta.CreatedAt != nil && !alloc.Meta.CreatedAt.AsTime().IsZero() { + + if alloc.Meta != nil && alloc.Meta.CreatedAt != nil && !alloc.Meta.CreatedAt.AsTime().IsZero() { started = alloc.Meta.CreatedAt.AsTime().Format(time.RFC3339) age = humanizeDuration(time.Since(alloc.Meta.CreatedAt.AsTime())) } diff --git a/cmd/tableprinters/token.go b/cmd/tableprinters/token.go index c5a4712..3f29f26 100644 --- a/cmd/tableprinters/token.go +++ b/cmd/tableprinters/token.go @@ -27,7 +27,7 @@ func (t *TablePrinter) TokenTable(data []*apiv2.Token, _ bool) ([]string, [][]st token.TokenType.String(), token.Uuid, admin, - token.UserId, + token.User, token.Description, strconv.Itoa(len(token.TenantRoles) + len(token.ProjectRoles)), strconv.Itoa(len(token.Permissions)), diff --git a/go.mod b/go.mod index 81f6214..3cad178 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 github.com/google/go-cmp v0.7.0 - github.com/metal-stack/api v0.0.12-0.20250818052029-1d3c54d0b728 + github.com/metal-stack/api v0.0.13 github.com/metal-stack/metal-lib v0.23.3 github.com/metal-stack/v v1.0.3 github.com/spf13/afero v1.14.0 @@ -60,6 +60,6 @@ require ( golang.org/x/text v0.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apimachinery v0.33.3 // indirect + k8s.io/apimachinery v0.33.4 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect ) diff --git a/go.sum b/go.sum index 89061d4..1a47519 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/metal-stack/api v0.0.12-0.20250818052029-1d3c54d0b728 h1:sxpFilvByXHRYOm0ho/3o03f5idi/FN3zHY2/PoPQWE= -github.com/metal-stack/api v0.0.12-0.20250818052029-1d3c54d0b728/go.mod h1:uWv5i8YyjETpq8ioewHI/fFmvWxW8yrzC1e2q3rKbHo= +github.com/metal-stack/api v0.0.13 h1:52vnhBmfKJ3JUZDP70oTwk8EhXGYpwJh3EUBgaN4Nn8= +github.com/metal-stack/api v0.0.13/go.mod h1:uWv5i8YyjETpq8ioewHI/fFmvWxW8yrzC1e2q3rKbHo= github.com/metal-stack/metal-lib v0.23.3 h1:ZcouyuCXy2bMZn+CB0yx1JdAl8SEkfjmlzNLyrmD/kA= github.com/metal-stack/metal-lib v0.23.3/go.mod h1:29JMehA4KSeDovG9y1xgQkdc5pAk2/MBvOufRNSk5Bc= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= @@ -123,8 +123,8 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= -k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s= +k8s.io/apimachinery v0.33.4/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= From 1bcacd3e50d2285f6ef1696ec61d7add899173b4 Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Mon, 8 Sep 2025 15:39:13 +0200 Subject: [PATCH 4/4] Update deps --- cmd/api/v1/tenant.go | 3 +-- go.mod | 23 ++++++++++---------- go.sum | 52 +++++++++++++++++++++++--------------------- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/cmd/api/v1/tenant.go b/cmd/api/v1/tenant.go index 3db8675..8893597 100644 --- a/cmd/api/v1/tenant.go +++ b/cmd/api/v1/tenant.go @@ -257,8 +257,7 @@ func (c *tenant) createRequestFromCLI() (*apiv2.TenantServiceCreateRequest, erro Name: viper.GetString("name"), Description: pointer.PointerOrNil(viper.GetString("description")), Email: pointer.PointerOrNil(viper.GetString("email")), - AvatarUrl: pointer.PointerOrNil(viper.GetString("phone")), - PhoneNumber: pointer.PointerOrNil(viper.GetString("avatar-url")), + AvatarUrl: pointer.PointerOrNil(viper.GetString("avatar-url")), }, nil } diff --git a/go.mod b/go.mod index 3cad178..df66d30 100644 --- a/go.mod +++ b/go.mod @@ -8,21 +8,21 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 github.com/google/go-cmp v0.7.0 - github.com/metal-stack/api v0.0.13 - github.com/metal-stack/metal-lib v0.23.3 + github.com/metal-stack/api v0.0.18 + github.com/metal-stack/metal-lib v0.23.4 github.com/metal-stack/v v1.0.3 github.com/spf13/afero v1.14.0 - github.com/spf13/cobra v1.9.1 - github.com/spf13/pflag v1.0.7 + github.com/spf13/cobra v1.10.1 + github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.20.1 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 golang.org/x/net v0.43.0 - google.golang.org/protobuf v1.36.7 + google.golang.org/protobuf v1.36.8 sigs.k8s.io/yaml v1.6.0 ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.7-20250717185734-6c6e0d3c608e.1 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.8-20250717185734-6c6e0d3c608e.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -42,8 +42,9 @@ require ( github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/olekukonko/cat v0.0.0-20250808191157-46fba99501f3 // indirect github.com/olekukonko/errors v1.1.0 // indirect - github.com/olekukonko/ll v0.0.9 // indirect + github.com/olekukonko/ll v0.1.1 // indirect github.com/olekukonko/tablewriter v1.0.9 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -56,10 +57,10 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect go.mongodb.org/mongo-driver v1.17.4 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apimachinery v0.33.4 // indirect + k8s.io/apimachinery v0.34.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect ) diff --git a/go.sum b/go.sum index 1a47519..165a999 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ bou.ke/monkey v1.0.2 h1:kWcnsrCNUatbxncxR/ThdYqbytgOIArtYWqcQLQzKLI= bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.7-20250717185734-6c6e0d3c608e.1 h1:/AZH8sVB6LHv8G+hZlAMCP31NevnesHwYgnlgS5Vt14= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.7-20250717185734-6c6e0d3c608e.1/go.mod h1:eva/VCrd8X7xuJw+JtwCEyrCKiRRASukFqmirnWBvFU= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.8-20250717185734-6c6e0d3c608e.1 h1:sjY1k5uszbIZfv11HO2keV4SLhNA47SabPO886v7Rvo= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.8-20250717185734-6c6e0d3c608e.1/go.mod h1:8EQ5GzyGJQ5tEIwMSxCl8RKJYsjCpAwkdcENoioXT6g= connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= @@ -53,20 +53,22 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/metal-stack/api v0.0.13 h1:52vnhBmfKJ3JUZDP70oTwk8EhXGYpwJh3EUBgaN4Nn8= -github.com/metal-stack/api v0.0.13/go.mod h1:uWv5i8YyjETpq8ioewHI/fFmvWxW8yrzC1e2q3rKbHo= -github.com/metal-stack/metal-lib v0.23.3 h1:ZcouyuCXy2bMZn+CB0yx1JdAl8SEkfjmlzNLyrmD/kA= -github.com/metal-stack/metal-lib v0.23.3/go.mod h1:29JMehA4KSeDovG9y1xgQkdc5pAk2/MBvOufRNSk5Bc= +github.com/metal-stack/api v0.0.18 h1:zOaU7Q0PJgvpgWZtWtQHLHnZQ6GRb70t8IprbFSVMLE= +github.com/metal-stack/api v0.0.18/go.mod h1:z3DWiuc85eAINClNVp7/4uBbhClYDdSLihynXPzEiBw= +github.com/metal-stack/metal-lib v0.23.4 h1:/xvCpgfvkGM8kdF0C3+mP7WGl0wiwU33MKG2UGIDXA8= +github.com/metal-stack/metal-lib v0.23.4/go.mod h1:Q0GXBtKJ5ycqtwGYtWI7NFKdGo0+q7TLGnstgfUwBCQ= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= github.com/metal-stack/v v1.0.3/go.mod h1:YTahEu7/ishwpYKnp/VaW/7nf8+PInogkfGwLcGPdXg= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/cat v0.0.0-20250808191157-46fba99501f3 h1:3o1yj5hu9LOTwjey1RE0IBaB4u1HCLbD+lDPhJrl8Y4= +github.com/olekukonko/cat v0.0.0-20250808191157-46fba99501f3/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= -github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI= -github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g= +github.com/olekukonko/ll v0.1.1 h1:9Dfeed5/Mgaxb9lHRAftLK9pVfYETvHn+If6lywVhJc= +github.com/olekukonko/ll v0.1.1/go.mod h1:2dJo+hYZcJMLMbKwHEWvxCUbAOLc/CXWS9noET22Mdo= github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8= github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= @@ -88,34 +90,34 @@ github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= -go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= -go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= -google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -123,8 +125,8 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s= -k8s.io/apimachinery v0.33.4/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0= +k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=