diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index e3f288c..03dd2cd 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -22,17 +22,17 @@ jobs: steps: - name: Log in to the container registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKER_REGISTRY_USER }} password: ${{ secrets.DOCKER_REGISTRY_TOKEN }} - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup Go - uses: actions/setup-go@v6 + uses: actions/setup-go@v7 with: go-version-file: 'go.mod' cache: false @@ -53,10 +53,10 @@ jobs: make all - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Push image - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . push: true diff --git a/Dockerfile b/Dockerfile index 4b4a05b..36718c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM scratch +FROM gcr.io/distroless/static-debian13:nonroot COPY bin/metal-metrics-exporter /metal-metrics-exporter USER 999 ENTRYPOINT ["/metal-metrics-exporter"] diff --git a/api-calls.md b/api-calls.md new file mode 100644 index 0000000..d5ddc46 --- /dev/null +++ b/api-calls.md @@ -0,0 +1,13 @@ +# Required token permissions + +| subject | methods | +|:-------:|------------------------------------------------| +| | /metalstack.admin.v2.ImageService/Usage | +| | /metalstack.admin.v2.MachineService/Issues | +| | /metalstack.admin.v2.MachineService/List | +| | /metalstack.admin.v2.MachineService/ListBMC | +| | /metalstack.admin.v2.NetworkService/List | +| | /metalstack.admin.v2.PartitionService/Capacity | +| | /metalstack.admin.v2.ProjectService/List | +| | /metalstack.admin.v2.SwitchService/List | +| | /metalstack.infra.v2.ComponentService/Ping | diff --git a/deploy/metal-metrics-exporter-deployment.yaml b/deploy/metal-metrics-exporter-deployment.yaml index 8311370..e09963e 100644 --- a/deploy/metal-metrics-exporter-deployment.yaml +++ b/deploy/metal-metrics-exporter-deployment.yaml @@ -31,16 +31,11 @@ spec: secretKeyRef: name: metal-metrics-exporter-config key: url - - name: METAL_API_HMAC + - name: METAL_API_TOKEN_FILE valueFrom: secretKeyRef: name: metal-metrics-exporter-config - key: hmac - - name: METAL_API_AUTH_TYPE - valueFrom: - secretKeyRef: - key: authtype - name: metal-metrics-exporter-config + key: token securityContext: runAsUser: 999 allowPrivilegeEscalation: false diff --git a/deploy/metal-metrics-exporter-secret.yaml b/deploy/metal-metrics-exporter-secret.yaml index 48c1958..b64f504 100644 --- a/deploy/metal-metrics-exporter-secret.yaml +++ b/deploy/metal-metrics-exporter-secret.yaml @@ -5,5 +5,4 @@ metadata: type: Opaque stringData: url: "https://api.example.com/metal" - hmac: "XXXX" - authtype: "Metal-View" + token: "XXXX" diff --git a/deploy/metal-token-refresher.yaml b/deploy/metal-token-refresher.yaml new file mode 100644 index 0000000..6c9b205 --- /dev/null +++ b/deploy/metal-token-refresher.yaml @@ -0,0 +1,58 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: metrics-exporter-token-refresher +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: metrics-exporter-token-refresher +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "update", "patch"] +- apiGroups: [""] + resources: ["secrets"] + resourceNames: ["metal-metrics-exporter-config"] + verbs: ["get", "update", "patch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: metrics-exporter-token-refresher +subjects: +- kind: ServiceAccount + name: metrics-exporter-token-refresher +roleRef: + kind: Role + name: metrics-exporter-token-refresher + apiGroup: rbac.authorization.k8s.io + +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: metrics-exporter-token-refresher +spec: + schedule: "@hourly" + jobTemplate: + metadata: + name: metrics-exporter-token-refresher + spec: + template: + spec: + serviceAccountName: metrics-exporter-token-refresher + restartPolicy: OnFailure + containers: + - name: token-refresher + image: ghcr.io/metal-stack/metal-token-refresher + env: + - name: METAL_APISERVER_URL + value: http://metal-apiserver:8080 + - name: TOKEN_SECRET_NAMESPACE + value: "metal-control-plane" + - name: TOKEN_SECRET_NAME + value: "metal-metrics-exporter-config" + - name: TOKEN_SECRET_KEY + value: "token" + diff --git a/go.mod b/go.mod index 6a210cb..438bc99 100644 --- a/go.mod +++ b/go.mod @@ -1,71 +1,29 @@ module github.com/metal-stack/metal-metrics-exporter -go 1.26 +go 1.26.5 require ( - github.com/metal-stack/metal-go v0.43.0 - github.com/metal-stack/metal-lib v0.23.5 - github.com/prometheus/client_golang v1.23.2 - golang.org/x/sync v0.19.0 + github.com/metal-stack/api v0.5.1 + github.com/metal-stack/metal-lib v0.26.3 + github.com/prometheus/client_golang v1.24.1 + golang.org/x/sync v0.22.0 + google.golang.org/protobuf v1.36.12 ) require ( + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260709200747-435963d16310.1 // indirect + connectrpc.com/connect v1.20.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/coreos/go-oidc/v3 v3.17.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.3 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.24.2 // indirect - github.com/go-openapi/errors v0.22.6 // indirect - github.com/go-openapi/jsonpointer v0.22.4 // indirect - github.com/go-openapi/jsonreference v0.21.4 // indirect - github.com/go-openapi/loads v0.23.2 // indirect - github.com/go-openapi/runtime v0.29.2 // indirect - github.com/go-openapi/spec v0.22.3 // indirect - github.com/go-openapi/strfmt v0.25.0 // indirect - github.com/go-openapi/swag v0.25.4 // indirect - github.com/go-openapi/swag/cmdutils v0.25.4 // indirect - github.com/go-openapi/swag/conv v0.25.4 // indirect - github.com/go-openapi/swag/fileutils v0.25.4 // indirect - github.com/go-openapi/swag/jsonname v0.25.4 // indirect - github.com/go-openapi/swag/jsonutils v0.25.4 // indirect - github.com/go-openapi/swag/loading v0.25.4 // indirect - github.com/go-openapi/swag/mangling v0.25.4 // indirect - github.com/go-openapi/swag/netutils v0.25.4 // indirect - github.com/go-openapi/swag/stringutils v0.25.4 // indirect - github.com/go-openapi/swag/typeutils v0.25.4 // indirect - github.com/go-openapi/swag/yamlutils v0.25.4 // indirect - github.com/go-openapi/validate v0.25.1 // indirect - github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/gorilla/mux v1.8.1 // indirect - github.com/lestrrat-go/blackmagic v1.0.4 // indirect - github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc/v3 v3.0.4 // indirect - github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect - github.com/lestrrat-go/option/v2 v2.0.0 // indirect - github.com/metal-stack/security v0.9.5 // indirect + github.com/klauspost/compress v1.19.2 // indirect + github.com/klauspost/connect-compress/v2 v2.1.1 // indirect + github.com/minio/minlz v1.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/oklog/ulid v1.3.1 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.19.2 // indirect - github.com/segmentio/asm v1.2.1 // indirect - go.mongodb.org/mongo-driver v1.17.9 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel v1.40.0 // indirect - go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.40.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.48.0 // indirect - golang.org/x/net v0.50.0 // indirect - golang.org/x/oauth2 v0.35.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/text v0.34.0 // indirect - google.golang.org/protobuf v1.36.11 // indirect + github.com/prometheus/common v0.70.1 // indirect + github.com/prometheus/procfs v0.21.1 // indirect + golang.org/x/sys v0.47.0 // indirect ) diff --git a/go.sum b/go.sum index 36ff7e5..525b46c 100644 --- a/go.sum +++ b/go.sum @@ -1,175 +1,54 @@ +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260709200747-435963d16310.1 h1:6nlcxMOui23ZRVAfJM451duu79P1npA5JRdZqMilrrQ= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260709200747-435963d16310.1/go.mod h1:TCt1lluMFnctISJXvkIQ4x3ABrPuUKCWKyjKdkJNBpw= +connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= +connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= -github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= -github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.24.2 h1:6p7WXEuKy1llDgOH8FooVeO+Uq2za9qoAOq4ZN08B50= -github.com/go-openapi/analysis v0.24.2/go.mod h1:x27OOHKANE0lutg2ml4kzYLoHGMKgRm1Cj2ijVOjJuE= -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/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= -github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= -github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= -github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= -github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4= -github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY= -github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0= -github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0= -github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= -github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= -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/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= -github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= -github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= -github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= -github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= -github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= -github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= -github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= -github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= -github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= -github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= -github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= -github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= -github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= -github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= -github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= -github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= -github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= -github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= -github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= -github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= -github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= -github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= -github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= -github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= -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-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw= -github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc= -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-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 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/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= -github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -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= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= +github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/connect-compress/v2 v2.1.1 h1:ycZNp4rWOZBodVE2Ls5AzK4aHkyK+GteEfzRZgKNs+c= +github.com/klauspost/connect-compress/v2 v2.1.1/go.mod h1:9oilsPHJMzGKkjafSBk9J7iVo4mO+dw0G0KSdVpnlVE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= -github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= -github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38= -github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo= -github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY= -github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU= -github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= -github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc/v3 v3.0.4 h1:pXyH2ppK8GYYggygxJ3TvxpCZnbEUWc9qSwRTTApaLA= -github.com/lestrrat-go/httprc/v3 v3.0.4/go.mod h1:mSMtkZW92Z98M5YoNNztbRGxbXHql7tSitCvaxvo9l0= -github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0VdSk= -github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU= -github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= -github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= -github.com/metal-stack/metal-go v0.43.0 h1:uODD0YCwnAYzyvFxWNakZrymBoMz1FAvP5hkhsR83VQ= -github.com/metal-stack/metal-go v0.43.0/go.mod h1:GSfXrAj55LGsUSMHWGDsmq5n056NG0yb1JM8bgfvKOw= -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/security v0.9.5 h1:dWJP1hJxvQhdyyrmKqYxsi5sZ5Wbxa66OZBiArT2z0k= -github.com/metal-stack/security v0.9.5/go.mod h1:2++bUdpPbx4d7faiEUiowelT4E65U79Kcw3UOHbuw4k= +github.com/metal-stack/api v0.5.1 h1:cA4i56RYXwcda/EkEWb2MG/YGQAEqJ4RvwxS7RtdUaA= +github.com/metal-stack/api v0.5.1/go.mod h1:QbDG4YRFIkDf1z6cGmeu0m/791IAMyk8/9U/lsrxH8E= +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/minio/minlz v1.2.0 h1:6IOBuiHg04QxvbFfgFLT/9sMaO/UhL7S+ApW1mK8q5A= +github.com/minio/minlz v1.2.0/go.mod h1:Ls9H7nlkASeCcdl5thjVD5Eraj6z+zGa7xtq57jIKD4= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= +github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= -github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= -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/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= -github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= +github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= +github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= +github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= -github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -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.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -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/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -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/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= -golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -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= -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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= +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= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index dff26c8..fd9cbc8 100644 --- a/main.go +++ b/main.go @@ -1,12 +1,14 @@ package main import ( + "context" "log/slog" "net/http" "os" "time" - metalgo "github.com/metal-stack/metal-go" + apiv2client "github.com/metal-stack/api/go/client" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" "github.com/metal-stack/metal-metrics-exporter/pkg/collector" "github.com/prometheus/client_golang/prometheus" @@ -25,18 +27,25 @@ func main() { log = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) url = os.Getenv("METAL_API_URL") - hmac = os.Getenv("METAL_API_HMAC") - authType = envOrDefault("METAL_API_AUTH_TYPE", "Metal-Admin") // backward compatibility, use "Metal-View" for read-only access - fetchIntervalEnv = envOrDefault("FETCH_INTERVAL", "90s") // time to sleep after every metrics fetch - updateTimeoutEnv = envOrDefault("UPDATE_TIMEOUT", "60s") // maximum time for metal-api to respond to all our requests until context gets cancelled + tokenFile = os.Getenv("METAL_API_TOKEN_FILE") + fetchIntervalEnv = envOrDefault("FETCH_INTERVAL", "90s") // time to sleep after every metrics fetch + updateTimeoutEnv = envOrDefault("UPDATE_TIMEOUT", "60s") // maximum time for metal-api to respond to all our requests until context gets cancelled err error ) - client, err := metalgo.NewDriver(url, "", hmac, metalgo.AuthType(authType)) + client, err := apiv2client.New(&apiv2client.DialConfig{ + BaseURL: url, + TokenFile: tokenFile, + TokenFileRereadDuration: 5 * time.Minute, + Log: log, + }) if err != nil { log.Error("error creating client", "error", err) os.Exit(1) } + client.Ping(context.Background(), &apiv2client.PingConfig{ + ComponentType: apiv2.ComponentType_COMPONENT_TYPE_METAL_METRICS_EXPORTER, + }) fetchInterval, err := time.ParseDuration(fetchIntervalEnv) if err != nil { diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index a04a141..0898242 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -4,27 +4,25 @@ import ( "context" "fmt" "slices" + "strconv" "strings" "sync" "time" - metalgo "github.com/metal-stack/metal-go" - "github.com/metal-stack/metal-go/api/client/image" - "github.com/metal-stack/metal-go/api/client/machine" - "github.com/metal-stack/metal-go/api/client/network" - "github.com/metal-stack/metal-go/api/client/partition" - "github.com/metal-stack/metal-go/api/client/project" - "github.com/metal-stack/metal-go/api/client/switch_operations" - "github.com/metal-stack/metal-go/api/models" + apiv2client "github.com/metal-stack/api/go/client" + "github.com/metal-stack/api/go/enum" + adminv2 "github.com/metal-stack/api/go/metalstack/admin/v2" + apiv2 "github.com/metal-stack/api/go/metalstack/api/v2" + "github.com/metal-stack/api/go/tag" "github.com/metal-stack/metal-lib/pkg/pointer" + "google.golang.org/protobuf/types/known/durationpb" - metaltag "github.com/metal-stack/metal-lib/pkg/tag" "github.com/prometheus/client_golang/prometheus" "golang.org/x/sync/errgroup" ) type collector struct { - client metalgo.Client + client apiv2client.Client updateTimeout time.Duration mu sync.Mutex @@ -272,7 +270,7 @@ var ( ) ) -func New(client metalgo.Client, updateTimeout time.Duration) *collector { +func New(client apiv2client.Client, updateTimeout time.Duration) *collector { return &collector{ client: client, updateTimeout: updateTimeout, @@ -336,65 +334,76 @@ func (c *collector) storeGaugeTimestamp(ts time.Time, desc *prometheus.Desc, val } func (c *collector) networkMetrics(ctx context.Context) error { - resp, err := c.client.Network().ListNetworks(network.NewListNetworksParams().WithContext(ctx), nil) + resp, err := c.client.Adminv2().Network().List(ctx, &adminv2.NetworkServiceListRequest{}) if err != nil { return fmt.Errorf("error retrieving networks: %w", err) } - for _, nw := range resp.Payload { - if nw.Privatesuper == nil || nw.Nat == nil || nw.Underlay == nil || nw.ID == nil { - continue - } - + for _, nw := range resp.Networks { var ( - nwID = pointer.SafeDeref(nw.ID) - privateSuper = fmt.Sprintf("%t", *nw.Privatesuper) - nat = fmt.Sprintf("%t", *nw.Nat) - underlay = fmt.Sprintf("%t", *nw.Underlay) + nwID = nw.Id + nat = nw.NatType == apiv2.NATType_NAT_TYPE_IPV4_MASQUERADE + underlay = nw.Type == apiv2.NetworkType_NETWORK_TYPE_UNDERLAY prefixes = strings.Join(nw.Prefixes, ",") - destPrefixes = strings.Join(nw.Destinationprefixes, ",") - vrf = fmt.Sprintf("%d", nw.Vrf) - clusterId = "" + destPrefixes = strings.Join(nw.DestinationPrefixes, ",") + vrf = "" + + isSuperNetwork bool + clusterId = "" ) + if nw.Vrf != nil { + vrf = fmt.Sprintf("%d", *nw.Vrf) + } + + if nw.Meta != nil && nw.Meta.Labels != nil && nw.Meta.Labels.Labels != nil { + if id, ok := nw.Meta.Labels.Labels[tag.ClusterID]; ok { + clusterId = id + } + } - if id, ok := nw.Labels[metaltag.ClusterID]; ok { - clusterId = id + if nw.Type == apiv2.NetworkType_NETWORK_TYPE_SUPER || nw.Type == apiv2.NetworkType_NETWORK_TYPE_SUPER_NAMESPACED { + isSuperNetwork = true } - c.storeGauge(metalNetworkInfo, 1.0, nwID, nw.Name, nw.Projectid, nw.Description, nw.Partitionid, vrf, prefixes, destPrefixes, nw.Parentnetworkid, privateSuper, nat, underlay, clusterId) + c.storeGauge(metalNetworkInfo, 1.0, nwID, + pointer.SafeDeref(nw.Name), + pointer.SafeDeref(nw.Project), + pointer.SafeDeref(nw.Description), + pointer.SafeDeref(nw.Partition), + vrf, + prefixes, + destPrefixes, + pointer.SafeDeref(nw.ParentNetwork), + strconv.FormatBool(isSuperNetwork), + strconv.FormatBool(nat), + strconv.FormatBool(underlay), + clusterId, + ) - if nw.Usage == nil { + if nw.Consumption == nil || nw.Consumption.Ipv4 == nil { continue } - c.storeGauge(metalNetworkUsedIPs, float64(pointer.SafeDeref(nw.Usage.UsedIps)), nwID) - c.storeGauge(metalNetworkAvailableIps, float64(pointer.SafeDeref(nw.Usage.AvailableIps)), nwID) - c.storeGauge(metalNetworkUsedPrefixes, float64(pointer.SafeDeref(nw.Usage.UsedPrefixes)), nwID) - c.storeGauge(metalNetworkAvailablePrefixes, float64(pointer.SafeDeref(nw.Usage.AvailablePrefixes)), nwID) + c.storeGauge(metalNetworkUsedIPs, float64(nw.Consumption.Ipv4.UsedIps), nwID) + c.storeGauge(metalNetworkAvailableIps, float64(nw.Consumption.Ipv4.AvailableIps), nwID) + c.storeGauge(metalNetworkUsedPrefixes, float64(nw.Consumption.Ipv4.UsedPrefixes), nwID) + c.storeGauge(metalNetworkAvailablePrefixes, float64(nw.Consumption.Ipv4.AvailablePrefixes), nwID) } return nil } func (c *collector) partitionMetrics(ctx context.Context) error { - resp, err := c.client.Partition().PartitionCapacity(partition.NewPartitionCapacityParams().WithBody(&models.V1PartitionCapacityRequest{}).WithContext(ctx), nil) + resp, err := c.client.Adminv2().Partition().Capacity(ctx, &adminv2.PartitionServiceCapacityRequest{}) if err != nil { return fmt.Errorf("error retrieving partitions: %w", err) } - for _, p := range resp.Payload { - if p.ID == nil { - continue - } - - for _, s := range p.Servers { - if s.Size == nil { - continue - } - + for _, p := range resp.PartitionCapacity { + for _, s := range p.MachineSizeCapacities { var ( - pID = pointer.SafeDeref(p.ID) - size = pointer.SafeDeref(s.Size) + pID = p.Partition + size = s.Size ) c.storeGauge(metalPartitionCapacityTotal, float64(s.Total), pID, size) @@ -404,7 +413,7 @@ func (c *collector) partitionMetrics(ctx context.Context) error { c.storeGauge(metalPartitionCapacityAllocatable, float64(s.Allocatable), pID, size) c.storeGauge(metalPartitionCapacityFaulty, float64(s.Faulty), pID, size) c.storeGauge(metalPartitionCapacityReservationsTotal, float64(s.Reservations), pID, size) - c.storeGauge(metalPartitionCapacityReservationsUsed, float64(s.Usedreservations), pID, size) + c.storeGauge(metalPartitionCapacityReservationsUsed, float64(s.UsedReservations), pID, size) c.storeGauge(metalPartitionCapacityPhonedHome, float64(s.PhonedHome), pID, size) c.storeGauge(metalPartitionCapacityUnavailable, float64(s.Unavailable), pID, size) c.storeGauge(metalPartitionCapacityOther, float64(s.Other), pID, size) @@ -415,68 +424,85 @@ func (c *collector) partitionMetrics(ctx context.Context) error { } func (c *collector) imageMetrics(ctx context.Context) error { - resp, err := c.client.Image().ListImages(image.NewListImagesParams().WithShowUsage(pointer.Pointer(true)).WithContext(ctx), nil) + resp, err := c.client.Adminv2().Image().Usage(ctx, &adminv2.ImageServiceUsageRequest{}) if err != nil { return fmt.Errorf("error retrieving images: %w", err) } - for _, i := range resp.Payload { - if i.ID == nil { - continue + for _, i := range resp.ImageUsage { + var imageFeatures []string + for _, feature := range i.Image.Features { + featureString, err := enum.GetStringValue(feature) + if err != nil { + continue + } + imageFeatures = append(imageFeatures, *featureString) } - var ( - id = pointer.SafeDeref(i.ID) - usage = len(i.Usedby) - created = fmt.Sprintf("%d", time.Time(i.Created).Unix()) - expirationDate = fmt.Sprintf("%d", time.Time(pointer.SafeDeref(i.ExpirationDate)).Unix()) - features = strings.Join(i.Features, ",") + id = i.Image.Id + usage = len(i.UsedBy) + created = fmt.Sprintf("%d", i.Image.Meta.CreatedAt.AsTime().Unix()) + expirationDate = fmt.Sprintf("%d", i.Image.ExpiresAt.AsTime().Unix()) + features = strings.Join(imageFeatures, ",") ) - c.storeGauge(metalImageUsedTotal, float64(usage), id, i.Name, i.Classification, created, expirationDate, features) + classification, err := enum.GetStringValue(i.Image.Classification) + if err != nil { + return fmt.Errorf("unable to get image classification string: %w", err) + } + + c.storeGauge(metalImageUsedTotal, float64(usage), id, *i.Image.Name, *classification, created, expirationDate, features) } return nil } func (c *collector) projectMetrics(ctx context.Context) error { - resp, err := c.client.Project().ListProjects(project.NewListProjectsParams().WithContext(ctx), nil) + resp, err := c.client.Adminv2().Project().List(ctx, &adminv2.ProjectServiceListRequest{}) if err != nil { return fmt.Errorf("error retrieving images: %w", err) } - for _, p := range resp.Payload { - c.storeGauge(metalProjectInfo, float64(1.0), p.Meta.ID, p.Name, p.TenantID) + for _, p := range resp.Projects { + c.storeGauge(metalProjectInfo, float64(1.0), p.Uuid, p.Name, p.Tenant) } return nil } func (c *collector) switchMetrics(ctx context.Context) error { - resp, err := c.client.SwitchOperations().ListSwitches(switch_operations.NewListSwitchesParams().WithContext(ctx), nil) + resp, err := c.client.Adminv2().Switch().List(ctx, &adminv2.SwitchServiceListRequest{}) if err != nil { return fmt.Errorf("error retrieving switches: %w", err) } - for _, s := range resp.Payload { + for _, s := range resp.Switches { var ( - lastSync = time.Time(pointer.SafeDeref(pointer.SafeDeref(s.LastSync).Time)) - lastSyncError = time.Time(pointer.SafeDeref(pointer.SafeDeref(s.LastSyncError).Time)) + lastSync = pointer.SafeDeref(s.LastSync).Time.AsTime() + lastSyncError = pointer.SafeDeref(s.LastSyncError).Time.AsTime() syncFailed = 0.0 - lastSyncDurationMs = float64(time.Duration(pointer.SafeDeref(pointer.SafeDeref(s.LastSync).Duration)).Milliseconds()) - lastSyncErrorDurationMs = float64(time.Duration(pointer.SafeDeref(pointer.SafeDeref(s.LastSyncError).Duration)).Milliseconds()) + lastSyncDurationMs = float64(pointer.SafeDeref(s.LastSync).Duration.AsDuration().Milliseconds()) + lastSyncErrorDurationMs = float64(pointer.SafeDeref(s.LastSyncError).Duration.AsDuration().Milliseconds()) - partitionID = pointer.SafeDeref(pointer.SafeDeref(s.Partition).ID) - rackID = pointer.SafeDeref(s.RackID) - osVendor = pointer.SafeDeref(s.Os).Vendor + partitionID = s.Partition + rackID = pointer.SafeDeref(s.Rack) + osVendor = "" osVersion = pointer.SafeDeref(s.Os).Version // metal core version is very long: v0.9.1 (1d5e42ea), tags/v0.9.1-0-g1d5e42e, go1.20.5 metalCoreVersion = strings.Split(pointer.SafeDeref(s.Os).MetalCoreVersion, ",")[0] metalCoreUp = 1.0 - managementIP = s.ManagementIP + managementIP = s.ManagementIp ) + if s.Os != nil { + vendor, err := enum.GetStringValue(s.Os.Vendor) + if err != nil { + return fmt.Errorf("unable to get switch os vendor string: %w", err) + } + osVendor = *vendor + } + if lastSyncError.After(lastSync) { syncFailed = 1.0 lastSyncDurationMs = lastSyncErrorDurationMs @@ -487,15 +513,15 @@ func (c *collector) switchMetrics(ctx context.Context) error { metalCoreUp = 0.0 } - c.storeGauge(metalSwitchInfo, 1.0, s.Name, partitionID, rackID, metalCoreVersion, osVendor, osVersion, managementIP) - c.storeGauge(metalSwitchMetalCoreUp, metalCoreUp, s.Name, partitionID, rackID) - c.storeGauge(metalSwitchSyncFailed, syncFailed, s.Name, partitionID, rackID) - c.storeGaugeTimestamp(lastSync, metalSwitchSyncDurationsMs, lastSyncDurationMs, s.Name, partitionID, rackID) + c.storeGauge(metalSwitchInfo, 1.0, s.Id, partitionID, rackID, metalCoreVersion, osVendor, osVersion, managementIP) + c.storeGauge(metalSwitchMetalCoreUp, metalCoreUp, s.Id, partitionID, rackID) + c.storeGauge(metalSwitchSyncFailed, syncFailed, s.Id, partitionID, rackID) + c.storeGaugeTimestamp(lastSync, metalSwitchSyncDurationsMs, lastSyncDurationMs, s.Id, partitionID, rackID) - for _, conn := range s.Connections { - c.storeGauge(metalSwitchInterfaceInfo, 1.0, s.Name, pointer.SafeDeref(pointer.SafeDeref(conn.Nic).Name), conn.MachineID, partitionID) + for _, conn := range s.MachineConnections { + c.storeGauge(metalSwitchInterfaceInfo, 1.0, s.Id, pointer.SafeDeref(conn.Nic).Name, conn.MachineId, partitionID) if conn.Nic.BgpPortState != nil { - c.storeGauge(switchInterfaceBGPTimeStampEstablished, float64(pointer.SafeDeref(conn.Nic.BgpPortState.BgpTimerUpEstablished)), s.Name, pointer.SafeDeref(pointer.SafeDeref(conn.Nic).Name), conn.MachineID, partitionID) + c.storeGauge(switchInterfaceBGPTimeStampEstablished, float64(pointer.SafeDeref(conn.Nic.BgpPortState.BgpTimerUpEstablished).Seconds), s.Id, pointer.SafeDeref(conn.Nic).Name, conn.MachineId, partitionID) } } @@ -505,48 +531,68 @@ func (c *collector) switchMetrics(ctx context.Context) error { } func (c *collector) machineMetrics(ctx context.Context) error { - machines, err := c.client.Machine().FindIPMIMachines(machine.NewFindIPMIMachinesParams().WithBody(&models.V1MachineFindRequest{}).WithContext(ctx), nil) + machines, err := c.client.Adminv2().Machine().List(ctx, &adminv2.MachineServiceListRequest{}) if err != nil { return fmt.Errorf("error retrieving machines: %w", err) } - allIssues, err := c.client.Machine().ListIssues(machine.NewListIssuesParams().WithContext(ctx), nil) + machineBMCs, err := c.client.Adminv2().Machine().ListBMC(ctx, &adminv2.MachineServiceListBMCRequest{}) + if err != nil { + return fmt.Errorf("error retrieving machine bmcs: %w", err) + } + + bmcs := make(map[string]*apiv2.MachineBMCDetails) + for _, machineBMC := range machineBMCs.BmcDetails { + bmcs[machineBMC.Uuid] = machineBMC + } + + allIssues, err := c.client.Adminv2().Machine().Issues(ctx, &adminv2.MachineServiceIssuesRequest{}) if err != nil { return fmt.Errorf("error retrieving machine issues list: %w", err) } - issues, err := c.client.Machine().Issues(machine.NewIssuesParams().WithBody(&models.V1MachineIssuesRequest{ - LastErrorThreshold: pointer.Pointer(int64(1 * time.Hour)), - }).WithContext(ctx), nil) + issues, err := c.client.Adminv2().Machine().Issues(ctx, &adminv2.MachineServiceIssuesRequest{ + Query: &apiv2.MachineIssuesQuery{ + LastErrorThreshold: durationpb.New(time.Hour), + }, + }) if err != nil { return fmt.Errorf("error retrieving machine issues: %w", err) } issuesByMachineID := map[string][]string{} - for _, issue := range issues.Payload { - if issue.Machineid == nil { - continue + for _, issue := range issues.Issues { + var issueTypes []string + for _, i := range issue.Issues { + typeString, err := enum.GetStringValue(i.Type) + if err != nil { + continue + } + issueTypes = append(issueTypes, *typeString) } - - issuesByMachineID[*issue.Machineid] = issue.Issues + issuesByMachineID[issue.Uuid] = issueTypes } allIssuesByID := map[string]bool{} - for _, issue := range allIssues.Payload { - if issue.ID == nil { - continue + for _, issue := range allIssues.Issues { + for _, i := range issue.Issues { + issueID, err := enum.GetStringValue(i.Type) + if err != nil { + return fmt.Errorf("unable to get issue string: %w", err) + } + if _, ok := allIssuesByID[*issueID]; ok { + continue + } + allIssuesByID[*issueID] = true + severityString, err := enum.GetStringValue(i.Severity) + if err != nil { + return fmt.Errorf("unable to get issue severity string: %w", err) + } + c.storeGauge(metalMachineIssuesInfo, 1.0, *issueID, i.Description, *severityString, i.ReferenceUrl) } - - allIssuesByID[*issue.ID] = true - - c.storeGauge(metalMachineIssuesInfo, 1.0, *issue.ID, pointer.SafeDeref(issue.Description), pointer.SafeDeref(issue.Severity), pointer.SafeDeref(issue.RefURL)) } - for _, m := range machines.Payload { - if m.ID == nil { - continue - } - + for _, m := range machines.Machines { var ( partitionID = "" role = "" @@ -557,39 +603,48 @@ func (c *collector) machineMetrics(ctx context.Context) error { imageId = "NOTALLOCATED" ) - if m.State != nil && m.State.Value != nil && *m.State.Value != "" { - state = *m.State.Value + if m.Status != nil && m.Status.Condition != nil { + stateString, err := enum.GetStringValue(m.Status.Condition.State) + if err != nil { + return err + } + state = strings.ToUpper(*stateString) } if m.Allocation != nil { - if m.Allocation.Role != nil { - role = *m.Allocation.Role + roleString, err := enum.GetStringValue(m.Allocation.AllocationType) + if err != nil { + return err } + role = *roleString - if m.Allocation.Hostname != nil { - hostname = *m.Allocation.Hostname - } + hostname = m.Allocation.Hostname - if m.Allocation.Image != nil && m.Allocation.Image.ID != nil { - imageId = *m.Allocation.Image.ID + if m.Allocation.Image != nil { + imageId = m.Allocation.Image.Id } - tm := metaltag.NewTagMap(m.Tags) - if id, ok := tm.Value(metaltag.ClusterID); ok { - clusterID = id - } - if asn, ok := tm.Value(metaltag.MachineNetworkPrimaryASN); ok { - primaryASN = asn + if m.Meta.Labels != nil && m.Meta.Labels.Labels != nil { + if id, ok := m.Meta.Labels.Labels[tag.ClusterID]; ok { + clusterID = id + } + if asn, ok := m.Meta.Labels.Labels[tag.MachineNetworkPrimaryASN]; ok { + primaryASN = asn + } } } - if m.Partition != nil && m.Partition.ID != nil { - partitionID = *m.Partition.ID + if m.Partition != nil { + partitionID = m.Partition.Id } - if m.Ipmi != nil { - if m.Ipmi.Powerstate != nil { - var powerstate float64 - switch *m.Ipmi.Powerstate { + if machineBMC, ok := bmcs[m.Uuid]; ok { + if machineBMC.BmcReport == nil { + continue + } + bmcReport := machineBMC.BmcReport + var powerstate float64 + if bmcReport.Bmc != nil { + switch bmcReport.Bmc.PowerState { case "ON": powerstate = 1 case "OFF": @@ -597,52 +652,58 @@ func (c *collector) machineMetrics(ctx context.Context) error { default: powerstate = -1 } - - c.storeGauge(metalMachinePowerState, powerstate, *m.ID) + c.storeGauge(metalMachinePowerState, powerstate, m.Uuid) } - if m.Ipmi.Powersupplies != nil { - c.storeGauge(metalMachinePowerSuppliesTotal, float64(len(m.Ipmi.Powersupplies)), *m.ID) + c.storeGauge(metalMachinePowerSuppliesTotal, float64(len(bmcReport.PowerSupplies)), m.Uuid) - healthy := 0 - for _, ps := range m.Ipmi.Powersupplies { - if ps.Status != nil && ps.Status.Health != nil && *ps.Status.Health == "OK" { - healthy++ - } + healthy := 0 + for _, ps := range bmcReport.PowerSupplies { + if ps.Health == "OK" { + healthy++ } - - c.storeGauge(metalMachinePowerSuppliesHealthy, float64(healthy), *m.ID) } - if m.Ipmi.Powermetric != nil && m.Ipmi.Powermetric.Averageconsumedwatts != nil { - c.storeGauge(metalMachinePowerUsage, float64(pointer.SafeDeref(m.Ipmi.Powermetric.Averageconsumedwatts)), *m.ID) + c.storeGauge(metalMachinePowerSuppliesHealthy, float64(healthy), m.Uuid) + + if bmcReport.PowerMetric != nil { + c.storeGauge(metalMachinePowerUsage, float64(bmcReport.PowerMetric.AverageConsumedWatts), m.Uuid) } size := "UNKNOWN" if m.Size != nil { - size = m.Size.Name + size = m.Size.Id } - if m.Bios != nil && m.Ipmi.Fru != nil { - c.storeGauge(metalMachineHardwareInfo, 1.0, *m.ID, partitionID, size, pointer.SafeDeref(m.Ipmi.Bmcversion), - pointer.SafeDeref(m.Bios.Version), m.Ipmi.Fru.ChassisPartNumber, m.Ipmi.Fru.ChassisPartSerial, m.Ipmi.Fru.BoardMfg, m.Ipmi.Fru.BoardMfgSerial, m.Ipmi.Fru.BoardPartNumber, - m.Ipmi.Fru.ProductManufacturer, m.Ipmi.Fru.ProductPartNumber, m.Ipmi.Fru.ProductSerial) + if bmcReport.Fru != nil { + c.storeGauge(metalMachineHardwareInfo, 1.0, m.Uuid, partitionID, size, + pointer.SafeDeref(bmcReport.Bmc).Version, + pointer.SafeDeref(bmcReport.Bios).Version, + pointer.SafeDeref(bmcReport.Fru.ChassisPartNumber), + pointer.SafeDeref(bmcReport.Fru.ChassisPartSerial), + pointer.SafeDeref(bmcReport.Fru.BoardMfg), + pointer.SafeDeref(bmcReport.Fru.BoardMfgSerial), + pointer.SafeDeref(bmcReport.Fru.BoardPartNumber), + pointer.SafeDeref(bmcReport.Fru.ProductManufacturer), + pointer.SafeDeref(bmcReport.Fru.ProductPartNumber), + pointer.SafeDeref(bmcReport.Fru.ProductSerial), + ) } } - c.storeGauge(metalMachineAllocationInfo, 1.0, *m.ID, partitionID, hostname, clusterID, primaryASN, role, state, imageId) + c.storeGauge(metalMachineAllocationInfo, 1.0, m.Uuid, partitionID, hostname, clusterID, primaryASN, role, state, imageId) for issueID := range allIssuesByID { - issues, ok := issuesByMachineID[*m.ID] + issues, ok := issuesByMachineID[m.Uuid] if !ok { - c.storeGauge(metalMachineIssues, 0.0, *m.ID, issueID) + c.storeGauge(metalMachineIssues, 0.0, m.Uuid, issueID) continue } if slices.Contains(issues, issueID) { - c.storeGauge(metalMachineIssues, 1.0, *m.ID, issueID) + c.storeGauge(metalMachineIssues, 1.0, m.Uuid, issueID) } else { - c.storeGauge(metalMachineIssues, 0.0, *m.ID, issueID) + c.storeGauge(metalMachineIssues, 0.0, m.Uuid, issueID) } } }