From e31a9eb4a82d0efd0ef14b30dbadba83c3b2762e Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Fri, 20 Feb 2026 14:45:11 +0100 Subject: [PATCH 1/4] Update to go-1.26 --- .github/workflows/docker.yaml | 6 +-- Dockerfile | 2 +- cmd/admin/v2/tenant.go | 1 - go.mod | 40 ++++++++--------- go.sum | 84 +++++++++++++++++------------------ 5 files changed, 66 insertions(+), 67 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 189060f..c02ff18 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@v6 @@ -30,7 +30,7 @@ jobs: cache: false - name: Lint - uses: golangci/golangci-lint-action@v8 + uses: golangci/golangci-lint-action@v9 with: args: --build-tags integration --timeout=5m @@ -58,7 +58,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Go uses: actions/setup-go@v6 diff --git a/Dockerfile b/Dockerfile index ec358d9..c86b98f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.22 +FROM alpine:3.23 LABEL maintainer="metal-stack authors " COPY bin/metalctlv2-linux-amd64 /metalctl ENTRYPOINT ["/metalctl"] diff --git a/cmd/admin/v2/tenant.go b/cmd/admin/v2/tenant.go index df4f8c2..f385a81 100644 --- a/cmd/admin/v2/tenant.go +++ b/cmd/admin/v2/tenant.go @@ -69,7 +69,6 @@ func (c *tenant) List() ([]*apiv2.Tenant, error) { req := &adminv2.TenantServiceListRequest{ Name: pointer.PointerOrNil(viper.GetString("name")), Login: pointer.PointerOrNil(viper.GetString("id")), - Email: pointer.PointerOrNil(viper.GetString("email")), } resp, err := c.c.Client.Adminv2().Tenant().List(ctx, req) diff --git a/go.mod b/go.mod index 9399bc5..2ef7933 100644 --- a/go.mod +++ b/go.mod @@ -1,45 +1,45 @@ module github.com/metal-stack/cli -go 1.25 +go 1.26 require ( github.com/dustin/go-humanize v1.0.1 github.com/fatih/color v1.18.0 github.com/google/go-cmp v0.7.0 - github.com/metal-stack/api v0.0.35 + github.com/metal-stack/api v0.0.51 github.com/metal-stack/metal-lib v0.23.5 github.com/metal-stack/v v1.0.3 github.com/spf13/afero v1.15.0 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 - golang.org/x/net v0.46.0 - google.golang.org/protobuf v1.36.10 + golang.org/x/net v0.50.0 + google.golang.org/protobuf v1.36.11 sigs.k8s.io/yaml v1.6.0 ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20250912141014-52f32327d4b0.1 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 // indirect connectrpc.com/connect v1.19.1 // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/clipperhouse/uax29/v2 v2.2.0 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-openapi/errors v0.22.3 // indirect - github.com/go-openapi/strfmt v0.24.0 // indirect + github.com/go-openapi/errors v0.22.6 // indirect + github.com/go-openapi/strfmt v0.25.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect - github.com/goccy/go-yaml v1.18.0 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.18.1 // indirect - github.com/klauspost/connect-compress/v2 v2.1.0 // indirect + github.com/klauspost/compress v1.18.4 // indirect + github.com/klauspost/connect-compress/v2 v2.1.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/mattn/go-runewidth v0.0.20 // indirect github.com/minio/minlz v1.0.1 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect @@ -54,13 +54,13 @@ require ( github.com/spf13/cast v1.10.0 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/subosito/gotenv v1.6.0 // indirect - go.mongodb.org/mongo-driver v1.17.4 // indirect + go.mongodb.org/mongo-driver v1.17.9 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/text v0.34.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apimachinery v0.34.1 // indirect + k8s.io/apimachinery v0.35.1 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect ) diff --git a/go.sum b/go.sum index cbca136..075d38a 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,13 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20250912141014-52f32327d4b0.1 h1:31on4W/yPcV4nZHL4+UCiCvLPsMqe/vJcNg8Rci0scc= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20250912141014-52f32327d4b0.1/go.mod h1:fUl8CEN/6ZAMk6bP8ahBJPUJw7rbp+j4x+wCcYi2IG4= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 h1:PMmTMyvHScV9Mn8wc6ASge9uRcHy0jtqPd+fM35LmsQ= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM= connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= -github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= -github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -21,28 +21,30 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-openapi/errors v0.22.3 h1:k6Hxa5Jg1TUyZnOwV2Lh81j8ayNw5VVYLvKrp4zFKFs= -github.com/go-openapi/errors v0.22.3/go.mod h1:+WvbaBBULWCOna//9B9TbLNGSFOfF8lY9dw4hGiEiKQ= -github.com/go-openapi/strfmt v0.24.0 h1:dDsopqbI3wrrlIzeXRbqMihRNnjzGC+ez4NQaAAJLuc= -github.com/go-openapi/strfmt v0.24.0/go.mod h1:Lnn1Bk9rZjXxU9VMADbEEOo7D7CDyKGLsSKekhFr7s4= +github.com/go-openapi/errors v0.22.6 h1:eDxcf89O8odEnohIXwEjY1IB4ph5vmbUsBMsFNwXWPo= +github.com/go-openapi/errors v0.22.6/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= +github.com/go-openapi/strfmt v0.25.0 h1:7R0RX7mbKLa9EYCTHRcCuIPcaqlyQiWNPTXwClK0saQ= +github.com/go-openapi/strfmt v0.25.0/go.mod h1:nNXct7OzbwrMY9+5tLX4I21pzcmE6ccMGXl3jFdPfn8= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= -github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= -github.com/klauspost/connect-compress/v2 v2.1.0 h1:8fM8QrVeHT69e5VVSh4yjDaQASYIvOp2uMZq7nVLj2U= -github.com/klauspost/connect-compress/v2 v2.1.0/go.mod h1:Ayurh2wscMMx3AwdGGVL+ylSR5316WfApREDgsqHyH8= +github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= +github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/klauspost/connect-compress/v2 v2.1.1 h1:ycZNp4rWOZBodVE2Ls5AzK4aHkyK+GteEfzRZgKNs+c= +github.com/klauspost/connect-compress/v2 v2.1.1/go.mod h1:9oilsPHJMzGKkjafSBk9J7iVo4mO+dw0G0KSdVpnlVE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -51,12 +53,10 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= -github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/metal-stack/api v0.0.35-0.20251124101516-a3076941d0f8 h1:PGBiFwASqhtmI6dGzVo0IEVQiBTIsQ2eo3pMriPViNY= -github.com/metal-stack/api v0.0.35-0.20251124101516-a3076941d0f8/go.mod h1:EBwS/oZr5tIcnV6hM7iK4aBQrw4wlU7vF5p+O1p3YIU= -github.com/metal-stack/api v0.0.35 h1:XxxYKTscSeYJg/ftL519nY3FAZ01atPeyD7+Zz/amQQ= -github.com/metal-stack/api v0.0.35/go.mod h1:EBwS/oZr5tIcnV6hM7iK4aBQrw4wlU7vF5p+O1p3YIU= +github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= +github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/metal-stack/api v0.0.51 h1:n3KgwUa5rMefb3CgR/hl4xQD1NmGkzj2MyELMqqN4wA= +github.com/metal-stack/api v0.0.51/go.mod h1:SAtqZaD4JvOn+NVc6bTlKzL2EDoj/QrlHF72ZMw+Btk= github.com/metal-stack/metal-lib v0.23.5 h1:ozrkB3DNr3Cqn8nkBvmzc/KKpYqC1j1mv2OVOj8i7Ac= github.com/metal-stack/metal-lib v0.23.5/go.mod h1:7uyHIrE19dkLwCZyeh2jmd7IEq5pEpzrzUGLoMN1eqY= github.com/metal-stack/v v1.0.3 h1:Sh2oBlnxrCUD+mVpzfC8HiqL045YWkxs0gpTvkjppqs= @@ -79,8 +79,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= @@ -89,8 +89,8 @@ github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -102,21 +102,21 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= -go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.mongodb.org/mongo-driver v1.17.9 h1:IexDdCuuNJ3BHrELgBlyaH9p60JXAvdzWR128q+U5tU= +go.mongodb.org/mongo-driver v1.17.9/go.mod h1:LlOhpH5NUEfhxcAwG0UEkMqwYcc4JU18gtCdGudk/tQ= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= +golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -124,8 +124,8 @@ 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= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU= +k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= From c4aec50d8f1ad1e937f781779c64e92192c2145f Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Fri, 20 Feb 2026 14:47:01 +0100 Subject: [PATCH 2/4] go fix --- cmd/admin/v2/image.go | 2 +- cmd/admin/v2/token.go | 3 +-- cmd/api/v2/ip.go | 7 +++---- cmd/api/v2/project.go | 4 ++-- cmd/api/v2/token.go | 2 +- cmd/common_test.go | 1 - cmd/config/config.go | 2 +- cmd/login.go | 5 ++--- cmd/root.go | 2 +- cmd/tableprinters/health.go | 7 +------ cmd/tableprinters/ip.go | 1 - pkg/helpers/viper.go | 9 +++------ 12 files changed, 16 insertions(+), 29 deletions(-) diff --git a/cmd/admin/v2/image.go b/cmd/admin/v2/image.go index 4aa0fda..06cd556 100644 --- a/cmd/admin/v2/image.go +++ b/cmd/admin/v2/image.go @@ -146,7 +146,7 @@ func (c *image) Update(rq *adminv2.ImageServiceUpdateRequest) (*apiv2.Image, err req := &adminv2.ImageServiceUpdateRequest{ Id: viper.GetString("id"), - Url: pointer.Pointer(viper.GetString("url")), + Url: new(viper.GetString("url")), Description: pointer.PointerOrNil(viper.GetString("description")), ExpiresAt: expiresAt, Features: imageFeaturesFromString(viper.GetStringSlice("features")), diff --git a/cmd/admin/v2/token.go b/cmd/admin/v2/token.go index 67b015a..51fc70a 100644 --- a/cmd/admin/v2/token.go +++ b/cmd/admin/v2/token.go @@ -9,7 +9,6 @@ import ( "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" ) @@ -57,7 +56,7 @@ func (c *token) List() ([]*apiv2.Token, error) { req := &adminv2.TokenServiceListRequest{} if viper.IsSet("user") { - req.User = pointer.Pointer(viper.GetString("user")) + req.User = new(viper.GetString("user")) } resp, err := c.c.Client.Adminv2().Token().List(ctx, req) diff --git a/cmd/api/v2/ip.go b/cmd/api/v2/ip.go index 2274e94..3e372ac 100644 --- a/cmd/api/v2/ip.go +++ b/cmd/api/v2/ip.go @@ -9,7 +9,6 @@ import ( "github.com/metal-stack/cli/pkg/helpers" "github.com/metal-stack/metal-lib/pkg/genericcli" "github.com/metal-stack/metal-lib/pkg/genericcli/printers" - "github.com/metal-stack/metal-lib/pkg/pointer" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -70,11 +69,11 @@ func newIPCmd(c *config.Config) *cobra.Command { CreateRequestFromCLI: func() (*apiv2.IPServiceCreateRequest, error) { return &apiv2.IPServiceCreateRequest{ Project: c.GetProject(), - Name: pointer.Pointer(viper.GetString("name")), - Description: pointer.Pointer(viper.GetString("description")), + Name: new(viper.GetString("name")), + Description: new(viper.GetString("description")), Network: viper.GetString("network"), // Labels: viper.GetStringSlice("tags"), // FIXME implement - Type: pointer.Pointer(ipStaticToType(viper.GetBool("static"))), + Type: new(ipStaticToType(viper.GetBool("static"))), AddressFamily: addressFamilyToType(viper.GetString("addressfamily")), }, nil }, diff --git a/cmd/api/v2/project.go b/cmd/api/v2/project.go index 46fa3f4..601d8db 100644 --- a/cmd/api/v2/project.go +++ b/cmd/api/v2/project.go @@ -228,8 +228,8 @@ func (c *project) Convert(r *apiv2.Project) (string, *apiv2.ProjectServiceCreate Description: r.Description, }, &apiv2.ProjectServiceUpdateRequest{ Project: r.Uuid, - Name: pointer.Pointer(r.Name), - Description: pointer.Pointer(r.Description), + Name: new(r.Name), + Description: new(r.Description), }, nil } diff --git a/cmd/api/v2/token.go b/cmd/api/v2/token.go index 425a8f8..7bcae98 100644 --- a/cmd/api/v2/token.go +++ b/cmd/api/v2/token.go @@ -85,7 +85,7 @@ func newTokenCmd(c *config.Config) *cobra.Command { return nil, fmt.Errorf("unknown role: %s", roleString) } - adminRole = pointer.Pointer(apiv2.AdminRole(role)) + adminRole = new(apiv2.AdminRole(role)) } return &apiv2.TokenServiceCreateRequest{ diff --git a/cmd/common_test.go b/cmd/common_test.go index bd80e37..5befb1a 100644 --- a/cmd/common_test.go +++ b/cmd/common_test.go @@ -62,7 +62,6 @@ func (c *Test[R]) TestCmd(t *testing.T) { } for _, format := range outputFormats(c) { - format := format t.Run(fmt.Sprintf("%v", format.Args()), func(t *testing.T) { _, out, conf := c.newMockConfig(t) diff --git a/cmd/config/config.go b/cmd/config/config.go index 67006b9..c54a2c8 100644 --- a/cmd/config/config.go +++ b/cmd/config/config.go @@ -44,7 +44,7 @@ func (c *Config) NewRequestContext() (context.Context, context.CancelFunc) { timeout = pointer.Pointer(30 * time.Second) } if viper.IsSet("timeout") { - timeout = pointer.Pointer(viper.GetDuration("timeout")) + timeout = new(viper.GetDuration("timeout")) } return context.WithTimeout(context.Background(), *timeout) diff --git a/cmd/login.go b/cmd/login.go index 960a172..02b1565 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -13,7 +13,6 @@ import ( apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" "github.com/metal-stack/cli/cmd/config" "github.com/metal-stack/metal-lib/pkg/genericcli" - "github.com/metal-stack/metal-lib/pkg/pointer" "github.com/spf13/cobra" "github.com/spf13/viper" "google.golang.org/protobuf/types/known/durationpb" @@ -69,7 +68,7 @@ func (l *login) login() error { if viper.IsSet("context") { newCtx.Name = viper.GetString("context") } - newCtx.ApiURL = pointer.Pointer(l.c.GetApiURL()) + newCtx.ApiURL = new(l.c.GetApiURL()) ctxs.Contexts = append(ctxs.Contexts, &newCtx) ctx = &newCtx } @@ -131,7 +130,7 @@ func (l *login) login() error { tokenResp, err := mc.Apiv2().Token().Create(context.Background(), &apiv2.TokenServiceCreateRequest{ Description: "admin access issues by metal cli", Expires: durationpb.New(3 * time.Hour), - AdminRole: pointer.Pointer(apiv2.AdminRole((apiv2.AdminRole_value[viper.GetString("admin-role")]))), + AdminRole: new(apiv2.AdminRole((apiv2.AdminRole_value[viper.GetString("admin-role")]))), }) if err != nil { return fmt.Errorf("unable to issue admin token: %w", err) diff --git a/cmd/root.go b/cmd/root.go index 5692737..f561d97 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,6 +1,7 @@ package cmd import ( + "context" "log/slog" "os" @@ -16,7 +17,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/cobra/doc" "github.com/spf13/viper" - "golang.org/x/net/context" ) func Execute() { diff --git a/cmd/tableprinters/health.go b/cmd/tableprinters/health.go index a9b3378..af9f4e8 100644 --- a/cmd/tableprinters/health.go +++ b/cmd/tableprinters/health.go @@ -6,7 +6,6 @@ import ( "github.com/fatih/color" "github.com/metal-stack/api/go/enum" apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" - "github.com/metal-stack/metal-lib/pkg/pointer" ) func (t *TablePrinter) HealthTable(data []*apiv2.Health, wide bool) ([]string, [][]string, error) { @@ -31,13 +30,11 @@ func (t *TablePrinter) HealthTable(data []*apiv2.Health, wide bool) ([]string, [ ) for _, h := range data { - h := h for _, s := range h.Services { - s := s name, err := enum.GetStringValue(s.Name) if err != nil { - name = pointer.Pointer("service status unknown") + name = new("service status unknown") } message := "All systems operational" if s.Message != "" { @@ -53,7 +50,6 @@ func (t *TablePrinter) HealthTable(data []*apiv2.Health, wide bool) ([]string, [ var partitions []partitionStatus for id, p := range s.Partitions { - p := p partitions = append(partitions, partitionStatus{ ID: id, @@ -66,7 +62,6 @@ func (t *TablePrinter) HealthTable(data []*apiv2.Health, wide bool) ([]string, [ }) for i, status := range partitions { - status := status prefix := "├" if i == len(partitions)-1 { diff --git a/cmd/tableprinters/ip.go b/cmd/tableprinters/ip.go index fdefa24..d60a149 100644 --- a/cmd/tableprinters/ip.go +++ b/cmd/tableprinters/ip.go @@ -18,7 +18,6 @@ func (t *TablePrinter) IPTable(data []*apiv2.IP, wide bool) ([]string, [][]strin } for _, ip := range data { - ip := ip var t string diff --git a/pkg/helpers/viper.go b/pkg/helpers/viper.go index 72dc361..00ccd72 100644 --- a/pkg/helpers/viper.go +++ b/pkg/helpers/viper.go @@ -1,12 +1,9 @@ package helpers +import "slices" + import "github.com/spf13/viper" func IsAnyViperFlagSet(names ...string) bool { - for _, name := range names { - if viper.IsSet(name) { - return true - } - } - return false + return slices.ContainsFunc(names, viper.IsSet) } From d679fcdb4b85cd34b31f72a7e7aaf55c9729d131 Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Fri, 20 Feb 2026 16:01:23 +0100 Subject: [PATCH 3/4] api and admin commands --- cmd/admin/v2/commands.go | 3 +- cmd/admin/v2/partition.go | 137 ++++++++++++++++++++++++++ cmd/api/v2/commands.go | 9 +- cmd/api/v2/partition.go | 87 ++++++++++++++++ cmd/completion/partition.go | 19 ++++ cmd/completion/size.go | 19 ++++ cmd/sorters/partition.go | 30 ++++++ cmd/tableprinters/common.go | 10 ++ cmd/tableprinters/partition.go | 134 +++++++++++++++++++++++++ docs/metalctlv2.md | 1 + docs/metalctlv2_partition.md | 33 +++++++ docs/metalctlv2_partition_describe.md | 31 ++++++ docs/metalctlv2_partition_list.md | 32 ++++++ go.mod | 11 ++- go.sum | 18 ++-- 15 files changed, 556 insertions(+), 18 deletions(-) create mode 100644 cmd/admin/v2/partition.go create mode 100644 cmd/api/v2/partition.go create mode 100644 cmd/completion/partition.go create mode 100644 cmd/completion/size.go create mode 100644 cmd/sorters/partition.go create mode 100644 cmd/tableprinters/partition.go create mode 100644 docs/metalctlv2_partition.md create mode 100644 docs/metalctlv2_partition_describe.md create mode 100644 docs/metalctlv2_partition_list.md diff --git a/cmd/admin/v2/commands.go b/cmd/admin/v2/commands.go index 881a7be..13dfa4d 100644 --- a/cmd/admin/v2/commands.go +++ b/cmd/admin/v2/commands.go @@ -15,9 +15,10 @@ func AddCmds(cmd *cobra.Command, c *config.Config) { } adminCmd.AddCommand(newImageCmd(c)) + adminCmd.AddCommand(newPartitionCmd(c)) + adminCmd.AddCommand(newProjectCmd(c)) adminCmd.AddCommand(newTenantCmd(c)) adminCmd.AddCommand(newTokenCmd(c)) - adminCmd.AddCommand(newProjectCmd(c)) cmd.AddCommand(adminCmd) } diff --git a/cmd/admin/v2/partition.go b/cmd/admin/v2/partition.go new file mode 100644 index 0000000..f621feb --- /dev/null +++ b/cmd/admin/v2/partition.go @@ -0,0 +1,137 @@ +package v2 + +import ( + "fmt" + "strings" + + 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 partition struct { + c *config.Config +} + +func newPartitionCmd(c *config.Config) *cobra.Command { + w := &partition{ + c: c, + } + + gcli := genericcli.NewGenericCLI(w).WithFS(c.Fs) + + cmdsConfig := &genericcli.CmdsConfig[any, any, *apiv2.Partition]{ + BinaryName: config.BinaryName, + GenericCLI: gcli, + Singular: "partition", + Plural: "partitions", + Description: "manage partitions", + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + OnlyCmds: genericcli.OnlyCmds(genericcli.DescribeCmd, genericcli.ListCmd), + DescribeCmdMutateFn: func(cmd *cobra.Command) { + cmd.RunE = func(cmd *cobra.Command, args []string) error { + return gcli.DescribeAndPrint("", w.c.DescribePrinter) + } + }, + } + + capacityCmd := &cobra.Command{ + Use: "capacity", + Short: "show partition capacity", + RunE: func(cmd *cobra.Command, args []string) error { + return w.capacity() + }, + } + + capacityCmd.Flags().StringP("id", "", "", "filter on partition id.") + capacityCmd.Flags().StringP("size", "", "", "filter on size id.") + capacityCmd.Flags().StringP("project", "", "", "consider project-specific counts, e.g. size reservations.") + capacityCmd.Flags().StringSlice("sort-by", []string{}, fmt.Sprintf("order by (comma separated) column(s), sort direction can be changed by appending :asc or :desc behind the column identifier. possible values: %s", strings.Join(sorters.PartitionCapacitySorter().AvailableKeys(), "|"))) + genericcli.Must(capacityCmd.RegisterFlagCompletionFunc("id", c.Completion.PartitionListCompletion)) + genericcli.Must(capacityCmd.RegisterFlagCompletionFunc("project", c.Completion.ProjectListCompletion)) + genericcli.Must(capacityCmd.RegisterFlagCompletionFunc("size", c.Completion.SizeListCompletion)) + genericcli.Must(capacityCmd.RegisterFlagCompletionFunc("sort-by", cobra.FixedCompletions(sorters.PartitionCapacitySorter().AvailableKeys(), cobra.ShellCompDirectiveNoFileComp))) + + return genericcli.NewCmds(cmdsConfig, capacityCmd) +} + +func (c *partition) capacity() error { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &adminv2.PartitionServiceCapacityRequest{} + + if viper.IsSet("id") { + req.Id = new(viper.GetString("id")) + } + if viper.IsSet("size") { + req.Size = new(viper.GetString("size")) + } + if viper.IsSet("project") { + req.Project = new(viper.GetString("project")) + } + resp, err := c.c.Client.Adminv2().Partition().Capacity(ctx, req) + if err != nil { + return fmt.Errorf("failed to get partition capacity: %w", err) + } + + err = sorters.PartitionCapacitySorter().SortBy(resp.PartitionCapacity) + if err != nil { + return err + } + + return c.c.ListPrinter.Print(resp.PartitionCapacity) +} + +func (c *partition) Get(id string) (*apiv2.Partition, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &apiv2.PartitionServiceGetRequest{Id: id} + + resp, err := c.c.Client.Apiv2().Partition().Get(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get partition: %w", err) + } + + return resp.Partition, nil +} + +func (c *partition) List() ([]*apiv2.Partition, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &apiv2.PartitionServiceListRequest{Query: &apiv2.PartitionQuery{ + Id: pointer.PointerOrNil(viper.GetString("id")), + }} + + resp, err := c.c.Client.Apiv2().Partition().List(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get partitions: %w", err) + } + + return resp.Partitions, nil +} + +func (c *partition) Create(rq any) (*apiv2.Partition, error) { + panic("unimplemented") +} + +func (c *partition) Delete(id string) (*apiv2.Partition, error) { + panic("unimplemented") +} + +func (t *partition) Convert(r *apiv2.Partition) (string, any, any, error) { + panic("unimplemented") +} + +func (t *partition) Update(rq any) (*apiv2.Partition, error) { + panic("unimplemented") +} diff --git a/cmd/api/v2/commands.go b/cmd/api/v2/commands.go index be489cc..0476351 100644 --- a/cmd/api/v2/commands.go +++ b/cmd/api/v2/commands.go @@ -6,13 +6,14 @@ import ( ) func AddCmds(cmd *cobra.Command, c *config.Config) { - cmd.AddCommand(newVersionCmd(c)) cmd.AddCommand(newHealthCmd(c)) - cmd.AddCommand(newTokenCmd(c)) - cmd.AddCommand(newIPCmd(c)) cmd.AddCommand(newImageCmd(c)) + cmd.AddCommand(newIPCmd(c)) + cmd.AddCommand(newMethodsCmd(c)) + cmd.AddCommand(newPartitionCmd(c)) cmd.AddCommand(newProjectCmd(c)) cmd.AddCommand(newTenantCmd(c)) - cmd.AddCommand(newMethodsCmd(c)) + cmd.AddCommand(newTokenCmd(c)) cmd.AddCommand(newUserCmd(c)) + cmd.AddCommand(newVersionCmd(c)) } diff --git a/cmd/api/v2/partition.go b/cmd/api/v2/partition.go new file mode 100644 index 0000000..ead6d61 --- /dev/null +++ b/cmd/api/v2/partition.go @@ -0,0 +1,87 @@ +package v2 + +import ( + "fmt" + + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/metal-stack/cli/cmd/config" + "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 partition struct { + c *config.Config +} + +func newPartitionCmd(c *config.Config) *cobra.Command { + w := &partition{ + c: c, + } + + gcli := genericcli.NewGenericCLI(w).WithFS(c.Fs) + + cmdsConfig := &genericcli.CmdsConfig[any, any, *apiv2.Partition]{ + BinaryName: config.BinaryName, + GenericCLI: gcli, + Singular: "partition", + Plural: "partitions", + Description: "list and get partitions", + DescribePrinter: func() printers.Printer { return c.DescribePrinter }, + ListPrinter: func() printers.Printer { return c.ListPrinter }, + OnlyCmds: genericcli.OnlyCmds(genericcli.DescribeCmd, genericcli.ListCmd), + ListCmdMutateFn: func(cmd *cobra.Command) { + cmd.Flags().StringP("id", "", "", "image id to filter for") + }, + } + + return genericcli.NewCmds(cmdsConfig) +} + +func (c *partition) Get(id string) (*apiv2.Partition, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &apiv2.PartitionServiceGetRequest{Id: id} + + resp, err := c.c.Client.Apiv2().Partition().Get(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get partition: %w", err) + } + + return resp.Partition, nil +} + +func (c *partition) List() ([]*apiv2.Partition, error) { + ctx, cancel := c.c.NewRequestContext() + defer cancel() + + req := &apiv2.PartitionServiceListRequest{Query: &apiv2.PartitionQuery{ + Id: pointer.PointerOrNil(viper.GetString("id")), + }} + + resp, err := c.c.Client.Apiv2().Partition().List(ctx, req) + if err != nil { + return nil, fmt.Errorf("failed to get partitions: %w", err) + } + + return resp.Partitions, nil +} + +func (c *partition) Create(rq any) (*apiv2.Partition, error) { + panic("unimplemented") +} + +func (c *partition) Delete(id string) (*apiv2.Partition, error) { + panic("unimplemented") +} + +func (t *partition) Convert(r *apiv2.Partition) (string, any, any, error) { + panic("unimplemented") +} + +func (t *partition) Update(rq any) (*apiv2.Partition, error) { + panic("unimplemented") +} diff --git a/cmd/completion/partition.go b/cmd/completion/partition.go new file mode 100644 index 0000000..0e71613 --- /dev/null +++ b/cmd/completion/partition.go @@ -0,0 +1,19 @@ +package completion + +import ( + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/spf13/cobra" +) + +func (c *Completion) PartitionListCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + req := &apiv2.PartitionServiceListRequest{} + resp, err := c.Client.Apiv2().Partition().List(c.Ctx, req) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + var names []string + for _, p := range resp.Partitions { + names = append(names, p.Id+"\t"+p.Description) + } + return names, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/completion/size.go b/cmd/completion/size.go new file mode 100644 index 0000000..8ebd814 --- /dev/null +++ b/cmd/completion/size.go @@ -0,0 +1,19 @@ +package completion + +import ( + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/spf13/cobra" +) + +func (c *Completion) SizeListCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + req := &apiv2.SizeServiceListRequest{} + resp, err := c.Client.Apiv2().Size().List(c.Ctx, req) + if err != nil { + return nil, cobra.ShellCompDirectiveError + } + var names []string + for _, s := range resp.Sizes { + names = append(names, s.Id) + } + return names, cobra.ShellCompDirectiveNoFileComp +} diff --git a/cmd/sorters/partition.go b/cmd/sorters/partition.go new file mode 100644 index 0000000..c0f2c46 --- /dev/null +++ b/cmd/sorters/partition.go @@ -0,0 +1,30 @@ +package sorters + +import ( + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/metal-stack/metal-lib/pkg/multisort" +) + +func PartitionSorter() *multisort.Sorter[*apiv2.Partition] { + return multisort.New(multisort.FieldMap[*apiv2.Partition]{ + "id": func(a, b *apiv2.Partition, descending bool) multisort.CompareResult { + return multisort.Compare(a.Id, b.Id, descending) + }, + "description": func(a, b *apiv2.Partition, descending bool) multisort.CompareResult { + return multisort.Compare(a.Description, b.Description, descending) + }, + }, multisort.Keys{{ID: "id"}, {ID: "description"}}) +} + +func PartitionCapacitySorter() *multisort.Sorter[*adminv2.PartitionCapacity] { + return multisort.New(multisort.FieldMap[*adminv2.PartitionCapacity]{ + "id": func(a, b *adminv2.PartitionCapacity, descending bool) multisort.CompareResult { + return multisort.Compare(a.Partition, b.Partition, descending) + }, + "size": func(a, b *adminv2.PartitionCapacity, descending bool) multisort.CompareResult { + // FIXME implement + return multisort.Compare(a.Partition, b.Partition, descending) + }, + }, multisort.Keys{{ID: "id"}, {ID: "size"}}) +} diff --git a/cmd/tableprinters/common.go b/cmd/tableprinters/common.go index 19c30e7..bbb62e5 100644 --- a/cmd/tableprinters/common.go +++ b/cmd/tableprinters/common.go @@ -6,6 +6,7 @@ import ( "strings" "time" + 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/metal-lib/pkg/genericcli/printers" @@ -53,6 +54,15 @@ func (t *TablePrinter) ToHeaderAndRows(data any, wide bool) ([]string, [][]strin case []*apiv2.ProjectMember: return t.ProjectMemberTable(d, wide) + case *apiv2.Partition: + return t.PartitionTable(pointer.WrapInSlice(d), wide) + case []*apiv2.Partition: + return t.PartitionTable(d, wide) + case *adminv2.PartitionCapacity: + return t.PartitionCapacityTable(pointer.WrapInSlice(d), wide) + case []*adminv2.PartitionCapacity: + return t.PartitionCapacityTable(d, wide) + case *apiv2.Token: return t.TokenTable(pointer.WrapInSlice(d), wide) case []*apiv2.Token: diff --git a/cmd/tableprinters/partition.go b/cmd/tableprinters/partition.go new file mode 100644 index 0000000..0c99a83 --- /dev/null +++ b/cmd/tableprinters/partition.go @@ -0,0 +1,134 @@ +package tableprinters + +import ( + "fmt" + "sort" + "strings" + + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/metal-stack/metal-lib/pkg/genericcli" +) + +func (t *TablePrinter) PartitionTable(data []*apiv2.Partition, wide bool) ([]string, [][]string, error) { + var ( + header = []string{"ID", "Description"} + rows [][]string + ) + + if wide { + header = []string{"ID", "Description", "Labels"} + } + + for _, p := range data { + row := []string{p.Id, p.Description} + + if wide { + labels := genericcli.MapToLabels(p.Meta.Labels.Labels) + sort.Strings(labels) + row = append(row, strings.Join(labels, "\n")) + } + + rows = append(rows, row) + } + + return header, rows, nil +} + +func (t *TablePrinter) PartitionCapacityTable(data []*adminv2.PartitionCapacity, wide bool) ([]string, [][]string, error) { + var ( + header = []string{"Partition", "Size", "Allocated", "Free", "Unavailable", "Reservations", "|", "Total", "|", "Faulty"} + rows [][]string + + allocatedCount int64 + faultyCount int64 + freeCount int64 + otherCount int64 + phonedHomeCount int64 + reservationCount int64 + totalCount int64 + unavailableCount int64 + usedReservationCount int64 + waitingCount int64 + ) + + if wide { + header = append(header, "Phoned Home", "Waiting", "Other") + } + + for _, pc := range data { + for _, c := range pc.MachineSizeCapacities { + id := c.Size + var ( + allocated = fmt.Sprintf("%d", c.Allocated) + faulty = fmt.Sprintf("%d", c.Faulty) + free = fmt.Sprintf("%d", c.Free) + other = fmt.Sprintf("%d", c.Other) + phonedHome = fmt.Sprintf("%d", c.PhonedHome) + reservations = "0" + total = fmt.Sprintf("%d", c.Total) + unavailable = fmt.Sprintf("%d", c.Unavailable) + waiting = fmt.Sprintf("%d", c.Waiting) + ) + + if c.Reservations > 0 { + reservations = fmt.Sprintf("%d (%d/%d used)", c.Reservations-c.UsedReservations, c.UsedReservations, c.Reservations) + } + + allocatedCount += c.Allocated + faultyCount += c.Faulty + freeCount += c.Free + otherCount += c.Other + phonedHomeCount += c.PhonedHome + reservationCount += c.Reservations + totalCount += c.Total + unavailableCount += c.Unavailable + usedReservationCount += c.UsedReservations + waitingCount += c.Waiting + + row := []string{pc.Partition, id, allocated, free, unavailable, reservations, "|", total, "|", faulty} + if wide { + row = append(row, phonedHome, waiting, other) + } + + rows = append(rows, row) + } + } + + footerRow := ([]string{ + "Total", + "", + fmt.Sprintf("%d", allocatedCount), + fmt.Sprintf("%d", freeCount), + fmt.Sprintf("%d", unavailableCount), + fmt.Sprintf("%d", reservationCount-usedReservationCount), + "|", + fmt.Sprintf("%d", totalCount), + "|", + fmt.Sprintf("%d", faultyCount), + }) + + if wide { + footerRow = append(footerRow, []string{ + fmt.Sprintf("%d", phonedHomeCount), + fmt.Sprintf("%d", waitingCount), + fmt.Sprintf("%d", otherCount), + }...) + } + + // if t.markdown { + // // for markdown we already have enough dividers, remove them + // removeDivider := func(e string) bool { + // return e == "|" + // } + // header = slices.DeleteFunc(header, removeDivider) + // footerRow = slices.DeleteFunc(footerRow, removeDivider) + // for i, row := range rows { + // rows[i] = slices.DeleteFunc(row, removeDivider) + // } + // } + + rows = append(rows, footerRow) + + return header, rows, nil +} diff --git a/docs/metalctlv2.md b/docs/metalctlv2.md index 4671daf..b96c43f 100644 --- a/docs/metalctlv2.md +++ b/docs/metalctlv2.md @@ -27,6 +27,7 @@ cli for managing entities in metal-stack * [metalctlv2 login](metalctlv2_login.md) - login * [metalctlv2 logout](metalctlv2_logout.md) - logout * [metalctlv2 markdown](metalctlv2_markdown.md) - create markdown documentation +* [metalctlv2 partition](metalctlv2_partition.md) - manage partition entities * [metalctlv2 project](metalctlv2_project.md) - manage project entities * [metalctlv2 tenant](metalctlv2_tenant.md) - manage tenant entities * [metalctlv2 token](metalctlv2_token.md) - manage token entities diff --git a/docs/metalctlv2_partition.md b/docs/metalctlv2_partition.md new file mode 100644 index 0000000..581fe57 --- /dev/null +++ b/docs/metalctlv2_partition.md @@ -0,0 +1,33 @@ +## metalctlv2 partition + +manage partition entities + +### Synopsis + +list and get partitions + +### Options + +``` + -h, --help help for partition +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2](metalctlv2.md) - cli for managing entities in metal-stack +* [metalctlv2 partition describe](metalctlv2_partition_describe.md) - describes the partition +* [metalctlv2 partition list](metalctlv2_partition_list.md) - list all partitions + diff --git a/docs/metalctlv2_partition_describe.md b/docs/metalctlv2_partition_describe.md new file mode 100644 index 0000000..4dc9186 --- /dev/null +++ b/docs/metalctlv2_partition_describe.md @@ -0,0 +1,31 @@ +## metalctlv2 partition describe + +describes the partition + +``` +metalctlv2 partition 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 (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 partition](metalctlv2_partition.md) - manage partition entities + diff --git a/docs/metalctlv2_partition_list.md b/docs/metalctlv2_partition_list.md new file mode 100644 index 0000000..8967f09 --- /dev/null +++ b/docs/metalctlv2_partition_list.md @@ -0,0 +1,32 @@ +## metalctlv2 partition list + +list all partitions + +``` +metalctlv2 partition list [flags] +``` + +### Options + +``` + -h, --help help for list + --id string image id to filter for +``` + +### Options inherited from parent commands + +``` + --api-token string the token used for api requests + --api-url string the url to the metal-stack.io api (default "https://api.metal-stack.io") + -c, --config string alternative config file path, (default is ~/.metal-stack/config.yaml) + --debug debug output + --force-color force colored output even without tty + -o, --output-format string output format (table|wide|markdown|json|yaml|template|jsonraw|yamlraw), wide is a table with more columns, jsonraw and yamlraw do not translate proto enums into string types but leave the original int32 values intact. (default "table") + --template string output template for template output-format, go template format. For property names inspect the output of -o json or -o yaml for reference. + --timeout duration request timeout used for api requests +``` + +### SEE ALSO + +* [metalctlv2 partition](metalctlv2_partition.md) - manage partition entities + diff --git a/go.mod b/go.mod index 2ef7933..c82f240 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 - golang.org/x/net v0.50.0 google.golang.org/protobuf v1.36.11 sigs.k8s.io/yaml v1.6.0 ) @@ -22,6 +21,8 @@ require ( require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 // indirect connectrpc.com/connect v1.19.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clipperhouse/displaywidth v0.6.2 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect @@ -43,12 +44,11 @@ require ( github.com/minio/minlz v1.0.1 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect - github.com/olekukonko/errors v1.1.0 // indirect - github.com/olekukonko/ll v0.1.2 // indirect - github.com/olekukonko/tablewriter v1.1.0 // indirect + github.com/olekukonko/errors v1.2.0 // indirect + github.com/olekukonko/ll v0.1.6 // indirect + github.com/olekukonko/tablewriter v1.1.3 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -57,6 +57,7 @@ require ( go.mongodb.org/mongo-driver v1.17.9 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/net v0.50.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect diff --git a/go.sum b/go.sum index 075d38a..c106f0c 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo= +github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= @@ -67,18 +71,16 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= -github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= -github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= -github.com/olekukonko/ll v0.1.2 h1:lkg/k/9mlsy0SxO5aC+WEpbdT5K83ddnNhAepz7TQc0= -github.com/olekukonko/ll v0.1.2/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew= -github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY= -github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo= +github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= +github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA= +github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88= +github.com/olekukonko/tablewriter v1.1.3 h1:VSHhghXxrP0JHl+0NnKid7WoEmd9/urKRJLysb70nnA= +github.com/olekukonko/tablewriter v1.1.3/go.mod h1:9VU0knjhmMkXjnMKrZ3+L2JhhtsQ/L38BbL3CRNE8tM= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= From ba3d14ffa6a4ca6632707c0cd39182671b91f3d7 Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Tue, 24 Feb 2026 08:42:29 +0100 Subject: [PATCH 4/4] Let AI create a test for admin/partition.go --- cmd/admin/v2/partition_test.go | 250 +++++++++++++++++++++++ cmd/root.go | 4 +- cmd/common_test.go => pkg/test/common.go | 9 +- 3 files changed, 257 insertions(+), 6 deletions(-) create mode 100644 cmd/admin/v2/partition_test.go rename cmd/common_test.go => pkg/test/common.go (98%) diff --git a/cmd/admin/v2/partition_test.go b/cmd/admin/v2/partition_test.go new file mode 100644 index 0000000..d20e695 --- /dev/null +++ b/cmd/admin/v2/partition_test.go @@ -0,0 +1,250 @@ +package v2_test + +import ( + "testing" + + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + apitests "github.com/metal-stack/api/go/tests" + "github.com/metal-stack/cli/pkg/test" + "github.com/stretchr/testify/mock" +) + +// Generated with AI + +var ( + testPartition1 = &apiv2.Partition{ + Id: "1", + Description: "partition 1", + MgmtServiceAddresses: []string{ + "192.168.1.1:1234", + }, + BootConfiguration: &apiv2.PartitionBootConfiguration{ + Commandline: "commandline", + ImageUrl: "imageurl", + KernelUrl: "kernelurl", + }, + Meta: &apiv2.Meta{ + Labels: &apiv2.Labels{ + Labels: map[string]string{ + "a": "b", + }, + }, + }, + } + testPartition2 = &apiv2.Partition{ + Id: "2", + Description: "partition 2", + MgmtServiceAddresses: []string{ + "192.168.1.2:1234", + }, + BootConfiguration: &apiv2.PartitionBootConfiguration{ + Commandline: "commandline", + ImageUrl: "imageurl", + KernelUrl: "kernelurl", + }, + Meta: &apiv2.Meta{ + Labels: &apiv2.Labels{ + Labels: nil, + }, + }, + } +) + +func Test_AdminPartitionCmd_List(t *testing.T) { + tests := []*test.Test[[]*apiv2.Partition]{ + { + Name: "list", + Cmd: func(want []*apiv2.Partition) []string { + return []string{"admin", "partition", "list"} + }, + ClientMocks: &apitests.ClientMockFns{ + Apiv2Mocks: &apitests.Apiv2MockFns{ + Partition: func(m *mock.Mock) { + m.On("List", mock.Anything, mock.Anything).Return(&apiv2.PartitionServiceListResponse{ + Partitions: []*apiv2.Partition{ + testPartition1, + testPartition2, + }, + }, nil) + }, + }, + }, + Want: []*apiv2.Partition{ + testPartition1, + testPartition2, + }, + WantTable: new(` +ID DESCRIPTION +1 partition 1 +2 partition 2 +`), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_AdminPartitionCmd_Describe(t *testing.T) { + tests := []*test.Test[*apiv2.Partition]{ + { + Name: "describe", + Cmd: func(want *apiv2.Partition) []string { + return []string{"admin", "partition", "describe", want.Id} + }, + ClientMocks: &apitests.ClientMockFns{ + Apiv2Mocks: &apitests.Apiv2MockFns{ + Partition: func(m *mock.Mock) { + m.On("Get", mock.Anything, mock.Anything).Return(&apiv2.PartitionServiceGetResponse{ + Partition: testPartition1, + }, nil) + }, + }, + }, + Want: testPartition1, + WantTable: new(` +ID DESCRIPTION +1 partition 1 +`), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_AdminPartitionCmd_Capacity(t *testing.T) { + tests := []*test.Test[[]*adminv2.PartitionCapacity]{ + { + Name: "capacity", + Cmd: func(want []*adminv2.PartitionCapacity) []string { + return []string{"admin", "partition", "capacity"} + }, + ClientMocks: &apitests.ClientMockFns{ + Adminv2Mocks: &apitests.Adminv2MockFns{ + Partition: func(m *mock.Mock) { + m.On("Capacity", mock.Anything, mock.Anything).Return(&adminv2.PartitionServiceCapacityResponse{ + PartitionCapacity: []*adminv2.PartitionCapacity{ + { + Partition: "partition-1", + MachineSizeCapacities: []*adminv2.MachineSizeCapacity{ + { + Size: "size-1", + Free: 3, + Allocated: 1, + Total: 5, + Faulty: 2, + Reservations: 3, + UsedReservations: 1, + }, + }, + }, + }, + }, nil) + }, + }, + }, + Want: []*adminv2.PartitionCapacity{ + { + Partition: "partition-1", + MachineSizeCapacities: []*adminv2.MachineSizeCapacity{ + { + Size: "size-1", + Free: 3, + Allocated: 1, + Total: 5, + Faulty: 2, + Reservations: 3, + UsedReservations: 1, + }, + }, + }, + }, + WantTable: new(` +PARTITION SIZE ALLOCATED FREE UNAVAILABLE RESERVATIONS | TOTAL | FAULTY +partition-1 size-1 1 3 0 2 (1/3 used) | 5 | 2 +Total 1 3 0 2 | 5 | 2 +`), + }, + { + Name: "capacity with filters", + Cmd: func(want []*adminv2.PartitionCapacity) []string { + return []string{"admin", "partition", "capacity", "--id", "partition-1", "--size", "size-1", "--project", "project-123", "--sort-by", "id"} + }, + ClientMocks: &apitests.ClientMockFns{ + Adminv2Mocks: &apitests.Adminv2MockFns{ + Partition: func(m *mock.Mock) { + m.On("Capacity", mock.Anything, mock.Anything).Return(&adminv2.PartitionServiceCapacityResponse{ + PartitionCapacity: []*adminv2.PartitionCapacity{ + { + Partition: "partition-1", + MachineSizeCapacities: []*adminv2.MachineSizeCapacity{ + { + Size: "size-1", + Free: 3, + Allocated: 1, + Total: 5, + Faulty: 2, + Reservations: 3, + UsedReservations: 1, + }, + }, + }, + }, + }, nil) + }, + }, + }, + Want: []*adminv2.PartitionCapacity{ + { + Partition: "partition-1", + MachineSizeCapacities: []*adminv2.MachineSizeCapacity{ + { + Size: "size-1", + Free: 3, + Allocated: 1, + Total: 5, + Faulty: 2, + Reservations: 3, + UsedReservations: 1, + }, + }, + }, + }, + WantTable: new(` +PARTITION SIZE ALLOCATED FREE UNAVAILABLE RESERVATIONS | TOTAL | FAULTY +partition-1 size-1 1 3 0 2 (1/3 used) | 5 | 2 +Total 1 3 0 2 | 5 | 2 +`), + }, + } + for _, tt := range tests { + tt.TestCmd(t) + } +} + +func Test_AdminPartitionCmd_ExhaustiveArgs(t *testing.T) { + tests := []struct { + name string + args []string + }{ + { + name: "list", + args: []string{"admin", "partition", "list"}, + }, + { + name: "describe", + args: []string{"admin", "partition", "describe", "1"}, + }, + { + name: "capacity", + args: []string{"admin", "partition", "capacity", "--id", "partition-1", "--size", "size-1", "--project", "project-123", "--sort-by", "id"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + test.AssertExhaustiveArgs(t, tt.args) + }) + } +} diff --git a/cmd/root.go b/cmd/root.go index f561d97..fc27a8f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -28,7 +28,7 @@ func Execute() { Completion: &completion.Completion{}, } - cmd := newRootCmd(cfg) + cmd := NewRootCmd(cfg) err := cmd.Execute() if err != nil { @@ -40,7 +40,7 @@ func Execute() { } } -func newRootCmd(c *config.Config) *cobra.Command { +func NewRootCmd(c *config.Config) *cobra.Command { rootCmd := &cobra.Command{ Use: config.BinaryName, Aliases: []string{"m"}, diff --git a/cmd/common_test.go b/pkg/test/common.go similarity index 98% rename from cmd/common_test.go rename to pkg/test/common.go index 5befb1a..50b3879 100644 --- a/cmd/common_test.go +++ b/pkg/test/common.go @@ -1,4 +1,4 @@ -package cmd +package test import ( "bytes" @@ -14,6 +14,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" apitests "github.com/metal-stack/api/go/tests" + "github.com/metal-stack/cli/cmd" "github.com/metal-stack/cli/cmd/completion" "github.com/metal-stack/cli/cmd/config" "github.com/metal-stack/metal-lib/pkg/pointer" @@ -52,7 +53,7 @@ func (c *Test[R]) TestCmd(t *testing.T) { if c.WantErr != nil { _, _, conf := c.newMockConfig(t) - cmd := newRootCmd(conf) + cmd := cmd.NewRootCmd(conf) os.Args = append([]string{config.BinaryName}, c.Cmd(c.Want)...) err := cmd.Execute() @@ -65,7 +66,7 @@ func (c *Test[R]) TestCmd(t *testing.T) { t.Run(fmt.Sprintf("%v", format.Args()), func(t *testing.T) { _, out, conf := c.newMockConfig(t) - cmd := newRootCmd(conf) + cmd := cmd.NewRootCmd(conf) os.Args = append([]string{config.BinaryName}, c.Cmd(c.Want)...) os.Args = append(os.Args, format.Args()...) @@ -119,7 +120,7 @@ func AssertExhaustiveArgs(t *testing.T, args []string, exclude ...string) { return fmt.Errorf("not exhaustive: does not contain %q", prefix) } - root := newRootCmd(&config.Config{}) + root := cmd.NewRootCmd(&config.Config{}) cmd, args, err := root.Find(args) require.NoError(t, err)