From 5b5098dec128b96d8c3d5662abe23c2f7b581432 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 29 Sep 2026 10:08:19 +0200 Subject: [PATCH 01/15] Fixes for applying static data. --- cmd/admin/v2/partition.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cmd/admin/v2/partition.go b/cmd/admin/v2/partition.go index 1fa3a1a..6c6bbde 100644 --- a/cmd/admin/v2/partition.go +++ b/cmd/admin/v2/partition.go @@ -48,14 +48,6 @@ func newPartitionCmd(c *config.Config) *cobra.Command { ListPrinter: func() printers.Printer { return c.ListPrinter }, ValidArgsFn: c.Completion.Partition, Sorter: sorters.PartitionSorter(), - OnlyCmds: genericcli.OnlyCmds( - genericcli.DescribeCmd, - genericcli.ListCmd, - genericcli.CreateCmd, - genericcli.UpdateCmd, - genericcli.DeleteCmd, - genericcli.EditCmd, - ), CreateCmdMutateFn: func(cmd *cobra.Command) { cmd.Flags().String("id", "", "the id of the partition to create") addMutableFlags(cmd) From 19b87c3d75228fdaefdd0011b4a6eef591414397 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 29 Sep 2026 10:14:20 +0200 Subject: [PATCH 02/15] Fix. --- cmd/admin/v2/network.go | 1 + cmd/admin/v2/partition.go | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/admin/v2/network.go b/cmd/admin/v2/network.go index e924374..b1805c4 100644 --- a/cmd/admin/v2/network.go +++ b/cmd/admin/v2/network.go @@ -205,6 +205,7 @@ func (c *networkCmd) Create(rq *adminv2.NetworkServiceCreateRequest) (*apiv2.Net if errorutil.IsConflict(err) { return nil, genericcli.AlreadyExistsError() } + return nil, err } diff --git a/cmd/admin/v2/partition.go b/cmd/admin/v2/partition.go index 6c6bbde..df401cf 100644 --- a/cmd/admin/v2/partition.go +++ b/cmd/admin/v2/partition.go @@ -5,6 +5,7 @@ import ( "sort" "strings" + "github.com/metal-stack/api/go/errorutil" 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" @@ -165,7 +166,11 @@ func (c *partition) Create(rq *adminv2.PartitionServiceCreateRequest) (*apiv2.Pa resp, err := c.c.Client.Adminv2().Partition().Create(ctx, rq) if err != nil { - return nil, fmt.Errorf("failed to create partition: %w", err) + if errorutil.IsConflict(err) { + return nil, genericcli.AlreadyExistsError() + } + + return nil, err } return resp.Partition, nil From bae859a786679cc3004438762b2a0666d824dc34 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 29 Sep 2026 11:39:39 +0200 Subject: [PATCH 03/15] Update deps. --- go.mod | 12 ++++++------ go.sum | 27 +++++++++++++-------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 12fadf8..fe403cd 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/fatih/color v1.19.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/metal-stack/api v0.5.5 - github.com/metal-stack/metal-console v0.7.7-0.20260909095012-75ec3d1e3e1a + github.com/metal-stack/api v0.7.0 + github.com/metal-stack/metal-console v0.8.2 github.com/metal-stack/metal-lib v0.26.3 github.com/metal-stack/v v1.0.3 github.com/spf13/afero v1.15.0 @@ -24,8 +24,9 @@ require ( require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.2 // indirect - buf.build/go/protovalidate v1.3.0 // indirect + buf.build/go/protovalidate v1.4.0 // indirect buf.build/go/protoyaml v0.7.0 // indirect + cel.dev/cel-go v0.32.0 // indirect cel.dev/expr v0.25.3 // indirect filippo.io/edwards25519 v1.2.0 // indirect github.com/akutz/memconn v0.1.0 // indirect @@ -53,7 +54,6 @@ require ( github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.31.0 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -103,8 +103,8 @@ require ( golang.org/x/time v0.15.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v1.0.1 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260831171406-18b4a7587f8a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260831171406-18b4a7587f8a // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gvisor.dev/gvisor v0.0.0-20260224225140-573d5e7127a8 // indirect diff --git a/go.sum b/go.sum index 09c16e9..f218831 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,12 @@ 9fans.net/go v0.0.8-0.20250307142834-96bdba94b63f/go.mod h1:hHyrZRryGqVdqrknjq5OWDLGCTJ2NeEvtrpR96mjraM= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.2 h1:NbnlmV26O7oZ1iM5tsCI+GEx+3ZSdrhvnKQ/eWSrLiY= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.2/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/protovalidate v1.4.0 h1:UjLrYbt5VX7+TMOs2+pG5FhZhIG1mSfK4EIopbb4LcM= +buf.build/go/protovalidate v1.4.0/go.mod h1:8vJfzNT6NIG2qm3uFsJDXMlRmG+bQJzbcIn1Aa0vPGs= buf.build/go/protoyaml v0.7.0 h1:z4oVoFicbpPefhT7WAykxUdfp0yEQlhMQ2mCZOY5V38= buf.build/go/protoyaml v0.7.0/go.mod h1:+a0cavd0uMvirb87xdu2ZMMmjlIQoiH/N2Ich5MGSQ0= +cel.dev/cel-go v0.32.0 h1:irvpFKr5EuGPyxeME03ERh0rii1TX+BDAnB9eL3IvNk= +cel.dev/cel-go v0.32.0/go.mod h1:DnVip7tpJSsgZymwfT+m1tnEVy3ivAjSMXPx12YrMkU= cel.dev/expr v0.25.3 h1:A2jO8jwOugrrovveCWfj0KEZOfqiLgAcwjpHPhzIGw0= cel.dev/expr v0.25.3/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= @@ -139,8 +141,6 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -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/go-tpm v0.9.4 h1:awZRf9FwOeTunQmHoDYSHJps3ie6f1UlhS1fOdPEt1I= @@ -191,10 +191,10 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A= github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg= -github.com/metal-stack/api v0.5.5 h1:AzPjTKi8Y4XNiJaEcRDCAjuOmIaLuuOEhKXVWliTtWc= -github.com/metal-stack/api v0.5.5/go.mod h1:U8c+awSMxXaRJjzWpo1IMcvlNxOi1ewu/fk1BzDM7hI= -github.com/metal-stack/metal-console v0.7.7-0.20260909095012-75ec3d1e3e1a h1:zFB1a0PMyzgBGtJU3HID75pEsD6l694BViuxJxKDdLg= -github.com/metal-stack/metal-console v0.7.7-0.20260909095012-75ec3d1e3e1a/go.mod h1:ZkselOaxF/iELFL9Tku3maXgpX4DM0CHjoG7CGuky6E= +github.com/metal-stack/api v0.7.0 h1:z+J788nCCmIyBaG/hNmKLPJ/S1K6nfePqSDyMIIX470= +github.com/metal-stack/api v0.7.0/go.mod h1:rWdj0dTAg2w/WuQhIZ+JVp9b68v6VVCm1k7sLx4U+PQ= +github.com/metal-stack/metal-console v0.8.2 h1:Y86penzEROJKcLica1PeE2ddtr9G0/u16HiCAYXaxxU= +github.com/metal-stack/metal-console v0.8.2/go.mod h1:ZkselOaxF/iELFL9Tku3maXgpX4DM0CHjoG7CGuky6E= github.com/metal-stack/metal-lib v0.26.3 h1:K5gLoD65m6p3l6qCPrfavIdvNdWfmF2QdXrvU2URaZs= github.com/metal-stack/metal-lib v0.26.3/go.mod h1:cNXjPBs8SFnjqfBobuSbm5mDk6E/jS8PVeIOrV/7POE= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= @@ -335,17 +335,16 @@ golang.zx2c4.com/wireguard v0.0.0-20260522210424-ecfc5a8d5446 h1:cqHQ3AycTHvM2R7 golang.zx2c4.com/wireguard v0.0.0-20260522210424-ecfc5a8d5446/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= golang.zx2c4.com/wireguard/windows v1.0.1 h1:eOxiDVbywPC+ZQqvdCK7x+ZwWXKbYv50TtH8ysFIbw8= golang.zx2c4.com/wireguard/windows v1.0.1/go.mod h1:+fbT3FFdX4zzYDLwJh5+HPEcNN/3HyNdzhNSVsQM+zs= -google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 h1:izFU9hz7aeLI/Mi1J0991ae+xcwRLr7hTqWnB/9aIIU= -google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5/go.mod h1:3LhxRw4YYkf+ylAfgaY9JlVLFKhokkCV8duhLLe7+t0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= +google.golang.org/genproto/googleapis/api v0.0.0-20260831171406-18b4a7587f8a h1:i3TAXhpKc7TUP1VAPiBBrv45kamjoizCC3rOC0cAbOs= +google.golang.org/genproto/googleapis/api v0.0.0-20260831171406-18b4a7587f8a/go.mod h1:CvYJHpbzPlT0fb/PsgtAamdwru/GVxUsomFdXTpOTI8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260831171406-18b4a7587f8a h1:3Dnd1cDaZlB68lziofO+bJXpjOy8UfRv8Unt+yH8tQ4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260831171406-18b4a7587f8a/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From 9d81c50e501ae9aa4759838e142116dbb3360e1d Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 29 Sep 2026 12:50:36 +0200 Subject: [PATCH 04/15] Pointer fixes. --- cmd/api/v2/tenant.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cmd/api/v2/tenant.go b/cmd/api/v2/tenant.go index 2ffbef4..8932e23 100644 --- a/cmd/api/v2/tenant.go +++ b/cmd/api/v2/tenant.go @@ -241,12 +241,11 @@ func (c *tenant) Delete(id string) (*apiv2.Tenant, error) { } func (c *tenant) Convert(r *apiv2.Tenant) (string, *apiv2.TenantServiceCreateRequest, *apiv2.TenantServiceUpdateRequest, error) { - return r.Login, &apiv2.TenantServiceCreateRequest{ Name: r.Name, - Description: &r.Description, - Email: &r.Email, - AvatarUrl: &r.AvatarUrl, + Description: pointer.PointerOrNil(r.Description), + Email: pointer.PointerOrNil(r.Email), + AvatarUrl: pointer.PointerOrNil(r.AvatarUrl), }, &apiv2.TenantServiceUpdateRequest{ Login: r.Login, From 1b5d601e91729779a6758c1f2a0148bfb410d86f Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 29 Sep 2026 14:20:34 +0200 Subject: [PATCH 05/15] Apply tests for image, partition and size. --- tests/e2e/admin/image_test.go | 81 +++++++++++++++++++++++++++++-- tests/e2e/admin/partition_test.go | 80 ++++++++++++++++++++++++++++++ tests/e2e/admin/size_test.go | 78 +++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 3 deletions(-) diff --git a/tests/e2e/admin/image_test.go b/tests/e2e/admin/image_test.go index f8e7a0f..fc84757 100644 --- a/tests/e2e/admin/image_test.go +++ b/tests/e2e/admin/image_test.go @@ -1,6 +1,7 @@ package admin_e2e import ( + "fmt" "testing" "connectrpc.com/connect" @@ -246,9 +247,83 @@ func Test_AdminImageCmd_Usage(t *testing.T) { ubuntu-24.04 Ubuntu 24.04 Ubuntu 24.04 LTS machine supported 2 `), WantWideTable: new(` - ID NAME DESCRIPTION FEATURES EXPIRATION STATUS USAGE - ubuntu-24.04 Ubuntu 24.04 Ubuntu 24.04 LTS machine supported machine-a - machine-b + ID NAME DESCRIPTION FEATURES EXPIRATION STATUS USAGE + ubuntu-24.04 Ubuntu 24.04 Ubuntu 24.04 LTS machine supported machine-a + machine-b + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_AdminImageCmd_Apply(t *testing.T) { + tests := []*e2e.Test[adminv2.ImageServiceUpdateResponse, *apiv2.Image]{ + { + Name: "apply", + CmdArgs: append([]string{"admin", "image", "apply"}, e2e.AppendFromFileCommonArgs()...), + NewRootCmd: e2erootcmd.NewRootCmd(t, + &e2erootcmd.TestConfig{ + FsMocks: func(fs *afero.Afero) { + require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, testresources.Image1()), 0755)) + }, + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.ImageServiceCreateRequest{ + Image: testresources.Image1(), + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.ImageServiceCreateResponse{ + Image: testresources.Image1(), + }) + }, + }, + }, + }), + WantTable: new(` + ID NAME DESCRIPTION FEATURES EXPIRATION STATUS + ubuntu-24.04 Ubuntu 24.04 Ubuntu 24.04 LTS machine supported + `), + }, + { + Name: "apply already exists", + CmdArgs: append([]string{"admin", "image", "apply"}, e2e.AppendFromFileCommonArgs()...), + NewRootCmd: e2erootcmd.NewRootCmd(t, + &e2erootcmd.TestConfig{ + FsMocks: func(fs *afero.Afero) { + require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, testresources.Image1()), 0755)) + }, + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.ImageServiceCreateRequest{ + Image: testresources.Image1(), + }, + WantError: connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("already exists")), + }, + { + WantRequest: &adminv2.ImageServiceUpdateRequest{ + Id: testresources.Image1().Id, + Url: new(testresources.Image1().Url), + Name: testresources.Image1().Name, + Description: testresources.Image1().Description, + Features: testresources.Image1().Features, + Classification: testresources.Image1().Classification, + UpdateMeta: &apiv2.UpdateMeta{ + LockingStrategy: apiv2.OptimisticLockingStrategy_OPTIMISTIC_LOCKING_STRATEGY_SERVER, + }, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.ImageServiceUpdateResponse{ + Image: testresources.Image1(), + }) + }, + }, + }, + }), + WantTable: new(` + ID NAME DESCRIPTION FEATURES EXPIRATION STATUS + ubuntu-24.04 Ubuntu 24.04 Ubuntu 24.04 LTS machine supported `), }, } diff --git a/tests/e2e/admin/partition_test.go b/tests/e2e/admin/partition_test.go index 2daac45..b34dfca 100644 --- a/tests/e2e/admin/partition_test.go +++ b/tests/e2e/admin/partition_test.go @@ -1,6 +1,7 @@ package admin_e2e import ( + "fmt" "testing" "connectrpc.com/connect" @@ -11,6 +12,8 @@ import ( e2erootcmd "github.com/metal-stack/cli/testing/e2e" "github.com/metal-stack/cli/tests/e2e/testresources" e2e "github.com/metal-stack/metal-lib/pkg/genericcli/e2e" + "github.com/spf13/afero" + "github.com/stretchr/testify/require" ) func Test_AdminPartitionCmd_List(t *testing.T) { @@ -188,3 +191,80 @@ partition-1 size-1 tt.TestCmd(t) } } + +func Test_AdminPartitionCmd_Apply(t *testing.T) { + tests := []*e2e.Test[adminv2.PartitionServiceUpdateResponse, *apiv2.Partition]{ + { + Name: "apply", + CmdArgs: append([]string{"admin", "partition", "apply"}, e2e.AppendFromFileCommonArgs()...), + NewRootCmd: e2erootcmd.NewRootCmd(t, + &e2erootcmd.TestConfig{ + FsMocks: func(fs *afero.Afero) { + require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, testresources.Partition1()), 0755)) + }, + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.PartitionServiceCreateRequest{ + Partition: testresources.Partition1(), + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.PartitionServiceCreateResponse{ + Partition: testresources.Partition1(), + }) + }, + }, + }, + }), + WantTable: new(` + ID DESCRIPTION + partition-1 partition 1 + `), + }, + { + Name: "apply already exists", + CmdArgs: append([]string{"admin", "partition", "apply"}, e2e.AppendFromFileCommonArgs()...), + NewRootCmd: e2erootcmd.NewRootCmd(t, + &e2erootcmd.TestConfig{ + FsMocks: func(fs *afero.Afero) { + require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, testresources.Partition1()), 0755)) + }, + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.PartitionServiceCreateRequest{ + Partition: testresources.Partition1(), + }, + WantError: connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("already exists")), + }, + { + WantRequest: &adminv2.PartitionServiceUpdateRequest{ + Id: testresources.Partition1().Id, + Description: new(testresources.Partition1().Description), + BootConfiguration: testresources.Partition1().BootConfiguration, + MgmtServiceAddresses: testresources.Partition1().MgmtServiceAddresses, + UpdateMeta: &apiv2.UpdateMeta{ + LockingStrategy: apiv2.OptimisticLockingStrategy_OPTIMISTIC_LOCKING_STRATEGY_SERVER, + }, + Labels: &apiv2.UpdateLabels{ + Strategy: &apiv2.UpdateLabels_Replace{ + Replace: testresources.Partition1().Meta.Labels, + }, + }, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.PartitionServiceUpdateResponse{ + Partition: testresources.Partition1(), + }) + }, + }, + }, + }), + WantTable: new(` + ID DESCRIPTION + partition-1 partition 1 + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} diff --git a/tests/e2e/admin/size_test.go b/tests/e2e/admin/size_test.go index 1caaefe..b9ae447 100644 --- a/tests/e2e/admin/size_test.go +++ b/tests/e2e/admin/size_test.go @@ -1,6 +1,7 @@ package admin_e2e import ( + "fmt" "testing" "connectrpc.com/connect" @@ -155,6 +156,83 @@ func Test_AdminSizeCmd_Create(t *testing.T) { } } +func Test_AdminSizeCmd_Apply(t *testing.T) { + tests := []*e2e.Test[adminv2.SizeServiceUpdateResponse, *apiv2.Size]{ + { + Name: "apply", + CmdArgs: append([]string{"admin", "size", "apply"}, e2e.AppendFromFileCommonArgs()...), + NewRootCmd: e2erootcmd.NewRootCmd(t, + &e2erootcmd.TestConfig{ + FsMocks: func(fs *afero.Afero) { + require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, testresources.Size1()), 0755)) + }, + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.SizeServiceCreateRequest{ + Size: testresources.Size1(), + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.SizeServiceCreateResponse{ + Size: testresources.Size1(), + }) + }, + }, + }, + }), + WantTable: new(` + ID NAME DESCRIPTION CPU RANGE MEMORY RANGE STORAGE RANGE GPU RANGE + v1-medium-x86 v1-medium-x86 Virtual size for mini-lab 4 - 4 500 MB - 4.0 GB 1.0 GB - 100 GB + `), + }, + { + Name: "apply already exists", + CmdArgs: append([]string{"admin", "size", "apply"}, e2e.AppendFromFileCommonArgs()...), + NewRootCmd: e2erootcmd.NewRootCmd(t, + &e2erootcmd.TestConfig{ + FsMocks: func(fs *afero.Afero) { + require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, testresources.Size1()), 0755)) + }, + ClientCalls: []client.ClientCall{ + { + WantRequest: &adminv2.SizeServiceCreateRequest{ + Size: testresources.Size1(), + }, + WantError: connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("already exists")), + }, + { + WantRequest: &adminv2.SizeServiceUpdateRequest{ + Id: testresources.Size1().Id, + Name: testresources.Size1().Name, + Description: testresources.Size1().Description, + UpdateMeta: &apiv2.UpdateMeta{ + LockingStrategy: apiv2.OptimisticLockingStrategy_OPTIMISTIC_LOCKING_STRATEGY_SERVER, + }, + Labels: &apiv2.UpdateLabels{ + Strategy: &apiv2.UpdateLabels_Replace{ + Replace: testresources.Size1().Meta.Labels, + }, + }, + Constraints: testresources.Size1().Constraints, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&adminv2.SizeServiceUpdateResponse{ + Size: testresources.Size1(), + }) + }, + }, + }, + }), + WantTable: new(` + ID NAME DESCRIPTION CPU RANGE MEMORY RANGE STORAGE RANGE GPU RANGE + v1-medium-x86 v1-medium-x86 Virtual size for mini-lab 4 - 4 500 MB - 4.0 GB 1.0 GB - 100 GB + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + func Test_AdminSizeCmd_Update(t *testing.T) { tests := []*e2e.Test[adminv2.SizeServiceUpdateResponse, *apiv2.Size]{ { From 7ba36c451c369ca3f8a81196c7ad29ed8bc145b2 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 29 Sep 2026 16:20:51 +0200 Subject: [PATCH 06/15] Rework members. --- cmd/api/v2/project.go | 115 +-------- cmd/api/v2/project_member.go | 219 ++++++++++++++++ cmd/api/v2/tenant.go | 130 +--------- cmd/api/v2/tenant_member.go | 234 ++++++++++++++++++ cmd/sorters/project.go | 21 +- cmd/sorters/tenant.go | 17 +- cmd/tableprinters/common.go | 9 +- cmd/tableprinters/project.go | 11 +- cmd/tableprinters/tenant.go | 9 +- docs/admin/metalctlv2_admin_partition.md | 1 + .../admin/metalctlv2_admin_partition_apply.md | 46 ++++ docs/metalctlv2_project.md | 2 +- docs/metalctlv2_project_member.md | 16 +- docs/metalctlv2_project_member_apply.md | 46 ++++ docs/metalctlv2_project_member_create.md | 47 ++++ docs/metalctlv2_project_member_delete.md | 25 +- docs/metalctlv2_project_member_describe.md | 32 +++ docs/metalctlv2_project_member_edit.md | 32 +++ docs/metalctlv2_project_member_list.md | 7 +- docs/metalctlv2_project_member_update.md | 27 +- docs/metalctlv2_tenant.md | 2 +- docs/metalctlv2_tenant_member.md | 16 +- docs/metalctlv2_tenant_member_apply.md | 46 ++++ docs/metalctlv2_tenant_member_create.md | 47 ++++ docs/metalctlv2_tenant_member_delete.md | 47 ++++ ...d => metalctlv2_tenant_member_describe.md} | 12 +- docs/metalctlv2_tenant_member_edit.md | 32 +++ docs/metalctlv2_tenant_member_list.md | 7 +- docs/metalctlv2_tenant_member_update.md | 27 +- go.mod | 2 +- go.sum | 4 +- tests/e2e/api/project_test.go | 26 +- tests/e2e/api/tenant_test.go | 119 +++++++-- tests/e2e/testresources/projects.go | 4 +- tests/e2e/testresources/tenants.go | 4 +- 35 files changed, 1093 insertions(+), 348 deletions(-) create mode 100644 cmd/api/v2/project_member.go create mode 100644 cmd/api/v2/tenant_member.go create mode 100644 docs/admin/metalctlv2_admin_partition_apply.md create mode 100644 docs/metalctlv2_project_member_apply.md create mode 100644 docs/metalctlv2_project_member_create.md create mode 100644 docs/metalctlv2_project_member_describe.md create mode 100644 docs/metalctlv2_project_member_edit.md create mode 100644 docs/metalctlv2_tenant_member_apply.md create mode 100644 docs/metalctlv2_tenant_member_create.md create mode 100644 docs/metalctlv2_tenant_member_delete.md rename docs/{metalctlv2_tenant_member_remove.md => metalctlv2_tenant_member_describe.md} (76%) create mode 100644 docs/metalctlv2_tenant_member_edit.md diff --git a/cmd/api/v2/project.go b/cmd/api/v2/project.go index 2f4f398..a81acc5 100644 --- a/cmd/api/v2/project.go +++ b/cmd/api/v2/project.go @@ -114,57 +114,7 @@ func newProjectCmd(c *config.Config) *cobra.Command { inviteCmd.AddCommand(generateInviteCmd, deleteInviteCmd, listInvitesCmd, joinProjectCmd) - memberCmd := &cobra.Command{ - Use: "member", - Aliases: []string{"members"}, - Short: "manage project members", - } - - listMembersCmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "lists members of a project", - RunE: func(cmd *cobra.Command, args []string) error { - return w.listMembers() - }, - } - - listMembersCmd.Flags().StringP("project", "p", "", "the project of which to list the members") - - genericcli.AddSortFlag(listMembersCmd, sorters.ProjectMemberSorter()) - - removeMemberCmd := &cobra.Command{ - Use: "delete ", - Aliases: []string{"destroy", "rm", "remove"}, - Short: "remove member from a project", - RunE: func(cmd *cobra.Command, args []string) error { - return w.removeMember(args) - }, - ValidArgsFunction: c.Completion.ProjectMember, - } - - removeMemberCmd.Flags().StringP("project", "p", "", "the project in which to remove the member") - - genericcli.Must(removeMemberCmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) - - updateMemberCmd := &cobra.Command{ - Use: "update ", - Short: "update member from a project", - RunE: func(cmd *cobra.Command, args []string) error { - return w.updateMember(args) - }, - ValidArgsFunction: c.Completion.ProjectMember, - } - - updateMemberCmd.Flags().StringP("project", "p", "", "the project in which to remove the member") - updateMemberCmd.Flags().String("role", "", "the role of the member") - - genericcli.Must(updateMemberCmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) - genericcli.Must(updateMemberCmd.RegisterFlagCompletionFunc("role", c.Completion.ProjectRole)) - - memberCmd.AddCommand(removeMemberCmd, updateMemberCmd, listMembersCmd) - - return genericcli.NewCmds(cmdsConfig, joinProjectCmd, inviteCmd, memberCmd) + return genericcli.NewCmds(cmdsConfig, joinProjectCmd, inviteCmd, newProjectMemberCmd(c)) } func (c *project) Get(id string) (*apiv2.Project, error) { @@ -409,66 +359,3 @@ func (c *project) deleteInvite(args []string) error { return nil } - -func (c *project) removeMember(args []string) error { - member, err := genericcli.GetExactlyOneArg(args) - if err != nil { - return err - } - - ctx, cancel := c.c.NewRequestContext() - defer cancel() - - _, err = c.c.Client.Apiv2().Project().RemoveMember(ctx, &apiv2.ProjectServiceRemoveMemberRequest{ - Project: c.c.GetProject(), - Member: member, - }) - if err != nil { - return fmt.Errorf("failed to remove member from project: %w", err) - } - - _, _ = fmt.Fprintf(c.c.Out, "%s successfully removed member %q\n", color.GreenString("✔"), member) - - return nil -} - -func (c *project) updateMember(args []string) error { - member, err := genericcli.GetExactlyOneArg(args) - if err != nil { - return err - } - - ctx, cancel := c.c.NewRequestContext() - defer cancel() - - resp, err := c.c.Client.Apiv2().Project().UpdateMember(ctx, &apiv2.ProjectServiceUpdateMemberRequest{ - Project: c.c.GetProject(), - Member: member, - Role: apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")]), - }) - if err != nil { - return fmt.Errorf("failed to update member: %w", err) - } - - return c.c.DescribePrinter.Print(resp.GetProjectMember()) -} - -func (c *project) listMembers() error { - ctx, cancel := c.c.NewRequestContext() - defer cancel() - - resp, err := c.c.Client.Apiv2().Project().Get(ctx, &apiv2.ProjectServiceGetRequest{ - Project: c.c.GetProject(), - }) - if err != nil { - return fmt.Errorf("failed to get project: %w", err) - } - - members := resp.GetProjectMembers() - - if err := sorters.ProjectMemberSorter().SortBy(members); err != nil { - return err - } - - return c.c.ListPrinter.Print(members) -} diff --git a/cmd/api/v2/project_member.go b/cmd/api/v2/project_member.go new file mode 100644 index 0000000..55ac197 --- /dev/null +++ b/cmd/api/v2/project_member.go @@ -0,0 +1,219 @@ +package v2 + +import ( + "fmt" + + "github.com/metal-stack/api/go/errorutil" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" + "github.com/metal-stack/cli/cmd/config" + "github.com/metal-stack/cli/cmd/sorters" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type projectMember struct { + c *config.Config +} + +func newProjectMemberCmd(c *config.Config) *cobra.Command { + w := &projectMember{ + c: c, + } + + cmdsConfig := &genericcli.CmdsConfig[*apiv2.ProjectServiceAddMemberRequest, *apiv2.ProjectServiceUpdateMemberRequest, *cliv2.ProjectMember]{ + BinaryName: config.BinaryName, + GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), + Singular: "member", + Plural: "members", + Description: "manage api project members", + Sorter: sorters.ProjectMemberSorter(), + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + DescribeCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project to describe the project members of, defaults to project of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + }, + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project to list the project members of, defaults to project of the default project") + cmd.Flags().String("role", "", "the role of the member") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.ProjectRole)) + }, + CreateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project for which to create a project member, defaults to project of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + }, + UpdateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project for which to update a project member, defaults to project of the default project") + cmd.Flags().String("role", "", "the role of the member") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.ProjectRole)) + + }, + DeleteCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project from which to delete a project member, defaults to project of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + }, + EditCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project from which to edit a project member, defaults to project of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + }, + CreateRequestFromCLI: w.createRequestFromCLI, + UpdateRequestFromCLI: w.updateRequestFromCLI, + ValidArgsFn: w.c.Completion.ProjectMember, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *projectMember) Get(id string) (*cliv2.ProjectMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + project := c.c.GetProject() + + resp, err := c.c.Client.Apiv2().Project().Get(ctx, &apiv2.ProjectServiceGetRequest{ + Project: project, + }) + if err != nil { + return nil, fmt.Errorf("failed to get project: %w", err) + } + + for _, member := range resp.GetProjectMembers() { + if member.Id == id { + return &cliv2.ProjectMember{ + Project: project, + ProjectMember: member, + }, nil + } + } + + return nil, fmt.Errorf("unable to find project members %q in project %q", id, project) +} + +func (c *projectMember) List() ([]*cliv2.ProjectMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + project := c.c.GetProject() + + resp, err := c.c.Client.Apiv2().Project().Get(ctx, &apiv2.ProjectServiceGetRequest{ + Project: project, + }) + if err != nil { + return nil, fmt.Errorf("failed to get project: %w", err) + } + + var res []*cliv2.ProjectMember + + for _, member := range resp.GetProjectMembers() { + res = append(res, &cliv2.ProjectMember{ + Project: project, + ProjectMember: member, + }) + } + + return res, nil +} + +func (c *projectMember) Create(rq *apiv2.ProjectServiceAddMemberRequest) (*cliv2.ProjectMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Project().AddMember(ctx, rq) + if err != nil { + if errorutil.IsConflict(err) { + return nil, genericcli.AlreadyExistsError() + } + + return nil, err + } + + return &cliv2.ProjectMember{ + Project: rq.Project, + ProjectMember: resp.ProjectMember, + }, nil +} + +func (c *projectMember) Delete(id string) (*cliv2.ProjectMember, error) { + project := c.c.GetProject() + + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Project().RemoveMember(ctx, &apiv2.ProjectServiceRemoveMemberRequest{ + Project: project, + Member: id, + }) + if err != nil { + return nil, fmt.Errorf("failed to remove member from project: %w", err) + } + + return &cliv2.ProjectMember{ + Project: project, + ProjectMember: resp.ProjectMember, + }, nil +} + +func (c *projectMember) Convert(r *cliv2.ProjectMember) (string, *apiv2.ProjectServiceAddMemberRequest, *apiv2.ProjectServiceUpdateMemberRequest, error) { + return r.ProjectMember.Id, &apiv2.ProjectServiceAddMemberRequest{ + Project: r.Project, + Member: r.ProjectMember.Id, + Role: r.ProjectMember.Role, + }, + &apiv2.ProjectServiceUpdateMemberRequest{ + Project: r.Project, + Member: r.ProjectMember.Id, + Role: r.ProjectMember.Role, + }, + nil +} + +func (c *projectMember) Update(rq *apiv2.ProjectServiceUpdateMemberRequest) (*cliv2.ProjectMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Project().UpdateMember(ctx, rq) + if err != nil { + return nil, fmt.Errorf("failed to update member: %w", err) + } + + return &cliv2.ProjectMember{ + Project: rq.Project, + ProjectMember: resp.ProjectMember, + }, nil +} + +func (c *projectMember) updateRequestFromCLI(args []string) (*apiv2.ProjectServiceUpdateMemberRequest, error) { + member, err := genericcli.GetExactlyOneArg(args) + if err != nil { + return nil, err + } + + project := c.c.GetProject() + + return &apiv2.ProjectServiceUpdateMemberRequest{ + Project: project, + Member: member, + Role: apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")]), + }, nil +} + +func (c *projectMember) createRequestFromCLI() (*apiv2.ProjectServiceAddMemberRequest, error) { + project := c.c.GetProject() + + return &apiv2.ProjectServiceAddMemberRequest{ + Project: project, + Member: viper.GetString("id"), + Role: apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")]), + }, nil +} diff --git a/cmd/api/v2/tenant.go b/cmd/api/v2/tenant.go index 8932e23..ef14878 100644 --- a/cmd/api/v2/tenant.go +++ b/cmd/api/v2/tenant.go @@ -111,59 +111,9 @@ func newTenantCmd(c *config.Config) *cobra.Command { }, } - memberCmd := &cobra.Command{ - Use: "member", - Aliases: []string{"members"}, - Short: "manage tenant members", - } - - listMembersCmd := &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "lists members of a tenant", - RunE: func(cmd *cobra.Command, args []string) error { - return w.listMembers() - }, - } - - listMembersCmd.Flags().String("tenant", "", "the tenant in which to remove the member") - - genericcli.AddSortFlag(listMembersCmd, sorters.TenantMemberSorter()) - - removeMemberCmd := &cobra.Command{ - Use: "remove ", - Short: "remove member from a tenant", - Aliases: []string{"destroy", "rm", "remove"}, - RunE: func(cmd *cobra.Command, args []string) error { - return w.removeMember(args) - }, - ValidArgsFunction: c.Completion.TenantMember, - } - - removeMemberCmd.Flags().String("tenant", "", "the tenant in which to remove the member") - - genericcli.Must(removeMemberCmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) - - updateMemberCmd := &cobra.Command{ - Use: "update ", - Short: "update member from a tenant", - RunE: func(cmd *cobra.Command, args []string) error { - return w.updateMember(args) - }, - ValidArgsFunction: c.Completion.TenantMember, - } - - updateMemberCmd.Flags().String("tenant", "", "the tenant in which to remove the member") - updateMemberCmd.Flags().String("role", "", "the role of the member") - - genericcli.Must(updateMemberCmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) - genericcli.Must(updateMemberCmd.RegisterFlagCompletionFunc("role", c.Completion.TenantRole)) - - memberCmd.AddCommand(removeMemberCmd, updateMemberCmd, listMembersCmd) - inviteCmd.AddCommand(generateInviteCmd, deleteInviteCmd, listInvitesCmd, joinTenantCmd) - return genericcli.NewCmds(cmdsConfig, joinTenantCmd, inviteCmd, memberCmd) + return genericcli.NewCmds(cmdsConfig, joinTenantCmd, inviteCmd, newTenantMemberCmd(c)) } func (c *tenant) Get(id string) (*apiv2.Tenant, error) { @@ -398,81 +348,3 @@ func (c *tenant) deleteInvite(args []string) error { return nil } - -func (c *tenant) removeMember(args []string) error { - member, err := genericcli.GetExactlyOneArg(args) - if err != nil { - return err - } - - tenant, err := c.c.GetTenant() - if err != nil { - return err - } - - ctx, cancel := c.c.NewRequestContext() - defer cancel() - - _, err = c.c.Client.Apiv2().Tenant().RemoveMember(ctx, &apiv2.TenantServiceRemoveMemberRequest{ - Login: tenant, - Member: member, - }) - if err != nil { - return fmt.Errorf("failed to remove member from tenant: %w", err) - } - - _, _ = fmt.Fprintf(c.c.Out, "%s successfully removed member %q\n", color.GreenString("✔"), member) - - return nil -} - -func (c *tenant) updateMember(args []string) error { - member, err := genericcli.GetExactlyOneArg(args) - if err != nil { - return err - } - - tenant, err := c.c.GetTenant() - if err != nil { - return err - } - - ctx, cancel := c.c.NewRequestContext() - defer cancel() - - resp, err := c.c.Client.Apiv2().Tenant().UpdateMember(ctx, &apiv2.TenantServiceUpdateMemberRequest{ - Login: tenant, - Member: member, - Role: apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")]), - }) - if err != nil { - return fmt.Errorf("failed to update member: %w", err) - } - - return c.c.DescribePrinter.Print(resp.GetTenantMember()) -} - -func (c *tenant) listMembers() error { - ctx, cancel := c.c.NewRequestContext() - defer cancel() - - tenant, err := c.c.GetTenant() - if err != nil { - return err - } - - resp, err := c.c.Client.Apiv2().Tenant().Get(ctx, &apiv2.TenantServiceGetRequest{ - Login: tenant, - }) - if err != nil { - return fmt.Errorf("failed to get tenant: %w", err) - } - - members := resp.GetTenantMembers() - - if err := sorters.TenantMemberSorter().SortBy(members); err != nil { - return err - } - - return c.c.ListPrinter.Print(members) -} diff --git a/cmd/api/v2/tenant_member.go b/cmd/api/v2/tenant_member.go new file mode 100644 index 0000000..da6246e --- /dev/null +++ b/cmd/api/v2/tenant_member.go @@ -0,0 +1,234 @@ +package v2 + +import ( + "fmt" + + "github.com/metal-stack/api/go/errorutil" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" + "github.com/metal-stack/cli/cmd/config" + "github.com/metal-stack/cli/cmd/sorters" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type tenantMember struct { + c *config.Config +} + +func newTenantMemberCmd(c *config.Config) *cobra.Command { + w := &tenantMember{ + c: c, + } + + cmdsConfig := &genericcli.CmdsConfig[*apiv2.TenantServiceAddMemberRequest, *apiv2.TenantServiceUpdateMemberRequest, *cliv2.TenantMember]{ + BinaryName: config.BinaryName, + GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), + Singular: "member", + Plural: "members", + Description: "manage api tenant members", + Sorter: sorters.TenantMemberSorter(), + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + DescribeCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant to describe the tenant members of, defaults to tenant of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + }, + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant to list the tenant members of, defaults to tenant of the default project") + cmd.Flags().String("role", "", "the role of the member") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.TenantRole)) + }, + CreateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant for which to create a tenant member, defaults to tenant of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + }, + UpdateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant for which to update a tenant member, defaults to tenant of the default project") + cmd.Flags().String("role", "", "the role of the member") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.TenantRole)) + + }, + DeleteCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant from which to delete a tenant member, defaults to tenant of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + }, + EditCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant from which to edit a tenant member, defaults to tenant of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + }, + CreateRequestFromCLI: w.createRequestFromCLI, + UpdateRequestFromCLI: w.updateRequestFromCLI, + ValidArgsFn: w.c.Completion.TenantMember, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *tenantMember) Get(id string) (*cliv2.TenantMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + resp, err := c.c.Client.Apiv2().Tenant().Get(ctx, &apiv2.TenantServiceGetRequest{ + Login: tenant, + }) + if err != nil { + return nil, fmt.Errorf("failed to get tenant: %w", err) + } + + for _, member := range resp.GetTenantMembers() { + if member.Id == id { + return &cliv2.TenantMember{ + Tenant: tenant, + TenantMember: member, + }, nil + } + } + + return nil, fmt.Errorf("unable to find tenant members %q in tenant %q", id, tenant) +} + +func (c *tenantMember) List() ([]*cliv2.TenantMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + resp, err := c.c.Client.Apiv2().Tenant().Get(ctx, &apiv2.TenantServiceGetRequest{ + Login: tenant, + }) + if err != nil { + return nil, fmt.Errorf("failed to get tenant: %w", err) + } + + var res []*cliv2.TenantMember + + for _, member := range resp.GetTenantMembers() { + res = append(res, &cliv2.TenantMember{ + Tenant: tenant, + TenantMember: member, + }) + } + + return res, nil +} + +func (c *tenantMember) Create(rq *apiv2.TenantServiceAddMemberRequest) (*cliv2.TenantMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Tenant().AddMember(ctx, rq) + if err != nil { + if errorutil.IsConflict(err) { + return nil, genericcli.AlreadyExistsError() + } + + return nil, err + } + + return &cliv2.TenantMember{ + Tenant: rq.Login, + TenantMember: resp.TenantMember, + }, nil +} + +func (c *tenantMember) Delete(id string) (*cliv2.TenantMember, error) { + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Tenant().RemoveMember(ctx, &apiv2.TenantServiceRemoveMemberRequest{ + Login: tenant, + Member: id, + }) + if err != nil { + return nil, fmt.Errorf("failed to remove member from tenant: %w", err) + } + + return &cliv2.TenantMember{ + Tenant: tenant, + TenantMember: resp.TenantMember, + }, nil +} + +func (c *tenantMember) Convert(r *cliv2.TenantMember) (string, *apiv2.TenantServiceAddMemberRequest, *apiv2.TenantServiceUpdateMemberRequest, error) { + return r.TenantMember.Id, &apiv2.TenantServiceAddMemberRequest{ + Login: r.Tenant, + Member: r.TenantMember.Id, + Role: r.TenantMember.Role, + }, + &apiv2.TenantServiceUpdateMemberRequest{ + Login: r.Tenant, + Member: r.TenantMember.Id, + Role: r.TenantMember.Role, + }, + nil +} + +func (c *tenantMember) Update(rq *apiv2.TenantServiceUpdateMemberRequest) (*cliv2.TenantMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Tenant().UpdateMember(ctx, rq) + if err != nil { + return nil, fmt.Errorf("failed to update member: %w", err) + } + + return &cliv2.TenantMember{ + Tenant: rq.Login, + TenantMember: resp.TenantMember, + }, nil +} + +func (c *tenantMember) updateRequestFromCLI(args []string) (*apiv2.TenantServiceUpdateMemberRequest, error) { + member, err := genericcli.GetExactlyOneArg(args) + if err != nil { + return nil, err + } + + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + return &apiv2.TenantServiceUpdateMemberRequest{ + Login: tenant, + Member: member, + Role: apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")]), + }, nil +} + +func (c *tenantMember) createRequestFromCLI() (*apiv2.TenantServiceAddMemberRequest, error) { + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + return &apiv2.TenantServiceAddMemberRequest{ + Login: tenant, + Member: viper.GetString("id"), + Role: apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")]), + }, nil +} diff --git a/cmd/sorters/project.go b/cmd/sorters/project.go index f80487c..70fcaee 100644 --- a/cmd/sorters/project.go +++ b/cmd/sorters/project.go @@ -2,6 +2,7 @@ package sorters import ( apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" "github.com/metal-stack/metal-lib/pkg/multisort" ) @@ -36,25 +37,25 @@ func ProjectInviteSorter() *multisort.Sorter[*apiv2.ProjectInvite] { }, multisort.Keys{{ID: "project"}, {ID: "role"}, {ID: "expiration"}}) } -func ProjectMemberSorter() *multisort.Sorter[*apiv2.ProjectMember] { - return multisort.New(multisort.FieldMap[*apiv2.ProjectMember]{ - "id": func(a, b *apiv2.ProjectMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.Id, b.Id, descending) +func ProjectMemberSorter() *multisort.Sorter[*cliv2.ProjectMember] { + return multisort.New(multisort.FieldMap[*cliv2.ProjectMember]{ + "id": func(a, b *cliv2.ProjectMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.ProjectMember.Id, b.ProjectMember.Id, descending) }, - "role": func(a, b *apiv2.ProjectMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.Role, b.Role, descending) + "role": func(a, b *cliv2.ProjectMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.ProjectMember.Role, b.ProjectMember.Role, descending) }, - "created": func(a, b *apiv2.ProjectMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.CreatedAt.AsTime().UnixMilli(), b.CreatedAt.AsTime().UnixMilli(), descending) + "created": func(a, b *cliv2.ProjectMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.ProjectMember.CreatedAt.AsTime().UnixMilli(), b.ProjectMember.CreatedAt.AsTime().UnixMilli(), descending) }, - "inherited": func(a, b *apiv2.ProjectMember, descending bool) multisort.CompareResult { + "inherited": func(a, b *cliv2.ProjectMember, descending bool) multisort.CompareResult { boolToInt := func(in bool) int { if in { return 1 } return 0 } - return multisort.Compare(boolToInt(a.InheritedMembership), boolToInt(b.InheritedMembership), descending) + return multisort.Compare(boolToInt(a.ProjectMember.InheritedMembership), boolToInt(b.ProjectMember.InheritedMembership), descending) }, }, multisort.Keys{{ID: "inherited", Descending: false}, {ID: "role"}, {ID: "id"}}) } diff --git a/cmd/sorters/tenant.go b/cmd/sorters/tenant.go index 159bb63..6e8554f 100644 --- a/cmd/sorters/tenant.go +++ b/cmd/sorters/tenant.go @@ -2,6 +2,7 @@ package sorters import ( apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" "github.com/metal-stack/metal-lib/pkg/multisort" ) @@ -36,16 +37,16 @@ func TenantInviteSorter() *multisort.Sorter[*apiv2.TenantInvite] { }, multisort.Keys{{ID: "tenant"}, {ID: "role"}, {ID: "expiration"}}) } -func TenantMemberSorter() *multisort.Sorter[*apiv2.TenantMember] { - return multisort.New(multisort.FieldMap[*apiv2.TenantMember]{ - "id": func(a, b *apiv2.TenantMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.Id, b.Id, descending) +func TenantMemberSorter() *multisort.Sorter[*cliv2.TenantMember] { + return multisort.New(multisort.FieldMap[*cliv2.TenantMember]{ + "id": func(a, b *cliv2.TenantMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.TenantMember.Id, b.TenantMember.Id, descending) }, - "role": func(a, b *apiv2.TenantMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.Role, b.Role, descending) + "role": func(a, b *cliv2.TenantMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.TenantMember.Role, b.TenantMember.Role, descending) }, - "created": func(a, b *apiv2.TenantMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.CreatedAt.AsTime().UnixMilli(), b.CreatedAt.AsTime().UnixMilli(), descending) + "created": func(a, b *cliv2.TenantMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.TenantMember.CreatedAt.AsTime().UnixMilli(), b.TenantMember.CreatedAt.AsTime().UnixMilli(), descending) }, }, multisort.Keys{{ID: "role"}, {ID: "id"}}) } diff --git a/cmd/tableprinters/common.go b/cmd/tableprinters/common.go index f7c820f..fdbfac2 100644 --- a/cmd/tableprinters/common.go +++ b/cmd/tableprinters/common.go @@ -8,6 +8,7 @@ import ( adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" "github.com/metal-stack/cli/cmd/config" "github.com/metal-stack/metal-lib/pkg/genericcli/printers" "github.com/metal-stack/metal-lib/pkg/pointer" @@ -93,9 +94,9 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin return t.ProjectInviteTable(pointer.WrapInSlice(d), wide) case []*apiv2.ProjectInvite: return t.ProjectInviteTable(d, wide) - case *apiv2.ProjectMember: + case *cliv2.ProjectMember: return t.ProjectMemberTable(pointer.WrapInSlice(d), wide) - case []*apiv2.ProjectMember: + case []*cliv2.ProjectMember: return t.ProjectMemberTable(d, wide) case *adminv2.TaskInfo: @@ -127,9 +128,9 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin return t.TenantInviteTable(pointer.WrapInSlice(d), wide) case []*apiv2.TenantInvite: return t.TenantInviteTable(d, wide) - case *apiv2.TenantMember: + case *cliv2.TenantMember: return t.TenantMemberTable(pointer.WrapInSlice(d), wide) - case []*apiv2.TenantMember: + case []*cliv2.TenantMember: return t.TenantMemberTable(d, wide) case *apiv2.Health: diff --git a/cmd/tableprinters/project.go b/cmd/tableprinters/project.go index 27e893a..c34fbb3 100644 --- a/cmd/tableprinters/project.go +++ b/cmd/tableprinters/project.go @@ -6,6 +6,7 @@ import ( "github.com/dustin/go-humanize" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" "github.com/metal-stack/metal-lib/pkg/genericcli" ) @@ -54,7 +55,7 @@ func (t *TablePrinter) ProjectInviteTable(data []*apiv2.ProjectInvite, _ bool) ( return header, rows, nil } -func (t *TablePrinter) ProjectMemberTable(data []*apiv2.ProjectMember, _ bool) ([]string, [][]string, error) { +func (t *TablePrinter) ProjectMemberTable(data []*cliv2.ProjectMember, _ bool) ([]string, [][]string, error) { var ( rows [][]string ) @@ -62,10 +63,10 @@ func (t *TablePrinter) ProjectMemberTable(data []*apiv2.ProjectMember, _ bool) ( for _, member := range data { row := []string{ - member.Id, - member.Role.String(), - strconv.FormatBool(member.InheritedMembership), - humanize.Time(member.CreatedAt.AsTime()), + member.ProjectMember.Id, + member.ProjectMember.Role.String(), + strconv.FormatBool(member.ProjectMember.InheritedMembership), + humanize.Time(member.ProjectMember.CreatedAt.AsTime()), } rows = append(rows, row) diff --git a/cmd/tableprinters/tenant.go b/cmd/tableprinters/tenant.go index 5e26223..6778559 100644 --- a/cmd/tableprinters/tenant.go +++ b/cmd/tableprinters/tenant.go @@ -4,6 +4,7 @@ import ( "github.com/dustin/go-humanize" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" ) func (t *TablePrinter) TenantTable(data []*apiv2.Tenant, wide bool) ([]string, [][]string, error) { @@ -24,7 +25,7 @@ func (t *TablePrinter) TenantTable(data []*apiv2.Tenant, wide bool) ([]string, [ return header, rows, nil } -func (t *TablePrinter) TenantMemberTable(data []*apiv2.TenantMember, _ bool) ([]string, [][]string, error) { +func (t *TablePrinter) TenantMemberTable(data []*cliv2.TenantMember, _ bool) ([]string, [][]string, error) { var ( rows [][]string ) @@ -32,9 +33,9 @@ func (t *TablePrinter) TenantMemberTable(data []*apiv2.TenantMember, _ bool) ([] for _, member := range data { row := []string{ - member.Id, - member.Role.String(), - humanize.Time(member.CreatedAt.AsTime()), + member.TenantMember.Id, + member.TenantMember.Role.String(), + humanize.Time(member.TenantMember.CreatedAt.AsTime()), } rows = append(rows, row) diff --git a/docs/admin/metalctlv2_admin_partition.md b/docs/admin/metalctlv2_admin_partition.md index c76bfe4..5792597 100644 --- a/docs/admin/metalctlv2_admin_partition.md +++ b/docs/admin/metalctlv2_admin_partition.md @@ -28,6 +28,7 @@ manage partitions ### SEE ALSO * [metalctlv2 admin](metalctlv2_admin.md) - admin commands +* [metalctlv2 admin partition apply](metalctlv2_admin_partition_apply.md) - applies one or more partitions from a given file * [metalctlv2 admin partition capacity](metalctlv2_admin_partition_capacity.md) - show partition capacity * [metalctlv2 admin partition create](metalctlv2_admin_partition_create.md) - creates the partition * [metalctlv2 admin partition delete](metalctlv2_admin_partition_delete.md) - deletes the partition diff --git a/docs/admin/metalctlv2_admin_partition_apply.md b/docs/admin/metalctlv2_admin_partition_apply.md new file mode 100644 index 0000000..e96d77b --- /dev/null +++ b/docs/admin/metalctlv2_admin_partition_apply.md @@ -0,0 +1,46 @@ +## metalctlv2 admin partition apply + +applies one or more partitions from a given file + +``` +metalctlv2 admin partition 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 partition describe partition-1 -o yaml > partition.yaml + $ vi partition.yaml + $ # either via stdin + $ cat partition.yaml | metalctlv2 partition apply -f - + $ # or via file + $ metalctlv2 partition apply -f partition.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 partition](metalctlv2_admin_partition.md) - manage partition entities + diff --git a/docs/metalctlv2_project.md b/docs/metalctlv2_project.md index d0c1aa6..a3b7b4f 100644 --- a/docs/metalctlv2_project.md +++ b/docs/metalctlv2_project.md @@ -36,6 +36,6 @@ manage api projects * [metalctlv2 project invite](metalctlv2_project_invite.md) - manage project invites * [metalctlv2 project join](metalctlv2_project_join.md) - join a project of someone who shared an invite secret with you * [metalctlv2 project list](metalctlv2_project_list.md) - list all projects -* [metalctlv2 project member](metalctlv2_project_member.md) - manage project members +* [metalctlv2 project member](metalctlv2_project_member.md) - manage member entities * [metalctlv2 project update](metalctlv2_project_update.md) - updates the project diff --git a/docs/metalctlv2_project_member.md b/docs/metalctlv2_project_member.md index 88bc350..8a9553f 100644 --- a/docs/metalctlv2_project_member.md +++ b/docs/metalctlv2_project_member.md @@ -1,6 +1,10 @@ ## metalctlv2 project member -manage project members +manage member entities + +### Synopsis + +manage api project members ### Options @@ -24,7 +28,11 @@ manage project members ### SEE ALSO * [metalctlv2 project](metalctlv2_project.md) - manage project entities -* [metalctlv2 project member delete](metalctlv2_project_member_delete.md) - remove member from a project -* [metalctlv2 project member list](metalctlv2_project_member_list.md) - lists members of a project -* [metalctlv2 project member update](metalctlv2_project_member_update.md) - update member from a project +* [metalctlv2 project member apply](metalctlv2_project_member_apply.md) - applies one or more members from a given file +* [metalctlv2 project member create](metalctlv2_project_member_create.md) - creates the member +* [metalctlv2 project member delete](metalctlv2_project_member_delete.md) - deletes the member +* [metalctlv2 project member describe](metalctlv2_project_member_describe.md) - describes the member +* [metalctlv2 project member edit](metalctlv2_project_member_edit.md) - edit the member through an editor and update +* [metalctlv2 project member list](metalctlv2_project_member_list.md) - list all members +* [metalctlv2 project member update](metalctlv2_project_member_update.md) - updates the member diff --git a/docs/metalctlv2_project_member_apply.md b/docs/metalctlv2_project_member_apply.md new file mode 100644 index 0000000..39d11ee --- /dev/null +++ b/docs/metalctlv2_project_member_apply.md @@ -0,0 +1,46 @@ +## metalctlv2 project member apply + +applies one or more members from a given file + +``` +metalctlv2 project member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member apply -f - + $ # or via file + $ metalctlv2 member apply -f member.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 project member](metalctlv2_project_member.md) - manage member entities + diff --git a/docs/metalctlv2_project_member_create.md b/docs/metalctlv2_project_member_create.md new file mode 100644 index 0000000..819c7d9 --- /dev/null +++ b/docs/metalctlv2_project_member_create.md @@ -0,0 +1,47 @@ +## metalctlv2 project member create + +creates the member + +``` +metalctlv2 project member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member create -f - + $ # or via file + $ metalctlv2 member create -f member.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for create + --project string the project for which to create a project member, defaults to project of the default project + --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 project member](metalctlv2_project_member.md) - manage member entities + diff --git a/docs/metalctlv2_project_member_delete.md b/docs/metalctlv2_project_member_delete.md index c7c2aae..049d2a9 100644 --- a/docs/metalctlv2_project_member_delete.md +++ b/docs/metalctlv2_project_member_delete.md @@ -1,16 +1,31 @@ ## metalctlv2 project member delete -remove member from a project +deletes the member ``` -metalctlv2 project member delete [flags] +metalctlv2 project member delete [flags] ``` ### Options ``` - -h, --help help for delete - -p, --project string the project in which to remove the member + --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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member delete -f - + $ # or via file + $ metalctlv2 member delete -f member.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for delete + --project string the project from which to delete a project member, defaults to project of the default project + --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 @@ -28,5 +43,5 @@ metalctlv2 project member delete [flags] ### SEE ALSO -* [metalctlv2 project member](metalctlv2_project_member.md) - manage project members +* [metalctlv2 project member](metalctlv2_project_member.md) - manage member entities diff --git a/docs/metalctlv2_project_member_describe.md b/docs/metalctlv2_project_member_describe.md new file mode 100644 index 0000000..099b4f3 --- /dev/null +++ b/docs/metalctlv2_project_member_describe.md @@ -0,0 +1,32 @@ +## metalctlv2 project member describe + +describes the member + +``` +metalctlv2 project member describe [flags] +``` + +### Options + +``` + -h, --help help for describe + --project string the project to describe the project members of, defaults to project of the default project +``` + +### 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 project member](metalctlv2_project_member.md) - manage member entities + diff --git a/docs/metalctlv2_project_member_edit.md b/docs/metalctlv2_project_member_edit.md new file mode 100644 index 0000000..cd2e095 --- /dev/null +++ b/docs/metalctlv2_project_member_edit.md @@ -0,0 +1,32 @@ +## metalctlv2 project member edit + +edit the member through an editor and update + +``` +metalctlv2 project member edit [flags] +``` + +### Options + +``` + -h, --help help for edit + --project string the project from which to edit a project member, defaults to project of the default project +``` + +### 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 project member](metalctlv2_project_member.md) - manage member entities + diff --git a/docs/metalctlv2_project_member_list.md b/docs/metalctlv2_project_member_list.md index e1ce860..c54b928 100644 --- a/docs/metalctlv2_project_member_list.md +++ b/docs/metalctlv2_project_member_list.md @@ -1,6 +1,6 @@ ## metalctlv2 project member list -lists members of a project +list all members ``` metalctlv2 project member list [flags] @@ -10,7 +10,8 @@ metalctlv2 project member list [flags] ``` -h, --help help for list - -p, --project string the project of which to list the members + --project string the project to list the project members of, defaults to project of the default project + --role string the role of the member --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: created|id|inherited|role ``` @@ -29,5 +30,5 @@ metalctlv2 project member list [flags] ### SEE ALSO -* [metalctlv2 project member](metalctlv2_project_member.md) - manage project members +* [metalctlv2 project member](metalctlv2_project_member.md) - manage member entities diff --git a/docs/metalctlv2_project_member_update.md b/docs/metalctlv2_project_member_update.md index 4f29aaf..033cb9a 100644 --- a/docs/metalctlv2_project_member_update.md +++ b/docs/metalctlv2_project_member_update.md @@ -1,17 +1,32 @@ ## metalctlv2 project member update -update member from a project +updates the member ``` -metalctlv2 project member update [flags] +metalctlv2 project member update [flags] ``` ### Options ``` - -h, --help help for update - -p, --project string the project in which to remove the member - --role string the role of the member + --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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member update -f - + $ # or via file + $ metalctlv2 member update -f member.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for update + --project string the project for which to update a project member, defaults to project of the default project + --role string the role of the member + --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 @@ -29,5 +44,5 @@ metalctlv2 project member update [flags] ### SEE ALSO -* [metalctlv2 project member](metalctlv2_project_member.md) - manage project members +* [metalctlv2 project member](metalctlv2_project_member.md) - manage member entities diff --git a/docs/metalctlv2_tenant.md b/docs/metalctlv2_tenant.md index 9b29e1b..37fcd73 100644 --- a/docs/metalctlv2_tenant.md +++ b/docs/metalctlv2_tenant.md @@ -36,6 +36,6 @@ manage api tenants * [metalctlv2 tenant invite](metalctlv2_tenant_invite.md) - manage tenant invites * [metalctlv2 tenant join](metalctlv2_tenant_join.md) - join a tenant of someone who shared an invite secret with you * [metalctlv2 tenant list](metalctlv2_tenant_list.md) - list all tenants -* [metalctlv2 tenant member](metalctlv2_tenant_member.md) - manage tenant members +* [metalctlv2 tenant member](metalctlv2_tenant_member.md) - manage member entities * [metalctlv2 tenant update](metalctlv2_tenant_update.md) - updates the tenant diff --git a/docs/metalctlv2_tenant_member.md b/docs/metalctlv2_tenant_member.md index 7db51c2..51ab871 100644 --- a/docs/metalctlv2_tenant_member.md +++ b/docs/metalctlv2_tenant_member.md @@ -1,6 +1,10 @@ ## metalctlv2 tenant member -manage tenant members +manage member entities + +### Synopsis + +manage api tenant members ### Options @@ -24,7 +28,11 @@ manage tenant members ### SEE ALSO * [metalctlv2 tenant](metalctlv2_tenant.md) - manage tenant entities -* [metalctlv2 tenant member list](metalctlv2_tenant_member_list.md) - lists members of a tenant -* [metalctlv2 tenant member remove](metalctlv2_tenant_member_remove.md) - remove member from a tenant -* [metalctlv2 tenant member update](metalctlv2_tenant_member_update.md) - update member from a tenant +* [metalctlv2 tenant member apply](metalctlv2_tenant_member_apply.md) - applies one or more members from a given file +* [metalctlv2 tenant member create](metalctlv2_tenant_member_create.md) - creates the member +* [metalctlv2 tenant member delete](metalctlv2_tenant_member_delete.md) - deletes the member +* [metalctlv2 tenant member describe](metalctlv2_tenant_member_describe.md) - describes the member +* [metalctlv2 tenant member edit](metalctlv2_tenant_member_edit.md) - edit the member through an editor and update +* [metalctlv2 tenant member list](metalctlv2_tenant_member_list.md) - list all members +* [metalctlv2 tenant member update](metalctlv2_tenant_member_update.md) - updates the member diff --git a/docs/metalctlv2_tenant_member_apply.md b/docs/metalctlv2_tenant_member_apply.md new file mode 100644 index 0000000..2c442dd --- /dev/null +++ b/docs/metalctlv2_tenant_member_apply.md @@ -0,0 +1,46 @@ +## metalctlv2 tenant member apply + +applies one or more members from a given file + +``` +metalctlv2 tenant member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member apply -f - + $ # or via file + $ metalctlv2 member apply -f member.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 tenant member](metalctlv2_tenant_member.md) - manage member entities + diff --git a/docs/metalctlv2_tenant_member_create.md b/docs/metalctlv2_tenant_member_create.md new file mode 100644 index 0000000..5403bbe --- /dev/null +++ b/docs/metalctlv2_tenant_member_create.md @@ -0,0 +1,47 @@ +## metalctlv2 tenant member create + +creates the member + +``` +metalctlv2 tenant member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member create -f - + $ # or via file + $ metalctlv2 member create -f member.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 + --tenant string the tenant for which to create a tenant member, defaults to tenant of the default project + --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 tenant member](metalctlv2_tenant_member.md) - manage member entities + diff --git a/docs/metalctlv2_tenant_member_delete.md b/docs/metalctlv2_tenant_member_delete.md new file mode 100644 index 0000000..72222dd --- /dev/null +++ b/docs/metalctlv2_tenant_member_delete.md @@ -0,0 +1,47 @@ +## metalctlv2 tenant member delete + +deletes the member + +``` +metalctlv2 tenant member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member delete -f - + $ # or via file + $ metalctlv2 member delete -f member.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 + --tenant string the tenant from which to delete a tenant member, defaults to tenant of the default project + --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 tenant member](metalctlv2_tenant_member.md) - manage member entities + diff --git a/docs/metalctlv2_tenant_member_remove.md b/docs/metalctlv2_tenant_member_describe.md similarity index 76% rename from docs/metalctlv2_tenant_member_remove.md rename to docs/metalctlv2_tenant_member_describe.md index 7c37c64..9e553c4 100644 --- a/docs/metalctlv2_tenant_member_remove.md +++ b/docs/metalctlv2_tenant_member_describe.md @@ -1,16 +1,16 @@ -## metalctlv2 tenant member remove +## metalctlv2 tenant member describe -remove member from a tenant +describes the member ``` -metalctlv2 tenant member remove [flags] +metalctlv2 tenant member describe [flags] ``` ### Options ``` - -h, --help help for remove - --tenant string the tenant in which to remove the member + -h, --help help for describe + --tenant string the tenant to describe the tenant members of, defaults to tenant of the default project ``` ### Options inherited from parent commands @@ -28,5 +28,5 @@ metalctlv2 tenant member remove [flags] ### SEE ALSO -* [metalctlv2 tenant member](metalctlv2_tenant_member.md) - manage tenant members +* [metalctlv2 tenant member](metalctlv2_tenant_member.md) - manage member entities diff --git a/docs/metalctlv2_tenant_member_edit.md b/docs/metalctlv2_tenant_member_edit.md new file mode 100644 index 0000000..a1cc671 --- /dev/null +++ b/docs/metalctlv2_tenant_member_edit.md @@ -0,0 +1,32 @@ +## metalctlv2 tenant member edit + +edit the member through an editor and update + +``` +metalctlv2 tenant member edit [flags] +``` + +### Options + +``` + -h, --help help for edit + --tenant string the tenant from which to edit a tenant member, defaults to tenant of the default project +``` + +### 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 tenant member](metalctlv2_tenant_member.md) - manage member entities + diff --git a/docs/metalctlv2_tenant_member_list.md b/docs/metalctlv2_tenant_member_list.md index 6037691..8bb93b9 100644 --- a/docs/metalctlv2_tenant_member_list.md +++ b/docs/metalctlv2_tenant_member_list.md @@ -1,6 +1,6 @@ ## metalctlv2 tenant member list -lists members of a tenant +list all members ``` metalctlv2 tenant member list [flags] @@ -10,8 +10,9 @@ metalctlv2 tenant member list [flags] ``` -h, --help help for list + --role string the role of the member --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: created|id|role - --tenant string the tenant in which to remove the member + --tenant string the tenant to list the tenant members of, defaults to tenant of the default project ``` ### Options inherited from parent commands @@ -29,5 +30,5 @@ metalctlv2 tenant member list [flags] ### SEE ALSO -* [metalctlv2 tenant member](metalctlv2_tenant_member.md) - manage tenant members +* [metalctlv2 tenant member](metalctlv2_tenant_member.md) - manage member entities diff --git a/docs/metalctlv2_tenant_member_update.md b/docs/metalctlv2_tenant_member_update.md index 3eeae30..cdc8083 100644 --- a/docs/metalctlv2_tenant_member_update.md +++ b/docs/metalctlv2_tenant_member_update.md @@ -1,17 +1,32 @@ ## metalctlv2 tenant member update -update member from a tenant +updates the member ``` -metalctlv2 tenant member update [flags] +metalctlv2 tenant member update [flags] ``` ### Options ``` - -h, --help help for update - --role string the role of the member - --tenant string the tenant in which to remove the member + --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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member update -f - + $ # or via file + $ metalctlv2 member update -f member.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for update + --role string the role of the member + --skip-security-prompts skips security prompt for bulk operations + --tenant string the tenant for which to update a tenant member, defaults to tenant of the default project + --timestamps when used with --file (bulk operation): prints timestamps in-between the operations ``` ### Options inherited from parent commands @@ -29,5 +44,5 @@ metalctlv2 tenant member update [flags] ### SEE ALSO -* [metalctlv2 tenant member](metalctlv2_tenant_member.md) - manage tenant members +* [metalctlv2 tenant member](metalctlv2_tenant_member.md) - manage member entities diff --git a/go.mod b/go.mod index fe403cd..f0dcc3e 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/fatih/color v1.19.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/metal-stack/api v0.7.0 + github.com/metal-stack/api v0.7.1-0.20260929132751-9a0d47e7a763 github.com/metal-stack/metal-console v0.8.2 github.com/metal-stack/metal-lib v0.26.3 github.com/metal-stack/v v1.0.3 diff --git a/go.sum b/go.sum index f218831..1c9d039 100644 --- a/go.sum +++ b/go.sum @@ -191,8 +191,8 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A= github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg= -github.com/metal-stack/api v0.7.0 h1:z+J788nCCmIyBaG/hNmKLPJ/S1K6nfePqSDyMIIX470= -github.com/metal-stack/api v0.7.0/go.mod h1:rWdj0dTAg2w/WuQhIZ+JVp9b68v6VVCm1k7sLx4U+PQ= +github.com/metal-stack/api v0.7.1-0.20260929132751-9a0d47e7a763 h1:MRhbVWis+YCJyOmys2kUR9DIhXrFHDCKzCnSxh2iabg= +github.com/metal-stack/api v0.7.1-0.20260929132751-9a0d47e7a763/go.mod h1:rWdj0dTAg2w/WuQhIZ+JVp9b68v6VVCm1k7sLx4U+PQ= github.com/metal-stack/metal-console v0.8.2 h1:Y86penzEROJKcLica1PeE2ddtr9G0/u16HiCAYXaxxU= github.com/metal-stack/metal-console v0.8.2/go.mod h1:ZkselOaxF/iELFL9Tku3maXgpX4DM0CHjoG7CGuky6E= github.com/metal-stack/metal-lib v0.26.3 h1:K5gLoD65m6p3l6qCPrfavIdvNdWfmF2QdXrvU2URaZs= diff --git a/tests/e2e/api/project_test.go b/tests/e2e/api/project_test.go index 1b2864b..9b4fdf8 100644 --- a/tests/e2e/api/project_test.go +++ b/tests/e2e/api/project_test.go @@ -548,7 +548,7 @@ func Test_ProjectCmd_ListMembers(t *testing.T) { return connect.NewResponse(&apiv2.ProjectServiceGetResponse{ Project: testresources.Project1(), ProjectMembers: []*apiv2.ProjectMember{ - testresources.Project1Members(), testresources.Project2Members(), + testresources.Project1Member(), testresources.Project2Member(), }, }) }, @@ -565,7 +565,7 @@ func Test_ProjectCmd_ListMembers(t *testing.T) { 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now 40c0da4b-9eb9-4371-91aa-1ae62193fa54 PROJECT_ROLE_EDITOR true now `), - Template: new("{{ .id }} {{ .role }}"), + Template: new("{{ .project_member.id }} {{ .project_member.role }}"), WantTemplate: new(` 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 1 40c0da4b-9eb9-4371-91aa-1ae62193fa54 2 @@ -587,21 +587,26 @@ func Test_ProjectCmd_DeleteMember(t *testing.T) { tests := []*e2e.Test[apiv2.ProjectServiceRemoveMemberResponse, string]{ { Name: "delete project member", - CmdArgs: []string{"project", "member", "delete", testresources.Project1Members().Id, "--project", testresources.Project1().Uuid}, + CmdArgs: []string{"project", "member", "delete", testresources.Project1Member().Id, "--project", testresources.Project1().Uuid}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { WantRequest: &apiv2.ProjectServiceRemoveMemberRequest{ Project: testresources.Project1().Uuid, - Member: testresources.Project1Members().Id, + Member: testresources.Project1Member().Id, }, WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.ProjectServiceRemoveMemberResponse{}) + return connect.NewResponse(&apiv2.ProjectServiceRemoveMemberResponse{ + ProjectMember: testresources.Project1Member(), + }) }, }, }, }), - WantDefault: new(fmt.Sprintf("✔ successfully removed member \"%s\"", testresources.Project1Members().Id)), + WantTable: new(` + ID ROLE INHERITED SINCE + 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now + `), }, } for _, tt := range tests { @@ -613,24 +618,23 @@ func Test_ProjectCmd_UpdateMember(t *testing.T) { tests := []*e2e.Test[apiv2.ProjectServiceUpdateMemberResponse, *apiv2.ProjectMember]{ { Name: "update project member", - CmdArgs: []string{"project", "member", "update", testresources.Project1Members().Id, "--project", testresources.Project1().Uuid, "--role", testresources.Project1Members().Role.String()}, + CmdArgs: []string{"project", "member", "update", testresources.Project1Member().Id, "--project", testresources.Project1().Uuid, "--role", testresources.Project1Member().Role.String()}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { WantRequest: &apiv2.ProjectServiceUpdateMemberRequest{ Project: testresources.Project1().Uuid, - Member: testresources.Project1Members().Id, - Role: testresources.Project1Members().Role, + Member: testresources.Project1Member().Id, + Role: testresources.Project1Member().Role, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.ProjectServiceUpdateMemberResponse{ - ProjectMember: testresources.Project1Members(), + ProjectMember: testresources.Project1Member(), }) }, }, }, }), - WantProtoObject: testresources.Project1Members(), WantTable: new(` ID ROLE INHERITED SINCE 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now diff --git a/tests/e2e/api/tenant_test.go b/tests/e2e/api/tenant_test.go index d806fe0..a2def94 100644 --- a/tests/e2e/api/tenant_test.go +++ b/tests/e2e/api/tenant_test.go @@ -8,6 +8,7 @@ import ( "github.com/dustin/go-humanize" "github.com/metal-stack/api/go/client" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + cliv2 "github.com/metal-stack/api/go/metalstack/cli/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" @@ -143,7 +144,7 @@ func Test_TenantCmd_Create(t *testing.T) { Name: testresources.Tenant1().Name, Description: &testresources.Tenant1().Description, Email: &testresources.Tenant1().Email, - AvatarUrl: new(""), + AvatarUrl: nil, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.TenantServiceCreateResponse{ @@ -170,7 +171,7 @@ func Test_TenantCmd_Create(t *testing.T) { WantRequest: &apiv2.TenantServiceCreateRequest{ Name: testresources.Tenant1().Name, Description: &testresources.Tenant1().Description, - AvatarUrl: new(""), + AvatarUrl: nil, Email: &testresources.Tenant1().Email, }, WantResponse: func() connect.AnyResponse { @@ -184,7 +185,7 @@ func Test_TenantCmd_Create(t *testing.T) { Name: testresources.Tenant2().Name, Description: &testresources.Tenant2().Description, Email: &testresources.Tenant2().Email, - AvatarUrl: new(""), + AvatarUrl: nil, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.TenantServiceCreateResponse{ @@ -331,7 +332,7 @@ func Test_TenantCmd_Apply(t *testing.T) { Email: new(testresources.Tenant1().Email), Description: new(testresources.Tenant1().Description), Name: testresources.Tenant1().Name, - AvatarUrl: new(""), + AvatarUrl: nil, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.TenantServiceCreateResponse{ @@ -361,7 +362,7 @@ func Test_TenantCmd_Apply(t *testing.T) { Email: new(testresources.Tenant1().Email), Description: new(testresources.Tenant1().Description), Name: testresources.Tenant1().Name, - AvatarUrl: new(""), + AvatarUrl: nil, }, WantError: connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("already exists")), }, @@ -407,7 +408,7 @@ func Test_TenantCmd_ListMembers(t *testing.T) { return connect.NewResponse(&apiv2.TenantServiceGetResponse{ Tenant: testresources.Tenant1(), TenantMembers: []*apiv2.TenantMember{ - testresources.Tenant1Members(), testresources.Tenant2Members(), + testresources.Tenant1Member(), testresources.Tenant2Member(), }, }) }, @@ -424,7 +425,7 @@ func Test_TenantCmd_ListMembers(t *testing.T) { 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now 40c0da4b-9eb9-4371-91aa-1ae62193fa54 TENANT_ROLE_EDITOR now `), - Template: new("{{ .id }} {{ .role }} {{ .projects }}"), + Template: new("{{ .tenant_member.id }} {{ .tenant_member.role }} {{ .tenant_member.projects }}"), WantTemplate: new(` 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 1 [0d81bca7-73f6-4da3-8397-4a8c52a0c583 f3b4e6a1-2c8d-4e5f-a7b9-1d3e5f7a9b0c] 40c0da4b-9eb9-4371-91aa-1ae62193fa54 2 [0d81bca7-73f6-4da3-8397-4a8c52a0c583] @@ -446,21 +447,26 @@ func Test_TenantCmd_DeleteMember(t *testing.T) { tests := []*e2e.Test[apiv2.TenantServiceRemoveMemberResponse, string]{ { Name: "delete tenant member", - CmdArgs: []string{"tenant", "member", "remove", testresources.Tenant1Members().Id, "--tenant", testresources.Tenant1().Login}, + CmdArgs: []string{"tenant", "member", "remove", testresources.Tenant1Member().Id, "--tenant", testresources.Tenant1().Login}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { WantRequest: &apiv2.TenantServiceRemoveMemberRequest{ Login: testresources.Tenant1().Login, - Member: testresources.Tenant1Members().Id, + Member: testresources.Tenant1Member().Id, }, WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.TenantServiceRemoveMemberResponse{}) + return connect.NewResponse(&apiv2.TenantServiceRemoveMemberResponse{ + TenantMember: testresources.Tenant1Member(), + }) }, }, }, }), - WantDefault: new(fmt.Sprintf("✔ successfully removed member \"%s\"", testresources.Tenant1Members().Id)), + WantTable: new(` + ID ROLE SINCE + 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now + `), }, } for _, tt := range tests { @@ -469,27 +475,106 @@ func Test_TenantCmd_DeleteMember(t *testing.T) { } func Test_TenantCmd_UpdateMember(t *testing.T) { - tests := []*e2e.Test[apiv2.TenantServiceUpdateMemberResponse, *apiv2.TenantMember]{ + tests := []*e2e.Test[apiv2.TenantServiceUpdateMemberResponse, *cliv2.TenantMember]{ { Name: "update tenant member", - CmdArgs: []string{"tenant", "member", "update", testresources.Tenant1Members().Id, "--tenant", testresources.Tenant1().Login, "--role", testresources.Tenant1Members().Role.String()}, + CmdArgs: []string{"tenant", "member", "update", testresources.Tenant1Member().Id, "--tenant", testresources.Tenant1().Login, "--role", testresources.Tenant1Member().Role.String()}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { WantRequest: &apiv2.TenantServiceUpdateMemberRequest{ Login: testresources.Tenant1().Login, - Member: testresources.Tenant1Members().Id, - Role: testresources.Tenant1Members().Role, + Member: testresources.Tenant1Member().Id, + Role: testresources.Tenant1Member().Role, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.TenantServiceUpdateMemberResponse{ - TenantMember: testresources.Tenant1Members(), + TenantMember: testresources.Tenant1Member(), }) }, }, }, }), - WantProtoObject: testresources.Tenant1Members(), + WantTable: new(` + ID ROLE SINCE + 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now + `), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_TenantCmd_ApplyMember(t *testing.T) { + tests := []*e2e.Test[apiv2.TenantServiceUpdateMemberResponse, *cliv2.TenantMember]{ + { + Name: "apply", + CmdArgs: append([]string{"tenant", "member", "apply"}, e2e.AppendFromFileCommonArgs()...), + NewRootCmd: e2erootcmd.NewRootCmd(t, + &e2erootcmd.TestConfig{ + FsMocks: func(fs *afero.Afero) { + require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, &cliv2.TenantMember{ + Tenant: testresources.Tenant1().Login, + TenantMember: testresources.Tenant1Member(), + }), 0755)) + }, + ClientCalls: []client.ClientCall{ + { + WantRequest: &apiv2.TenantServiceAddMemberRequest{ + Login: testresources.Tenant1().Login, + Member: testresources.Tenant1Member().Id, + Role: testresources.Tenant1Member().Role, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&apiv2.TenantServiceAddMemberResponse{ + TenantMember: testresources.Tenant1Member(), + }) + }, + }, + }, + }, + ), + WantTable: new(` + ID ROLE SINCE + 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now + `), + }, + { + Name: "apply already exists", + CmdArgs: append([]string{"tenant", "member", "apply"}, e2e.AppendFromFileCommonArgs()...), + NewRootCmd: e2erootcmd.NewRootCmd(t, + &e2erootcmd.TestConfig{ + FsMocks: func(fs *afero.Afero) { + require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, &cliv2.TenantMember{ + Tenant: testresources.Tenant1().Login, + TenantMember: testresources.Tenant1Member(), + }), 0755)) + }, + ClientCalls: []client.ClientCall{ + { + WantRequest: &apiv2.TenantServiceAddMemberRequest{ + Login: testresources.Tenant1().Login, + Member: testresources.Tenant1Member().Id, + Role: testresources.Tenant1Member().Role, + }, + WantError: connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("already exists")), + }, + { + WantRequest: &apiv2.TenantServiceUpdateMemberRequest{ + Login: testresources.Tenant1().Login, + Member: testresources.Tenant1Member().Id, + Role: testresources.Tenant1Member().Role, + }, + WantResponse: func() connect.AnyResponse { + return connect.NewResponse(&apiv2.TenantServiceUpdateMemberResponse{ + TenantMember: testresources.Tenant1Member(), + }) + }, + }, + }, + }, + ), WantTable: new(` ID ROLE SINCE 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now diff --git a/tests/e2e/testresources/projects.go b/tests/e2e/testresources/projects.go index 4859fc1..50d88e4 100644 --- a/tests/e2e/testresources/projects.go +++ b/tests/e2e/testresources/projects.go @@ -55,7 +55,7 @@ var ( } } - Project1Members = func() *apiv2.ProjectMember { + Project1Member = func() *apiv2.ProjectMember { return &apiv2.ProjectMember{ Id: "16d6e8ba-f574-494f-8d5e-74f6cb2d8db0", Role: apiv2.ProjectRole_PROJECT_ROLE_OWNER, @@ -63,7 +63,7 @@ var ( CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), } } - Project2Members = func() *apiv2.ProjectMember { + Project2Member = func() *apiv2.ProjectMember { return &apiv2.ProjectMember{ Id: "40c0da4b-9eb9-4371-91aa-1ae62193fa54", Role: apiv2.ProjectRole_PROJECT_ROLE_EDITOR, diff --git a/tests/e2e/testresources/tenants.go b/tests/e2e/testresources/tenants.go index c8eb5b4..cf25580 100644 --- a/tests/e2e/testresources/tenants.go +++ b/tests/e2e/testresources/tenants.go @@ -57,7 +57,7 @@ var ( } } - Tenant1Members = func() *apiv2.TenantMember { + Tenant1Member = func() *apiv2.TenantMember { return &apiv2.TenantMember{ Id: "16d6e8ba-f574-494f-8d5e-74f6cb2d8db0", Role: apiv2.TenantRole_TENANT_ROLE_OWNER, @@ -65,7 +65,7 @@ var ( Projects: []string{Project1().Uuid, Project2().Uuid}, } } - Tenant2Members = func() *apiv2.TenantMember { + Tenant2Member = func() *apiv2.TenantMember { return &apiv2.TenantMember{ Id: "40c0da4b-9eb9-4371-91aa-1ae62193fa54", Role: apiv2.TenantRole_TENANT_ROLE_EDITOR, From 3f2bac31c91fac77925b1b5665638c72fb08c4fd Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 29 Sep 2026 16:24:01 +0200 Subject: [PATCH 07/15] Satisfy linter. --- tests/e2e/testresources/switch.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/testresources/switch.go b/tests/e2e/testresources/switch.go index ac51006..b970054 100644 --- a/tests/e2e/testresources/switch.go +++ b/tests/e2e/testresources/switch.go @@ -68,7 +68,7 @@ var ( return &apiv2.SwitchNic{ Name: "Ethernet0", Identifier: "oid:0x1000000000001", - Mac: new("52:54:00:ab:cd:01"), + Mac: new("52:54:00:ab:cd:01"), //nolint:staticcheck Vrf: new("default"), State: &apiv2.NicState{ Desired: new(apiv2.SwitchPortStatus_SWITCH_PORT_STATUS_UP), @@ -99,7 +99,7 @@ var ( return &apiv2.SwitchNic{ Name: "Ethernet4", Identifier: "oid:0x1000000000002", - Mac: new("52:54:00:ab:cd:02"), + Mac: new("52:54:00:ab:cd:02"), //nolint:staticcheck State: &apiv2.NicState{ Desired: new(apiv2.SwitchPortStatus_SWITCH_PORT_STATUS_UP), Actual: apiv2.SwitchPortStatus_SWITCH_PORT_STATUS_DOWN, From c1d9b8e14c2ebff6f0710a03b7afc73cbd693d26 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Tue, 29 Sep 2026 17:00:36 +0200 Subject: [PATCH 08/15] More crud, more fixes. --- cmd/admin/v2/project.go | 73 +++++++++++++++++-- cmd/admin/v2/tenant.go | 62 ++++++++++++++-- cmd/api/v2/project.go | 10 ++- cmd/api/v2/tenant.go | 3 + docs/admin/metalctlv2_admin_tenant.md | 5 ++ docs/admin/metalctlv2_admin_tenant_apply.md | 46 ++++++++++++ docs/admin/metalctlv2_admin_tenant_delete.md | 46 ++++++++++++ .../admin/metalctlv2_admin_tenant_describe.md | 31 ++++++++ docs/admin/metalctlv2_admin_tenant_edit.md | 31 ++++++++ docs/admin/metalctlv2_admin_tenant_update.md | 46 ++++++++++++ go.mod | 2 +- go.sum | 4 +- tests/e2e/api/project_test.go | 6 ++ tests/e2e/api/tenant_test.go | 6 ++ 14 files changed, 350 insertions(+), 21 deletions(-) create mode 100644 docs/admin/metalctlv2_admin_tenant_apply.md create mode 100644 docs/admin/metalctlv2_admin_tenant_delete.md create mode 100644 docs/admin/metalctlv2_admin_tenant_describe.md create mode 100644 docs/admin/metalctlv2_admin_tenant_edit.md create mode 100644 docs/admin/metalctlv2_admin_tenant_update.md diff --git a/cmd/admin/v2/project.go b/cmd/admin/v2/project.go index 5ca54f0..d47234e 100644 --- a/cmd/admin/v2/project.go +++ b/cmd/admin/v2/project.go @@ -3,6 +3,7 @@ package v2 import ( "fmt" + "github.com/metal-stack/api/go/errorutil" 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" @@ -24,7 +25,7 @@ func newProjectCmd(c *config.Config) *cobra.Command { c: c, } - cmdsConfig := &genericcli.CmdsConfig[*apiv2.ProjectServiceCreateRequest, *apiv2.ProjectServiceUpdateRequest, *apiv2.Project]{ + cmdsConfig := &genericcli.CmdsConfig[*adminv2.ProjectServiceCreateRequest, *apiv2.ProjectServiceUpdateRequest, *apiv2.Project]{ BinaryName: config.BinaryName, GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), Singular: "project", @@ -43,7 +44,19 @@ func newProjectCmd(c *config.Config) *cobra.Command { } func (c *project) Get(id string) (*apiv2.Project, error) { - panic("unimplemented") + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &apiv2.ProjectServiceGetRequest{ + Project: id, + } + + resp, err := c.c.Client.Apiv2().Project().Get(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get project: %w", err) + } + + return resp.GetProject(), nil } func (c *project) List() ([]*apiv2.Project, error) { @@ -73,18 +86,62 @@ func (c *project) List() ([]*apiv2.Project, error) { return resp.GetProjects(), nil } -func (c *project) Create(rq *apiv2.ProjectServiceCreateRequest) (*apiv2.Project, error) { - panic("unimplemented") +func (c *project) Create(rq *adminv2.ProjectServiceCreateRequest) (*apiv2.Project, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Adminv2().Project().Create(ctx, rq) + if err != nil { + if errorutil.IsConflict(err) { + return nil, genericcli.AlreadyExistsError() + } + + return nil, err + } + + return resp.Project, nil } func (c *project) Delete(id string) (*apiv2.Project, error) { - panic("unimplemented") + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Project().Delete(ctx, &apiv2.ProjectServiceDeleteRequest{ + Project: id, + }) + if err != nil { + return nil, fmt.Errorf("failed to delete project: %w", err) + } + + return resp.Project, nil } -func (c *project) Convert(r *apiv2.Project) (string, *apiv2.ProjectServiceCreateRequest, *apiv2.ProjectServiceUpdateRequest, error) { - panic("unimplemented") +func (c *project) Convert(r *apiv2.Project) (string, *adminv2.ProjectServiceCreateRequest, *apiv2.ProjectServiceUpdateRequest, error) { + return r.Uuid, &adminv2.ProjectServiceCreateRequest{ + Login: r.Tenant, + Name: r.Name, + Description: r.Description, + AvatarUrl: r.AvatarUrl, + Labels: pointer.SafeDeref(r.Meta).Labels, + Project: pointer.PointerOrNil(r.Uuid), + }, &apiv2.ProjectServiceUpdateRequest{ + Project: r.Uuid, + Name: pointer.PointerOrNil(r.Name), + Description: pointer.PointerOrNil(r.Description), + AvatarUrl: r.AvatarUrl, + UpdateMeta: helpers.UpdateMetaFromMeta(r.Meta), + Labels: helpers.UpdateLabelsFromMeta(r.Meta), + }, nil } func (c *project) Update(rq *apiv2.ProjectServiceUpdateRequest) (*apiv2.Project, error) { - panic("unimplemented") + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Project().Update(ctx, rq) + if err != nil { + return nil, fmt.Errorf("failed to update project: %w", err) + } + + return resp.Project, nil } diff --git a/cmd/admin/v2/tenant.go b/cmd/admin/v2/tenant.go index e8948c3..b64daee 100644 --- a/cmd/admin/v2/tenant.go +++ b/cmd/admin/v2/tenant.go @@ -25,7 +25,7 @@ func newTenantCmd(c *config.Config) *cobra.Command { c: c, } - cmdsConfig := &genericcli.CmdsConfig[*adminv2.TenantServiceCreateRequest, any, *apiv2.Tenant]{ + cmdsConfig := &genericcli.CmdsConfig[*adminv2.TenantServiceCreateRequest, *apiv2.TenantServiceUpdateRequest, *apiv2.Tenant]{ BinaryName: config.BinaryName, GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), Singular: "tenant", @@ -54,7 +54,6 @@ func newTenantCmd(c *config.Config) *cobra.Command { AvatarUrl: pointer.PointerOrNil(viper.GetString("avatar-url")), }, nil }, - OnlyCmds: genericcli.OnlyCmds(genericcli.ListCmd, genericcli.CreateCmd), ValidArgsFn: w.c.Completion.AdminTenant, } @@ -62,7 +61,19 @@ func newTenantCmd(c *config.Config) *cobra.Command { } func (c *tenant) Get(id string) (*apiv2.Tenant, error) { - panic("unimplemented") + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &apiv2.TenantServiceGetRequest{ + Login: id, + } + + resp, err := c.c.Client.Apiv2().Tenant().Get(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get tenant: %w", err) + } + + return resp.GetTenant(), nil } func (c *tenant) List() ([]*apiv2.Tenant, error) { @@ -110,15 +121,50 @@ func (c *tenant) Create(rq *adminv2.TenantServiceCreateRequest) (*apiv2.Tenant, } func (c *tenant) Delete(id string) (*apiv2.Tenant, error) { - panic("unimplemented") + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Tenant().Delete(ctx, &apiv2.TenantServiceDeleteRequest{ + Login: id, + }) + if err != nil { + return nil, fmt.Errorf("failed to delete tenant: %w", err) + } + + return resp.Tenant, nil } -func (c *tenant) Convert(r *apiv2.Tenant) (string, *adminv2.TenantServiceCreateRequest, any, error) { - panic("unimplemented") +func (c *tenant) Convert(r *apiv2.Tenant) (string, *adminv2.TenantServiceCreateRequest, *apiv2.TenantServiceUpdateRequest, error) { + return r.Login, &adminv2.TenantServiceCreateRequest{ + Name: r.Name, + Description: pointer.PointerOrNil(r.Description), + Email: pointer.PointerOrNil(r.Email), + AvatarUrl: pointer.PointerOrNil(r.AvatarUrl), + Labels: pointer.SafeDeref(r.Meta).Labels, + Login: pointer.PointerOrNil(r.Login), + }, + &apiv2.TenantServiceUpdateRequest{ + Login: r.Login, + Name: pointer.PointerOrNil(r.Name), + Email: pointer.PointerOrNil(r.Email), + Description: pointer.PointerOrNil(r.Description), + AvatarUrl: pointer.PointerOrNil(r.AvatarUrl), + UpdateMeta: helpers.UpdateMetaFromMeta(r.Meta), + Labels: helpers.UpdateLabelsFromMeta(r.Meta), + }, + nil } -func (c *tenant) Update(rq any) (*apiv2.Tenant, error) { - panic("unimplemented") +func (c *tenant) Update(rq *apiv2.TenantServiceUpdateRequest) (*apiv2.Tenant, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().Tenant().Update(ctx, rq) + if err != nil { + return nil, fmt.Errorf("failed to update tenant: %w", err) + } + + return resp.Tenant, nil } func newAddMemberCmd(c *config.Config) *cobra.Command { diff --git a/cmd/api/v2/project.go b/cmd/api/v2/project.go index a81acc5..f9b8dd8 100644 --- a/cmd/api/v2/project.go +++ b/cmd/api/v2/project.go @@ -173,6 +173,7 @@ func (c *project) Create(rq *apiv2.ProjectServiceCreateRequest) (*apiv2.Project, return nil, err } + return resp.Project, nil } @@ -195,10 +196,15 @@ func (c *project) Convert(r *apiv2.Project) (string, *apiv2.ProjectServiceCreate Login: r.Tenant, Name: r.Name, Description: r.Description, + AvatarUrl: r.AvatarUrl, + Labels: pointer.SafeDeref(r.Meta).Labels, }, &apiv2.ProjectServiceUpdateRequest{ Project: r.Uuid, - Name: new(r.Name), - Description: new(r.Description), + Name: pointer.PointerOrNil(r.Name), + Description: pointer.PointerOrNil(r.Description), + AvatarUrl: r.AvatarUrl, + UpdateMeta: helpers.UpdateMetaFromMeta(r.Meta), + Labels: helpers.UpdateLabelsFromMeta(r.Meta), }, nil } diff --git a/cmd/api/v2/tenant.go b/cmd/api/v2/tenant.go index ef14878..345b03a 100644 --- a/cmd/api/v2/tenant.go +++ b/cmd/api/v2/tenant.go @@ -196,6 +196,7 @@ func (c *tenant) Convert(r *apiv2.Tenant) (string, *apiv2.TenantServiceCreateReq Description: pointer.PointerOrNil(r.Description), Email: pointer.PointerOrNil(r.Email), AvatarUrl: pointer.PointerOrNil(r.AvatarUrl), + Labels: pointer.SafeDeref(r.Meta).Labels, }, &apiv2.TenantServiceUpdateRequest{ Login: r.Login, @@ -203,6 +204,8 @@ func (c *tenant) Convert(r *apiv2.Tenant) (string, *apiv2.TenantServiceCreateReq Email: pointer.PointerOrNil(r.Email), Description: pointer.PointerOrNil(r.Description), AvatarUrl: pointer.PointerOrNil(r.AvatarUrl), + UpdateMeta: helpers.UpdateMetaFromMeta(r.Meta), + Labels: helpers.UpdateLabelsFromMeta(r.Meta), }, nil } diff --git a/docs/admin/metalctlv2_admin_tenant.md b/docs/admin/metalctlv2_admin_tenant.md index 66f3544..b4bf20d 100644 --- a/docs/admin/metalctlv2_admin_tenant.md +++ b/docs/admin/metalctlv2_admin_tenant.md @@ -29,6 +29,11 @@ manage api tenants * [metalctlv2 admin](metalctlv2_admin.md) - admin commands * [metalctlv2 admin tenant add-member](metalctlv2_admin_tenant_add-member.md) - Add a new member to a tenant +* [metalctlv2 admin tenant apply](metalctlv2_admin_tenant_apply.md) - applies one or more tenants from a given file * [metalctlv2 admin tenant create](metalctlv2_admin_tenant_create.md) - creates the tenant +* [metalctlv2 admin tenant delete](metalctlv2_admin_tenant_delete.md) - deletes the tenant +* [metalctlv2 admin tenant describe](metalctlv2_admin_tenant_describe.md) - describes the tenant +* [metalctlv2 admin tenant edit](metalctlv2_admin_tenant_edit.md) - edit the tenant through an editor and update * [metalctlv2 admin tenant list](metalctlv2_admin_tenant_list.md) - list all tenants +* [metalctlv2 admin tenant update](metalctlv2_admin_tenant_update.md) - updates the tenant diff --git a/docs/admin/metalctlv2_admin_tenant_apply.md b/docs/admin/metalctlv2_admin_tenant_apply.md new file mode 100644 index 0000000..4352b63 --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_apply.md @@ -0,0 +1,46 @@ +## metalctlv2 admin tenant apply + +applies one or more tenants from a given file + +``` +metalctlv2 admin tenant 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 tenant describe tenant-1 -o yaml > tenant.yaml + $ vi tenant.yaml + $ # either via stdin + $ cat tenant.yaml | metalctlv2 tenant apply -f - + $ # or via file + $ metalctlv2 tenant apply -f tenant.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 tenant](metalctlv2_admin_tenant.md) - manage tenant entities + diff --git a/docs/admin/metalctlv2_admin_tenant_delete.md b/docs/admin/metalctlv2_admin_tenant_delete.md new file mode 100644 index 0000000..788535a --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_delete.md @@ -0,0 +1,46 @@ +## metalctlv2 admin tenant delete + +deletes the tenant + +``` +metalctlv2 admin tenant 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 tenant describe tenant-1 -o yaml > tenant.yaml + $ vi tenant.yaml + $ # either via stdin + $ cat tenant.yaml | metalctlv2 tenant delete -f - + $ # or via file + $ metalctlv2 tenant delete -f tenant.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 tenant](metalctlv2_admin_tenant.md) - manage tenant entities + diff --git a/docs/admin/metalctlv2_admin_tenant_describe.md b/docs/admin/metalctlv2_admin_tenant_describe.md new file mode 100644 index 0000000..3821f57 --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_describe.md @@ -0,0 +1,31 @@ +## metalctlv2 admin tenant describe + +describes the tenant + +``` +metalctlv2 admin tenant 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 tenant](metalctlv2_admin_tenant.md) - manage tenant entities + diff --git a/docs/admin/metalctlv2_admin_tenant_edit.md b/docs/admin/metalctlv2_admin_tenant_edit.md new file mode 100644 index 0000000..e33d329 --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_edit.md @@ -0,0 +1,31 @@ +## metalctlv2 admin tenant edit + +edit the tenant through an editor and update + +``` +metalctlv2 admin tenant 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 tenant](metalctlv2_admin_tenant.md) - manage tenant entities + diff --git a/docs/admin/metalctlv2_admin_tenant_update.md b/docs/admin/metalctlv2_admin_tenant_update.md new file mode 100644 index 0000000..3920b4d --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_update.md @@ -0,0 +1,46 @@ +## metalctlv2 admin tenant update + +updates the tenant + +``` +metalctlv2 admin tenant 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 tenant describe tenant-1 -o yaml > tenant.yaml + $ vi tenant.yaml + $ # either via stdin + $ cat tenant.yaml | metalctlv2 tenant update -f - + $ # or via file + $ metalctlv2 tenant update -f tenant.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 tenant](metalctlv2_admin_tenant.md) - manage tenant entities + diff --git a/go.mod b/go.mod index f0dcc3e..24dc1a1 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/fatih/color v1.19.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/metal-stack/api v0.7.1-0.20260929132751-9a0d47e7a763 + github.com/metal-stack/api v0.7.1-0.20260929135827-11f6a1ed8099 github.com/metal-stack/metal-console v0.8.2 github.com/metal-stack/metal-lib v0.26.3 github.com/metal-stack/v v1.0.3 diff --git a/go.sum b/go.sum index 1c9d039..5c74b21 100644 --- a/go.sum +++ b/go.sum @@ -191,8 +191,8 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A= github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg= -github.com/metal-stack/api v0.7.1-0.20260929132751-9a0d47e7a763 h1:MRhbVWis+YCJyOmys2kUR9DIhXrFHDCKzCnSxh2iabg= -github.com/metal-stack/api v0.7.1-0.20260929132751-9a0d47e7a763/go.mod h1:rWdj0dTAg2w/WuQhIZ+JVp9b68v6VVCm1k7sLx4U+PQ= +github.com/metal-stack/api v0.7.1-0.20260929135827-11f6a1ed8099 h1:suq0OfxZkzAHArsXhPfgyXnYd4w/Sb3GRI8n3OKKmTU= +github.com/metal-stack/api v0.7.1-0.20260929135827-11f6a1ed8099/go.mod h1:rWdj0dTAg2w/WuQhIZ+JVp9b68v6VVCm1k7sLx4U+PQ= github.com/metal-stack/metal-console v0.8.2 h1:Y86penzEROJKcLica1PeE2ddtr9G0/u16HiCAYXaxxU= github.com/metal-stack/metal-console v0.8.2/go.mod h1:ZkselOaxF/iELFL9Tku3maXgpX4DM0CHjoG7CGuky6E= github.com/metal-stack/metal-lib v0.26.3 h1:K5gLoD65m6p3l6qCPrfavIdvNdWfmF2QdXrvU2URaZs= diff --git a/tests/e2e/api/project_test.go b/tests/e2e/api/project_test.go index 9b4fdf8..3376cba 100644 --- a/tests/e2e/api/project_test.go +++ b/tests/e2e/api/project_test.go @@ -243,6 +243,9 @@ func Test_ProjectCmd_Update(t *testing.T) { Project: testresources.Project1().Uuid, Name: new(testresources.Project1().Name), Description: new(testresources.Project1().Description), + UpdateMeta: &apiv2.UpdateMeta{ + LockingStrategy: apiv2.OptimisticLockingStrategy_OPTIMISTIC_LOCKING_STRATEGY_SERVER, + }, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.ProjectServiceUpdateResponse{ @@ -365,6 +368,9 @@ func Test_ProjectCmd_Apply(t *testing.T) { Project: testresources.Project1().Uuid, Description: &testresources.Project1().Description, Name: &testresources.Project1().Name, + UpdateMeta: &apiv2.UpdateMeta{ + LockingStrategy: apiv2.OptimisticLockingStrategy_OPTIMISTIC_LOCKING_STRATEGY_SERVER, + }, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.ProjectServiceUpdateResponse{ diff --git a/tests/e2e/api/tenant_test.go b/tests/e2e/api/tenant_test.go index a2def94..81acee3 100644 --- a/tests/e2e/api/tenant_test.go +++ b/tests/e2e/api/tenant_test.go @@ -296,6 +296,9 @@ func Test_TenantCmd_Update(t *testing.T) { Name: new(testresources.Tenant1().Name), Email: new(testresources.Tenant1().Email), Description: new(testresources.Tenant1().Description), + UpdateMeta: &apiv2.UpdateMeta{ + LockingStrategy: apiv2.OptimisticLockingStrategy_OPTIMISTIC_LOCKING_STRATEGY_SERVER, + }, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.TenantServiceUpdateResponse{ @@ -372,6 +375,9 @@ func Test_TenantCmd_Apply(t *testing.T) { Email: new(testresources.Tenant1().Email), Description: new(testresources.Tenant1().Description), Name: new(testresources.Tenant1().Name), + UpdateMeta: &apiv2.UpdateMeta{ + LockingStrategy: apiv2.OptimisticLockingStrategy_OPTIMISTIC_LOCKING_STRATEGY_SERVER, + }, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.TenantServiceUpdateResponse{ From ab87e130b07a832baeafba7501f61b7cbfe2df5f Mon Sep 17 00:00:00 2001 From: Gerrit Date: Wed, 21 Oct 2026 13:51:52 +0200 Subject: [PATCH 09/15] Updates. --- cmd/admin/v2/project.go | 14 +- cmd/admin/v2/tenant.go | 26 +- cmd/api/v2/project_member.go | 93 ++--- cmd/api/v2/tenant_member.go | 92 ++--- cmd/completion/project.go | 6 +- cmd/completion/tenant.go | 6 +- cmd/sorters/project.go | 22 +- cmd/sorters/tenant.go | 18 +- cmd/tableprinters/common.go | 10 +- cmd/tableprinters/project.go | 12 +- cmd/tableprinters/tenant.go | 9 +- docs/admin/metalctlv2_admin_machine_create.md | 79 ++-- docs/admin/metalctlv2_admin_tenant_create.md | 1 + docs/metalctlv2_machine_create.md | 77 ++-- go.mod | 22 +- go.sum | 52 +-- tests/e2e/admin/tenant_test.go | 8 +- tests/e2e/api/project_test.go | 221 +++++----- tests/e2e/api/tenant_test.go | 381 +++++++++--------- tests/e2e/testresources/projects.go | 12 +- tests/e2e/testresources/tenants.go | 20 +- 21 files changed, 578 insertions(+), 603 deletions(-) diff --git a/cmd/admin/v2/project.go b/cmd/admin/v2/project.go index d47234e..738e262 100644 --- a/cmd/admin/v2/project.go +++ b/cmd/admin/v2/project.go @@ -118,12 +118,14 @@ func (c *project) Delete(id string) (*apiv2.Project, error) { func (c *project) Convert(r *apiv2.Project) (string, *adminv2.ProjectServiceCreateRequest, *apiv2.ProjectServiceUpdateRequest, error) { return r.Uuid, &adminv2.ProjectServiceCreateRequest{ - Login: r.Tenant, - Name: r.Name, - Description: r.Description, - AvatarUrl: r.AvatarUrl, - Labels: pointer.SafeDeref(r.Meta).Labels, - Project: pointer.PointerOrNil(r.Uuid), + Id: pointer.PointerOrNil(r.Uuid), + Project: &apiv2.ProjectServiceCreateRequest{ + Login: r.Tenant, + Name: r.Name, + Description: r.Description, + AvatarUrl: r.AvatarUrl, + Labels: pointer.SafeDeref(r.Meta).Labels, + }, }, &apiv2.ProjectServiceUpdateRequest{ Project: r.Uuid, Name: pointer.PointerOrNil(r.Name), diff --git a/cmd/admin/v2/tenant.go b/cmd/admin/v2/tenant.go index b64daee..dabd553 100644 --- a/cmd/admin/v2/tenant.go +++ b/cmd/admin/v2/tenant.go @@ -41,6 +41,7 @@ func newTenantCmd(c *config.Config) *cobra.Command { cmd.Flags().StringSlice("labels", nil, "lists only tenant with the given labels") }, CreateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("login", "", "the login id of the tenant to create") cmd.Flags().String("name", "", "the name of the tenant to create") cmd.Flags().String("description", "", "the description of the tenant to create") cmd.Flags().String("email", "", "the email of the tenant to create") @@ -48,10 +49,13 @@ func newTenantCmd(c *config.Config) *cobra.Command { }, CreateRequestFromCLI: func() (*adminv2.TenantServiceCreateRequest, error) { return &adminv2.TenantServiceCreateRequest{ - Name: viper.GetString("name"), - Description: pointer.PointerOrNil(viper.GetString("description")), - Email: pointer.PointerOrNil(viper.GetString("email")), - AvatarUrl: pointer.PointerOrNil(viper.GetString("avatar-url")), + Login: pointer.PointerOrNil(viper.GetString("login")), + Tenant: &apiv2.TenantServiceCreateRequest{ + Name: viper.GetString("name"), + Description: pointer.PointerOrNil(viper.GetString("description")), + Email: pointer.PointerOrNil(viper.GetString("email")), + AvatarUrl: pointer.PointerOrNil(viper.GetString("avatar-url")), + }, }, nil }, ValidArgsFn: w.c.Completion.AdminTenant, @@ -136,12 +140,14 @@ func (c *tenant) Delete(id string) (*apiv2.Tenant, error) { func (c *tenant) Convert(r *apiv2.Tenant) (string, *adminv2.TenantServiceCreateRequest, *apiv2.TenantServiceUpdateRequest, error) { return r.Login, &adminv2.TenantServiceCreateRequest{ - Name: r.Name, - Description: pointer.PointerOrNil(r.Description), - Email: pointer.PointerOrNil(r.Email), - AvatarUrl: pointer.PointerOrNil(r.AvatarUrl), - Labels: pointer.SafeDeref(r.Meta).Labels, - Login: pointer.PointerOrNil(r.Login), + Login: pointer.PointerOrNil(r.Login), + Tenant: &apiv2.TenantServiceCreateRequest{ + Name: r.Name, + Description: pointer.PointerOrNil(r.Description), + Email: pointer.PointerOrNil(r.Email), + AvatarUrl: pointer.PointerOrNil(r.AvatarUrl), + Labels: pointer.SafeDeref(r.Meta).Labels, + }, }, &apiv2.TenantServiceUpdateRequest{ Login: r.Login, diff --git a/cmd/api/v2/project_member.go b/cmd/api/v2/project_member.go index 55ac197..fe07ba8 100644 --- a/cmd/api/v2/project_member.go +++ b/cmd/api/v2/project_member.go @@ -5,11 +5,12 @@ import ( "github.com/metal-stack/api/go/errorutil" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" + "github.com/metal-stack/cli/cmd/config" "github.com/metal-stack/cli/cmd/sorters" "github.com/metal-stack/metal-lib/pkg/genericcli" "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/metal-stack/metal-lib/pkg/pointer" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -23,7 +24,7 @@ func newProjectMemberCmd(c *config.Config) *cobra.Command { c: c, } - cmdsConfig := &genericcli.CmdsConfig[*apiv2.ProjectServiceAddMemberRequest, *apiv2.ProjectServiceUpdateMemberRequest, *cliv2.ProjectMember]{ + cmdsConfig := &genericcli.CmdsConfig[*apiv2.ProjectMemberServiceCreateRequest, *apiv2.ProjectMemberServiceUpdateRequest, *apiv2.ProjectMember]{ BinaryName: config.BinaryName, GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), Singular: "member", @@ -75,61 +76,44 @@ func newProjectMemberCmd(c *config.Config) *cobra.Command { return genericcli.NewCmds(cmdsConfig) } -func (c *projectMember) Get(id string) (*cliv2.ProjectMember, error) { +func (c *projectMember) Get(id string) (*apiv2.ProjectMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() project := c.c.GetProject() - resp, err := c.c.Client.Apiv2().Project().Get(ctx, &apiv2.ProjectServiceGetRequest{ + resp, err := c.c.Client.Apiv2().ProjectMember().Get(ctx, &apiv2.ProjectMemberServiceGetRequest{ Project: project, + Member: id, }) if err != nil { - return nil, fmt.Errorf("failed to get project: %w", err) - } - - for _, member := range resp.GetProjectMembers() { - if member.Id == id { - return &cliv2.ProjectMember{ - Project: project, - ProjectMember: member, - }, nil - } + return nil, err } - return nil, fmt.Errorf("unable to find project members %q in project %q", id, project) + return resp.Member, nil } -func (c *projectMember) List() ([]*cliv2.ProjectMember, error) { +func (c *projectMember) List() ([]*apiv2.ProjectMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() project := c.c.GetProject() - resp, err := c.c.Client.Apiv2().Project().Get(ctx, &apiv2.ProjectServiceGetRequest{ + resp, err := c.c.Client.Apiv2().ProjectMember().List(ctx, &apiv2.ProjectMemberServiceListRequest{ Project: project, }) if err != nil { - return nil, fmt.Errorf("failed to get project: %w", err) - } - - var res []*cliv2.ProjectMember - - for _, member := range resp.GetProjectMembers() { - res = append(res, &cliv2.ProjectMember{ - Project: project, - ProjectMember: member, - }) + return nil, err } - return res, nil + return resp.Members, nil } -func (c *projectMember) Create(rq *apiv2.ProjectServiceAddMemberRequest) (*cliv2.ProjectMember, error) { +func (c *projectMember) Create(rq *apiv2.ProjectMemberServiceCreateRequest) (*apiv2.ProjectMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() - resp, err := c.c.Client.Apiv2().Project().AddMember(ctx, rq) + resp, err := c.c.Client.Apiv2().ProjectMember().Create(ctx, rq) if err != nil { if errorutil.IsConflict(err) { return nil, genericcli.AlreadyExistsError() @@ -138,19 +122,16 @@ func (c *projectMember) Create(rq *apiv2.ProjectServiceAddMemberRequest) (*cliv2 return nil, err } - return &cliv2.ProjectMember{ - Project: rq.Project, - ProjectMember: resp.ProjectMember, - }, nil + return resp.Member, nil } -func (c *projectMember) Delete(id string) (*cliv2.ProjectMember, error) { +func (c *projectMember) Delete(id string) (*apiv2.ProjectMember, error) { project := c.c.GetProject() ctx, cancel := c.c.NewRequestContext() defer cancel() - resp, err := c.c.Client.Apiv2().Project().RemoveMember(ctx, &apiv2.ProjectServiceRemoveMemberRequest{ + resp, err := c.c.Client.Apiv2().ProjectMember().Delete(ctx, &apiv2.ProjectMemberServiceDeleteRequest{ Project: project, Member: id, }) @@ -158,42 +139,36 @@ func (c *projectMember) Delete(id string) (*cliv2.ProjectMember, error) { return nil, fmt.Errorf("failed to remove member from project: %w", err) } - return &cliv2.ProjectMember{ - Project: project, - ProjectMember: resp.ProjectMember, - }, nil + return resp.Member, nil } -func (c *projectMember) Convert(r *cliv2.ProjectMember) (string, *apiv2.ProjectServiceAddMemberRequest, *apiv2.ProjectServiceUpdateMemberRequest, error) { - return r.ProjectMember.Id, &apiv2.ProjectServiceAddMemberRequest{ +func (c *projectMember) Convert(r *apiv2.ProjectMember) (string, *apiv2.ProjectMemberServiceCreateRequest, *apiv2.ProjectMemberServiceUpdateRequest, error) { + return r.Member, &apiv2.ProjectMemberServiceCreateRequest{ Project: r.Project, - Member: r.ProjectMember.Id, - Role: r.ProjectMember.Role, + Member: r.Member, + Role: r.Role, }, - &apiv2.ProjectServiceUpdateMemberRequest{ + &apiv2.ProjectMemberServiceUpdateRequest{ Project: r.Project, - Member: r.ProjectMember.Id, - Role: r.ProjectMember.Role, + Member: r.Member, + Role: pointer.PointerOrNil(r.Role), }, nil } -func (c *projectMember) Update(rq *apiv2.ProjectServiceUpdateMemberRequest) (*cliv2.ProjectMember, error) { +func (c *projectMember) Update(rq *apiv2.ProjectMemberServiceUpdateRequest) (*apiv2.ProjectMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() - resp, err := c.c.Client.Apiv2().Project().UpdateMember(ctx, rq) + resp, err := c.c.Client.Apiv2().ProjectMember().Update(ctx, rq) if err != nil { return nil, fmt.Errorf("failed to update member: %w", err) } - return &cliv2.ProjectMember{ - Project: rq.Project, - ProjectMember: resp.ProjectMember, - }, nil + return resp.Member, nil } -func (c *projectMember) updateRequestFromCLI(args []string) (*apiv2.ProjectServiceUpdateMemberRequest, error) { +func (c *projectMember) updateRequestFromCLI(args []string) (*apiv2.ProjectMemberServiceUpdateRequest, error) { member, err := genericcli.GetExactlyOneArg(args) if err != nil { return nil, err @@ -201,19 +176,21 @@ func (c *projectMember) updateRequestFromCLI(args []string) (*apiv2.ProjectServi project := c.c.GetProject() - return &apiv2.ProjectServiceUpdateMemberRequest{ + return &apiv2.ProjectMemberServiceUpdateRequest{ Project: project, Member: member, - Role: apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")]), + Role: pointer.PointerOrNil(apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")])), }, nil } -func (c *projectMember) createRequestFromCLI() (*apiv2.ProjectServiceAddMemberRequest, error) { +func (c *projectMember) createRequestFromCLI() (*apiv2.ProjectMemberServiceCreateRequest, error) { project := c.c.GetProject() - return &apiv2.ProjectServiceAddMemberRequest{ + return &apiv2.ProjectMemberServiceCreateRequest{ Project: project, Member: viper.GetString("id"), Role: apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")]), }, nil } + +// TODO: implement Leave diff --git a/cmd/api/v2/tenant_member.go b/cmd/api/v2/tenant_member.go index da6246e..c0e4513 100644 --- a/cmd/api/v2/tenant_member.go +++ b/cmd/api/v2/tenant_member.go @@ -5,11 +5,12 @@ import ( "github.com/metal-stack/api/go/errorutil" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" + "github.com/metal-stack/cli/cmd/config" "github.com/metal-stack/cli/cmd/sorters" "github.com/metal-stack/metal-lib/pkg/genericcli" "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/metal-stack/metal-lib/pkg/pointer" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -23,7 +24,7 @@ func newTenantMemberCmd(c *config.Config) *cobra.Command { c: c, } - cmdsConfig := &genericcli.CmdsConfig[*apiv2.TenantServiceAddMemberRequest, *apiv2.TenantServiceUpdateMemberRequest, *cliv2.TenantMember]{ + cmdsConfig := &genericcli.CmdsConfig[*apiv2.TenantMemberServiceCreateRequest, *apiv2.TenantMemberServiceUpdateRequest, *apiv2.TenantMember]{ BinaryName: config.BinaryName, GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), Singular: "member", @@ -75,7 +76,7 @@ func newTenantMemberCmd(c *config.Config) *cobra.Command { return genericcli.NewCmds(cmdsConfig) } -func (c *tenantMember) Get(id string) (*cliv2.TenantMember, error) { +func (c *tenantMember) Get(id string) (*apiv2.TenantMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() @@ -84,26 +85,17 @@ func (c *tenantMember) Get(id string) (*cliv2.TenantMember, error) { return nil, err } - resp, err := c.c.Client.Apiv2().Tenant().Get(ctx, &apiv2.TenantServiceGetRequest{ + resp, err := c.c.Client.Apiv2().TenantMember().Get(ctx, &apiv2.TenantMemberServiceGetRequest{ Login: tenant, }) if err != nil { - return nil, fmt.Errorf("failed to get tenant: %w", err) - } - - for _, member := range resp.GetTenantMembers() { - if member.Id == id { - return &cliv2.TenantMember{ - Tenant: tenant, - TenantMember: member, - }, nil - } + return nil, err } - return nil, fmt.Errorf("unable to find tenant members %q in tenant %q", id, tenant) + return resp.Member, nil } -func (c *tenantMember) List() ([]*cliv2.TenantMember, error) { +func (c *tenantMember) List() ([]*apiv2.TenantMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() @@ -112,30 +104,21 @@ func (c *tenantMember) List() ([]*cliv2.TenantMember, error) { return nil, err } - resp, err := c.c.Client.Apiv2().Tenant().Get(ctx, &apiv2.TenantServiceGetRequest{ + resp, err := c.c.Client.Apiv2().TenantMember().List(ctx, &apiv2.TenantMemberServiceListRequest{ Login: tenant, }) if err != nil { - return nil, fmt.Errorf("failed to get tenant: %w", err) + return nil, nil } - var res []*cliv2.TenantMember - - for _, member := range resp.GetTenantMembers() { - res = append(res, &cliv2.TenantMember{ - Tenant: tenant, - TenantMember: member, - }) - } - - return res, nil + return resp.Members, nil } -func (c *tenantMember) Create(rq *apiv2.TenantServiceAddMemberRequest) (*cliv2.TenantMember, error) { +func (c *tenantMember) Create(rq *apiv2.TenantMemberServiceCreateRequest) (*apiv2.TenantMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() - resp, err := c.c.Client.Apiv2().Tenant().AddMember(ctx, rq) + resp, err := c.c.Client.Apiv2().TenantMember().Create(ctx, rq) if err != nil { if errorutil.IsConflict(err) { return nil, genericcli.AlreadyExistsError() @@ -144,13 +127,10 @@ func (c *tenantMember) Create(rq *apiv2.TenantServiceAddMemberRequest) (*cliv2.T return nil, err } - return &cliv2.TenantMember{ - Tenant: rq.Login, - TenantMember: resp.TenantMember, - }, nil + return resp.Member, nil } -func (c *tenantMember) Delete(id string) (*cliv2.TenantMember, error) { +func (c *tenantMember) Delete(id string) (*apiv2.TenantMember, error) { tenant, err := c.c.GetTenant() if err != nil { return nil, err @@ -159,7 +139,7 @@ func (c *tenantMember) Delete(id string) (*cliv2.TenantMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() - resp, err := c.c.Client.Apiv2().Tenant().RemoveMember(ctx, &apiv2.TenantServiceRemoveMemberRequest{ + resp, err := c.c.Client.Apiv2().TenantMember().Delete(ctx, &apiv2.TenantMemberServiceDeleteRequest{ Login: tenant, Member: id, }) @@ -167,42 +147,36 @@ func (c *tenantMember) Delete(id string) (*cliv2.TenantMember, error) { return nil, fmt.Errorf("failed to remove member from tenant: %w", err) } - return &cliv2.TenantMember{ - Tenant: tenant, - TenantMember: resp.TenantMember, - }, nil + return resp.Member, nil } -func (c *tenantMember) Convert(r *cliv2.TenantMember) (string, *apiv2.TenantServiceAddMemberRequest, *apiv2.TenantServiceUpdateMemberRequest, error) { - return r.TenantMember.Id, &apiv2.TenantServiceAddMemberRequest{ +func (c *tenantMember) Convert(r *apiv2.TenantMember) (string, *apiv2.TenantMemberServiceCreateRequest, *apiv2.TenantMemberServiceUpdateRequest, error) { + return r.Member, &apiv2.TenantMemberServiceCreateRequest{ Login: r.Tenant, - Member: r.TenantMember.Id, - Role: r.TenantMember.Role, + Member: r.Member, + Role: r.Role, }, - &apiv2.TenantServiceUpdateMemberRequest{ + &apiv2.TenantMemberServiceUpdateRequest{ Login: r.Tenant, - Member: r.TenantMember.Id, - Role: r.TenantMember.Role, + Member: r.Member, + Role: pointer.PointerOrNil(r.Role), }, nil } -func (c *tenantMember) Update(rq *apiv2.TenantServiceUpdateMemberRequest) (*cliv2.TenantMember, error) { +func (c *tenantMember) Update(rq *apiv2.TenantMemberServiceUpdateRequest) (*apiv2.TenantMember, error) { ctx, cancel := c.c.NewRequestContext() defer cancel() - resp, err := c.c.Client.Apiv2().Tenant().UpdateMember(ctx, rq) + resp, err := c.c.Client.Apiv2().TenantMember().Update(ctx, rq) if err != nil { return nil, fmt.Errorf("failed to update member: %w", err) } - return &cliv2.TenantMember{ - Tenant: rq.Login, - TenantMember: resp.TenantMember, - }, nil + return resp.Member, nil } -func (c *tenantMember) updateRequestFromCLI(args []string) (*apiv2.TenantServiceUpdateMemberRequest, error) { +func (c *tenantMember) updateRequestFromCLI(args []string) (*apiv2.TenantMemberServiceUpdateRequest, error) { member, err := genericcli.GetExactlyOneArg(args) if err != nil { return nil, err @@ -213,22 +187,24 @@ func (c *tenantMember) updateRequestFromCLI(args []string) (*apiv2.TenantService return nil, err } - return &apiv2.TenantServiceUpdateMemberRequest{ + return &apiv2.TenantMemberServiceUpdateRequest{ Login: tenant, Member: member, - Role: apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")]), + Role: pointer.PointerOrNil(apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")])), }, nil } -func (c *tenantMember) createRequestFromCLI() (*apiv2.TenantServiceAddMemberRequest, error) { +func (c *tenantMember) createRequestFromCLI() (*apiv2.TenantMemberServiceCreateRequest, error) { tenant, err := c.c.GetTenant() if err != nil { return nil, err } - return &apiv2.TenantServiceAddMemberRequest{ + return &apiv2.TenantMemberServiceCreateRequest{ Login: tenant, Member: viper.GetString("id"), Role: apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")]), }, nil } + +// TODO: implement Leave diff --git a/cmd/completion/project.go b/cmd/completion/project.go index c4cc89a..689b355 100644 --- a/cmd/completion/project.go +++ b/cmd/completion/project.go @@ -53,7 +53,7 @@ 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{ + resp, err := c.Client.Apiv2().ProjectMember().List(cmd.Context(), &apiv2.ProjectMemberServiceListRequest{ Project: c.Proj, }) if err != nil { @@ -62,8 +62,8 @@ func (c *Completion) ProjectMember(cmd *cobra.Command, args []string, toComplete var names []string - for _, member := range resp.ProjectMembers { - names = append(names, member.Id+"\t"+member.Role.String()) + for _, member := range resp.Members { + names = append(names, member.Member+"\t"+member.Role.String()) } return names, cobra.ShellCompDirectiveNoFileComp diff --git a/cmd/completion/tenant.go b/cmd/completion/tenant.go index e99e735..934d406 100644 --- a/cmd/completion/tenant.go +++ b/cmd/completion/tenant.go @@ -66,7 +66,7 @@ func (c *Completion) TenantMember(cmd *cobra.Command, args []string, toComplete return nil, cobra.ShellCompDirectiveError } - resp, err := c.Client.Apiv2().Tenant().Get(cmd.Context(), &apiv2.TenantServiceGetRequest{ + resp, err := c.Client.Apiv2().TenantMember().List(cmd.Context(), &apiv2.TenantMemberServiceListRequest{ Login: projectResp.Project.Tenant, }) if err != nil { @@ -75,8 +75,8 @@ func (c *Completion) TenantMember(cmd *cobra.Command, args []string, toComplete var names []string - for _, member := range resp.TenantMembers { - names = append(names, member.Id+"\t"+member.Role.String()) + for _, member := range resp.Members { + names = append(names, member.Member+"\t"+member.Role.String()) } return names, cobra.ShellCompDirectiveNoFileComp diff --git a/cmd/sorters/project.go b/cmd/sorters/project.go index 70fcaee..b38377c 100644 --- a/cmd/sorters/project.go +++ b/cmd/sorters/project.go @@ -2,7 +2,7 @@ package sorters import ( apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" + "github.com/metal-stack/metal-lib/pkg/multisort" ) @@ -37,25 +37,25 @@ func ProjectInviteSorter() *multisort.Sorter[*apiv2.ProjectInvite] { }, multisort.Keys{{ID: "project"}, {ID: "role"}, {ID: "expiration"}}) } -func ProjectMemberSorter() *multisort.Sorter[*cliv2.ProjectMember] { - return multisort.New(multisort.FieldMap[*cliv2.ProjectMember]{ - "id": func(a, b *cliv2.ProjectMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.ProjectMember.Id, b.ProjectMember.Id, descending) +func ProjectMemberSorter() *multisort.Sorter[*apiv2.ProjectMember] { + return multisort.New(multisort.FieldMap[*apiv2.ProjectMember]{ + "id": func(a, b *apiv2.ProjectMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.Member, b.Member, descending) }, - "role": func(a, b *cliv2.ProjectMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.ProjectMember.Role, b.ProjectMember.Role, descending) + "role": func(a, b *apiv2.ProjectMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.Role, b.Role, descending) }, - "created": func(a, b *cliv2.ProjectMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.ProjectMember.CreatedAt.AsTime().UnixMilli(), b.ProjectMember.CreatedAt.AsTime().UnixMilli(), descending) + "created": func(a, b *apiv2.ProjectMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.Meta.CreatedAt.AsTime().UnixMilli(), b.Meta.CreatedAt.AsTime().UnixMilli(), descending) }, - "inherited": func(a, b *cliv2.ProjectMember, descending bool) multisort.CompareResult { + "inherited": func(a, b *apiv2.ProjectMember, descending bool) multisort.CompareResult { boolToInt := func(in bool) int { if in { return 1 } return 0 } - return multisort.Compare(boolToInt(a.ProjectMember.InheritedMembership), boolToInt(b.ProjectMember.InheritedMembership), descending) + return multisort.Compare(boolToInt(a.InheritedMembership), boolToInt(b.InheritedMembership), descending) }, }, multisort.Keys{{ID: "inherited", Descending: false}, {ID: "role"}, {ID: "id"}}) } diff --git a/cmd/sorters/tenant.go b/cmd/sorters/tenant.go index 6e8554f..7768c60 100644 --- a/cmd/sorters/tenant.go +++ b/cmd/sorters/tenant.go @@ -2,7 +2,7 @@ package sorters import ( apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" + "github.com/metal-stack/metal-lib/pkg/multisort" ) @@ -37,16 +37,16 @@ func TenantInviteSorter() *multisort.Sorter[*apiv2.TenantInvite] { }, multisort.Keys{{ID: "tenant"}, {ID: "role"}, {ID: "expiration"}}) } -func TenantMemberSorter() *multisort.Sorter[*cliv2.TenantMember] { - return multisort.New(multisort.FieldMap[*cliv2.TenantMember]{ - "id": func(a, b *cliv2.TenantMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.TenantMember.Id, b.TenantMember.Id, descending) +func TenantMemberSorter() *multisort.Sorter[*apiv2.TenantMember] { + return multisort.New(multisort.FieldMap[*apiv2.TenantMember]{ + "id": func(a, b *apiv2.TenantMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.Member, b.Member, descending) }, - "role": func(a, b *cliv2.TenantMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.TenantMember.Role, b.TenantMember.Role, descending) + "role": func(a, b *apiv2.TenantMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.Role, b.Role, descending) }, - "created": func(a, b *cliv2.TenantMember, descending bool) multisort.CompareResult { - return multisort.Compare(a.TenantMember.CreatedAt.AsTime().UnixMilli(), b.TenantMember.CreatedAt.AsTime().UnixMilli(), descending) + "created": func(a, b *apiv2.TenantMember, descending bool) multisort.CompareResult { + return multisort.Compare(a.Meta.CreatedAt.AsTime().UnixMilli(), b.Meta.CreatedAt.AsTime().UnixMilli(), descending) }, }, multisort.Keys{{ID: "role"}, {ID: "id"}}) } diff --git a/cmd/tableprinters/common.go b/cmd/tableprinters/common.go index fdbfac2..5397881 100644 --- a/cmd/tableprinters/common.go +++ b/cmd/tableprinters/common.go @@ -8,7 +8,7 @@ import ( adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" + "github.com/metal-stack/cli/cmd/config" "github.com/metal-stack/metal-lib/pkg/genericcli/printers" "github.com/metal-stack/metal-lib/pkg/pointer" @@ -94,9 +94,9 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin return t.ProjectInviteTable(pointer.WrapInSlice(d), wide) case []*apiv2.ProjectInvite: return t.ProjectInviteTable(d, wide) - case *cliv2.ProjectMember: + case *apiv2.ProjectMember: return t.ProjectMemberTable(pointer.WrapInSlice(d), wide) - case []*cliv2.ProjectMember: + case []*apiv2.ProjectMember: return t.ProjectMemberTable(d, wide) case *adminv2.TaskInfo: @@ -128,9 +128,9 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin return t.TenantInviteTable(pointer.WrapInSlice(d), wide) case []*apiv2.TenantInvite: return t.TenantInviteTable(d, wide) - case *cliv2.TenantMember: + case *apiv2.TenantMember: return t.TenantMemberTable(pointer.WrapInSlice(d), wide) - case []*cliv2.TenantMember: + case []*apiv2.TenantMember: return t.TenantMemberTable(d, wide) case *apiv2.Health: diff --git a/cmd/tableprinters/project.go b/cmd/tableprinters/project.go index c34fbb3..88df538 100644 --- a/cmd/tableprinters/project.go +++ b/cmd/tableprinters/project.go @@ -6,7 +6,7 @@ import ( "github.com/dustin/go-humanize" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" + "github.com/metal-stack/metal-lib/pkg/genericcli" ) @@ -55,7 +55,7 @@ func (t *TablePrinter) ProjectInviteTable(data []*apiv2.ProjectInvite, _ bool) ( return header, rows, nil } -func (t *TablePrinter) ProjectMemberTable(data []*cliv2.ProjectMember, _ bool) ([]string, [][]string, error) { +func (t *TablePrinter) ProjectMemberTable(data []*apiv2.ProjectMember, _ bool) ([]string, [][]string, error) { var ( rows [][]string ) @@ -63,10 +63,10 @@ func (t *TablePrinter) ProjectMemberTable(data []*cliv2.ProjectMember, _ bool) ( for _, member := range data { row := []string{ - member.ProjectMember.Id, - member.ProjectMember.Role.String(), - strconv.FormatBool(member.ProjectMember.InheritedMembership), - humanize.Time(member.ProjectMember.CreatedAt.AsTime()), + member.Member, + member.Role.String(), + strconv.FormatBool(member.InheritedMembership), + humanize.Time(member.Meta.CreatedAt.AsTime()), } rows = append(rows, row) diff --git a/cmd/tableprinters/tenant.go b/cmd/tableprinters/tenant.go index 6778559..35558e9 100644 --- a/cmd/tableprinters/tenant.go +++ b/cmd/tableprinters/tenant.go @@ -4,7 +4,6 @@ import ( "github.com/dustin/go-humanize" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - cliv2 "github.com/metal-stack/api/go/metalstack/cli/v2" ) func (t *TablePrinter) TenantTable(data []*apiv2.Tenant, wide bool) ([]string, [][]string, error) { @@ -25,7 +24,7 @@ func (t *TablePrinter) TenantTable(data []*apiv2.Tenant, wide bool) ([]string, [ return header, rows, nil } -func (t *TablePrinter) TenantMemberTable(data []*cliv2.TenantMember, _ bool) ([]string, [][]string, error) { +func (t *TablePrinter) TenantMemberTable(data []*apiv2.TenantMember, _ bool) ([]string, [][]string, error) { var ( rows [][]string ) @@ -33,9 +32,9 @@ func (t *TablePrinter) TenantMemberTable(data []*cliv2.TenantMember, _ bool) ([] for _, member := range data { row := []string{ - member.TenantMember.Id, - member.TenantMember.Role.String(), - humanize.Time(member.TenantMember.CreatedAt.AsTime()), + member.Member, + member.Role.String(), + humanize.Time(member.Meta.CreatedAt.AsTime()), } rows = append(rows, row) diff --git a/docs/admin/metalctlv2_admin_machine_create.md b/docs/admin/metalctlv2_admin_machine_create.md index 2076e74..6fc1dc6 100644 --- a/docs/admin/metalctlv2_admin_machine_create.md +++ b/docs/admin/metalctlv2_admin_machine_create.md @@ -49,45 +49,46 @@ Once created the machine installation can not be modified anymore. ### Options ``` - --allocation-type string allocation type, can be either machine|firewall (default "machine") - --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. - --description string Description of the machine to create. [optional] - --dns-servers strings dns servers to add to the machine or firewall. [optional] - -f, --file string filename of the create or update request in yaml format, or - for stdin. - - Example: - $ metalctlv2 machine describe machine-1 -o yaml > machine.yaml - $ vi machine.yaml - $ # either via stdin - $ cat machine.yaml | metalctlv2 machine create -f - - $ # or via file - $ metalctlv2 machine create -f machine.yaml - - the file can also contain multiple documents and perform a bulk operation. - - --filesystem-layout string Filesystemlayout to use during machine installation. [optional] - -h, --help help for create - --hostname string Hostname of the machine. [required] - --id string id of the machine to create. [optional] - --image string OS Image to install. [required] - --labels strings labels to add to the machine, use it like: --labels "a=b" or --labels "a=". - --name string Name of the machine. [optional] - --networks strings Adds a network. Usage: [--networks NETWORK[:ip[;ip]][,NETWORK[:ip[;ip]]... - NETWORK specifies the name or id of an existing network. - IPs can be added per network colon separated, these ips must be already allocated upfront. If no ip(s) are specified per network, one ip per network is allocated. - - --ntp-servers strings ntp servers to add to the machine or firewall. [optional] - --partition string partition/datacenter where the machine is created. [required, except for reserved machines] - --placement-labels strings placement tags used for rack spreading - -p, --project string Project where the machine should belong to. [required] - --size string Size of the machine. [required, except for reserved machines] - --skip-security-prompts skips security prompt for bulk operations - -i, --ssh-public-key string SSH public key for access via ssh and console. [optional] - Can be either the public key as string, or pointing to the public key file to use e.g.: "@~/.ssh/id_rsa.pub". - If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picked as default, matching the first one in this order. - --timestamps when used with --file (bulk operation): prints timestamps in-between the operations - --userdata string cloud-init.io compatible userdata. [optional] - Can be either the userdata as string, or pointing to the userdata file to use e.g.: "@/tmp/userdata.cfg". + --allocation-type string allocation type, can be either machine|firewall (default "machine") + --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. + --description string Description of the machine to create. [optional] + --dns-servers strings dns servers to add to the machine or firewall. [optional] + -f, --file string filename of the create or update request in yaml format, or - for stdin. + + Example: + $ metalctlv2 machine describe machine-1 -o yaml > machine.yaml + $ vi machine.yaml + $ # either via stdin + $ cat machine.yaml | metalctlv2 machine create -f - + $ # or via file + $ metalctlv2 machine create -f machine.yaml + + the file can also contain multiple documents and perform a bulk operation. + + --filesystem-layout string Filesystemlayout to use during machine installation. [optional] + -h, --help help for create + --hostname string Hostname of the machine. [required] + --id string id of the machine to create. [optional] + --image string OS Image to install. [required] + --labels strings labels to add to the machine, use it like: --labels "a=b" or --labels "a=". + --name string Name of the machine. [optional] + --networks strings Adds a network. Usage: [--networks NETWORK[:ip[;ip]][,NETWORK[:ip[;ip]]... + NETWORK specifies the name or id of an existing network. + IPs can be added per network colon separated, these ips must be already allocated upfront. If no ip(s) are specified per network, one ip per network is allocated. + + --ntp-servers strings ntp servers to add to the machine or firewall. [optional] + --partition string partition/datacenter where the machine is created. [required, except for reserved machines] + --placement-labels strings placement tags used for rack spreading + -p, --project string Project where the machine should belong to. [required] + --size string Size of the machine. [required, except for reserved machines] + --skip-security-prompts skips security prompt for bulk operations + -i, --ssh-public-key stringArray SSH public key for access via ssh and console. [optional] + Multiple keys can be passed by doing -i [key] -i [key] ... + Can be either the public keys as strings, or pointing to the public keys file to use e.g.: "@~/.ssh/id_rsa.pub". + If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picked as default, matching the first one in this order. + --timestamps when used with --file (bulk operation): prints timestamps in-between the operations + --userdata string cloud-init.io compatible userdata. [optional] + Can be either the userdata as string, or pointing to the userdata file to use e.g.: "@/tmp/userdata.cfg". ``` ### Options inherited from parent commands diff --git a/docs/admin/metalctlv2_admin_tenant_create.md b/docs/admin/metalctlv2_admin_tenant_create.md index 63e9dbb..901b76c 100644 --- a/docs/admin/metalctlv2_admin_tenant_create.md +++ b/docs/admin/metalctlv2_admin_tenant_create.md @@ -26,6 +26,7 @@ metalctlv2 admin tenant create [flags] the file can also contain multiple documents and perform a bulk operation. -h, --help help for create + --login string the login id of the tenant to create --name string the name of the tenant to create --skip-security-prompts skips security prompt for bulk operations --timestamps when used with --file (bulk operation): prints timestamps in-between the operations diff --git a/docs/metalctlv2_machine_create.md b/docs/metalctlv2_machine_create.md index 325fcf1..d1f474a 100644 --- a/docs/metalctlv2_machine_create.md +++ b/docs/metalctlv2_machine_create.md @@ -9,44 +9,45 @@ metalctlv2 machine create [flags] ### Options ``` - --allocation-type string allocation type, can be either machine|firewall (default "machine") - --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. - --description string Description of the machine to create. [optional] - --dns-servers strings dns servers to add to the machine or firewall. [optional] - -f, --file string filename of the create or update request in yaml format, or - for stdin. - - Example: - $ metalctlv2 machine describe machine-1 -o yaml > machine.yaml - $ vi machine.yaml - $ # either via stdin - $ cat machine.yaml | metalctlv2 machine create -f - - $ # or via file - $ metalctlv2 machine create -f machine.yaml - - the file can also contain multiple documents and perform a bulk operation. - - --filesystem-layout string Filesystemlayout to use during machine installation. [optional] - -h, --help help for create - --hostname string Hostname of the machine. [required] - --image string OS Image to install. [required] - --labels strings labels to add to the machine, use it like: --labels "a=b" or --labels "a=". - --name string Name of the machine. [optional] - --networks strings Adds a network. Usage: [--networks NETWORK[:ip[;ip]][,NETWORK[:ip[;ip]]... - NETWORK specifies the name or id of an existing network. - IPs can be added per network colon separated, these ips must be already allocated upfront. If no ip(s) are specified per network, one ip per network is allocated. - - --ntp-servers strings ntp servers to add to the machine or firewall. [optional] - --partition string partition/datacenter where the machine is created. [required, except for reserved machines] - --placement-labels strings placement tags used for rack spreading - -p, --project string Project where the machine should belong to. [required] - --size string Size of the machine. [required, except for reserved machines] - --skip-security-prompts skips security prompt for bulk operations - -i, --ssh-public-key string SSH public key for access via ssh and console. [optional] - Can be either the public key as string, or pointing to the public key file to use e.g.: "@~/.ssh/id_rsa.pub". - If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picked as default, matching the first one in this order. - --timestamps when used with --file (bulk operation): prints timestamps in-between the operations - --userdata string cloud-init.io compatible userdata. [optional] - Can be either the userdata as string, or pointing to the userdata file to use e.g.: "@/tmp/userdata.cfg". + --allocation-type string allocation type, can be either machine|firewall (default "machine") + --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. + --description string Description of the machine to create. [optional] + --dns-servers strings dns servers to add to the machine or firewall. [optional] + -f, --file string filename of the create or update request in yaml format, or - for stdin. + + Example: + $ metalctlv2 machine describe machine-1 -o yaml > machine.yaml + $ vi machine.yaml + $ # either via stdin + $ cat machine.yaml | metalctlv2 machine create -f - + $ # or via file + $ metalctlv2 machine create -f machine.yaml + + the file can also contain multiple documents and perform a bulk operation. + + --filesystem-layout string Filesystemlayout to use during machine installation. [optional] + -h, --help help for create + --hostname string Hostname of the machine. [required] + --image string OS Image to install. [required] + --labels strings labels to add to the machine, use it like: --labels "a=b" or --labels "a=". + --name string Name of the machine. [optional] + --networks strings Adds a network. Usage: [--networks NETWORK[:ip[;ip]][,NETWORK[:ip[;ip]]... + NETWORK specifies the name or id of an existing network. + IPs can be added per network colon separated, these ips must be already allocated upfront. If no ip(s) are specified per network, one ip per network is allocated. + + --ntp-servers strings ntp servers to add to the machine or firewall. [optional] + --partition string partition/datacenter where the machine is created. [required, except for reserved machines] + --placement-labels strings placement tags used for rack spreading + -p, --project string Project where the machine should belong to. [required] + --size string Size of the machine. [required, except for reserved machines] + --skip-security-prompts skips security prompt for bulk operations + -i, --ssh-public-key stringArray SSH public key for access via ssh and console. [optional] + Multiple keys can be passed by doing -i [key] -i [key] ... + Can be either the public keys as strings, or pointing to the public keys file to use e.g.: "@~/.ssh/id_rsa.pub". + If ~/.ssh/[id_ed25519.pub | id_rsa.pub | id_dsa.pub] is present it will be picked as default, matching the first one in this order. + --timestamps when used with --file (bulk operation): prints timestamps in-between the operations + --userdata string cloud-init.io compatible userdata. [optional] + Can be either the userdata as string, or pointing to the userdata file to use e.g.: "@/tmp/userdata.cfg". ``` ### Options inherited from parent commands diff --git a/go.mod b/go.mod index cf0dfe8..281b176 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module github.com/metal-stack/cli go 1.27 require ( - connectrpc.com/connect v1.20.0 + connectrpc.com/connect v1.21.1 connectrpc.com/validate v0.6.0 github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.19.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/metal-stack/api v0.8.0 + github.com/metal-stack/api v0.8.3-0.20261021120011-e0b0b375ce54 github.com/metal-stack/metal-console v0.8.2 github.com/metal-stack/metal-lib v0.26.3 github.com/metal-stack/v v1.0.3 @@ -17,7 +17,7 @@ require ( github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.12.1 - google.golang.org/grpc v1.83.2 + google.golang.org/grpc v1.84.0 google.golang.org/protobuf v1.36.12 sigs.k8s.io/yaml v1.6.0 ) @@ -58,14 +58,14 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jsimonetti/rtnetlink v1.4.2 // indirect - github.com/klauspost/compress v1.20.0 // indirect + github.com/klauspost/compress v1.20.3 // 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 github.com/mattn/go-runewidth v0.0.28 // indirect github.com/mdlayher/netlink v1.11.2 // indirect github.com/mdlayher/socket v0.6.1 // indirect - github.com/minio/minlz v1.2.0 // indirect + github.com/minio/minlz v1.2.2 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect github.com/oklog/ulid/v2 v2.1.2 // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect @@ -93,18 +93,18 @@ require ( go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect go4.org/netipx v0.0.0-20260823151212-3075585bcbeb // indirect golang.org/x/crypto v0.56.0 // indirect - golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa // indirect + golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.22.0 // indirect - golang.org/x/sys v0.47.0 // indirect + golang.org/x/sync v0.24.0 // indirect + golang.org/x/sys v0.49.0 // indirect golang.org/x/term v0.45.0 // indirect - golang.org/x/text v0.41.0 // indirect + golang.org/x/text v0.43.0 // indirect golang.org/x/time v0.15.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard/windows v1.0.1 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260831171406-18b4a7587f8a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260831171406-18b4a7587f8a // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20261012220534-4341edf3f941 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gvisor.dev/gvisor v0.0.0-20260224225140-573d5e7127a8 // indirect diff --git a/go.sum b/go.sum index 2aa4f99..8cad458 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,8 @@ cel.dev/cel-go v0.32.0 h1:irvpFKr5EuGPyxeME03ERh0rii1TX+BDAnB9eL3IvNk= cel.dev/cel-go v0.32.0/go.mod h1:DnVip7tpJSsgZymwfT+m1tnEVy3ivAjSMXPx12YrMkU= cel.dev/expr v0.25.3 h1:A2jO8jwOugrrovveCWfj0KEZOfqiLgAcwjpHPhzIGw0= cel.dev/expr v0.25.3/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= -connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= +connectrpc.com/connect v1.21.1 h1:h2kacdlzXpZU2OQFSv98xkjYXNUSr/ptxmGVbS/+CRM= +connectrpc.com/connect v1.21.1/go.mod h1:fhMcVJ5w6r/yTYhnkEbW6+sIF7OZ5Ay7KYgmfebh2ek= connectrpc.com/validate v0.6.0 h1:DcrgDKt2ZScrUs/d/mh9itD2yeEa0UbBBa+i0mwzx+4= connectrpc.com/validate v0.6.0/go.mod h1:ihrpI+8gVbLH1fvVWJL1I3j0CfWnF8P/90LsmluRiZs= filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= @@ -165,8 +165,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jsimonetti/rtnetlink v1.4.2 h1:Df9w9TZ3npHTyDn0Ev9e1uzmN2odmXd0QX+J5GTEn90= github.com/jsimonetti/rtnetlink v1.4.2/go.mod h1:92s6LJdE+1iOrw+F2/RO7LYI2Qd8pPpFNNUYW06gcoM= -github.com/klauspost/compress v1.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA= -github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= +github.com/klauspost/compress v1.20.3 h1:zWNoFqq/lSuYQRim3dUXlXeA0IqwuZGicdG5ZTfY5j4= +github.com/klauspost/compress v1.20.3/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= 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/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ= @@ -191,8 +191,8 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A= github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg= -github.com/metal-stack/api v0.8.0 h1:olAQtHx7uv3f/jaKKo7aVILhZgO5McQTLeKfS0FkolM= -github.com/metal-stack/api v0.8.0/go.mod h1:rWdj0dTAg2w/WuQhIZ+JVp9b68v6VVCm1k7sLx4U+PQ= +github.com/metal-stack/api v0.8.3-0.20261021120011-e0b0b375ce54 h1:lE8/IUqC62CmpwrVtGNm/KxslAnVZioJSko2GpiQSfQ= +github.com/metal-stack/api v0.8.3-0.20261021120011-e0b0b375ce54/go.mod h1:mx2KOLoCwogU/riKSYTYAb1DXtP7n4A0+FitzOxlCUE= github.com/metal-stack/metal-console v0.8.2 h1:Y86penzEROJKcLica1PeE2ddtr9G0/u16HiCAYXaxxU= github.com/metal-stack/metal-console v0.8.2/go.mod h1:ZkselOaxF/iELFL9Tku3maXgpX4DM0CHjoG7CGuky6E= github.com/metal-stack/metal-lib v0.26.3 h1:K5gLoD65m6p3l6qCPrfavIdvNdWfmF2QdXrvU2URaZs= @@ -201,8 +201,8 @@ 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/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= -github.com/minio/minlz v1.2.0 h1:6IOBuiHg04QxvbFfgFLT/9sMaO/UhL7S+ApW1mK8q5A= -github.com/minio/minlz v1.2.0/go.mod h1:Ls9H7nlkASeCcdl5thjVD5Eraj6z+zGa7xtq57jIKD4= +github.com/minio/minlz v1.2.2 h1:o3YoP6f3VG0oY3Ykhx2mHrpCywsoobsFPVWslgfKcbY= +github.com/minio/minlz v1.2.2/go.mod h1:xHhZ+MJmC2U4ssK/rSENsoyCfv0VFQo1mT6AdCA2RJ8= github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -304,43 +304,43 @@ go4.org/netipx v0.0.0-20260823151212-3075585bcbeb h1:XBM4hvfwGAttkkiTIFfeigdfcL1 go4.org/netipx v0.0.0-20260823151212-3075585bcbeb/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y= golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I= -golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa h1:QSyA8ishJCyT21kER9KwNt0b7BM3iRK4x9QXhjN5Fdk= -golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa/go.mod h1:zeBbvyFKDaLwa7CH/zI8KXt7gTl14SF7sO08Pl5jBCM= +golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e h1:HgI6W1OolKK6tcV74is+rl9SMT0njsHlp0TbQiGXI/A= +golang.org/x/exp v0.0.0-20261014143641-f0251eb4aa3e/go.mod h1:DBoxwWhjaW8DJJw/YeM4zpeOKMC1FMXSblsYy+1mFSw= golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8= golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.41.0 h1:8wS72eGJMJaBxK6okTzd4WaXumUlTVlb753MlsSvTCo= golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA= -golang.org/x/mod v0.39.0 h1:UF5zwQdCRRUpHfyPwr7d4UrGiVeldIsogtzWVnczL74= -golang.org/x/mod v0.39.0/go.mod h1:bvIbwjQ0HUFFf5AKukeeYQG4ZBUG9yxQbR9aEweIwYY= +golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg= +golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs= golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= -golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= -golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA= +golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= +golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= +golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= -golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= -golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= +golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI= -golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo= +golang.org/x/tools v0.52.0 h1:ryxg4U63oaUYk7uOLTF37n43f4qgtGvqV4Ioit1BaEs= +golang.org/x/tools v0.52.0/go.mod h1:ItcP5UNaVGG+Yc4eIYmaFLNJ4soEopkx5Y2d4lcEurs= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20260522210424-ecfc5a8d5446 h1:cqHQ3AycTHvM2R7ikgyX57D+XvtcSnGylsLkOVhta/w= golang.zx2c4.com/wireguard v0.0.0-20260522210424-ecfc5a8d5446/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= golang.zx2c4.com/wireguard/windows v1.0.1 h1:eOxiDVbywPC+ZQqvdCK7x+ZwWXKbYv50TtH8ysFIbw8= golang.zx2c4.com/wireguard/windows v1.0.1/go.mod h1:+fbT3FFdX4zzYDLwJh5+HPEcNN/3HyNdzhNSVsQM+zs= -google.golang.org/genproto/googleapis/api v0.0.0-20260831171406-18b4a7587f8a h1:i3TAXhpKc7TUP1VAPiBBrv45kamjoizCC3rOC0cAbOs= -google.golang.org/genproto/googleapis/api v0.0.0-20260831171406-18b4a7587f8a/go.mod h1:CvYJHpbzPlT0fb/PsgtAamdwru/GVxUsomFdXTpOTI8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260831171406-18b4a7587f8a h1:3Dnd1cDaZlB68lziofO+bJXpjOy8UfRv8Unt+yH8tQ4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260831171406-18b4a7587f8a/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= +google.golang.org/genproto/googleapis/api v0.0.0-20261012220534-4341edf3f941 h1:DS/w3N8rsfrwxwgGSj6WhMsPo7aNQGmDx74J81zd0NQ= +google.golang.org/genproto/googleapis/api v0.0.0-20261012220534-4341edf3f941/go.mod h1:AuGAdi9KnXss7xbLxIKhCvk+Ro/SxpET/iS45DNYN+M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941 h1:QG6hS4zFplRZvOQedzei4t2pT/zkQc3VR1u+S9/rcAw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261012220534-4341edf3f941/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/tests/e2e/admin/tenant_test.go b/tests/e2e/admin/tenant_test.go index 62cb850..fe59aeb 100644 --- a/tests/e2e/admin/tenant_test.go +++ b/tests/e2e/admin/tenant_test.go @@ -46,9 +46,11 @@ func Test_AdminTenantCmd_Create(t *testing.T) { ClientCalls: []client.ClientCall{ { WantRequest: &adminv2.TenantServiceCreateRequest{ - Name: adminTenant1().Name, - Description: new(adminTenant1().Description), - Email: new(adminTenant1().Email), + Tenant: &apiv2.TenantServiceCreateRequest{ + Name: adminTenant1().Name, + Description: new(adminTenant1().Description), + Email: new(adminTenant1().Email), + }, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&adminv2.TenantServiceCreateResponse{ diff --git a/tests/e2e/api/project_test.go b/tests/e2e/api/project_test.go index 3376cba..83b8b7c 100644 --- a/tests/e2e/api/project_test.go +++ b/tests/e2e/api/project_test.go @@ -539,115 +539,116 @@ func Test_ProjectCmd_Join(t *testing.T) { } } -func Test_ProjectCmd_ListMembers(t *testing.T) { - tests := []*e2e.Test[apiv2.ProjectServiceGetRequest, []apiv2.ProjectMember]{ - { - Name: "list project members", - CmdArgs: []string{"project", "member", "list", "--project", testresources.Project1().Uuid}, - NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ - ClientCalls: []client.ClientCall{ - { - WantRequest: &apiv2.ProjectServiceGetRequest{ - Project: testresources.Project1().Uuid, - }, - WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.ProjectServiceGetResponse{ - Project: testresources.Project1(), - ProjectMembers: []*apiv2.ProjectMember{ - testresources.Project1Member(), testresources.Project2Member(), - }, - }) - }, - }, - }, - }), - WantTable: new(` - ID ROLE INHERITED SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now - 40c0da4b-9eb9-4371-91aa-1ae62193fa54 PROJECT_ROLE_EDITOR true now - `), - WantWideTable: new(` - ID ROLE INHERITED SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now - 40c0da4b-9eb9-4371-91aa-1ae62193fa54 PROJECT_ROLE_EDITOR true now - `), - Template: new("{{ .project_member.id }} {{ .project_member.role }}"), - WantTemplate: new(` -16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 1 -40c0da4b-9eb9-4371-91aa-1ae62193fa54 2 - `), - WantMarkdown: new(` - | ID | ROLE | INHERITED | SINCE | - |--------------------------------------|---------------------|-----------|-------| - | 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 | PROJECT_ROLE_OWNER | false | now | - | 40c0da4b-9eb9-4371-91aa-1ae62193fa54 | PROJECT_ROLE_EDITOR | true | now | - `), - }, - } - for _, tt := range tests { - tt.TestCmd(t) - } -} +// FIXME: create member tests +// func Test_ProjectCmd_ListMembers(t *testing.T) { +// tests := []*e2e.Test[apiv2.ProjectServiceGetRequest, []apiv2.ProjectMember]{ +// { +// Name: "list project members", +// CmdArgs: []string{"project", "member", "list", "--project", testresources.Project1().Uuid}, +// NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ +// ClientCalls: []client.ClientCall{ +// { +// WantRequest: &apiv2.ProjectServiceGetRequest{ +// Project: testresources.Project1().Uuid, +// }, +// WantResponse: func() connect.AnyResponse { +// return connect.NewResponse(&apiv2.ProjectServiceGetResponse{ +// Project: testresources.Project1(), +// ProjectMembers: []*apiv2.ProjectMember{ +// testresources.Project1Member(), testresources.Project2Member(), +// }, +// }) +// }, +// }, +// }, +// }), +// WantTable: new(` +// ID ROLE INHERITED SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now +// 40c0da4b-9eb9-4371-91aa-1ae62193fa54 PROJECT_ROLE_EDITOR true now +// `), +// WantWideTable: new(` +// ID ROLE INHERITED SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now +// 40c0da4b-9eb9-4371-91aa-1ae62193fa54 PROJECT_ROLE_EDITOR true now +// `), +// Template: new("{{ .project_member.id }} {{ .project_member.role }}"), +// WantTemplate: new(` +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 1 +// 40c0da4b-9eb9-4371-91aa-1ae62193fa54 2 +// `), +// WantMarkdown: new(` +// | ID | ROLE | INHERITED | SINCE | +// |--------------------------------------|---------------------|-----------|-------| +// | 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 | PROJECT_ROLE_OWNER | false | now | +// | 40c0da4b-9eb9-4371-91aa-1ae62193fa54 | PROJECT_ROLE_EDITOR | true | now | +// `), +// }, +// } +// for _, tt := range tests { +// tt.TestCmd(t) +// } +// } -func Test_ProjectCmd_DeleteMember(t *testing.T) { - tests := []*e2e.Test[apiv2.ProjectServiceRemoveMemberResponse, string]{ - { - Name: "delete project member", - CmdArgs: []string{"project", "member", "delete", testresources.Project1Member().Id, "--project", testresources.Project1().Uuid}, - NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ - ClientCalls: []client.ClientCall{ - { - WantRequest: &apiv2.ProjectServiceRemoveMemberRequest{ - Project: testresources.Project1().Uuid, - Member: testresources.Project1Member().Id, - }, - WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.ProjectServiceRemoveMemberResponse{ - ProjectMember: testresources.Project1Member(), - }) - }, - }, - }, - }), - WantTable: new(` - ID ROLE INHERITED SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now - `), - }, - } - for _, tt := range tests { - tt.TestCmd(t) - } -} +// func Test_ProjectCmd_DeleteMember(t *testing.T) { +// tests := []*e2e.Test[apiv2.ProjectServiceRemoveMemberResponse, string]{ +// { +// Name: "delete project member", +// CmdArgs: []string{"project", "member", "delete", testresources.Project1Member().Id, "--project", testresources.Project1().Uuid}, +// NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ +// ClientCalls: []client.ClientCall{ +// { +// WantRequest: &apiv2.ProjectServiceRemoveMemberRequest{ +// Project: testresources.Project1().Uuid, +// Member: testresources.Project1Member().Id, +// }, +// WantResponse: func() connect.AnyResponse { +// return connect.NewResponse(&apiv2.ProjectServiceRemoveMemberResponse{ +// ProjectMember: testresources.Project1Member(), +// }) +// }, +// }, +// }, +// }), +// WantTable: new(` +// ID ROLE INHERITED SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now +// `), +// }, +// } +// for _, tt := range tests { +// tt.TestCmd(t) +// } +// } -func Test_ProjectCmd_UpdateMember(t *testing.T) { - tests := []*e2e.Test[apiv2.ProjectServiceUpdateMemberResponse, *apiv2.ProjectMember]{ - { - Name: "update project member", - CmdArgs: []string{"project", "member", "update", testresources.Project1Member().Id, "--project", testresources.Project1().Uuid, "--role", testresources.Project1Member().Role.String()}, - NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ - ClientCalls: []client.ClientCall{ - { - WantRequest: &apiv2.ProjectServiceUpdateMemberRequest{ - Project: testresources.Project1().Uuid, - Member: testresources.Project1Member().Id, - Role: testresources.Project1Member().Role, - }, - WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.ProjectServiceUpdateMemberResponse{ - ProjectMember: testresources.Project1Member(), - }) - }, - }, - }, - }), - WantTable: new(` - ID ROLE INHERITED SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now - `), - }, - } - for _, tt := range tests { - tt.TestCmd(t) - } -} +// func Test_ProjectCmd_UpdateMember(t *testing.T) { +// tests := []*e2e.Test[apiv2.ProjectServiceUpdateMemberResponse, *apiv2.ProjectMember]{ +// { +// Name: "update project member", +// CmdArgs: []string{"project", "member", "update", testresources.Project1Member().Id, "--project", testresources.Project1().Uuid, "--role", testresources.Project1Member().Role.String()}, +// NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ +// ClientCalls: []client.ClientCall{ +// { +// WantRequest: &apiv2.ProjectServiceUpdateMemberRequest{ +// Project: testresources.Project1().Uuid, +// Member: testresources.Project1Member().Id, +// Role: testresources.Project1Member().Role, +// }, +// WantResponse: func() connect.AnyResponse { +// return connect.NewResponse(&apiv2.ProjectServiceUpdateMemberResponse{ +// ProjectMember: testresources.Project1Member(), +// }) +// }, +// }, +// }, +// }), +// WantTable: new(` +// ID ROLE INHERITED SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 PROJECT_ROLE_OWNER false now +// `), +// }, +// } +// for _, tt := range tests { +// tt.TestCmd(t) +// } +// } diff --git a/tests/e2e/api/tenant_test.go b/tests/e2e/api/tenant_test.go index 81acee3..96f995c 100644 --- a/tests/e2e/api/tenant_test.go +++ b/tests/e2e/api/tenant_test.go @@ -8,7 +8,7 @@ import ( "github.com/dustin/go-humanize" "github.com/metal-stack/api/go/client" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - cliv2 "github.com/metal-stack/api/go/metalstack/cli/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" @@ -399,198 +399,199 @@ func Test_TenantCmd_Apply(t *testing.T) { } } -func Test_TenantCmd_ListMembers(t *testing.T) { - tests := []*e2e.Test[apiv2.TenantServiceGetRequest, []apiv2.TenantMember]{ - { - Name: "list tenant members", - CmdArgs: []string{"tenant", "member", "list", "--tenant", testresources.Tenant1().Login}, - NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ - ClientCalls: []client.ClientCall{ - { - WantRequest: &apiv2.TenantServiceGetRequest{ - Login: testresources.Tenant1().Login, - }, - WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.TenantServiceGetResponse{ - Tenant: testresources.Tenant1(), - TenantMembers: []*apiv2.TenantMember{ - testresources.Tenant1Member(), testresources.Tenant2Member(), - }, - }) - }, - }, - }, - }), - WantTable: new(` - ID ROLE SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now - 40c0da4b-9eb9-4371-91aa-1ae62193fa54 TENANT_ROLE_EDITOR now - `), - WantWideTable: new(` - ID ROLE SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now - 40c0da4b-9eb9-4371-91aa-1ae62193fa54 TENANT_ROLE_EDITOR now - `), - Template: new("{{ .tenant_member.id }} {{ .tenant_member.role }} {{ .tenant_member.projects }}"), - WantTemplate: new(` -16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 1 [0d81bca7-73f6-4da3-8397-4a8c52a0c583 f3b4e6a1-2c8d-4e5f-a7b9-1d3e5f7a9b0c] -40c0da4b-9eb9-4371-91aa-1ae62193fa54 2 [0d81bca7-73f6-4da3-8397-4a8c52a0c583] - `), - WantMarkdown: new(` - | ID | ROLE | SINCE | - |--------------------------------------|--------------------|-------| - | 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 | TENANT_ROLE_OWNER | now | - | 40c0da4b-9eb9-4371-91aa-1ae62193fa54 | TENANT_ROLE_EDITOR | now | - `), - }, - } - for _, tt := range tests { - tt.TestCmd(t) - } -} +// FIXME: create member tests +// func Test_TenantCmd_ListMembers(t *testing.T) { +// tests := []*e2e.Test[apiv2.TenantServiceGetRequest, []apiv2.TenantMember]{ +// { +// Name: "list tenant members", +// CmdArgs: []string{"tenant", "member", "list", "--tenant", testresources.Tenant1().Login}, +// NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ +// ClientCalls: []client.ClientCall{ +// { +// WantRequest: &apiv2.TenantServiceGetRequest{ +// Login: testresources.Tenant1().Login, +// }, +// WantResponse: func() connect.AnyResponse { +// return connect.NewResponse(&apiv2.TenantServiceGetResponse{ +// Tenant: testresources.Tenant1(), +// TenantMembers: []*apiv2.TenantMember{ +// testresources.Tenant1Member(), testresources.Tenant2Member(), +// }, +// }) +// }, +// }, +// }, +// }), +// WantTable: new(` +// ID ROLE SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now +// 40c0da4b-9eb9-4371-91aa-1ae62193fa54 TENANT_ROLE_EDITOR now +// `), +// WantWideTable: new(` +// ID ROLE SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now +// 40c0da4b-9eb9-4371-91aa-1ae62193fa54 TENANT_ROLE_EDITOR now +// `), +// Template: new("{{ .tenant_member.id }} {{ .tenant_member.role }} {{ .tenant_member.projects }}"), +// WantTemplate: new(` +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 1 [0d81bca7-73f6-4da3-8397-4a8c52a0c583 f3b4e6a1-2c8d-4e5f-a7b9-1d3e5f7a9b0c] +// 40c0da4b-9eb9-4371-91aa-1ae62193fa54 2 [0d81bca7-73f6-4da3-8397-4a8c52a0c583] +// `), +// WantMarkdown: new(` +// | ID | ROLE | SINCE | +// |--------------------------------------|--------------------|-------| +// | 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 | TENANT_ROLE_OWNER | now | +// | 40c0da4b-9eb9-4371-91aa-1ae62193fa54 | TENANT_ROLE_EDITOR | now | +// `), +// }, +// } +// for _, tt := range tests { +// tt.TestCmd(t) +// } +// } -func Test_TenantCmd_DeleteMember(t *testing.T) { - tests := []*e2e.Test[apiv2.TenantServiceRemoveMemberResponse, string]{ - { - Name: "delete tenant member", - CmdArgs: []string{"tenant", "member", "remove", testresources.Tenant1Member().Id, "--tenant", testresources.Tenant1().Login}, - NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ - ClientCalls: []client.ClientCall{ - { - WantRequest: &apiv2.TenantServiceRemoveMemberRequest{ - Login: testresources.Tenant1().Login, - Member: testresources.Tenant1Member().Id, - }, - WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.TenantServiceRemoveMemberResponse{ - TenantMember: testresources.Tenant1Member(), - }) - }, - }, - }, - }), - WantTable: new(` - ID ROLE SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now - `), - }, - } - for _, tt := range tests { - tt.TestCmd(t) - } -} +// func Test_TenantCmd_DeleteMember(t *testing.T) { +// tests := []*e2e.Test[apiv2.TenantServiceRemoveMemberResponse, string]{ +// { +// Name: "delete tenant member", +// CmdArgs: []string{"tenant", "member", "remove", testresources.Tenant1Member().Id, "--tenant", testresources.Tenant1().Login}, +// NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ +// ClientCalls: []client.ClientCall{ +// { +// WantRequest: &apiv2.TenantServiceRemoveMemberRequest{ +// Login: testresources.Tenant1().Login, +// Member: testresources.Tenant1Member().Id, +// }, +// WantResponse: func() connect.AnyResponse { +// return connect.NewResponse(&apiv2.TenantServiceRemoveMemberResponse{ +// TenantMember: testresources.Tenant1Member(), +// }) +// }, +// }, +// }, +// }), +// WantTable: new(` +// ID ROLE SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now +// `), +// }, +// } +// for _, tt := range tests { +// tt.TestCmd(t) +// } +// } -func Test_TenantCmd_UpdateMember(t *testing.T) { - tests := []*e2e.Test[apiv2.TenantServiceUpdateMemberResponse, *cliv2.TenantMember]{ - { - Name: "update tenant member", - CmdArgs: []string{"tenant", "member", "update", testresources.Tenant1Member().Id, "--tenant", testresources.Tenant1().Login, "--role", testresources.Tenant1Member().Role.String()}, - NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ - ClientCalls: []client.ClientCall{ - { - WantRequest: &apiv2.TenantServiceUpdateMemberRequest{ - Login: testresources.Tenant1().Login, - Member: testresources.Tenant1Member().Id, - Role: testresources.Tenant1Member().Role, - }, - WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.TenantServiceUpdateMemberResponse{ - TenantMember: testresources.Tenant1Member(), - }) - }, - }, - }, - }), - WantTable: new(` - ID ROLE SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now - `), - }, - } - for _, tt := range tests { - tt.TestCmd(t) - } -} +// func Test_TenantCmd_UpdateMember(t *testing.T) { +// tests := []*e2e.Test[apiv2.TenantServiceUpdateMemberResponse, *apiv2.TenantMember]{ +// { +// Name: "update tenant member", +// CmdArgs: []string{"tenant", "member", "update", testresources.Tenant1Member().Id, "--tenant", testresources.Tenant1().Login, "--role", testresources.Tenant1Member().Role.String()}, +// NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ +// ClientCalls: []client.ClientCall{ +// { +// WantRequest: &apiv2.TenantServiceUpdateMemberRequest{ +// Login: testresources.Tenant1().Login, +// Member: testresources.Tenant1Member().Id, +// Role: testresources.Tenant1Member().Role, +// }, +// WantResponse: func() connect.AnyResponse { +// return connect.NewResponse(&apiv2.TenantServiceUpdateMemberResponse{ +// TenantMember: testresources.Tenant1Member(), +// }) +// }, +// }, +// }, +// }), +// WantTable: new(` +// ID ROLE SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now +// `), +// }, +// } +// for _, tt := range tests { +// tt.TestCmd(t) +// } +// } -func Test_TenantCmd_ApplyMember(t *testing.T) { - tests := []*e2e.Test[apiv2.TenantServiceUpdateMemberResponse, *cliv2.TenantMember]{ - { - Name: "apply", - CmdArgs: append([]string{"tenant", "member", "apply"}, e2e.AppendFromFileCommonArgs()...), - NewRootCmd: e2erootcmd.NewRootCmd(t, - &e2erootcmd.TestConfig{ - FsMocks: func(fs *afero.Afero) { - require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, &cliv2.TenantMember{ - Tenant: testresources.Tenant1().Login, - TenantMember: testresources.Tenant1Member(), - }), 0755)) - }, - ClientCalls: []client.ClientCall{ - { - WantRequest: &apiv2.TenantServiceAddMemberRequest{ - Login: testresources.Tenant1().Login, - Member: testresources.Tenant1Member().Id, - Role: testresources.Tenant1Member().Role, - }, - WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.TenantServiceAddMemberResponse{ - TenantMember: testresources.Tenant1Member(), - }) - }, - }, - }, - }, - ), - WantTable: new(` - ID ROLE SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now - `), - }, - { - Name: "apply already exists", - CmdArgs: append([]string{"tenant", "member", "apply"}, e2e.AppendFromFileCommonArgs()...), - NewRootCmd: e2erootcmd.NewRootCmd(t, - &e2erootcmd.TestConfig{ - FsMocks: func(fs *afero.Afero) { - require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, &cliv2.TenantMember{ - Tenant: testresources.Tenant1().Login, - TenantMember: testresources.Tenant1Member(), - }), 0755)) - }, - ClientCalls: []client.ClientCall{ - { - WantRequest: &apiv2.TenantServiceAddMemberRequest{ - Login: testresources.Tenant1().Login, - Member: testresources.Tenant1Member().Id, - Role: testresources.Tenant1Member().Role, - }, - WantError: connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("already exists")), - }, - { - WantRequest: &apiv2.TenantServiceUpdateMemberRequest{ - Login: testresources.Tenant1().Login, - Member: testresources.Tenant1Member().Id, - Role: testresources.Tenant1Member().Role, - }, - WantResponse: func() connect.AnyResponse { - return connect.NewResponse(&apiv2.TenantServiceUpdateMemberResponse{ - TenantMember: testresources.Tenant1Member(), - }) - }, - }, - }, - }, - ), - WantTable: new(` - ID ROLE SINCE - 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now - `), - }, - } - for _, tt := range tests { - tt.TestCmd(t) - } -} +// func Test_TenantCmd_ApplyMember(t *testing.T) { +// tests := []*e2e.Test[apiv2.TenantServiceUpdateMemberResponse, *apiv2.TenantMember]{ +// { +// Name: "apply", +// CmdArgs: append([]string{"tenant", "member", "apply"}, e2e.AppendFromFileCommonArgs()...), +// NewRootCmd: e2erootcmd.NewRootCmd(t, +// &e2erootcmd.TestConfig{ +// FsMocks: func(fs *afero.Afero) { +// require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, &apiv2.TenantMember{ +// Tenant: testresources.Tenant1().Login, +// TenantMember: testresources.Tenant1Member(), +// }), 0755)) +// }, +// ClientCalls: []client.ClientCall{ +// { +// WantRequest: &apiv2.TenantServiceAddMemberRequest{ +// Login: testresources.Tenant1().Login, +// Member: testresources.Tenant1Member().Id, +// Role: testresources.Tenant1Member().Role, +// }, +// WantResponse: func() connect.AnyResponse { +// return connect.NewResponse(&apiv2.TenantServiceAddMemberResponse{ +// TenantMember: testresources.Tenant1Member(), +// }) +// }, +// }, +// }, +// }, +// ), +// WantTable: new(` +// ID ROLE SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now +// `), +// }, +// { +// Name: "apply already exists", +// CmdArgs: append([]string{"tenant", "member", "apply"}, e2e.AppendFromFileCommonArgs()...), +// NewRootCmd: e2erootcmd.NewRootCmd(t, +// &e2erootcmd.TestConfig{ +// FsMocks: func(fs *afero.Afero) { +// require.NoError(t, fs.WriteFile(e2e.InputFilePath, e2e.MustMarshal(t, &apiv2.TenantMember{ +// Tenant: testresources.Tenant1().Login, +// TenantMember: testresources.Tenant1Member(), +// }), 0755)) +// }, +// ClientCalls: []client.ClientCall{ +// { +// WantRequest: &apiv2.TenantServiceAddMemberRequest{ +// Login: testresources.Tenant1().Login, +// Member: testresources.Tenant1Member().Id, +// Role: testresources.Tenant1Member().Role, +// }, +// WantError: connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("already exists")), +// }, +// { +// WantRequest: &apiv2.TenantServiceUpdateMemberRequest{ +// Login: testresources.Tenant1().Login, +// Member: testresources.Tenant1Member().Id, +// Role: testresources.Tenant1Member().Role, +// }, +// WantResponse: func() connect.AnyResponse { +// return connect.NewResponse(&apiv2.TenantServiceUpdateMemberResponse{ +// TenantMember: testresources.Tenant1Member(), +// }) +// }, +// }, +// }, +// }, +// ), +// WantTable: new(` +// ID ROLE SINCE +// 16d6e8ba-f574-494f-8d5e-74f6cb2d8db0 TENANT_ROLE_OWNER now +// `), +// }, +// } +// for _, tt := range tests { +// tt.TestCmd(t) +// } +// } func Test_TenantCmd_ListInvites(t *testing.T) { tests := []*e2e.Test[apiv2.TenantServiceInvitesListResponse, apiv2.TenantInvite]{ diff --git a/tests/e2e/testresources/projects.go b/tests/e2e/testresources/projects.go index 50d88e4..b72a75c 100644 --- a/tests/e2e/testresources/projects.go +++ b/tests/e2e/testresources/projects.go @@ -57,18 +57,22 @@ var ( Project1Member = func() *apiv2.ProjectMember { return &apiv2.ProjectMember{ - Id: "16d6e8ba-f574-494f-8d5e-74f6cb2d8db0", + Member: "16d6e8ba-f574-494f-8d5e-74f6cb2d8db0", Role: apiv2.ProjectRole_PROJECT_ROLE_OWNER, InheritedMembership: false, - CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), + Meta: &apiv2.Meta{ + CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), + }, } } Project2Member = func() *apiv2.ProjectMember { return &apiv2.ProjectMember{ - Id: "40c0da4b-9eb9-4371-91aa-1ae62193fa54", + Member: "40c0da4b-9eb9-4371-91aa-1ae62193fa54", Role: apiv2.ProjectRole_PROJECT_ROLE_EDITOR, InheritedMembership: true, - CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), + Meta: &apiv2.Meta{ + CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), + }, } } ) diff --git a/tests/e2e/testresources/tenants.go b/tests/e2e/testresources/tenants.go index cf25580..2ea419d 100644 --- a/tests/e2e/testresources/tenants.go +++ b/tests/e2e/testresources/tenants.go @@ -59,18 +59,22 @@ var ( Tenant1Member = func() *apiv2.TenantMember { return &apiv2.TenantMember{ - Id: "16d6e8ba-f574-494f-8d5e-74f6cb2d8db0", - Role: apiv2.TenantRole_TENANT_ROLE_OWNER, - CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), - Projects: []string{Project1().Uuid, Project2().Uuid}, + Member: "16d6e8ba-f574-494f-8d5e-74f6cb2d8db0", + Role: apiv2.TenantRole_TENANT_ROLE_OWNER, + Projects: []string{Project1().Uuid, Project2().Uuid}, + Meta: &apiv2.Meta{ + CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), + }, } } Tenant2Member = func() *apiv2.TenantMember { return &apiv2.TenantMember{ - Id: "40c0da4b-9eb9-4371-91aa-1ae62193fa54", - Role: apiv2.TenantRole_TENANT_ROLE_EDITOR, - CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), - Projects: []string{Project1().Uuid}, + Member: "40c0da4b-9eb9-4371-91aa-1ae62193fa54", + Role: apiv2.TenantRole_TENANT_ROLE_EDITOR, + Projects: []string{Project1().Uuid}, + Meta: &apiv2.Meta{ + CreatedAt: timestamppb.New(e2e.TimeBubbleStartTime()), + }, } } ) From f8e5aa82d13d76780882e251dbe4ef735a9fd15f Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 26 Oct 2026 12:48:14 +0100 Subject: [PATCH 10/15] Docs. --- docs/admin/metalctlv2_admin_filesystem_match.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/admin/metalctlv2_admin_filesystem_match.md b/docs/admin/metalctlv2_admin_filesystem_match.md index 8530e1a..262dcd8 100644 --- a/docs/admin/metalctlv2_admin_filesystem_match.md +++ b/docs/admin/metalctlv2_admin_filesystem_match.md @@ -14,11 +14,11 @@ metalctlv2 admin filesystem match [flags] ### Options ``` - --filesystem-layout string id of the filesystem layout to check against a machine - -h, --help help for match - --image string machine image to match against a filesystem layout - --machine string uuid of the machine to check against a filesystem layout - --size string machine size to match against a filesystem layout + -h, --help help for match + --id string id of the filesystem layout to check against a machine + --image string machine image to match against a filesystem layout + --machine string uuid of the machine to check against a filesystem layout + --size string machine size to match against a filesystem layout ``` ### Options inherited from parent commands From 9cc920066af4feb6253cc7d41431e665c40db90b Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 26 Oct 2026 13:01:19 +0100 Subject: [PATCH 11/15] Fix network apply. --- cmd/admin/v2/network.go | 6 +++--- cmd/api/v2/network.go | 6 +++--- pkg/helpers/network.go | 10 ++++++++-- tests/e2e/admin/network_test.go | 7 +++---- tests/e2e/api/network_test.go | 6 +++--- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/cmd/admin/v2/network.go b/cmd/admin/v2/network.go index b1805c4..3ca7662 100644 --- a/cmd/admin/v2/network.go +++ b/cmd/admin/v2/network.go @@ -224,13 +224,13 @@ func (c *networkCmd) Update(rq *adminv2.NetworkServiceUpdateRequest) (*apiv2.Net return resp.Network, nil } -func (c *networkCmd) Convert(r *apiv2.Network) (string, *adminv2.NetworkServiceCreateRequest, *adminv2.NetworkServiceUpdateRequest, error) { - addressFamily, err := helpers.AddressFamilyFromPrefixes(r.Prefixes...) +func (c *networkCmd) Convert(n *apiv2.Network) (string, *adminv2.NetworkServiceCreateRequest, *adminv2.NetworkServiceUpdateRequest, error) { + addressFamily, err := helpers.AddressFamilyFromPrefixes(n) if err != nil { return "", nil, nil, err } - return r.Id, networkResponseToCreate(r, addressFamily), networkResponseToUpdate(r), nil + return n.Id, networkResponseToCreate(n, addressFamily), networkResponseToUpdate(n), nil } func networkResponseToCreate(r *apiv2.Network, addressFamily *apiv2.NetworkAddressFamily) *adminv2.NetworkServiceCreateRequest { diff --git a/cmd/api/v2/network.go b/cmd/api/v2/network.go index 392145d..6b44873 100644 --- a/cmd/api/v2/network.go +++ b/cmd/api/v2/network.go @@ -218,13 +218,13 @@ func (c *networkCmd) Update(rq *apiv2.NetworkServiceUpdateRequest) (*apiv2.Netwo return resp.Network, nil } -func (c *networkCmd) Convert(r *apiv2.Network) (string, *apiv2.NetworkServiceCreateRequest, *apiv2.NetworkServiceUpdateRequest, error) { - addressFamily, err := helpers.AddressFamilyFromPrefixes(r.Prefixes...) +func (c *networkCmd) Convert(n *apiv2.Network) (string, *apiv2.NetworkServiceCreateRequest, *apiv2.NetworkServiceUpdateRequest, error) { + addressFamily, err := helpers.AddressFamilyFromPrefixes(n) if err != nil { return "", nil, nil, err } - return helpers.EncodeProject(r.Id, pointer.SafeDeref(r.Project)), networkResponseToCreate(r, addressFamily), networkResponseToUpdate(r), nil + return helpers.EncodeProject(n.Id, pointer.SafeDeref(n.Project)), networkResponseToCreate(n, addressFamily), networkResponseToUpdate(n), nil } func networkResponseToCreate(r *apiv2.Network, addressFamily *apiv2.NetworkAddressFamily) *apiv2.NetworkServiceCreateRequest { diff --git a/pkg/helpers/network.go b/pkg/helpers/network.go index b244ded..82faa3c 100644 --- a/pkg/helpers/network.go +++ b/pkg/helpers/network.go @@ -43,14 +43,20 @@ func NetworkAddressFamilyToType(af string) *apiv2.NetworkAddressFamily { } } -func AddressFamilyFromPrefixes(prefixes ...string) (*apiv2.NetworkAddressFamily, error) { +func AddressFamilyFromPrefixes(n *apiv2.Network) (*apiv2.NetworkAddressFamily, error) { + switch n.Type { + case apiv2.NetworkType_NETWORK_TYPE_EXTERNAL, apiv2.NetworkType_NETWORK_TYPE_SUPER, apiv2.NetworkType_NETWORK_TYPE_UNDERLAY: + // will be derived by metal-apiserver + return nil, nil + } + var ( addressFamily *apiv2.NetworkAddressFamily isIPv4 bool isIPv6 bool ) - for _, pfx := range prefixes { + for _, pfx := range n.Prefixes { p, err := netip.ParsePrefix(pfx) if err != nil { return nil, err diff --git a/tests/e2e/admin/network_test.go b/tests/e2e/admin/network_test.go index d0e82f9..fc52012 100644 --- a/tests/e2e/admin/network_test.go +++ b/tests/e2e/admin/network_test.go @@ -160,7 +160,6 @@ func Test_AdminNetworkCmd_Create(t *testing.T) { "--prefixes", "10.0.0.0/16,2001:db8::/32", "--destination-prefixes", strings.Join(testresources.Network1().DestinationPrefixes, ","), "--additional-announcable-cidrs", "1.2.3.4/32", - "--addressfamily", "ipv4", "--default-ipv4-prefix-length", "17", "--default-ipv6-prefix-length", "64", "--ipv4-prefix-length", "18", @@ -171,7 +170,7 @@ func Test_AdminNetworkCmd_Create(t *testing.T) { "--vrf", strconv.Itoa(int(pointer.SafeDeref(testresources.Network1().Vrf))), }, AssertExhaustiveArgs: true, - AssertExhaustiveExcludes: e2e.CommonExcludedFileArgs(), + AssertExhaustiveExcludes: append(e2e.CommonExcludedFileArgs(), "addressfamily"), NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { @@ -188,7 +187,7 @@ func Test_AdminNetworkCmd_Create(t *testing.T) { DestinationPrefixes: testresources.Network1().DestinationPrefixes, Vrf: new(uint32(0)), ParentNetwork: testresources.Network2().ParentNetwork, - AddressFamily: apiv2.NetworkAddressFamily_NETWORK_ADDRESS_FAMILY_V4.Enum(), + AddressFamily: nil, Labels: &apiv2.Labels{ Labels: map[string]string{"a": "b"}, }, @@ -237,7 +236,7 @@ func Test_AdminNetworkCmd_Create(t *testing.T) { ParentNetwork: testresources.Network1().ParentNetwork, AdditionalAnnouncableCidrs: testresources.Network1().AdditionalAnnouncableCidrs, Labels: testresources.Network1().Meta.Labels, - AddressFamily: apiv2.NetworkAddressFamily_NETWORK_ADDRESS_FAMILY_DUAL_STACK.Enum(), + AddressFamily: nil, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&adminv2.NetworkServiceCreateResponse{ diff --git a/tests/e2e/api/network_test.go b/tests/e2e/api/network_test.go index e7bc07e..e10eded 100644 --- a/tests/e2e/api/network_test.go +++ b/tests/e2e/api/network_test.go @@ -190,7 +190,7 @@ func Test_NetworkCmd_Create(t *testing.T) { Partition: testresources.Network1().Partition, Labels: testresources.Network1().Meta.Labels, ParentNetwork: testresources.Network1().ParentNetwork, - AddressFamily: apiv2.NetworkAddressFamily_NETWORK_ADDRESS_FAMILY_DUAL_STACK.Enum(), + AddressFamily: nil, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.NetworkServiceCreateResponse{ @@ -365,7 +365,7 @@ func Test_NetworkCmd_Apply(t *testing.T) { Partition: testresources.Network1().Partition, Labels: testresources.Network1().Meta.Labels, ParentNetwork: testresources.Network1().ParentNetwork, - AddressFamily: apiv2.NetworkAddressFamily_NETWORK_ADDRESS_FAMILY_DUAL_STACK.Enum(), + AddressFamily: nil, }, WantResponse: func() connect.AnyResponse { return connect.NewResponse(&apiv2.NetworkServiceCreateResponse{ @@ -397,7 +397,7 @@ func Test_NetworkCmd_Apply(t *testing.T) { Partition: testresources.Network1().Partition, Labels: testresources.Network1().Meta.Labels, ParentNetwork: testresources.Network1().ParentNetwork, - AddressFamily: apiv2.NetworkAddressFamily_NETWORK_ADDRESS_FAMILY_DUAL_STACK.Enum(), + AddressFamily: nil, }, WantError: connect.NewError(connect.CodeAlreadyExists, fmt.Errorf("already exists")), }, From bf06ef6fc0d71063175df6f71938773617e0c95b Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 26 Oct 2026 13:58:44 +0100 Subject: [PATCH 12/15] Add admin member crud. --- cmd/admin/v2/project.go | 2 +- cmd/admin/v2/project_member.go | 194 ++++++++++++++++ cmd/admin/v2/tenant.go | 48 +--- cmd/admin/v2/tenant_member.go | 209 ++++++++++++++++++ cmd/api/v2/project_member.go | 27 ++- cmd/api/v2/tenant_member.go | 31 ++- docs/admin/metalctlv2_admin_tenant.md | 2 +- docs/admin/metalctlv2_admin_tenant_member.md | 38 ++++ .../metalctlv2_admin_tenant_member_apply.md | 46 ++++ .../metalctlv2_admin_tenant_member_create.md | 47 ++++ .../metalctlv2_admin_tenant_member_delete.md | 47 ++++ ...etalctlv2_admin_tenant_member_describe.md} | 18 +- .../metalctlv2_admin_tenant_member_edit.md | 32 +++ .../metalctlv2_admin_tenant_member_list.md | 34 +++ .../metalctlv2_admin_tenant_member_update.md | 48 ++++ docs/metalctlv2_project_member.md | 1 + docs/metalctlv2_project_member_leave.md | 32 +++ docs/metalctlv2_tenant_member.md | 1 + docs/metalctlv2_tenant_member_leave.md | 32 +++ 19 files changed, 823 insertions(+), 66 deletions(-) create mode 100644 cmd/admin/v2/project_member.go create mode 100644 cmd/admin/v2/tenant_member.go create mode 100644 docs/admin/metalctlv2_admin_tenant_member.md create mode 100644 docs/admin/metalctlv2_admin_tenant_member_apply.md create mode 100644 docs/admin/metalctlv2_admin_tenant_member_create.md create mode 100644 docs/admin/metalctlv2_admin_tenant_member_delete.md rename docs/admin/{metalctlv2_admin_tenant_add-member.md => metalctlv2_admin_tenant_member_describe.md} (60%) create mode 100644 docs/admin/metalctlv2_admin_tenant_member_edit.md create mode 100644 docs/admin/metalctlv2_admin_tenant_member_list.md create mode 100644 docs/admin/metalctlv2_admin_tenant_member_update.md create mode 100644 docs/metalctlv2_project_member_leave.md create mode 100644 docs/metalctlv2_tenant_member_leave.md diff --git a/cmd/admin/v2/project.go b/cmd/admin/v2/project.go index 738e262..62fc235 100644 --- a/cmd/admin/v2/project.go +++ b/cmd/admin/v2/project.go @@ -40,7 +40,7 @@ func newProjectCmd(c *config.Config) *cobra.Command { }, } - return genericcli.NewCmds(cmdsConfig) + return genericcli.NewCmds(cmdsConfig, newProjectMemberCmd(c)) } func (c *project) Get(id string) (*apiv2.Project, error) { diff --git a/cmd/admin/v2/project_member.go b/cmd/admin/v2/project_member.go new file mode 100644 index 0000000..653a68e --- /dev/null +++ b/cmd/admin/v2/project_member.go @@ -0,0 +1,194 @@ +package v2 + +import ( + "fmt" + + "github.com/metal-stack/api/go/errorutil" + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + + "github.com/metal-stack/cli/cmd/config" + "github.com/metal-stack/cli/cmd/sorters" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/metal-stack/metal-lib/pkg/pointer" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type projectMember struct { + c *config.Config +} + +func newProjectMemberCmd(c *config.Config) *cobra.Command { + w := &projectMember{ + c: c, + } + + cmdsConfig := &genericcli.CmdsConfig[*adminv2.ProjectServiceAddMemberRequest, *apiv2.ProjectMemberServiceUpdateRequest, *apiv2.ProjectMember]{ + BinaryName: config.BinaryName, + GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), + Singular: "member", + Plural: "members", + Description: "manage api project members", + Sorter: sorters.ProjectMemberSorter(), + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + DescribeCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project to describe the project members of, defaults to project of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + }, + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project to list the project members of, defaults to project of the default project") + cmd.Flags().String("role", "", "the role of the member") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.ProjectRole)) + }, + CreateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project for which to create a project member, defaults to project of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + }, + UpdateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project for which to update a project member, defaults to project of the default project") + cmd.Flags().String("role", "", "the role of the member") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.ProjectRole)) + }, + DeleteCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project from which to delete a project member, defaults to project of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + }, + EditCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("project", "", "the project from which to edit a project member, defaults to project of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + }, + CreateRequestFromCLI: w.createRequestFromCLI, + UpdateRequestFromCLI: w.updateRequestFromCLI, + ValidArgsFn: w.c.Completion.ProjectMember, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *projectMember) Get(id string) (*apiv2.ProjectMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + project := c.c.GetProject() + + resp, err := c.c.Client.Apiv2().ProjectMember().Get(ctx, &apiv2.ProjectMemberServiceGetRequest{ + Project: project, + Member: id, + }) + if err != nil { + return nil, err + } + + return resp.Member, nil +} + +func (c *projectMember) List() ([]*apiv2.ProjectMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + project := c.c.GetProject() + + resp, err := c.c.Client.Apiv2().ProjectMember().List(ctx, &apiv2.ProjectMemberServiceListRequest{ + Project: project, + }) + if err != nil { + return nil, err + } + + return resp.Members, nil +} + +func (c *projectMember) Create(rq *adminv2.ProjectServiceAddMemberRequest) (*apiv2.ProjectMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Adminv2().Project().AddMember(ctx, rq) + if err != nil { + if errorutil.IsConflict(err) { + return nil, genericcli.AlreadyExistsError() + } + + return nil, err + } + + return resp.Member, nil +} + +func (c *projectMember) Delete(id string) (*apiv2.ProjectMember, error) { + project := c.c.GetProject() + + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().ProjectMember().Delete(ctx, &apiv2.ProjectMemberServiceDeleteRequest{ + Project: project, + Member: id, + }) + if err != nil { + return nil, fmt.Errorf("failed to remove member from project: %w", err) + } + + return resp.Member, nil +} + +func (c *projectMember) Convert(r *apiv2.ProjectMember) (string, *adminv2.ProjectServiceAddMemberRequest, *apiv2.ProjectMemberServiceUpdateRequest, error) { + return r.Member, &adminv2.ProjectServiceAddMemberRequest{ + Project: r.Project, + Member: r.Member, + Role: r.Role, + }, + &apiv2.ProjectMemberServiceUpdateRequest{ + Project: r.Project, + Member: r.Member, + Role: pointer.PointerOrNil(r.Role), + }, + nil +} + +func (c *projectMember) Update(rq *apiv2.ProjectMemberServiceUpdateRequest) (*apiv2.ProjectMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().ProjectMember().Update(ctx, rq) + if err != nil { + return nil, fmt.Errorf("failed to update member: %w", err) + } + + return resp.Member, nil +} + +func (c *projectMember) updateRequestFromCLI(args []string) (*apiv2.ProjectMemberServiceUpdateRequest, error) { + member, err := genericcli.GetExactlyOneArg(args) + if err != nil { + return nil, err + } + + project := c.c.GetProject() + + return &apiv2.ProjectMemberServiceUpdateRequest{ + Project: project, + Member: member, + Role: pointer.PointerOrNil(apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")])), + }, nil +} + +func (c *projectMember) createRequestFromCLI() (*adminv2.ProjectServiceAddMemberRequest, error) { + project := c.c.GetProject() + + return &adminv2.ProjectServiceAddMemberRequest{ + Project: project, + Member: viper.GetString("id"), + Role: apiv2.ProjectRole(apiv2.ProjectRole_value[viper.GetString("role")]), + }, nil +} diff --git a/cmd/admin/v2/tenant.go b/cmd/admin/v2/tenant.go index dabd553..89c6801 100644 --- a/cmd/admin/v2/tenant.go +++ b/cmd/admin/v2/tenant.go @@ -61,7 +61,7 @@ func newTenantCmd(c *config.Config) *cobra.Command { ValidArgsFn: w.c.Completion.AdminTenant, } - return genericcli.NewCmds(cmdsConfig, newAddMemberCmd(c)) + return genericcli.NewCmds(cmdsConfig, newTenantMemberCmd(c)) } func (c *tenant) Get(id string) (*apiv2.Tenant, error) { @@ -172,49 +172,3 @@ func (c *tenant) Update(rq *apiv2.TenantServiceUpdateRequest) (*apiv2.Tenant, er return resp.Tenant, nil } - -func newAddMemberCmd(c *config.Config) *cobra.Command { - cmd := &cobra.Command{ - Use: "add-member", - Short: "Add a new member to a tenant", - Long: `Add a new member to an existing tenant by specifying the tenant ID, member's ID, and role.`, - RunE: func(cmd *cobra.Command, args []string) error { - ctx, cancel := c.NewRequestContext() - defer cancel() - - var ( - tenantId = viper.GetString("tenant-id") - memberId = viper.GetString("member-id") - memberRole = viper.GetString("role") - ) - - if tenantId == "" || memberId == "" || memberRole == "" { - return fmt.Errorf("tenant ID, member ID, and role must all be specified") - } - - _, err := c.Client.Adminv2().Tenant().AddMember(ctx, &adminv2.TenantServiceAddMemberRequest{ - Role: apiv2.TenantRole(apiv2.TenantRole_value[memberRole]), - Tenant: tenantId, - Member: memberId, - }) - if err != nil { - return fmt.Errorf("failed to add member to tenant: %w", err) - } - - return nil - }, - } - - cmd.Flags().String("tenant-id", "", "ID of the tenant where the member is added") - cmd.Flags().String("member-id", "", "ID of the member to be added") - cmd.Flags().String("role", "", "Role of the member within the tenant") - genericcli.Must(cmd.MarkFlagRequired("tenant-id")) - genericcli.Must(cmd.MarkFlagRequired("member-id")) - genericcli.Must(cmd.MarkFlagRequired("role")) - - genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant-id", c.Completion.AdminTenant)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("member-id", c.Completion.AdminTenant)) - genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.TenantRole)) - - return cmd -} diff --git a/cmd/admin/v2/tenant_member.go b/cmd/admin/v2/tenant_member.go new file mode 100644 index 0000000..48fd412 --- /dev/null +++ b/cmd/admin/v2/tenant_member.go @@ -0,0 +1,209 @@ +package v2 + +import ( + "fmt" + + "github.com/metal-stack/api/go/errorutil" + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + + "github.com/metal-stack/cli/cmd/config" + "github.com/metal-stack/cli/cmd/sorters" + "github.com/metal-stack/metal-lib/pkg/genericcli" + "github.com/metal-stack/metal-lib/pkg/genericcli/printers" + "github.com/metal-stack/metal-lib/pkg/pointer" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +type tenantMember struct { + c *config.Config +} + +func newTenantMemberCmd(c *config.Config) *cobra.Command { + w := &tenantMember{ + c: c, + } + + cmdsConfig := &genericcli.CmdsConfig[*adminv2.TenantServiceAddMemberRequest, *apiv2.TenantMemberServiceUpdateRequest, *apiv2.TenantMember]{ + BinaryName: config.BinaryName, + GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), + Singular: "member", + Plural: "members", + Description: "manage api tenant members", + Sorter: sorters.TenantMemberSorter(), + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + DescribeCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant to describe the tenant members of, defaults to tenant of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + }, + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant to list the tenant members of, defaults to tenant of the default project") + cmd.Flags().String("role", "", "the role of the member") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.TenantRole)) + }, + CreateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant for which to create a tenant member, defaults to tenant of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + }, + UpdateCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant for which to update a tenant member, defaults to tenant of the default project") + cmd.Flags().String("role", "", "the role of the member") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.TenantRole)) + + }, + DeleteCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant from which to delete a tenant member, defaults to tenant of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + }, + EditCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().String("tenant", "", "the tenant from which to edit a tenant member, defaults to tenant of the default project") + + genericcli.Must(cmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + }, + CreateRequestFromCLI: w.createRequestFromCLI, + UpdateRequestFromCLI: w.updateRequestFromCLI, + ValidArgsFn: w.c.Completion.TenantMember, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *tenantMember) Get(id string) (*apiv2.TenantMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + resp, err := c.c.Client.Apiv2().TenantMember().Get(ctx, &apiv2.TenantMemberServiceGetRequest{ + Login: tenant, + }) + if err != nil { + return nil, err + } + + return resp.Member, nil +} + +func (c *tenantMember) List() ([]*apiv2.TenantMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + resp, err := c.c.Client.Apiv2().TenantMember().List(ctx, &apiv2.TenantMemberServiceListRequest{ + Login: tenant, + }) + if err != nil { + return nil, nil + } + + return resp.Members, nil +} + +func (c *tenantMember) Create(rq *adminv2.TenantServiceAddMemberRequest) (*apiv2.TenantMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Adminv2().Tenant().AddMember(ctx, rq) + if err != nil { + if errorutil.IsConflict(err) { + return nil, genericcli.AlreadyExistsError() + } + + return nil, err + } + + return resp.Member, nil +} + +func (c *tenantMember) Delete(id string) (*apiv2.TenantMember, error) { + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().TenantMember().Delete(ctx, &apiv2.TenantMemberServiceDeleteRequest{ + Login: tenant, + Member: id, + }) + if err != nil { + return nil, fmt.Errorf("failed to remove member from tenant: %w", err) + } + + return resp.Member, nil +} + +func (c *tenantMember) Convert(r *apiv2.TenantMember) (string, *adminv2.TenantServiceAddMemberRequest, *apiv2.TenantMemberServiceUpdateRequest, error) { + return r.Member, &adminv2.TenantServiceAddMemberRequest{ + Tenant: r.Tenant, + Member: r.Member, + Role: r.Role, + }, + &apiv2.TenantMemberServiceUpdateRequest{ + Login: r.Tenant, + Member: r.Member, + Role: pointer.PointerOrNil(r.Role), + }, + nil +} + +func (c *tenantMember) Update(rq *apiv2.TenantMemberServiceUpdateRequest) (*apiv2.TenantMember, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().TenantMember().Update(ctx, rq) + if err != nil { + return nil, fmt.Errorf("failed to update member: %w", err) + } + + return resp.Member, nil +} + +func (c *tenantMember) updateRequestFromCLI(args []string) (*apiv2.TenantMemberServiceUpdateRequest, error) { + member, err := genericcli.GetExactlyOneArg(args) + if err != nil { + return nil, err + } + + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + return &apiv2.TenantMemberServiceUpdateRequest{ + Login: tenant, + Member: member, + Role: pointer.PointerOrNil(apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")])), + }, nil +} + +func (c *tenantMember) createRequestFromCLI() (*adminv2.TenantServiceAddMemberRequest, error) { + tenant, err := c.c.GetTenant() + if err != nil { + return nil, err + } + + return &adminv2.TenantServiceAddMemberRequest{ + Tenant: tenant, + Member: viper.GetString("id"), + Role: apiv2.TenantRole(apiv2.TenantRole_value[viper.GetString("role")]), + }, nil +} diff --git a/cmd/api/v2/project_member.go b/cmd/api/v2/project_member.go index fe07ba8..df80f39 100644 --- a/cmd/api/v2/project_member.go +++ b/cmd/api/v2/project_member.go @@ -56,7 +56,6 @@ func newProjectMemberCmd(c *config.Config) *cobra.Command { genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) genericcli.Must(cmd.RegisterFlagCompletionFunc("role", c.Completion.ProjectRole)) - }, DeleteCmdMutateFn: func(cmd *cobra.Command) { cmd.Flags().String("project", "", "the project from which to delete a project member, defaults to project of the default project") @@ -73,7 +72,17 @@ func newProjectMemberCmd(c *config.Config) *cobra.Command { ValidArgsFn: w.c.Completion.ProjectMember, } - return genericcli.NewCmds(cmdsConfig) + leaveCmd := &cobra.Command{ + Use: "leave", + Short: "leave the given project", + RunE: func(cmd *cobra.Command, _ []string) error { + return w.leave() + }, + } + leaveCmd.Flags().String("project", "", "the project to leave, defaults to project of the default project") + genericcli.Must(leaveCmd.RegisterFlagCompletionFunc("project", c.Completion.Project)) + + return genericcli.NewCmds(cmdsConfig, leaveCmd) } func (c *projectMember) Get(id string) (*apiv2.ProjectMember, error) { @@ -193,4 +202,16 @@ func (c *projectMember) createRequestFromCLI() (*apiv2.ProjectMemberServiceCreat }, nil } -// TODO: implement Leave +func (c *projectMember) leave() error { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + resp, err := c.c.Client.Apiv2().ProjectMember().Leave(ctx, &apiv2.ProjectMemberServiceLeaveRequest{ + Project: c.c.GetProject(), + }) + if err != nil { + return fmt.Errorf("failed to leave project: %w", err) + } + + return c.c.DescribePrinter.Print(resp.Member) +} diff --git a/cmd/api/v2/tenant_member.go b/cmd/api/v2/tenant_member.go index c0e4513..27cc97f 100644 --- a/cmd/api/v2/tenant_member.go +++ b/cmd/api/v2/tenant_member.go @@ -73,7 +73,17 @@ func newTenantMemberCmd(c *config.Config) *cobra.Command { ValidArgsFn: w.c.Completion.TenantMember, } - return genericcli.NewCmds(cmdsConfig) + leaveCmd := &cobra.Command{ + Use: "leave", + Short: "leave the given tenant", + RunE: func(cmd *cobra.Command, _ []string) error { + return w.leave() + }, + } + leaveCmd.Flags().String("tenant", "", "the tenant to leave, defaults to tenant of the default project") + genericcli.Must(leaveCmd.RegisterFlagCompletionFunc("tenant", c.Completion.Tenant)) + + return genericcli.NewCmds(cmdsConfig, leaveCmd) } func (c *tenantMember) Get(id string) (*apiv2.TenantMember, error) { @@ -207,4 +217,21 @@ func (c *tenantMember) createRequestFromCLI() (*apiv2.TenantMemberServiceCreateR }, nil } -// TODO: implement Leave +func (c *tenantMember) leave() error { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + tenant, err := c.c.GetTenant() + if err != nil { + return err + } + + resp, err := c.c.Client.Apiv2().TenantMember().Leave(ctx, &apiv2.TenantMemberServiceLeaveRequest{ + Login: tenant, + }) + if err != nil { + return fmt.Errorf("failed to leave project: %w", err) + } + + return c.c.DescribePrinter.Print(resp.Member) +} diff --git a/docs/admin/metalctlv2_admin_tenant.md b/docs/admin/metalctlv2_admin_tenant.md index b4bf20d..5fd1013 100644 --- a/docs/admin/metalctlv2_admin_tenant.md +++ b/docs/admin/metalctlv2_admin_tenant.md @@ -28,12 +28,12 @@ manage api tenants ### SEE ALSO * [metalctlv2 admin](metalctlv2_admin.md) - admin commands -* [metalctlv2 admin tenant add-member](metalctlv2_admin_tenant_add-member.md) - Add a new member to a tenant * [metalctlv2 admin tenant apply](metalctlv2_admin_tenant_apply.md) - applies one or more tenants from a given file * [metalctlv2 admin tenant create](metalctlv2_admin_tenant_create.md) - creates the tenant * [metalctlv2 admin tenant delete](metalctlv2_admin_tenant_delete.md) - deletes the tenant * [metalctlv2 admin tenant describe](metalctlv2_admin_tenant_describe.md) - describes the tenant * [metalctlv2 admin tenant edit](metalctlv2_admin_tenant_edit.md) - edit the tenant through an editor and update * [metalctlv2 admin tenant list](metalctlv2_admin_tenant_list.md) - list all tenants +* [metalctlv2 admin tenant member](metalctlv2_admin_tenant_member.md) - manage member entities * [metalctlv2 admin tenant update](metalctlv2_admin_tenant_update.md) - updates the tenant diff --git a/docs/admin/metalctlv2_admin_tenant_member.md b/docs/admin/metalctlv2_admin_tenant_member.md new file mode 100644 index 0000000..feb9b0b --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_member.md @@ -0,0 +1,38 @@ +## metalctlv2 admin tenant member + +manage member entities + +### Synopsis + +manage api tenant members + +### Options + +``` + -h, --help help for member +``` + +### 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 tenant](metalctlv2_admin_tenant.md) - manage tenant entities +* [metalctlv2 admin tenant member apply](metalctlv2_admin_tenant_member_apply.md) - applies one or more members from a given file +* [metalctlv2 admin tenant member create](metalctlv2_admin_tenant_member_create.md) - creates the member +* [metalctlv2 admin tenant member delete](metalctlv2_admin_tenant_member_delete.md) - deletes the member +* [metalctlv2 admin tenant member describe](metalctlv2_admin_tenant_member_describe.md) - describes the member +* [metalctlv2 admin tenant member edit](metalctlv2_admin_tenant_member_edit.md) - edit the member through an editor and update +* [metalctlv2 admin tenant member list](metalctlv2_admin_tenant_member_list.md) - list all members +* [metalctlv2 admin tenant member update](metalctlv2_admin_tenant_member_update.md) - updates the member + diff --git a/docs/admin/metalctlv2_admin_tenant_member_apply.md b/docs/admin/metalctlv2_admin_tenant_member_apply.md new file mode 100644 index 0000000..b1fb0ed --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_member_apply.md @@ -0,0 +1,46 @@ +## metalctlv2 admin tenant member apply + +applies one or more members from a given file + +``` +metalctlv2 admin tenant member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member apply -f - + $ # or via file + $ metalctlv2 member apply -f member.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 tenant member](metalctlv2_admin_tenant_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_tenant_member_create.md b/docs/admin/metalctlv2_admin_tenant_member_create.md new file mode 100644 index 0000000..281487d --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_member_create.md @@ -0,0 +1,47 @@ +## metalctlv2 admin tenant member create + +creates the member + +``` +metalctlv2 admin tenant member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member create -f - + $ # or via file + $ metalctlv2 member create -f member.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 + --tenant string the tenant for which to create a tenant member, defaults to tenant of the default project + --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 tenant member](metalctlv2_admin_tenant_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_tenant_member_delete.md b/docs/admin/metalctlv2_admin_tenant_member_delete.md new file mode 100644 index 0000000..3d54add --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_member_delete.md @@ -0,0 +1,47 @@ +## metalctlv2 admin tenant member delete + +deletes the member + +``` +metalctlv2 admin tenant member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member delete -f - + $ # or via file + $ metalctlv2 member delete -f member.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 + --tenant string the tenant from which to delete a tenant member, defaults to tenant of the default project + --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 tenant member](metalctlv2_admin_tenant_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_tenant_add-member.md b/docs/admin/metalctlv2_admin_tenant_member_describe.md similarity index 60% rename from docs/admin/metalctlv2_admin_tenant_add-member.md rename to docs/admin/metalctlv2_admin_tenant_member_describe.md index bf95939..eb96e54 100644 --- a/docs/admin/metalctlv2_admin_tenant_add-member.md +++ b/docs/admin/metalctlv2_admin_tenant_member_describe.md @@ -1,22 +1,16 @@ -## metalctlv2 admin tenant add-member +## metalctlv2 admin tenant member describe -Add a new member to a tenant - -### Synopsis - -Add a new member to an existing tenant by specifying the tenant ID, member's ID, and role. +describes the member ``` -metalctlv2 admin tenant add-member [flags] +metalctlv2 admin tenant member describe [flags] ``` ### Options ``` - -h, --help help for add-member - --member-id string ID of the member to be added - --role string Role of the member within the tenant - --tenant-id string ID of the tenant where the member is added + -h, --help help for describe + --tenant string the tenant to describe the tenant members of, defaults to tenant of the default project ``` ### Options inherited from parent commands @@ -34,5 +28,5 @@ metalctlv2 admin tenant add-member [flags] ### SEE ALSO -* [metalctlv2 admin tenant](metalctlv2_admin_tenant.md) - manage tenant entities +* [metalctlv2 admin tenant member](metalctlv2_admin_tenant_member.md) - manage member entities diff --git a/docs/admin/metalctlv2_admin_tenant_member_edit.md b/docs/admin/metalctlv2_admin_tenant_member_edit.md new file mode 100644 index 0000000..4fc4077 --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_member_edit.md @@ -0,0 +1,32 @@ +## metalctlv2 admin tenant member edit + +edit the member through an editor and update + +``` +metalctlv2 admin tenant member edit [flags] +``` + +### Options + +``` + -h, --help help for edit + --tenant string the tenant from which to edit a tenant member, defaults to tenant of the default project +``` + +### 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 tenant member](metalctlv2_admin_tenant_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_tenant_member_list.md b/docs/admin/metalctlv2_admin_tenant_member_list.md new file mode 100644 index 0000000..990efb4 --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_member_list.md @@ -0,0 +1,34 @@ +## metalctlv2 admin tenant member list + +list all members + +``` +metalctlv2 admin tenant member list [flags] +``` + +### Options + +``` + -h, --help help for list + --role string the role of the member + --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: created|id|role + --tenant string the tenant to list the tenant members of, defaults to tenant of the default project +``` + +### 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 tenant member](metalctlv2_admin_tenant_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_tenant_member_update.md b/docs/admin/metalctlv2_admin_tenant_member_update.md new file mode 100644 index 0000000..cb9511e --- /dev/null +++ b/docs/admin/metalctlv2_admin_tenant_member_update.md @@ -0,0 +1,48 @@ +## metalctlv2 admin tenant member update + +updates the member + +``` +metalctlv2 admin tenant member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member update -f - + $ # or via file + $ metalctlv2 member update -f member.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for update + --role string the role of the member + --skip-security-prompts skips security prompt for bulk operations + --tenant string the tenant for which to update a tenant member, defaults to tenant of the default project + --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 tenant member](metalctlv2_admin_tenant_member.md) - manage member entities + diff --git a/docs/metalctlv2_project_member.md b/docs/metalctlv2_project_member.md index 8a9553f..d7271b8 100644 --- a/docs/metalctlv2_project_member.md +++ b/docs/metalctlv2_project_member.md @@ -33,6 +33,7 @@ manage api project members * [metalctlv2 project member delete](metalctlv2_project_member_delete.md) - deletes the member * [metalctlv2 project member describe](metalctlv2_project_member_describe.md) - describes the member * [metalctlv2 project member edit](metalctlv2_project_member_edit.md) - edit the member through an editor and update +* [metalctlv2 project member leave](metalctlv2_project_member_leave.md) - leave the given project * [metalctlv2 project member list](metalctlv2_project_member_list.md) - list all members * [metalctlv2 project member update](metalctlv2_project_member_update.md) - updates the member diff --git a/docs/metalctlv2_project_member_leave.md b/docs/metalctlv2_project_member_leave.md new file mode 100644 index 0000000..7f2d740 --- /dev/null +++ b/docs/metalctlv2_project_member_leave.md @@ -0,0 +1,32 @@ +## metalctlv2 project member leave + +leave the given project + +``` +metalctlv2 project member leave [flags] +``` + +### Options + +``` + -h, --help help for leave + --project string the project to leave, defaults to project of the default project +``` + +### 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 project member](metalctlv2_project_member.md) - manage member entities + diff --git a/docs/metalctlv2_tenant_member.md b/docs/metalctlv2_tenant_member.md index 51ab871..ac4dd69 100644 --- a/docs/metalctlv2_tenant_member.md +++ b/docs/metalctlv2_tenant_member.md @@ -33,6 +33,7 @@ manage api tenant members * [metalctlv2 tenant member delete](metalctlv2_tenant_member_delete.md) - deletes the member * [metalctlv2 tenant member describe](metalctlv2_tenant_member_describe.md) - describes the member * [metalctlv2 tenant member edit](metalctlv2_tenant_member_edit.md) - edit the member through an editor and update +* [metalctlv2 tenant member leave](metalctlv2_tenant_member_leave.md) - leave the given tenant * [metalctlv2 tenant member list](metalctlv2_tenant_member_list.md) - list all members * [metalctlv2 tenant member update](metalctlv2_tenant_member_update.md) - updates the member diff --git a/docs/metalctlv2_tenant_member_leave.md b/docs/metalctlv2_tenant_member_leave.md new file mode 100644 index 0000000..547d212 --- /dev/null +++ b/docs/metalctlv2_tenant_member_leave.md @@ -0,0 +1,32 @@ +## metalctlv2 tenant member leave + +leave the given tenant + +``` +metalctlv2 tenant member leave [flags] +``` + +### Options + +``` + -h, --help help for leave + --tenant string the tenant to leave, defaults to tenant of the default project +``` + +### 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 tenant member](metalctlv2_tenant_member.md) - manage member entities + From a3bd2c263b85686e32d47e4b1fda92a943ec2546 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 26 Oct 2026 14:43:44 +0100 Subject: [PATCH 13/15] Fixes. --- cmd/admin/v2/project_member.go | 9 ++-- cmd/admin/v2/tenant_member.go | 9 ++-- cmd/api/v2/project_member.go | 10 ++-- cmd/api/v2/tenant_member.go | 10 ++-- docs/admin/metalctlv2_admin_project.md | 1 + docs/admin/metalctlv2_admin_project_member.md | 38 +++++++++++++++ .../metalctlv2_admin_project_member_apply.md | 46 ++++++++++++++++++ .../metalctlv2_admin_project_member_create.md | 47 ++++++++++++++++++ .../metalctlv2_admin_project_member_delete.md | 47 ++++++++++++++++++ ...etalctlv2_admin_project_member_describe.md | 32 +++++++++++++ .../metalctlv2_admin_project_member_edit.md | 32 +++++++++++++ .../metalctlv2_admin_project_member_list.md | 34 +++++++++++++ .../metalctlv2_admin_project_member_update.md | 48 +++++++++++++++++++ 13 files changed, 351 insertions(+), 12 deletions(-) create mode 100644 docs/admin/metalctlv2_admin_project_member.md create mode 100644 docs/admin/metalctlv2_admin_project_member_apply.md create mode 100644 docs/admin/metalctlv2_admin_project_member_create.md create mode 100644 docs/admin/metalctlv2_admin_project_member_delete.md create mode 100644 docs/admin/metalctlv2_admin_project_member_describe.md create mode 100644 docs/admin/metalctlv2_admin_project_member_edit.md create mode 100644 docs/admin/metalctlv2_admin_project_member_list.md create mode 100644 docs/admin/metalctlv2_admin_project_member_update.md diff --git a/cmd/admin/v2/project_member.go b/cmd/admin/v2/project_member.go index 653a68e..6d1ff6d 100644 --- a/cmd/admin/v2/project_member.go +++ b/cmd/admin/v2/project_member.go @@ -9,6 +9,7 @@ import ( "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" @@ -149,9 +150,11 @@ func (c *projectMember) Convert(r *apiv2.ProjectMember) (string, *adminv2.Projec Role: r.Role, }, &apiv2.ProjectMemberServiceUpdateRequest{ - Project: r.Project, - Member: r.Member, - Role: pointer.PointerOrNil(r.Role), + Project: r.Project, + Member: r.Member, + Role: pointer.PointerOrNil(r.Role), + UpdateMeta: helpers.UpdateMetaFromMeta(r.Meta), + Labels: helpers.UpdateLabelsFromMeta(r.Meta), }, nil } diff --git a/cmd/admin/v2/tenant_member.go b/cmd/admin/v2/tenant_member.go index 48fd412..ce1168a 100644 --- a/cmd/admin/v2/tenant_member.go +++ b/cmd/admin/v2/tenant_member.go @@ -9,6 +9,7 @@ import ( "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" @@ -158,9 +159,11 @@ func (c *tenantMember) Convert(r *apiv2.TenantMember) (string, *adminv2.TenantSe Role: r.Role, }, &apiv2.TenantMemberServiceUpdateRequest{ - Login: r.Tenant, - Member: r.Member, - Role: pointer.PointerOrNil(r.Role), + Login: r.Tenant, + Member: r.Member, + Role: pointer.PointerOrNil(r.Role), + UpdateMeta: helpers.UpdateMetaFromMeta(r.Meta), + Labels: helpers.UpdateLabelsFromMeta(r.Meta), }, nil } diff --git a/cmd/api/v2/project_member.go b/cmd/api/v2/project_member.go index df80f39..1b39eca 100644 --- a/cmd/api/v2/project_member.go +++ b/cmd/api/v2/project_member.go @@ -8,6 +8,7 @@ import ( "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" @@ -156,11 +157,14 @@ func (c *projectMember) Convert(r *apiv2.ProjectMember) (string, *apiv2.ProjectM Project: r.Project, Member: r.Member, Role: r.Role, + Labels: pointer.SafeDeref(r.Meta).Labels, }, &apiv2.ProjectMemberServiceUpdateRequest{ - Project: r.Project, - Member: r.Member, - Role: pointer.PointerOrNil(r.Role), + Project: r.Project, + Member: r.Member, + Role: pointer.PointerOrNil(r.Role), + UpdateMeta: helpers.UpdateMetaFromMeta(r.Meta), + Labels: helpers.UpdateLabelsFromMeta(r.Meta), }, nil } diff --git a/cmd/api/v2/tenant_member.go b/cmd/api/v2/tenant_member.go index 27cc97f..09cab79 100644 --- a/cmd/api/v2/tenant_member.go +++ b/cmd/api/v2/tenant_member.go @@ -8,6 +8,7 @@ import ( "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" @@ -165,11 +166,14 @@ func (c *tenantMember) Convert(r *apiv2.TenantMember) (string, *apiv2.TenantMemb Login: r.Tenant, Member: r.Member, Role: r.Role, + Labels: pointer.SafeDeref(r.Meta).Labels, }, &apiv2.TenantMemberServiceUpdateRequest{ - Login: r.Tenant, - Member: r.Member, - Role: pointer.PointerOrNil(r.Role), + Login: r.Tenant, + Member: r.Member, + Role: pointer.PointerOrNil(r.Role), + UpdateMeta: helpers.UpdateMetaFromMeta(r.Meta), + Labels: helpers.UpdateLabelsFromMeta(r.Meta), }, nil } diff --git a/docs/admin/metalctlv2_admin_project.md b/docs/admin/metalctlv2_admin_project.md index 6db9799..88cedcd 100644 --- a/docs/admin/metalctlv2_admin_project.md +++ b/docs/admin/metalctlv2_admin_project.md @@ -34,5 +34,6 @@ manage api projects * [metalctlv2 admin project describe](metalctlv2_admin_project_describe.md) - describes the project * [metalctlv2 admin project edit](metalctlv2_admin_project_edit.md) - edit the project through an editor and update * [metalctlv2 admin project list](metalctlv2_admin_project_list.md) - list all projects +* [metalctlv2 admin project member](metalctlv2_admin_project_member.md) - manage member entities * [metalctlv2 admin project update](metalctlv2_admin_project_update.md) - updates the project diff --git a/docs/admin/metalctlv2_admin_project_member.md b/docs/admin/metalctlv2_admin_project_member.md new file mode 100644 index 0000000..d3e82b6 --- /dev/null +++ b/docs/admin/metalctlv2_admin_project_member.md @@ -0,0 +1,38 @@ +## metalctlv2 admin project member + +manage member entities + +### Synopsis + +manage api project members + +### Options + +``` + -h, --help help for member +``` + +### 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 project](metalctlv2_admin_project.md) - manage project entities +* [metalctlv2 admin project member apply](metalctlv2_admin_project_member_apply.md) - applies one or more members from a given file +* [metalctlv2 admin project member create](metalctlv2_admin_project_member_create.md) - creates the member +* [metalctlv2 admin project member delete](metalctlv2_admin_project_member_delete.md) - deletes the member +* [metalctlv2 admin project member describe](metalctlv2_admin_project_member_describe.md) - describes the member +* [metalctlv2 admin project member edit](metalctlv2_admin_project_member_edit.md) - edit the member through an editor and update +* [metalctlv2 admin project member list](metalctlv2_admin_project_member_list.md) - list all members +* [metalctlv2 admin project member update](metalctlv2_admin_project_member_update.md) - updates the member + diff --git a/docs/admin/metalctlv2_admin_project_member_apply.md b/docs/admin/metalctlv2_admin_project_member_apply.md new file mode 100644 index 0000000..0f92bb4 --- /dev/null +++ b/docs/admin/metalctlv2_admin_project_member_apply.md @@ -0,0 +1,46 @@ +## metalctlv2 admin project member apply + +applies one or more members from a given file + +``` +metalctlv2 admin project member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member apply -f - + $ # or via file + $ metalctlv2 member apply -f member.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 project member](metalctlv2_admin_project_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_project_member_create.md b/docs/admin/metalctlv2_admin_project_member_create.md new file mode 100644 index 0000000..0145459 --- /dev/null +++ b/docs/admin/metalctlv2_admin_project_member_create.md @@ -0,0 +1,47 @@ +## metalctlv2 admin project member create + +creates the member + +``` +metalctlv2 admin project member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member create -f - + $ # or via file + $ metalctlv2 member create -f member.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for create + --project string the project for which to create a project member, defaults to project of the default project + --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 project member](metalctlv2_admin_project_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_project_member_delete.md b/docs/admin/metalctlv2_admin_project_member_delete.md new file mode 100644 index 0000000..1a2eef0 --- /dev/null +++ b/docs/admin/metalctlv2_admin_project_member_delete.md @@ -0,0 +1,47 @@ +## metalctlv2 admin project member delete + +deletes the member + +``` +metalctlv2 admin project member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member delete -f - + $ # or via file + $ metalctlv2 member delete -f member.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for delete + --project string the project from which to delete a project member, defaults to project of the default project + --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 project member](metalctlv2_admin_project_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_project_member_describe.md b/docs/admin/metalctlv2_admin_project_member_describe.md new file mode 100644 index 0000000..ebfc716 --- /dev/null +++ b/docs/admin/metalctlv2_admin_project_member_describe.md @@ -0,0 +1,32 @@ +## metalctlv2 admin project member describe + +describes the member + +``` +metalctlv2 admin project member describe [flags] +``` + +### Options + +``` + -h, --help help for describe + --project string the project to describe the project members of, defaults to project of the default project +``` + +### 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 project member](metalctlv2_admin_project_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_project_member_edit.md b/docs/admin/metalctlv2_admin_project_member_edit.md new file mode 100644 index 0000000..5010707 --- /dev/null +++ b/docs/admin/metalctlv2_admin_project_member_edit.md @@ -0,0 +1,32 @@ +## metalctlv2 admin project member edit + +edit the member through an editor and update + +``` +metalctlv2 admin project member edit [flags] +``` + +### Options + +``` + -h, --help help for edit + --project string the project from which to edit a project member, defaults to project of the default project +``` + +### 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 project member](metalctlv2_admin_project_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_project_member_list.md b/docs/admin/metalctlv2_admin_project_member_list.md new file mode 100644 index 0000000..125493b --- /dev/null +++ b/docs/admin/metalctlv2_admin_project_member_list.md @@ -0,0 +1,34 @@ +## metalctlv2 admin project member list + +list all members + +``` +metalctlv2 admin project member list [flags] +``` + +### Options + +``` + -h, --help help for list + --project string the project to list the project members of, defaults to project of the default project + --role string the role of the member + --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: created|id|inherited|role +``` + +### 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 project member](metalctlv2_admin_project_member.md) - manage member entities + diff --git a/docs/admin/metalctlv2_admin_project_member_update.md b/docs/admin/metalctlv2_admin_project_member_update.md new file mode 100644 index 0000000..9db4264 --- /dev/null +++ b/docs/admin/metalctlv2_admin_project_member_update.md @@ -0,0 +1,48 @@ +## metalctlv2 admin project member update + +updates the member + +``` +metalctlv2 admin project member 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 member describe member-1 -o yaml > member.yaml + $ vi member.yaml + $ # either via stdin + $ cat member.yaml | metalctlv2 member update -f - + $ # or via file + $ metalctlv2 member update -f member.yaml + + the file can also contain multiple documents and perform a bulk operation. + + -h, --help help for update + --project string the project for which to update a project member, defaults to project of the default project + --role string the role of the member + --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 project member](metalctlv2_admin_project_member.md) - manage member entities + From 6facb20926a8f44eda5d24165f73f041eadf1c8c Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 26 Oct 2026 14:47:54 +0100 Subject: [PATCH 14/15] Update. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2635e88..f2864b6 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/fatih/color v1.19.0 github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 - github.com/metal-stack/api v0.8.5-0.20261026114232-c7f418456ee8 + github.com/metal-stack/api v0.8.5-0.20261026134536-c07a525c4b94 github.com/metal-stack/metal-console v0.8.2 github.com/metal-stack/metal-lib v0.26.4 github.com/metal-stack/v v1.0.3 diff --git a/go.sum b/go.sum index 74c3b35..c204474 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= github.com/mdlayher/socket v0.8.0 h1:1lIVvHAs3sT623kPkPDa+I0nLVusskwnpWzp6ZUcNyc= github.com/mdlayher/socket v0.8.0/go.mod h1:EhNb63aXqexVoTJDblxvDOSuIGIJinC4H5ZktnaJ6Rk= -github.com/metal-stack/api v0.8.5-0.20261026114232-c7f418456ee8 h1:ouCNMukKGG4oyJSZsgAxJTc2QyKDSrSWjOMM36rmNAE= -github.com/metal-stack/api v0.8.5-0.20261026114232-c7f418456ee8/go.mod h1:U0CJxoo35jpS87HNDbVDrilxR106TnE5rJ/xSmgQkPg= +github.com/metal-stack/api v0.8.5-0.20261026134536-c07a525c4b94 h1:04P1q9TE0WXXvwvfMuEWJEVzNXQtlpJvhJ07cu8WjDk= +github.com/metal-stack/api v0.8.5-0.20261026134536-c07a525c4b94/go.mod h1:U0CJxoo35jpS87HNDbVDrilxR106TnE5rJ/xSmgQkPg= github.com/metal-stack/metal-console v0.8.2 h1:Y86penzEROJKcLica1PeE2ddtr9G0/u16HiCAYXaxxU= github.com/metal-stack/metal-console v0.8.2/go.mod h1:ZkselOaxF/iELFL9Tku3maXgpX4DM0CHjoG7CGuky6E= github.com/metal-stack/metal-lib v0.26.4 h1:Qg+JorX0AgQCkrCK07wvstGEoRcm/Y97E2UG5ZBAXjo= From 94ba556f608cb2f671680c138f37198bda264a22 Mon Sep 17 00:00:00 2001 From: Gerrit Date: Mon, 26 Oct 2026 15:34:27 +0100 Subject: [PATCH 15/15] Rename fsl. --- cmd/admin/v2/filesystem.go | 5 +- cmd/api/v2/filesystem.go | 5 +- docs/admin/metalctlv2_admin.md | 2 +- docs/admin/metalctlv2_admin_filesystem.md | 39 ---------- .../metalctlv2_admin_filesystemlayout.md | 39 ++++++++++ ...etalctlv2_admin_filesystemlayout_apply.md} | 16 ++-- ...talctlv2_admin_filesystemlayout_create.md} | 16 ++-- ...talctlv2_admin_filesystemlayout_delete.md} | 16 ++-- ...lctlv2_admin_filesystemlayout_describe.md} | 8 +- ...metalctlv2_admin_filesystemlayout_edit.md} | 8 +- ...metalctlv2_admin_filesystemlayout_list.md} | 8 +- ...etalctlv2_admin_filesystemlayout_match.md} | 6 +- ...talctlv2_admin_filesystemlayout_update.md} | 16 ++-- docs/metalctlv2.md | 2 +- ...stem.md => metalctlv2_filesystemlayout.md} | 10 +-- ...> metalctlv2_filesystemlayout_describe.md} | 8 +- ...md => metalctlv2_filesystemlayout_list.md} | 8 +- tests/e2e/admin/filesystem_test.go | 78 +++++++++---------- tests/e2e/api/filesystem_test.go | 24 +++--- 19 files changed, 158 insertions(+), 156 deletions(-) delete mode 100644 docs/admin/metalctlv2_admin_filesystem.md create mode 100644 docs/admin/metalctlv2_admin_filesystemlayout.md rename docs/admin/{metalctlv2_admin_filesystem_apply.md => metalctlv2_admin_filesystemlayout_apply.md} (73%) rename docs/admin/{metalctlv2_admin_filesystem_create.md => metalctlv2_admin_filesystemlayout_create.md} (74%) rename docs/admin/{metalctlv2_admin_filesystem_delete.md => metalctlv2_admin_filesystemlayout_delete.md} (73%) rename docs/{metalctlv2_filesystem_describe.md => admin/metalctlv2_admin_filesystemlayout_describe.md} (77%) rename docs/admin/{metalctlv2_admin_filesystem_edit.md => metalctlv2_admin_filesystemlayout_edit.md} (76%) rename docs/admin/{metalctlv2_admin_filesystem_list.md => metalctlv2_admin_filesystemlayout_list.md} (83%) rename docs/admin/{metalctlv2_admin_filesystem_match.md => metalctlv2_admin_filesystemlayout_match.md} (87%) rename docs/admin/{metalctlv2_admin_filesystem_update.md => metalctlv2_admin_filesystemlayout_update.md} (74%) rename docs/{metalctlv2_filesystem.md => metalctlv2_filesystemlayout.md} (75%) rename docs/{admin/metalctlv2_admin_filesystem_describe.md => metalctlv2_filesystemlayout_describe.md} (79%) rename docs/{metalctlv2_filesystem_list.md => metalctlv2_filesystemlayout_list.md} (84%) diff --git a/cmd/admin/v2/filesystem.go b/cmd/admin/v2/filesystem.go index 6f13aec..fbda5b1 100644 --- a/cmd/admin/v2/filesystem.go +++ b/cmd/admin/v2/filesystem.go @@ -28,8 +28,9 @@ func newFilesystemCmd(c *config.Config) *cobra.Command { cmdsConfig := &genericcli.CmdsConfig[*adminv2.FilesystemServiceCreateRequest, *adminv2.FilesystemServiceUpdateRequest, *apiv2.FilesystemLayout]{ BinaryName: config.BinaryName, GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), - Singular: "filesystem", - Plural: "filesystems", + Singular: "filesystemlayout", + Plural: "filesystemlayouts", + Aliases: []string{"fsl"}, Description: "manage filesystem layouts which define how disks, raid arrays and volumes are set up on machines during installation", Sorter: sorters.FilesystemSorter(), DescribePrinter: func() printers.Printer { return c.DescribePrinter }, diff --git a/cmd/api/v2/filesystem.go b/cmd/api/v2/filesystem.go index 4de5032..f9922bc 100644 --- a/cmd/api/v2/filesystem.go +++ b/cmd/api/v2/filesystem.go @@ -25,8 +25,9 @@ func newFilesystemCmd(c *config.Config) *cobra.Command { cmdsConfig := &genericcli.CmdsConfig[any, any, *apiv2.FilesystemLayout]{ BinaryName: config.BinaryName, GenericCLI: genericcli.NewGenericCLI(w).WithFS(c.Fs), - Singular: "filesystem", - Plural: "filesystems", + Singular: "filesystemlayout", + Plural: "filesystemlayouts", + Aliases: []string{"fsl"}, Description: "a filesystem layout defines how disks, raid arrays and volumes are set up on a machine during installation", Sorter: sorters.FilesystemSorter(), OnlyCmds: genericcli.OnlyCmds(genericcli.ListCmd, genericcli.DescribeCmd), diff --git a/docs/admin/metalctlv2_admin.md b/docs/admin/metalctlv2_admin.md index f6b5a8e..e620c5f 100644 --- a/docs/admin/metalctlv2_admin.md +++ b/docs/admin/metalctlv2_admin.md @@ -30,7 +30,7 @@ these commands utilize the admin api, which can only be accessed by metal-stack * [metalctlv2](metalctlv2.md) - cli for managing entities in metal-stack * [metalctlv2 admin audit](metalctlv2_admin_audit.md) - manage audit entities * [metalctlv2 admin component](metalctlv2_admin_component.md) - manage component entities -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities * [metalctlv2 admin image](metalctlv2_admin_image.md) - manage image entities * [metalctlv2 admin ip](metalctlv2_admin_ip.md) - manage ip entities * [metalctlv2 admin machine](metalctlv2_admin_machine.md) - manage machine entities diff --git a/docs/admin/metalctlv2_admin_filesystem.md b/docs/admin/metalctlv2_admin_filesystem.md deleted file mode 100644 index bd92e27..0000000 --- a/docs/admin/metalctlv2_admin_filesystem.md +++ /dev/null @@ -1,39 +0,0 @@ -## metalctlv2 admin filesystem - -manage filesystem entities - -### Synopsis - -manage filesystem layouts which define how disks, raid arrays and volumes are set up on machines during installation - -### Options - -``` - -h, --help help for filesystem -``` - -### 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 filesystem apply](metalctlv2_admin_filesystem_apply.md) - applies one or more filesystems from a given file -* [metalctlv2 admin filesystem create](metalctlv2_admin_filesystem_create.md) - creates the filesystem -* [metalctlv2 admin filesystem delete](metalctlv2_admin_filesystem_delete.md) - deletes the filesystem -* [metalctlv2 admin filesystem describe](metalctlv2_admin_filesystem_describe.md) - describes the filesystem -* [metalctlv2 admin filesystem edit](metalctlv2_admin_filesystem_edit.md) - edit the filesystem through an editor and update -* [metalctlv2 admin filesystem list](metalctlv2_admin_filesystem_list.md) - list all filesystems -* [metalctlv2 admin filesystem match](metalctlv2_admin_filesystem_match.md) - matches a filesystem layout to a size and image or to a machine -* [metalctlv2 admin filesystem update](metalctlv2_admin_filesystem_update.md) - updates the filesystem - diff --git a/docs/admin/metalctlv2_admin_filesystemlayout.md b/docs/admin/metalctlv2_admin_filesystemlayout.md new file mode 100644 index 0000000..89dea36 --- /dev/null +++ b/docs/admin/metalctlv2_admin_filesystemlayout.md @@ -0,0 +1,39 @@ +## metalctlv2 admin filesystemlayout + +manage filesystemlayout entities + +### Synopsis + +manage filesystem layouts which define how disks, raid arrays and volumes are set up on machines during installation + +### Options + +``` + -h, --help help for filesystemlayout +``` + +### 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 filesystemlayout apply](metalctlv2_admin_filesystemlayout_apply.md) - applies one or more filesystemlayouts from a given file +* [metalctlv2 admin filesystemlayout create](metalctlv2_admin_filesystemlayout_create.md) - creates the filesystemlayout +* [metalctlv2 admin filesystemlayout delete](metalctlv2_admin_filesystemlayout_delete.md) - deletes the filesystemlayout +* [metalctlv2 admin filesystemlayout describe](metalctlv2_admin_filesystemlayout_describe.md) - describes the filesystemlayout +* [metalctlv2 admin filesystemlayout edit](metalctlv2_admin_filesystemlayout_edit.md) - edit the filesystemlayout through an editor and update +* [metalctlv2 admin filesystemlayout list](metalctlv2_admin_filesystemlayout_list.md) - list all filesystemlayouts +* [metalctlv2 admin filesystemlayout match](metalctlv2_admin_filesystemlayout_match.md) - matches a filesystem layout to a size and image or to a machine +* [metalctlv2 admin filesystemlayout update](metalctlv2_admin_filesystemlayout_update.md) - updates the filesystemlayout + diff --git a/docs/admin/metalctlv2_admin_filesystem_apply.md b/docs/admin/metalctlv2_admin_filesystemlayout_apply.md similarity index 73% rename from docs/admin/metalctlv2_admin_filesystem_apply.md rename to docs/admin/metalctlv2_admin_filesystemlayout_apply.md index fc4b645..3b633f8 100644 --- a/docs/admin/metalctlv2_admin_filesystem_apply.md +++ b/docs/admin/metalctlv2_admin_filesystemlayout_apply.md @@ -1,9 +1,9 @@ -## metalctlv2 admin filesystem apply +## metalctlv2 admin filesystemlayout apply -applies one or more filesystems from a given file +applies one or more filesystemlayouts from a given file ``` -metalctlv2 admin filesystem apply [flags] +metalctlv2 admin filesystemlayout apply [flags] ``` ### Options @@ -13,12 +13,12 @@ metalctlv2 admin filesystem apply [flags] -f, --file string filename of the create or update request in yaml format, or - for stdin. Example: - $ metalctlv2 filesystem describe filesystem-1 -o yaml > filesystem.yaml - $ vi filesystem.yaml + $ metalctlv2 filesystemlayout describe filesystemlayout-1 -o yaml > filesystemlayout.yaml + $ vi filesystemlayout.yaml $ # either via stdin - $ cat filesystem.yaml | metalctlv2 filesystem apply -f - + $ cat filesystemlayout.yaml | metalctlv2 filesystemlayout apply -f - $ # or via file - $ metalctlv2 filesystem apply -f filesystem.yaml + $ metalctlv2 filesystemlayout apply -f filesystemlayout.yaml the file can also contain multiple documents and perform a bulk operation. @@ -42,5 +42,5 @@ metalctlv2 admin filesystem apply [flags] ### SEE ALSO -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/admin/metalctlv2_admin_filesystem_create.md b/docs/admin/metalctlv2_admin_filesystemlayout_create.md similarity index 74% rename from docs/admin/metalctlv2_admin_filesystem_create.md rename to docs/admin/metalctlv2_admin_filesystemlayout_create.md index 7f451a3..16fc9d7 100644 --- a/docs/admin/metalctlv2_admin_filesystem_create.md +++ b/docs/admin/metalctlv2_admin_filesystemlayout_create.md @@ -1,9 +1,9 @@ -## metalctlv2 admin filesystem create +## metalctlv2 admin filesystemlayout create -creates the filesystem +creates the filesystemlayout ``` -metalctlv2 admin filesystem create [flags] +metalctlv2 admin filesystemlayout create [flags] ``` ### Options @@ -13,12 +13,12 @@ metalctlv2 admin filesystem create [flags] -f, --file string filename of the create or update request in yaml format, or - for stdin. Example: - $ metalctlv2 filesystem describe filesystem-1 -o yaml > filesystem.yaml - $ vi filesystem.yaml + $ metalctlv2 filesystemlayout describe filesystemlayout-1 -o yaml > filesystemlayout.yaml + $ vi filesystemlayout.yaml $ # either via stdin - $ cat filesystem.yaml | metalctlv2 filesystem create -f - + $ cat filesystemlayout.yaml | metalctlv2 filesystemlayout create -f - $ # or via file - $ metalctlv2 filesystem create -f filesystem.yaml + $ metalctlv2 filesystemlayout create -f filesystemlayout.yaml the file can also contain multiple documents and perform a bulk operation. @@ -42,5 +42,5 @@ metalctlv2 admin filesystem create [flags] ### SEE ALSO -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/admin/metalctlv2_admin_filesystem_delete.md b/docs/admin/metalctlv2_admin_filesystemlayout_delete.md similarity index 73% rename from docs/admin/metalctlv2_admin_filesystem_delete.md rename to docs/admin/metalctlv2_admin_filesystemlayout_delete.md index da89f53..34c7491 100644 --- a/docs/admin/metalctlv2_admin_filesystem_delete.md +++ b/docs/admin/metalctlv2_admin_filesystemlayout_delete.md @@ -1,9 +1,9 @@ -## metalctlv2 admin filesystem delete +## metalctlv2 admin filesystemlayout delete -deletes the filesystem +deletes the filesystemlayout ``` -metalctlv2 admin filesystem delete [flags] +metalctlv2 admin filesystemlayout delete [flags] ``` ### Options @@ -13,12 +13,12 @@ metalctlv2 admin filesystem delete [flags] -f, --file string filename of the create or update request in yaml format, or - for stdin. Example: - $ metalctlv2 filesystem describe filesystem-1 -o yaml > filesystem.yaml - $ vi filesystem.yaml + $ metalctlv2 filesystemlayout describe filesystemlayout-1 -o yaml > filesystemlayout.yaml + $ vi filesystemlayout.yaml $ # either via stdin - $ cat filesystem.yaml | metalctlv2 filesystem delete -f - + $ cat filesystemlayout.yaml | metalctlv2 filesystemlayout delete -f - $ # or via file - $ metalctlv2 filesystem delete -f filesystem.yaml + $ metalctlv2 filesystemlayout delete -f filesystemlayout.yaml the file can also contain multiple documents and perform a bulk operation. @@ -42,5 +42,5 @@ metalctlv2 admin filesystem delete [flags] ### SEE ALSO -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/metalctlv2_filesystem_describe.md b/docs/admin/metalctlv2_admin_filesystemlayout_describe.md similarity index 77% rename from docs/metalctlv2_filesystem_describe.md rename to docs/admin/metalctlv2_admin_filesystemlayout_describe.md index 5ad91d7..cee52a7 100644 --- a/docs/metalctlv2_filesystem_describe.md +++ b/docs/admin/metalctlv2_admin_filesystemlayout_describe.md @@ -1,9 +1,9 @@ -## metalctlv2 filesystem describe +## metalctlv2 admin filesystemlayout describe -describes the filesystem +describes the filesystemlayout ``` -metalctlv2 filesystem describe [flags] +metalctlv2 admin filesystemlayout describe [flags] ``` ### Options @@ -27,5 +27,5 @@ metalctlv2 filesystem describe [flags] ### SEE ALSO -* [metalctlv2 filesystem](metalctlv2_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/admin/metalctlv2_admin_filesystem_edit.md b/docs/admin/metalctlv2_admin_filesystemlayout_edit.md similarity index 76% rename from docs/admin/metalctlv2_admin_filesystem_edit.md rename to docs/admin/metalctlv2_admin_filesystemlayout_edit.md index 0f5e807..4cc6e00 100644 --- a/docs/admin/metalctlv2_admin_filesystem_edit.md +++ b/docs/admin/metalctlv2_admin_filesystemlayout_edit.md @@ -1,9 +1,9 @@ -## metalctlv2 admin filesystem edit +## metalctlv2 admin filesystemlayout edit -edit the filesystem through an editor and update +edit the filesystemlayout through an editor and update ``` -metalctlv2 admin filesystem edit [flags] +metalctlv2 admin filesystemlayout edit [flags] ``` ### Options @@ -27,5 +27,5 @@ metalctlv2 admin filesystem edit [flags] ### SEE ALSO -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/admin/metalctlv2_admin_filesystem_list.md b/docs/admin/metalctlv2_admin_filesystemlayout_list.md similarity index 83% rename from docs/admin/metalctlv2_admin_filesystem_list.md rename to docs/admin/metalctlv2_admin_filesystemlayout_list.md index 552e4fc..db8e657 100644 --- a/docs/admin/metalctlv2_admin_filesystem_list.md +++ b/docs/admin/metalctlv2_admin_filesystemlayout_list.md @@ -1,9 +1,9 @@ -## metalctlv2 admin filesystem list +## metalctlv2 admin filesystemlayout list -list all filesystems +list all filesystemlayouts ``` -metalctlv2 admin filesystem list [flags] +metalctlv2 admin filesystemlayout list [flags] ``` ### Options @@ -29,5 +29,5 @@ metalctlv2 admin filesystem list [flags] ### SEE ALSO -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/admin/metalctlv2_admin_filesystem_match.md b/docs/admin/metalctlv2_admin_filesystemlayout_match.md similarity index 87% rename from docs/admin/metalctlv2_admin_filesystem_match.md rename to docs/admin/metalctlv2_admin_filesystemlayout_match.md index 262dcd8..caa4be2 100644 --- a/docs/admin/metalctlv2_admin_filesystem_match.md +++ b/docs/admin/metalctlv2_admin_filesystemlayout_match.md @@ -1,4 +1,4 @@ -## metalctlv2 admin filesystem match +## metalctlv2 admin filesystemlayout match matches a filesystem layout to a size and image or to a machine @@ -8,7 +8,7 @@ matches a filesystem layout either to a given size and image combination, or checks whether a given filesystem layout matches a given machine. ``` -metalctlv2 admin filesystem match [flags] +metalctlv2 admin filesystemlayout match [flags] ``` ### Options @@ -36,5 +36,5 @@ metalctlv2 admin filesystem match [flags] ### SEE ALSO -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/admin/metalctlv2_admin_filesystem_update.md b/docs/admin/metalctlv2_admin_filesystemlayout_update.md similarity index 74% rename from docs/admin/metalctlv2_admin_filesystem_update.md rename to docs/admin/metalctlv2_admin_filesystemlayout_update.md index 92a8ba9..848ed6d 100644 --- a/docs/admin/metalctlv2_admin_filesystem_update.md +++ b/docs/admin/metalctlv2_admin_filesystemlayout_update.md @@ -1,9 +1,9 @@ -## metalctlv2 admin filesystem update +## metalctlv2 admin filesystemlayout update -updates the filesystem +updates the filesystemlayout ``` -metalctlv2 admin filesystem update [flags] +metalctlv2 admin filesystemlayout update [flags] ``` ### Options @@ -13,12 +13,12 @@ metalctlv2 admin filesystem update [flags] -f, --file string filename of the create or update request in yaml format, or - for stdin. Example: - $ metalctlv2 filesystem describe filesystem-1 -o yaml > filesystem.yaml - $ vi filesystem.yaml + $ metalctlv2 filesystemlayout describe filesystemlayout-1 -o yaml > filesystemlayout.yaml + $ vi filesystemlayout.yaml $ # either via stdin - $ cat filesystem.yaml | metalctlv2 filesystem update -f - + $ cat filesystemlayout.yaml | metalctlv2 filesystemlayout update -f - $ # or via file - $ metalctlv2 filesystem update -f filesystem.yaml + $ metalctlv2 filesystemlayout update -f filesystemlayout.yaml the file can also contain multiple documents and perform a bulk operation. @@ -42,5 +42,5 @@ metalctlv2 admin filesystem update [flags] ### SEE ALSO -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 admin filesystemlayout](metalctlv2_admin_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/metalctlv2.md b/docs/metalctlv2.md index d13d48c..5560c94 100644 --- a/docs/metalctlv2.md +++ b/docs/metalctlv2.md @@ -22,7 +22,7 @@ cli for managing entities in metal-stack * [metalctlv2 audit](metalctlv2_audit.md) - manage audit entities * [metalctlv2 completion](metalctlv2_completion.md) - Generate the autocompletion script for the specified shell * [metalctlv2 context](metalctlv2_context.md) - manage cli contexts -* [metalctlv2 filesystem](metalctlv2_filesystem.md) - manage filesystem entities +* [metalctlv2 filesystemlayout](metalctlv2_filesystemlayout.md) - manage filesystemlayout entities * [metalctlv2 health](metalctlv2_health.md) - print the client and server health information * [metalctlv2 image](metalctlv2_image.md) - manage image entities * [metalctlv2 ip](metalctlv2_ip.md) - manage ip entities diff --git a/docs/metalctlv2_filesystem.md b/docs/metalctlv2_filesystemlayout.md similarity index 75% rename from docs/metalctlv2_filesystem.md rename to docs/metalctlv2_filesystemlayout.md index 91624e2..518ad5c 100644 --- a/docs/metalctlv2_filesystem.md +++ b/docs/metalctlv2_filesystemlayout.md @@ -1,6 +1,6 @@ -## metalctlv2 filesystem +## metalctlv2 filesystemlayout -manage filesystem entities +manage filesystemlayout entities ### Synopsis @@ -9,7 +9,7 @@ a filesystem layout defines how disks, raid arrays and volumes are set up on a m ### Options ``` - -h, --help help for filesystem + -h, --help help for filesystemlayout ``` ### Options inherited from parent commands @@ -28,6 +28,6 @@ a filesystem layout defines how disks, raid arrays and volumes are set up on a m ### SEE ALSO * [metalctlv2](metalctlv2.md) - cli for managing entities in metal-stack -* [metalctlv2 filesystem describe](metalctlv2_filesystem_describe.md) - describes the filesystem -* [metalctlv2 filesystem list](metalctlv2_filesystem_list.md) - list all filesystems +* [metalctlv2 filesystemlayout describe](metalctlv2_filesystemlayout_describe.md) - describes the filesystemlayout +* [metalctlv2 filesystemlayout list](metalctlv2_filesystemlayout_list.md) - list all filesystemlayouts diff --git a/docs/admin/metalctlv2_admin_filesystem_describe.md b/docs/metalctlv2_filesystemlayout_describe.md similarity index 79% rename from docs/admin/metalctlv2_admin_filesystem_describe.md rename to docs/metalctlv2_filesystemlayout_describe.md index f331aa9..09231b3 100644 --- a/docs/admin/metalctlv2_admin_filesystem_describe.md +++ b/docs/metalctlv2_filesystemlayout_describe.md @@ -1,9 +1,9 @@ -## metalctlv2 admin filesystem describe +## metalctlv2 filesystemlayout describe -describes the filesystem +describes the filesystemlayout ``` -metalctlv2 admin filesystem describe [flags] +metalctlv2 filesystemlayout describe [flags] ``` ### Options @@ -27,5 +27,5 @@ metalctlv2 admin filesystem describe [flags] ### SEE ALSO -* [metalctlv2 admin filesystem](metalctlv2_admin_filesystem.md) - manage filesystem entities +* [metalctlv2 filesystemlayout](metalctlv2_filesystemlayout.md) - manage filesystemlayout entities diff --git a/docs/metalctlv2_filesystem_list.md b/docs/metalctlv2_filesystemlayout_list.md similarity index 84% rename from docs/metalctlv2_filesystem_list.md rename to docs/metalctlv2_filesystemlayout_list.md index 35a5a9d..7379b84 100644 --- a/docs/metalctlv2_filesystem_list.md +++ b/docs/metalctlv2_filesystemlayout_list.md @@ -1,9 +1,9 @@ -## metalctlv2 filesystem list +## metalctlv2 filesystemlayout list -list all filesystems +list all filesystemlayouts ``` -metalctlv2 filesystem list [flags] +metalctlv2 filesystemlayout list [flags] ``` ### Options @@ -29,5 +29,5 @@ metalctlv2 filesystem list [flags] ### SEE ALSO -* [metalctlv2 filesystem](metalctlv2_filesystem.md) - manage filesystem entities +* [metalctlv2 filesystemlayout](metalctlv2_filesystemlayout.md) - manage filesystemlayout entities diff --git a/tests/e2e/admin/filesystem_test.go b/tests/e2e/admin/filesystem_test.go index f75a35c..84da935 100644 --- a/tests/e2e/admin/filesystem_test.go +++ b/tests/e2e/admin/filesystem_test.go @@ -19,7 +19,7 @@ func Test_AdminFileSystemCmd_List(t *testing.T) { tests := []*e2e.Test[apiv2.FilesystemServiceListResponse, *apiv2.FilesystemLayout]{ { Name: "list", - CmdArgs: []string{"admin", "filesystem", "list"}, + CmdArgs: []string{"admin", "filesystemlayout", "list"}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { @@ -36,14 +36,14 @@ func Test_AdminFileSystemCmd_List(t *testing.T) { }, }), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 - ubuntu-raid1 Ubuntu Raid1 Raid1 layout for ubuntu machines m2.large ubuntu:>= 24.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ubuntu-raid1 Ubuntu Raid1 Raid1 layout for ubuntu machines m2.large ubuntu:>= 24.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 - ubuntu-raid1 Ubuntu Raid1 Raid1 layout for ubuntu machines m2.large ubuntu:>= 24.0 0 0 1 1 1 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ubuntu-raid1 Ubuntu Raid1 Raid1 layout for ubuntu machines m2.large ubuntu:>= 24.0 0 0 1 1 1 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(` @@ -67,7 +67,7 @@ func Test_AdminFileSystemCmd_Describe(t *testing.T) { tests := []*e2e.Test[apiv2.FilesystemServiceGetResponse, *apiv2.FilesystemLayout]{ { Name: "describe", - CmdArgs: []string{"admin", "filesystem", "describe", testresources.FilesystemLayout1().Id}, + CmdArgs: []string{"admin", "filesystemlayout", "describe", testresources.FilesystemLayout1().Id}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { @@ -84,12 +84,12 @@ func Test_AdminFileSystemCmd_Describe(t *testing.T) { }), WantProtoObject: testresources.FilesystemLayout1(), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(` @@ -111,7 +111,7 @@ func Test_AdminFileSystemCmd_Create(t *testing.T) { tests := []*e2e.Test[adminv2.FilesystemServiceCreateResponse, *apiv2.FilesystemLayout]{ { Name: "create from file", - CmdArgs: append([]string{"admin", "filesystem", "create"}, e2e.AppendFromFileCommonArgs()...), + CmdArgs: append([]string{"admin", "filesystemlayout", "create"}, e2e.AppendFromFileCommonArgs()...), NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ FsMocks: func(fs *afero.Afero) { @@ -131,12 +131,12 @@ func Test_AdminFileSystemCmd_Create(t *testing.T) { }, }), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(` @@ -160,7 +160,7 @@ func Test_AdminFileSystemCmd_Update(t *testing.T) { tests := []*e2e.Test[adminv2.FilesystemServiceUpdateResponse, *apiv2.FilesystemLayout]{ { Name: "update from file", - CmdArgs: append([]string{"admin", "filesystem", "update"}, e2e.AppendFromFileCommonArgs()...), + CmdArgs: append([]string{"admin", "filesystemlayout", "update"}, e2e.AppendFromFileCommonArgs()...), NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ FsMocks: func(fs *afero.Afero) { @@ -189,12 +189,12 @@ func Test_AdminFileSystemCmd_Update(t *testing.T) { }, }), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(` @@ -216,7 +216,7 @@ func Test_AdminFileSystemCmd_Delete(t *testing.T) { tests := []*e2e.Test[adminv2.FilesystemServiceDeleteResponse, *apiv2.FilesystemLayout]{ { Name: "delete", - CmdArgs: []string{"admin", "filesystem", "delete", testresources.FilesystemLayout1().Id}, + CmdArgs: []string{"admin", "filesystemlayout", "delete", testresources.FilesystemLayout1().Id}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { @@ -233,12 +233,12 @@ func Test_AdminFileSystemCmd_Delete(t *testing.T) { }), WantProtoObject: testresources.FilesystemLayout1(), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(` @@ -260,7 +260,7 @@ func Test_AdminFileSystemCmd_Match_SizeAndImage(t *testing.T) { tests := []*e2e.Test[adminv2.FilesystemServiceMatchResponse, *apiv2.FilesystemLayout]{ { Name: "match size and image", - CmdArgs: []string{"admin", "filesystem", "match", "--size", "m2.small", "--image", "debian"}, + CmdArgs: []string{"admin", "filesystemlayout", "match", "--size", "m2.small", "--image", "debian"}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { @@ -281,12 +281,12 @@ func Test_AdminFileSystemCmd_Match_SizeAndImage(t *testing.T) { }, }), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(` @@ -310,7 +310,7 @@ func Test_AdminFileSystemCmd_Match_MachineAndLayout(t *testing.T) { tests := []*e2e.Test[adminv2.FilesystemServiceMatchResponse, *apiv2.FilesystemLayout]{ { Name: "match machine and filesystem layout", - CmdArgs: []string{"admin", "filesystem", "match", "--machine", machineUUID, "--id", testresources.FilesystemLayout1().Id}, + CmdArgs: []string{"admin", "filesystemlayout", "match", "--machine", machineUUID, "--id", testresources.FilesystemLayout1().Id}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { @@ -331,12 +331,12 @@ func Test_AdminFileSystemCmd_Match_MachineAndLayout(t *testing.T) { }, }), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(` @@ -358,13 +358,13 @@ func Test_AdminFileSystemCmd_Match_InvalidFlags(t *testing.T) { tests := []*e2e.Test[any, any]{ { Name: "match without flags fails", - CmdArgs: []string{"admin", "filesystem", "match"}, + CmdArgs: []string{"admin", "filesystemlayout", "match"}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{}), WantErr: fmt.Errorf("either both --size and --image or both --machine and --id must be set"), }, { Name: "match with only size fails", - CmdArgs: []string{"admin", "filesystem", "match", "--size", "m2.small"}, + CmdArgs: []string{"admin", "filesystemlayout", "match", "--size", "m2.small"}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{}), WantErr: fmt.Errorf("either both --size and --image or both --machine and --id must be set"), }, diff --git a/tests/e2e/api/filesystem_test.go b/tests/e2e/api/filesystem_test.go index e9a447c..99c2f82 100644 --- a/tests/e2e/api/filesystem_test.go +++ b/tests/e2e/api/filesystem_test.go @@ -15,7 +15,7 @@ func Test_FileSystemCmd_List(t *testing.T) { tests := []*e2e.Test[apiv2.FilesystemServiceListResponse, *apiv2.FilesystemLayout]{ { Name: "list", - CmdArgs: []string{"filesystem", "list"}, + CmdArgs: []string{"filesystemlayout", "list"}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { @@ -32,14 +32,14 @@ func Test_FileSystemCmd_List(t *testing.T) { }, }), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 - ubuntu-raid1 Ubuntu Raid1 Raid1 layout for ubuntu machines m2.large ubuntu:>= 24.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ubuntu-raid1 Ubuntu Raid1 Raid1 layout for ubuntu machines m2.large ubuntu:>= 24.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 - ubuntu-raid1 Ubuntu Raid1 Raid1 layout for ubuntu machines m2.large ubuntu:>= 24.0 0 0 1 1 1 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ubuntu-raid1 Ubuntu Raid1 Raid1 layout for ubuntu machines m2.large ubuntu:>= 24.0 0 0 1 1 1 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(` @@ -63,7 +63,7 @@ func Test_FileSystemCmd_Describe(t *testing.T) { tests := []*e2e.Test[apiv2.FilesystemServiceGetResponse, *apiv2.FilesystemLayout]{ { Name: "describe", - CmdArgs: []string{"filesystem", "describe", testresources.FilesystemLayout1().Id}, + CmdArgs: []string{"filesystemlayout", "describe", testresources.FilesystemLayout1().Id}, NewRootCmd: e2erootcmd.NewRootCmd(t, &e2erootcmd.TestConfig{ ClientCalls: []client.ClientCall{ { @@ -80,12 +80,12 @@ func Test_FileSystemCmd_Describe(t *testing.T) { }), WantProtoObject: testresources.FilesystemLayout1(), WantTable: new(` - ID NAME DESCRIPTION SIZES IMAGES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 + ID NAME DESCRIPTION SIZES IMAGES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 `), WantWideTable: new(` - ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES - debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 + ID NAME DESCRIPTION SIZES IMAGES FILESYSTEMS DISKS RAID VOLUME GROUPS LOGICAL VOLUMES + debian-default Debian Default Default layout for debian machines m2.small debian:>= 12.0 1 1 0 0 0 `), Template: new("{{ .id }} {{ .name }}"), WantTemplate: new(`