diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8aca12d..29e1f09 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -35,13 +35,13 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -49,11 +49,11 @@ jobs: - if: matrix.build-mode == 'manual' env: # fix "go: download go1.22 for linux/amd64: toolchain not available" error - GOTOOLCHAIN: "go1.24.6" + GOTOOLCHAIN: "go1.25.4" run: | make go-build - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 7a3a674..2d54934 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -16,11 +16,11 @@ jobs: runs-on: ubuntu-latest steps: - id: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: 'Dependency Review' - uses: actions/dependency-review-action@bc41886e18ea39df68b1b1245f4184881938e050 # v4.7.2 + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 with: # fail if a pull request introduce vulnerabilities of level "low" or higher fail-on-severity: low diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 880a81f..77469e8 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -17,10 +17,10 @@ jobs: runs-on: ubuntu-latest steps: - id: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 with: go-version-file: 'go.mod' cache: false diff --git a/.github/workflows/gosec.yml b/.github/workflows/gosec.yml index dd159f8..2c5458f 100644 --- a/.github/workflows/gosec.yml +++ b/.github/workflows/gosec.yml @@ -19,12 +19,12 @@ jobs: GO111MODULE: on steps: - id: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: Run Gosec Security Scanner env: - GOTOOLCHAIN: "go1.24.6" - uses: securego/gosec@c9453023c4e81ebdb6dde29e22d9cd5e2285fb16 # v2.22.8 + GOTOOLCHAIN: "go1.25.4" + uses: securego/gosec@6be2b51fd78feca86af91f5186b7964d76cb1256 # v2.22.10 with: args: ./... diff --git a/.github/workflows/osv-scan.yml b/.github/workflows/osv-scan.yml index 145214d..2afde81 100644 --- a/.github/workflows/osv-scan.yml +++ b/.github/workflows/osv-scan.yml @@ -21,4 +21,4 @@ jobs: security-events: write contents: read actions: read - uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@6c57776178c26313323dcdf6c082ed195314fd17" #v2.2.1 + uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@b77c075a1235514558f0eb88dbd31e22c45e0cd2" #v2.3.0 diff --git a/.github/workflows/release-verification.yml b/.github/workflows/release-verification.yml index a6250d0..baa1834 100644 --- a/.github/workflows/release-verification.yml +++ b/.github/workflows/release-verification.yml @@ -19,7 +19,7 @@ jobs: uses: slsa-framework/slsa-verifier/actions/installer@ea584f4502babc6f60d9bc799dbbb13c1caa9ee6 # v2.7.1 - name: Install Cosign - uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1 + uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - name: Download assets env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee64e51..12f4d1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,25 +21,25 @@ jobs: id-token: write # sign archives with cosign steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false fetch-depth: 0 - name: Update goreportcard uses: creekorful/goreportcard-action@1f35ced8cdac2cba28c9a2f2288a16aacfd507f9 # v1.0 - name: Setup go - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 with: go-version-file: 'go.mod' check-latest: true cache: false - name: Install Syft - uses: anchore/sbom-action/download-syft@cee1b8e05ae5b2593a75e197229729eabaa9f8ec # v0.20.2 + uses: anchore/sbom-action/download-syft@fbfd9c6c189226748411491745178e0c2017392d # v0.20.10 - name: Install Cosign - uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1 + uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - name: Run GoReleaser id: goreleaser - uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 + uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0 with: version: '~> v2' args: release --clean @@ -64,18 +64,18 @@ jobs: id-token: write # sign archives with cosign steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false fetch-depth: 0 - name: Setup go - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 with: go-version-file: 'go.mod' check-latest: true cache: false - name: Install Cosign - uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1 + uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - name: Publish pbs-exporter id: release uses: ./.github/actions/publish-image @@ -129,20 +129,20 @@ jobs: permissions: read-all steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false fetch-depth: 0 - name: Login - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Install Cosign - uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3.9.1 + uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0 - name: Verify provenance of image env: diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 08e6255..319a7aa 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -29,12 +29,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif @@ -58,6 +58,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: sarif_file: results.sarif diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9025d0d..64be46a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,7 +12,7 @@ repos: - id: check-executables-have-shebangs - id: check-merge-conflict - repo: https://github.com/gitleaks/gitleaks - rev: v8.28.0 + rev: v8.30.0 hooks: - id: gitleaks - repo: https://github.com/dnephin/pre-commit-golang @@ -23,6 +23,6 @@ repos: - id: go-imports - id: go-unit-tests - repo: https://github.com/golangci/golangci-lint - rev: v2.4.0 + rev: v2.6.2 hooks: - id: golangci-lint diff --git a/Makefile b/Makefile index 7aee973..d7a7353 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ DESCRIPTION ?= Export Proxmox Backup Server metrics for Prometheus .PHONY: go-tidy go-tidy: - go mod tidy -compat=1.24 + go mod tidy -compat=1.25 @echo "Go modules tidied." .PHONY: go-update diff --git a/go.mod b/go.mod index c7eaa9a..c9fc464 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,18 @@ module github.com/natrontech/pbs-exporter -go 1.24.6 +go 1.25.4 -require github.com/prometheus/client_golang v1.23.0 +require github.com/prometheus/client_golang v1.23.2 require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/kr/text v0.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect - github.com/prometheus/procfs v0.17.0 // indirect - golang.org/x/sys v0.35.0 // indirect - google.golang.org/protobuf v1.36.8 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/sys v0.38.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect ) diff --git a/go.sum b/go.sum index c977dc2..340321e 100644 --- a/go.sum +++ b/go.sum @@ -2,33 +2,45 @@ 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +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_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.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= -github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= -github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +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.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +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= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/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/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 5b3d57e..1e7da8e 100644 --- a/main.go +++ b/main.go @@ -107,6 +107,21 @@ var ( "The verify status of the last backup of a VM.", []string{"datastore", "namespace", "vm_id", "vm_name"}, nil, ) + subscription_status = prometheus.NewDesc( + prometheus.BuildFQName(promNamespace, "", "host_subscription_status"), + "The subscription status of the host.", + []string{"status"}, nil, + ) + subscription_info = prometheus.NewDesc( + prometheus.BuildFQName(promNamespace, "", "host_subscription_info"), + "The subscription info of the host.", + []string{"productname", "status"}, nil, + ) + subscription_due_timestamp_seconds = prometheus.NewDesc( + prometheus.BuildFQName(promNamespace, "", "host_subscription_due_timestamp_seconds"), + "The subscription next due timestamp (unix seconds) of the host.", + []string{"productname"}, nil, + ) host_cpu_usage = prometheus.NewDesc( prometheus.BuildFQName(promNamespace, "", "host_cpu_usage"), "The CPU usage of the host.", @@ -291,6 +306,9 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { ch <- snapshot_vm_count ch <- snapshot_vm_last_timestamp ch <- snapshot_vm_last_verify + ch <- subscription_info + ch <- subscription_status + ch <- subscription_due_timestamp_seconds ch <- host_cpu_usage ch <- host_memory_free ch <- host_memory_total @@ -392,6 +410,12 @@ func (e *Exporter) collectFromAPI(ch chan<- prometheus.Metric) error { return err } + // get node subscription metrics + err = e.getNodeSubscriptionMetrics(ch) + if err != nil { + return err + } + return nil } @@ -450,6 +474,89 @@ func (e *Exporter) getVersion(ch chan<- prometheus.Metric) error { return nil } +func (e *Exporter) getNodeSubscriptionMetrics(ch chan<- prometheus.Metric) error { + req, err := http.NewRequest("GET", e.endpoint+nodeApi+"/localhost/subscription", nil) + if err != nil { + return err + } + + // add Authorization header + req.Header.Set("Authorization", e.authorizationHeader) + + if *loglevel == "debug" { + log.Printf("DEBUG: Request URL: %s", req.URL) + } + + resp, err := client.Do(req) + if err != nil { + return err + } + body, err := io.ReadAll(resp.Body) + if err := resp.Body.Close(); err != nil { + log.Printf("Error closing response body: %v", err) + } + if err != nil { + return err + } + + if resp.StatusCode != 200 { + return fmt.Errorf("ERROR: Status code %d returned from endpoint: %s", resp.StatusCode, e.endpoint) + } + + if *loglevel == "debug" { + log.Printf("DEBUG: Status code %d returned from endpoint: %s", resp.StatusCode, e.endpoint) + } + + var raw struct { + Data map[string]any `json:"data"` + } + if err := json.Unmarshal(body, &raw); err != nil { + return err + } + + // default values + statusStr := "" + productName := "unknown" + dueTs := int64(0) + + if v, ok := raw.Data["status"]; ok && v != nil { + statusStr = fmt.Sprintf("%v", v) + } + if v, ok := raw.Data["productname"]; ok && v != nil { + productName = fmt.Sprintf("%v", v) + } + if v, ok := raw.Data["nextduedate"]; ok && v != nil { + if s, ok := v.(string); ok { + t, err := time.Parse("2006-01-02", s) + if err == nil { + dueTs = t.Unix() + } + } + } + + ch <- prometheus.MustNewConstMetric( + subscription_info, prometheus.GaugeValue, 1, productName, statusStr, + ) + + ch <- prometheus.MustNewConstMetric( + subscription_due_timestamp_seconds, prometheus.GaugeValue, float64(dueTs), productName, + ) + + // Emit a metric for each possible status with 1/0 + statuses := []string{"new", "notfound", "active", "invalid", "expired", "suspended"} + for _, s := range statuses { + val := 0.0 + if statusStr == s { + val = 1.0 + } + ch <- prometheus.MustNewConstMetric( + subscription_status, prometheus.GaugeValue, val, s, + ) + } + + return nil +} + func (e *Exporter) getNodeMetrics(ch chan<- prometheus.Metric) error { // NOTE: According to the api documentation, we have to provide the node name (won't work with the node ip), // but it seems to work with any name, so we just use "localhost" here.