Compare commits
122 Commits
Author | SHA1 | Date |
---|---|---|
|
bc45a49c9b | |
|
012b46d220 | |
|
1556ac589f | |
|
547afad282 | |
|
997272f779 | |
|
bd174abd54 | |
|
b5820387c0 | |
|
bc1e7f8965 | |
|
b30335fa54 | |
|
9222ba465f | |
|
95b29ac8a9 | |
|
f8f39bc9fc | |
|
3a0eec4626 | |
|
d680e8c991 | |
|
921147623e | |
|
5de7a63748 | |
|
0a50cabaf9 | |
|
72ed667151 | |
|
9add5df58e | |
|
61cb551bb5 | |
|
fc6ab5bf65 | |
|
c309d3a00f | |
|
cb6bdd2046 | |
|
dc54dc6580 | |
|
63a0298d5a | |
|
61be81fa1f | |
|
c106a38fab | |
|
104c83fb9f | |
|
9f6f1cd762 | |
|
f421b9a69a | |
|
dd2ca7b09b | |
|
6942640a45 | |
|
a483701408 | |
|
56d3b561e6 | |
|
dff9a1e8f5 | |
|
a0889d56c8 | |
|
edc9db4649 | |
|
0a8f32ff12 | |
|
291a962840 | |
|
c6ae6f53f7 | |
|
9576360078 | |
|
8b2aa61c70 | |
|
d59fe58df9 | |
|
5e8e4fffa6 | |
|
301240eccc | |
|
a5789613f1 | |
|
15e6171c37 | |
|
873c140040 | |
|
5c130dadad | |
|
2258ba71e8 | |
|
55d2a7cfac | |
|
97bfa326ab | |
|
ea6b123caf | |
|
b9068888d0 | |
|
dbf6e9b4e2 | |
|
d84b10a125 | |
|
775e4586ad | |
|
bea09e59f0 | |
|
4519b2848b | |
|
99224f2c29 | |
|
84e2d8fe69 | |
|
6beab20acd | |
|
bb2ad9d2ea | |
|
28bf0e817d | |
|
1018969872 | |
|
043b59ab08 | |
|
8ec1fb3c15 | |
|
e178d8e582 | |
|
20af6bac6d | |
|
9ea1495953 | |
|
090535fcd2 | |
|
d45250dec2 | |
|
3b21633bee | |
|
0b01e3abce | |
|
012b785665 | |
|
209f3ab5fa | |
|
d060e4f32c | |
|
aab32a9e1a | |
|
e204ab31cb | |
|
38e2fa6e53 | |
|
f6e2b5fbda | |
|
3aee5ff2bb | |
|
55e124f509 | |
|
f0f9898fa1 | |
|
a0d9835e78 | |
|
a09d690396 | |
|
05187b47e2 | |
|
518db3d3cd | |
|
0c029b690e | |
|
ffd8f41131 | |
|
4f62f0fde4 | |
|
1c923dc83b | |
|
a6ff8ae41c | |
|
b528c28f7e | |
|
bc2a57140c | |
|
5f116914f1 | |
|
8b631bbff4 | |
|
bc60722ddd | |
|
456ac8f2e8 | |
|
aa6e88c2d5 | |
|
b3d26f7a9f | |
|
40602cb3e3 | |
|
a3972f90ab | |
|
dab65d7cb4 | |
|
05e45869ff | |
|
3d73ddbd09 | |
|
5906b6dc9e | |
|
c853e41fee | |
|
2c423907a3 | |
|
0129759430 | |
|
c405dcf1f3 | |
|
7f9b0d1189 | |
|
a775fe3458 | |
|
3527ceb4d5 | |
|
9ce7e6b771 | |
|
cd49aeac35 | |
|
16db8b7368 | |
|
350a7c472e | |
|
919ce06ec7 | |
|
2a95d4649b | |
|
dc60c09548 | |
|
4fa35209c1 |
|
@ -9,10 +9,10 @@ assignees: ''
|
|||
|
||||
<!-- Please only use this template for submitting feature requests -->
|
||||
|
||||
**What would you like to be added**:
|
||||
**What would you like to be added:**
|
||||
|
||||
**Why is this needed**:
|
||||
**Why is this needed:**
|
||||
|
||||
**Describe the solution you'd like**
|
||||
**Describe the solution you'd like:**
|
||||
|
||||
**Additional context**
|
||||
**Additional context:**
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
4. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
|
||||
-->
|
||||
|
||||
**What this PR does / why we need it**:
|
||||
<!-- markdownlint-disable-next-line MD041 -->
|
||||
**What this PR does / why we need it:**
|
||||
|
||||
**How does this change affect the cardinality of KSM**: *(increases, decreases or does not change cardinality)*
|
||||
**How does this change affect the cardinality of KSM:** *(increases, decreases or does not change cardinality)*
|
||||
|
||||
**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
|
||||
**Which issue(s) this PR fixes:** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*
|
||||
Fixes #
|
||||
|
|
|
@ -8,10 +8,6 @@ updates:
|
|||
- "k8s.io*"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "tools"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
|
|
|
@ -20,8 +20,8 @@ env:
|
|||
E2E_SETUP_KIND: yes
|
||||
E2E_SETUP_KUBECTL: yes
|
||||
SUDO: sudo
|
||||
GO_VERSION: "^1.23"
|
||||
GOLANGCI_LINT_VERSION: "v1.61.0"
|
||||
GO_VERSION: "^1.24"
|
||||
GOLANGCI_LINT_VERSION: "v2.0.2"
|
||||
|
||||
jobs:
|
||||
ci-go-lint:
|
||||
|
@ -29,18 +29,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
make install-tools
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${{ env.GOLANGCI_LINT_VERSION }}
|
||||
|
@ -51,18 +47,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
make install-tools
|
||||
|
||||
- name: Validate generated manifests
|
||||
run: |
|
||||
make validate-manifests
|
||||
|
@ -72,18 +64,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
make install-tools
|
||||
|
||||
- name: Validate go modules
|
||||
run: |
|
||||
make validate-modules
|
||||
|
@ -93,18 +81,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
make install-tools
|
||||
|
||||
- name: Check that all metrics are documented and templates have no delta
|
||||
run: |
|
||||
make doccheck
|
||||
|
@ -114,18 +98,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
make install-tools
|
||||
|
||||
- name: Unit tests
|
||||
run: |
|
||||
make test-unit
|
||||
|
@ -135,7 +115,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Setup promtool
|
||||
run: |
|
||||
|
@ -150,18 +130,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
make install-tools
|
||||
|
||||
- name: Benchmark tests
|
||||
run: |
|
||||
BENCHSTAT_OUTPUT_FILE=result.txt make test-benchmark-compare
|
||||
|
@ -181,18 +157,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
make install-tools
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
make build
|
||||
|
@ -202,18 +174,14 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
id: go
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
make install-tools
|
||||
|
||||
- name: End-to-end tests
|
||||
run: |
|
||||
make e2e
|
||||
|
|
|
@ -6,7 +6,7 @@ on:
|
|||
- cron: '0 0 * * 1'
|
||||
|
||||
env:
|
||||
GO_VERSION: "^1.23"
|
||||
GO_VERSION: "^1.24"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
@ -15,10 +15,10 @@ jobs:
|
|||
ci-security-checks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
name: Checkout code
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Install govulncheck binary
|
||||
|
|
|
@ -18,7 +18,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set environment variables
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
|
|
@ -21,10 +21,10 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Fetch source code into GITHUB_WORKSPACE
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Install Kubernetes BOM
|
||||
uses: kubernetes-sigs/release-actions/setup-bom@a69972745f85aab4ba5d6c681e2a0e7f73eaff2b # v0.3.0
|
||||
uses: kubernetes-sigs/release-actions/setup-bom@a30d93cf2aa029e1e4c8a6c79f766aebf429fddb # v0.3.1
|
||||
|
||||
- name: Generate SBOM
|
||||
run: |
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
run:
|
||||
timeout: 10m
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
|
@ -18,22 +13,48 @@ linters:
|
|||
- staticcheck
|
||||
- unconvert
|
||||
- unused
|
||||
exclusions:
|
||||
generated: lax
|
||||
rules:
|
||||
- linters:
|
||||
- promlinter
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- gosec
|
||||
text: 'G104:'
|
||||
- linters:
|
||||
- revive
|
||||
text: 'package-comments:|var-naming:'
|
||||
# This needs to stay as long as we support exposing v1.endpoints metrics
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: 'SA1019: v1.Endpoint'
|
||||
path: 'internal/store/endpoint.*.go|internal/store/builder.go'
|
||||
# TODO: Use functions with context https://github.com/kubernetes/kube-state-metrics/issues/2721
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: 'SA1019: .*List|Watch'
|
||||
path: 'pkg/sharding/listwatch.go|pkg/watch/watch.go'
|
||||
|
||||
linters-settings:
|
||||
goimports:
|
||||
local-prefixes: k8s.io/kube-state-metrics,k8s.io/kube-state-metrics/v2
|
||||
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
exclude-rules:
|
||||
# We don't check metrics naming in the tests.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- promlinter
|
||||
# TODO(mrueg) Improve error handling
|
||||
- text: "G104:"
|
||||
linters:
|
||||
- gosec
|
||||
- text: "package-comments:"
|
||||
linters:
|
||||
- revive
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
settings:
|
||||
goimports:
|
||||
local-prefixes:
|
||||
- k8s.io/kube-state-metrics
|
||||
- k8s.io/kube-state-metrics/v2
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
|
|
@ -8,10 +8,12 @@
|
|||
"style": "asterisk"
|
||||
},
|
||||
"MD013": false, // https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md#md013---line-length
|
||||
"MD024": false, // https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md#md024---multiple-headers-with-the-same-content
|
||||
"MD033": false, // https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md#md033---inline-html
|
||||
"MD036": false, // https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md#md036---emphasis-used-instead-of-a-header
|
||||
"MD040": false, // https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md#md040---fenced-code-blocks-should-have-a-language-specified
|
||||
"MD041": false // https://github.com/markdownlint/markdownlint/blob/main/docs/RULES.md#md041---first-line-in-file-should-be-a-top-level-header
|
||||
"MD033": {
|
||||
"allowed_elements": [
|
||||
"details",
|
||||
"summary",
|
||||
"br"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ To add new statements to publish data about a vulnerability,
|
|||
download [vexctl](https://github.com/openvex/vexctl)
|
||||
and append new statements using `vexctl add`. For example:
|
||||
|
||||
```
|
||||
```bash
|
||||
vexctl add --in-place main.openvex.json pkg:oci/test CVE-2014-1234567 fixed
|
||||
```
|
||||
|
||||
|
|
33
CHANGELOG.md
33
CHANGELOG.md
|
@ -1,6 +1,23 @@
|
|||
# Changelog
|
||||
|
||||
## v2.16.0 / 2025-06-23
|
||||
|
||||
**Note:**
|
||||
|
||||
* This release builds with Golang `v1.24.4`
|
||||
* This release builds with `k8s.io/client-go`: `v0.32.6`
|
||||
|
||||
* [FEATURE] Add a `reclaim_policy` label to the `kube_persistentvolume_info` metric by @SuperQ in <https://github.com/kubernetes/kube-state-metrics/pull/2615>
|
||||
* [FEATURE] Use dlclark/regexp2 over standard library's package to support lookarounds by @rexagod in <https://github.com/kubernetes/kube-state-metrics/pull/2616>
|
||||
* [BUGFIX] Report correct values in `kube_pod_status_reason` metric by @carlosmorenokm1 in <https://github.com/kubernetes/kube-state-metrics/pull/2644>
|
||||
* [FEATURE] Add add `pathType` to `kube_ingress_path` by @rexagod in <https://github.com/kubernetes/kube-state-metrics/pull/2670>
|
||||
* [FEATURE] Introduce object limits by @mrueg in <https://github.com/kubernetes/kube-state-metrics/pull/2626>
|
||||
* [BUGFIX] Close reflectors once their corresponding CRDs are dropped by @rexagod in <https://github.com/kubernetes/kube-state-metrics/pull/2672>
|
||||
* [FEATURE] Incorporate `WithAuthenticationAndAuthorization` to support endpoint authn/z by @mrueg in <https://github.com/kubernetes/kube-state-metrics/pull/2686>
|
||||
|
||||
## v2.15.0 / 2025-02-03
|
||||
|
||||
## Note
|
||||
**Note:**
|
||||
|
||||
* This release builds with Golang `v1.23.5`
|
||||
* This release builds with `k8s.io/client-go`: `v0.32.1`
|
||||
|
@ -16,7 +33,7 @@
|
|||
|
||||
## v2.14.0 / 2024-11-08
|
||||
|
||||
### Note
|
||||
**Note:**
|
||||
|
||||
* This release builds with Golang `v1.23.3`
|
||||
* This release builds with `k8s.io/client-go`: `v0.31.2`
|
||||
|
@ -39,7 +56,7 @@
|
|||
|
||||
## v2.13.0 / 2024-07-18
|
||||
|
||||
### Note
|
||||
**Note:**
|
||||
|
||||
* This release builds with Golang `v1.22.5`.
|
||||
* This release builds with `k8s.io/client-go`: `v0.30.3`.
|
||||
|
@ -54,7 +71,7 @@
|
|||
|
||||
## v2.12.0 / 2024-04-02
|
||||
|
||||
### Note
|
||||
**Note:**
|
||||
|
||||
* This release addresses a critical issue where scraping the exposition data for certain types caused metrics-backends to crash: <https://github.com/kubernetes/kube-state-metrics/issues/2248>.
|
||||
* This release builds with `k8s.io/client-go`: `v0.29.3`.
|
||||
|
@ -65,7 +82,7 @@
|
|||
|
||||
## v2.11.0 / 2024-03-04
|
||||
|
||||
### Note
|
||||
**Note:**
|
||||
|
||||
This release builds with Golang `v1.21.8`.
|
||||
|
||||
|
@ -82,7 +99,7 @@ This release builds with Golang `v1.21.8`.
|
|||
|
||||
## v2.10.1 / 2023-10-09
|
||||
|
||||
### Note
|
||||
**Note:**
|
||||
|
||||
* This release addresses a regression introduced in [#2105](https://github.com/kubernetes/kube-state-metrics/pull/2105).
|
||||
|
||||
|
@ -91,7 +108,7 @@ This release builds with Golang `v1.21.8`.
|
|||
|
||||
## v2.10.0 / 2023-08-31
|
||||
|
||||
### Note
|
||||
**Note:**
|
||||
|
||||
* Label and annotation metrics aren't exposed by default anymore to reduce the memory usage of the default configuration of kube-state-metrics. Before this change, they used to only include the name and namespace of the objects which is not relevant to users not opting in these metrics.
|
||||
|
||||
|
@ -124,7 +141,7 @@ This release does not incorporate any user-facing changes. Re-running release pr
|
|||
|
||||
The changes mentioned below are only the user-facing ones. For a list of the complete set of changes, refer the changelog associated with the release tag.
|
||||
|
||||
### Note
|
||||
**Note:**
|
||||
|
||||
* The deprecated experimental VerticalPodAutoscaler metrics are no longer supported, and have been removed. We recommend to use CustomResourceState metrics to gather metrics from custom resources like the Vertical Pod Autoscaler.
|
||||
* #2004 regulated label names to adhere with [OTel-Prometheus standards](https://github.com/open-telemetry/opentelemetry-specification/blob/8946dfc6a2302f78b0224fcc3f4dfb816a7bb1f4/specification/compatibility/prometheus_and_openmetrics.md?plain=1#L224-L229), so existing label names that do not follow the same may be replaced by the ones that do. Please refer to the PR for more details.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ARG GOVERSION=1.23
|
||||
ARG GOVERSION=1.24
|
||||
ARG GOARCH
|
||||
FROM golang:${GOVERSION} AS builder
|
||||
ARG GOARCH
|
||||
|
@ -6,7 +6,7 @@ ENV GOARCH=${GOARCH}
|
|||
WORKDIR /go/src/k8s.io/kube-state-metrics/
|
||||
COPY . /go/src/k8s.io/kube-state-metrics/
|
||||
|
||||
RUN make install-tools && make build-local
|
||||
RUN make build-local
|
||||
|
||||
FROM gcr.io/distroless/static-debian12:latest-${GOARCH}
|
||||
COPY --from=builder /go/src/k8s.io/kube-state-metrics/kube-state-metrics /
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
kube-state-metrics is welcoming contributions from the community. If you are interested in intensifying your contributions and becoming a maintainer, this doc describes the necessary steps.
|
||||
|
||||
As part of the Kubernetes project, we use the community membership process as described [here](https://github.com/kubernetes/community/blob/master/community-membership.md). We do not adhere strictly to the numbers of contributions and reviews. Still as becoming a maintainer is a trust-based process and we desire positive outcomes for the project, we look for a long-term interest and engagement.
|
||||
As part of the Kubernetes project, we rely on the [community membership process](https://github.com/kubernetes/community/blob/master/community-membership.md). We do not adhere strictly to the numbers of contributions and reviews. Still as becoming a maintainer is a trust-based process and we desire positive outcomes for the project, we look for a long-term interest and engagement.
|
||||
|
||||
## Adding a new reviewer
|
||||
|
||||
|
|
33
Makefile
33
Makefile
|
@ -6,8 +6,6 @@ VERSION = $(shell grep '^version:' data.yaml | grep -oE "[0-9]+.[0-9]+.[0-9]+")
|
|||
TAG ?= $(TAG_PREFIX)$(VERSION)
|
||||
LATEST_RELEASE_BRANCH := release-$(shell echo $(VERSION) | grep -ohE "[0-9]+.[0-9]+")
|
||||
BRANCH = $(strip $(shell git rev-parse --abbrev-ref HEAD))
|
||||
DOCKER_CLI ?= docker
|
||||
PROMTOOL_CLI ?= promtool
|
||||
PKGS = $(shell go list ./... | grep -v /vendor/ | grep -v /tests/e2e)
|
||||
ARCH ?= $(shell go env GOARCH)
|
||||
BUILD_DATE = $(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
|
@ -15,14 +13,21 @@ GIT_COMMIT ?= $(shell git rev-parse --short HEAD)
|
|||
OS ?= $(shell uname -s | tr A-Z a-z)
|
||||
ALL_ARCH = amd64 arm arm64 ppc64le s390x
|
||||
PKG = github.com/prometheus/common
|
||||
PROMETHEUS_VERSION = 2.55.1
|
||||
GO_VERSION = 1.23.5
|
||||
PROMETHEUS_VERSION = 3.4.1
|
||||
GO_VERSION = 1.24.4
|
||||
IMAGE = $(REGISTRY)/kube-state-metrics
|
||||
MULTI_ARCH_IMG = $(IMAGE)-$(ARCH)
|
||||
USER ?= $(shell id -u -n)
|
||||
HOST ?= $(shell hostname)
|
||||
MARKDOWNLINT_CLI2_VERSION = 0.17.2
|
||||
MARKDOWNLINT_CLI2_VERSION = 0.18.1
|
||||
|
||||
DOCKER_CLI ?= docker
|
||||
PROMTOOL_CLI ?= promtool
|
||||
GOMPLATE_CLI ?= go tool github.com/hairyhenderson/gomplate/v4/cmd/gomplate
|
||||
GOJSONTOYAML_CLI ?= go tool github.com/brancz/gojsontoyaml
|
||||
EMBEDMD_CLI ?= go tool github.com/campoy/embedmd
|
||||
JSONNET_CLI ?= go tool github.com/google/go-jsonnet/cmd/jsonnet
|
||||
JB_CLI ?= go tool github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb
|
||||
|
||||
export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
|
||||
|
@ -88,7 +93,7 @@ fix-markdown-format:
|
|||
${DOCKER_CLI} run -v "${PWD}:/workdir" davidanson/markdownlint-cli2:v${MARKDOWNLINT_CLI2_VERSION} --fix --config .markdownlint-cli2.jsonc
|
||||
|
||||
generate-template:
|
||||
gomplate -d config=./data.yaml --file README.md.tpl > README.md
|
||||
${GOMPLATE_CLI} -d config=./data.yaml --file README.md.tpl > README.md
|
||||
|
||||
validate-template: generate-template
|
||||
git diff --no-ext-diff --quiet --exit-code README.md
|
||||
|
@ -138,7 +143,7 @@ e2e:
|
|||
generate: build-local generate-template
|
||||
@echo ">> generating docs"
|
||||
@./scripts/generate-help-text.sh
|
||||
embedmd -w `find . -path ./vendor -prune -o -name "*.md" -print`
|
||||
${EMBEDMD_CLI} -w `find . -path ./vendor -prune -o -name "*.md" -print`
|
||||
|
||||
validate-manifests: examples
|
||||
@git diff --exit-code
|
||||
|
@ -147,31 +152,27 @@ mixin: examples/prometheus-alerting-rules/alerts.yaml
|
|||
|
||||
examples/prometheus-alerting-rules/alerts.yaml: jsonnet $(shell find jsonnet | grep ".libsonnet") scripts/mixin.jsonnet scripts/vendor
|
||||
mkdir -p examples/prometheus-alerting-rules
|
||||
jsonnet -J scripts/vendor scripts/mixin.jsonnet | gojsontoyaml > examples/prometheus-alerting-rules/alerts.yaml
|
||||
${JSONNET_CLI} -J scripts/vendor scripts/mixin.jsonnet | ${GOJSONTOYAML_CLI} > examples/prometheus-alerting-rules/alerts.yaml
|
||||
|
||||
examples: examples/standard examples/autosharding examples/daemonsetsharding mixin
|
||||
|
||||
examples/standard: jsonnet $(shell find jsonnet | grep ".libsonnet") scripts/standard.jsonnet scripts/vendor
|
||||
mkdir -p examples/standard
|
||||
jsonnet -J scripts/vendor -m examples/standard --ext-str version="$(VERSION)" scripts/standard.jsonnet | xargs -I{} sh -c 'cat {} | gojsontoyaml > `echo {} | sed "s/\(.\)\([A-Z]\)/\1-\2/g" | tr "[:upper:]" "[:lower:]"`.yaml' -- {}
|
||||
${JSONNET_CLI} -J scripts/vendor -m examples/standard --ext-str version="$(VERSION)" scripts/standard.jsonnet | xargs -I{} sh -c 'cat {} | ${GOJSONTOYAML_CLI} > `echo {} | sed "s/\(.\)\([A-Z]\)/\1-\2/g" | tr "[:upper:]" "[:lower:]"`.yaml' -- {}
|
||||
find examples -type f ! -name '*.yaml' -delete
|
||||
|
||||
examples/autosharding: jsonnet $(shell find jsonnet | grep ".libsonnet") scripts/autosharding.jsonnet scripts/vendor
|
||||
mkdir -p examples/autosharding
|
||||
jsonnet -J scripts/vendor -m examples/autosharding --ext-str version="$(VERSION)" scripts/autosharding.jsonnet | xargs -I{} sh -c 'cat {} | gojsontoyaml > `echo {} | sed "s/\(.\)\([A-Z]\)/\1-\2/g" | tr "[:upper:]" "[:lower:]"`.yaml' -- {}
|
||||
${JSONNET_CLI} -J scripts/vendor -m examples/autosharding --ext-str version="$(VERSION)" scripts/autosharding.jsonnet | xargs -I{} sh -c 'cat {} | ${GOJSONTOYAML_CLI} > `echo {} | sed "s/\(.\)\([A-Z]\)/\1-\2/g" | tr "[:upper:]" "[:lower:]"`.yaml' -- {}
|
||||
find examples -type f ! -name '*.yaml' -delete
|
||||
|
||||
examples/daemonsetsharding: jsonnet $(shell find jsonnet | grep ".libsonnet") scripts/daemonsetsharding.jsonnet scripts/vendor
|
||||
mkdir -p examples/daemonsetsharding
|
||||
jsonnet -J scripts/vendor -m examples/daemonsetsharding --ext-str version="$(VERSION)" scripts/daemonsetsharding.jsonnet | xargs -I{} sh -c 'cat {} | gojsontoyaml > `echo {} | sed "s/\(.\)\([A-Z]\)/\1-\2/g" | tr "[:upper:]" "[:lower:]"`.yaml' -- {}
|
||||
${JSONNET_CLI} -J scripts/vendor -m examples/daemonsetsharding --ext-str version="$(VERSION)" scripts/daemonsetsharding.jsonnet | xargs -I{} sh -c 'cat {} | ${GOJSONTOYAML_CLI} > `echo {} | sed "s/\(.\)\([A-Z]\)/\1-\2/g" | tr "[:upper:]" "[:lower:]"`.yaml' -- {}
|
||||
find examples -type f ! -name '*.yaml' -delete
|
||||
|
||||
scripts/vendor: scripts/jsonnetfile.json scripts/jsonnetfile.lock.json
|
||||
cd scripts && jb install
|
||||
|
||||
install-tools:
|
||||
@echo Installing tools from tools.go
|
||||
grep '^\s*_' tools/tools.go | awk '{print $$2}' | xargs -tI % go install -mod=readonly -modfile=tools/go.mod %
|
||||
cd scripts && ${JB_CLI} install
|
||||
|
||||
install-promtool:
|
||||
@echo Installing promtool
|
||||
|
|
1
OWNERS
1
OWNERS
|
@ -5,6 +5,7 @@ reviewers:
|
|||
- mrueg
|
||||
- rexagod
|
||||
approvers:
|
||||
- CatherineF-dev
|
||||
- dgrisonnet
|
||||
- mrueg
|
||||
- rexagod
|
||||
|
|
46
README.md
46
README.md
|
@ -41,6 +41,7 @@ are deleted they are no longer visible on the `/metrics` endpoint.
|
|||
* [Resource group version compatibility](#resource-group-version-compatibility)
|
||||
* [Container Image](#container-image)
|
||||
* [Metrics Documentation](#metrics-documentation)
|
||||
* [ECMAScript regular expression support for allow and deny lists](#ecmascript-regular-expression-support-for-allow-and-deny-lists)
|
||||
* [Conflict resolution in label names](#conflict-resolution-in-label-names)
|
||||
* [Kube-state-metrics self metrics](#kube-state-metrics-self-metrics)
|
||||
* [Resource recommendation](#resource-recommendation)
|
||||
|
@ -67,9 +68,8 @@ are deleted they are no longer visible on the `/metrics` endpoint.
|
|||
#### Kubernetes Version
|
||||
|
||||
kube-state-metrics uses [`client-go`](https://github.com/kubernetes/client-go) to talk with
|
||||
Kubernetes clusters. The supported Kubernetes cluster version is determined by `client-go`.
|
||||
The compatibility matrix for client-go and Kubernetes cluster can be found
|
||||
[here](https://github.com/kubernetes/client-go#compatibility-matrix).
|
||||
Kubernetes clusters. The supported Kubernetes cluster version is determined by
|
||||
[`client-go`](https://github.com/kubernetes/client-go#compatibility-matrix).
|
||||
All additional compatibility is only best effort, or happens to still/already be supported.
|
||||
|
||||
#### Compatibility matrix
|
||||
|
@ -79,12 +79,12 @@ Generally, it is recommended to use the latest release of kube-state-metrics. If
|
|||
|
||||
| kube-state-metrics | Kubernetes client-go Version |
|
||||
|--------------------|:----------------------------:|
|
||||
| **v2.11.0** | v1.28 |
|
||||
| **v2.12.0** | v1.29 |
|
||||
| **v2.13.0** | v1.30 |
|
||||
| **v2.14.0** | v1.31 |
|
||||
| **v2.15.0** | v1.32 |
|
||||
| **main** | v1.32 |
|
||||
| **v2.16.0** | v1.32 |
|
||||
| **main** | v1.33 |
|
||||
|
||||
#### Resource group version compatibility
|
||||
|
||||
|
@ -96,8 +96,8 @@ release.
|
|||
|
||||
The latest container image can be found at:
|
||||
|
||||
* `registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.15.0` (arch: `amd64`, `arm`, `arm64`, `ppc64le` and `s390x`)
|
||||
* View all multi-architecture images at [here](https://explore.ggcr.dev/?image=registry.k8s.io%2Fkube-state-metrics%2Fkube-state-metrics:v2.15.0)
|
||||
* `registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.16.0` (arch: `amd64`, `arm`, `arm64`, `ppc64le` and `s390x`)
|
||||
* [Multi-architecture images](https://explore.ggcr.dev/?image=registry.k8s.io%2Fkube-state-metrics%2Fkube-state-metrics:v2.16.0)
|
||||
|
||||
### Metrics Documentation
|
||||
|
||||
|
@ -129,6 +129,10 @@ e.g. by standardizing Kubernetes labels using an
|
|||
[Admission Webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/)
|
||||
that ensures that there are no possible conflicts.
|
||||
|
||||
#### ECMAScript regular expression support for allow and deny lists
|
||||
|
||||
Starting from [#2616](https://github.com/kubernetes/kube-state-metrics/pull/2616/files), kube-state-metrics supports ECMAScript's `regexp` for allow and deny lists. This was incorporated as a workaround for the limitations of the `regexp` package in Go, which does not support lookarounds due to their non-linear time complexity. Please note that while lookarounds are now supported for allow and deny lists, regular expressions' evaluation time is capped at a minute to prevent performance issues.
|
||||
|
||||
### Kube-state-metrics self metrics
|
||||
|
||||
kube-state-metrics exposes its own general process metrics under `--telemetry-host` and `--telemetry-port` (default 8081).
|
||||
|
@ -139,7 +143,7 @@ at the logs of kube-state-metrics.
|
|||
|
||||
Example of the above mentioned metrics:
|
||||
|
||||
```
|
||||
```prometheus
|
||||
kube_state_metrics_list_total{resource="*v1.Node",result="success"} 1
|
||||
kube_state_metrics_list_total{resource="*v1.Node",result="error"} 52
|
||||
kube_state_metrics_watch_total{resource="*v1beta1.Ingress",result="success"} 1
|
||||
|
@ -147,7 +151,7 @@ kube_state_metrics_watch_total{resource="*v1beta1.Ingress",result="success"} 1
|
|||
|
||||
kube-state-metrics also exposes some http request metrics, examples of those are:
|
||||
|
||||
```
|
||||
```prometheus
|
||||
http_request_duration_seconds_bucket{handler="metrics",method="get",le="2.5"} 30
|
||||
http_request_duration_seconds_bucket{handler="metrics",method="get",le="5"} 30
|
||||
http_request_duration_seconds_bucket{handler="metrics",method="get",le="10"} 30
|
||||
|
@ -158,20 +162,20 @@ http_request_duration_seconds_count{handler="metrics",method="get"} 30
|
|||
|
||||
kube-state-metrics also exposes build and configuration metrics:
|
||||
|
||||
```
|
||||
```prometheus
|
||||
kube_state_metrics_build_info{branch="main",goversion="go1.15.3",revision="6c9d775d",version="v2.0.0-beta"} 1
|
||||
kube_state_metrics_shard_ordinal{shard_ordinal="0"} 0
|
||||
kube_state_metrics_total_shards 1
|
||||
```
|
||||
|
||||
`kube_state_metrics_build_info` is used to expose version and other build information. For more usage about the info pattern,
|
||||
please check the blog post [here](https://www.robustperception.io/exposing-the-software-version-to-prometheus).
|
||||
please check this [blog post](https://www.robustperception.io/exposing-the-software-version-to-prometheus).
|
||||
Sharding metrics expose `--shard` and `--total-shards` flags and can be used to validate
|
||||
run-time configuration, see [`/examples/prometheus-alerting-rules`](./examples/prometheus-alerting-rules).
|
||||
|
||||
kube-state-metrics also exposes metrics about it config file and the Custom Resource State config file:
|
||||
|
||||
```
|
||||
```prometheus
|
||||
kube_state_metrics_config_hash{filename="crs.yml",type="customresourceconfig"} 2.38272279311849e+14
|
||||
kube_state_metrics_config_hash{filename="config.yml",type="config"} 2.65285922340846e+14
|
||||
kube_state_metrics_last_config_reload_success_timestamp_seconds{filename="crs.yml",type="customresourceconfig"} 1.6704882592037103e+09
|
||||
|
@ -198,7 +202,7 @@ Note that if CPU limits are set too low, kube-state-metrics' internal queues wil
|
|||
|
||||
In a 100 node cluster scaling test the latency numbers were as follows:
|
||||
|
||||
```
|
||||
```text
|
||||
"Perc50": 259615384 ns,
|
||||
"Perc90": 475000000 ns,
|
||||
"Perc99": 906666666 ns.
|
||||
|
@ -262,7 +266,7 @@ Each kube-state-metrics pod uses FieldSelector (spec.nodeName) to watch/list pod
|
|||
|
||||
A daemonset kube-state-metrics example:
|
||||
|
||||
```
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
spec:
|
||||
|
@ -284,7 +288,7 @@ spec:
|
|||
|
||||
To track metrics for unassigned pods, you need to add an additional deployment and set `--track-unscheduled-pods`, as shown in the following example:
|
||||
|
||||
```
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
|
@ -304,8 +308,8 @@ Other metrics can be sharded via [Horizontal sharding](#horizontal-sharding).
|
|||
|
||||
Install this project to your `$GOPATH` using `go get`:
|
||||
|
||||
```
|
||||
go get k8s.io/kube-state-metrics
|
||||
```bash
|
||||
go get k8s.io/kube-state-metrics/v2
|
||||
```
|
||||
|
||||
#### Building the Docker container
|
||||
|
@ -313,7 +317,7 @@ go get k8s.io/kube-state-metrics
|
|||
Simply run the following command in this root folder, which will create a
|
||||
self-contained, statically-linked binary and build a Docker image:
|
||||
|
||||
```
|
||||
```bash
|
||||
make container
|
||||
```
|
||||
|
||||
|
@ -336,7 +340,7 @@ To have Prometheus discover kube-state-metrics instances it is advised to create
|
|||
|
||||
**Note:** Google Kubernetes Engine (GKE) Users - GKE has strict role permissions that will prevent the kube-state-metrics roles and role bindings from being created. To work around this, you can give your GCP identity the cluster-admin role by running the following one-liner:
|
||||
|
||||
```
|
||||
```bash
|
||||
kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud info --format='value(config.account)')
|
||||
```
|
||||
|
||||
|
@ -411,14 +415,14 @@ When developing, test a metric dump against your local Kubernetes cluster by run
|
|||
|
||||
> Users can override the apiserver address in KUBE-CONFIG file with `--apiserver` command line.
|
||||
|
||||
```
|
||||
```bash
|
||||
go install
|
||||
kube-state-metrics --port=8080 --telemetry-port=8081 --kubeconfig=<KUBE-CONFIG> --apiserver=<APISERVER>
|
||||
```
|
||||
|
||||
Then curl the metrics endpoint
|
||||
|
||||
```
|
||||
```bash
|
||||
curl localhost:8080/metrics
|
||||
```
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ are deleted they are no longer visible on the `/metrics` endpoint.
|
|||
* [Resource group version compatibility](#resource-group-version-compatibility)
|
||||
* [Container Image](#container-image)
|
||||
* [Metrics Documentation](#metrics-documentation)
|
||||
* [ECMAScript regular expression support for allow and deny lists](#ecmascript-regular-expression-support-for-allow-and-deny-lists)
|
||||
* [Conflict resolution in label names](#conflict-resolution-in-label-names)
|
||||
* [Kube-state-metrics self metrics](#kube-state-metrics-self-metrics)
|
||||
* [Resource recommendation](#resource-recommendation)
|
||||
|
@ -67,9 +68,8 @@ are deleted they are no longer visible on the `/metrics` endpoint.
|
|||
#### Kubernetes Version
|
||||
|
||||
kube-state-metrics uses [`client-go`](https://github.com/kubernetes/client-go) to talk with
|
||||
Kubernetes clusters. The supported Kubernetes cluster version is determined by `client-go`.
|
||||
The compatibility matrix for client-go and Kubernetes cluster can be found
|
||||
[here](https://github.com/kubernetes/client-go#compatibility-matrix).
|
||||
Kubernetes clusters. The supported Kubernetes cluster version is determined by
|
||||
[`client-go`](https://github.com/kubernetes/client-go#compatibility-matrix).
|
||||
All additional compatibility is only best effort, or happens to still/already be supported.
|
||||
|
||||
#### Compatibility matrix
|
||||
|
@ -98,7 +98,7 @@ The latest container image can be found at:
|
|||
{{ (index . (math.Sub (len .) 2)).version -}}
|
||||
{{ end }}
|
||||
* `registry.k8s.io/kube-state-metrics/kube-state-metrics:{{ template "get-latest-release" (datasource "config").compat }}` (arch: `amd64`, `arm`, `arm64`, `ppc64le` and `s390x`)
|
||||
* View all multi-architecture images at [here](https://explore.ggcr.dev/?image=registry.k8s.io%2Fkube-state-metrics%2Fkube-state-metrics:{{ template "get-latest-release" (datasource "config").compat -}})
|
||||
* [Multi-architecture images](https://explore.ggcr.dev/?image=registry.k8s.io%2Fkube-state-metrics%2Fkube-state-metrics:{{ template "get-latest-release" (datasource "config").compat -}})
|
||||
|
||||
### Metrics Documentation
|
||||
|
||||
|
@ -130,6 +130,10 @@ e.g. by standardizing Kubernetes labels using an
|
|||
[Admission Webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/)
|
||||
that ensures that there are no possible conflicts.
|
||||
|
||||
#### ECMAScript regular expression support for allow and deny lists
|
||||
|
||||
Starting from [#2616](https://github.com/kubernetes/kube-state-metrics/pull/2616/files), kube-state-metrics supports ECMAScript's `regexp` for allow and deny lists. This was incorporated as a workaround for the limitations of the `regexp` package in Go, which does not support lookarounds due to their non-linear time complexity. Please note that while lookarounds are now supported for allow and deny lists, regular expressions' evaluation time is capped at a minute to prevent performance issues.
|
||||
|
||||
### Kube-state-metrics self metrics
|
||||
|
||||
kube-state-metrics exposes its own general process metrics under `--telemetry-host` and `--telemetry-port` (default 8081).
|
||||
|
@ -140,7 +144,7 @@ at the logs of kube-state-metrics.
|
|||
|
||||
Example of the above mentioned metrics:
|
||||
|
||||
```
|
||||
```prometheus
|
||||
kube_state_metrics_list_total{resource="*v1.Node",result="success"} 1
|
||||
kube_state_metrics_list_total{resource="*v1.Node",result="error"} 52
|
||||
kube_state_metrics_watch_total{resource="*v1beta1.Ingress",result="success"} 1
|
||||
|
@ -148,7 +152,7 @@ kube_state_metrics_watch_total{resource="*v1beta1.Ingress",result="success"} 1
|
|||
|
||||
kube-state-metrics also exposes some http request metrics, examples of those are:
|
||||
|
||||
```
|
||||
```prometheus
|
||||
http_request_duration_seconds_bucket{handler="metrics",method="get",le="2.5"} 30
|
||||
http_request_duration_seconds_bucket{handler="metrics",method="get",le="5"} 30
|
||||
http_request_duration_seconds_bucket{handler="metrics",method="get",le="10"} 30
|
||||
|
@ -159,20 +163,20 @@ http_request_duration_seconds_count{handler="metrics",method="get"} 30
|
|||
|
||||
kube-state-metrics also exposes build and configuration metrics:
|
||||
|
||||
```
|
||||
```prometheus
|
||||
kube_state_metrics_build_info{branch="main",goversion="go1.15.3",revision="6c9d775d",version="v2.0.0-beta"} 1
|
||||
kube_state_metrics_shard_ordinal{shard_ordinal="0"} 0
|
||||
kube_state_metrics_total_shards 1
|
||||
```
|
||||
|
||||
`kube_state_metrics_build_info` is used to expose version and other build information. For more usage about the info pattern,
|
||||
please check the blog post [here](https://www.robustperception.io/exposing-the-software-version-to-prometheus).
|
||||
please check this [blog post](https://www.robustperception.io/exposing-the-software-version-to-prometheus).
|
||||
Sharding metrics expose `--shard` and `--total-shards` flags and can be used to validate
|
||||
run-time configuration, see [`/examples/prometheus-alerting-rules`](./examples/prometheus-alerting-rules).
|
||||
|
||||
kube-state-metrics also exposes metrics about it config file and the Custom Resource State config file:
|
||||
|
||||
```
|
||||
```prometheus
|
||||
kube_state_metrics_config_hash{filename="crs.yml",type="customresourceconfig"} 2.38272279311849e+14
|
||||
kube_state_metrics_config_hash{filename="config.yml",type="config"} 2.65285922340846e+14
|
||||
kube_state_metrics_last_config_reload_success_timestamp_seconds{filename="crs.yml",type="customresourceconfig"} 1.6704882592037103e+09
|
||||
|
@ -199,7 +203,7 @@ Note that if CPU limits are set too low, kube-state-metrics' internal queues wil
|
|||
|
||||
In a 100 node cluster scaling test the latency numbers were as follows:
|
||||
|
||||
```
|
||||
```text
|
||||
"Perc50": 259615384 ns,
|
||||
"Perc90": 475000000 ns,
|
||||
"Perc99": 906666666 ns.
|
||||
|
@ -263,7 +267,7 @@ Each kube-state-metrics pod uses FieldSelector (spec.nodeName) to watch/list pod
|
|||
|
||||
A daemonset kube-state-metrics example:
|
||||
|
||||
```
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
spec:
|
||||
|
@ -285,7 +289,7 @@ spec:
|
|||
|
||||
To track metrics for unassigned pods, you need to add an additional deployment and set `--track-unscheduled-pods`, as shown in the following example:
|
||||
|
||||
```
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
|
@ -305,8 +309,8 @@ Other metrics can be sharded via [Horizontal sharding](#horizontal-sharding).
|
|||
|
||||
Install this project to your `$GOPATH` using `go get`:
|
||||
|
||||
```
|
||||
go get k8s.io/kube-state-metrics
|
||||
```bash
|
||||
go get k8s.io/kube-state-metrics/v2
|
||||
```
|
||||
|
||||
#### Building the Docker container
|
||||
|
@ -314,7 +318,7 @@ go get k8s.io/kube-state-metrics
|
|||
Simply run the following command in this root folder, which will create a
|
||||
self-contained, statically-linked binary and build a Docker image:
|
||||
|
||||
```
|
||||
```bash
|
||||
make container
|
||||
```
|
||||
|
||||
|
@ -337,7 +341,7 @@ To have Prometheus discover kube-state-metrics instances it is advised to create
|
|||
|
||||
**Note:** Google Kubernetes Engine (GKE) Users - GKE has strict role permissions that will prevent the kube-state-metrics roles and role bindings from being created. To work around this, you can give your GCP identity the cluster-admin role by running the following one-liner:
|
||||
|
||||
```
|
||||
```bash
|
||||
kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud info --format='value(config.account)')
|
||||
```
|
||||
|
||||
|
@ -412,14 +416,14 @@ When developing, test a metric dump against your local Kubernetes cluster by run
|
|||
|
||||
> Users can override the apiserver address in KUBE-CONFIG file with `--apiserver` command line.
|
||||
|
||||
```
|
||||
```bash
|
||||
go install
|
||||
kube-state-metrics --port=8080 --telemetry-port=8081 --kubeconfig=<KUBE-CONFIG> --apiserver=<APISERVER>
|
||||
```
|
||||
|
||||
Then curl the metrics endpoint
|
||||
|
||||
```
|
||||
```bash
|
||||
curl localhost:8080/metrics
|
||||
```
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ Maintaining the release branches for older minor releases happens on a best effo
|
|||
* Only include user relevant changes
|
||||
* Entries in the [`CHANGELOG.md`](CHANGELOG.md) are meant to be in this order:
|
||||
|
||||
```
|
||||
```text
|
||||
[CHANGE]
|
||||
[FEATURE]
|
||||
[ENHANCEMENT]
|
||||
|
|
|
@ -13,3 +13,4 @@
|
|||
dgrisonnet
|
||||
mrueg
|
||||
rexagod
|
||||
CatherineF-dev
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
# The purpose of this config is to keep all versions in a single file and make them machine accessible
|
||||
|
||||
# Marks the latest release
|
||||
version: "2.15.0"
|
||||
version: "2.16.0"
|
||||
|
||||
# List at max 5 releases here + the main branch
|
||||
compat:
|
||||
- version: "v2.11.0"
|
||||
kubernetes: "1.28"
|
||||
- version: "v2.12.0"
|
||||
kubernetes: "1.29"
|
||||
- version: "v2.13.0"
|
||||
|
@ -15,5 +13,7 @@ compat:
|
|||
kubernetes: "1.31"
|
||||
- version: "v2.15.0"
|
||||
kubernetes: "1.32"
|
||||
- version: "main"
|
||||
- version: "v2.16.0"
|
||||
kubernetes: "1.32"
|
||||
- version: "main"
|
||||
kubernetes: "1.33"
|
||||
|
|
|
@ -79,13 +79,13 @@ can be used to extend single metrics output.
|
|||
This example adds `label_release` to the set of default labels of the `kube_pod_status_ready` metric
|
||||
and allows you select or group the metrics by Helm release label:
|
||||
|
||||
```
|
||||
```promql
|
||||
kube_pod_status_ready * on (namespace, pod) group_left(label_release) kube_pod_labels
|
||||
```
|
||||
|
||||
Another useful example would be to query the memory usage of pods by its `phase`, such as `Running`:
|
||||
|
||||
```
|
||||
```promql
|
||||
sum(kube_pod_container_resource_requests{resource="memory"}) by (namespace, pod, node)
|
||||
* on (namespace, pod) group_left() (sum(kube_pod_status_phase{phase="Running"}) by (pod, namespace) == 1)
|
||||
```
|
||||
|
@ -97,3 +97,49 @@ See [Custom Resource State Metrics](metrics/extend/customresourcestate-metrics.m
|
|||
## CLI Arguments
|
||||
|
||||
Additionally, options for `kube-state-metrics` can be passed when executing as a CLI, or in a kubernetes / openshift environment. More information can be found here: [CLI Arguments](developer/cli-arguments.md)
|
||||
|
||||
## Protecting /metrics endpoints
|
||||
|
||||
Kube-State-Metrics' metrics can contain sensitive information about the state of the cluster, which you as an operator might want to additionally protect from unauthorized access.
|
||||
In order to achieve this, you need to enable the `--auth-filter` flag on kube-state-metrics.
|
||||
With this, kube-state-metrics will only accept authenticated and authorized requests to the /metrics endpoints.
|
||||
Kube-state-metrics uses Kubernetes' RBAC mechanisms for this, so this means that every scrape will trigger a request against the API Server for TokenReview and SubjectAccessReview.
|
||||
The clients scraping the endpoint, need to use a token which can be provided by a ServiceAccount that can be set up the following way:
|
||||
|
||||
A ClusterRole providing access like this:
|
||||
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: metrics-reader
|
||||
rules:
|
||||
- nonResourceURLs:
|
||||
- "/metrics"
|
||||
verbs:
|
||||
- get
|
||||
```
|
||||
|
||||
and a matching ClusterRoleBinding
|
||||
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: metrics-reader-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: metrics-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: YOUR_SERVICE_ACCOUNT
|
||||
namespace: NAMESPACE_OF_THE_SERVICE_ACCOUNT
|
||||
```
|
||||
|
||||
Your client can then use either this ServiceAccount to gather metrics or you can create a token, that can be used to fetch data like this:
|
||||
|
||||
```bash
|
||||
TOKEN=$(kubectl create token YOUR_SERVICE_ACCOUNT -n NAMESPACE_OF_THE_SERVICE_ACCOUNT)
|
||||
curl -H "Authorization: Bearer $TOKEN" KUBE_STATE_METRICS_URL:8080/metrics
|
||||
```
|
||||
|
|
|
@ -58,7 +58,7 @@ properties in memory via the Kubernetes client-go cache, use a map, addressable
|
|||
by the Kubernetes object uuid, containing all time series of that object as a
|
||||
single multi-line string.
|
||||
|
||||
```
|
||||
```go
|
||||
var cache = map[uuid][]byte{}
|
||||
```
|
||||
|
||||
|
@ -76,7 +76,7 @@ On a scrape request on the /metrics endpoint, kube-state-metrics iterates over
|
|||
the cache map and concatenates all time series string blobs into a single
|
||||
string, which is finally passed on as a response.
|
||||
|
||||
```
|
||||
```text
|
||||
+---------------+ +-----------+ +---------------+ +-------------------+
|
||||
| pod_reflector | | pod_store | | pod_collector | | metrics_endpoint |
|
||||
+---------------+ +-----------+ +---------------+ +-------------------+
|
||||
|
@ -116,7 +116,7 @@ string, which is finally passed on as a response.
|
|||
|
||||
Build via [text-diagram](http://weidagang.github.io/text-diagram/)
|
||||
|
||||
```
|
||||
```text
|
||||
object pod_reflector pod_store pod_collector metrics_endpoint
|
||||
|
||||
note left of pod_reflector: new pod p1
|
||||
|
@ -163,8 +163,6 @@ pod_collector -> metrics_endpoint: concat(metrics)
|
|||
of saving unstructured strings inside the cache map, one can structure them,
|
||||
using pointers to deduplicate e.g. metric names.
|
||||
|
||||
* ...
|
||||
|
||||
* Kube-state-metrics does not make use of all properties of all Kubernetes
|
||||
objects. Instead of unmarshalling unused properties, their json struct tags or
|
||||
their Protobuf representation could be removed.
|
||||
|
|
|
@ -41,9 +41,10 @@ Flags:
|
|||
--add_dir_header If true, adds the file directory to the header of the log messages
|
||||
--alsologtostderr log to standard error as well as files (no effect when -logtostderr=true)
|
||||
--apiserver string The URL of the apiserver to use as a master
|
||||
--auth-filter If true, requires authentication and authorization through Kubernetes API to access metrics endpoints
|
||||
--auto-gomemlimit Automatically set GOMEMLIMIT to match container or system memory limit. (experimental)
|
||||
--auto-gomemlimit-ratio float The ratio of reserved GOMEMLIMIT memory to the detected maximum container or system memory. (experimental) (default 0.9)
|
||||
--config string Path to the kube-state-metrics options config file
|
||||
--config string Path to the kube-state-metrics options config YAML file. If this flag is set, the flags defined in the file override the command line flags.
|
||||
--custom-resource-state-config string Inline Custom Resource State Metrics config YAML (experimental)
|
||||
--custom-resource-state-config-file string Path to a Custom Resource State Metrics config file (experimental)
|
||||
--custom-resource-state-only Only provide Custom Resource State metrics (experimental)
|
||||
|
@ -56,14 +57,15 @@ Flags:
|
|||
--log_file string If non-empty, use this log file (no effect when -logtostderr=true)
|
||||
--log_file_max_size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
|
||||
--logtostderr log to standard error instead of files (default true)
|
||||
--metric-allowlist string Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.
|
||||
--metric-allowlist string Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or *ECMAScript-based* regex patterns. The allowlist and denylist are mutually exclusive.
|
||||
--metric-annotations-allowlist string Comma-separated list of Kubernetes annotations keys that will be used in the resource' labels metric. By default the annotations metrics are not exposed. To include them, provide a list of resource names in their plural form and Kubernetes annotation keys you would like to allow for them (Example: '=namespaces=[kubernetes.io/team,...],pods=[kubernetes.io/team],...)'. A single '*' can be provided per resource instead to allow any annotations, but that has severe performance implications (Example: '=pods=[*]').
|
||||
--metric-denylist string Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.
|
||||
--metric-denylist string Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or *ECMAScript-based* regex patterns. The allowlist and denylist are mutually exclusive.
|
||||
--metric-labels-allowlist string Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the labels metrics are not exposed. To include them, provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]'). Additionally, an asterisk (*) can be provided as a key, which will resolve to all resources, i.e., assuming '--resources=deployments,pods', '=*=[*]' will resolve to '=deployments=[*],pods=[*]'.
|
||||
--metric-opt-in-list string Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists
|
||||
--namespaces string Comma-separated list of namespaces to be enabled. Defaults to ""
|
||||
--namespaces-denylist string Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used.
|
||||
--node string Name of the node that contains the kube-state-metrics pod. Most likely it should be passed via the downward API. This is used for daemonset sharding. Only available for resources (pod metrics) that support spec.nodeName fieldSelector. This is experimental.
|
||||
--object-limit int The total number of objects to list per resource from the API Server. (experimental)
|
||||
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true)
|
||||
--pod string Name of the pod that contains the kube-state-metrics container. When set, it is expected that --pod and --pod-namespace are both set. Most likely this should be passed via the downward API. This is used for auto-detecting sharding. If set, this has preference over statically configured sharding. This is experimental, it may be removed without notice.
|
||||
--pod-namespace string Name of the namespace of the pod specified by --pod. When set, it is expected that --pod and --pod-namespace are both set. Most likely this should be passed via the downward API. This is used for auto-detecting sharding. If set, this has preference over statically configured sharding. This is experimental, it may be removed without notice.
|
||||
|
|
|
@ -269,7 +269,7 @@ kube_customresource_ref_info{customresource_group="myteam.io", customresource_ki
|
|||
|
||||
For example in VPA we have above attributes and we want to have a same metrics for both CPU and Memory, you can use below config:
|
||||
|
||||
```
|
||||
```yaml
|
||||
kind: CustomResourceStateMetrics
|
||||
spec:
|
||||
resources:
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# Ingress Metrics
|
||||
|
||||
| Metric name | Metric type | Description | Labels/tags | Status |
|
||||
| -------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
|
||||
| kube_ingress_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `annotation_INGRESS_ANNOTATION`=<ANNOTATION_LABEL> | EXPERIMENTAL |
|
||||
| kube_ingress_info | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `ingressclass`=<ingress-class> or `_default` if not set | STABLE |
|
||||
| kube_ingress_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `label_INGRESS_LABEL`=<INGRESS_LABEL> | STABLE |
|
||||
| kube_ingress_created | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> | STABLE |
|
||||
| kube_ingress_metadata_resource_version | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> | EXPERIMENTAL |
|
||||
| kube_ingress_path | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `host`=<ingress-host> <br> `path`=<ingress-path> <br><i> If path served by Service Backend</i> <br> `service_name`=<service name for the path> <br> `service_port`=<service port for the path><br><i> If path served by Resource Backend</i><br> `resource_api_group`=<resource backend api group> <br> `resource_kind`=<resource backend kind> <br> `resource_name`=<resource backend name> | STABLE |
|
||||
| kube_ingress_tls | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `tls_host`=<tls hostname> <br> `secret`=<tls secret name> | STABLE |
|
||||
| Metric name | Metric type | Description | Labels/tags | Status |
|
||||
| -------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------------ |
|
||||
| kube_ingress_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `annotation_INGRESS_ANNOTATION`=<ANNOTATION_LABEL> | EXPERIMENTAL |
|
||||
| kube_ingress_info | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `ingressclass`=<ingress-class> or `_default` if not set | STABLE |
|
||||
| kube_ingress_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `label_INGRESS_LABEL`=<INGRESS_LABEL> | STABLE |
|
||||
| kube_ingress_created | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> | STABLE |
|
||||
| kube_ingress_metadata_resource_version | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> | EXPERIMENTAL |
|
||||
| kube_ingress_path | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `host`=<ingress-host> <br> `path`=<ingress-path> <br> `path_type`=<ingress-path type> <br> If path served by Service Backend <br> `service_name`=<service name for the path> <br> `service_port`=<service port for the path> <br> If path served by Resource Backend <br> `resource_api_group`=<resource backend api group> <br> `resource_kind`=<resource backend kind> <br> `resource_name`=<resource backend name> | STABLE |
|
||||
| kube_ingress_tls | Gauge | | `ingress`=<ingress-name> <br> `namespace`=<ingress-namespace> <br> `tls_host`=<tls hostname> <br> `secret`=<tls secret name> | STABLE |
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
| kube_persistentvolume_annotations | Gauge | Kubernetes annotations converted to Prometheus labels controlled via [--metric-annotations-allowlist](../../developer/cli-arguments.md) | | `persistentvolume`=<persistentvolume-name> <br> `annotation_PERSISTENTVOLUME_ANNOTATION`=<PERSISTENTVOLUME_ANNOTATION> | EXPERIMENTAL |
|
||||
| kube_persistentvolume_capacity_bytes | Gauge | | | `persistentvolume`=<pv-name> | STABLE |
|
||||
| kube_persistentvolume_status_phase | Gauge | | | `persistentvolume`=<pv-name> <br>`phase`=<Bound\|Failed\|Pending\|Available\|Released> | STABLE |
|
||||
| kube_persistentvolume_claim_ref | Gauge | | | `persistentvolume`=<pv-name> <br>`claim_namespace`=<<namespace>> <br>`name`=<<name>> | STABLE |
|
||||
| kube_persistentvolume_claim_ref | Gauge | | | `persistentvolume`=<pv-name> <br>`claim_namespace`=`<namespace>`; <br>`name`=`<name>`; | STABLE |
|
||||
| kube_persistentvolume_labels | Gauge | Kubernetes labels converted to Prometheus labels controlled via [--metric-labels-allowlist](../../developer/cli-arguments.md) | | `persistentvolume`=<persistentvolume-name> <br> `label_PERSISTENTVOLUME_LABEL`=<PERSISTENTVOLUME_LABEL> | STABLE |
|
||||
| kube_persistentvolume_info | Gauge | Information about Persistent Volumes | | `persistentvolume`=<pv-name> <br> `storageclass`=<storageclass-name> <br> `gce_persistent_disk_name`=<pd-name> <br> `host_path`=<path-of-a-host-volume> <br> `host_path_type`=<host-mount-type> <br> `ebs_volume_id`=<ebs-volume-id> <br> `azure_disk_name`=<azure-disk-name> <br> `fc_wwids`=<fc-wwids-comma-separated> <br> `fc_lun`=<fc-lun> <br> `fc_target_wwns`=<fc-target-wwns-comma-separated> <br> `iscsi_target_portal`=<iscsi-target-portal> <br> `iscsi_iqn`=<iscsi-iqn> <br> `iscsi_lun`=<iscsi-lun> <br> `iscsi_initiator_name`=<iscsi-initiator-name> <br> `local_path`=<path-of-a-local-volume> <br> `local_fs`=<local-volume-fs-type> <br> `nfs_server`=<nfs-server> <br> `nfs_path`=<nfs-path> <br> `csi_driver`=<csi-driver> <br> `csi_volume_handle`=<csi-volume-handle> | STABLE |
|
||||
| kube_persistentvolume_info | Gauge | Information about Persistent Volumes | | `persistentvolume`=<pv-name> <br> `storageclass`=<storageclass-name> <br> `gce_persistent_disk_name`=<pd-name> <br> `host_path`=<path-of-a-host-volume> <br> `host_path_type`=<host-mount-type> <br> `ebs_volume_id`=<ebs-volume-id> <br> `azure_disk_name`=<azure-disk-name> <br> `fc_wwids`=<fc-wwids-comma-separated> <br> `fc_lun`=<fc-lun> <br> `fc_target_wwns`=<fc-target-wwns-comma-separated> <br> `iscsi_target_portal`=<iscsi-target-portal> <br> `iscsi_iqn`=<iscsi-iqn> <br> `iscsi_lun`=<iscsi-lun> <br> `iscsi_initiator_name`=<iscsi-initiator-name> <br> `local_path`=<path-of-a-local-volume> <br> `local_fs`=<local-volume-fs-type> <br> `nfs_server`=<nfs-server> <br> `nfs_path`=<nfs-path> <br> `csi_driver`=<csi-driver> <br> `csi_volume_handle`=<csi-volume-handle> <br> `reclaim_policy`=<reclaim-policy> | STABLE |
|
||||
| kube_persistentvolume_created | Gauge | Unix creation timestamp | seconds | `persistentvolume`=<persistentvolume-name> <br> | EXPERIMENTAL |
|
||||
| kube_persistentvolume_deletion_timestamp | Gauge | Unix deletion timestamp | seconds | `persistentvolume`=<persistentvolume-name> <br> | EXPERIMENTAL |
|
||||
| kube_persistentvolume_csi_attributes | Gauge | CSI attributes of the Persistent Volume, disabled by default, manage with [--metric-opt-in-list](../../developer/cli-arguments.md)) | | `persistentvolume`=<persistentvolume-name> <br> `csi_mounter`=<csi-mounter> <br> `csi_map_options`=<csi-map-options> | EXPERIMENTAL |
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
| kube_deployment_status_replicas_unavailable | Gauge | | `deployment`=<deployment-name> <br> `namespace`=<deployment-namespace> | STABLE |
|
||||
| kube_deployment_status_replicas_updated | Gauge | | `deployment`=<deployment-name> <br> `namespace`=<deployment-namespace> | STABLE |
|
||||
| kube_deployment_status_observed_generation | Gauge | | `deployment`=<deployment-name> <br> `namespace`=<deployment-namespace> | STABLE |
|
||||
| kube_deployment_status_condition | Gauge | | `deployment`=<deployment-name> <br> `namespace`=<deployment-namespace> <br> `condition`=<deployment-condition> <br> `status`=<true\|false\|unknown> | STABLE |
|
||||
| kube_deployment_status_condition | Gauge | | `deployment`=<deployment-name> <br> `namespace`=<deployment-namespace> <br> `reason`=<deployment-transition-reason> <br> `condition`=<deployment-condition> <br> `status`=<true\|false\|unknown> | STABLE |
|
||||
| kube_deployment_spec_replicas | Gauge | | `deployment`=<deployment-name> <br> `namespace`=<deployment-namespace> | STABLE |
|
||||
| kube_deployment_spec_paused | Gauge | | `deployment`=<deployment-name> <br> `namespace`=<deployment-namespace> | STABLE |
|
||||
| kube_deployment_spec_strategy_rollingupdate_max_unavailable | Gauge | | `deployment`=<deployment-name> <br> `namespace`=<deployment-namespace> | STABLE |
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
| kube_pod_status_reason | Gauge | The pod status reasons | | `pod`=<pod-name> <br> `namespace`=<pod-namespace> <br> `reason`=<Evicted\|NodeAffinity\|NodeLost\|Shutdown\|UnexpectedAdmissionError> <br> `uid`=<pod-uid> | EXPERIMENTAL | - |
|
||||
| kube_pod_status_scheduled_time | Gauge | Unix timestamp when pod moved into scheduled status | seconds | `pod`=<pod-name> <br> `namespace`=<pod-namespace> <br> `uid`=<pod-uid> | STABLE | - |
|
||||
| kube_pod_status_unschedulable | Gauge | Describes the unschedulable status for the pod | | `pod`=<pod-name> <br> `namespace`=<pod-namespace> <br> `uid`=<pod-uid> | STABLE | - |
|
||||
| kube_pod_status_unscheduled_time | Gauge | Unix timestamp when pod moved into unscheduled status | seconds | `pod`=<pod-name> <br> `namespace`=<pod-namespace> <br> `uid`=<pod-uid> | EXPERIMENTAL | - |
|
||||
| kube_pod_tolerations | Gauge | Information about the pod tolerations | | `pod`=<pod-name> <br> `namespace`=<pod-namespace> <br> `uid`=<pod-uid> <br> `key`=<toleration-key> <br> `operator`=<toleration-operator> <br> `value`=<toleration-value> <br> `effect`=<toleration-effect> `toleration_seconds`=<toleration-seconds> | EXPERIMENTAL | - |
|
||||
| kube_pod_service_account | Gauge | The service account for a pod | | `pod`=<pod-name> <br> `namespace`=<pod-namespace> <br> `uid`=<pod-uid> <br> `service_account`=<service_account> | EXPERIMENTAL | - |
|
||||
| kube_pod_scheduler | Gauge | The scheduler for a pod | | `pod`=<pod-name> <br> `namespace`=<pod-namespace> <br> `uid`=<pod-uid> <br> `name`=<scheduler-name> | EXPERIMENTAL | - |
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
rules:
|
||||
- apiGroups:
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
rules:
|
||||
|
|
|
@ -5,6 +5,6 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
@ -18,7 +18,7 @@ spec:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
spec:
|
||||
automountServiceAccountToken: true
|
||||
containers:
|
||||
|
@ -34,7 +34,7 @@ spec:
|
|||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.15.0
|
||||
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.16.0
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
rules:
|
||||
- apiGroups:
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics-shard
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics-shard
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics-shard
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics-shard
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
@ -16,7 +16,7 @@ spec:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics-shard
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
spec:
|
||||
automountServiceAccountToken: true
|
||||
containers:
|
||||
|
@ -29,7 +29,7 @@ spec:
|
|||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: spec.nodeName
|
||||
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.15.0
|
||||
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.16.0
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics-unscheduled-pods-fetching
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics-unscheduled-pods-fetching
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics-unscheduled-pods-fetching
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics-unscheduled-pods-fetching
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
@ -17,14 +17,14 @@ spec:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics-unscheduled-pods-fetching
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
spec:
|
||||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- args:
|
||||
- --resources=pods
|
||||
- --track-unscheduled-pods
|
||||
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.15.0
|
||||
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.16.0
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
@ -17,13 +17,13 @@ spec:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
spec:
|
||||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- args:
|
||||
- --resources=certificatesigningrequests,configmaps,cronjobs,daemonsets,deployments,endpoints,horizontalpodautoscalers,ingresses,jobs,leases,limitranges,mutatingwebhookconfigurations,namespaces,networkpolicies,nodes,persistentvolumeclaims,persistentvolumes,poddisruptionbudgets,replicasets,replicationcontrollers,resourcequotas,secrets,services,statefulsets,storageclasses,validatingwebhookconfigurations,volumeattachments
|
||||
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.15.0
|
||||
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.16.0
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
|
|
|
@ -5,6 +5,6 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
rules:
|
||||
- apiGroups:
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
@ -17,11 +17,11 @@ spec:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
spec:
|
||||
automountServiceAccountToken: true
|
||||
containers:
|
||||
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.15.0
|
||||
- image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.16.0
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /livez
|
||||
|
|
|
@ -5,6 +5,6 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
|
|
|
@ -4,7 +4,7 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/component: exporter
|
||||
app.kubernetes.io/name: kube-state-metrics
|
||||
app.kubernetes.io/version: 2.15.0
|
||||
app.kubernetes.io/version: 2.16.0
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
||||
spec:
|
||||
|
|
262
go.mod
262
go.mod
|
@ -1,96 +1,264 @@
|
|||
module k8s.io/kube-state-metrics/v2
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/KimMachineGun/automemlimit v0.7.0
|
||||
github.com/KimMachineGun/automemlimit v0.7.4
|
||||
github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/dlclark/regexp2 v1.11.5
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/go-logr/logr v1.4.3
|
||||
github.com/gobuffalo/flect v1.0.3
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/oklog/run v1.1.0
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/prometheus/client_model v0.6.1
|
||||
github.com/prometheus/common v0.62.0
|
||||
github.com/prometheus/exporter-toolkit v0.13.2
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/oklog/run v1.2.0
|
||||
github.com/prometheus/client_golang v1.23.0
|
||||
github.com/prometheus/client_model v0.6.2
|
||||
github.com/prometheus/common v0.65.0
|
||||
github.com/prometheus/exporter-toolkit v0.14.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/viper v1.20.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.32.1
|
||||
k8s.io/apimachinery v0.32.1
|
||||
k8s.io/client-go v0.32.1
|
||||
k8s.io/component-base v0.32.1
|
||||
k8s.io/api v0.33.3
|
||||
k8s.io/apimachinery v0.33.3
|
||||
k8s.io/client-go v0.33.3
|
||||
k8s.io/component-base v0.33.3
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/sample-controller v0.32.1
|
||||
k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078
|
||||
k8s.io/sample-controller v0.33.3
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
||||
sigs.k8s.io/controller-runtime v0.21.0
|
||||
sigs.k8s.io/yaml v1.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.19.1 // indirect
|
||||
cloud.google.com/go v0.116.0 // indirect
|
||||
cloud.google.com/go/auth v0.13.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.2.2 // indirect
|
||||
cloud.google.com/go/monitoring v1.21.2 // indirect
|
||||
cloud.google.com/go/storage v1.49.0 // indirect
|
||||
cuelang.org/go v0.11.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.0.0 // indirect
|
||||
github.com/Shopify/ejson v1.5.3 // indirect
|
||||
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.55.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.47 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssm v1.56.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect
|
||||
github.com/aws/smithy-go v1.22.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/brancz/gojsontoyaml v0.1.0 // indirect
|
||||
github.com/campoy/embedmd v1.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.9 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dustin/gojson v0.0.0-20160307161227-2e71ec9dd5ad // indirect
|
||||
github.com/elliotchance/orderedmap/v2 v2.2.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.13.1 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/cel-go v0.23.2 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-jsonnet v0.20.0 // indirect
|
||||
github.com/google/s2a-go v0.1.8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/google/wire v0.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gosimple/slug v1.14.0 // indirect
|
||||
github.com/gosimple/unidecode v1.0.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect
|
||||
github.com/hack-pad/hackpadfs v0.2.4 // indirect
|
||||
github.com/hairyhenderson/go-fsimpl v0.2.1 // indirect
|
||||
github.com/hairyhenderson/go-git/v5 v5.12.1-0.20240530140403-1b868a7b8a3c // indirect
|
||||
github.com/hairyhenderson/gomplate/v4 v4.3.0 // indirect
|
||||
github.com/hairyhenderson/toml v0.4.2-0.20210923231440-40456b8e66cf // indirect
|
||||
github.com/hairyhenderson/xignore v0.3.3-0.20230403012150-95fe86932830 // indirect
|
||||
github.com/hairyhenderson/yaml v0.0.0-20220618171115-2d35fca545ce // indirect
|
||||
github.com/hashicorp/consul/api v1.30.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/hashicorp/vault/api v1.15.0 // indirect
|
||||
github.com/hashicorp/vault/api/auth/approle v0.8.0 // indirect
|
||||
github.com/hashicorp/vault/api/auth/aws v0.8.0 // indirect
|
||||
github.com/hashicorp/vault/api/auth/userpass v0.8.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/itchyny/gojq v0.12.17 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.6 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/jsonnet-bundler/jsonnet-bundler v0.6.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/lmittmann/tint v1.0.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/mdlayher/vsock v1.2.1 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||
go.opentelemetry.io/otel v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/oauth2 v0.24.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
google.golang.org/protobuf v1.36.1 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.3 // indirect
|
||||
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20231121144256-b99613f794b6 // indirect
|
||||
gocloud.dev v0.40.0 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/perf v0.0.0-20250214215153-c95ad7d5b636 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240716161551-93cc26a95ae9 // indirect
|
||||
google.golang.org/api v0.215.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||
google.golang.org/grpc v1.68.1 // indirect
|
||||
google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
|
||||
k8s.io/apiserver v0.33.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
)
|
||||
|
||||
tool (
|
||||
github.com/brancz/gojsontoyaml
|
||||
github.com/campoy/embedmd
|
||||
github.com/google/go-jsonnet/cmd/jsonnet
|
||||
github.com/hairyhenderson/gomplate/v4/cmd/gomplate
|
||||
github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb
|
||||
golang.org/x/perf/cmd/benchstat
|
||||
)
|
||||
|
|
|
@ -64,8 +64,8 @@ func (r *CRDiscoverer) StartDiscovery(ctx context.Context, config *rest.Config)
|
|||
},
|
||||
Plural: p,
|
||||
}
|
||||
r.AppendToMap(gotGVKP)
|
||||
r.SafeWrite(func() {
|
||||
r.AppendToMap(gotGVKP)
|
||||
r.WasUpdated = true
|
||||
})
|
||||
}
|
||||
|
@ -89,8 +89,8 @@ func (r *CRDiscoverer) StartDiscovery(ctx context.Context, config *rest.Config)
|
|||
},
|
||||
Plural: p,
|
||||
}
|
||||
r.RemoveFromMap(gotGVKP)
|
||||
r.SafeWrite(func() {
|
||||
r.RemoveFromMap(gotGVKP)
|
||||
r.WasUpdated = true
|
||||
})
|
||||
}
|
||||
|
|
|
@ -45,6 +45,8 @@ type CRDiscoverer struct {
|
|||
CRDsCacheCountGauge prometheus.Gauge
|
||||
// Map is a cache of the collected GVKs.
|
||||
Map map[string]map[string][]kindPlural
|
||||
// GVKToReflectorStopChanMap is a map of GVKs to channels that can be used to stop their corresponding reflector.
|
||||
GVKToReflectorStopChanMap map[string]chan struct{}
|
||||
// m is a mutex to protect the cache.
|
||||
m sync.RWMutex
|
||||
// ShouldUpdate is a flag that indicates whether the cache was updated.
|
||||
|
@ -70,6 +72,9 @@ func (r *CRDiscoverer) AppendToMap(gvkps ...groupVersionKindPlural) {
|
|||
if r.Map == nil {
|
||||
r.Map = map[string]map[string][]kindPlural{}
|
||||
}
|
||||
if r.GVKToReflectorStopChanMap == nil {
|
||||
r.GVKToReflectorStopChanMap = map[string]chan struct{}{}
|
||||
}
|
||||
for _, gvkp := range gvkps {
|
||||
if _, ok := r.Map[gvkp.Group]; !ok {
|
||||
r.Map[gvkp.Group] = map[string][]kindPlural{}
|
||||
|
@ -78,6 +83,7 @@ func (r *CRDiscoverer) AppendToMap(gvkps ...groupVersionKindPlural) {
|
|||
r.Map[gvkp.Group][gvkp.Version] = []kindPlural{}
|
||||
}
|
||||
r.Map[gvkp.Group][gvkp.Version] = append(r.Map[gvkp.Group][gvkp.Version], kindPlural{Kind: gvkp.Kind, Plural: gvkp.Plural})
|
||||
r.GVKToReflectorStopChanMap[gvkp.GroupVersionKind.String()] = make(chan struct{})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,6 +98,8 @@ func (r *CRDiscoverer) RemoveFromMap(gvkps ...groupVersionKindPlural) {
|
|||
}
|
||||
for i, el := range r.Map[gvkp.Group][gvkp.Version] {
|
||||
if el.Kind == gvkp.Kind {
|
||||
close(r.GVKToReflectorStopChanMap[gvkp.GroupVersionKind.String()])
|
||||
delete(r.GVKToReflectorStopChanMap, gvkp.GroupVersionKind.String())
|
||||
if len(r.Map[gvkp.Group][gvkp.Version]) == 1 {
|
||||
delete(r.Map[gvkp.Group], gvkp.Version)
|
||||
if len(r.Map[gvkp.Group]) == 0 {
|
||||
|
|
|
@ -38,6 +38,7 @@ import (
|
|||
policyv1 "k8s.io/api/policy/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
storagev1 "k8s.io/api/storage/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/klog/v2"
|
||||
|
@ -83,6 +84,9 @@ type Builder struct {
|
|||
totalShards int
|
||||
shard int32
|
||||
useAPIServerCache bool
|
||||
objectLimit int64
|
||||
|
||||
GVKToReflectorStopChanMap *map[string]chan struct{}
|
||||
}
|
||||
|
||||
// NewBuilder returns a new builder.
|
||||
|
@ -165,6 +169,12 @@ func (b *Builder) WithUsingAPIServerCache(u bool) {
|
|||
b.useAPIServerCache = u
|
||||
}
|
||||
|
||||
// WithObjectLimit sets a limit on how many objects you can list from the APIServer.
|
||||
// This is to protect kube-state-metrics from running out of memory if the APIServer has a lot of objects.
|
||||
func (b *Builder) WithObjectLimit(l int64) {
|
||||
b.objectLimit = l
|
||||
}
|
||||
|
||||
// WithFamilyGeneratorFilter configures the family generator filter which decides which
|
||||
// metrics are to be exposed by the store build by the Builder.
|
||||
func (b *Builder) WithFamilyGeneratorFilter(l generator.FamilyGeneratorFilter) {
|
||||
|
@ -215,6 +225,7 @@ func (b *Builder) WithCustomResourceStoreFactories(fs ...customresource.Registry
|
|||
f.ExpectedType(),
|
||||
f.ListWatch,
|
||||
b.useAPIServerCache,
|
||||
b.objectLimit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -363,150 +374,150 @@ func availableResources() []string {
|
|||
}
|
||||
|
||||
func (b *Builder) buildConfigMapStores() []cache.Store {
|
||||
return b.buildStoresFunc(configMapMetricFamilies(b.allowAnnotationsList["configmaps"], b.allowLabelsList["configmaps"]), &v1.ConfigMap{}, createConfigMapListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(configMapMetricFamilies(b.allowAnnotationsList["configmaps"], b.allowLabelsList["configmaps"]), &v1.ConfigMap{}, createConfigMapListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildCronJobStores() []cache.Store {
|
||||
return b.buildStoresFunc(cronJobMetricFamilies(b.allowAnnotationsList["cronjobs"], b.allowLabelsList["cronjobs"]), &batchv1.CronJob{}, createCronJobListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(cronJobMetricFamilies(b.allowAnnotationsList["cronjobs"], b.allowLabelsList["cronjobs"]), &batchv1.CronJob{}, createCronJobListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildDaemonSetStores() []cache.Store {
|
||||
return b.buildStoresFunc(daemonSetMetricFamilies(b.allowAnnotationsList["daemonsets"], b.allowLabelsList["daemonsets"]), &appsv1.DaemonSet{}, createDaemonSetListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(daemonSetMetricFamilies(b.allowAnnotationsList["daemonsets"], b.allowLabelsList["daemonsets"]), &appsv1.DaemonSet{}, createDaemonSetListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildDeploymentStores() []cache.Store {
|
||||
return b.buildStoresFunc(deploymentMetricFamilies(b.allowAnnotationsList["deployments"], b.allowLabelsList["deployments"]), &appsv1.Deployment{}, createDeploymentListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(deploymentMetricFamilies(b.allowAnnotationsList["deployments"], b.allowLabelsList["deployments"]), &appsv1.Deployment{}, createDeploymentListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildEndpointsStores() []cache.Store {
|
||||
return b.buildStoresFunc(endpointMetricFamilies(b.allowAnnotationsList["endpoints"], b.allowLabelsList["endpoints"]), &v1.Endpoints{}, createEndpointsListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(endpointMetricFamilies(b.allowAnnotationsList["endpoints"], b.allowLabelsList["endpoints"]), &v1.Endpoints{}, createEndpointsListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildEndpointSlicesStores() []cache.Store {
|
||||
return b.buildStoresFunc(endpointSliceMetricFamilies(b.allowAnnotationsList["endpointslices"], b.allowLabelsList["endpointslices"]), &discoveryv1.EndpointSlice{}, createEndpointSliceListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(endpointSliceMetricFamilies(b.allowAnnotationsList["endpointslices"], b.allowLabelsList["endpointslices"]), &discoveryv1.EndpointSlice{}, createEndpointSliceListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildHPAStores() []cache.Store {
|
||||
return b.buildStoresFunc(hpaMetricFamilies(b.allowAnnotationsList["horizontalpodautoscalers"], b.allowLabelsList["horizontalpodautoscalers"]), &autoscaling.HorizontalPodAutoscaler{}, createHPAListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(hpaMetricFamilies(b.allowAnnotationsList["horizontalpodautoscalers"], b.allowLabelsList["horizontalpodautoscalers"]), &autoscaling.HorizontalPodAutoscaler{}, createHPAListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildIngressStores() []cache.Store {
|
||||
return b.buildStoresFunc(ingressMetricFamilies(b.allowAnnotationsList["ingresses"], b.allowLabelsList["ingresses"]), &networkingv1.Ingress{}, createIngressListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(ingressMetricFamilies(b.allowAnnotationsList["ingresses"], b.allowLabelsList["ingresses"]), &networkingv1.Ingress{}, createIngressListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildJobStores() []cache.Store {
|
||||
return b.buildStoresFunc(jobMetricFamilies(b.allowAnnotationsList["jobs"], b.allowLabelsList["jobs"]), &batchv1.Job{}, createJobListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(jobMetricFamilies(b.allowAnnotationsList["jobs"], b.allowLabelsList["jobs"]), &batchv1.Job{}, createJobListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildLimitRangeStores() []cache.Store {
|
||||
return b.buildStoresFunc(limitRangeMetricFamilies, &v1.LimitRange{}, createLimitRangeListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(limitRangeMetricFamilies, &v1.LimitRange{}, createLimitRangeListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildMutatingWebhookConfigurationStores() []cache.Store {
|
||||
return b.buildStoresFunc(mutatingWebhookConfigurationMetricFamilies, &admissionregistrationv1.MutatingWebhookConfiguration{}, createMutatingWebhookConfigurationListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(mutatingWebhookConfigurationMetricFamilies, &admissionregistrationv1.MutatingWebhookConfiguration{}, createMutatingWebhookConfigurationListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildNamespaceStores() []cache.Store {
|
||||
return b.buildStoresFunc(namespaceMetricFamilies(b.allowAnnotationsList["namespaces"], b.allowLabelsList["namespaces"]), &v1.Namespace{}, createNamespaceListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(namespaceMetricFamilies(b.allowAnnotationsList["namespaces"], b.allowLabelsList["namespaces"]), &v1.Namespace{}, createNamespaceListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildNetworkPolicyStores() []cache.Store {
|
||||
return b.buildStoresFunc(networkPolicyMetricFamilies(b.allowAnnotationsList["networkpolicies"], b.allowLabelsList["networkpolicies"]), &networkingv1.NetworkPolicy{}, createNetworkPolicyListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(networkPolicyMetricFamilies(b.allowAnnotationsList["networkpolicies"], b.allowLabelsList["networkpolicies"]), &networkingv1.NetworkPolicy{}, createNetworkPolicyListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildNodeStores() []cache.Store {
|
||||
return b.buildStoresFunc(nodeMetricFamilies(b.allowAnnotationsList["nodes"], b.allowLabelsList["nodes"]), &v1.Node{}, createNodeListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(nodeMetricFamilies(b.allowAnnotationsList["nodes"], b.allowLabelsList["nodes"]), &v1.Node{}, createNodeListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildPersistentVolumeClaimStores() []cache.Store {
|
||||
return b.buildStoresFunc(persistentVolumeClaimMetricFamilies(b.allowAnnotationsList["persistentvolumeclaims"], b.allowLabelsList["persistentvolumeclaims"]), &v1.PersistentVolumeClaim{}, createPersistentVolumeClaimListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(persistentVolumeClaimMetricFamilies(b.allowAnnotationsList["persistentvolumeclaims"], b.allowLabelsList["persistentvolumeclaims"]), &v1.PersistentVolumeClaim{}, createPersistentVolumeClaimListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildPersistentVolumeStores() []cache.Store {
|
||||
return b.buildStoresFunc(persistentVolumeMetricFamilies(b.allowAnnotationsList["persistentvolumes"], b.allowLabelsList["persistentvolumes"]), &v1.PersistentVolume{}, createPersistentVolumeListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(persistentVolumeMetricFamilies(b.allowAnnotationsList["persistentvolumes"], b.allowLabelsList["persistentvolumes"]), &v1.PersistentVolume{}, createPersistentVolumeListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildPodDisruptionBudgetStores() []cache.Store {
|
||||
return b.buildStoresFunc(podDisruptionBudgetMetricFamilies(b.allowAnnotationsList["poddisruptionbudgets"], b.allowLabelsList["poddisruptionbudgets"]), &policyv1.PodDisruptionBudget{}, createPodDisruptionBudgetListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(podDisruptionBudgetMetricFamilies(b.allowAnnotationsList["poddisruptionbudgets"], b.allowLabelsList["poddisruptionbudgets"]), &policyv1.PodDisruptionBudget{}, createPodDisruptionBudgetListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildReplicaSetStores() []cache.Store {
|
||||
return b.buildStoresFunc(replicaSetMetricFamilies(b.allowAnnotationsList["replicasets"], b.allowLabelsList["replicasets"]), &appsv1.ReplicaSet{}, createReplicaSetListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(replicaSetMetricFamilies(b.allowAnnotationsList["replicasets"], b.allowLabelsList["replicasets"]), &appsv1.ReplicaSet{}, createReplicaSetListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildReplicationControllerStores() []cache.Store {
|
||||
return b.buildStoresFunc(replicationControllerMetricFamilies, &v1.ReplicationController{}, createReplicationControllerListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(replicationControllerMetricFamilies, &v1.ReplicationController{}, createReplicationControllerListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildResourceQuotaStores() []cache.Store {
|
||||
return b.buildStoresFunc(resourceQuotaMetricFamilies(b.allowAnnotationsList["resourcequotas"], b.allowLabelsList["resourcequotas"]), &v1.ResourceQuota{}, createResourceQuotaListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(resourceQuotaMetricFamilies(b.allowAnnotationsList["resourcequotas"], b.allowLabelsList["resourcequotas"]), &v1.ResourceQuota{}, createResourceQuotaListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildSecretStores() []cache.Store {
|
||||
return b.buildStoresFunc(secretMetricFamilies(b.allowAnnotationsList["secrets"], b.allowLabelsList["secrets"]), &v1.Secret{}, createSecretListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(secretMetricFamilies(b.allowAnnotationsList["secrets"], b.allowLabelsList["secrets"]), &v1.Secret{}, createSecretListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildServiceAccountStores() []cache.Store {
|
||||
return b.buildStoresFunc(serviceAccountMetricFamilies(b.allowAnnotationsList["serviceaccounts"], b.allowLabelsList["serviceaccounts"]), &v1.ServiceAccount{}, createServiceAccountListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(serviceAccountMetricFamilies(b.allowAnnotationsList["serviceaccounts"], b.allowLabelsList["serviceaccounts"]), &v1.ServiceAccount{}, createServiceAccountListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildServiceStores() []cache.Store {
|
||||
return b.buildStoresFunc(serviceMetricFamilies(b.allowAnnotationsList["services"], b.allowLabelsList["services"]), &v1.Service{}, createServiceListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(serviceMetricFamilies(b.allowAnnotationsList["services"], b.allowLabelsList["services"]), &v1.Service{}, createServiceListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildStatefulSetStores() []cache.Store {
|
||||
return b.buildStoresFunc(statefulSetMetricFamilies(b.allowAnnotationsList["statefulsets"], b.allowLabelsList["statefulsets"]), &appsv1.StatefulSet{}, createStatefulSetListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(statefulSetMetricFamilies(b.allowAnnotationsList["statefulsets"], b.allowLabelsList["statefulsets"]), &appsv1.StatefulSet{}, createStatefulSetListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildStorageClassStores() []cache.Store {
|
||||
return b.buildStoresFunc(storageClassMetricFamilies(b.allowAnnotationsList["storageclasses"], b.allowLabelsList["storageclasses"]), &storagev1.StorageClass{}, createStorageClassListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(storageClassMetricFamilies(b.allowAnnotationsList["storageclasses"], b.allowLabelsList["storageclasses"]), &storagev1.StorageClass{}, createStorageClassListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildPodStores() []cache.Store {
|
||||
return b.buildStoresFunc(podMetricFamilies(b.allowAnnotationsList["pods"], b.allowLabelsList["pods"]), &v1.Pod{}, createPodListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(podMetricFamilies(b.allowAnnotationsList["pods"], b.allowLabelsList["pods"]), &v1.Pod{}, createPodListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildCsrStores() []cache.Store {
|
||||
return b.buildStoresFunc(csrMetricFamilies(b.allowAnnotationsList["certificatesigningrequests"], b.allowLabelsList["certificatesigningrequests"]), &certv1.CertificateSigningRequest{}, createCSRListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(csrMetricFamilies(b.allowAnnotationsList["certificatesigningrequests"], b.allowLabelsList["certificatesigningrequests"]), &certv1.CertificateSigningRequest{}, createCSRListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildValidatingWebhookConfigurationStores() []cache.Store {
|
||||
return b.buildStoresFunc(validatingWebhookConfigurationMetricFamilies, &admissionregistrationv1.ValidatingWebhookConfiguration{}, createValidatingWebhookConfigurationListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(validatingWebhookConfigurationMetricFamilies, &admissionregistrationv1.ValidatingWebhookConfiguration{}, createValidatingWebhookConfigurationListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildVolumeAttachmentStores() []cache.Store {
|
||||
return b.buildStoresFunc(volumeAttachmentMetricFamilies, &storagev1.VolumeAttachment{}, createVolumeAttachmentListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(volumeAttachmentMetricFamilies, &storagev1.VolumeAttachment{}, createVolumeAttachmentListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildLeasesStores() []cache.Store {
|
||||
return b.buildStoresFunc(leaseMetricFamilies, &coordinationv1.Lease{}, createLeaseListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(leaseMetricFamilies, &coordinationv1.Lease{}, createLeaseListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildClusterRoleStores() []cache.Store {
|
||||
return b.buildStoresFunc(clusterRoleMetricFamilies(b.allowAnnotationsList["clusterroles"], b.allowLabelsList["clusterroles"]), &rbacv1.ClusterRole{}, createClusterRoleListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(clusterRoleMetricFamilies(b.allowAnnotationsList["clusterroles"], b.allowLabelsList["clusterroles"]), &rbacv1.ClusterRole{}, createClusterRoleListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildRoleStores() []cache.Store {
|
||||
return b.buildStoresFunc(roleMetricFamilies(b.allowAnnotationsList["roles"], b.allowLabelsList["roles"]), &rbacv1.Role{}, createRoleListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(roleMetricFamilies(b.allowAnnotationsList["roles"], b.allowLabelsList["roles"]), &rbacv1.Role{}, createRoleListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildClusterRoleBindingStores() []cache.Store {
|
||||
return b.buildStoresFunc(clusterRoleBindingMetricFamilies(b.allowAnnotationsList["clusterrolebindings"], b.allowLabelsList["clusterrolebindings"]), &rbacv1.ClusterRoleBinding{}, createClusterRoleBindingListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(clusterRoleBindingMetricFamilies(b.allowAnnotationsList["clusterrolebindings"], b.allowLabelsList["clusterrolebindings"]), &rbacv1.ClusterRoleBinding{}, createClusterRoleBindingListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildRoleBindingStores() []cache.Store {
|
||||
return b.buildStoresFunc(roleBindingMetricFamilies(b.allowAnnotationsList["rolebindings"], b.allowLabelsList["rolebindings"]), &rbacv1.RoleBinding{}, createRoleBindingListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(roleBindingMetricFamilies(b.allowAnnotationsList["rolebindings"], b.allowLabelsList["rolebindings"]), &rbacv1.RoleBinding{}, createRoleBindingListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildIngressClassStores() []cache.Store {
|
||||
return b.buildStoresFunc(ingressClassMetricFamilies(b.allowAnnotationsList["ingressclasses"], b.allowLabelsList["ingressclasses"]), &networkingv1.IngressClass{}, createIngressClassListWatch, b.useAPIServerCache)
|
||||
return b.buildStoresFunc(ingressClassMetricFamilies(b.allowAnnotationsList["ingressclasses"], b.allowLabelsList["ingressclasses"]), &networkingv1.IngressClass{}, createIngressClassListWatch, b.useAPIServerCache, b.objectLimit)
|
||||
}
|
||||
|
||||
func (b *Builder) buildStores(
|
||||
metricFamilies []generator.FamilyGenerator,
|
||||
expectedType interface{},
|
||||
listWatchFunc func(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher,
|
||||
useAPIServerCache bool,
|
||||
useAPIServerCache bool, objectLimit int64,
|
||||
) []cache.Store {
|
||||
metricFamilies = generator.FilterFamilyGenerators(b.familyGeneratorFilter, metricFamilies)
|
||||
composedMetricGenFuncs := generator.ComposeMetricGenFuncs(metricFamilies)
|
||||
|
@ -521,7 +532,7 @@ func (b *Builder) buildStores(
|
|||
klog.InfoS("FieldSelector is used", "fieldSelector", b.fieldSelectorFilter)
|
||||
}
|
||||
listWatcher := listWatchFunc(b.kubeClient, v1.NamespaceAll, b.fieldSelectorFilter)
|
||||
b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
|
||||
b.startReflector(expectedType, store, listWatcher, useAPIServerCache, objectLimit)
|
||||
return []cache.Store{store}
|
||||
}
|
||||
|
||||
|
@ -535,7 +546,7 @@ func (b *Builder) buildStores(
|
|||
klog.InfoS("FieldSelector is used", "fieldSelector", b.fieldSelectorFilter)
|
||||
}
|
||||
listWatcher := listWatchFunc(b.kubeClient, ns, b.fieldSelectorFilter)
|
||||
b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
|
||||
b.startReflector(expectedType, store, listWatcher, useAPIServerCache, objectLimit)
|
||||
stores = append(stores, store)
|
||||
}
|
||||
|
||||
|
@ -547,7 +558,7 @@ func (b *Builder) buildCustomResourceStores(resourceName string,
|
|||
metricFamilies []generator.FamilyGenerator,
|
||||
expectedType interface{},
|
||||
listWatchFunc func(customResourceClient interface{}, ns string, fieldSelector string) cache.ListerWatcher,
|
||||
useAPIServerCache bool,
|
||||
useAPIServerCache bool, objectLimit int64,
|
||||
) []cache.Store {
|
||||
metricFamilies = generator.FilterFamilyGenerators(b.familyGeneratorFilter, metricFamilies)
|
||||
composedMetricGenFuncs := generator.ComposeMetricGenFuncs(metricFamilies)
|
||||
|
@ -579,7 +590,7 @@ func (b *Builder) buildCustomResourceStores(resourceName string,
|
|||
klog.InfoS("FieldSelector is used", "fieldSelector", b.fieldSelectorFilter)
|
||||
}
|
||||
listWatcher := listWatchFunc(customResourceClient, v1.NamespaceAll, b.fieldSelectorFilter)
|
||||
b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
|
||||
b.startReflector(expectedType, store, listWatcher, useAPIServerCache, objectLimit)
|
||||
return []cache.Store{store}
|
||||
}
|
||||
|
||||
|
@ -591,7 +602,7 @@ func (b *Builder) buildCustomResourceStores(resourceName string,
|
|||
)
|
||||
klog.InfoS("FieldSelector is used", "fieldSelector", b.fieldSelectorFilter)
|
||||
listWatcher := listWatchFunc(customResourceClient, ns, b.fieldSelectorFilter)
|
||||
b.startReflector(expectedType, store, listWatcher, useAPIServerCache)
|
||||
b.startReflector(expectedType, store, listWatcher, useAPIServerCache, objectLimit)
|
||||
stores = append(stores, store)
|
||||
}
|
||||
|
||||
|
@ -605,10 +616,15 @@ func (b *Builder) startReflector(
|
|||
store cache.Store,
|
||||
listWatcher cache.ListerWatcher,
|
||||
useAPIServerCache bool,
|
||||
objectLimit int64,
|
||||
) {
|
||||
instrumentedListWatch := watch.NewInstrumentedListerWatcher(listWatcher, b.listWatchMetrics, reflect.TypeOf(expectedType).String(), useAPIServerCache)
|
||||
instrumentedListWatch := watch.NewInstrumentedListerWatcher(listWatcher, b.listWatchMetrics, reflect.TypeOf(expectedType).String(), useAPIServerCache, objectLimit)
|
||||
reflector := cache.NewReflectorWithOptions(sharding.NewShardedListWatch(b.shard, b.totalShards, instrumentedListWatch), expectedType, store, cache.ReflectorOptions{ResyncPeriod: 0})
|
||||
go reflector.Run(b.ctx.Done())
|
||||
if cr, ok := expectedType.(*unstructured.Unstructured); ok {
|
||||
go reflector.Run((*b.GVKToReflectorStopChanMap)[cr.GroupVersionKind().String()])
|
||||
} else {
|
||||
go reflector.Run(b.ctx.Done())
|
||||
}
|
||||
}
|
||||
|
||||
// cacheStoresToMetricStores converts []cache.Store into []*metricsstore.MetricsStore
|
||||
|
|
|
@ -131,7 +131,7 @@ func clusterRoleMetricFamilies(allowAnnotationsList, allowLabelsList []string) [
|
|||
"",
|
||||
wrapClusterRoleFunc(func(r *rbacv1.ClusterRole) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(r.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(r.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -133,7 +133,7 @@ func clusterRoleBindingMetricFamilies(allowAnnotationsList, allowLabelsList []st
|
|||
"",
|
||||
wrapClusterRoleBindingFunc(func(r *rbacv1.ClusterRoleBinding) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(r.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(r.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -127,7 +127,7 @@ func configMapMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g
|
|||
"",
|
||||
wrapConfigMapFunc(func(c *v1.ConfigMap) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(c.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(c.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -273,7 +273,7 @@ func cronJobMetricFamilies(allowAnnotationsList, allowLabelsList []string) []gen
|
|||
"",
|
||||
wrapCronJobFunc(func(j *batchv1.CronJob) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(j.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(j.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
@ -358,7 +358,7 @@ func getNextScheduledTime(schedule string, lastScheduleTime *metav1.Time, create
|
|||
|
||||
sched, err := cron.ParseStandard(schedule)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("Failed to parse cron job schedule '%s': %w", schedule, err)
|
||||
return time.Time{}, fmt.Errorf("failed to parse cron job schedule '%s': %w", schedule, err)
|
||||
}
|
||||
if !lastScheduleTime.IsZero() {
|
||||
return sched.Next(lastScheduleTime.Time), nil
|
||||
|
|
|
@ -217,7 +217,7 @@ func daemonSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) []g
|
|||
{
|
||||
LabelKeys: []string{},
|
||||
LabelValues: []string{},
|
||||
Value: float64(d.ObjectMeta.Generation),
|
||||
Value: float64(d.Generation),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -41,6 +41,23 @@ var (
|
|||
descDeploymentLabelsDefaultLabels = []string{"namespace", "deployment"}
|
||||
)
|
||||
|
||||
// Reasons copied from kubernetes/pkg/controller/deployment/deployment_utils.go.
|
||||
var (
|
||||
allowedDeploymentReasons = map[string]struct{}{
|
||||
"ReplicaSetUpdated": {},
|
||||
"ReplicaSetCreateError": {},
|
||||
"NewReplicaSetCreated": {},
|
||||
"FoundNewReplicaSet": {},
|
||||
"NewReplicaSetAvailable": {},
|
||||
"ProgressDeadlineExceeded": {},
|
||||
"DeploymentPaused": {},
|
||||
"DeploymentResumed": {},
|
||||
"MinimumReplicasAvailable": {},
|
||||
"MinimumReplicasUnavailable": {},
|
||||
"": {},
|
||||
}
|
||||
)
|
||||
|
||||
func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator {
|
||||
return []generator.FamilyGenerator{
|
||||
*generator.NewFamilyGeneratorWithStability(
|
||||
|
@ -174,8 +191,13 @@ func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) []
|
|||
for j, m := range conditionMetrics {
|
||||
metric := m
|
||||
|
||||
metric.LabelKeys = []string{"condition", "status"}
|
||||
metric.LabelValues = append([]string{string(c.Type)}, metric.LabelValues...)
|
||||
reason := c.Reason
|
||||
if _, ok := allowedDeploymentReasons[reason]; !ok {
|
||||
reason = "unknown"
|
||||
}
|
||||
|
||||
metric.LabelKeys = []string{"reason", "condition", "status"}
|
||||
metric.LabelValues = append([]string{reason, string(c.Type)}, metric.LabelValues...)
|
||||
ms[i*len(conditionStatuses)+j] = metric
|
||||
}
|
||||
}
|
||||
|
@ -277,7 +299,7 @@ func deploymentMetricFamilies(allowAnnotationsList, allowLabelsList []string) []
|
|||
return &metric.Family{
|
||||
Metrics: []*metric.Metric{
|
||||
{
|
||||
Value: float64(d.ObjectMeta.Generation),
|
||||
Value: float64(d.Generation),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
var (
|
||||
depl1Replicas int32 = 200
|
||||
depl2Replicas int32 = 5
|
||||
depl3Replicas int32 = 1
|
||||
|
||||
depl1MaxUnavailable = intstr.FromInt(10)
|
||||
depl2MaxUnavailable = intstr.FromString("25%")
|
||||
|
@ -98,8 +99,8 @@ func TestDeploymentStore(t *testing.T) {
|
|||
UpdatedReplicas: 2,
|
||||
ObservedGeneration: 111,
|
||||
Conditions: []v1.DeploymentCondition{
|
||||
{Type: v1.DeploymentAvailable, Status: corev1.ConditionTrue},
|
||||
{Type: v1.DeploymentProgressing, Status: corev1.ConditionTrue},
|
||||
{Type: v1.DeploymentAvailable, Status: corev1.ConditionTrue, Reason: "MinimumReplicasAvailable"},
|
||||
{Type: v1.DeploymentProgressing, Status: corev1.ConditionTrue, Reason: "NewReplicaSetAvailable"},
|
||||
},
|
||||
},
|
||||
Spec: v1.DeploymentSpec{
|
||||
|
@ -126,12 +127,12 @@ func TestDeploymentStore(t *testing.T) {
|
|||
kube_deployment_status_replicas_updated{deployment="depl1",namespace="ns1"} 2
|
||||
kube_deployment_status_replicas{deployment="depl1",namespace="ns1"} 15
|
||||
kube_deployment_status_replicas_ready{deployment="depl1",namespace="ns1"} 10
|
||||
kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="true"} 1
|
||||
kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="true"} 1
|
||||
kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="false"} 0
|
||||
kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="false"} 0
|
||||
kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Available",status="unknown"} 0
|
||||
kube_deployment_status_condition{deployment="depl1",namespace="ns1",condition="Progressing",status="unknown"} 0
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl1",namespace="ns1",reason="MinimumReplicasAvailable",status="true"} 1
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl1",namespace="ns1",reason="MinimumReplicasAvailable",status="false"} 0
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl1",namespace="ns1",reason="MinimumReplicasAvailable",status="unknown"} 0
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl1",namespace="ns1",reason="NewReplicaSetAvailable",status="true"} 1
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl1",namespace="ns1",reason="NewReplicaSetAvailable",status="false"} 0
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl1",namespace="ns1",reason="NewReplicaSetAvailable",status="unknown"} 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -152,9 +153,9 @@ func TestDeploymentStore(t *testing.T) {
|
|||
UpdatedReplicas: 1,
|
||||
ObservedGeneration: 1111,
|
||||
Conditions: []v1.DeploymentCondition{
|
||||
{Type: v1.DeploymentAvailable, Status: corev1.ConditionFalse},
|
||||
{Type: v1.DeploymentProgressing, Status: corev1.ConditionFalse},
|
||||
{Type: v1.DeploymentReplicaFailure, Status: corev1.ConditionTrue},
|
||||
{Type: v1.DeploymentAvailable, Status: corev1.ConditionFalse, Reason: "MinimumReplicasUnavailable"},
|
||||
{Type: v1.DeploymentProgressing, Status: corev1.ConditionFalse, Reason: "ProgressDeadlineExceeded"},
|
||||
{Type: v1.DeploymentReplicaFailure, Status: corev1.ConditionTrue, Reason: "ReplicaSetCreateError"},
|
||||
},
|
||||
},
|
||||
Spec: v1.DeploymentSpec{
|
||||
|
@ -180,15 +181,49 @@ func TestDeploymentStore(t *testing.T) {
|
|||
kube_deployment_status_replicas_updated{deployment="depl2",namespace="ns2"} 1
|
||||
kube_deployment_status_replicas{deployment="depl2",namespace="ns2"} 10
|
||||
kube_deployment_status_replicas_ready{deployment="depl2",namespace="ns2"} 5
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="true"} 0
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="true"} 0
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="true"} 1
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="false"} 1
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="false"} 1
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="false"} 0
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Available",status="unknown"} 0
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="Progressing",status="unknown"} 0
|
||||
kube_deployment_status_condition{deployment="depl2",namespace="ns2",condition="ReplicaFailure",status="unknown"} 0
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl2",namespace="ns2",reason="MinimumReplicasUnavailable",status="true"} 0
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl2",namespace="ns2",reason="MinimumReplicasUnavailable",status="false"} 1
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl2",namespace="ns2",reason="MinimumReplicasUnavailable",status="unknown"} 0
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl2",namespace="ns2",reason="ProgressDeadlineExceeded",status="true"} 0
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl2",namespace="ns2",reason="ProgressDeadlineExceeded",status="false"} 1
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl2",namespace="ns2",reason="ProgressDeadlineExceeded",status="unknown"} 0
|
||||
kube_deployment_status_condition{condition="ReplicaFailure",deployment="depl2",namespace="ns2",reason="ReplicaSetCreateError",status="true"} 1
|
||||
kube_deployment_status_condition{condition="ReplicaFailure",deployment="depl2",namespace="ns2",reason="ReplicaSetCreateError",status="false"} 0
|
||||
kube_deployment_status_condition{condition="ReplicaFailure",deployment="depl2",namespace="ns2",reason="ReplicaSetCreateError",status="unknown"} 0
|
||||
`,
|
||||
},
|
||||
{
|
||||
Obj: &v1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "depl3",
|
||||
Namespace: "ns3",
|
||||
},
|
||||
Status: v1.DeploymentStatus{
|
||||
Conditions: []v1.DeploymentCondition{
|
||||
{Type: v1.DeploymentAvailable, Status: corev1.ConditionFalse, Reason: "ThisReasonIsNotAllowed"},
|
||||
{Type: v1.DeploymentProgressing, Status: corev1.ConditionTrue},
|
||||
},
|
||||
},
|
||||
Spec: v1.DeploymentSpec{
|
||||
Replicas: &depl3Replicas,
|
||||
},
|
||||
},
|
||||
Want: metadata + `
|
||||
kube_deployment_metadata_generation{deployment="depl3",namespace="ns3"} 0
|
||||
kube_deployment_spec_paused{deployment="depl3",namespace="ns3"} 0
|
||||
kube_deployment_spec_replicas{deployment="depl3",namespace="ns3"} 1
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl3",namespace="ns3",reason="unknown",status="true"} 0
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl3",namespace="ns3",reason="unknown",status="false"} 1
|
||||
kube_deployment_status_condition{condition="Available",deployment="depl3",namespace="ns3",reason="unknown",status="unknown"} 0
|
||||
kube_deployment_status_observed_generation{deployment="depl3",namespace="ns3"} 0
|
||||
kube_deployment_status_replicas{deployment="depl3",namespace="ns3"} 0
|
||||
kube_deployment_status_replicas_available{deployment="depl3",namespace="ns3"} 0
|
||||
kube_deployment_status_replicas_ready{deployment="depl3",namespace="ns3"} 0
|
||||
kube_deployment_status_replicas_unavailable{deployment="depl3",namespace="ns3"} 0
|
||||
kube_deployment_status_replicas_updated{deployment="depl3",namespace="ns3"} 0
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="false"} 0
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="true"} 1
|
||||
kube_deployment_status_condition{condition="Progressing",deployment="depl3",namespace="ns3",reason="",status="unknown"} 0
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ func createHPAMetaDataGeneration() generator.FamilyGenerator {
|
|||
return &metric.Family{
|
||||
Metrics: []*metric.Metric{
|
||||
{
|
||||
Value: float64(a.ObjectMeta.Generation),
|
||||
Value: float64(a.Generation),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ func ingressMetricFamilies(allowAnnotationsList, allowLabelsList []string) []gen
|
|||
"",
|
||||
wrapIngressFunc(func(i *networkingv1.Ingress) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(i.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(i.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
@ -155,10 +155,14 @@ func ingressMetricFamilies(allowAnnotationsList, allowLabelsList []string) []gen
|
|||
for _, rule := range i.Spec.Rules {
|
||||
if rule.HTTP != nil {
|
||||
for _, path := range rule.HTTP.Paths {
|
||||
pathType := ""
|
||||
if path.PathType != nil {
|
||||
pathType = string(*path.PathType)
|
||||
}
|
||||
if path.Backend.Service != nil {
|
||||
ms = append(ms, &metric.Metric{
|
||||
LabelKeys: []string{"host", "path", "service_name", "service_port"},
|
||||
LabelValues: []string{rule.Host, path.Path, path.Backend.Service.Name, strconv.Itoa(int(path.Backend.Service.Port.Number))},
|
||||
LabelKeys: []string{"host", "path", "path_type", "service_name", "service_port"},
|
||||
LabelValues: []string{rule.Host, path.Path, pathType, path.Backend.Service.Name, strconv.Itoa(int(path.Backend.Service.Port.Number))},
|
||||
Value: 1,
|
||||
})
|
||||
} else {
|
||||
|
@ -167,8 +171,8 @@ func ingressMetricFamilies(allowAnnotationsList, allowLabelsList []string) []gen
|
|||
apiGroup = *path.Backend.Resource.APIGroup
|
||||
}
|
||||
ms = append(ms, &metric.Metric{
|
||||
LabelKeys: []string{"host", "path", "resource_api_group", "resource_kind", "resource_name"},
|
||||
LabelValues: []string{rule.Host, path.Path, apiGroup, path.Backend.Resource.Kind, path.Backend.Resource.Name},
|
||||
LabelKeys: []string{"host", "path", "path_type", "resource_api_group", "resource_kind", "resource_name"},
|
||||
LabelValues: []string{rule.Host, path.Path, pathType, apiGroup, path.Backend.Resource.Kind, path.Backend.Resource.Name},
|
||||
Value: 1,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
v1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator"
|
||||
)
|
||||
|
@ -141,6 +142,7 @@ func TestIngressStore(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
PathType: ptr.To(networkingv1.PathTypeExact),
|
||||
},
|
||||
{
|
||||
Path: "/somepath2",
|
||||
|
@ -164,8 +166,8 @@ func TestIngressStore(t *testing.T) {
|
|||
Want: metadata + `
|
||||
kube_ingress_info{namespace="ns4",ingress="ingress4",ingressclass="_default"} 1
|
||||
kube_ingress_created{namespace="ns4",ingress="ingress4"} 1.501569018e+09
|
||||
kube_ingress_path{namespace="ns4",ingress="ingress4",host="somehost",path="/somepath",service_name="someservice",service_port="1234"} 1
|
||||
kube_ingress_path{namespace="ns4",ingress="ingress4",host="somehost",path="/somepath2",resource_api_group="",resource_kind="somekind",resource_name="somename"} 1
|
||||
kube_ingress_path{namespace="ns4",ingress="ingress4",host="somehost",path="/somepath",path_type="Exact",service_name="someservice",service_port="1234"} 1
|
||||
kube_ingress_path{namespace="ns4",ingress="ingress4",host="somehost",path="/somepath2",path_type="",resource_api_group="",resource_kind="somekind",resource_name="somename"} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_ingress_info", "kube_ingress_metadata_resource_version", "kube_ingress_created", "kube_ingress_labels", "kube_ingress_path", "kube_ingress_tls"},
|
||||
},
|
||||
|
|
|
@ -78,7 +78,7 @@ var (
|
|||
"",
|
||||
wrapMutatingWebhookConfigurationFunc(func(mwc *admissionregistrationv1.MutatingWebhookConfiguration) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(mwc.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(mwc.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -243,50 +243,50 @@ func createPersistentVolumeInfo() generator.FamilyGenerator {
|
|||
)
|
||||
|
||||
switch {
|
||||
case p.Spec.PersistentVolumeSource.GCEPersistentDisk != nil:
|
||||
gcePDDiskName = p.Spec.PersistentVolumeSource.GCEPersistentDisk.PDName
|
||||
case p.Spec.PersistentVolumeSource.AWSElasticBlockStore != nil:
|
||||
ebsVolumeID = p.Spec.PersistentVolumeSource.AWSElasticBlockStore.VolumeID
|
||||
case p.Spec.PersistentVolumeSource.AzureDisk != nil:
|
||||
azureDiskName = p.Spec.PersistentVolumeSource.AzureDisk.DiskName
|
||||
case p.Spec.PersistentVolumeSource.FC != nil:
|
||||
if p.Spec.PersistentVolumeSource.FC.Lun != nil {
|
||||
fcLun = strconv.FormatInt(int64(*p.Spec.PersistentVolumeSource.FC.Lun), 10)
|
||||
case p.Spec.GCEPersistentDisk != nil:
|
||||
gcePDDiskName = p.Spec.GCEPersistentDisk.PDName
|
||||
case p.Spec.AWSElasticBlockStore != nil:
|
||||
ebsVolumeID = p.Spec.AWSElasticBlockStore.VolumeID
|
||||
case p.Spec.AzureDisk != nil:
|
||||
azureDiskName = p.Spec.AzureDisk.DiskName
|
||||
case p.Spec.FC != nil:
|
||||
if p.Spec.FC.Lun != nil {
|
||||
fcLun = strconv.FormatInt(int64(*p.Spec.FC.Lun), 10)
|
||||
}
|
||||
for _, wwn := range p.Spec.PersistentVolumeSource.FC.TargetWWNs {
|
||||
for _, wwn := range p.Spec.FC.TargetWWNs {
|
||||
if len(fcTargetWWNs) != 0 {
|
||||
fcTargetWWNs += ","
|
||||
}
|
||||
fcTargetWWNs += wwn
|
||||
}
|
||||
for _, wwid := range p.Spec.PersistentVolumeSource.FC.WWIDs {
|
||||
for _, wwid := range p.Spec.FC.WWIDs {
|
||||
if len(fcWWIDs) != 0 {
|
||||
fcWWIDs += ","
|
||||
}
|
||||
fcWWIDs += wwid
|
||||
}
|
||||
case p.Spec.PersistentVolumeSource.ISCSI != nil:
|
||||
iscsiTargetPortal = p.Spec.PersistentVolumeSource.ISCSI.TargetPortal
|
||||
iscsiIQN = p.Spec.PersistentVolumeSource.ISCSI.IQN
|
||||
iscsiLun = strconv.FormatInt(int64(p.Spec.PersistentVolumeSource.ISCSI.Lun), 10)
|
||||
if p.Spec.PersistentVolumeSource.ISCSI.InitiatorName != nil {
|
||||
iscsiInitiatorName = *p.Spec.PersistentVolumeSource.ISCSI.InitiatorName
|
||||
case p.Spec.ISCSI != nil:
|
||||
iscsiTargetPortal = p.Spec.ISCSI.TargetPortal
|
||||
iscsiIQN = p.Spec.ISCSI.IQN
|
||||
iscsiLun = strconv.FormatInt(int64(p.Spec.ISCSI.Lun), 10)
|
||||
if p.Spec.ISCSI.InitiatorName != nil {
|
||||
iscsiInitiatorName = *p.Spec.ISCSI.InitiatorName
|
||||
}
|
||||
case p.Spec.PersistentVolumeSource.NFS != nil:
|
||||
nfsServer = p.Spec.PersistentVolumeSource.NFS.Server
|
||||
nfsPath = p.Spec.PersistentVolumeSource.NFS.Path
|
||||
case p.Spec.PersistentVolumeSource.CSI != nil:
|
||||
csiDriver = p.Spec.PersistentVolumeSource.CSI.Driver
|
||||
csiVolumeHandle = p.Spec.PersistentVolumeSource.CSI.VolumeHandle
|
||||
case p.Spec.PersistentVolumeSource.Local != nil:
|
||||
localPath = p.Spec.PersistentVolumeSource.Local.Path
|
||||
if p.Spec.PersistentVolumeSource.Local.FSType != nil {
|
||||
localFS = *p.Spec.PersistentVolumeSource.Local.FSType
|
||||
case p.Spec.NFS != nil:
|
||||
nfsServer = p.Spec.NFS.Server
|
||||
nfsPath = p.Spec.NFS.Path
|
||||
case p.Spec.CSI != nil:
|
||||
csiDriver = p.Spec.CSI.Driver
|
||||
csiVolumeHandle = p.Spec.CSI.VolumeHandle
|
||||
case p.Spec.Local != nil:
|
||||
localPath = p.Spec.Local.Path
|
||||
if p.Spec.Local.FSType != nil {
|
||||
localFS = *p.Spec.Local.FSType
|
||||
}
|
||||
case p.Spec.PersistentVolumeSource.HostPath != nil:
|
||||
hostPath = p.Spec.PersistentVolumeSource.HostPath.Path
|
||||
if p.Spec.PersistentVolumeSource.HostPath.Type != nil {
|
||||
hostPathType = string(*p.Spec.PersistentVolumeSource.HostPath.Type)
|
||||
case p.Spec.HostPath != nil:
|
||||
hostPath = p.Spec.HostPath.Path
|
||||
if p.Spec.HostPath.Type != nil {
|
||||
hostPathType = string(*p.Spec.HostPath.Type)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,6 +313,7 @@ func createPersistentVolumeInfo() generator.FamilyGenerator {
|
|||
"local_fs",
|
||||
"host_path",
|
||||
"host_path_type",
|
||||
"reclaim_policy",
|
||||
},
|
||||
LabelValues: []string{
|
||||
p.Spec.StorageClassName,
|
||||
|
@ -334,6 +335,7 @@ func createPersistentVolumeInfo() generator.FamilyGenerator {
|
|||
localFS,
|
||||
hostPath,
|
||||
hostPathType,
|
||||
string(p.Spec.PersistentVolumeReclaimPolicy),
|
||||
},
|
||||
Value: 1,
|
||||
},
|
||||
|
@ -428,11 +430,12 @@ func createPersistentVolumeCSIAttributes() generator.FamilyGenerator {
|
|||
}
|
||||
|
||||
var csiMounter, csiMapOptions string
|
||||
for k, v := range p.Spec.PersistentVolumeSource.CSI.VolumeAttributes {
|
||||
for k, v := range p.Spec.CSI.VolumeAttributes {
|
||||
// storage attributes handled by external CEPH CSI driver
|
||||
if k == "mapOptions" {
|
||||
switch k {
|
||||
case "mapOptions":
|
||||
csiMapOptions = v
|
||||
} else if k == "mounter" {
|
||||
case "mounter":
|
||||
csiMounter = v
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
func TestPersistentVolumeStore(t *testing.T) {
|
||||
iscsiInitiatorName := "iqn.my.test.initiator:112233"
|
||||
volumeMode := v1.PersistentVolumeBlock
|
||||
var reclaimPolicy v1.PersistentVolumeReclaimPolicy = "Retain"
|
||||
cases := []generateMetricsTestCase{
|
||||
// Verify phase enumerations.
|
||||
{
|
||||
|
@ -166,6 +167,9 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-pv-available",
|
||||
},
|
||||
Spec: v1.PersistentVolumeSpec{
|
||||
PersistentVolumeReclaimPolicy: reclaimPolicy,
|
||||
},
|
||||
Status: v1.PersistentVolumeStatus{
|
||||
Phase: v1.VolumeAvailable,
|
||||
},
|
||||
|
@ -173,7 +177,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="Retain",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -192,7 +196,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -215,7 +219,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="name",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="name",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -238,7 +242,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="aws://eu-west-1c/vol-012d34d567890123b",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="aws://eu-west-1c/vol-012d34d567890123b",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -261,7 +265,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="azure_disk_1",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="azure_disk_1",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -285,7 +289,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="123",fc_target_wwns="0123456789abcdef,abcdef0123456789",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="123",fc_target_wwns="0123456789abcdef,abcdef0123456789",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -308,7 +312,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="0123456789abcdef,abcdef0123456789",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="0123456789abcdef,abcdef0123456789",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -333,7 +337,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="iqn.my.test.server.target00",iscsi_lun="123",iscsi_target_portal="1.2.3.4:3260",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="iqn.my.test.server.target00",iscsi_lun="123",iscsi_target_portal="1.2.3.4:3260",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -359,7 +363,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="iqn.my.test.initiator:112233",iscsi_iqn="iqn.my.test.server.target00",iscsi_lun="123",iscsi_target_portal="1.2.3.4:3260",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="iqn.my.test.initiator:112233",iscsi_iqn="iqn.my.test.server.target00",iscsi_lun="123",iscsi_target_portal="1.2.3.4:3260",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -383,7 +387,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="/myPath",nfs_server="1.2.3.4",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="/myPath",nfs_server="1.2.3.4",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -407,7 +411,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="test-driver",csi_volume_handle="test-volume-handle",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="test-driver",csi_volume_handle="test-volume-handle",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -431,7 +435,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="/mnt/data",local_fs="ext4",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="/mnt/data",local_fs="ext4",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -454,7 +458,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="/mnt/data",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="/mnt/data",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -478,7 +482,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="/mnt/data",host_path_type="Directory",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="/mnt/data",host_path_type="Directory",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
@ -501,7 +505,7 @@ func TestPersistentVolumeStore(t *testing.T) {
|
|||
Want: `
|
||||
# HELP kube_persistentvolume_info [STABLE] Information about persistentvolume.
|
||||
# TYPE kube_persistentvolume_info gauge
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="/mnt/data",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",storageclass=""} 1
|
||||
kube_persistentvolume_info{azure_disk_name="",ebs_volume_id="",fc_lun="",fc_target_wwns="",fc_wwids="",gce_persistent_disk_name="",host_path="/mnt/data",host_path_type="",iscsi_initiator_name="",iscsi_iqn="",iscsi_lun="",iscsi_target_portal="",local_path="",local_fs="",nfs_path="",nfs_server="",csi_driver="",csi_volume_handle="",persistentvolume="test-pv-available",reclaim_policy="",storageclass=""} 1
|
||||
`,
|
||||
MetricNames: []string{"kube_persistentvolume_info"},
|
||||
},
|
||||
|
|
|
@ -92,6 +92,7 @@ func podMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generat
|
|||
createPodStatusScheduledFamilyGenerator(),
|
||||
createPodStatusScheduledTimeFamilyGenerator(),
|
||||
createPodStatusUnschedulableFamilyGenerator(),
|
||||
createPodStatusUnscheduledTimeFamilyGenerator(),
|
||||
createPodTolerationsFamilyGenerator(),
|
||||
createPodNodeSelectorsFamilyGenerator(),
|
||||
createPodServiceAccountFamilyGenerator(),
|
||||
|
@ -1541,15 +1542,12 @@ func createPodStatusReasonFamilyGenerator() generator.FamilyGenerator {
|
|||
ms := []*metric.Metric{}
|
||||
|
||||
for _, reason := range podStatusReasons {
|
||||
metric := &metric.Metric{}
|
||||
metric.LabelKeys = []string{"reason"}
|
||||
metric.LabelValues = []string{reason}
|
||||
if p.Status.Reason == reason {
|
||||
metric.Value = boolFloat64(true)
|
||||
} else {
|
||||
metric.Value = boolFloat64(false)
|
||||
m := &metric.Metric{
|
||||
LabelKeys: []string{"reason"},
|
||||
LabelValues: []string{reason},
|
||||
Value: getPodStatusReasonValue(p, reason),
|
||||
}
|
||||
ms = append(ms, metric)
|
||||
ms = append(ms, m)
|
||||
}
|
||||
|
||||
return &metric.Family{
|
||||
|
@ -1559,6 +1557,23 @@ func createPodStatusReasonFamilyGenerator() generator.FamilyGenerator {
|
|||
)
|
||||
}
|
||||
|
||||
func getPodStatusReasonValue(p *v1.Pod, reason string) float64 {
|
||||
if p.Status.Reason == reason {
|
||||
return 1
|
||||
}
|
||||
for _, cond := range p.Status.Conditions {
|
||||
if cond.Reason == reason {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
for _, cs := range p.Status.ContainerStatuses {
|
||||
if cs.State.Terminated != nil && cs.State.Terminated.Reason == reason {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func createPodStatusScheduledFamilyGenerator() generator.FamilyGenerator {
|
||||
return *generator.NewFamilyGeneratorWithStability(
|
||||
"kube_pod_status_scheduled",
|
||||
|
@ -1642,6 +1657,33 @@ func createPodStatusUnschedulableFamilyGenerator() generator.FamilyGenerator {
|
|||
)
|
||||
}
|
||||
|
||||
func createPodStatusUnscheduledTimeFamilyGenerator() generator.FamilyGenerator {
|
||||
return *generator.NewFamilyGeneratorWithStability(
|
||||
"kube_pod_status_unscheduled_time",
|
||||
"Unix timestamp when pod moved into unscheduled status",
|
||||
metric.Gauge,
|
||||
basemetrics.ALPHA,
|
||||
"",
|
||||
wrapPodFunc(func(p *v1.Pod) *metric.Family {
|
||||
ms := []*metric.Metric{}
|
||||
|
||||
for _, c := range p.Status.Conditions {
|
||||
if c.Type == v1.PodScheduled && c.Status == v1.ConditionFalse {
|
||||
ms = append(ms, &metric.Metric{
|
||||
LabelKeys: []string{},
|
||||
LabelValues: []string{},
|
||||
Value: float64(c.LastTransitionTime.Unix()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return &metric.Family{
|
||||
Metrics: ms,
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// getUniqueTolerations takes an array
|
||||
func getUniqueTolerations(tolerations []v1.Toleration) []v1.Toleration {
|
||||
uniqueTolerationsMap := make(map[v1.Toleration]struct{})
|
||||
|
|
|
@ -2282,7 +2282,7 @@ func BenchmarkPodStore(b *testing.B) {
|
|||
},
|
||||
}
|
||||
|
||||
expectedFamilies := 54
|
||||
expectedFamilies := 55
|
||||
for n := 0; n < b.N; n++ {
|
||||
families := f(pod)
|
||||
if len(families) != expectedFamilies {
|
||||
|
@ -2290,3 +2290,85 @@ func BenchmarkPodStore(b *testing.B) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodStatusReasonValue(t *testing.T) {
|
||||
reason := "TestReason"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *v1.Pod
|
||||
want float64
|
||||
}{
|
||||
{
|
||||
name: "matches Status.Reason",
|
||||
pod: &v1.Pod{
|
||||
Status: v1.PodStatus{
|
||||
Reason: "TestReason",
|
||||
},
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "matches condition Reason",
|
||||
pod: &v1.Pod{
|
||||
Status: v1.PodStatus{
|
||||
Conditions: []v1.PodCondition{
|
||||
{
|
||||
Reason: "TestReason",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "matches container terminated Reason",
|
||||
pod: &v1.Pod{
|
||||
Status: v1.PodStatus{
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{
|
||||
Reason: "TestReason",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "no match returns 0",
|
||||
pod: &v1.Pod{
|
||||
Status: v1.PodStatus{
|
||||
Reason: "OtherReason",
|
||||
Conditions: []v1.PodCondition{
|
||||
{
|
||||
Reason: "NotTestReason",
|
||||
},
|
||||
},
|
||||
ContainerStatuses: []v1.ContainerStatus{
|
||||
{
|
||||
State: v1.ContainerState{
|
||||
Terminated: &v1.ContainerStateTerminated{
|
||||
Reason: "AnotherReason",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := getPodStatusReasonValue(tt.pod, reason)
|
||||
if got != tt.want {
|
||||
t.Errorf("getPodStatusReasonValue() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ func replicaSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) []
|
|||
return &metric.Family{
|
||||
Metrics: []*metric.Metric{
|
||||
{
|
||||
Value: float64(r.ObjectMeta.Generation),
|
||||
Value: float64(r.Generation),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ var (
|
|||
return &metric.Family{
|
||||
Metrics: []*metric.Metric{
|
||||
{
|
||||
Value: float64(r.ObjectMeta.Generation),
|
||||
Value: float64(r.Generation),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ func roleMetricFamilies(allowAnnotationsList, allowLabelsList []string) []genera
|
|||
"",
|
||||
wrapRoleFunc(func(r *rbacv1.Role) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(r.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(r.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -133,7 +133,7 @@ func roleBindingMetricFamilies(allowAnnotationsList, allowLabelsList []string) [
|
|||
"",
|
||||
wrapRoleBindingFunc(func(r *rbacv1.RoleBinding) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(r.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(r.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -150,7 +150,7 @@ func secretMetricFamilies(allowAnnotationsList, allowLabelsList []string) []gene
|
|||
"",
|
||||
wrapSecretFunc(func(s *v1.Secret) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(s.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(s.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -208,7 +208,7 @@ func statefulSetMetricFamilies(allowAnnotationsList, allowLabelsList []string) [
|
|||
return &metric.Family{
|
||||
Metrics: []*metric.Metric{
|
||||
{
|
||||
Value: float64(s.ObjectMeta.Generation),
|
||||
Value: float64(s.Generation),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -257,7 +257,7 @@ func TestKubeLabelsToPrometheusLabels(t *testing.T) {
|
|||
}
|
||||
|
||||
for i := range tc.expectKeys {
|
||||
if !(tc.expectKeys[i] == labelKeys[i] && tc.expectValues[i] == labelValues[i]) {
|
||||
if tc.expectKeys[i] != labelKeys[i] || tc.expectValues[i] != labelValues[i] {
|
||||
t.Errorf("Got Prometheus label %q: %q but expected %q: %q", labelKeys[i], labelValues[i], tc.expectKeys[i], tc.expectValues[i])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ var (
|
|||
"",
|
||||
wrapValidatingWebhookConfigurationFunc(func(vwc *admissionregistrationv1.ValidatingWebhookConfiguration) *metric.Family {
|
||||
return &metric.Family{
|
||||
Metrics: resourceVersionMetric(vwc.ObjectMeta.ResourceVersion),
|
||||
Metrics: resourceVersionMetric(vwc.ResourceVersion),
|
||||
}
|
||||
}),
|
||||
),
|
||||
|
|
|
@ -25,8 +25,8 @@ import (
|
|||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
"gopkg.in/yaml.v3"
|
||||
"k8s.io/klog/v2"
|
||||
yaml "sigs.k8s.io/yaml/goyaml.v3"
|
||||
|
||||
"k8s.io/kube-state-metrics/v2/pkg/app"
|
||||
"k8s.io/kube-state-metrics/v2/pkg/options"
|
||||
|
|
|
@ -18,13 +18,24 @@ package allowdenylist
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
regexp "github.com/dlclark/regexp2"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator"
|
||||
)
|
||||
|
||||
// AllowDenyList encapsulates the logic needed to filter based on a string.
|
||||
// Use ECMAScript as the default regexp spec to support lookarounds (#2594).
|
||||
var (
|
||||
once sync.Once
|
||||
regexpDefaultSpec regexp.RegexOptions = regexp.ECMAScript
|
||||
regexpDefaultTimeout = time.Minute
|
||||
)
|
||||
|
||||
// AllowDenyList namespaceencapsulates the logic needed to filter based on a string.
|
||||
type AllowDenyList struct {
|
||||
list map[string]struct{}
|
||||
rList []*regexp.Regexp
|
||||
|
@ -34,6 +45,9 @@ type AllowDenyList struct {
|
|||
// New constructs a new AllowDenyList based on a allow- and a
|
||||
// denylist. Only one of them can be not empty.
|
||||
func New(allow, deny map[string]struct{}) (*AllowDenyList, error) {
|
||||
once.Do(func() {
|
||||
regexp.DefaultMatchTimeout = regexpDefaultTimeout
|
||||
})
|
||||
if len(allow) != 0 && len(deny) != 0 {
|
||||
return nil, errors.New(
|
||||
"allowlist and denylist are both set, they are mutually exclusive, only one of them can be set",
|
||||
|
@ -62,7 +76,7 @@ func New(allow, deny map[string]struct{}) (*AllowDenyList, error) {
|
|||
func (l *AllowDenyList) Parse() error {
|
||||
regexes := make([]*regexp.Regexp, 0, len(l.list))
|
||||
for item := range l.list {
|
||||
r, err := regexp.Compile(item)
|
||||
r, err := regexp.Compile(item, regexpDefaultSpec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -99,25 +113,36 @@ func (l *AllowDenyList) Exclude(items []string) {
|
|||
}
|
||||
|
||||
// IsIncluded returns if the given item is included.
|
||||
func (l *AllowDenyList) IsIncluded(item string) bool {
|
||||
var matched bool
|
||||
func (l *AllowDenyList) IsIncluded(item string) (bool, error) {
|
||||
var (
|
||||
matched bool
|
||||
err error
|
||||
)
|
||||
for _, r := range l.rList {
|
||||
matched = r.MatchString(item)
|
||||
matched, err = r.MatchString(item)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if matched {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if l.isAllowList {
|
||||
return matched
|
||||
return matched, nil
|
||||
}
|
||||
|
||||
return !matched
|
||||
return !matched, nil
|
||||
}
|
||||
|
||||
// IsExcluded returns if the given item is excluded.
|
||||
func (l *AllowDenyList) IsExcluded(item string) bool {
|
||||
return !l.IsIncluded(item)
|
||||
func (l *AllowDenyList) IsExcluded(item string) (bool, error) {
|
||||
isIncluded, err := l.IsIncluded(item)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return !isIncluded, nil
|
||||
}
|
||||
|
||||
// Status returns the status of the AllowDenyList that can e.g. be passed into
|
||||
|
@ -137,7 +162,13 @@ func (l *AllowDenyList) Status() string {
|
|||
|
||||
// Test returns if the given family generator passes (is included in) the AllowDenyList
|
||||
func (l *AllowDenyList) Test(generator generator.FamilyGenerator) bool {
|
||||
return l.IsIncluded(generator.Name)
|
||||
isIncluded, err := l.IsIncluded(generator.Name)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Error while processing allow-deny entries for generator", "generator", generator.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
return isIncluded
|
||||
}
|
||||
|
||||
func copyList(l map[string]struct{}) map[string]struct{} {
|
||||
|
|
|
@ -17,8 +17,12 @@ limitations under the License.
|
|||
package allowdenylist
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
regexp "github.com/dlclark/regexp2"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
|
@ -76,7 +80,11 @@ func TestInclude(t *testing.T) {
|
|||
t.Fatal("expected Parse() to not fail")
|
||||
}
|
||||
|
||||
if !allowlist.IsIncluded("item1") {
|
||||
isIncluded, err := allowlist.IsIncluded("item1")
|
||||
if err != nil {
|
||||
t.Fatal("expected IsIncluded() to not fail")
|
||||
}
|
||||
if !isIncluded {
|
||||
t.Fatal("expected included item to be included")
|
||||
}
|
||||
})
|
||||
|
@ -93,7 +101,11 @@ func TestInclude(t *testing.T) {
|
|||
t.Fatalf("expected Parse() to not fail, but got error : %v", err)
|
||||
}
|
||||
|
||||
if !denylist.IsIncluded(item1) {
|
||||
isIncluded, err := denylist.IsIncluded(item1)
|
||||
if err != nil {
|
||||
t.Fatal("expected IsIncluded() to not fail")
|
||||
}
|
||||
if !isIncluded {
|
||||
t.Fatal("expected included item to be included")
|
||||
}
|
||||
})
|
||||
|
@ -103,13 +115,17 @@ func TestInclude(t *testing.T) {
|
|||
t.Fatal("expected New() to not fail")
|
||||
}
|
||||
|
||||
allowlist.Include([]string{"kube_.*_info"})
|
||||
allowlist.Include([]string{"kube_(?=secret).*_info"})
|
||||
err = allowlist.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("expected Parse() to not fail, but got error : %v", err)
|
||||
}
|
||||
|
||||
if !allowlist.IsIncluded("kube_secret_info") {
|
||||
isIncluded, err := allowlist.IsIncluded("kube_secret_info")
|
||||
if err != nil {
|
||||
t.Fatal("expected IsIncluded() to not fail")
|
||||
}
|
||||
if !isIncluded {
|
||||
t.Fatal("expected included item to be included")
|
||||
}
|
||||
})
|
||||
|
@ -124,22 +140,38 @@ func TestInclude(t *testing.T) {
|
|||
t.Fatal("expected New() to not fail")
|
||||
}
|
||||
|
||||
denylist.Exclude([]string{"kube_node_.*_cores", "kube_pod_.*_bytes"})
|
||||
denylist.Exclude([]string{"kube_(?=node.*cores|pod.*bytes)"})
|
||||
err = denylist.Parse()
|
||||
if err != nil {
|
||||
t.Fatalf("expected Parse() to not fail, but got error : %v", err)
|
||||
}
|
||||
|
||||
if denylist.IsExcluded(item1) {
|
||||
isExcluded, err := denylist.IsExcluded(item1)
|
||||
if err != nil {
|
||||
t.Fatal("expected IsExcluded() to not fail")
|
||||
}
|
||||
if isExcluded {
|
||||
t.Fatalf("expected included %s to be included", item1)
|
||||
}
|
||||
if denylist.IsIncluded(item2) {
|
||||
isIncluded, err := denylist.IsIncluded(item2)
|
||||
if err != nil {
|
||||
t.Fatal("expected IsIncluded() to not fail")
|
||||
}
|
||||
if isIncluded {
|
||||
t.Fatalf("expected included %s to be excluded", item2)
|
||||
}
|
||||
if denylist.IsIncluded(item3) {
|
||||
isIncluded, err = denylist.IsIncluded(item3)
|
||||
if err != nil {
|
||||
t.Fatal("expected IsIncluded() to not fail")
|
||||
}
|
||||
if isIncluded {
|
||||
t.Fatalf("expected included %s to be excluded", item3)
|
||||
}
|
||||
if denylist.IsExcluded(item4) {
|
||||
isExcluded, err = denylist.IsExcluded(item4)
|
||||
if err != nil {
|
||||
t.Fatal("expected IsExcluded() to not fail")
|
||||
}
|
||||
if isExcluded {
|
||||
t.Fatalf("expected included %s to be included", item4)
|
||||
}
|
||||
})
|
||||
|
@ -159,7 +191,11 @@ func TestExclude(t *testing.T) {
|
|||
t.Fatalf("expected Parse() to not fail, but got error : %v", err)
|
||||
}
|
||||
|
||||
if allowlist.IsIncluded(item1) {
|
||||
isIncluded, err := allowlist.IsIncluded(item1)
|
||||
if err != nil {
|
||||
t.Fatal("expected IsIncluded() to not fail")
|
||||
}
|
||||
if isIncluded {
|
||||
t.Fatal("expected excluded item to be excluded")
|
||||
}
|
||||
})
|
||||
|
@ -176,7 +212,11 @@ func TestExclude(t *testing.T) {
|
|||
t.Fatalf("expected Parse() to not fail, but got error : %v", err)
|
||||
}
|
||||
|
||||
if denylist.IsIncluded(item1) {
|
||||
isIncluded, err := denylist.IsIncluded(item1)
|
||||
if err != nil {
|
||||
t.Fatal("expected IsIncluded() to not fail")
|
||||
}
|
||||
if isIncluded {
|
||||
t.Fatal("expected excluded item to be excluded")
|
||||
}
|
||||
})
|
||||
|
@ -224,7 +264,8 @@ func TestStatus(t *testing.T) {
|
|||
allowlist, _ := New(map[string]struct{}{item1: {}, item2: {}}, map[string]struct{}{})
|
||||
actualStatusString := allowlist.Status()
|
||||
expectedRegexPattern := `^Including the following lists that were on allowlist: (item1|item2), (item2|item1)$`
|
||||
matched, _ := regexp.MatchString(expectedRegexPattern, actualStatusString)
|
||||
re := regexp.MustCompile(expectedRegexPattern, regexpDefaultSpec)
|
||||
matched, _ := re.MatchString(actualStatusString)
|
||||
if !matched {
|
||||
t.Errorf("expected status %q but got %q", expectedRegexPattern, actualStatusString)
|
||||
}
|
||||
|
@ -244,9 +285,38 @@ func TestStatus(t *testing.T) {
|
|||
denylist, _ := New(map[string]struct{}{}, map[string]struct{}{item1: {}, item2: {}})
|
||||
actualStatusString := denylist.Status()
|
||||
expectedRegexPattern := `^Excluding the following lists that were on denylist: (item1|item2), (item2|item1)$`
|
||||
matched, _ := regexp.MatchString(expectedRegexPattern, actualStatusString)
|
||||
re := regexp.MustCompile(expectedRegexPattern, regexpDefaultSpec)
|
||||
matched, _ := re.MatchString(actualStatusString)
|
||||
if !matched {
|
||||
t.Errorf("expected status %q but got %q", expectedRegexPattern, actualStatusString)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCatastrophicBacktrackTimeout(t *testing.T) {
|
||||
r, err := regexp.Compile("(.+)*\\?", 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var exp = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
|
||||
exp = strings.Repeat(exp, 2^10)
|
||||
|
||||
timeout := regexpDefaultTimeout
|
||||
t.Logf("regexp.DefaultMatchTimeout set to: %v", timeout)
|
||||
buffer := 500 * time.Millisecond
|
||||
t.Run(fmt.Sprint(timeout), func(t *testing.T) {
|
||||
r.MatchTimeout = timeout
|
||||
start := time.Now()
|
||||
_, err = r.FindStringMatch(exp)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "match timeout") {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("expected catastrophic backtracking error")
|
||||
}
|
||||
elapsed := time.Since(start)
|
||||
if elapsed > timeout+buffer {
|
||||
t.Fatalf("timeout %v exceeded: %v", timeout, elapsed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -33,11 +33,13 @@ import (
|
|||
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth" // Initialize common client auth plugins.
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
|
||||
yaml "sigs.k8s.io/yaml/goyaml.v3"
|
||||
|
||||
"github.com/KimMachineGun/automemlimit/memlimit"
|
||||
"github.com/oklog/run"
|
||||
|
@ -147,6 +149,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
|
|||
hash := md5HashAsMetricValue(configFile)
|
||||
configHash.WithLabelValues("config", filepath.Clean(got)).Set(hash)
|
||||
}
|
||||
opts = configureResourcesAndMetrics(opts, configFile)
|
||||
}
|
||||
|
||||
if opts.AutoGoMemlimit {
|
||||
|
@ -247,6 +250,7 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
|
|||
))
|
||||
|
||||
storeBuilder.WithUsingAPIServerCache(opts.UseAPIServerCache)
|
||||
storeBuilder.WithObjectLimit(opts.ObjectLimit)
|
||||
storeBuilder.WithGenerateStoresFunc(storeBuilder.DefaultGenerateStoresFunc())
|
||||
proc.StartReaper()
|
||||
|
||||
|
@ -261,9 +265,12 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
|
|||
if err := storeBuilder.WithAllowAnnotations(opts.AnnotationsAllowList); err != nil {
|
||||
return fmt.Errorf("failed to set up annotations allowlist: %v", err)
|
||||
}
|
||||
klog.InfoS("Using annotations allowlist", "annotationsAllowList", opts.AnnotationsAllowList)
|
||||
|
||||
if err := storeBuilder.WithAllowLabels(opts.LabelsAllowList); err != nil {
|
||||
return fmt.Errorf("failed to set up labels allowlist: %v", err)
|
||||
}
|
||||
klog.InfoS("Using labels allowlist", "labelsAllowList", opts.LabelsAllowList)
|
||||
|
||||
ksmMetricsRegistry.MustRegister(
|
||||
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
|
||||
|
@ -295,6 +302,8 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
|
|||
CRDsDeleteEventsCounter: crdsDeleteEventsCounter,
|
||||
CRDsCacheCountGauge: crdsCacheCountGauge,
|
||||
}
|
||||
// storeBuilder starts reflectors for the discovered GVKs, and as such, should close them too.
|
||||
storeBuilder.GVKToReflectorStopChanMap = &discovererInstance.GVKToReflectorStopChanMap
|
||||
// This starts a goroutine that will watch for any new GVKs to extract from CRDs.
|
||||
err = discovererInstance.StartDiscovery(ctx, kubeConfig)
|
||||
if err != nil {
|
||||
|
@ -315,18 +324,17 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
|
|||
)
|
||||
}
|
||||
|
||||
telemetryMux := buildTelemetryServer(ksmMetricsRegistry)
|
||||
telemetryMux := buildTelemetryServer(ksmMetricsRegistry, opts.AuthFilter, kubeConfig)
|
||||
telemetryListenAddress := net.JoinHostPort(opts.TelemetryHost, strconv.Itoa(opts.TelemetryPort))
|
||||
telemetryServer := http.Server{
|
||||
Handler: telemetryMux,
|
||||
ReadHeaderTimeout: 5 * time.Second}
|
||||
telemetryFlags := web.FlagConfig{
|
||||
WebListenAddresses: &[]string{telemetryListenAddress},
|
||||
WebSystemdSocket: new(bool),
|
||||
WebConfigFile: &tlsConfig,
|
||||
}
|
||||
|
||||
metricsMux := buildMetricsServer(m, durationVec, kubeClient)
|
||||
metricsMux := buildMetricsServer(m, durationVec, kubeClient, opts.AuthFilter, kubeConfig)
|
||||
metricsServerListenAddress := net.JoinHostPort(opts.Host, strconv.Itoa(opts.Port))
|
||||
metricsServer := http.Server{
|
||||
Handler: metricsMux,
|
||||
|
@ -337,7 +345,6 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
|
|||
}
|
||||
metricsFlags := web.FlagConfig{
|
||||
WebListenAddresses: &[]string{metricsServerListenAddress},
|
||||
WebSystemdSocket: new(bool),
|
||||
WebConfigFile: &tlsConfig,
|
||||
}
|
||||
|
||||
|
@ -375,14 +382,86 @@ func RunKubeStateMetrics(ctx context.Context, opts *options.Options) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func buildTelemetryServer(registry prometheus.Gatherer) *http.ServeMux {
|
||||
func configureResourcesAndMetrics(opts *options.Options, configFile []byte) *options.Options {
|
||||
// If the config file is set, we will overwrite the opts with the config file.
|
||||
// This is only needed for maps because the default behaviour of yaml.Unmarshal is to append keys (and overwrite any conflicting ones).
|
||||
config := options.NewOptions()
|
||||
err := yaml.Unmarshal(configFile, &config)
|
||||
if err == nil {
|
||||
if len(config.Resources) > 0 {
|
||||
opts.Resources = options.ResourceSet{}
|
||||
for resource := range config.Resources {
|
||||
opts.Resources[resource] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.MetricAllowlist) > 0 {
|
||||
opts.MetricAllowlist = options.MetricSet{}
|
||||
for metric := range config.MetricAllowlist {
|
||||
opts.MetricAllowlist[metric] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.MetricDenylist) > 0 {
|
||||
opts.MetricDenylist = options.MetricSet{}
|
||||
for metric := range config.MetricDenylist {
|
||||
opts.MetricDenylist[metric] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.MetricOptInList) > 0 {
|
||||
opts.MetricOptInList = options.MetricSet{}
|
||||
for metric := range config.MetricOptInList {
|
||||
opts.MetricOptInList[metric] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.LabelsAllowList) > 0 {
|
||||
opts.LabelsAllowList = options.LabelsAllowList{}
|
||||
for label, value := range config.LabelsAllowList {
|
||||
opts.LabelsAllowList[label] = value
|
||||
}
|
||||
}
|
||||
|
||||
if len(config.AnnotationsAllowList) > 0 {
|
||||
opts.AnnotationsAllowList = options.LabelsAllowList{}
|
||||
for annotation, value := range config.AnnotationsAllowList {
|
||||
opts.AnnotationsAllowList[annotation] = value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
klog.ErrorS(err, "failed to unmarshal configFile")
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func buildTelemetryServer(registry prometheus.Gatherer, authFilter bool, kubeConfig *rest.Config) *http.ServeMux {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
handler := logr.ToSlogHandler(klog.Background())
|
||||
sLogger := slog.NewLogLogger(handler, slog.LevelError)
|
||||
loghandler := logr.ToSlogHandler(klog.Background())
|
||||
sLogger := slog.NewLogLogger(loghandler, slog.LevelError)
|
||||
|
||||
// Add metricsPath
|
||||
mux.Handle(metricsPath, promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: sLogger}))
|
||||
metricsHandler := promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: sLogger})
|
||||
|
||||
// Add Authentication/Authorization via Kubernetes API
|
||||
if authFilter {
|
||||
client, err := rest.HTTPClientFor(kubeConfig)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to create HTTP client from config")
|
||||
}
|
||||
|
||||
metricsFilter, err := filters.WithAuthenticationAndAuthorization(kubeConfig, client)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to create auth handler")
|
||||
}
|
||||
|
||||
metricsHandler, err = metricsFilter(klog.Background(), metricsHandler)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to apply metrics filter")
|
||||
}
|
||||
}
|
||||
mux.Handle(metricsPath, metricsHandler)
|
||||
|
||||
// Add readyzPath
|
||||
mux.Handle(readyzPath, http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
|
@ -429,7 +508,7 @@ func handleClusterDelegationForProber(client kubernetes.Interface, probeType str
|
|||
}
|
||||
}
|
||||
|
||||
func buildMetricsServer(m *metricshandler.MetricsHandler, durationObserver prometheus.ObserverVec, client kubernetes.Interface) *http.ServeMux {
|
||||
func buildMetricsServer(m *metricshandler.MetricsHandler, durationObserver prometheus.ObserverVec, client kubernetes.Interface, authFilter bool, kubeConfig *rest.Config) *http.ServeMux {
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// TODO: This doesn't belong into serveMetrics
|
||||
|
@ -440,7 +519,29 @@ func buildMetricsServer(m *metricshandler.MetricsHandler, durationObserver prome
|
|||
mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
|
||||
|
||||
// Add metricsPath
|
||||
mux.Handle(metricsPath, promhttp.InstrumentHandlerDuration(durationObserver, m))
|
||||
metricsHandler := promhttp.InstrumentHandlerDuration(durationObserver, m)
|
||||
|
||||
// Add Authentication/Authorization via Kubernetes API
|
||||
if authFilter {
|
||||
client, err := rest.HTTPClientFor(kubeConfig)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to create HTTP client from config")
|
||||
}
|
||||
|
||||
metricsFilter, err := filters.WithAuthenticationAndAuthorization(kubeConfig, client)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to create auth handler")
|
||||
}
|
||||
|
||||
handler, err := metricsFilter(klog.Background(), metricsHandler)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to apply metrics filter")
|
||||
}
|
||||
metricsHandler = handler.(http.HandlerFunc)
|
||||
|
||||
}
|
||||
|
||||
mux.Handle(metricsPath, metricsHandler)
|
||||
|
||||
// Add livezPath
|
||||
mux.Handle(livezPath, handleClusterDelegationForProber(client, livezPath))
|
||||
|
@ -497,7 +598,7 @@ func resolveCustomResourceConfig(opts *options.Options) (customresourcestate.Con
|
|||
if file := opts.CustomResourceConfigFile; file != "" {
|
||||
f, err := os.Open(filepath.Clean(file))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Custom Resource State Metrics file could not be opened: %v", err)
|
||||
return nil, fmt.Errorf("unable to open Custom Resource State Metrics file: %v", err)
|
||||
}
|
||||
return yaml.NewDecoder(f), nil
|
||||
}
|
||||
|
|
|
@ -259,6 +259,7 @@ func TestFullScrapeCycle(t *testing.T) {
|
|||
# HELP kube_pod_status_scheduled [STABLE] Describes the status of the scheduling process for the pod.
|
||||
# HELP kube_pod_status_scheduled_time [STABLE] Unix timestamp when pod moved into scheduled status
|
||||
# HELP kube_pod_status_unschedulable [STABLE] Describes the unschedulable status for the pod.
|
||||
# HELP kube_pod_status_unscheduled_time Unix timestamp when pod moved into unscheduled status
|
||||
# HELP kube_pod_tolerations Information about the pod tolerations
|
||||
# TYPE kube_pod_annotations gauge
|
||||
# TYPE kube_pod_completion_time gauge
|
||||
|
@ -312,6 +313,7 @@ func TestFullScrapeCycle(t *testing.T) {
|
|||
# TYPE kube_pod_status_scheduled gauge
|
||||
# TYPE kube_pod_status_scheduled_time gauge
|
||||
# TYPE kube_pod_status_unschedulable gauge
|
||||
# TYPE kube_pod_status_unscheduled_time gauge
|
||||
# TYPE kube_pod_tolerations gauge
|
||||
kube_pod_container_info{namespace="default",pod="pod0",uid="abc-0",container="pod1_con1",image_spec="k8s.gcr.io/hyperkube2_spec",image="k8s.gcr.io/hyperkube2",image_id="docker://sha256:bbb",container_id="docker://cd456"} 1
|
||||
kube_pod_container_info{namespace="default",pod="pod0",uid="abc-0",container="pod1_con2",image_spec="k8s.gcr.io/hyperkube3_spec",image="k8s.gcr.io/hyperkube3",image_id="docker://sha256:ccc",container_id="docker://ef789"} 1
|
||||
|
@ -387,7 +389,7 @@ kube_pod_status_reason{namespace="default",pod="pod0",uid="abc-0",reason="Unexpe
|
|||
}
|
||||
}
|
||||
|
||||
telemetryMux := buildTelemetryServer(reg)
|
||||
telemetryMux := buildTelemetryServer(reg, false, nil)
|
||||
|
||||
req2 := httptest.NewRequest("GET", "http://localhost:8081/metrics", nil)
|
||||
|
||||
|
@ -979,3 +981,96 @@ func (f *fooFactory) ListWatch(customResourceClient interface{}, ns string, fiel
|
|||
},
|
||||
}
|
||||
}
|
||||
func TestConfigureResourcesAndMetrics(t *testing.T) {
|
||||
// Prepare a config file in YAML format
|
||||
configYAML := `
|
||||
"resources":
|
||||
"pod": {}
|
||||
"service": {}
|
||||
"metric_allowlist":
|
||||
"kube_pod_info": {}
|
||||
"metric_denylist":
|
||||
"kube_pod_labels": {}
|
||||
"metric_opt_in_list":
|
||||
"kube_pod_status_phase": {}
|
||||
"labels_allow_list":
|
||||
"labelX":
|
||||
- foo
|
||||
- bar
|
||||
"annotations_allow_list":
|
||||
"annotationY":
|
||||
- baz
|
||||
`
|
||||
opts := options.NewOptions()
|
||||
// Set some initial values to be overwritten
|
||||
opts.Resources = options.ResourceSet{"oldresource": {}}
|
||||
opts.MetricAllowlist = options.MetricSet{"oldallow": {}}
|
||||
opts.MetricDenylist = options.MetricSet{"olddeny": {}}
|
||||
opts.MetricOptInList = options.MetricSet{"oldoptin": {}}
|
||||
opts.LabelsAllowList = options.LabelsAllowList{"oldlabel": {"oldvalue"}}
|
||||
opts.AnnotationsAllowList = options.LabelsAllowList{"oldannotation": {"oldvalue"}}
|
||||
|
||||
newOpts := configureResourcesAndMetrics(opts, []byte(configYAML))
|
||||
|
||||
// Check resources
|
||||
expectedResources := []string{"pod", "service"}
|
||||
for _, r := range expectedResources {
|
||||
if _, ok := newOpts.Resources[r]; !ok {
|
||||
t.Errorf("expected resource %q in opts.Resources", r)
|
||||
}
|
||||
}
|
||||
if _, ok := newOpts.Resources["oldresource"]; ok {
|
||||
t.Errorf("expected oldresource to be overwritten")
|
||||
}
|
||||
|
||||
// Check metric allowlist
|
||||
if _, ok := newOpts.MetricAllowlist["kube_pod_info"]; !ok {
|
||||
t.Errorf("expected kube_pod_info in MetricAllowlist")
|
||||
}
|
||||
if _, ok := newOpts.MetricAllowlist["oldallow"]; ok {
|
||||
t.Errorf("expected oldallow to be overwritten")
|
||||
}
|
||||
|
||||
// Check metric denylist
|
||||
if _, ok := newOpts.MetricDenylist["kube_pod_labels"]; !ok {
|
||||
t.Errorf("expected kube_pod_labels in MetricDenylist")
|
||||
}
|
||||
if _, ok := newOpts.MetricDenylist["olddeny"]; ok {
|
||||
t.Errorf("expected olddeny to be overwritten")
|
||||
}
|
||||
|
||||
// Check metric opt-in list
|
||||
if _, ok := newOpts.MetricOptInList["kube_pod_status_phase"]; !ok {
|
||||
t.Errorf("expected kube_pod_status_phase in MetricOptInList")
|
||||
}
|
||||
if _, ok := newOpts.MetricOptInList["oldoptin"]; ok {
|
||||
t.Errorf("expected oldoptin to be overwritten")
|
||||
}
|
||||
|
||||
// Check labels allow list
|
||||
if vals, ok := newOpts.LabelsAllowList["labelX"]; !ok || len(vals) != 2 || vals[0] != "foo" || vals[1] != "bar" {
|
||||
t.Errorf("expected labelX with values [foo bar], got %v", vals)
|
||||
}
|
||||
if vals, ok := newOpts.LabelsAllowList["oldlabel"]; ok {
|
||||
t.Errorf("expected oldlabel to be overwritten, got %v", vals)
|
||||
}
|
||||
|
||||
// Check annotations allow list
|
||||
if vals, ok := newOpts.AnnotationsAllowList["annotationY"]; !ok || len(vals) != 1 || vals[0] != "baz" {
|
||||
t.Errorf("expected annotationY with value [baz], got %v", vals)
|
||||
}
|
||||
if vals, ok := newOpts.AnnotationsAllowList["oldannotation"]; ok {
|
||||
t.Errorf("expected oldannotation to be overwritten, got %v", vals)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestConfigureResourcesAndMetrics_InvalidYAML(t *testing.T) {
|
||||
opts := options.NewOptions()
|
||||
invalidYAML := []byte("invalid: [unclosed")
|
||||
// Should not panic or overwrite opts
|
||||
result := configureResourcesAndMetrics(opts, invalidYAML)
|
||||
if result != opts {
|
||||
t.Errorf("expected opts to be returned unchanged on invalid YAML")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ func customStore(_ []generator.FamilyGenerator,
|
|||
_ interface{},
|
||||
_ func(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher,
|
||||
_ bool,
|
||||
_ int64,
|
||||
) []cache.Store {
|
||||
stores := make([]cache.Store, 0, 2)
|
||||
stores = append(stores, newFakeStore(fakeMetricLists[0]))
|
||||
|
|
|
@ -57,7 +57,7 @@ type BuilderInterface interface {
|
|||
type BuildStoresFunc func(metricFamilies []generator.FamilyGenerator,
|
||||
expectedType interface{},
|
||||
listWatchFunc func(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher,
|
||||
useAPIServerCache bool,
|
||||
useAPIServerCache bool, limit int64,
|
||||
) []cache.Store
|
||||
|
||||
// BuildCustomResourceStoresFunc function signature that is used to return a list of custom resource cache.Store
|
||||
|
@ -65,7 +65,7 @@ type BuildCustomResourceStoresFunc func(resourceName string,
|
|||
metricFamilies []generator.FamilyGenerator,
|
||||
expectedType interface{},
|
||||
listWatchFunc func(customResourceClient interface{}, ns string, fieldSelector string) cache.ListerWatcher,
|
||||
useAPIServerCache bool,
|
||||
useAPIServerCache bool, limit int64,
|
||||
) []cache.Store
|
||||
|
||||
// AllowDenyLister interface for AllowDeny lister that can allow or exclude metrics by there names
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"k8s.io/klog/v2"
|
||||
yaml "sigs.k8s.io/yaml/goyaml.v3"
|
||||
)
|
||||
|
||||
//go:embed example_config.yaml
|
||||
|
|
|
@ -70,7 +70,7 @@ func compileCommon(c MetricMeta) (*compiledCommon, error) {
|
|||
}
|
||||
|
||||
func compileFamily(f Generator, resource Resource) (*compiledFamily, error) {
|
||||
labels := resource.Labels.Merge(f.Labels)
|
||||
labels := resource.Merge(f.Labels)
|
||||
|
||||
if f.Each.Type == metric.Info && !strings.HasSuffix(f.Name, "_info") {
|
||||
klog.InfoS("Info metric does not have _info suffix", "gvk", resource.GroupVersionKind.String(), "name", f.Name)
|
||||
|
@ -652,7 +652,7 @@ func compilePath(path []string) (out valuePath, _ error) {
|
|||
// negative index
|
||||
i += len(s)
|
||||
}
|
||||
if !(0 <= i && i < len(s)) {
|
||||
if i < 0 || i >= len(s) {
|
||||
return fmt.Errorf("list index out of range: %s", part)
|
||||
}
|
||||
return s[i]
|
||||
|
|
|
@ -79,6 +79,8 @@ type Options struct {
|
|||
Help bool `yaml:"help"`
|
||||
TrackUnscheduledPods bool `yaml:"track_unscheduled_pods"`
|
||||
UseAPIServerCache bool `yaml:"use_api_server_cache"`
|
||||
ObjectLimit int64 `yaml:"object_limit"`
|
||||
AuthFilter bool `yaml:"auth_filter"`
|
||||
}
|
||||
|
||||
// GetConfigFile is the getter for --config value.
|
||||
|
@ -143,11 +145,13 @@ func (o *Options) AddFlags(cmd *cobra.Command) {
|
|||
o.cmd.Flags().BoolVar(&o.TrackUnscheduledPods, "track-unscheduled-pods", false, "This configuration is used in conjunction with node configuration. When this configuration is true, node configuration is empty and the metric of unscheduled pods is fetched from the Kubernetes API Server. This is experimental.")
|
||||
o.cmd.Flags().BoolVarP(&o.Help, "help", "h", false, "Print Help text")
|
||||
o.cmd.Flags().BoolVarP(&o.UseAPIServerCache, "use-apiserver-cache", "", false, "Sets resourceVersion=0 for ListWatch requests, using cached resources from the apiserver instead of an etcd quorum read.")
|
||||
o.cmd.Flags().Int64Var(&o.ObjectLimit, "object-limit", 0, "The total number of objects to list per resource from the API Server. (experimental)")
|
||||
o.cmd.Flags().Int32Var(&o.Shard, "shard", int32(0), "The instances shard nominal (zero indexed) within the total number of shards. (default 0)")
|
||||
o.cmd.Flags().IntVar(&o.Port, "port", 8080, `Port to expose metrics on.`)
|
||||
o.cmd.Flags().IntVar(&o.TelemetryPort, "telemetry-port", 8081, `Port to expose kube-state-metrics self metrics on.`)
|
||||
o.cmd.Flags().IntVar(&o.TotalShards, "total-shards", 1, "The total number of shards. Sharding is disabled when total shards is set to 1.")
|
||||
o.cmd.Flags().StringVar(&o.Apiserver, "apiserver", "", `The URL of the apiserver to use as a master`)
|
||||
o.cmd.Flags().BoolVar(&o.AuthFilter, "auth-filter", false, "If true, requires authentication and authorization through Kubernetes API to access metrics endpoints")
|
||||
o.cmd.Flags().BoolVar(&o.AutoGoMemlimit, "auto-gomemlimit", false, "Automatically set GOMEMLIMIT to match container or system memory limit. (experimental)")
|
||||
o.cmd.Flags().Float64Var(&o.AutoGoMemlimitRatio, "auto-gomemlimit-ratio", float64(0.9), "The ratio of reserved GOMEMLIMIT memory to the detected maximum container or system memory. (experimental)")
|
||||
o.cmd.Flags().StringVar(&o.CustomResourceConfig, "custom-resource-state-config", "", "Inline Custom Resource State Metrics config YAML (experimental)")
|
||||
|
@ -158,12 +162,12 @@ func (o *Options) AddFlags(cmd *cobra.Command) {
|
|||
o.cmd.Flags().StringVar(&o.Pod, "pod", "", "Name of the pod that contains the kube-state-metrics container. "+autoshardingNotice)
|
||||
o.cmd.Flags().StringVar(&o.TLSConfig, "tls-config", "", "Path to the TLS configuration file")
|
||||
o.cmd.Flags().StringVar(&o.TelemetryHost, "telemetry-host", "::", `Host to expose kube-state-metrics self metrics on.`)
|
||||
o.cmd.Flags().StringVar(&o.Config, "config", "", "Path to the kube-state-metrics options config file")
|
||||
o.cmd.Flags().StringVar(&o.Config, "config", "", "Path to the kube-state-metrics options config YAML file. If this flag is set, the flags defined in the file override the command line flags.")
|
||||
o.cmd.Flags().StringVar((*string)(&o.Node), "node", "", "Name of the node that contains the kube-state-metrics pod. Most likely it should be passed via the downward API. This is used for daemonset sharding. Only available for resources (pod metrics) that support spec.nodeName fieldSelector. This is experimental.")
|
||||
o.cmd.Flags().Var(&o.AnnotationsAllowList, "metric-annotations-allowlist", "Comma-separated list of Kubernetes annotations keys that will be used in the resource' labels metric. By default the annotations metrics are not exposed. To include them, provide a list of resource names in their plural form and Kubernetes annotation keys you would like to allow for them (Example: '=namespaces=[kubernetes.io/team,...],pods=[kubernetes.io/team],...)'. A single '*' can be provided per resource instead to allow any annotations, but that has severe performance implications (Example: '=pods=[*]').")
|
||||
o.cmd.Flags().Var(&o.LabelsAllowList, "metric-labels-allowlist", "Comma-separated list of additional Kubernetes label keys that will be used in the resource' labels metric. By default the labels metrics are not exposed. To include them, provide a list of resource names in their plural form and Kubernetes label keys you would like to allow for them (Example: '=namespaces=[k8s-label-1,k8s-label-n,...],pods=[app],...)'. A single '*' can be provided per resource instead to allow any labels, but that has severe performance implications (Example: '=pods=[*]'). Additionally, an asterisk (*) can be provided as a key, which will resolve to all resources, i.e., assuming '--resources=deployments,pods', '=*=[*]' will resolve to '=deployments=[*],pods=[*]'.")
|
||||
o.cmd.Flags().Var(&o.MetricAllowlist, "metric-allowlist", "Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.")
|
||||
o.cmd.Flags().Var(&o.MetricDenylist, "metric-denylist", "Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or regex patterns. The allowlist and denylist are mutually exclusive.")
|
||||
o.cmd.Flags().Var(&o.MetricAllowlist, "metric-allowlist", "Comma-separated list of metrics to be exposed. This list comprises of exact metric names and/or *ECMAScript-based* regex patterns. The allowlist and denylist are mutually exclusive.")
|
||||
o.cmd.Flags().Var(&o.MetricDenylist, "metric-denylist", "Comma-separated list of metrics not to be enabled. This list comprises of exact metric names and/or *ECMAScript-based* regex patterns. The allowlist and denylist are mutually exclusive.")
|
||||
o.cmd.Flags().Var(&o.MetricOptInList, "metric-opt-in-list", "Comma-separated list of metrics which are opt-in and not enabled by default. This is in addition to the metric allow- and denylists")
|
||||
o.cmd.Flags().Var(&o.Namespaces, "namespaces", fmt.Sprintf("Comma-separated list of namespaces to be enabled. Defaults to %q", &DefaultNamespaces))
|
||||
o.cmd.Flags().Var(&o.NamespacesDenylist, "namespaces-denylist", "Comma-separated list of namespaces not to be enabled. If namespaces and namespaces-denylist are both set, only namespaces that are excluded in namespaces-denylist will be used.")
|
||||
|
@ -202,5 +206,9 @@ func (o *Options) Validate() error {
|
|||
return fmt.Errorf("value for --auto-gomemlimit-ratio=%f must be greater than 0 and less than or equal to 1", o.AutoGoMemlimitRatio)
|
||||
}
|
||||
|
||||
if o.ObjectLimit < 0 {
|
||||
return fmt.Errorf("value for --object-limit=%d must be equal or greater than 0", o.ObjectLimit)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ func TestResourceSetSet(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
cs := &ResourceSet{}
|
||||
gotError := cs.Set(test.Value)
|
||||
if !(((gotError == nil && !test.WantedError) || (gotError != nil && test.WantedError)) && reflect.DeepEqual(*cs, test.Wanted)) {
|
||||
if ((gotError != nil || test.WantedError) && (gotError == nil || !test.WantedError)) || !reflect.DeepEqual(*cs, test.Wanted) {
|
||||
t.Errorf("Test error for Desc: %s. Want: %+v. Got: %+v. Wanted Error: %v, Got Error: %v", test.Desc, test.Wanted, *cs, test.WantedError, gotError)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ func (s *shardedListWatch) List(options metav1.ListOptions) (runtime.Object, err
|
|||
res.Items = append(res.Items, runtime.RawExtension{Object: item})
|
||||
}
|
||||
}
|
||||
res.ListMeta.ResourceVersion = metaObj.GetResourceVersion()
|
||||
res.ResourceVersion = metaObj.GetResourceVersion()
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ func TestSharding(t *testing.T) {
|
|||
totalShards: 2,
|
||||
}
|
||||
|
||||
if !(s1.keep(cm) || s2.keep(cm)) {
|
||||
if !s1.keep(cm) && !s2.keep(cm) {
|
||||
t.Fatal("One shard must pick up the object.")
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ func GVRFromType(resourceName string, expectedType interface{}) (*schema.GroupVe
|
|||
}
|
||||
t, err := meta.TypeAccessor(expectedType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to get type accessor for %T: %w", expectedType, err)
|
||||
return nil, fmt.Errorf("failed to get type accessor for %T: %w", expectedType, err)
|
||||
}
|
||||
apiVersion := t.GetAPIVersion()
|
||||
g, v, found := strings.Cut(apiVersion, "/")
|
||||
|
|
|
@ -19,6 +19,7 @@ package watch
|
|||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
|
@ -27,8 +28,10 @@ import (
|
|||
|
||||
// ListWatchMetrics stores the pointers of kube_state_metrics_[list|watch]_total metrics.
|
||||
type ListWatchMetrics struct {
|
||||
WatchTotal *prometheus.CounterVec
|
||||
ListTotal *prometheus.CounterVec
|
||||
WatchRequestsTotal *prometheus.CounterVec
|
||||
ListRequestsTotal *prometheus.CounterVec
|
||||
ListObjectsLimit *prometheus.GaugeVec
|
||||
ListObjectsCurrent *prometheus.GaugeVec
|
||||
}
|
||||
|
||||
// NewListWatchMetrics takes in a prometheus registry and initializes
|
||||
|
@ -36,20 +39,34 @@ type ListWatchMetrics struct {
|
|||
// kube_state_metrics_watch_total metrics. It returns those registered metrics.
|
||||
func NewListWatchMetrics(r prometheus.Registerer) *ListWatchMetrics {
|
||||
return &ListWatchMetrics{
|
||||
WatchTotal: promauto.With(r).NewCounterVec(
|
||||
WatchRequestsTotal: promauto.With(r).NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "kube_state_metrics_watch_total",
|
||||
Help: "Number of total resource watches in kube-state-metrics",
|
||||
Help: "Number of total resource watch calls in kube-state-metrics",
|
||||
},
|
||||
[]string{"result", "resource"},
|
||||
),
|
||||
ListTotal: promauto.With(r).NewCounterVec(
|
||||
ListRequestsTotal: promauto.With(r).NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "kube_state_metrics_list_total",
|
||||
Help: "Number of total resource list in kube-state-metrics",
|
||||
Help: "Number of total resource list calls in kube-state-metrics",
|
||||
},
|
||||
[]string{"result", "resource"},
|
||||
),
|
||||
ListObjectsCurrent: promauto.With(r).NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "kube_state_metrics_list_objects",
|
||||
Help: "Number of resources listed in kube-state-metrics",
|
||||
},
|
||||
[]string{"resource"},
|
||||
),
|
||||
ListObjectsLimit: promauto.With(r).NewGaugeVec(
|
||||
prometheus.GaugeOpts{
|
||||
Name: "kube_state_metrics_list_objects_limit",
|
||||
Help: "Number of resource list limit in kube-state-metrics",
|
||||
},
|
||||
[]string{"resource"},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,34 +77,59 @@ type InstrumentedListerWatcher struct {
|
|||
metrics *ListWatchMetrics
|
||||
resource string
|
||||
useAPIServerCache bool
|
||||
limit int64
|
||||
}
|
||||
|
||||
// NewInstrumentedListerWatcher returns a new InstrumentedListerWatcher.
|
||||
func NewInstrumentedListerWatcher(lw cache.ListerWatcher, metrics *ListWatchMetrics, resource string, useAPIServerCache bool) cache.ListerWatcher {
|
||||
func NewInstrumentedListerWatcher(lw cache.ListerWatcher, metrics *ListWatchMetrics, resource string, useAPIServerCache bool, limit int64) cache.ListerWatcher {
|
||||
return &InstrumentedListerWatcher{
|
||||
lw: lw,
|
||||
metrics: metrics,
|
||||
resource: resource,
|
||||
useAPIServerCache: useAPIServerCache,
|
||||
limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
// List is a wrapper func around the cache.ListerWatcher.List func. It increases the success/error
|
||||
// / counters based on the outcome of the List operation it instruments.
|
||||
// It supports setting object limits, this means if it is set it will only list and process
|
||||
// n objects of the same resource type.
|
||||
func (i *InstrumentedListerWatcher) List(options metav1.ListOptions) (runtime.Object, error) {
|
||||
|
||||
if i.useAPIServerCache {
|
||||
options.ResourceVersion = "0"
|
||||
}
|
||||
|
||||
if i.limit > 0 {
|
||||
options.Limit = i.limit
|
||||
i.metrics.ListObjectsLimit.WithLabelValues(i.resource).Set(float64(i.limit))
|
||||
}
|
||||
|
||||
res, err := i.lw.List(options)
|
||||
|
||||
if err != nil {
|
||||
i.metrics.ListTotal.WithLabelValues("error", i.resource).Inc()
|
||||
i.metrics.ListRequestsTotal.WithLabelValues("error", i.resource).Inc()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i.metrics.ListTotal.WithLabelValues("success", i.resource).Inc()
|
||||
list, err := meta.ExtractList(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i.metrics.ListRequestsTotal.WithLabelValues("success", i.resource).Inc()
|
||||
|
||||
if i.limit > 0 {
|
||||
if int64(len(list)) > i.limit {
|
||||
meta.SetList(res, list[0:i.limit])
|
||||
i.metrics.ListObjectsCurrent.WithLabelValues(i.resource).Set(float64(i.limit))
|
||||
} else {
|
||||
i.metrics.ListObjectsCurrent.WithLabelValues(i.resource).Set(float64(len(list)))
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
||||
}
|
||||
|
||||
// Watch is a wrapper func around the cache.ListerWatcher.Watch func. It increases the success/error
|
||||
|
@ -95,10 +137,10 @@ func (i *InstrumentedListerWatcher) List(options metav1.ListOptions) (runtime.Ob
|
|||
func (i *InstrumentedListerWatcher) Watch(options metav1.ListOptions) (watch.Interface, error) {
|
||||
res, err := i.lw.Watch(options)
|
||||
if err != nil {
|
||||
i.metrics.WatchTotal.WithLabelValues("error", i.resource).Inc()
|
||||
i.metrics.WatchRequestsTotal.WithLabelValues("error", i.resource).Inc()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i.metrics.WatchTotal.WithLabelValues("success", i.resource).Inc()
|
||||
i.metrics.WatchRequestsTotal.WithLabelValues("success", i.resource).Inc()
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ echo "### Result"
|
|||
echo "old=${REF_TO_COMPARE} new=${REF_CURRENT}"
|
||||
|
||||
if [[ -z "${BENCHSTAT_OUTPUT_FILE}" ]]; then
|
||||
benchstat "${REF_TO_COMPARE}=${RESULT_TO_COMPARE}" "${REF_CURRENT}=${RESULT_CURRENT}"
|
||||
go tool golang.org/x/perf/cmd/benchstat "${REF_TO_COMPARE}=${RESULT_TO_COMPARE}" "${REF_CURRENT}=${RESULT_CURRENT}"
|
||||
else
|
||||
benchstat "${REF_TO_COMPARE}=${RESULT_TO_COMPARE}" "${REF_CURRENT}=${RESULT_CURRENT}" | tee -a "${BENCHSTAT_OUTPUT_FILE}"
|
||||
go tool golang.org/x/perf/cmd/benchstat "${REF_TO_COMPARE}=${RESULT_TO_COMPARE}" "${REF_CURRENT}=${RESULT_CURRENT}" | tee -a "${BENCHSTAT_OUTPUT_FILE}"
|
||||
fi
|
||||
|
|
10
tests/e2e.sh
10
tests/e2e.sh
|
@ -25,13 +25,13 @@ case $(uname -m) in
|
|||
esac
|
||||
|
||||
NODE_IMAGE_NAME="docker.io/kindest/node"
|
||||
KUBERNETES_VERSION=${KUBERNETES_VERSION:-"v1.32.0"}
|
||||
KUBERNETES_VERSION=${KUBERNETES_VERSION:-"v1.33.0"}
|
||||
KUBE_STATE_METRICS_LOG_DIR=./log
|
||||
KUBE_STATE_METRICS_CURRENT_IMAGE_NAME="registry.k8s.io/kube-state-metrics/kube-state-metrics"
|
||||
KUBE_STATE_METRICS_IMAGE_NAME="registry.k8s.io/kube-state-metrics/kube-state-metrics-${ARCH}"
|
||||
E2E_SETUP_KIND=${E2E_SETUP_KIND:-}
|
||||
E2E_SETUP_KUBECTL=${E2E_SETUP_KUBECTL:-}
|
||||
KIND_VERSION=v0.25.0
|
||||
KIND_VERSION=v0.29.0
|
||||
SUDO=${SUDO:-}
|
||||
|
||||
OS=$(uname -s | awk '{print tolower($0)}')
|
||||
|
@ -247,9 +247,15 @@ sleep 33
|
|||
klog_err=E$(date +%m%d)
|
||||
echo "check for errors in logs"
|
||||
|
||||
echo "running authfiler tests.."
|
||||
go test -v ./tests/e2e/auth-filter_test.go
|
||||
|
||||
echo "running discovery tests..."
|
||||
go test -race -v ./tests/e2e/discovery_test.go
|
||||
|
||||
echo "running object limits test..."
|
||||
go test -v ./tests/e2e/object-limits_test.go
|
||||
|
||||
echo "running hot-reload tests..."
|
||||
go test -v ./tests/e2e/hot-reload_test.go
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# End-to-End Tests
|
||||
|
||||
To run these tests, you need to provide two CLI flags:
|
||||
|
||||
* `--ksm-http-metrics-url`: url to access the kube-state-metrics service
|
||||
|
@ -5,7 +7,7 @@ To run these tests, you need to provide two CLI flags:
|
|||
|
||||
Example:
|
||||
|
||||
```
|
||||
```bash
|
||||
go test -v ./tests/e2e \
|
||||
--ksm-http-metrics-url=http://localhost:8080/ \
|
||||
--ksm-telemetry-url=http://localhost:8081/
|
||||
|
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
Copyright 2025 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"k8s.io/kube-state-metrics/v2/internal"
|
||||
"k8s.io/kube-state-metrics/v2/pkg/options"
|
||||
)
|
||||
|
||||
func TestAuthFilter(t *testing.T) {
|
||||
|
||||
// Initialise options.
|
||||
opts := options.NewOptions()
|
||||
cmd := options.InitCommand
|
||||
opts.AddFlags(cmd)
|
||||
klog.InfoS("options", "options", opts)
|
||||
|
||||
// Populate options, and parse them.
|
||||
opts.AuthFilter = true
|
||||
opts.Kubeconfig = os.Getenv("HOME") + "/.kube/config"
|
||||
if err := opts.Parse(); err != nil {
|
||||
t.Fatalf("failed to parse options: %v", err)
|
||||
}
|
||||
klog.InfoS("parsed options", "options", opts)
|
||||
|
||||
var err error
|
||||
// Create ClusterRole and ClusterRoleBinding
|
||||
klog.InfoS("Setting up RBAC")
|
||||
err = exec.Command("kubectl", "apply", "-f", "testdata/metrics-reader_role.yaml").Run()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set up clusterrole")
|
||||
}
|
||||
err = exec.Command("kubectl", "apply", "-f", "testdata/metrics-reader_rolebinding.yaml").Run()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set up clusterrolebinding")
|
||||
}
|
||||
// Make the process asynchronous.
|
||||
go internal.RunKubeStateMetricsWrapper(opts)
|
||||
klog.InfoS("started KSM")
|
||||
|
||||
// Wait for port 8080 to come up.
|
||||
err = wait.PollUntilContextTimeout(context.TODO(), 1*time.Second, 20*time.Second, true, func(_ context.Context) (bool, error) {
|
||||
conn, err := net.Dial("tcp", "localhost:8080")
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed while waiting for port 8080 to come up: %v", err)
|
||||
}
|
||||
klog.InfoS("port 8080 up")
|
||||
|
||||
// Wait for the metric to be available.
|
||||
ch := make(chan bool, 1)
|
||||
klog.InfoS("waiting for first metrics to become available")
|
||||
testMetric := `kube_configmap_info`
|
||||
|
||||
token, err := exec.Command("kubectl", "create", "token", "kube-state-metrics", "-n", "kube-system").Output()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create token %v", err)
|
||||
}
|
||||
klog.InfoS("token created")
|
||||
|
||||
err = wait.PollUntilContextTimeout(context.TODO(), 1*time.Second, 20*time.Second, true, func(_ context.Context) (bool, error) {
|
||||
out, err := exec.Command("curl", "-H", "Authorization: Bearer "+string(token), "localhost:8080/metrics").Output() // nolint:gosec
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if string(out) == "" {
|
||||
return false, nil
|
||||
}
|
||||
if strings.Contains(string(out), testMetric) {
|
||||
klog.InfoS("metrics fetched with bearer token")
|
||||
// Signal the process to exit, since we know the metrics are being generated as expected.
|
||||
|
||||
out, err := exec.Command("curl", "localhost:8080/metrics").Output()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if string(out) == "Unauthorized\n" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed while waiting for initial metrics to be available: %v", err)
|
||||
}
|
||||
|
||||
klog.InfoS("waiting for first telemetry metrics to become available")
|
||||
|
||||
telemetryTestMetric := "kube_state_metrics_build_info"
|
||||
err = wait.PollUntilContextTimeout(context.TODO(), 1*time.Second, 20*time.Second, true, func(_ context.Context) (bool, error) {
|
||||
out, err := exec.Command("curl", "-H", "Authorization: Bearer "+string(token), "localhost:8081/metrics").Output() // nolint:gosec
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if string(out) == "" {
|
||||
return false, nil
|
||||
}
|
||||
// Note: we use count to make sure that only one metrics handler is running
|
||||
if strings.Contains(string(out), telemetryTestMetric) {
|
||||
// klog.InfoS("metrics available", "metric", string(out))
|
||||
klog.InfoS("telemetry metrics fetched with bearer token")
|
||||
// Signal the process to exit, since we know the metrics are being generated as expected.
|
||||
|
||||
out, err := exec.Command("curl", "localhost:8081/metrics").Output()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if string(out) == "Unauthorized\n" {
|
||||
ch <- true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed while waiting for initial telemetry metrics to be available: %v", err)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
err = exec.Command("kubectl", "delete", "clusterrolebindings.rbac.authorization.k8s.io", "metrics-reader-rolebinding").Run()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to clean up clusterrolebinding")
|
||||
}
|
||||
err = exec.Command("kubectl", "delete", "clusterroles.rbac.authorization.k8s.io", "metrics-reader").Run()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to clean up clusterrole")
|
||||
}
|
||||
|
||||
// Wait for process to exit.
|
||||
select {
|
||||
case <-ch:
|
||||
t.Log("initial metrics are available")
|
||||
case <-time.After(40 * time.Second):
|
||||
t.Fatal("timed out waiting for test to pass, check the logs for more info")
|
||||
}
|
||||
}
|
|
@ -118,7 +118,7 @@ func (k *KSMClient) IsHealthz() (bool, error) {
|
|||
|
||||
func (k *KSMClient) writeMetrics(endpoint *url.URL, w io.Writer) error {
|
||||
if endpoint == nil {
|
||||
return errors.New("Endpoint is nil")
|
||||
return errors.New("endpoint is nil")
|
||||
}
|
||||
|
||||
u := *endpoint
|
||||
|
@ -165,7 +165,7 @@ func (f *Framework) ParseMetrics(metrics func(io.Writer) error) (map[string]*dto
|
|||
buf := &bytes.Buffer{}
|
||||
err := metrics(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to get metrics: %w", err)
|
||||
return nil, fmt.Errorf("failed to get metrics: %w", err)
|
||||
}
|
||||
|
||||
parser := &expfmt.TextParser{}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
Copyright 2025 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"k8s.io/kube-state-metrics/v2/internal"
|
||||
"k8s.io/kube-state-metrics/v2/pkg/options"
|
||||
)
|
||||
|
||||
func TestObjectLimits(t *testing.T) {
|
||||
|
||||
// Initialise options.
|
||||
opts := options.NewOptions()
|
||||
cmd := options.InitCommand
|
||||
opts.AddFlags(cmd)
|
||||
klog.InfoS("options", "options", opts)
|
||||
|
||||
// Populate options, and parse them.
|
||||
opts.ObjectLimit = 5
|
||||
opts.Resources = options.ResourceSet{"configmaps": struct{}{}}
|
||||
opts.Kubeconfig = os.Getenv("HOME") + "/.kube/config"
|
||||
if err := opts.Parse(); err != nil {
|
||||
t.Fatalf("failed to parse options: %v", err)
|
||||
}
|
||||
klog.InfoS("parsed options", "options", opts)
|
||||
|
||||
// Create ConfigMaps as Test Objects
|
||||
for i := 0; i < 6; i++ {
|
||||
err := exec.Command("kubectl", "create", "configmap", fmt.Sprintf("testcm%v", i)).Run() //nolint:gosec
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create configmap : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make the process asynchronous.
|
||||
go internal.RunKubeStateMetricsWrapper(opts)
|
||||
klog.InfoS("started KSM")
|
||||
|
||||
// Wait for port 8080 to come up.
|
||||
err := wait.PollUntilContextTimeout(context.TODO(), 1*time.Second, 20*time.Second, true, func(_ context.Context) (bool, error) {
|
||||
conn, err := net.Dial("tcp", "localhost:8080")
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed while waiting for port 8080 to come up: %v", err)
|
||||
}
|
||||
klog.InfoS("port 8080 up")
|
||||
|
||||
// Wait for the metric to be available.
|
||||
ch := make(chan bool, 1)
|
||||
klog.InfoS("waiting for first metrics to become available")
|
||||
testMetric := `kube_configmap_info{namespace="default"`
|
||||
err = wait.PollUntilContextTimeout(context.TODO(), 1*time.Second, 20*time.Second, true, func(_ context.Context) (bool, error) {
|
||||
out, err := exec.Command("curl", "localhost:8080/metrics").Output()
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if string(out) == "" {
|
||||
return false, nil
|
||||
}
|
||||
// Note: we use count to make sure that only one metrics handler is running
|
||||
if strings.Count(string(out), testMetric) == 5 {
|
||||
// klog.InfoS("metrics available", "metric", string(out))
|
||||
// Signal the process to exit, since we know the metrics are being generated as expected.
|
||||
ch <- true
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed while waiting for initial metrics to be available: %v", err)
|
||||
}
|
||||
|
||||
// Delete ConfigMaps as Test Objects
|
||||
for i := 0; i < 6; i++ {
|
||||
err := exec.Command("kubectl", "delete", "configmap", fmt.Sprintf("testcm%v", i)).Run() //nolint:gosec
|
||||
if err != nil {
|
||||
t.Fatalf("failed to delete configmap : %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for process to exit.
|
||||
select {
|
||||
case <-ch:
|
||||
t.Log("initial metrics are available")
|
||||
case <-time.After(40 * time.Second):
|
||||
t.Fatal("timed out waiting for test to pass, check the logs for more info")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: metrics-reader
|
||||
rules:
|
||||
- nonResourceURLs:
|
||||
- "/metrics"
|
||||
verbs:
|
||||
- get
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: metrics-reader-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: metrics-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kube-state-metrics
|
||||
namespace: kube-system
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue