From fc517b820704c3da52150c6922fc7ba1a2293061 Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Fri, 31 Jul 2026 16:52:28 +0200 Subject: [PATCH 1/5] Make completion work, add network completion for ip create --- cmd/api/v2/ip.go | 1 + cmd/completion/completion.go | 11 ++--------- cmd/completion/ip.go | 4 ++-- cmd/completion/network.go | 10 +++++----- cmd/completion/partition.go | 2 +- cmd/completion/project.go | 10 +++++----- cmd/completion/size.go | 2 +- cmd/completion/switch.go | 12 ++++++------ cmd/completion/tenant.go | 16 ++++++++-------- cmd/completion/token.go | 10 +++++----- cmd/root.go | 12 +++++++----- 11 files changed, 43 insertions(+), 47 deletions(-) diff --git a/cmd/api/v2/ip.go b/cmd/api/v2/ip.go index 866a7a6..e092907 100644 --- a/cmd/api/v2/ip.go +++ b/cmd/api/v2/ip.go @@ -46,6 +46,7 @@ func newIPCmd(c *config.Config) *cobra.Command { cmd.Flags().StringP("addressfamily", "", "", "addressfamily, can be either IPv4|IPv6, defaults to IPv4 (optional)") genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("network", c.Completion.Network)) }, UpdateCmdMutateFn: func(cmd *cobra.Command) { cmd.Flags().StringP("project", "p", "", "project of the ip") diff --git a/cmd/completion/completion.go b/cmd/completion/completion.go index 90d554d..aca23c9 100644 --- a/cmd/completion/completion.go +++ b/cmd/completion/completion.go @@ -6,15 +6,8 @@ import ( ) type Completion struct { - client client.Client - project string -} - -func New(c client.Client, project string) *Completion { - return &Completion{ - client: c, - project: project, - } + Client client.Client + Proj string } func OutputFormat(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/cmd/completion/ip.go b/cmd/completion/ip.go index c8f4b24..509f4a5 100644 --- a/cmd/completion/ip.go +++ b/cmd/completion/ip.go @@ -7,9 +7,9 @@ import ( func (c *Completion) Ip(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { req := &apiv2.IPServiceListRequest{ - Project: c.project, + Project: c.Proj, } - resp, err := c.client.Apiv2().IP().List(cmd.Context(), req) + resp, err := c.Client.Apiv2().IP().List(cmd.Context(), req) if err != nil { return nil, cobra.ShellCompDirectiveError } diff --git a/cmd/completion/network.go b/cmd/completion/network.go index 91feaee..c302495 100644 --- a/cmd/completion/network.go +++ b/cmd/completion/network.go @@ -9,15 +9,15 @@ import ( ) func (c *Completion) Network(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - ownNetworks, err := c.client.Apiv2().Network().List(cmd.Context(), &apiv2.NetworkServiceListRequest{ - Project: c.project, + ownNetworks, err := c.Client.Apiv2().Network().List(cmd.Context(), &apiv2.NetworkServiceListRequest{ + Project: c.Proj, }) if err != nil { return nil, cobra.ShellCompDirectiveError } - baseNetworks, err := c.client.Apiv2().Network().ListBaseNetworks(cmd.Context(), &apiv2.NetworkServiceListBaseNetworksRequest{ - Project: c.project, + baseNetworks, err := c.Client.Apiv2().Network().ListBaseNetworks(cmd.Context(), &apiv2.NetworkServiceListBaseNetworksRequest{ + Project: c.Proj, }) if err != nil { return nil, cobra.ShellCompDirectiveError @@ -71,7 +71,7 @@ func (c *Completion) NetworkAddressFamily(cmd *cobra.Command, args []string, toC } func (c *Completion) NetworkAdmin(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - networks, err := c.client.Adminv2().Network().List(cmd.Context(), &adminv2.NetworkServiceListRequest{}) + networks, err := c.Client.Adminv2().Network().List(cmd.Context(), &adminv2.NetworkServiceListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } diff --git a/cmd/completion/partition.go b/cmd/completion/partition.go index af22118..c1b3082 100644 --- a/cmd/completion/partition.go +++ b/cmd/completion/partition.go @@ -7,7 +7,7 @@ import ( func (c *Completion) Partition(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { req := &apiv2.PartitionServiceListRequest{} - resp, err := c.client.Apiv2().Partition().List(cmd.Context(), req) + resp, err := c.Client.Apiv2().Partition().List(cmd.Context(), req) if err != nil { return nil, cobra.ShellCompDirectiveError } diff --git a/cmd/completion/project.go b/cmd/completion/project.go index f5cf734..c4cc89a 100644 --- a/cmd/completion/project.go +++ b/cmd/completion/project.go @@ -8,7 +8,7 @@ import ( func (c *Completion) Project(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { req := &apiv2.ProjectServiceListRequest{} - resp, err := c.client.Apiv2().Project().List(cmd.Context(), req) + resp, err := c.Client.Apiv2().Project().List(cmd.Context(), req) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -36,8 +36,8 @@ func (c *Completion) ProjectRole(cmd *cobra.Command, args []string, toComplete s } func (c *Completion) ProjectInvite(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - resp, err := c.client.Apiv2().Project().InvitesList(cmd.Context(), &apiv2.ProjectServiceInvitesListRequest{ - Project: c.project, + resp, err := c.Client.Apiv2().Project().InvitesList(cmd.Context(), &apiv2.ProjectServiceInvitesListRequest{ + Project: c.Proj, }) if err != nil { return nil, cobra.ShellCompDirectiveError @@ -53,8 +53,8 @@ func (c *Completion) ProjectInvite(cmd *cobra.Command, args []string, toComplete } func (c *Completion) ProjectMember(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - resp, err := c.client.Apiv2().Project().Get(cmd.Context(), &apiv2.ProjectServiceGetRequest{ - Project: c.project, + resp, err := c.Client.Apiv2().Project().Get(cmd.Context(), &apiv2.ProjectServiceGetRequest{ + Project: c.Proj, }) if err != nil { return nil, cobra.ShellCompDirectiveError diff --git a/cmd/completion/size.go b/cmd/completion/size.go index 204f97f..b9b84cb 100644 --- a/cmd/completion/size.go +++ b/cmd/completion/size.go @@ -7,7 +7,7 @@ import ( func (c *Completion) Size(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { req := &apiv2.SizeServiceListRequest{} - resp, err := c.client.Apiv2().Size().List(cmd.Context(), req) + resp, err := c.Client.Apiv2().Size().List(cmd.Context(), req) if err != nil { return nil, cobra.ShellCompDirectiveError } diff --git a/cmd/completion/switch.go b/cmd/completion/switch.go index 9889946..a1e33b4 100644 --- a/cmd/completion/switch.go +++ b/cmd/completion/switch.go @@ -7,7 +7,7 @@ import ( ) func (c *Completion) Switch(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - resp, err := c.client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) + resp, err := c.Client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -21,7 +21,7 @@ func (c *Completion) Switch(cmd *cobra.Command, args []string, toComplete string } func (c *Completion) SwitchPartition(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - resp, err := c.client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) + resp, err := c.Client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -35,7 +35,7 @@ func (c *Completion) SwitchPartition(cmd *cobra.Command, args []string, toComple } func (c *Completion) SwitchRack(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - resp, err := c.client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) + resp, err := c.Client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -49,7 +49,7 @@ func (c *Completion) SwitchRack(cmd *cobra.Command, args []string, toComplete st } func (c *Completion) SwitchOSVendor(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - resp, err := c.client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) + resp, err := c.Client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -63,7 +63,7 @@ func (c *Completion) SwitchOSVendor(cmd *cobra.Command, args []string, toComplet } func (c *Completion) SwitchOSVersion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - resp, err := c.client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) + resp, err := c.Client.Adminv2().Switch().List(cmd.Context(), &adminv2.SwitchServiceListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -82,7 +82,7 @@ func (c *Completion) SwitchPorts(cmd *cobra.Command, args []string, toComplete s return nil, cobra.ShellCompDirectiveNoFileComp } - resp, err := c.client.Adminv2().Switch().Get(cmd.Context(), &adminv2.SwitchServiceGetRequest{ + resp, err := c.Client.Adminv2().Switch().Get(cmd.Context(), &adminv2.SwitchServiceGetRequest{ Id: args[0], }) if err != nil { diff --git a/cmd/completion/tenant.go b/cmd/completion/tenant.go index e6e8d13..e99e735 100644 --- a/cmd/completion/tenant.go +++ b/cmd/completion/tenant.go @@ -8,7 +8,7 @@ import ( func (c *Completion) Tenant(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { req := &apiv2.TenantServiceListRequest{} - resp, err := c.client.Apiv2().Tenant().List(cmd.Context(), req) + resp, err := c.Client.Apiv2().Tenant().List(cmd.Context(), req) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -35,14 +35,14 @@ func (c *Completion) TenantRole(cmd *cobra.Command, args []string, toComplete st } func (c *Completion) TenantInvite(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - projectResp, err := c.client.Apiv2().Project().Get(cmd.Context(), &apiv2.ProjectServiceGetRequest{ - Project: c.project, + projectResp, err := c.Client.Apiv2().Project().Get(cmd.Context(), &apiv2.ProjectServiceGetRequest{ + Project: c.Proj, }) if err != nil { return nil, cobra.ShellCompDirectiveError } - resp, err := c.client.Apiv2().Tenant().InvitesList(cmd.Context(), &apiv2.TenantServiceInvitesListRequest{ + resp, err := c.Client.Apiv2().Tenant().InvitesList(cmd.Context(), &apiv2.TenantServiceInvitesListRequest{ Login: projectResp.Project.Tenant, }) if err != nil { @@ -59,14 +59,14 @@ func (c *Completion) TenantInvite(cmd *cobra.Command, args []string, toComplete } func (c *Completion) TenantMember(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - projectResp, err := c.client.Apiv2().Project().Get(cmd.Context(), &apiv2.ProjectServiceGetRequest{ - Project: c.project, + projectResp, err := c.Client.Apiv2().Project().Get(cmd.Context(), &apiv2.ProjectServiceGetRequest{ + Project: c.Proj, }) if err != nil { return nil, cobra.ShellCompDirectiveError } - resp, err := c.client.Apiv2().Tenant().Get(cmd.Context(), &apiv2.TenantServiceGetRequest{ + resp, err := c.Client.Apiv2().Tenant().Get(cmd.Context(), &apiv2.TenantServiceGetRequest{ Login: projectResp.Project.Tenant, }) if err != nil { @@ -84,7 +84,7 @@ func (c *Completion) TenantMember(cmd *cobra.Command, args []string, toComplete func (c *Completion) AdminTenant(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { req := &adminv2.TenantServiceListRequest{} - resp, err := c.client.Adminv2().Tenant().List(cmd.Context(), req) + resp, err := c.Client.Adminv2().Tenant().List(cmd.Context(), req) if err != nil { return nil, cobra.ShellCompDirectiveError } diff --git a/cmd/completion/token.go b/cmd/completion/token.go index 9aa49b3..623d170 100644 --- a/cmd/completion/token.go +++ b/cmd/completion/token.go @@ -10,7 +10,7 @@ import ( func (c *Completion) Token(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { req := &apiv2.TokenServiceListRequest{} - resp, err := c.client.Apiv2().Token().List(cmd.Context(), req) + resp, err := c.Client.Apiv2().Token().List(cmd.Context(), req) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -24,7 +24,7 @@ func (c *Completion) Token(cmd *cobra.Command, args []string, toComplete string) } func (c *Completion) TokenProjectRoles(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - methods, err := c.client.Apiv2().Method().TokenScopedList(cmd.Context(), &apiv2.MethodServiceTokenScopedListRequest{}) + methods, err := c.Client.Apiv2().Method().TokenScopedList(cmd.Context(), &apiv2.MethodServiceTokenScopedListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -42,7 +42,7 @@ func (c *Completion) TokenProjectRoles(cmd *cobra.Command, args []string, toComp } func (c *Completion) TokenTenantRoles(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - methods, err := c.client.Apiv2().Method().TokenScopedList(cmd.Context(), &apiv2.MethodServiceTokenScopedListRequest{}) + methods, err := c.Client.Apiv2().Method().TokenScopedList(cmd.Context(), &apiv2.MethodServiceTokenScopedListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -60,7 +60,7 @@ func (c *Completion) TokenTenantRoles(cmd *cobra.Command, args []string, toCompl } func (c *Completion) TokenMachineRoles(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - methods, err := c.client.Apiv2().Method().TokenScopedList(cmd.Context(), &apiv2.MethodServiceTokenScopedListRequest{}) + methods, err := c.Client.Apiv2().Method().TokenScopedList(cmd.Context(), &apiv2.MethodServiceTokenScopedListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } @@ -104,7 +104,7 @@ func (c *Completion) TokenInfraRole(cmd *cobra.Command, args []string, toComplet } func (c *Completion) TokenPermissions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - methods, err := c.client.Apiv2().Method().List(cmd.Context(), &apiv2.MethodServiceListRequest{}) + methods, err := c.Client.Apiv2().Method().List(cmd.Context(), &apiv2.MethodServiceListRequest{}) if err != nil { return nil, cobra.ShellCompDirectiveError } diff --git a/cmd/root.go b/cmd/root.go index 8041aae..ae6ea2d 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,10 +20,11 @@ import ( func Execute() { cfg := &config.Config{ - Fs: afero.NewOsFs(), - Out: os.Stdout, - PromptOut: os.Stdout, - In: os.Stdin, + Fs: afero.NewOsFs(), + Out: os.Stdout, + PromptOut: os.Stdout, + In: os.Stdin, + Completion: &completion.Completion{}, } cmd := NewRootCmd(cfg) @@ -127,7 +128,8 @@ func initConfigWithViperCtx(c *config.Config) error { } c.Client = mc - c.Completion = completion.New(mc, c.GetProject()) + c.Completion.Client = mc + c.Completion.Proj = c.GetProject() return nil } From f8a8d616d8dc442add431d00e0f8db3f9cbc071d Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Fri, 31 Jul 2026 17:12:41 +0200 Subject: [PATCH 2/5] More completions --- cmd/api/v2/ip.go | 1 + cmd/completion/ip.go | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/cmd/api/v2/ip.go b/cmd/api/v2/ip.go index e092907..66c8f84 100644 --- a/cmd/api/v2/ip.go +++ b/cmd/api/v2/ip.go @@ -47,6 +47,7 @@ func newIPCmd(c *config.Config) *cobra.Command { genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) genericcli.Must(cmd.RegisterFlagCompletionFunc("network", c.Completion.Network)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("addressfamily", c.Completion.AddressFamily)) }, UpdateCmdMutateFn: func(cmd *cobra.Command) { cmd.Flags().StringP("project", "p", "", "project of the ip") diff --git a/cmd/completion/ip.go b/cmd/completion/ip.go index 509f4a5..3ff3aa0 100644 --- a/cmd/completion/ip.go +++ b/cmd/completion/ip.go @@ -1,6 +1,7 @@ package completion import ( + "github.com/metal-stack/api/go/enum" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" "github.com/spf13/cobra" ) @@ -19,3 +20,17 @@ func (c *Completion) Ip(cmd *cobra.Command, args []string, toComplete string) ([ } return names, cobra.ShellCompDirectiveNoFileComp } +func (c *Completion) AddressFamily(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var afs []string + for _, af := range []apiv2.IPAddressFamily{ + apiv2.IPAddressFamily_IP_ADDRESS_FAMILY_V4, + apiv2.IPAddressFamily_IP_ADDRESS_FAMILY_V6} { + stringValue, err := enum.GetStringValue(af) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + afs = append(afs, *stringValue) + } + + return afs, cobra.ShellCompDirectiveNoFileComp +} From ebf3c5b1f0d4727dc7c3ba0c88f9890a342c4dae Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Mon, 3 Aug 2026 11:43:20 +0200 Subject: [PATCH 3/5] Fixes --- cmd/api/v2/ip.go | 15 +-------------- testing/e2e/test_cmd.go | 14 ++++++++------ 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/cmd/api/v2/ip.go b/cmd/api/v2/ip.go index 66c8f84..afe2341 100644 --- a/cmd/api/v2/ip.go +++ b/cmd/api/v2/ip.go @@ -90,7 +90,7 @@ func (c *ip) createFromCLI() (*apiv2.IPServiceCreateRequest, error) { Description: pointer.PointerOrNil(viper.GetString("description")), Labels: labels, Type: new(ipStaticToType(viper.GetBool("static"))), - AddressFamily: addressFamilyToType(viper.GetString("addressfamily")), + AddressFamily: helpers.IPAddressFamilyToType(viper.GetString("addressfamily")), }, nil } @@ -259,16 +259,3 @@ func ipStaticToType(b bool) apiv2.IPType { } return apiv2.IPType_IP_TYPE_EPHEMERAL } - -func addressFamilyToType(af string) *apiv2.IPAddressFamily { - switch af { - case "": - return nil - case "ipv4", "IPv4": - return apiv2.IPAddressFamily_IP_ADDRESS_FAMILY_V4.Enum() - case "ipv6", "IPv6": - return apiv2.IPAddressFamily_IP_ADDRESS_FAMILY_V6.Enum() - default: - return apiv2.IPAddressFamily_IP_ADDRESS_FAMILY_UNSPECIFIED.Enum() - } -} diff --git a/testing/e2e/test_cmd.go b/testing/e2e/test_cmd.go index 405464f..ba36594 100644 --- a/testing/e2e/test_cmd.go +++ b/testing/e2e/test_cmd.go @@ -57,12 +57,14 @@ func NewRootCmd(t *testing.T, c *TestConfig) e2e_test.NewRootCmdFunc { viper.Reset() return cmd.NewRootCmd(&config.Config{ - Fs: fs, - Out: &out, - In: in, - PromptOut: io.Discard, - Completion: completion.New(cl, ""), - Client: cl, + Fs: fs, + Out: &out, + In: in, + PromptOut: io.Discard, + Completion: &completion.Completion{ + Client: cl, + }, + Client: cl, }), &out } } From 035b6d4e5f87fdbe97ff38ba0454ac42c68191c6 Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Mon, 3 Aug 2026 15:46:52 +0200 Subject: [PATCH 4/5] add admin ip command --- cmd/admin/v2/commands.go | 1 + cmd/admin/v2/ip.go | 101 +++++++++++++++++++++ cmd/completion/ip.go | 14 +++ cmd/completion/machine.go | 25 +++++ docs/admin/metalctlv2_admin.md | 1 + docs/admin/metalctlv2_admin_ip.md | 38 ++++++++ docs/admin/metalctlv2_admin_ip_apply.md | 46 ++++++++++ docs/admin/metalctlv2_admin_ip_create.md | 46 ++++++++++ docs/admin/metalctlv2_admin_ip_delete.md | 46 ++++++++++ docs/admin/metalctlv2_admin_ip_describe.md | 31 +++++++ docs/admin/metalctlv2_admin_ip_edit.md | 31 +++++++ docs/admin/metalctlv2_admin_ip_list.md | 40 ++++++++ docs/admin/metalctlv2_admin_ip_update.md | 46 ++++++++++ pkg/helpers/network.go | 9 ++ 14 files changed, 475 insertions(+) create mode 100644 cmd/admin/v2/ip.go create mode 100644 cmd/completion/machine.go create mode 100644 docs/admin/metalctlv2_admin_ip.md create mode 100644 docs/admin/metalctlv2_admin_ip_apply.md create mode 100644 docs/admin/metalctlv2_admin_ip_create.md create mode 100644 docs/admin/metalctlv2_admin_ip_delete.md create mode 100644 docs/admin/metalctlv2_admin_ip_describe.md create mode 100644 docs/admin/metalctlv2_admin_ip_edit.md create mode 100644 docs/admin/metalctlv2_admin_ip_list.md create mode 100644 docs/admin/metalctlv2_admin_ip_update.md diff --git a/cmd/admin/v2/commands.go b/cmd/admin/v2/commands.go index 38a1947..3aad5c0 100644 --- a/cmd/admin/v2/commands.go +++ b/cmd/admin/v2/commands.go @@ -17,6 +17,7 @@ func AddCmds(cmd *cobra.Command, c *config.Config) { adminCmd.AddCommand(newAuditCmd(c)) adminCmd.AddCommand(newComponentCmd(c)) adminCmd.AddCommand(newImageCmd(c)) + adminCmd.AddCommand(newIPCmd(c)) adminCmd.AddCommand(newNetworkCmd(c)) adminCmd.AddCommand(newPartitionCmd(c)) adminCmd.AddCommand(newProjectCmd(c)) diff --git a/cmd/admin/v2/ip.go b/cmd/admin/v2/ip.go new file mode 100644 index 0000000..fb70b8c --- /dev/null +++ b/cmd/admin/v2/ip.go @@ -0,0 +1,101 @@ +package v2 + +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/cli/pkg/helpers" + "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 ip struct { + c *config.Config +} + +func newIPCmd(c *config.Config) *cobra.Command { + w := &ip{ + c: c, + } + + cmdsConfig := &genericcli.CmdsConfig[any, any, *apiv2.IP]{ + BinaryName: config.BinaryName, + GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), + Singular: "ip", + Plural: "ips", + Description: "an ip address of metal-stack.io", + Sorter: sorters.IPSorter(), + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("ip", "", "ip which should be listed") + cmd.Flags().String("uuid", "", "allocation uuid of ip which should be listed") + cmd.Flags().String("project", "", "project from where ips should be listed") + cmd.Flags().String("name", "", "name from ips which should be listed") + cmd.Flags().String("network", "", "network from where ips should be listed") + cmd.Flags().String("machine", "", "machine where ips are attached to") + cmd.Flags().String("addressfamily", "", "addressfamily of ips which should be listed") + cmd.Flags().String("type", "", "type of ips which should be listed") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("network", c.Completion.Network)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("machine", c.Completion.Machine)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("addressfamily", c.Completion.AddressFamily)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("type", c.Completion.IPType)) + + }, + ValidArgsFn: c.Completion.Ip, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *ip) Create(_ any) (*apiv2.IP, error) { + panic("unimplemented") +} + +func (c *ip) Delete(id string) (*apiv2.IP, error) { + panic("unimplemented") +} + +func (c *ip) Get(id string) (*apiv2.IP, error) { + panic("unimplemented") +} + +func (c *ip) List() ([]*apiv2.IP, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Adminv2().IP().List(ctx, &adminv2.IPServiceListRequest{ + Query: &apiv2.IPQuery{ + Ip: pointer.PointerOrNil(viper.GetString("ip")), + Uuid: pointer.PointerOrNil(viper.GetString("uuid")), + Network: pointer.PointerOrNil(viper.GetString("ip")), + Project: pointer.PointerOrNil(viper.GetString("project")), + Name: pointer.PointerOrNil(viper.GetString("name")), + Machine: pointer.PointerOrNil(viper.GetString("machine")), + ParentPrefixCidr: pointer.PointerOrNil(viper.GetString("parent-prefix")), + // Labels: , + Type: helpers.IPTypeToType(viper.GetString("type")), + AddressFamily: helpers.IPAddressFamilyToType(viper.GetString("addressfamily")), + Namespace: pointer.PointerOrNil(viper.GetString("namespace")), + }, + }) + if err != nil { + return nil, err + } + + return resp.Ips, nil +} + +func (c *ip) Update(_ any) (*apiv2.IP, error) { + panic("unimplemented") +} + +func (c *ip) Convert(r *apiv2.IP) (string, any, any, error) { + panic("unimplemented") +} diff --git a/cmd/completion/ip.go b/cmd/completion/ip.go index 3ff3aa0..8071fc6 100644 --- a/cmd/completion/ip.go +++ b/cmd/completion/ip.go @@ -34,3 +34,17 @@ func (c *Completion) AddressFamily(cmd *cobra.Command, args []string, toComplete return afs, cobra.ShellCompDirectiveNoFileComp } +func (c *Completion) IPType(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + var afs []string + for _, af := range []apiv2.IPType{ + apiv2.IPType_IP_TYPE_STATIC, + apiv2.IPType_IP_TYPE_EPHEMERAL} { + stringValue, err := enum.GetStringValue(af) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + afs = append(afs, *stringValue) + } + + return afs, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/completion/machine.go b/cmd/completion/machine.go new file mode 100644 index 0000000..36f1b06 --- /dev/null +++ b/cmd/completion/machine.go @@ -0,0 +1,25 @@ +package completion + +import ( + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + "github.com/spf13/cobra" +) + +func (c *Completion) Machine(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + req := &adminv2.MachineServiceListRequest{} + resp, err := c.Client.Adminv2().Machine().List(cmd.Context(), req) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + var names []string + for _, m := range resp.Machines { + var hostname string + if m.Allocation != nil { + hostname = m.Allocation.Hostname + names = append(names, m.Uuid+"\t"+hostname) + } else { + names = append(names, m.Uuid) + } + } + return names, cobra.ShellCompDirectiveNoFileComp +} diff --git a/docs/admin/metalctlv2_admin.md b/docs/admin/metalctlv2_admin.md index ebad65f..9ed2106 100644 --- a/docs/admin/metalctlv2_admin.md +++ b/docs/admin/metalctlv2_admin.md @@ -31,6 +31,7 @@ these commands utilize the admin api, which can only be accessed by metal-stack * [metalctlv2 admin audit](metalctlv2_admin_audit.md) - manage audit entities * [metalctlv2 admin component](metalctlv2_admin_component.md) - manage component entities * [metalctlv2 admin image](metalctlv2_admin_image.md) - manage image entities +* [metalctlv2 admin ip](metalctlv2_admin_ip.md) - manage ip entities * [metalctlv2 admin network](metalctlv2_admin_network.md) - manage network entities * [metalctlv2 admin partition](metalctlv2_admin_partition.md) - manage partition entities * [metalctlv2 admin project](metalctlv2_admin_project.md) - manage project entities diff --git a/docs/admin/metalctlv2_admin_ip.md b/docs/admin/metalctlv2_admin_ip.md new file mode 100644 index 0000000..bf34bb2 --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip.md @@ -0,0 +1,38 @@ +## metalctlv2 admin ip + +manage ip entities + +### Synopsis + +an ip address of metal-stack.io + +### Options + +``` + -h, --help help for ip +``` + +### 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 + -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), wide is a table with more columns. (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 admin](metalctlv2_admin.md) - admin commands +* [metalctlv2 admin ip apply](metalctlv2_admin_ip_apply.md) - applies one or more ips from a given file +* [metalctlv2 admin ip create](metalctlv2_admin_ip_create.md) - creates the ip +* [metalctlv2 admin ip delete](metalctlv2_admin_ip_delete.md) - deletes the ip +* [metalctlv2 admin ip describe](metalctlv2_admin_ip_describe.md) - describes the ip +* [metalctlv2 admin ip edit](metalctlv2_admin_ip_edit.md) - edit the ip through an editor and update +* [metalctlv2 admin ip list](metalctlv2_admin_ip_list.md) - list all ips +* [metalctlv2 admin ip update](metalctlv2_admin_ip_update.md) - updates the ip + diff --git a/docs/admin/metalctlv2_admin_ip_apply.md b/docs/admin/metalctlv2_admin_ip_apply.md new file mode 100644 index 0000000..8ee2600 --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip_apply.md @@ -0,0 +1,46 @@ +## metalctlv2 admin ip apply + +applies one or more ips from a given file + +``` +metalctlv2 admin ip 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 ip describe ip-1 -o yaml > ip.yaml + $ vi ip.yaml + $ # either via stdin + $ cat ip.yaml | metalctlv2 ip apply -f - + $ # or via file + $ metalctlv2 ip apply -f ip.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 + -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), wide is a table with more columns. (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 admin ip](metalctlv2_admin_ip.md) - manage ip entities + diff --git a/docs/admin/metalctlv2_admin_ip_create.md b/docs/admin/metalctlv2_admin_ip_create.md new file mode 100644 index 0000000..32aee67 --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip_create.md @@ -0,0 +1,46 @@ +## metalctlv2 admin ip create + +creates the ip + +``` +metalctlv2 admin ip 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 ip describe ip-1 -o yaml > ip.yaml + $ vi ip.yaml + $ # either via stdin + $ cat ip.yaml | metalctlv2 ip create -f - + $ # or via file + $ metalctlv2 ip create -f ip.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 + -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), wide is a table with more columns. (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 admin ip](metalctlv2_admin_ip.md) - manage ip entities + diff --git a/docs/admin/metalctlv2_admin_ip_delete.md b/docs/admin/metalctlv2_admin_ip_delete.md new file mode 100644 index 0000000..fffbd54 --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip_delete.md @@ -0,0 +1,46 @@ +## metalctlv2 admin ip delete + +deletes the ip + +``` +metalctlv2 admin ip 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 ip describe ip-1 -o yaml > ip.yaml + $ vi ip.yaml + $ # either via stdin + $ cat ip.yaml | metalctlv2 ip delete -f - + $ # or via file + $ metalctlv2 ip delete -f ip.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 + -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), wide is a table with more columns. (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 admin ip](metalctlv2_admin_ip.md) - manage ip entities + diff --git a/docs/admin/metalctlv2_admin_ip_describe.md b/docs/admin/metalctlv2_admin_ip_describe.md new file mode 100644 index 0000000..637d0d7 --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip_describe.md @@ -0,0 +1,31 @@ +## metalctlv2 admin ip describe + +describes the ip + +``` +metalctlv2 admin ip describe [flags] +``` + +### Options + +``` + -h, --help help for describe +``` + +### 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 + -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), wide is a table with more columns. (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 admin ip](metalctlv2_admin_ip.md) - manage ip entities + diff --git a/docs/admin/metalctlv2_admin_ip_edit.md b/docs/admin/metalctlv2_admin_ip_edit.md new file mode 100644 index 0000000..ea29590 --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip_edit.md @@ -0,0 +1,31 @@ +## metalctlv2 admin ip edit + +edit the ip through an editor and update + +``` +metalctlv2 admin ip 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 + -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), wide is a table with more columns. (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 admin ip](metalctlv2_admin_ip.md) - manage ip entities + diff --git a/docs/admin/metalctlv2_admin_ip_list.md b/docs/admin/metalctlv2_admin_ip_list.md new file mode 100644 index 0000000..0233b0b --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip_list.md @@ -0,0 +1,40 @@ +## metalctlv2 admin ip list + +list all ips + +``` +metalctlv2 admin ip list [flags] +``` + +### Options + +``` + --addressfamily string addressfamily of ips which should be listed + -h, --help help for list + --ip string ip which should be listed + --machine string machine where ips are attached to + --name string name from ips which should be listed + --network string network from where ips should be listed + --project string project from where ips 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: ip|name|network|project|type|uuid + --type string type of ips which should be listed + --uuid string allocation uuid of ip which should be listed +``` + +### 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 + -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), wide is a table with more columns. (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 admin ip](metalctlv2_admin_ip.md) - manage ip entities + diff --git a/docs/admin/metalctlv2_admin_ip_update.md b/docs/admin/metalctlv2_admin_ip_update.md new file mode 100644 index 0000000..70aa5b0 --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip_update.md @@ -0,0 +1,46 @@ +## metalctlv2 admin ip update + +updates the ip + +``` +metalctlv2 admin ip 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 ip describe ip-1 -o yaml > ip.yaml + $ vi ip.yaml + $ # either via stdin + $ cat ip.yaml | metalctlv2 ip update -f - + $ # or via file + $ metalctlv2 ip update -f ip.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 + -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), wide is a table with more columns. (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 admin ip](metalctlv2_admin_ip.md) - manage ip entities + diff --git a/pkg/helpers/network.go b/pkg/helpers/network.go index 9f064d3..b244ded 100644 --- a/pkg/helpers/network.go +++ b/pkg/helpers/network.go @@ -3,9 +3,18 @@ package helpers import ( "net/netip" + "github.com/metal-stack/api/go/enum" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" ) +func IPTypeToType(t string) *apiv2.IPType { + ipt, err := enum.GetEnum[apiv2.IPType](t) + if err != nil { + return nil + } + return &ipt +} + func IPAddressFamilyToType(af string) *apiv2.IPAddressFamily { switch af { case "": From 39b453287851b751e0a740837e3810b84f78cafe Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Mon, 3 Aug 2026 16:28:44 +0200 Subject: [PATCH 5/5] admin ip get --- cmd/admin/v2/ip.go | 43 +++++++++++++++++++---- cmd/tableprinters/ip.go | 4 +-- docs/admin/metalctlv2_admin_ip_list.md | 1 + tests/e2e/api/ip_test.go | 48 +++++++++++++------------- 4 files changed, 64 insertions(+), 32 deletions(-) diff --git a/cmd/admin/v2/ip.go b/cmd/admin/v2/ip.go index fb70b8c..1fa5aed 100644 --- a/cmd/admin/v2/ip.go +++ b/cmd/admin/v2/ip.go @@ -1,6 +1,8 @@ package v2 import ( + "fmt" + 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" @@ -38,6 +40,7 @@ func newIPCmd(c *config.Config) *cobra.Command { cmd.Flags().String("name", "", "name from ips which should be listed") cmd.Flags().String("network", "", "network from where ips should be listed") cmd.Flags().String("machine", "", "machine where ips are attached to") + cmd.Flags().StringSlice("labels", nil, "lists only ips with the given labels") cmd.Flags().String("addressfamily", "", "addressfamily of ips which should be listed") cmd.Flags().String("type", "", "type of ips which should be listed") @@ -63,26 +66,54 @@ func (c *ip) Delete(id string) (*apiv2.IP, error) { } func (c *ip) Get(id string) (*apiv2.IP, error) { - panic("unimplemented") + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Adminv2().IP().List(ctx, &adminv2.IPServiceListRequest{ + Query: &apiv2.IPQuery{ + Ip: &id, + }, + }) + if err != nil { + return nil, err + } + switch len(resp.Ips) { + case 0: + return nil, fmt.Errorf("no ip found for ip:%s", id) + case 1: + return resp.Ips[0], nil + default: + return nil, fmt.Errorf("more than one ip found for ip:%s", id) + } } func (c *ip) List() ([]*apiv2.IP, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() + var labels *apiv2.Labels + if labelSlice := viper.GetStringSlice("labels"); len(labelSlice) > 0 { + var err error + + labels, err = helpers.LabelsFromSlice(labelSlice) + if err != nil { + return nil, err + } + } + resp, err := c.c.Client.Adminv2().IP().List(ctx, &adminv2.IPServiceListRequest{ Query: &apiv2.IPQuery{ Ip: pointer.PointerOrNil(viper.GetString("ip")), Uuid: pointer.PointerOrNil(viper.GetString("uuid")), - Network: pointer.PointerOrNil(viper.GetString("ip")), + Network: pointer.PointerOrNil(viper.GetString("network")), Project: pointer.PointerOrNil(viper.GetString("project")), Name: pointer.PointerOrNil(viper.GetString("name")), Machine: pointer.PointerOrNil(viper.GetString("machine")), ParentPrefixCidr: pointer.PointerOrNil(viper.GetString("parent-prefix")), - // Labels: , - Type: helpers.IPTypeToType(viper.GetString("type")), - AddressFamily: helpers.IPAddressFamilyToType(viper.GetString("addressfamily")), - Namespace: pointer.PointerOrNil(viper.GetString("namespace")), + Labels: labels, + Type: helpers.IPTypeToType(viper.GetString("type")), + AddressFamily: helpers.IPAddressFamilyToType(viper.GetString("addressfamily")), + Namespace: pointer.PointerOrNil(viper.GetString("namespace")), }, }) if err != nil { diff --git a/cmd/tableprinters/ip.go b/cmd/tableprinters/ip.go index bb978f2..203620c 100644 --- a/cmd/tableprinters/ip.go +++ b/cmd/tableprinters/ip.go @@ -12,7 +12,7 @@ import ( func (t *TablePrinter) IPTable(data []*apiv2.IP, wide bool) ([]string, [][]string, error) { var ( rows [][]string - header = []string{"IP", "Project", "ID", "Type", "Name", "Attached Service"} + header = []string{"IP", "Project", "ID", "Network", "Type", "Name", "Attached Service"} ) if wide { @@ -33,7 +33,7 @@ func (t *TablePrinter) IPTable(data []*apiv2.IP, wide bool) ([]string, [][]strin if wide { rows = append(rows, []string{ip.Ip, ip.Project, ip.Uuid, pointer.SafeDeref(t), ip.Name, ip.Description, strings.Join(labels, "\n")}) } else { - rows = append(rows, []string{ip.Ip, ip.Project, ip.Uuid, pointer.SafeDeref(t), ip.Name, attachedService}) + rows = append(rows, []string{ip.Ip, ip.Project, ip.Uuid, ip.Network, pointer.SafeDeref(t), ip.Name, attachedService}) } } diff --git a/docs/admin/metalctlv2_admin_ip_list.md b/docs/admin/metalctlv2_admin_ip_list.md index 0233b0b..ce9219c 100644 --- a/docs/admin/metalctlv2_admin_ip_list.md +++ b/docs/admin/metalctlv2_admin_ip_list.md @@ -12,6 +12,7 @@ metalctlv2 admin ip list [flags] --addressfamily string addressfamily of ips which should be listed -h, --help help for list --ip string ip which should be listed + --labels strings lists only ips with the given labels --machine string machine where ips are attached to --name string name from ips which should be listed --network string network from where ips should be listed diff --git a/tests/e2e/api/ip_test.go b/tests/e2e/api/ip_test.go index e360f12..b78d2ec 100644 --- a/tests/e2e/api/ip_test.go +++ b/tests/e2e/api/ip_test.go @@ -37,9 +37,9 @@ func Test_IPCmd_List(t *testing.T) { }, }), WantTable: new(` - IP PROJECT ID TYPE NAME ATTACHED SERVICE - 4.3.2.1 46bdfc45-9c8d-4268-b359-b40e3079d384 9cef40ec-29c6-4dfa-aee8-47ee1f49223d ephemeral b - 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a + IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 4.3.2.1 46bdfc45-9c8d-4268-b359-b40e3079d384 9cef40ec-29c6-4dfa-aee8-47ee1f49223d internet ephemeral b + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 internet static a `), WantWideTable: new(` IP PROJECT ID TYPE NAME DESCRIPTION LABELS @@ -52,10 +52,10 @@ func Test_IPCmd_List(t *testing.T) { 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 `), WantMarkdown: new(` - | IP | PROJECT | ID | TYPE | NAME | ATTACHED SERVICE | - |---------|--------------------------------------|--------------------------------------|-----------|------|------------------| - | 4.3.2.1 | 46bdfc45-9c8d-4268-b359-b40e3079d384 | 9cef40ec-29c6-4dfa-aee8-47ee1f49223d | ephemeral | b | | - | 1.1.1.1 | ce19a655-7933-4745-8f3e-9592b4a90488 | 2e0144a2-09ef-42b7-b629-4263295db6e8 | static | a | | + | IP | PROJECT | ID | NETWORK | TYPE | NAME | ATTACHED SERVICE | + |---------|--------------------------------------|--------------------------------------|----------|-----------|------|------------------| + | 4.3.2.1 | 46bdfc45-9c8d-4268-b359-b40e3079d384 | 9cef40ec-29c6-4dfa-aee8-47ee1f49223d | internet | ephemeral | b | | + | 1.1.1.1 | ce19a655-7933-4745-8f3e-9592b4a90488 | 2e0144a2-09ef-42b7-b629-4263295db6e8 | internet | static | a | | `), }, } @@ -86,8 +86,8 @@ func Test_IPCmd_Describe(t *testing.T) { }), WantProtoObject: testresources.IP1(), WantTable: new(` - IP PROJECT ID TYPE NAME ATTACHED SERVICE - 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a + IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 internet static a `), WantWideTable: new(` IP PROJECT ID TYPE NAME DESCRIPTION LABELS @@ -98,9 +98,9 @@ func Test_IPCmd_Describe(t *testing.T) { 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 `), WantMarkdown: new(` - | IP | PROJECT | ID | TYPE | NAME | ATTACHED SERVICE | - |---------|--------------------------------------|--------------------------------------|--------|------|------------------| - | 1.1.1.1 | ce19a655-7933-4745-8f3e-9592b4a90488 | 2e0144a2-09ef-42b7-b629-4263295db6e8 | static | a | | + | IP | PROJECT | ID | NETWORK | TYPE | NAME | ATTACHED SERVICE | + |---------|--------------------------------------|--------------------------------------|----------|--------|------|------------------| + | 1.1.1.1 | ce19a655-7933-4745-8f3e-9592b4a90488 | 2e0144a2-09ef-42b7-b629-4263295db6e8 | internet | static | a | | `), }, } @@ -161,8 +161,8 @@ func Test_IPCmd_Create(t *testing.T) { }, }), WantTable: new(` - IP PROJECT ID TYPE NAME ATTACHED SERVICE - 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a + IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 internet static a `), }, } @@ -217,8 +217,8 @@ func Test_IPCmd_Delete(t *testing.T) { }, ), WantTable: new(` - IP PROJECT ID TYPE NAME ATTACHED SERVICE - 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a + IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 internet static a `), }, } @@ -255,8 +255,8 @@ func Test_IPCmd_Update(t *testing.T) { ), WantProtoObject: testresources.IP1(), WantTable: new(` - IP PROJECT ID TYPE NAME ATTACHED SERVICE - 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a + IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 internet static a `), WantWideTable: new(` IP PROJECT ID TYPE NAME DESCRIPTION LABELS @@ -300,8 +300,8 @@ func Test_IPCmd_Update(t *testing.T) { }, ), WantTable: new(` - IP PROJECT ID TYPE NAME ATTACHED SERVICE - 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a + IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 internet static a `), }, } @@ -342,8 +342,8 @@ func Test_IPCmd_Apply(t *testing.T) { }, ), WantTable: new(` - IP PROJECT ID TYPE NAME ATTACHED SERVICE - 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a + IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 internet static a `), }, { @@ -395,8 +395,8 @@ func Test_IPCmd_Apply(t *testing.T) { }, ), WantTable: new(` - IP PROJECT ID TYPE NAME ATTACHED SERVICE - 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a + IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 internet static a `), }, }