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..733295d --- /dev/null +++ b/cmd/admin/v2/ip.go @@ -0,0 +1,136 @@ +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" + "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().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)) + + }, + ValidArgsFn: c.Completion.Ip, + OnlyCmds: map[genericcli.DefaultCmd]bool{ + genericcli.ListCmd: true, + genericcli.DescribeCmd: true, + }, + } + + 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) { + 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("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: 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/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.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..269255d --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip.md @@ -0,0 +1,33 @@ +## 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 describe](metalctlv2_admin_ip_describe.md) - describes the ip +* [metalctlv2 admin ip list](metalctlv2_admin_ip_list.md) - list all ips + 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_list.md b/docs/admin/metalctlv2_admin_ip_list.md new file mode 100644 index 0000000..ce9219c --- /dev/null +++ b/docs/admin/metalctlv2_admin_ip_list.md @@ -0,0 +1,41 @@ +## 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 + --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 + --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/go.mod b/go.mod index 75251b0..ee0d712 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/metal-stack/cli -go 1.26.0 +go 1.26.5 require ( connectrpc.com/connect v1.20.0 @@ -10,20 +10,20 @@ require ( github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/metal-stack/api v0.4.4 - github.com/metal-stack/metal-lib v0.26.1 + github.com/metal-stack/metal-lib v0.26.2 github.com/metal-stack/v v1.0.3 github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 - google.golang.org/grpc v1.82.1 - google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af + google.golang.org/grpc v1.83.0 + google.golang.org/protobuf v1.36.12 sigs.k8s.io/yaml v1.6.0 ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1 // indirect - buf.build/go/protovalidate v1.2.0 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260709200747-435963d16310.1 // indirect + buf.build/go/protovalidate v1.3.0 // indirect buf.build/go/protoyaml v0.7.0 // indirect cel.dev/expr v0.25.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect @@ -40,9 +40,9 @@ require ( github.com/goccy/go-json v0.10.6 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect - github.com/google/cel-go v0.30.0 // indirect + github.com/google/cel-go v0.31.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.19.1 // indirect + github.com/klauspost/compress v1.19.2 // indirect github.com/klauspost/connect-compress/v2 v2.1.1 // indirect github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.24 // indirect @@ -63,12 +63,12 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/exp v0.0.0-20260727155853-b88d891fe743 // indirect - golang.org/x/net v0.57.0 // indirect + golang.org/x/exp v0.0.0-20260812173653-3d80eb74bc5b // indirect + golang.org/x/net v0.58.0 // indirect golang.org/x/sys v0.47.0 // indirect - golang.org/x/text v0.40.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260727163830-6c54dddc4772 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260727163830-6c54dddc4772 // indirect + golang.org/x/text v0.41.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260810153831-ec0a7760b754 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260810153831-ec0a7760b754 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apimachinery v0.36.3 // indirect diff --git a/go.sum b/go.sum index b2fceca..816f4a3 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1 h1:fXh8CsdNpjRr8R5vFdqtIxPt/Lno2IIJlYOdZBIZn0w= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM= -buf.build/go/protovalidate v1.2.0 h1:DQVrUWkmGTBij+kOYv/x2LLxwcLaGKMdzShj1/6/3H0= -buf.build/go/protovalidate v1.2.0/go.mod h1:7rYiQEhqvAipoazpVNBBH2S2f8bjG4huMVy1V2Yofn4= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260709200747-435963d16310.1 h1:6nlcxMOui23ZRVAfJM451duu79P1npA5JRdZqMilrrQ= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260709200747-435963d16310.1/go.mod h1:TCt1lluMFnctISJXvkIQ4x3ABrPuUKCWKyjKdkJNBpw= +buf.build/go/protovalidate v1.3.0 h1:8ITcnZGkAHx6TyhZvro+iET/AyqU8gEWQJK2WsT62ms= +buf.build/go/protovalidate v1.3.0/go.mod h1:82s5g+rFRj1CZPiLv6OTA31jBu2fpq7mLXHwa9mZfEs= buf.build/go/protoyaml v0.7.0 h1:z4oVoFicbpPefhT7WAykxUdfp0yEQlhMQ2mCZOY5V38= buf.build/go/protoyaml v0.7.0/go.mod h1:+a0cavd0uMvirb87xdu2ZMMmjlIQoiH/N2Ich5MGSQ0= cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs= @@ -51,16 +51,16 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/cel-go v0.30.0 h1:ll54AkzKunWkBn9wSoiUXbFZXYZTkdJGNXTBXUoolGo= -github.com/google/cel-go v0.30.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= +github.com/google/cel-go v0.31.0 h1:H0bhpFTqOvmHrBGrWKp7ZlhBm5Hh8PYUEXnwxT1LL7A= +github.com/google/cel-go v0.31.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk= -github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= +github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/connect-compress/v2 v2.1.1 h1:ycZNp4rWOZBodVE2Ls5AzK4aHkyK+GteEfzRZgKNs+c= github.com/klauspost/connect-compress/v2 v2.1.1/go.mod h1:9oilsPHJMzGKkjafSBk9J7iVo4mO+dw0G0KSdVpnlVE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -75,8 +75,8 @@ github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhV github.com/mattn/go-runewidth v0.0.27/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8= github.com/metal-stack/api v0.4.4 h1:NwJKCFHnbmsU7DjHLtBmpdPuPniXc2wDabHjdpXk3qA= github.com/metal-stack/api v0.4.4/go.mod h1:E7f2GkKNSr4vBxhQwWrr/Mjnf4ctPqCX2dpDtXBqkBk= -github.com/metal-stack/metal-lib v0.26.1 h1:wMrhENm/HPq0t7cjCkhzepGD6dyxmX/4IMsaJZea874= -github.com/metal-stack/metal-lib v0.26.1/go.mod h1:tnx4MM5oml10EMN6Nq6oFSbjOKB9B27WUZQUyZ4MywA= +github.com/metal-stack/metal-lib v0.26.2 h1:ajPbC9wGe8LySw9J3JT03IGLZghvxsXSb7iceFp/FuQ= +github.com/metal-stack/metal-lib v0.26.2/go.mod h1:cNXjPBs8SFnjqfBobuSbm5mDk6E/jS8PVeIOrV/7POE= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= github.com/metal-stack/v v1.0.3/go.mod h1:YTahEu7/ishwpYKnp/VaW/7nf8+PInogkfGwLcGPdXg= github.com/minio/minlz v1.2.0 h1:6IOBuiHg04QxvbFfgFLT/9sMaO/UhL7S+ApW1mK8q5A= @@ -126,22 +126,22 @@ go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= -golang.org/x/exp v0.0.0-20260727155853-b88d891fe743 h1:ex206bKw+v3K0dm3andkrIF+ijyQKJG1pLgwQ2PYdQM= -golang.org/x/exp v0.0.0-20260727155853-b88d891fe743/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q= -golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= -golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/exp v0.0.0-20260812173653-3d80eb74bc5b h1:3XR0v9KL97wjTmRb+6RqMylL4d7ZWM1sQYIITGDxtmg= +golang.org/x/exp v0.0.0-20260812173653-3d80eb74bc5b/go.mod h1:EdfpwwqSu+0Li0mzskwHU6FWDV3t9Q+RZDo3QMUtL3Q= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= -golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= -google.golang.org/genproto/googleapis/api v0.0.0-20260727163830-6c54dddc4772 h1:4namukbyF7JY83aWHQwi9J5ugNTnDReLJ9ZcpqOpRB4= -google.golang.org/genproto/googleapis/api v0.0.0-20260727163830-6c54dddc4772/go.mod h1:1brfde68Npq6+WA75c1EHWPijZEG1kMus61ygPZfn4A= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260727163830-6c54dddc4772 h1:zuslGE3FGxH0hC6veLvSLME3TZzun9MQzjYwo1CBN+k= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260727163830-6c54dddc4772/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE= -google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= -google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= -google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +google.golang.org/genproto/googleapis/api v0.0.0-20260810153831-ec0a7760b754 h1:dWeMvEJ3JhYgqSCAHUZZJgMUyfniiiCvDc72x5EqJP0= +google.golang.org/genproto/googleapis/api v0.0.0-20260810153831-ec0a7760b754/go.mod h1:q/3oV3jAi5vwelxsVAprMBC8BcM2zmNe+IjRGd+9/ks= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260810153831-ec0a7760b754 h1:k5CJw9e5ONCcA/u0webKt092npXuY+KeGh3Q8NAVf0g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260810153831-ec0a7760b754/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ= +google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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 "": diff --git a/tests/e2e/admin/ip_test.go b/tests/e2e/admin/ip_test.go new file mode 100644 index 0000000..7eb106f --- /dev/null +++ b/tests/e2e/admin/ip_test.go @@ -0,0 +1,111 @@ +package admin_e2e + +import ( + "testing" + + "connectrpc.com/connect" + "github.com/metal-stack/api/go/client" + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + 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" +) + +func Test_IPCmd_List(t *testing.T) { + tests := []*e2e.Test[adminv2.IPServiceListResponse, apiv2.IP]{ + { + Name: "list", + CmdArgs: []string{"admin", "ip", "list"}, + NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.IPServiceListRequest{ + Query: &apiv2.IPQuery{}, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.IPServiceListResponse{ + Ips: []*apiv2.IP{ + testresources.IP1(), + testresources.IP2(), + }, + }) + }, + }, + }, + }), + 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 + 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 + 4.3.2.1 46bdfc45-9c8d-4268-b359-b40e3079d384 9cef40ec-29c6-4dfa-aee8-47ee1f49223d ephemeral b b description a=b + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a a description cluster.metal-stack.io/id/namespace/service=/default/ingress-nginx + `), + Template: new("{{ .ip }} {{ .project }}"), + WantTemplate: new(` +4.3.2.1 46bdfc45-9c8d-4268-b359-b40e3079d384 +1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 + `), + WantMarkdown: 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 | | + | 1.1.1.1 | ce19a655-7933-4745-8f3e-9592b4a90488 | 2e0144a2-09ef-42b7-b629-4263295db6e8 | internet | static | a | | + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_IPCmd_Describe(t *testing.T) { + tests := []*e2e.Test[apiv2.IPServiceGetResponse, *apiv2.IP]{ + { + Name: "describe", + CmdArgs: []string{"admin", "ip", "describe", testresources.IP1().Ip}, + NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.IPServiceListRequest{ + Query: &apiv2.IPQuery{ + Ip: &testresources.IP1().Ip, + }, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.IPServiceListResponse{ + Ips: []*apiv2.IP{ + testresources.IP1(), + }, + }) + }, + }, + }, + }), + WantProtoObject: 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 + `), + WantWideTable: new(` + IP PROJECT ID TYPE NAME DESCRIPTION LABELS + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 2e0144a2-09ef-42b7-b629-4263295db6e8 static a a description cluster.metal-stack.io/id/namespace/service=/default/ingress-nginx + `), + Template: new("{{ .ip }} {{ .project }}"), + WantTemplate: new(` + 1.1.1.1 ce19a655-7933-4745-8f3e-9592b4a90488 + `), + WantMarkdown: 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) + } +} 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 `), }, }