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..0a6dc56 --- /dev/null +++ b/cmd/admin/v1/machine.go @@ -0,0 +1,104 @@ +package v1 + +import ( + 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, &adminv2.MachineServiceGetRequest{ + Uuid: id, + }) + if err != nil { + return nil, err + } + + return resp.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, &adminv2.MachineServiceListRequest{ + Query: &apiv2.MachineQuery{ + // FIXME implement + }, + }) + if err != nil { + return nil, err + } + + return resp.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/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..5a98e7a --- /dev/null +++ b/cmd/api/v1/machine.go @@ -0,0 +1,108 @@ +package v1 + +import ( + 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, &apiv2.MachineServiceGetRequest{ + Project: c.c.GetProject(), + Uuid: id, + }) + if err != nil { + return nil, err + } + + return resp.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, &apiv2.MachineServiceListRequest{ + Project: c.c.GetProject(), + Query: &apiv2.MachineQuery{ + // FIXME implement + }, + }) + if err != nil { + return nil, err + } + + return resp.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..cf5f1e9 --- /dev/null +++ b/cmd/completion/machine.go @@ -0,0 +1,21 @@ +package completion + +import ( + 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, req) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + var names []string + for _, s := range resp.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..569cbf1 --- /dev/null +++ b/cmd/tableprinters/machine.go @@ -0,0 +1,165 @@ +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(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 != 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())) + } + 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) + 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) + + 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/go.mod b/go.mod index 1b2f7a0..621c30a 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.25 require ( bou.ke/monkey v1.0.2 - connectrpc.com/connect v1.19.1 github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 github.com/google/go-cmp v0.7.0 @@ -23,6 +22,7 @@ require ( require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20250912141014-52f32327d4b0.1 // indirect + connectrpc.com/connect v1.19.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect 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. +` +}