diff --git a/cmd/admin/v2/image.go b/cmd/admin/v2/image.go index 72e2487..4d3cde3 100644 --- a/cmd/admin/v2/image.go +++ b/cmd/admin/v2/image.go @@ -81,6 +81,7 @@ func newImageCmd(c *config.Config) *cobra.Command { genericcli.Must(cmd.RegisterFlagCompletionFunc("classification", c.Completion.ImageClassification)) }, UpdateRequestFromCLI: w.updateFromCLI, + ValidArgsFn: c.Completion.Image, } usageCmd := &cobra.Command{ diff --git a/cmd/admin/v2/ip.go b/cmd/admin/v2/ip.go index 733295d..ac10f21 100644 --- a/cmd/admin/v2/ip.go +++ b/cmd/admin/v2/ip.go @@ -81,13 +81,14 @@ func (c *ip) Get(id string) (*apiv2.IP, error) { if err != nil { return nil, err } + switch len(resp.Ips) { case 0: - return nil, fmt.Errorf("no ip found for ip:%s", id) + 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) + return nil, fmt.Errorf("more than one ip found for ip: %s", id) } } diff --git a/cmd/api/v2/ip.go b/cmd/api/v2/ip.go index 9f46a0f..76ee740 100644 --- a/cmd/api/v2/ip.go +++ b/cmd/api/v2/ip.go @@ -33,8 +33,20 @@ func newIPCmd(c *config.Config) *cobra.Command { ListPrinter: func() printers.Printer { return c.ListPrinter }, ListCmdMutateFn: func(cmd *cobra.Command) { cmd.Flags().StringP("project", "p", "", "project from where ips should be listed") + cmd.Flags().String("ip", "", "ip which should be listed") + cmd.Flags().String("uuid", "", "allocation uuid of ip which 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().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") 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)) }, CreateCmdMutateFn: func(cmd *cobra.Command) { cmd.Flags().StringP("project", "p", "", "project of the ip") @@ -188,8 +200,28 @@ 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.Apiv2().IP().List(ctx, &apiv2.IPServiceListRequest{ Project: c.c.GetProject(), + Query: &apiv2.IPQuery{ + Ip: pointer.PointerOrNil(viper.GetString("ip")), + Uuid: pointer.PointerOrNil(viper.GetString("uuid")), + Network: pointer.PointerOrNil(viper.GetString("network")), + Name: pointer.PointerOrNil(viper.GetString("name")), + Machine: pointer.PointerOrNil(viper.GetString("machine")), + Labels: labels, + Type: helpers.IPTypeToType(viper.GetString("type")), + AddressFamily: helpers.IPAddressFamilyToType(viper.GetString("addressfamily")), + }, }) if err != nil { return nil, err diff --git a/cmd/completion/ip.go b/cmd/completion/ip.go index 0ae829d..bb617b1 100644 --- a/cmd/completion/ip.go +++ b/cmd/completion/ip.go @@ -7,17 +7,19 @@ import ( ) func (c *Completion) Ip(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - req := &apiv2.IPServiceListRequest{ + resp, err := c.Client.Apiv2().IP().List(cmd.Context(), &apiv2.IPServiceListRequest{ Project: c.Proj, - } - resp, err := c.Client.Apiv2().IP().List(cmd.Context(), req) + }) if err != nil { return nil, cobra.ShellCompDirectiveError } + var names []string + for _, s := range resp.Ips { - names = append(names, s.Uuid+"\t"+s.Ip+"\t"+s.Name) + names = append(names, s.Ip+"\t"+s.Uuid+"\t"+s.Name) } + return names, cobra.ShellCompDirectiveNoFileComp } diff --git a/docs/metalctlv2_ip_list.md b/docs/metalctlv2_ip_list.md index 3ba837a..5932ec1 100644 --- a/docs/metalctlv2_ip_list.md +++ b/docs/metalctlv2_ip_list.md @@ -9,9 +9,17 @@ metalctlv2 ip list [flags] ### Options ``` - -h, --help help for list - -p, --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 + --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 + -p, --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 diff --git a/tests/e2e/api/ip_test.go b/tests/e2e/api/ip_test.go index b78d2ec..f9ab8fa 100644 --- a/tests/e2e/api/ip_test.go +++ b/tests/e2e/api/ip_test.go @@ -10,6 +10,7 @@ import ( e2erootcmd "github.com/metal-stack/cli/testing/e2e" "github.com/metal-stack/cli/tests/e2e/testresources" "github.com/metal-stack/metal-lib/pkg/genericcli/e2e" + "github.com/metal-stack/metal-lib/pkg/pointer" "github.com/spf13/afero" "github.com/stretchr/testify/require" ) @@ -24,6 +25,7 @@ func Test_IPCmd_List(t *testing.T) { { WantRequest: &apiv2.IPServiceListRequest{ Project: testresources.IP1().Project, + Query: &apiv2.IPQuery{}, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.IPServiceListResponse{ @@ -37,8 +39,8 @@ func Test_IPCmd_List(t *testing.T) { }, }), WantTable: new(` - 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 + 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(` @@ -58,6 +60,54 @@ func Test_IPCmd_List(t *testing.T) { | 1.1.1.1 | ce19a655-7933-4745-8f3e-9592b4a90488 | 2e0144a2-09ef-42b7-b629-4263295db6e8 | internet | static | a | | `), }, + { + Name: "list filters", + CmdArgs: []string{"ip", "list", + "--project", testresources.IP1().Project, + "--ip", testresources.IP1().Ip, + "--uuid", testresources.IP1().Uuid, + "--name", testresources.IP1().Name, + "--network", testresources.IP1().Network, + "--machine", "machine-1", + "--labels", "a=b", + "--addressfamily", "IPv4", + "--type", "static", + }, + AssertExhaustiveArgs: true, + AssertExhaustiveExcludes: []string{"sort-by"}, + NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &apiv2.IPServiceListRequest{ + Project: testresources.IP1().Project, + Query: &apiv2.IPQuery{ + Ip: pointer.PointerOrNil(testresources.IP1().Ip), + Uuid: pointer.PointerOrNil(testresources.IP1().Uuid), + Name: pointer.PointerOrNil(testresources.IP1().Name), + Network: pointer.PointerOrNil(testresources.IP1().Network), + Machine: pointer.PointerOrNil("machine-1"), + Type: pointer.PointerOrNil(apiv2.IPType_IP_TYPE_STATIC), + AddressFamily: apiv2.IPAddressFamily_IP_ADDRESS_FAMILY_V4.Enum(), + Labels: &apiv2.Labels{ + Labels: map[string]string{"a": "b"}, + }, + }, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&apiv2.IPServiceListResponse{ + Ips: []*apiv2.IP{ + testresources.IP1(), + }, + }) + }, + }, + }, + }), + WantTable: new(` + 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 + `), + }, } for _, tt := range tests { tt.TestCmd(t) @@ -86,7 +136,7 @@ func Test_IPCmd_Describe(t *testing.T) { }), WantProtoObject: testresources.IP1(), WantTable: new(` - IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 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(` @@ -161,7 +211,7 @@ func Test_IPCmd_Create(t *testing.T) { }, }), WantTable: new(` - IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 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,7 +267,7 @@ func Test_IPCmd_Delete(t *testing.T) { }, ), WantTable: new(` - IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 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,7 +305,7 @@ func Test_IPCmd_Update(t *testing.T) { ), WantProtoObject: testresources.IP1(), WantTable: new(` - IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 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(` @@ -300,7 +350,7 @@ func Test_IPCmd_Update(t *testing.T) { }, ), WantTable: new(` - IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 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,7 +392,7 @@ func Test_IPCmd_Apply(t *testing.T) { }, ), WantTable: new(` - IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 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,7 +445,7 @@ func Test_IPCmd_Apply(t *testing.T) { }, ), WantTable: new(` - IP PROJECT ID NETWORK TYPE NAME ATTACHED SERVICE + 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 `), },