diff --git a/.chloggen/add-hrq-k8sclusterreceiver.yaml b/.chloggen/add-hrq-k8sclusterreceiver.yaml new file mode 100755 index 000000000000..542d7757b768 --- /dev/null +++ b/.chloggen/add-hrq-k8sclusterreceiver.yaml @@ -0,0 +1,28 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: k8sclusterreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add hnc.x-k8s.io/v1alpha2 HierarchicalResourceQuota as a target for metrics collection (Optional) + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [29555] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] + \ No newline at end of file diff --git a/cmd/configschema/go.mod b/cmd/configschema/go.mod index 3409cde860c9..1dabded155a3 100644 --- a/cmd/configschema/go.mod +++ b/cmd/configschema/go.mod @@ -709,6 +709,7 @@ require ( k8s.io/kubelet v0.28.4 // indirect k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect sigs.k8s.io/controller-runtime v0.16.3 // indirect + sigs.k8s.io/hierarchical-namespaces v1.1.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/cmd/configschema/go.sum b/cmd/configschema/go.sum index 4dc123d12edd..8474ddbefa33 100644 --- a/cmd/configschema/go.sum +++ b/cmd/configschema/go.sum @@ -2335,6 +2335,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/hierarchical-namespaces v1.1.0 h1:aPf67ywj8hJaIl5TWPY5wgN+Gwa3oxQNyNqhAHznU3A= +sigs.k8s.io/hierarchical-namespaces v1.1.0/go.mod h1:nO9svtAyRqoWOAz65zZRjQx2u8fMA/EYbZxYVL/IqkU= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/cmd/otelcontribcol/go.mod b/cmd/otelcontribcol/go.mod index bd755612f907..44c3eb409154 100644 --- a/cmd/otelcontribcol/go.mod +++ b/cmd/otelcontribcol/go.mod @@ -719,6 +719,7 @@ require ( k8s.io/kubelet v0.28.4 // indirect k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect sigs.k8s.io/controller-runtime v0.16.3 // indirect + sigs.k8s.io/hierarchical-namespaces v1.1.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/cmd/otelcontribcol/go.sum b/cmd/otelcontribcol/go.sum index e3bca3262b91..7dcfc3c4de4b 100644 --- a/cmd/otelcontribcol/go.sum +++ b/cmd/otelcontribcol/go.sum @@ -2335,6 +2335,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/hierarchical-namespaces v1.1.0 h1:aPf67ywj8hJaIl5TWPY5wgN+Gwa3oxQNyNqhAHznU3A= +sigs.k8s.io/hierarchical-namespaces v1.1.0/go.mod h1:nO9svtAyRqoWOAz65zZRjQx2u8fMA/EYbZxYVL/IqkU= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/go.mod b/go.mod index e8f106acc95b..b480b26930cc 100644 --- a/go.mod +++ b/go.mod @@ -708,6 +708,7 @@ require ( k8s.io/kubelet v0.28.4 // indirect k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect sigs.k8s.io/controller-runtime v0.16.3 // indirect + sigs.k8s.io/hierarchical-namespaces v1.1.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/go.sum b/go.sum index dc2e6e82799d..cd805fcd445a 100644 --- a/go.sum +++ b/go.sum @@ -2342,6 +2342,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/hierarchical-namespaces v1.1.0 h1:aPf67ywj8hJaIl5TWPY5wgN+Gwa3oxQNyNqhAHznU3A= +sigs.k8s.io/hierarchical-namespaces v1.1.0/go.mod h1:nO9svtAyRqoWOAz65zZRjQx2u8fMA/EYbZxYVL/IqkU= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/receiver/k8sclusterreceiver/README.md b/receiver/k8sclusterreceiver/README.md index 9cd60947cfd1..d490b5b0e11a 100644 --- a/receiver/k8sclusterreceiver/README.md +++ b/receiver/k8sclusterreceiver/README.md @@ -342,3 +342,35 @@ Add the following rules to your ClusterRole: - watch ``` +### HierarchicalResourceQuota + +You can enable [HierarchicalResourceQuota](https://github.com/kubernetes-sigs/hierarchical-namespaces) support to collect aggregate quota restrictions as metrics. + +Example: + +```yaml + k8s_cluster: + resource_attributes: + k8s.hierarchicalresourcequota.name: + enabled: true + k8s.hierarchicalresourcequota.uid: + enabled: true + metrics: + k8s.hierarchical_resource_quota.hard_limit: + enabled: true + k8s.hierarchical_resource_quota.used: + enabled: true +``` + +Add the following rules to your ClusterRole: + +```yaml +- apiGroups: + - hnc.x-k8s.io + resources: + - hierarchicalresourcequotas + verbs: + - get + - list + - watch +``` \ No newline at end of file diff --git a/receiver/k8sclusterreceiver/documentation.md b/receiver/k8sclusterreceiver/documentation.md index 5cbe995baaa0..5722ba97d8b1 100644 --- a/receiver/k8sclusterreceiver/documentation.md +++ b/receiver/k8sclusterreceiver/documentation.md @@ -396,6 +396,34 @@ metrics: enabled: true ``` +### k8s.hierarchical_resource_quota.hard_limit + +The upper limit for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {resource} | Gauge | Int | + +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| resource | the name of the resource on which the quota is applied | Any Str | + +### k8s.hierarchical_resource_quota.used + +The usage for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| {resource} | Gauge | Int | + +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| resource | the name of the resource on which the quota is applied | Any Str | + ### k8s.node.condition The condition of a particular Node. @@ -432,6 +460,8 @@ Current status reason of the pod (1 - Evicted, 2 - NodeAffinity, 3 - NodeLost, 4 | k8s.daemonset.uid | The k8s daemonset uid. | Any Str | true | | k8s.deployment.name | The name of the Deployment. | Any Str | true | | k8s.deployment.uid | The UID of the Deployment. | Any Str | true | +| k8s.hierarchicalresourcequota.name | The k8s HierarchicalresourceQuota name. | Any Str | false | +| k8s.hierarchicalresourcequota.uid | The k8s HierarchicalResourceQuota uid. | Any Str | false | | k8s.hpa.name | The k8s hpa name. | Any Str | true | | k8s.hpa.uid | The k8s hpa uid. | Any Str | true | | k8s.job.name | The k8s pod name. | Any Str | true | diff --git a/receiver/k8sclusterreceiver/factory_test.go b/receiver/k8sclusterreceiver/factory_test.go index 3d41b3e28d03..39b392ecb458 100644 --- a/receiver/k8sclusterreceiver/factory_test.go +++ b/receiver/k8sclusterreceiver/factory_test.go @@ -16,6 +16,9 @@ import ( "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/receiver/receivertest" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/dynamic" + fakeDynamic "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" @@ -93,6 +96,9 @@ func newTestReceiver(t *testing.T, cfg *Config) *kubernetesReceiver { rcvr.resourceWatcher.makeClient = func(_ k8sconfig.APIConfig) (kubernetes.Interface, error) { return fake.NewSimpleClientset(), nil } + rcvr.resourceWatcher.makeDynamicClient = func(_ k8sconfig.APIConfig) (dynamic.Interface, error) { + return fakeDynamic.NewSimpleDynamicClient(runtime.NewScheme()), nil + } rcvr.resourceWatcher.makeOpenShiftQuotaClient = func(_ k8sconfig.APIConfig) (quotaclientset.Interface, error) { return fakeQuota.NewSimpleClientset(), nil } diff --git a/receiver/k8sclusterreceiver/go.mod b/receiver/k8sclusterreceiver/go.mod index c8b4cfaa9080..2ba40df8a6ad 100644 --- a/receiver/k8sclusterreceiver/go.mod +++ b/receiver/k8sclusterreceiver/go.mod @@ -30,6 +30,7 @@ require ( k8s.io/api v0.28.4 k8s.io/apimachinery v0.28.4 k8s.io/client-go v0.28.4 + sigs.k8s.io/hierarchical-namespaces v1.1.0 ) require ( @@ -128,6 +129,7 @@ require ( k8s.io/klog/v2 v2.100.1 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + sigs.k8s.io/controller-runtime v0.11.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect sigs.k8s.io/yaml v1.3.0 // indirect diff --git a/receiver/k8sclusterreceiver/go.sum b/receiver/k8sclusterreceiver/go.sum index 0b5256680697..d2838d4263e6 100644 --- a/receiver/k8sclusterreceiver/go.sum +++ b/receiver/k8sclusterreceiver/go.sum @@ -12,8 +12,8 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -304,10 +304,11 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -784,6 +785,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -830,6 +832,10 @@ k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/controller-runtime v0.11.0 h1:DqO+c8mywcZLFJWILq4iktoECTyn30Bkj0CwgqMpZWQ= +sigs.k8s.io/controller-runtime v0.11.0/go.mod h1:KKwLiTooNGu+JmLZGn9Sl3Gjmfj66eMbCQznLP5zcqA= +sigs.k8s.io/hierarchical-namespaces v1.1.0 h1:aPf67ywj8hJaIl5TWPY5wgN+Gwa3oxQNyNqhAHznU3A= +sigs.k8s.io/hierarchical-namespaces v1.1.0/go.mod h1:nO9svtAyRqoWOAz65zZRjQx2u8fMA/EYbZxYVL/IqkU= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= diff --git a/receiver/k8sclusterreceiver/internal/collection/collector.go b/receiver/k8sclusterreceiver/internal/collection/collector.go index 4269d4f112ae..cbb83cd97edd 100644 --- a/receiver/k8sclusterreceiver/internal/collection/collector.go +++ b/receiver/k8sclusterreceiver/internal/collection/collector.go @@ -10,16 +10,19 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver" + "go.uber.org/zap" appsv1 "k8s.io/api/apps/v1" autoscalingv2 "k8s.io/api/autoscaling/v2" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/cronjob" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/demonset" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/deployment" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvk" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/hpa" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/jobs" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" @@ -104,6 +107,14 @@ func (dc *DataCollector) CollectMetricData(currentTime time.Time) pmetric.Metric dc.metadataStore.ForEach(gvk.ClusterResourceQuota, func(o any) { clusterresourcequota.RecordMetrics(dc.metricsBuilder, o.(*quotav1.ClusterResourceQuota), ts) }) + dc.metadataStore.ForEach(gvk.HierarchicalResourceQuota, func(o any) { + hrq, err := hierarchicalresourcequota.Parse(o.(*unstructured.Unstructured)) + if err != nil { + dc.settings.Logger.Error("failed to parse hierarchicalresourcequota object", zap.Error(err)) + } else { + hierarchicalresourcequota.RecordMetrics(dc.metricsBuilder, hrq, ts) + } + }) m := dc.metricsBuilder.Emit() customRMs.MoveAndAppendTo(m.ResourceMetrics()) diff --git a/receiver/k8sclusterreceiver/internal/gvk/gvk.go b/receiver/k8sclusterreceiver/internal/gvk/gvk.go index 58e540821077..8fc587597332 100644 --- a/receiver/k8sclusterreceiver/internal/gvk/gvk.go +++ b/receiver/k8sclusterreceiver/internal/gvk/gvk.go @@ -7,18 +7,19 @@ import "k8s.io/apimachinery/pkg/runtime/schema" // Kubernetes group version kinds var ( - Pod = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"} - Node = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Node"} - Namespace = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"} - ReplicationController = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ReplicationController"} - ResourceQuota = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ResourceQuota"} - Service = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"} - DaemonSet = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "DaemonSet"} - Deployment = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"} - ReplicaSet = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "ReplicaSet"} - StatefulSet = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"} - Job = schema.GroupVersionKind{Group: "batch", Version: "v1", Kind: "Job"} - CronJob = schema.GroupVersionKind{Group: "batch", Version: "v1", Kind: "CronJob"} - HorizontalPodAutoscaler = schema.GroupVersionKind{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscaler"} - ClusterResourceQuota = schema.GroupVersionKind{Group: "quota", Version: "v1", Kind: "ClusterResourceQuota"} + Pod = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"} + Node = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Node"} + Namespace = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"} + ReplicationController = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ReplicationController"} + ResourceQuota = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "ResourceQuota"} + Service = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Service"} + DaemonSet = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "DaemonSet"} + Deployment = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"} + ReplicaSet = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "ReplicaSet"} + StatefulSet = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "StatefulSet"} + Job = schema.GroupVersionKind{Group: "batch", Version: "v1", Kind: "Job"} + CronJob = schema.GroupVersionKind{Group: "batch", Version: "v1", Kind: "CronJob"} + HorizontalPodAutoscaler = schema.GroupVersionKind{Group: "autoscaling", Version: "v2", Kind: "HorizontalPodAutoscaler"} + ClusterResourceQuota = schema.GroupVersionKind{Group: "quota", Version: "v1", Kind: "ClusterResourceQuota"} + HierarchicalResourceQuota = schema.GroupVersionKind{Group: "hnc.x-k8s.io", Version: "v1alpha2", Kind: "HierarchicalResourceQuota"} ) diff --git a/receiver/k8sclusterreceiver/internal/gvr/gvr.go b/receiver/k8sclusterreceiver/internal/gvr/gvr.go new file mode 100644 index 000000000000..ddfb434f9377 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/gvr/gvr.go @@ -0,0 +1,11 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package gvr // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvr" + +import "k8s.io/apimachinery/pkg/runtime/schema" + +// Kubernetes group version resources +var ( + HierarchicalResourceQuota = schema.GroupVersionResource{Group: "hnc.x-k8s.io", Version: "v1alpha2", Resource: "hierarchicalresourcequotas"} +) diff --git a/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/hierarchicalresourcequotas.go b/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/hierarchicalresourcequotas.go new file mode 100644 index 000000000000..f069d09654a3 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/hierarchicalresourcequotas.go @@ -0,0 +1,93 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package hierarchicalresourcequota // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota" + +import ( + "fmt" + "strings" + + "go.opentelemetry.io/collector/pdata/pcommon" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/hierarchical-namespaces/api/v1alpha2" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" +) + +func RecordMetrics(mb *metadata.MetricsBuilder, hrq *v1alpha2.HierarchicalResourceQuota, ts pcommon.Timestamp) { + for k, v := range hrq.Status.Hard { + val := parseQuantityValue(k, v) + mb.RecordK8sHierarchicalResourceQuotaHardLimitDataPoint(ts, val, string(k)) + } + for k, v := range hrq.Status.Used { + val := parseQuantityValue(k, v) + mb.RecordK8sHierarchicalResourceQuotaUsedDataPoint(ts, val, string(k)) + } + rb := mb.NewResourceBuilder() + rb.SetK8sHierarchicalresourcequotaUID(string(hrq.UID)) + rb.SetK8sHierarchicalresourcequotaName(hrq.Name) + rb.SetK8sNamespaceName(hrq.Namespace) + mb.EmitForResource(metadata.WithResource(rb.Emit())) +} + +func Parse(unstructedHRQ *unstructured.Unstructured) (*v1alpha2.HierarchicalResourceQuota, error) { + name, found, err := unstructured.NestedString(unstructedHRQ.Object, "metadata", "name") + if err != nil { + return nil, err + } else if !found { + return nil, fmt.Errorf("not found HierarchicalResourceQuota %s property", "name") + } + uid, found, err := unstructured.NestedString(unstructedHRQ.Object, "metadata", "uid") + if err != nil { + return nil, err + } else if !found { + return nil, fmt.Errorf("not found HierarchicalResourceQuota %s property", "uid") + } + namespace, found, err := unstructured.NestedString(unstructedHRQ.Object, "metadata", "namespace") + if err != nil { + return nil, err + } else if !found { + return nil, fmt.Errorf("not found HierarchicalResourceQuota %s property", "namespace") + } + + hrqs := v1alpha2.HierarchicalResourceQuotaStatus{ + Hard: corev1.ResourceList{}, + Used: corev1.ResourceList{}, + } + statusHard, found, err := unstructured.NestedMap(unstructedHRQ.Object, "status", "hard") + if err != nil { + return nil, err + } else if found { + for k, v := range statusHard { + hrqs.Hard[corev1.ResourceName(k)] = resource.MustParse(v.(string)) + } + } + statusUsed, found, err := unstructured.NestedMap(unstructedHRQ.Object, "status", "used") + if err != nil { + return nil, err + } else if found { + for k, v := range statusUsed { + hrqs.Used[corev1.ResourceName(k)] = resource.MustParse(v.(string)) + } + } + hrq := &v1alpha2.HierarchicalResourceQuota{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + UID: types.UID(uid), + }, + Status: hrqs, + } + return hrq, nil +} + +func parseQuantityValue(k corev1.ResourceName, v resource.Quantity) int64 { + if strings.HasSuffix(k.String(), ".cpu") { + return v.MilliValue() + } + return v.Value() +} diff --git a/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/hierarchicalresourcequotas_test.go b/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/hierarchicalresourcequotas_test.go new file mode 100644 index 000000000000..de86b755b3b6 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/hierarchicalresourcequotas_test.go @@ -0,0 +1,105 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package hierarchicalresourcequota + +import ( + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver/receivertest" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/hierarchical-namespaces/api/v1alpha2" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/testutils" +) + +func TestHierarchicalRequestQuotaMetrics(t *testing.T) { + hrq := testutils.NewHierarchicalResourceQuota("1") + ts := pcommon.Timestamp(time.Now().UnixNano()) + mbc := metadata.DefaultMetricsBuilderConfig() + mb := metadata.NewMetricsBuilder(mbc, receivertest.NewNopCreateSettings()) + RecordMetrics(mb, hrq, ts) + m := mb.Emit() + + expected := pmetric.NewMetrics() + require.NoError(t, pmetrictest.CompareMetrics(expected, m)) +} + +func TestHierarchicalRequestQuotaMetricsEnabled(t *testing.T) { + hrq := testutils.NewHierarchicalResourceQuota("1") + ts := pcommon.Timestamp(time.Now().UnixNano()) + mbc := metadata.DefaultMetricsBuilderConfig() + mbc.ResourceAttributes.K8sHierarchicalresourcequotaName.Enabled = true + mbc.ResourceAttributes.K8sHierarchicalresourcequotaUID.Enabled = true + mbc.Metrics.K8sHierarchicalResourceQuotaHardLimit.Enabled = true + mbc.Metrics.K8sHierarchicalResourceQuotaUsed.Enabled = true + mb := metadata.NewMetricsBuilder(mbc, receivertest.NewNopCreateSettings()) + RecordMetrics(mb, hrq, ts) + m := mb.Emit() + + expected, err := golden.ReadMetrics(filepath.Join("testdata", "expected.yaml")) + require.NoError(t, err) + require.NoError(t, pmetrictest.CompareMetrics(expected, m, + pmetrictest.IgnoreTimestamp(), + pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreResourceMetricsOrder(), + pmetrictest.IgnoreMetricsOrder(), + pmetrictest.IgnoreScopeMetricsOrder(), + pmetrictest.IgnoreMetricDataPointsOrder(), + ), + ) +} + +func TestParse(t *testing.T) { + original := &unstructured.Unstructured{ + Object: map[string]any{ + "metadata": map[string]any{ + "name": "test-hierarchicalresourcequota-1", + "uid": "test-hierarchicalresourcequota-1-uid", + "namespace": "default", + }, + "status": map[string]any{ + "hard": map[string]any{ + "requests.cpu": "1", + "requests.memory": "1Gi", + }, + "used": map[string]any{ + "requests.cpu": "500m", + "requests.memory": "512Mi", + }, + }, + }, + } + wantHRQ := &v1alpha2.HierarchicalResourceQuota{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-hierarchicalresourcequota-1", + Namespace: "default", + UID: "test-hierarchicalresourcequota-1-uid", + }, + Status: v1alpha2.HierarchicalResourceQuotaStatus{ + Hard: corev1.ResourceList{ + corev1.ResourceName("requests.cpu"): resource.MustParse("1"), + corev1.ResourceName("requests.memory"): resource.MustParse("1Gi"), + }, + Used: corev1.ResourceList{ + corev1.ResourceName("requests.cpu"): resource.MustParse("500m"), + corev1.ResourceName("requests.memory"): resource.MustParse("512Mi"), + }, + }, + } + actual, err := Parse(original) + assert.NoError(t, err) + assert.Equal(t, wantHRQ, actual) +} diff --git a/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/testdata/expected.yaml b/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/testdata/expected.yaml new file mode 100644 index 000000000000..d736a77e1ec9 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/hierarchicalresourcequota/testdata/expected.yaml @@ -0,0 +1,48 @@ +resourceMetrics: + - resource: + attributes: + - key: k8s.namespace.name + value: + stringValue: test-namespace + - key: k8s.hierarchicalresourcequota.name + value: + stringValue: test-hierarchicalresourcequota-1 + - key: k8s.hierarchicalresourcequota.uid + value: + stringValue: test-hierarchicalresourcequota-1-uid + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: The usage for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores + gauge: + dataPoints: + - asInt: "500" + attributes: + - key: resource + value: + stringValue: requests.cpu + - asInt: "536870912" + attributes: + - key: resource + value: + stringValue: requests.memory + name: k8s.hierarchical_resource_quota.used + unit: "{resource}" + - description: The upper limit for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores + gauge: + dataPoints: + - asInt: "1000" + attributes: + - key: resource + value: + stringValue: requests.cpu + - asInt: "1073741824" + attributes: + - key: resource + value: + stringValue: requests.memory + name: k8s.hierarchical_resource_quota.hard_limit + unit: "{resource}" + scope: + name: otelcol/k8sclusterreceiver + version: latest diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_config.go b/receiver/k8sclusterreceiver/internal/metadata/generated_config.go index 163b8b4bbddf..6b376cd7b258 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_config.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_config.go @@ -25,50 +25,52 @@ func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { // MetricsConfig provides config for k8s_cluster metrics. type MetricsConfig struct { - K8sContainerCPULimit MetricConfig `mapstructure:"k8s.container.cpu_limit"` - K8sContainerCPURequest MetricConfig `mapstructure:"k8s.container.cpu_request"` - K8sContainerEphemeralstorageLimit MetricConfig `mapstructure:"k8s.container.ephemeralstorage_limit"` - K8sContainerEphemeralstorageRequest MetricConfig `mapstructure:"k8s.container.ephemeralstorage_request"` - K8sContainerMemoryLimit MetricConfig `mapstructure:"k8s.container.memory_limit"` - K8sContainerMemoryRequest MetricConfig `mapstructure:"k8s.container.memory_request"` - K8sContainerReady MetricConfig `mapstructure:"k8s.container.ready"` - K8sContainerRestarts MetricConfig `mapstructure:"k8s.container.restarts"` - K8sContainerStorageLimit MetricConfig `mapstructure:"k8s.container.storage_limit"` - K8sContainerStorageRequest MetricConfig `mapstructure:"k8s.container.storage_request"` - K8sCronjobActiveJobs MetricConfig `mapstructure:"k8s.cronjob.active_jobs"` - K8sDaemonsetCurrentScheduledNodes MetricConfig `mapstructure:"k8s.daemonset.current_scheduled_nodes"` - K8sDaemonsetDesiredScheduledNodes MetricConfig `mapstructure:"k8s.daemonset.desired_scheduled_nodes"` - K8sDaemonsetMisscheduledNodes MetricConfig `mapstructure:"k8s.daemonset.misscheduled_nodes"` - K8sDaemonsetReadyNodes MetricConfig `mapstructure:"k8s.daemonset.ready_nodes"` - K8sDeploymentAvailable MetricConfig `mapstructure:"k8s.deployment.available"` - K8sDeploymentDesired MetricConfig `mapstructure:"k8s.deployment.desired"` - K8sHpaCurrentReplicas MetricConfig `mapstructure:"k8s.hpa.current_replicas"` - K8sHpaDesiredReplicas MetricConfig `mapstructure:"k8s.hpa.desired_replicas"` - K8sHpaMaxReplicas MetricConfig `mapstructure:"k8s.hpa.max_replicas"` - K8sHpaMinReplicas MetricConfig `mapstructure:"k8s.hpa.min_replicas"` - K8sJobActivePods MetricConfig `mapstructure:"k8s.job.active_pods"` - K8sJobDesiredSuccessfulPods MetricConfig `mapstructure:"k8s.job.desired_successful_pods"` - K8sJobFailedPods MetricConfig `mapstructure:"k8s.job.failed_pods"` - K8sJobMaxParallelPods MetricConfig `mapstructure:"k8s.job.max_parallel_pods"` - K8sJobSuccessfulPods MetricConfig `mapstructure:"k8s.job.successful_pods"` - K8sNamespacePhase MetricConfig `mapstructure:"k8s.namespace.phase"` - K8sNodeCondition MetricConfig `mapstructure:"k8s.node.condition"` - K8sPodPhase MetricConfig `mapstructure:"k8s.pod.phase"` - K8sPodStatusReason MetricConfig `mapstructure:"k8s.pod.status_reason"` - K8sReplicasetAvailable MetricConfig `mapstructure:"k8s.replicaset.available"` - K8sReplicasetDesired MetricConfig `mapstructure:"k8s.replicaset.desired"` - K8sReplicationControllerAvailable MetricConfig `mapstructure:"k8s.replication_controller.available"` - K8sReplicationControllerDesired MetricConfig `mapstructure:"k8s.replication_controller.desired"` - K8sResourceQuotaHardLimit MetricConfig `mapstructure:"k8s.resource_quota.hard_limit"` - K8sResourceQuotaUsed MetricConfig `mapstructure:"k8s.resource_quota.used"` - K8sStatefulsetCurrentPods MetricConfig `mapstructure:"k8s.statefulset.current_pods"` - K8sStatefulsetDesiredPods MetricConfig `mapstructure:"k8s.statefulset.desired_pods"` - K8sStatefulsetReadyPods MetricConfig `mapstructure:"k8s.statefulset.ready_pods"` - K8sStatefulsetUpdatedPods MetricConfig `mapstructure:"k8s.statefulset.updated_pods"` - OpenshiftAppliedclusterquotaLimit MetricConfig `mapstructure:"openshift.appliedclusterquota.limit"` - OpenshiftAppliedclusterquotaUsed MetricConfig `mapstructure:"openshift.appliedclusterquota.used"` - OpenshiftClusterquotaLimit MetricConfig `mapstructure:"openshift.clusterquota.limit"` - OpenshiftClusterquotaUsed MetricConfig `mapstructure:"openshift.clusterquota.used"` + K8sContainerCPULimit MetricConfig `mapstructure:"k8s.container.cpu_limit"` + K8sContainerCPURequest MetricConfig `mapstructure:"k8s.container.cpu_request"` + K8sContainerEphemeralstorageLimit MetricConfig `mapstructure:"k8s.container.ephemeralstorage_limit"` + K8sContainerEphemeralstorageRequest MetricConfig `mapstructure:"k8s.container.ephemeralstorage_request"` + K8sContainerMemoryLimit MetricConfig `mapstructure:"k8s.container.memory_limit"` + K8sContainerMemoryRequest MetricConfig `mapstructure:"k8s.container.memory_request"` + K8sContainerReady MetricConfig `mapstructure:"k8s.container.ready"` + K8sContainerRestarts MetricConfig `mapstructure:"k8s.container.restarts"` + K8sContainerStorageLimit MetricConfig `mapstructure:"k8s.container.storage_limit"` + K8sContainerStorageRequest MetricConfig `mapstructure:"k8s.container.storage_request"` + K8sCronjobActiveJobs MetricConfig `mapstructure:"k8s.cronjob.active_jobs"` + K8sDaemonsetCurrentScheduledNodes MetricConfig `mapstructure:"k8s.daemonset.current_scheduled_nodes"` + K8sDaemonsetDesiredScheduledNodes MetricConfig `mapstructure:"k8s.daemonset.desired_scheduled_nodes"` + K8sDaemonsetMisscheduledNodes MetricConfig `mapstructure:"k8s.daemonset.misscheduled_nodes"` + K8sDaemonsetReadyNodes MetricConfig `mapstructure:"k8s.daemonset.ready_nodes"` + K8sDeploymentAvailable MetricConfig `mapstructure:"k8s.deployment.available"` + K8sDeploymentDesired MetricConfig `mapstructure:"k8s.deployment.desired"` + K8sHierarchicalResourceQuotaHardLimit MetricConfig `mapstructure:"k8s.hierarchical_resource_quota.hard_limit"` + K8sHierarchicalResourceQuotaUsed MetricConfig `mapstructure:"k8s.hierarchical_resource_quota.used"` + K8sHpaCurrentReplicas MetricConfig `mapstructure:"k8s.hpa.current_replicas"` + K8sHpaDesiredReplicas MetricConfig `mapstructure:"k8s.hpa.desired_replicas"` + K8sHpaMaxReplicas MetricConfig `mapstructure:"k8s.hpa.max_replicas"` + K8sHpaMinReplicas MetricConfig `mapstructure:"k8s.hpa.min_replicas"` + K8sJobActivePods MetricConfig `mapstructure:"k8s.job.active_pods"` + K8sJobDesiredSuccessfulPods MetricConfig `mapstructure:"k8s.job.desired_successful_pods"` + K8sJobFailedPods MetricConfig `mapstructure:"k8s.job.failed_pods"` + K8sJobMaxParallelPods MetricConfig `mapstructure:"k8s.job.max_parallel_pods"` + K8sJobSuccessfulPods MetricConfig `mapstructure:"k8s.job.successful_pods"` + K8sNamespacePhase MetricConfig `mapstructure:"k8s.namespace.phase"` + K8sNodeCondition MetricConfig `mapstructure:"k8s.node.condition"` + K8sPodPhase MetricConfig `mapstructure:"k8s.pod.phase"` + K8sPodStatusReason MetricConfig `mapstructure:"k8s.pod.status_reason"` + K8sReplicasetAvailable MetricConfig `mapstructure:"k8s.replicaset.available"` + K8sReplicasetDesired MetricConfig `mapstructure:"k8s.replicaset.desired"` + K8sReplicationControllerAvailable MetricConfig `mapstructure:"k8s.replication_controller.available"` + K8sReplicationControllerDesired MetricConfig `mapstructure:"k8s.replication_controller.desired"` + K8sResourceQuotaHardLimit MetricConfig `mapstructure:"k8s.resource_quota.hard_limit"` + K8sResourceQuotaUsed MetricConfig `mapstructure:"k8s.resource_quota.used"` + K8sStatefulsetCurrentPods MetricConfig `mapstructure:"k8s.statefulset.current_pods"` + K8sStatefulsetDesiredPods MetricConfig `mapstructure:"k8s.statefulset.desired_pods"` + K8sStatefulsetReadyPods MetricConfig `mapstructure:"k8s.statefulset.ready_pods"` + K8sStatefulsetUpdatedPods MetricConfig `mapstructure:"k8s.statefulset.updated_pods"` + OpenshiftAppliedclusterquotaLimit MetricConfig `mapstructure:"openshift.appliedclusterquota.limit"` + OpenshiftAppliedclusterquotaUsed MetricConfig `mapstructure:"openshift.appliedclusterquota.used"` + OpenshiftClusterquotaLimit MetricConfig `mapstructure:"openshift.clusterquota.limit"` + OpenshiftClusterquotaUsed MetricConfig `mapstructure:"openshift.clusterquota.used"` } func DefaultMetricsConfig() MetricsConfig { @@ -124,6 +126,12 @@ func DefaultMetricsConfig() MetricsConfig { K8sDeploymentDesired: MetricConfig{ Enabled: true, }, + K8sHierarchicalResourceQuotaHardLimit: MetricConfig{ + Enabled: false, + }, + K8sHierarchicalResourceQuotaUsed: MetricConfig{ + Enabled: false, + }, K8sHpaCurrentReplicas: MetricConfig{ Enabled: true, }, @@ -229,39 +237,41 @@ func (rac *ResourceAttributeConfig) Unmarshal(parser *confmap.Conf) error { // ResourceAttributesConfig provides config for k8s_cluster resource attributes. type ResourceAttributesConfig struct { - ContainerID ResourceAttributeConfig `mapstructure:"container.id"` - ContainerImageName ResourceAttributeConfig `mapstructure:"container.image.name"` - ContainerImageTag ResourceAttributeConfig `mapstructure:"container.image.tag"` - K8sContainerName ResourceAttributeConfig `mapstructure:"k8s.container.name"` - K8sCronjobName ResourceAttributeConfig `mapstructure:"k8s.cronjob.name"` - K8sCronjobUID ResourceAttributeConfig `mapstructure:"k8s.cronjob.uid"` - K8sDaemonsetName ResourceAttributeConfig `mapstructure:"k8s.daemonset.name"` - K8sDaemonsetUID ResourceAttributeConfig `mapstructure:"k8s.daemonset.uid"` - K8sDeploymentName ResourceAttributeConfig `mapstructure:"k8s.deployment.name"` - K8sDeploymentUID ResourceAttributeConfig `mapstructure:"k8s.deployment.uid"` - K8sHpaName ResourceAttributeConfig `mapstructure:"k8s.hpa.name"` - K8sHpaUID ResourceAttributeConfig `mapstructure:"k8s.hpa.uid"` - K8sJobName ResourceAttributeConfig `mapstructure:"k8s.job.name"` - K8sJobUID ResourceAttributeConfig `mapstructure:"k8s.job.uid"` - K8sKubeletVersion ResourceAttributeConfig `mapstructure:"k8s.kubelet.version"` - K8sKubeproxyVersion ResourceAttributeConfig `mapstructure:"k8s.kubeproxy.version"` - K8sNamespaceName ResourceAttributeConfig `mapstructure:"k8s.namespace.name"` - K8sNamespaceUID ResourceAttributeConfig `mapstructure:"k8s.namespace.uid"` - K8sNodeName ResourceAttributeConfig `mapstructure:"k8s.node.name"` - K8sNodeUID ResourceAttributeConfig `mapstructure:"k8s.node.uid"` - K8sPodName ResourceAttributeConfig `mapstructure:"k8s.pod.name"` - K8sPodQosClass ResourceAttributeConfig `mapstructure:"k8s.pod.qos_class"` - K8sPodUID ResourceAttributeConfig `mapstructure:"k8s.pod.uid"` - K8sReplicasetName ResourceAttributeConfig `mapstructure:"k8s.replicaset.name"` - K8sReplicasetUID ResourceAttributeConfig `mapstructure:"k8s.replicaset.uid"` - K8sReplicationcontrollerName ResourceAttributeConfig `mapstructure:"k8s.replicationcontroller.name"` - K8sReplicationcontrollerUID ResourceAttributeConfig `mapstructure:"k8s.replicationcontroller.uid"` - K8sResourcequotaName ResourceAttributeConfig `mapstructure:"k8s.resourcequota.name"` - K8sResourcequotaUID ResourceAttributeConfig `mapstructure:"k8s.resourcequota.uid"` - K8sStatefulsetName ResourceAttributeConfig `mapstructure:"k8s.statefulset.name"` - K8sStatefulsetUID ResourceAttributeConfig `mapstructure:"k8s.statefulset.uid"` - OpenshiftClusterquotaName ResourceAttributeConfig `mapstructure:"openshift.clusterquota.name"` - OpenshiftClusterquotaUID ResourceAttributeConfig `mapstructure:"openshift.clusterquota.uid"` + ContainerID ResourceAttributeConfig `mapstructure:"container.id"` + ContainerImageName ResourceAttributeConfig `mapstructure:"container.image.name"` + ContainerImageTag ResourceAttributeConfig `mapstructure:"container.image.tag"` + K8sContainerName ResourceAttributeConfig `mapstructure:"k8s.container.name"` + K8sCronjobName ResourceAttributeConfig `mapstructure:"k8s.cronjob.name"` + K8sCronjobUID ResourceAttributeConfig `mapstructure:"k8s.cronjob.uid"` + K8sDaemonsetName ResourceAttributeConfig `mapstructure:"k8s.daemonset.name"` + K8sDaemonsetUID ResourceAttributeConfig `mapstructure:"k8s.daemonset.uid"` + K8sDeploymentName ResourceAttributeConfig `mapstructure:"k8s.deployment.name"` + K8sDeploymentUID ResourceAttributeConfig `mapstructure:"k8s.deployment.uid"` + K8sHierarchicalresourcequotaName ResourceAttributeConfig `mapstructure:"k8s.hierarchicalresourcequota.name"` + K8sHierarchicalresourcequotaUID ResourceAttributeConfig `mapstructure:"k8s.hierarchicalresourcequota.uid"` + K8sHpaName ResourceAttributeConfig `mapstructure:"k8s.hpa.name"` + K8sHpaUID ResourceAttributeConfig `mapstructure:"k8s.hpa.uid"` + K8sJobName ResourceAttributeConfig `mapstructure:"k8s.job.name"` + K8sJobUID ResourceAttributeConfig `mapstructure:"k8s.job.uid"` + K8sKubeletVersion ResourceAttributeConfig `mapstructure:"k8s.kubelet.version"` + K8sKubeproxyVersion ResourceAttributeConfig `mapstructure:"k8s.kubeproxy.version"` + K8sNamespaceName ResourceAttributeConfig `mapstructure:"k8s.namespace.name"` + K8sNamespaceUID ResourceAttributeConfig `mapstructure:"k8s.namespace.uid"` + K8sNodeName ResourceAttributeConfig `mapstructure:"k8s.node.name"` + K8sNodeUID ResourceAttributeConfig `mapstructure:"k8s.node.uid"` + K8sPodName ResourceAttributeConfig `mapstructure:"k8s.pod.name"` + K8sPodQosClass ResourceAttributeConfig `mapstructure:"k8s.pod.qos_class"` + K8sPodUID ResourceAttributeConfig `mapstructure:"k8s.pod.uid"` + K8sReplicasetName ResourceAttributeConfig `mapstructure:"k8s.replicaset.name"` + K8sReplicasetUID ResourceAttributeConfig `mapstructure:"k8s.replicaset.uid"` + K8sReplicationcontrollerName ResourceAttributeConfig `mapstructure:"k8s.replicationcontroller.name"` + K8sReplicationcontrollerUID ResourceAttributeConfig `mapstructure:"k8s.replicationcontroller.uid"` + K8sResourcequotaName ResourceAttributeConfig `mapstructure:"k8s.resourcequota.name"` + K8sResourcequotaUID ResourceAttributeConfig `mapstructure:"k8s.resourcequota.uid"` + K8sStatefulsetName ResourceAttributeConfig `mapstructure:"k8s.statefulset.name"` + K8sStatefulsetUID ResourceAttributeConfig `mapstructure:"k8s.statefulset.uid"` + OpenshiftClusterquotaName ResourceAttributeConfig `mapstructure:"openshift.clusterquota.name"` + OpenshiftClusterquotaUID ResourceAttributeConfig `mapstructure:"openshift.clusterquota.uid"` } func DefaultResourceAttributesConfig() ResourceAttributesConfig { @@ -296,6 +306,12 @@ func DefaultResourceAttributesConfig() ResourceAttributesConfig { K8sDeploymentUID: ResourceAttributeConfig{ Enabled: true, }, + K8sHierarchicalresourcequotaName: ResourceAttributeConfig{ + Enabled: false, + }, + K8sHierarchicalresourcequotaUID: ResourceAttributeConfig{ + Enabled: false, + }, K8sHpaName: ResourceAttributeConfig{ Enabled: true, }, diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go b/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go index c6178f76878c..7430f0b2b7f8 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go @@ -26,85 +26,89 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "all_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - K8sContainerCPULimit: MetricConfig{Enabled: true}, - K8sContainerCPURequest: MetricConfig{Enabled: true}, - K8sContainerEphemeralstorageLimit: MetricConfig{Enabled: true}, - K8sContainerEphemeralstorageRequest: MetricConfig{Enabled: true}, - K8sContainerMemoryLimit: MetricConfig{Enabled: true}, - K8sContainerMemoryRequest: MetricConfig{Enabled: true}, - K8sContainerReady: MetricConfig{Enabled: true}, - K8sContainerRestarts: MetricConfig{Enabled: true}, - K8sContainerStorageLimit: MetricConfig{Enabled: true}, - K8sContainerStorageRequest: MetricConfig{Enabled: true}, - K8sCronjobActiveJobs: MetricConfig{Enabled: true}, - K8sDaemonsetCurrentScheduledNodes: MetricConfig{Enabled: true}, - K8sDaemonsetDesiredScheduledNodes: MetricConfig{Enabled: true}, - K8sDaemonsetMisscheduledNodes: MetricConfig{Enabled: true}, - K8sDaemonsetReadyNodes: MetricConfig{Enabled: true}, - K8sDeploymentAvailable: MetricConfig{Enabled: true}, - K8sDeploymentDesired: MetricConfig{Enabled: true}, - K8sHpaCurrentReplicas: MetricConfig{Enabled: true}, - K8sHpaDesiredReplicas: MetricConfig{Enabled: true}, - K8sHpaMaxReplicas: MetricConfig{Enabled: true}, - K8sHpaMinReplicas: MetricConfig{Enabled: true}, - K8sJobActivePods: MetricConfig{Enabled: true}, - K8sJobDesiredSuccessfulPods: MetricConfig{Enabled: true}, - K8sJobFailedPods: MetricConfig{Enabled: true}, - K8sJobMaxParallelPods: MetricConfig{Enabled: true}, - K8sJobSuccessfulPods: MetricConfig{Enabled: true}, - K8sNamespacePhase: MetricConfig{Enabled: true}, - K8sNodeCondition: MetricConfig{Enabled: true}, - K8sPodPhase: MetricConfig{Enabled: true}, - K8sPodStatusReason: MetricConfig{Enabled: true}, - K8sReplicasetAvailable: MetricConfig{Enabled: true}, - K8sReplicasetDesired: MetricConfig{Enabled: true}, - K8sReplicationControllerAvailable: MetricConfig{Enabled: true}, - K8sReplicationControllerDesired: MetricConfig{Enabled: true}, - K8sResourceQuotaHardLimit: MetricConfig{Enabled: true}, - K8sResourceQuotaUsed: MetricConfig{Enabled: true}, - K8sStatefulsetCurrentPods: MetricConfig{Enabled: true}, - K8sStatefulsetDesiredPods: MetricConfig{Enabled: true}, - K8sStatefulsetReadyPods: MetricConfig{Enabled: true}, - K8sStatefulsetUpdatedPods: MetricConfig{Enabled: true}, - OpenshiftAppliedclusterquotaLimit: MetricConfig{Enabled: true}, - OpenshiftAppliedclusterquotaUsed: MetricConfig{Enabled: true}, - OpenshiftClusterquotaLimit: MetricConfig{Enabled: true}, - OpenshiftClusterquotaUsed: MetricConfig{Enabled: true}, + K8sContainerCPULimit: MetricConfig{Enabled: true}, + K8sContainerCPURequest: MetricConfig{Enabled: true}, + K8sContainerEphemeralstorageLimit: MetricConfig{Enabled: true}, + K8sContainerEphemeralstorageRequest: MetricConfig{Enabled: true}, + K8sContainerMemoryLimit: MetricConfig{Enabled: true}, + K8sContainerMemoryRequest: MetricConfig{Enabled: true}, + K8sContainerReady: MetricConfig{Enabled: true}, + K8sContainerRestarts: MetricConfig{Enabled: true}, + K8sContainerStorageLimit: MetricConfig{Enabled: true}, + K8sContainerStorageRequest: MetricConfig{Enabled: true}, + K8sCronjobActiveJobs: MetricConfig{Enabled: true}, + K8sDaemonsetCurrentScheduledNodes: MetricConfig{Enabled: true}, + K8sDaemonsetDesiredScheduledNodes: MetricConfig{Enabled: true}, + K8sDaemonsetMisscheduledNodes: MetricConfig{Enabled: true}, + K8sDaemonsetReadyNodes: MetricConfig{Enabled: true}, + K8sDeploymentAvailable: MetricConfig{Enabled: true}, + K8sDeploymentDesired: MetricConfig{Enabled: true}, + K8sHierarchicalResourceQuotaHardLimit: MetricConfig{Enabled: true}, + K8sHierarchicalResourceQuotaUsed: MetricConfig{Enabled: true}, + K8sHpaCurrentReplicas: MetricConfig{Enabled: true}, + K8sHpaDesiredReplicas: MetricConfig{Enabled: true}, + K8sHpaMaxReplicas: MetricConfig{Enabled: true}, + K8sHpaMinReplicas: MetricConfig{Enabled: true}, + K8sJobActivePods: MetricConfig{Enabled: true}, + K8sJobDesiredSuccessfulPods: MetricConfig{Enabled: true}, + K8sJobFailedPods: MetricConfig{Enabled: true}, + K8sJobMaxParallelPods: MetricConfig{Enabled: true}, + K8sJobSuccessfulPods: MetricConfig{Enabled: true}, + K8sNamespacePhase: MetricConfig{Enabled: true}, + K8sNodeCondition: MetricConfig{Enabled: true}, + K8sPodPhase: MetricConfig{Enabled: true}, + K8sPodStatusReason: MetricConfig{Enabled: true}, + K8sReplicasetAvailable: MetricConfig{Enabled: true}, + K8sReplicasetDesired: MetricConfig{Enabled: true}, + K8sReplicationControllerAvailable: MetricConfig{Enabled: true}, + K8sReplicationControllerDesired: MetricConfig{Enabled: true}, + K8sResourceQuotaHardLimit: MetricConfig{Enabled: true}, + K8sResourceQuotaUsed: MetricConfig{Enabled: true}, + K8sStatefulsetCurrentPods: MetricConfig{Enabled: true}, + K8sStatefulsetDesiredPods: MetricConfig{Enabled: true}, + K8sStatefulsetReadyPods: MetricConfig{Enabled: true}, + K8sStatefulsetUpdatedPods: MetricConfig{Enabled: true}, + OpenshiftAppliedclusterquotaLimit: MetricConfig{Enabled: true}, + OpenshiftAppliedclusterquotaUsed: MetricConfig{Enabled: true}, + OpenshiftClusterquotaLimit: MetricConfig{Enabled: true}, + OpenshiftClusterquotaUsed: MetricConfig{Enabled: true}, }, ResourceAttributes: ResourceAttributesConfig{ - ContainerID: ResourceAttributeConfig{Enabled: true}, - ContainerImageName: ResourceAttributeConfig{Enabled: true}, - ContainerImageTag: ResourceAttributeConfig{Enabled: true}, - K8sContainerName: ResourceAttributeConfig{Enabled: true}, - K8sCronjobName: ResourceAttributeConfig{Enabled: true}, - K8sCronjobUID: ResourceAttributeConfig{Enabled: true}, - K8sDaemonsetName: ResourceAttributeConfig{Enabled: true}, - K8sDaemonsetUID: ResourceAttributeConfig{Enabled: true}, - K8sDeploymentName: ResourceAttributeConfig{Enabled: true}, - K8sDeploymentUID: ResourceAttributeConfig{Enabled: true}, - K8sHpaName: ResourceAttributeConfig{Enabled: true}, - K8sHpaUID: ResourceAttributeConfig{Enabled: true}, - K8sJobName: ResourceAttributeConfig{Enabled: true}, - K8sJobUID: ResourceAttributeConfig{Enabled: true}, - K8sKubeletVersion: ResourceAttributeConfig{Enabled: true}, - K8sKubeproxyVersion: ResourceAttributeConfig{Enabled: true}, - K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, - K8sNamespaceUID: ResourceAttributeConfig{Enabled: true}, - K8sNodeName: ResourceAttributeConfig{Enabled: true}, - K8sNodeUID: ResourceAttributeConfig{Enabled: true}, - K8sPodName: ResourceAttributeConfig{Enabled: true}, - K8sPodQosClass: ResourceAttributeConfig{Enabled: true}, - K8sPodUID: ResourceAttributeConfig{Enabled: true}, - K8sReplicasetName: ResourceAttributeConfig{Enabled: true}, - K8sReplicasetUID: ResourceAttributeConfig{Enabled: true}, - K8sReplicationcontrollerName: ResourceAttributeConfig{Enabled: true}, - K8sReplicationcontrollerUID: ResourceAttributeConfig{Enabled: true}, - K8sResourcequotaName: ResourceAttributeConfig{Enabled: true}, - K8sResourcequotaUID: ResourceAttributeConfig{Enabled: true}, - K8sStatefulsetName: ResourceAttributeConfig{Enabled: true}, - K8sStatefulsetUID: ResourceAttributeConfig{Enabled: true}, - OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: true}, - OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: true}, + ContainerID: ResourceAttributeConfig{Enabled: true}, + ContainerImageName: ResourceAttributeConfig{Enabled: true}, + ContainerImageTag: ResourceAttributeConfig{Enabled: true}, + K8sContainerName: ResourceAttributeConfig{Enabled: true}, + K8sCronjobName: ResourceAttributeConfig{Enabled: true}, + K8sCronjobUID: ResourceAttributeConfig{Enabled: true}, + K8sDaemonsetName: ResourceAttributeConfig{Enabled: true}, + K8sDaemonsetUID: ResourceAttributeConfig{Enabled: true}, + K8sDeploymentName: ResourceAttributeConfig{Enabled: true}, + K8sDeploymentUID: ResourceAttributeConfig{Enabled: true}, + K8sHierarchicalresourcequotaName: ResourceAttributeConfig{Enabled: true}, + K8sHierarchicalresourcequotaUID: ResourceAttributeConfig{Enabled: true}, + K8sHpaName: ResourceAttributeConfig{Enabled: true}, + K8sHpaUID: ResourceAttributeConfig{Enabled: true}, + K8sJobName: ResourceAttributeConfig{Enabled: true}, + K8sJobUID: ResourceAttributeConfig{Enabled: true}, + K8sKubeletVersion: ResourceAttributeConfig{Enabled: true}, + K8sKubeproxyVersion: ResourceAttributeConfig{Enabled: true}, + K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, + K8sNamespaceUID: ResourceAttributeConfig{Enabled: true}, + K8sNodeName: ResourceAttributeConfig{Enabled: true}, + K8sNodeUID: ResourceAttributeConfig{Enabled: true}, + K8sPodName: ResourceAttributeConfig{Enabled: true}, + K8sPodQosClass: ResourceAttributeConfig{Enabled: true}, + K8sPodUID: ResourceAttributeConfig{Enabled: true}, + K8sReplicasetName: ResourceAttributeConfig{Enabled: true}, + K8sReplicasetUID: ResourceAttributeConfig{Enabled: true}, + K8sReplicationcontrollerName: ResourceAttributeConfig{Enabled: true}, + K8sReplicationcontrollerUID: ResourceAttributeConfig{Enabled: true}, + K8sResourcequotaName: ResourceAttributeConfig{Enabled: true}, + K8sResourcequotaUID: ResourceAttributeConfig{Enabled: true}, + K8sStatefulsetName: ResourceAttributeConfig{Enabled: true}, + K8sStatefulsetUID: ResourceAttributeConfig{Enabled: true}, + OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: true}, + OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: true}, }, }, }, @@ -112,85 +116,89 @@ func TestMetricsBuilderConfig(t *testing.T) { name: "none_set", want: MetricsBuilderConfig{ Metrics: MetricsConfig{ - K8sContainerCPULimit: MetricConfig{Enabled: false}, - K8sContainerCPURequest: MetricConfig{Enabled: false}, - K8sContainerEphemeralstorageLimit: MetricConfig{Enabled: false}, - K8sContainerEphemeralstorageRequest: MetricConfig{Enabled: false}, - K8sContainerMemoryLimit: MetricConfig{Enabled: false}, - K8sContainerMemoryRequest: MetricConfig{Enabled: false}, - K8sContainerReady: MetricConfig{Enabled: false}, - K8sContainerRestarts: MetricConfig{Enabled: false}, - K8sContainerStorageLimit: MetricConfig{Enabled: false}, - K8sContainerStorageRequest: MetricConfig{Enabled: false}, - K8sCronjobActiveJobs: MetricConfig{Enabled: false}, - K8sDaemonsetCurrentScheduledNodes: MetricConfig{Enabled: false}, - K8sDaemonsetDesiredScheduledNodes: MetricConfig{Enabled: false}, - K8sDaemonsetMisscheduledNodes: MetricConfig{Enabled: false}, - K8sDaemonsetReadyNodes: MetricConfig{Enabled: false}, - K8sDeploymentAvailable: MetricConfig{Enabled: false}, - K8sDeploymentDesired: MetricConfig{Enabled: false}, - K8sHpaCurrentReplicas: MetricConfig{Enabled: false}, - K8sHpaDesiredReplicas: MetricConfig{Enabled: false}, - K8sHpaMaxReplicas: MetricConfig{Enabled: false}, - K8sHpaMinReplicas: MetricConfig{Enabled: false}, - K8sJobActivePods: MetricConfig{Enabled: false}, - K8sJobDesiredSuccessfulPods: MetricConfig{Enabled: false}, - K8sJobFailedPods: MetricConfig{Enabled: false}, - K8sJobMaxParallelPods: MetricConfig{Enabled: false}, - K8sJobSuccessfulPods: MetricConfig{Enabled: false}, - K8sNamespacePhase: MetricConfig{Enabled: false}, - K8sNodeCondition: MetricConfig{Enabled: false}, - K8sPodPhase: MetricConfig{Enabled: false}, - K8sPodStatusReason: MetricConfig{Enabled: false}, - K8sReplicasetAvailable: MetricConfig{Enabled: false}, - K8sReplicasetDesired: MetricConfig{Enabled: false}, - K8sReplicationControllerAvailable: MetricConfig{Enabled: false}, - K8sReplicationControllerDesired: MetricConfig{Enabled: false}, - K8sResourceQuotaHardLimit: MetricConfig{Enabled: false}, - K8sResourceQuotaUsed: MetricConfig{Enabled: false}, - K8sStatefulsetCurrentPods: MetricConfig{Enabled: false}, - K8sStatefulsetDesiredPods: MetricConfig{Enabled: false}, - K8sStatefulsetReadyPods: MetricConfig{Enabled: false}, - K8sStatefulsetUpdatedPods: MetricConfig{Enabled: false}, - OpenshiftAppliedclusterquotaLimit: MetricConfig{Enabled: false}, - OpenshiftAppliedclusterquotaUsed: MetricConfig{Enabled: false}, - OpenshiftClusterquotaLimit: MetricConfig{Enabled: false}, - OpenshiftClusterquotaUsed: MetricConfig{Enabled: false}, + K8sContainerCPULimit: MetricConfig{Enabled: false}, + K8sContainerCPURequest: MetricConfig{Enabled: false}, + K8sContainerEphemeralstorageLimit: MetricConfig{Enabled: false}, + K8sContainerEphemeralstorageRequest: MetricConfig{Enabled: false}, + K8sContainerMemoryLimit: MetricConfig{Enabled: false}, + K8sContainerMemoryRequest: MetricConfig{Enabled: false}, + K8sContainerReady: MetricConfig{Enabled: false}, + K8sContainerRestarts: MetricConfig{Enabled: false}, + K8sContainerStorageLimit: MetricConfig{Enabled: false}, + K8sContainerStorageRequest: MetricConfig{Enabled: false}, + K8sCronjobActiveJobs: MetricConfig{Enabled: false}, + K8sDaemonsetCurrentScheduledNodes: MetricConfig{Enabled: false}, + K8sDaemonsetDesiredScheduledNodes: MetricConfig{Enabled: false}, + K8sDaemonsetMisscheduledNodes: MetricConfig{Enabled: false}, + K8sDaemonsetReadyNodes: MetricConfig{Enabled: false}, + K8sDeploymentAvailable: MetricConfig{Enabled: false}, + K8sDeploymentDesired: MetricConfig{Enabled: false}, + K8sHierarchicalResourceQuotaHardLimit: MetricConfig{Enabled: false}, + K8sHierarchicalResourceQuotaUsed: MetricConfig{Enabled: false}, + K8sHpaCurrentReplicas: MetricConfig{Enabled: false}, + K8sHpaDesiredReplicas: MetricConfig{Enabled: false}, + K8sHpaMaxReplicas: MetricConfig{Enabled: false}, + K8sHpaMinReplicas: MetricConfig{Enabled: false}, + K8sJobActivePods: MetricConfig{Enabled: false}, + K8sJobDesiredSuccessfulPods: MetricConfig{Enabled: false}, + K8sJobFailedPods: MetricConfig{Enabled: false}, + K8sJobMaxParallelPods: MetricConfig{Enabled: false}, + K8sJobSuccessfulPods: MetricConfig{Enabled: false}, + K8sNamespacePhase: MetricConfig{Enabled: false}, + K8sNodeCondition: MetricConfig{Enabled: false}, + K8sPodPhase: MetricConfig{Enabled: false}, + K8sPodStatusReason: MetricConfig{Enabled: false}, + K8sReplicasetAvailable: MetricConfig{Enabled: false}, + K8sReplicasetDesired: MetricConfig{Enabled: false}, + K8sReplicationControllerAvailable: MetricConfig{Enabled: false}, + K8sReplicationControllerDesired: MetricConfig{Enabled: false}, + K8sResourceQuotaHardLimit: MetricConfig{Enabled: false}, + K8sResourceQuotaUsed: MetricConfig{Enabled: false}, + K8sStatefulsetCurrentPods: MetricConfig{Enabled: false}, + K8sStatefulsetDesiredPods: MetricConfig{Enabled: false}, + K8sStatefulsetReadyPods: MetricConfig{Enabled: false}, + K8sStatefulsetUpdatedPods: MetricConfig{Enabled: false}, + OpenshiftAppliedclusterquotaLimit: MetricConfig{Enabled: false}, + OpenshiftAppliedclusterquotaUsed: MetricConfig{Enabled: false}, + OpenshiftClusterquotaLimit: MetricConfig{Enabled: false}, + OpenshiftClusterquotaUsed: MetricConfig{Enabled: false}, }, ResourceAttributes: ResourceAttributesConfig{ - ContainerID: ResourceAttributeConfig{Enabled: false}, - ContainerImageName: ResourceAttributeConfig{Enabled: false}, - ContainerImageTag: ResourceAttributeConfig{Enabled: false}, - K8sContainerName: ResourceAttributeConfig{Enabled: false}, - K8sCronjobName: ResourceAttributeConfig{Enabled: false}, - K8sCronjobUID: ResourceAttributeConfig{Enabled: false}, - K8sDaemonsetName: ResourceAttributeConfig{Enabled: false}, - K8sDaemonsetUID: ResourceAttributeConfig{Enabled: false}, - K8sDeploymentName: ResourceAttributeConfig{Enabled: false}, - K8sDeploymentUID: ResourceAttributeConfig{Enabled: false}, - K8sHpaName: ResourceAttributeConfig{Enabled: false}, - K8sHpaUID: ResourceAttributeConfig{Enabled: false}, - K8sJobName: ResourceAttributeConfig{Enabled: false}, - K8sJobUID: ResourceAttributeConfig{Enabled: false}, - K8sKubeletVersion: ResourceAttributeConfig{Enabled: false}, - K8sKubeproxyVersion: ResourceAttributeConfig{Enabled: false}, - K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, - K8sNamespaceUID: ResourceAttributeConfig{Enabled: false}, - K8sNodeName: ResourceAttributeConfig{Enabled: false}, - K8sNodeUID: ResourceAttributeConfig{Enabled: false}, - K8sPodName: ResourceAttributeConfig{Enabled: false}, - K8sPodQosClass: ResourceAttributeConfig{Enabled: false}, - K8sPodUID: ResourceAttributeConfig{Enabled: false}, - K8sReplicasetName: ResourceAttributeConfig{Enabled: false}, - K8sReplicasetUID: ResourceAttributeConfig{Enabled: false}, - K8sReplicationcontrollerName: ResourceAttributeConfig{Enabled: false}, - K8sReplicationcontrollerUID: ResourceAttributeConfig{Enabled: false}, - K8sResourcequotaName: ResourceAttributeConfig{Enabled: false}, - K8sResourcequotaUID: ResourceAttributeConfig{Enabled: false}, - K8sStatefulsetName: ResourceAttributeConfig{Enabled: false}, - K8sStatefulsetUID: ResourceAttributeConfig{Enabled: false}, - OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: false}, - OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: false}, + ContainerID: ResourceAttributeConfig{Enabled: false}, + ContainerImageName: ResourceAttributeConfig{Enabled: false}, + ContainerImageTag: ResourceAttributeConfig{Enabled: false}, + K8sContainerName: ResourceAttributeConfig{Enabled: false}, + K8sCronjobName: ResourceAttributeConfig{Enabled: false}, + K8sCronjobUID: ResourceAttributeConfig{Enabled: false}, + K8sDaemonsetName: ResourceAttributeConfig{Enabled: false}, + K8sDaemonsetUID: ResourceAttributeConfig{Enabled: false}, + K8sDeploymentName: ResourceAttributeConfig{Enabled: false}, + K8sDeploymentUID: ResourceAttributeConfig{Enabled: false}, + K8sHierarchicalresourcequotaName: ResourceAttributeConfig{Enabled: false}, + K8sHierarchicalresourcequotaUID: ResourceAttributeConfig{Enabled: false}, + K8sHpaName: ResourceAttributeConfig{Enabled: false}, + K8sHpaUID: ResourceAttributeConfig{Enabled: false}, + K8sJobName: ResourceAttributeConfig{Enabled: false}, + K8sJobUID: ResourceAttributeConfig{Enabled: false}, + K8sKubeletVersion: ResourceAttributeConfig{Enabled: false}, + K8sKubeproxyVersion: ResourceAttributeConfig{Enabled: false}, + K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, + K8sNamespaceUID: ResourceAttributeConfig{Enabled: false}, + K8sNodeName: ResourceAttributeConfig{Enabled: false}, + K8sNodeUID: ResourceAttributeConfig{Enabled: false}, + K8sPodName: ResourceAttributeConfig{Enabled: false}, + K8sPodQosClass: ResourceAttributeConfig{Enabled: false}, + K8sPodUID: ResourceAttributeConfig{Enabled: false}, + K8sReplicasetName: ResourceAttributeConfig{Enabled: false}, + K8sReplicasetUID: ResourceAttributeConfig{Enabled: false}, + K8sReplicationcontrollerName: ResourceAttributeConfig{Enabled: false}, + K8sReplicationcontrollerUID: ResourceAttributeConfig{Enabled: false}, + K8sResourcequotaName: ResourceAttributeConfig{Enabled: false}, + K8sResourcequotaUID: ResourceAttributeConfig{Enabled: false}, + K8sStatefulsetName: ResourceAttributeConfig{Enabled: false}, + K8sStatefulsetUID: ResourceAttributeConfig{Enabled: false}, + OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: false}, + OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: false}, }, }, }, @@ -227,77 +235,81 @@ func TestResourceAttributesConfig(t *testing.T) { { name: "all_set", want: ResourceAttributesConfig{ - ContainerID: ResourceAttributeConfig{Enabled: true}, - ContainerImageName: ResourceAttributeConfig{Enabled: true}, - ContainerImageTag: ResourceAttributeConfig{Enabled: true}, - K8sContainerName: ResourceAttributeConfig{Enabled: true}, - K8sCronjobName: ResourceAttributeConfig{Enabled: true}, - K8sCronjobUID: ResourceAttributeConfig{Enabled: true}, - K8sDaemonsetName: ResourceAttributeConfig{Enabled: true}, - K8sDaemonsetUID: ResourceAttributeConfig{Enabled: true}, - K8sDeploymentName: ResourceAttributeConfig{Enabled: true}, - K8sDeploymentUID: ResourceAttributeConfig{Enabled: true}, - K8sHpaName: ResourceAttributeConfig{Enabled: true}, - K8sHpaUID: ResourceAttributeConfig{Enabled: true}, - K8sJobName: ResourceAttributeConfig{Enabled: true}, - K8sJobUID: ResourceAttributeConfig{Enabled: true}, - K8sKubeletVersion: ResourceAttributeConfig{Enabled: true}, - K8sKubeproxyVersion: ResourceAttributeConfig{Enabled: true}, - K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, - K8sNamespaceUID: ResourceAttributeConfig{Enabled: true}, - K8sNodeName: ResourceAttributeConfig{Enabled: true}, - K8sNodeUID: ResourceAttributeConfig{Enabled: true}, - K8sPodName: ResourceAttributeConfig{Enabled: true}, - K8sPodQosClass: ResourceAttributeConfig{Enabled: true}, - K8sPodUID: ResourceAttributeConfig{Enabled: true}, - K8sReplicasetName: ResourceAttributeConfig{Enabled: true}, - K8sReplicasetUID: ResourceAttributeConfig{Enabled: true}, - K8sReplicationcontrollerName: ResourceAttributeConfig{Enabled: true}, - K8sReplicationcontrollerUID: ResourceAttributeConfig{Enabled: true}, - K8sResourcequotaName: ResourceAttributeConfig{Enabled: true}, - K8sResourcequotaUID: ResourceAttributeConfig{Enabled: true}, - K8sStatefulsetName: ResourceAttributeConfig{Enabled: true}, - K8sStatefulsetUID: ResourceAttributeConfig{Enabled: true}, - OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: true}, - OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: true}, + ContainerID: ResourceAttributeConfig{Enabled: true}, + ContainerImageName: ResourceAttributeConfig{Enabled: true}, + ContainerImageTag: ResourceAttributeConfig{Enabled: true}, + K8sContainerName: ResourceAttributeConfig{Enabled: true}, + K8sCronjobName: ResourceAttributeConfig{Enabled: true}, + K8sCronjobUID: ResourceAttributeConfig{Enabled: true}, + K8sDaemonsetName: ResourceAttributeConfig{Enabled: true}, + K8sDaemonsetUID: ResourceAttributeConfig{Enabled: true}, + K8sDeploymentName: ResourceAttributeConfig{Enabled: true}, + K8sDeploymentUID: ResourceAttributeConfig{Enabled: true}, + K8sHierarchicalresourcequotaName: ResourceAttributeConfig{Enabled: true}, + K8sHierarchicalresourcequotaUID: ResourceAttributeConfig{Enabled: true}, + K8sHpaName: ResourceAttributeConfig{Enabled: true}, + K8sHpaUID: ResourceAttributeConfig{Enabled: true}, + K8sJobName: ResourceAttributeConfig{Enabled: true}, + K8sJobUID: ResourceAttributeConfig{Enabled: true}, + K8sKubeletVersion: ResourceAttributeConfig{Enabled: true}, + K8sKubeproxyVersion: ResourceAttributeConfig{Enabled: true}, + K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, + K8sNamespaceUID: ResourceAttributeConfig{Enabled: true}, + K8sNodeName: ResourceAttributeConfig{Enabled: true}, + K8sNodeUID: ResourceAttributeConfig{Enabled: true}, + K8sPodName: ResourceAttributeConfig{Enabled: true}, + K8sPodQosClass: ResourceAttributeConfig{Enabled: true}, + K8sPodUID: ResourceAttributeConfig{Enabled: true}, + K8sReplicasetName: ResourceAttributeConfig{Enabled: true}, + K8sReplicasetUID: ResourceAttributeConfig{Enabled: true}, + K8sReplicationcontrollerName: ResourceAttributeConfig{Enabled: true}, + K8sReplicationcontrollerUID: ResourceAttributeConfig{Enabled: true}, + K8sResourcequotaName: ResourceAttributeConfig{Enabled: true}, + K8sResourcequotaUID: ResourceAttributeConfig{Enabled: true}, + K8sStatefulsetName: ResourceAttributeConfig{Enabled: true}, + K8sStatefulsetUID: ResourceAttributeConfig{Enabled: true}, + OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: true}, + OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: true}, }, }, { name: "none_set", want: ResourceAttributesConfig{ - ContainerID: ResourceAttributeConfig{Enabled: false}, - ContainerImageName: ResourceAttributeConfig{Enabled: false}, - ContainerImageTag: ResourceAttributeConfig{Enabled: false}, - K8sContainerName: ResourceAttributeConfig{Enabled: false}, - K8sCronjobName: ResourceAttributeConfig{Enabled: false}, - K8sCronjobUID: ResourceAttributeConfig{Enabled: false}, - K8sDaemonsetName: ResourceAttributeConfig{Enabled: false}, - K8sDaemonsetUID: ResourceAttributeConfig{Enabled: false}, - K8sDeploymentName: ResourceAttributeConfig{Enabled: false}, - K8sDeploymentUID: ResourceAttributeConfig{Enabled: false}, - K8sHpaName: ResourceAttributeConfig{Enabled: false}, - K8sHpaUID: ResourceAttributeConfig{Enabled: false}, - K8sJobName: ResourceAttributeConfig{Enabled: false}, - K8sJobUID: ResourceAttributeConfig{Enabled: false}, - K8sKubeletVersion: ResourceAttributeConfig{Enabled: false}, - K8sKubeproxyVersion: ResourceAttributeConfig{Enabled: false}, - K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, - K8sNamespaceUID: ResourceAttributeConfig{Enabled: false}, - K8sNodeName: ResourceAttributeConfig{Enabled: false}, - K8sNodeUID: ResourceAttributeConfig{Enabled: false}, - K8sPodName: ResourceAttributeConfig{Enabled: false}, - K8sPodQosClass: ResourceAttributeConfig{Enabled: false}, - K8sPodUID: ResourceAttributeConfig{Enabled: false}, - K8sReplicasetName: ResourceAttributeConfig{Enabled: false}, - K8sReplicasetUID: ResourceAttributeConfig{Enabled: false}, - K8sReplicationcontrollerName: ResourceAttributeConfig{Enabled: false}, - K8sReplicationcontrollerUID: ResourceAttributeConfig{Enabled: false}, - K8sResourcequotaName: ResourceAttributeConfig{Enabled: false}, - K8sResourcequotaUID: ResourceAttributeConfig{Enabled: false}, - K8sStatefulsetName: ResourceAttributeConfig{Enabled: false}, - K8sStatefulsetUID: ResourceAttributeConfig{Enabled: false}, - OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: false}, - OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: false}, + ContainerID: ResourceAttributeConfig{Enabled: false}, + ContainerImageName: ResourceAttributeConfig{Enabled: false}, + ContainerImageTag: ResourceAttributeConfig{Enabled: false}, + K8sContainerName: ResourceAttributeConfig{Enabled: false}, + K8sCronjobName: ResourceAttributeConfig{Enabled: false}, + K8sCronjobUID: ResourceAttributeConfig{Enabled: false}, + K8sDaemonsetName: ResourceAttributeConfig{Enabled: false}, + K8sDaemonsetUID: ResourceAttributeConfig{Enabled: false}, + K8sDeploymentName: ResourceAttributeConfig{Enabled: false}, + K8sDeploymentUID: ResourceAttributeConfig{Enabled: false}, + K8sHierarchicalresourcequotaName: ResourceAttributeConfig{Enabled: false}, + K8sHierarchicalresourcequotaUID: ResourceAttributeConfig{Enabled: false}, + K8sHpaName: ResourceAttributeConfig{Enabled: false}, + K8sHpaUID: ResourceAttributeConfig{Enabled: false}, + K8sJobName: ResourceAttributeConfig{Enabled: false}, + K8sJobUID: ResourceAttributeConfig{Enabled: false}, + K8sKubeletVersion: ResourceAttributeConfig{Enabled: false}, + K8sKubeproxyVersion: ResourceAttributeConfig{Enabled: false}, + K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, + K8sNamespaceUID: ResourceAttributeConfig{Enabled: false}, + K8sNodeName: ResourceAttributeConfig{Enabled: false}, + K8sNodeUID: ResourceAttributeConfig{Enabled: false}, + K8sPodName: ResourceAttributeConfig{Enabled: false}, + K8sPodQosClass: ResourceAttributeConfig{Enabled: false}, + K8sPodUID: ResourceAttributeConfig{Enabled: false}, + K8sReplicasetName: ResourceAttributeConfig{Enabled: false}, + K8sReplicasetUID: ResourceAttributeConfig{Enabled: false}, + K8sReplicationcontrollerName: ResourceAttributeConfig{Enabled: false}, + K8sReplicationcontrollerUID: ResourceAttributeConfig{Enabled: false}, + K8sResourcequotaName: ResourceAttributeConfig{Enabled: false}, + K8sResourcequotaUID: ResourceAttributeConfig{Enabled: false}, + K8sStatefulsetName: ResourceAttributeConfig{Enabled: false}, + K8sStatefulsetUID: ResourceAttributeConfig{Enabled: false}, + OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: false}, + OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: false}, }, }, } diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go index c4a7d7c735fb..ebcf191816f3 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go @@ -845,6 +845,108 @@ func newMetricK8sDeploymentDesired(cfg MetricConfig) metricK8sDeploymentDesired return m } +type metricK8sHierarchicalResourceQuotaHardLimit struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.hierarchical_resource_quota.hard_limit metric with initial data. +func (m *metricK8sHierarchicalResourceQuotaHardLimit) init() { + m.data.SetName("k8s.hierarchical_resource_quota.hard_limit") + m.data.SetDescription("The upper limit for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores") + m.data.SetUnit("{resource}") + m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricK8sHierarchicalResourceQuotaHardLimit) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, resourceAttributeValue string) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("resource", resourceAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricK8sHierarchicalResourceQuotaHardLimit) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sHierarchicalResourceQuotaHardLimit) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sHierarchicalResourceQuotaHardLimit(cfg MetricConfig) metricK8sHierarchicalResourceQuotaHardLimit { + m := metricK8sHierarchicalResourceQuotaHardLimit{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricK8sHierarchicalResourceQuotaUsed struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.hierarchical_resource_quota.used metric with initial data. +func (m *metricK8sHierarchicalResourceQuotaUsed) init() { + m.data.SetName("k8s.hierarchical_resource_quota.used") + m.data.SetDescription("The usage for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores") + m.data.SetUnit("{resource}") + m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricK8sHierarchicalResourceQuotaUsed) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, resourceAttributeValue string) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("resource", resourceAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricK8sHierarchicalResourceQuotaUsed) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sHierarchicalResourceQuotaUsed) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sHierarchicalResourceQuotaUsed(cfg MetricConfig) metricK8sHierarchicalResourceQuotaUsed { + m := metricK8sHierarchicalResourceQuotaUsed{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricK8sHpaCurrentReplicas struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -2187,55 +2289,57 @@ func newMetricOpenshiftClusterquotaUsed(cfg MetricConfig) metricOpenshiftCluster // MetricsBuilder provides an interface for scrapers to report metrics while taking care of all the transformations // required to produce metric representation defined in metadata and user config. type MetricsBuilder struct { - config MetricsBuilderConfig // config of the metrics builder. - startTime pcommon.Timestamp // start time that will be applied to all recorded data points. - metricsCapacity int // maximum observed number of metrics per resource. - metricsBuffer pmetric.Metrics // accumulates metrics data before emitting. - buildInfo component.BuildInfo // contains version information. - metricK8sContainerCPULimit metricK8sContainerCPULimit - metricK8sContainerCPURequest metricK8sContainerCPURequest - metricK8sContainerEphemeralstorageLimit metricK8sContainerEphemeralstorageLimit - metricK8sContainerEphemeralstorageRequest metricK8sContainerEphemeralstorageRequest - metricK8sContainerMemoryLimit metricK8sContainerMemoryLimit - metricK8sContainerMemoryRequest metricK8sContainerMemoryRequest - metricK8sContainerReady metricK8sContainerReady - metricK8sContainerRestarts metricK8sContainerRestarts - metricK8sContainerStorageLimit metricK8sContainerStorageLimit - metricK8sContainerStorageRequest metricK8sContainerStorageRequest - metricK8sCronjobActiveJobs metricK8sCronjobActiveJobs - metricK8sDaemonsetCurrentScheduledNodes metricK8sDaemonsetCurrentScheduledNodes - metricK8sDaemonsetDesiredScheduledNodes metricK8sDaemonsetDesiredScheduledNodes - metricK8sDaemonsetMisscheduledNodes metricK8sDaemonsetMisscheduledNodes - metricK8sDaemonsetReadyNodes metricK8sDaemonsetReadyNodes - metricK8sDeploymentAvailable metricK8sDeploymentAvailable - metricK8sDeploymentDesired metricK8sDeploymentDesired - metricK8sHpaCurrentReplicas metricK8sHpaCurrentReplicas - metricK8sHpaDesiredReplicas metricK8sHpaDesiredReplicas - metricK8sHpaMaxReplicas metricK8sHpaMaxReplicas - metricK8sHpaMinReplicas metricK8sHpaMinReplicas - metricK8sJobActivePods metricK8sJobActivePods - metricK8sJobDesiredSuccessfulPods metricK8sJobDesiredSuccessfulPods - metricK8sJobFailedPods metricK8sJobFailedPods - metricK8sJobMaxParallelPods metricK8sJobMaxParallelPods - metricK8sJobSuccessfulPods metricK8sJobSuccessfulPods - metricK8sNamespacePhase metricK8sNamespacePhase - metricK8sNodeCondition metricK8sNodeCondition - metricK8sPodPhase metricK8sPodPhase - metricK8sPodStatusReason metricK8sPodStatusReason - metricK8sReplicasetAvailable metricK8sReplicasetAvailable - metricK8sReplicasetDesired metricK8sReplicasetDesired - metricK8sReplicationControllerAvailable metricK8sReplicationControllerAvailable - metricK8sReplicationControllerDesired metricK8sReplicationControllerDesired - metricK8sResourceQuotaHardLimit metricK8sResourceQuotaHardLimit - metricK8sResourceQuotaUsed metricK8sResourceQuotaUsed - metricK8sStatefulsetCurrentPods metricK8sStatefulsetCurrentPods - metricK8sStatefulsetDesiredPods metricK8sStatefulsetDesiredPods - metricK8sStatefulsetReadyPods metricK8sStatefulsetReadyPods - metricK8sStatefulsetUpdatedPods metricK8sStatefulsetUpdatedPods - metricOpenshiftAppliedclusterquotaLimit metricOpenshiftAppliedclusterquotaLimit - metricOpenshiftAppliedclusterquotaUsed metricOpenshiftAppliedclusterquotaUsed - metricOpenshiftClusterquotaLimit metricOpenshiftClusterquotaLimit - metricOpenshiftClusterquotaUsed metricOpenshiftClusterquotaUsed + config MetricsBuilderConfig // config of the metrics builder. + startTime pcommon.Timestamp // start time that will be applied to all recorded data points. + metricsCapacity int // maximum observed number of metrics per resource. + metricsBuffer pmetric.Metrics // accumulates metrics data before emitting. + buildInfo component.BuildInfo // contains version information. + metricK8sContainerCPULimit metricK8sContainerCPULimit + metricK8sContainerCPURequest metricK8sContainerCPURequest + metricK8sContainerEphemeralstorageLimit metricK8sContainerEphemeralstorageLimit + metricK8sContainerEphemeralstorageRequest metricK8sContainerEphemeralstorageRequest + metricK8sContainerMemoryLimit metricK8sContainerMemoryLimit + metricK8sContainerMemoryRequest metricK8sContainerMemoryRequest + metricK8sContainerReady metricK8sContainerReady + metricK8sContainerRestarts metricK8sContainerRestarts + metricK8sContainerStorageLimit metricK8sContainerStorageLimit + metricK8sContainerStorageRequest metricK8sContainerStorageRequest + metricK8sCronjobActiveJobs metricK8sCronjobActiveJobs + metricK8sDaemonsetCurrentScheduledNodes metricK8sDaemonsetCurrentScheduledNodes + metricK8sDaemonsetDesiredScheduledNodes metricK8sDaemonsetDesiredScheduledNodes + metricK8sDaemonsetMisscheduledNodes metricK8sDaemonsetMisscheduledNodes + metricK8sDaemonsetReadyNodes metricK8sDaemonsetReadyNodes + metricK8sDeploymentAvailable metricK8sDeploymentAvailable + metricK8sDeploymentDesired metricK8sDeploymentDesired + metricK8sHierarchicalResourceQuotaHardLimit metricK8sHierarchicalResourceQuotaHardLimit + metricK8sHierarchicalResourceQuotaUsed metricK8sHierarchicalResourceQuotaUsed + metricK8sHpaCurrentReplicas metricK8sHpaCurrentReplicas + metricK8sHpaDesiredReplicas metricK8sHpaDesiredReplicas + metricK8sHpaMaxReplicas metricK8sHpaMaxReplicas + metricK8sHpaMinReplicas metricK8sHpaMinReplicas + metricK8sJobActivePods metricK8sJobActivePods + metricK8sJobDesiredSuccessfulPods metricK8sJobDesiredSuccessfulPods + metricK8sJobFailedPods metricK8sJobFailedPods + metricK8sJobMaxParallelPods metricK8sJobMaxParallelPods + metricK8sJobSuccessfulPods metricK8sJobSuccessfulPods + metricK8sNamespacePhase metricK8sNamespacePhase + metricK8sNodeCondition metricK8sNodeCondition + metricK8sPodPhase metricK8sPodPhase + metricK8sPodStatusReason metricK8sPodStatusReason + metricK8sReplicasetAvailable metricK8sReplicasetAvailable + metricK8sReplicasetDesired metricK8sReplicasetDesired + metricK8sReplicationControllerAvailable metricK8sReplicationControllerAvailable + metricK8sReplicationControllerDesired metricK8sReplicationControllerDesired + metricK8sResourceQuotaHardLimit metricK8sResourceQuotaHardLimit + metricK8sResourceQuotaUsed metricK8sResourceQuotaUsed + metricK8sStatefulsetCurrentPods metricK8sStatefulsetCurrentPods + metricK8sStatefulsetDesiredPods metricK8sStatefulsetDesiredPods + metricK8sStatefulsetReadyPods metricK8sStatefulsetReadyPods + metricK8sStatefulsetUpdatedPods metricK8sStatefulsetUpdatedPods + metricOpenshiftAppliedclusterquotaLimit metricOpenshiftAppliedclusterquotaLimit + metricOpenshiftAppliedclusterquotaUsed metricOpenshiftAppliedclusterquotaUsed + metricOpenshiftClusterquotaLimit metricOpenshiftClusterquotaLimit + metricOpenshiftClusterquotaUsed metricOpenshiftClusterquotaUsed } // metricBuilderOption applies changes to default metrics builder. @@ -2260,47 +2364,49 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSetting metricK8sContainerCPULimit: newMetricK8sContainerCPULimit(mbc.Metrics.K8sContainerCPULimit), metricK8sContainerCPURequest: newMetricK8sContainerCPURequest(mbc.Metrics.K8sContainerCPURequest), metricK8sContainerEphemeralstorageLimit: newMetricK8sContainerEphemeralstorageLimit(mbc.Metrics.K8sContainerEphemeralstorageLimit), - metricK8sContainerEphemeralstorageRequest: newMetricK8sContainerEphemeralstorageRequest(mbc.Metrics.K8sContainerEphemeralstorageRequest), - metricK8sContainerMemoryLimit: newMetricK8sContainerMemoryLimit(mbc.Metrics.K8sContainerMemoryLimit), - metricK8sContainerMemoryRequest: newMetricK8sContainerMemoryRequest(mbc.Metrics.K8sContainerMemoryRequest), - metricK8sContainerReady: newMetricK8sContainerReady(mbc.Metrics.K8sContainerReady), - metricK8sContainerRestarts: newMetricK8sContainerRestarts(mbc.Metrics.K8sContainerRestarts), - metricK8sContainerStorageLimit: newMetricK8sContainerStorageLimit(mbc.Metrics.K8sContainerStorageLimit), - metricK8sContainerStorageRequest: newMetricK8sContainerStorageRequest(mbc.Metrics.K8sContainerStorageRequest), - metricK8sCronjobActiveJobs: newMetricK8sCronjobActiveJobs(mbc.Metrics.K8sCronjobActiveJobs), - metricK8sDaemonsetCurrentScheduledNodes: newMetricK8sDaemonsetCurrentScheduledNodes(mbc.Metrics.K8sDaemonsetCurrentScheduledNodes), - metricK8sDaemonsetDesiredScheduledNodes: newMetricK8sDaemonsetDesiredScheduledNodes(mbc.Metrics.K8sDaemonsetDesiredScheduledNodes), - metricK8sDaemonsetMisscheduledNodes: newMetricK8sDaemonsetMisscheduledNodes(mbc.Metrics.K8sDaemonsetMisscheduledNodes), - metricK8sDaemonsetReadyNodes: newMetricK8sDaemonsetReadyNodes(mbc.Metrics.K8sDaemonsetReadyNodes), - metricK8sDeploymentAvailable: newMetricK8sDeploymentAvailable(mbc.Metrics.K8sDeploymentAvailable), - metricK8sDeploymentDesired: newMetricK8sDeploymentDesired(mbc.Metrics.K8sDeploymentDesired), - metricK8sHpaCurrentReplicas: newMetricK8sHpaCurrentReplicas(mbc.Metrics.K8sHpaCurrentReplicas), - metricK8sHpaDesiredReplicas: newMetricK8sHpaDesiredReplicas(mbc.Metrics.K8sHpaDesiredReplicas), - metricK8sHpaMaxReplicas: newMetricK8sHpaMaxReplicas(mbc.Metrics.K8sHpaMaxReplicas), - metricK8sHpaMinReplicas: newMetricK8sHpaMinReplicas(mbc.Metrics.K8sHpaMinReplicas), - metricK8sJobActivePods: newMetricK8sJobActivePods(mbc.Metrics.K8sJobActivePods), - metricK8sJobDesiredSuccessfulPods: newMetricK8sJobDesiredSuccessfulPods(mbc.Metrics.K8sJobDesiredSuccessfulPods), - metricK8sJobFailedPods: newMetricK8sJobFailedPods(mbc.Metrics.K8sJobFailedPods), - metricK8sJobMaxParallelPods: newMetricK8sJobMaxParallelPods(mbc.Metrics.K8sJobMaxParallelPods), - metricK8sJobSuccessfulPods: newMetricK8sJobSuccessfulPods(mbc.Metrics.K8sJobSuccessfulPods), - metricK8sNamespacePhase: newMetricK8sNamespacePhase(mbc.Metrics.K8sNamespacePhase), - metricK8sNodeCondition: newMetricK8sNodeCondition(mbc.Metrics.K8sNodeCondition), - metricK8sPodPhase: newMetricK8sPodPhase(mbc.Metrics.K8sPodPhase), - metricK8sPodStatusReason: newMetricK8sPodStatusReason(mbc.Metrics.K8sPodStatusReason), - metricK8sReplicasetAvailable: newMetricK8sReplicasetAvailable(mbc.Metrics.K8sReplicasetAvailable), - metricK8sReplicasetDesired: newMetricK8sReplicasetDesired(mbc.Metrics.K8sReplicasetDesired), - metricK8sReplicationControllerAvailable: newMetricK8sReplicationControllerAvailable(mbc.Metrics.K8sReplicationControllerAvailable), - metricK8sReplicationControllerDesired: newMetricK8sReplicationControllerDesired(mbc.Metrics.K8sReplicationControllerDesired), - metricK8sResourceQuotaHardLimit: newMetricK8sResourceQuotaHardLimit(mbc.Metrics.K8sResourceQuotaHardLimit), - metricK8sResourceQuotaUsed: newMetricK8sResourceQuotaUsed(mbc.Metrics.K8sResourceQuotaUsed), - metricK8sStatefulsetCurrentPods: newMetricK8sStatefulsetCurrentPods(mbc.Metrics.K8sStatefulsetCurrentPods), - metricK8sStatefulsetDesiredPods: newMetricK8sStatefulsetDesiredPods(mbc.Metrics.K8sStatefulsetDesiredPods), - metricK8sStatefulsetReadyPods: newMetricK8sStatefulsetReadyPods(mbc.Metrics.K8sStatefulsetReadyPods), - metricK8sStatefulsetUpdatedPods: newMetricK8sStatefulsetUpdatedPods(mbc.Metrics.K8sStatefulsetUpdatedPods), - metricOpenshiftAppliedclusterquotaLimit: newMetricOpenshiftAppliedclusterquotaLimit(mbc.Metrics.OpenshiftAppliedclusterquotaLimit), - metricOpenshiftAppliedclusterquotaUsed: newMetricOpenshiftAppliedclusterquotaUsed(mbc.Metrics.OpenshiftAppliedclusterquotaUsed), - metricOpenshiftClusterquotaLimit: newMetricOpenshiftClusterquotaLimit(mbc.Metrics.OpenshiftClusterquotaLimit), - metricOpenshiftClusterquotaUsed: newMetricOpenshiftClusterquotaUsed(mbc.Metrics.OpenshiftClusterquotaUsed), + metricK8sContainerEphemeralstorageRequest: newMetricK8sContainerEphemeralstorageRequest(mbc.Metrics.K8sContainerEphemeralstorageRequest), + metricK8sContainerMemoryLimit: newMetricK8sContainerMemoryLimit(mbc.Metrics.K8sContainerMemoryLimit), + metricK8sContainerMemoryRequest: newMetricK8sContainerMemoryRequest(mbc.Metrics.K8sContainerMemoryRequest), + metricK8sContainerReady: newMetricK8sContainerReady(mbc.Metrics.K8sContainerReady), + metricK8sContainerRestarts: newMetricK8sContainerRestarts(mbc.Metrics.K8sContainerRestarts), + metricK8sContainerStorageLimit: newMetricK8sContainerStorageLimit(mbc.Metrics.K8sContainerStorageLimit), + metricK8sContainerStorageRequest: newMetricK8sContainerStorageRequest(mbc.Metrics.K8sContainerStorageRequest), + metricK8sCronjobActiveJobs: newMetricK8sCronjobActiveJobs(mbc.Metrics.K8sCronjobActiveJobs), + metricK8sDaemonsetCurrentScheduledNodes: newMetricK8sDaemonsetCurrentScheduledNodes(mbc.Metrics.K8sDaemonsetCurrentScheduledNodes), + metricK8sDaemonsetDesiredScheduledNodes: newMetricK8sDaemonsetDesiredScheduledNodes(mbc.Metrics.K8sDaemonsetDesiredScheduledNodes), + metricK8sDaemonsetMisscheduledNodes: newMetricK8sDaemonsetMisscheduledNodes(mbc.Metrics.K8sDaemonsetMisscheduledNodes), + metricK8sDaemonsetReadyNodes: newMetricK8sDaemonsetReadyNodes(mbc.Metrics.K8sDaemonsetReadyNodes), + metricK8sDeploymentAvailable: newMetricK8sDeploymentAvailable(mbc.Metrics.K8sDeploymentAvailable), + metricK8sDeploymentDesired: newMetricK8sDeploymentDesired(mbc.Metrics.K8sDeploymentDesired), + metricK8sHierarchicalResourceQuotaHardLimit: newMetricK8sHierarchicalResourceQuotaHardLimit(mbc.Metrics.K8sHierarchicalResourceQuotaHardLimit), + metricK8sHierarchicalResourceQuotaUsed: newMetricK8sHierarchicalResourceQuotaUsed(mbc.Metrics.K8sHierarchicalResourceQuotaUsed), + metricK8sHpaCurrentReplicas: newMetricK8sHpaCurrentReplicas(mbc.Metrics.K8sHpaCurrentReplicas), + metricK8sHpaDesiredReplicas: newMetricK8sHpaDesiredReplicas(mbc.Metrics.K8sHpaDesiredReplicas), + metricK8sHpaMaxReplicas: newMetricK8sHpaMaxReplicas(mbc.Metrics.K8sHpaMaxReplicas), + metricK8sHpaMinReplicas: newMetricK8sHpaMinReplicas(mbc.Metrics.K8sHpaMinReplicas), + metricK8sJobActivePods: newMetricK8sJobActivePods(mbc.Metrics.K8sJobActivePods), + metricK8sJobDesiredSuccessfulPods: newMetricK8sJobDesiredSuccessfulPods(mbc.Metrics.K8sJobDesiredSuccessfulPods), + metricK8sJobFailedPods: newMetricK8sJobFailedPods(mbc.Metrics.K8sJobFailedPods), + metricK8sJobMaxParallelPods: newMetricK8sJobMaxParallelPods(mbc.Metrics.K8sJobMaxParallelPods), + metricK8sJobSuccessfulPods: newMetricK8sJobSuccessfulPods(mbc.Metrics.K8sJobSuccessfulPods), + metricK8sNamespacePhase: newMetricK8sNamespacePhase(mbc.Metrics.K8sNamespacePhase), + metricK8sNodeCondition: newMetricK8sNodeCondition(mbc.Metrics.K8sNodeCondition), + metricK8sPodPhase: newMetricK8sPodPhase(mbc.Metrics.K8sPodPhase), + metricK8sPodStatusReason: newMetricK8sPodStatusReason(mbc.Metrics.K8sPodStatusReason), + metricK8sReplicasetAvailable: newMetricK8sReplicasetAvailable(mbc.Metrics.K8sReplicasetAvailable), + metricK8sReplicasetDesired: newMetricK8sReplicasetDesired(mbc.Metrics.K8sReplicasetDesired), + metricK8sReplicationControllerAvailable: newMetricK8sReplicationControllerAvailable(mbc.Metrics.K8sReplicationControllerAvailable), + metricK8sReplicationControllerDesired: newMetricK8sReplicationControllerDesired(mbc.Metrics.K8sReplicationControllerDesired), + metricK8sResourceQuotaHardLimit: newMetricK8sResourceQuotaHardLimit(mbc.Metrics.K8sResourceQuotaHardLimit), + metricK8sResourceQuotaUsed: newMetricK8sResourceQuotaUsed(mbc.Metrics.K8sResourceQuotaUsed), + metricK8sStatefulsetCurrentPods: newMetricK8sStatefulsetCurrentPods(mbc.Metrics.K8sStatefulsetCurrentPods), + metricK8sStatefulsetDesiredPods: newMetricK8sStatefulsetDesiredPods(mbc.Metrics.K8sStatefulsetDesiredPods), + metricK8sStatefulsetReadyPods: newMetricK8sStatefulsetReadyPods(mbc.Metrics.K8sStatefulsetReadyPods), + metricK8sStatefulsetUpdatedPods: newMetricK8sStatefulsetUpdatedPods(mbc.Metrics.K8sStatefulsetUpdatedPods), + metricOpenshiftAppliedclusterquotaLimit: newMetricOpenshiftAppliedclusterquotaLimit(mbc.Metrics.OpenshiftAppliedclusterquotaLimit), + metricOpenshiftAppliedclusterquotaUsed: newMetricOpenshiftAppliedclusterquotaUsed(mbc.Metrics.OpenshiftAppliedclusterquotaUsed), + metricOpenshiftClusterquotaLimit: newMetricOpenshiftClusterquotaLimit(mbc.Metrics.OpenshiftClusterquotaLimit), + metricOpenshiftClusterquotaUsed: newMetricOpenshiftClusterquotaUsed(mbc.Metrics.OpenshiftClusterquotaUsed), } for _, op := range options { op(mb) @@ -2380,6 +2486,8 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricK8sDaemonsetReadyNodes.emit(ils.Metrics()) mb.metricK8sDeploymentAvailable.emit(ils.Metrics()) mb.metricK8sDeploymentDesired.emit(ils.Metrics()) + mb.metricK8sHierarchicalResourceQuotaHardLimit.emit(ils.Metrics()) + mb.metricK8sHierarchicalResourceQuotaUsed.emit(ils.Metrics()) mb.metricK8sHpaCurrentReplicas.emit(ils.Metrics()) mb.metricK8sHpaDesiredReplicas.emit(ils.Metrics()) mb.metricK8sHpaMaxReplicas.emit(ils.Metrics()) @@ -2512,6 +2620,16 @@ func (mb *MetricsBuilder) RecordK8sDeploymentDesiredDataPoint(ts pcommon.Timesta mb.metricK8sDeploymentDesired.recordDataPoint(mb.startTime, ts, val) } +// RecordK8sHierarchicalResourceQuotaHardLimitDataPoint adds a data point to k8s.hierarchical_resource_quota.hard_limit metric. +func (mb *MetricsBuilder) RecordK8sHierarchicalResourceQuotaHardLimitDataPoint(ts pcommon.Timestamp, val int64, resourceAttributeValue string) { + mb.metricK8sHierarchicalResourceQuotaHardLimit.recordDataPoint(mb.startTime, ts, val, resourceAttributeValue) +} + +// RecordK8sHierarchicalResourceQuotaUsedDataPoint adds a data point to k8s.hierarchical_resource_quota.used metric. +func (mb *MetricsBuilder) RecordK8sHierarchicalResourceQuotaUsedDataPoint(ts pcommon.Timestamp, val int64, resourceAttributeValue string) { + mb.metricK8sHierarchicalResourceQuotaUsed.recordDataPoint(mb.startTime, ts, val, resourceAttributeValue) +} + // RecordK8sHpaCurrentReplicasDataPoint adds a data point to k8s.hpa.current_replicas metric. func (mb *MetricsBuilder) RecordK8sHpaCurrentReplicasDataPoint(ts pcommon.Timestamp, val int64) { mb.metricK8sHpaCurrentReplicas.recordDataPoint(mb.startTime, ts, val) diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go index b0a6a520633e..d9d3f6d8139d 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go @@ -127,6 +127,12 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordK8sDeploymentDesiredDataPoint(ts, 1) + allMetricsCount++ + mb.RecordK8sHierarchicalResourceQuotaHardLimitDataPoint(ts, 1, "resource-val") + + allMetricsCount++ + mb.RecordK8sHierarchicalResourceQuotaUsedDataPoint(ts, 1, "resource-val") + defaultMetricsCount++ allMetricsCount++ mb.RecordK8sHpaCurrentReplicasDataPoint(ts, 1) @@ -244,6 +250,8 @@ func TestMetricsBuilder(t *testing.T) { rb.SetK8sDaemonsetUID("k8s.daemonset.uid-val") rb.SetK8sDeploymentName("k8s.deployment.name-val") rb.SetK8sDeploymentUID("k8s.deployment.uid-val") + rb.SetK8sHierarchicalresourcequotaName("k8s.hierarchicalresourcequota.name-val") + rb.SetK8sHierarchicalresourcequotaUID("k8s.hierarchicalresourcequota.uid-val") rb.SetK8sHpaName("k8s.hpa.name-val") rb.SetK8sHpaUID("k8s.hpa.uid-val") rb.SetK8sJobName("k8s.job.name-val") @@ -493,6 +501,36 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) + case "k8s.hierarchical_resource_quota.hard_limit": + assert.False(t, validatedMetrics["k8s.hierarchical_resource_quota.hard_limit"], "Found a duplicate in the metrics slice: k8s.hierarchical_resource_quota.hard_limit") + validatedMetrics["k8s.hierarchical_resource_quota.hard_limit"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The upper limit for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores", ms.At(i).Description()) + assert.Equal(t, "{resource}", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) + attrVal, ok := dp.Attributes().Get("resource") + assert.True(t, ok) + assert.EqualValues(t, "resource-val", attrVal.Str()) + case "k8s.hierarchical_resource_quota.used": + assert.False(t, validatedMetrics["k8s.hierarchical_resource_quota.used"], "Found a duplicate in the metrics slice: k8s.hierarchical_resource_quota.used") + validatedMetrics["k8s.hierarchical_resource_quota.used"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "The usage for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores", ms.At(i).Description()) + assert.Equal(t, "{resource}", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) + attrVal, ok := dp.Attributes().Get("resource") + assert.True(t, ok) + assert.EqualValues(t, "resource-val", attrVal.Str()) case "k8s.hpa.current_replicas": assert.False(t, validatedMetrics["k8s.hpa.current_replicas"], "Found a duplicate in the metrics slice: k8s.hpa.current_replicas") validatedMetrics["k8s.hpa.current_replicas"] = true diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_resource.go b/receiver/k8sclusterreceiver/internal/metadata/generated_resource.go index ff2df65ff6e4..0c8744be7f3a 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_resource.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_resource.go @@ -91,6 +91,20 @@ func (rb *ResourceBuilder) SetK8sDeploymentUID(val string) { } } +// SetK8sHierarchicalresourcequotaName sets provided value as "k8s.hierarchicalresourcequota.name" attribute. +func (rb *ResourceBuilder) SetK8sHierarchicalresourcequotaName(val string) { + if rb.config.K8sHierarchicalresourcequotaName.Enabled { + rb.res.Attributes().PutStr("k8s.hierarchicalresourcequota.name", val) + } +} + +// SetK8sHierarchicalresourcequotaUID sets provided value as "k8s.hierarchicalresourcequota.uid" attribute. +func (rb *ResourceBuilder) SetK8sHierarchicalresourcequotaUID(val string) { + if rb.config.K8sHierarchicalresourcequotaUID.Enabled { + rb.res.Attributes().PutStr("k8s.hierarchicalresourcequota.uid", val) + } +} + // SetK8sHpaName sets provided value as "k8s.hpa.name" attribute. func (rb *ResourceBuilder) SetK8sHpaName(val string) { if rb.config.K8sHpaName.Enabled { diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_resource_test.go b/receiver/k8sclusterreceiver/internal/metadata/generated_resource_test.go index 5eb06be19048..a66a6470b680 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_resource_test.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_resource_test.go @@ -23,6 +23,8 @@ func TestResourceBuilder(t *testing.T) { rb.SetK8sDaemonsetUID("k8s.daemonset.uid-val") rb.SetK8sDeploymentName("k8s.deployment.name-val") rb.SetK8sDeploymentUID("k8s.deployment.uid-val") + rb.SetK8sHierarchicalresourcequotaName("k8s.hierarchicalresourcequota.name-val") + rb.SetK8sHierarchicalresourcequotaUID("k8s.hierarchicalresourcequota.uid-val") rb.SetK8sHpaName("k8s.hpa.name-val") rb.SetK8sHpaUID("k8s.hpa.uid-val") rb.SetK8sJobName("k8s.job.name-val") @@ -54,7 +56,7 @@ func TestResourceBuilder(t *testing.T) { case "default": assert.Equal(t, 30, res.Attributes().Len()) case "all_set": - assert.Equal(t, 33, res.Attributes().Len()) + assert.Equal(t, 35, res.Attributes().Len()) case "none_set": assert.Equal(t, 0, res.Attributes().Len()) return @@ -112,6 +114,16 @@ func TestResourceBuilder(t *testing.T) { if ok { assert.EqualValues(t, "k8s.deployment.uid-val", val.Str()) } + val, ok = res.Attributes().Get("k8s.hierarchicalresourcequota.name") + assert.Equal(t, test == "all_set", ok) + if ok { + assert.EqualValues(t, "k8s.hierarchicalresourcequota.name-val", val.Str()) + } + val, ok = res.Attributes().Get("k8s.hierarchicalresourcequota.uid") + assert.Equal(t, test == "all_set", ok) + if ok { + assert.EqualValues(t, "k8s.hierarchicalresourcequota.uid-val", val.Str()) + } val, ok = res.Attributes().Get("k8s.hpa.name") assert.True(t, ok) if ok { diff --git a/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml b/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml index 11f9f2c95cb4..1e448c5187c5 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml @@ -35,6 +35,10 @@ all_set: enabled: true k8s.deployment.desired: enabled: true + k8s.hierarchical_resource_quota.hard_limit: + enabled: true + k8s.hierarchical_resource_quota.used: + enabled: true k8s.hpa.current_replicas: enabled: true k8s.hpa.desired_replicas: @@ -110,6 +114,10 @@ all_set: enabled: true k8s.deployment.uid: enabled: true + k8s.hierarchicalresourcequota.name: + enabled: true + k8s.hierarchicalresourcequota.uid: + enabled: true k8s.hpa.name: enabled: true k8s.hpa.uid: @@ -192,6 +200,10 @@ none_set: enabled: false k8s.deployment.desired: enabled: false + k8s.hierarchical_resource_quota.hard_limit: + enabled: false + k8s.hierarchical_resource_quota.used: + enabled: false k8s.hpa.current_replicas: enabled: false k8s.hpa.desired_replicas: @@ -267,6 +279,10 @@ none_set: enabled: false k8s.deployment.uid: enabled: false + k8s.hierarchicalresourcequota.name: + enabled: false + k8s.hierarchicalresourcequota.uid: + enabled: false k8s.hpa.name: enabled: false k8s.hpa.uid: diff --git a/receiver/k8sclusterreceiver/internal/testutils/objects.go b/receiver/k8sclusterreceiver/internal/testutils/objects.go index f94b2a90bfbf..b4cbeae18963 100644 --- a/receiver/k8sclusterreceiver/internal/testutils/objects.go +++ b/receiver/k8sclusterreceiver/internal/testutils/objects.go @@ -12,6 +12,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/hierarchical-namespaces/api/v1alpha2" ) func NewHPA(id string) *autoscalingv2.HorizontalPodAutoscaler { @@ -397,3 +398,27 @@ func NewCronJob(id string) *batchv1.CronJob { }, } } + +func NewHierarchicalResourceQuota(id string) *v1alpha2.HierarchicalResourceQuota { + return &v1alpha2.HierarchicalResourceQuota{ + ObjectMeta: v1.ObjectMeta{ + Name: "test-hierarchicalresourcequota-" + id, + Namespace: "test-namespace", + UID: types.UID("test-hierarchicalresourcequota-" + id + "-uid"), + Labels: map[string]string{ + "foo": "bar", + "foo1": "", + }, + }, + Status: v1alpha2.HierarchicalResourceQuotaStatus{ + Hard: corev1.ResourceList{ + "requests.cpu": resource.MustParse("1"), + "requests.memory": resource.MustParse("1Gi"), + }, + Used: corev1.ResourceList{ + "requests.cpu": resource.MustParse("500m"), + "requests.memory": resource.MustParse("512Mi"), + }, + }, + } +} diff --git a/receiver/k8sclusterreceiver/metadata.yaml b/receiver/k8sclusterreceiver/metadata.yaml index 7c1117081d0d..ddcde2e54344 100644 --- a/receiver/k8sclusterreceiver/metadata.yaml +++ b/receiver/k8sclusterreceiver/metadata.yaml @@ -179,6 +179,16 @@ resource_attributes: type: string enabled: true + k8s.hierarchicalresourcequota.uid: + description: The k8s HierarchicalResourceQuota uid. + type: string + enabled: false + + k8s.hierarchicalresourcequota.name: + description: The k8s HierarchicalresourceQuota name. + type: string + enabled: false + attributes: k8s.namespace.name: description: The k8s namespace name. @@ -497,3 +507,19 @@ metrics: # k8s.node.allocatable_* metrics (k8s.node.allocatable_cpu, k8s.node.allocatable_memory, etc) are controlled # by allocatable_types_to_report config option. By default, none of them are reported. + k8s.hierarchical_resource_quota.hard_limit: + enabled: false + description: The upper limit for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores + unit: "{resource}" + gauge: + value_type: int + attributes: + - resource + k8s.hierarchical_resource_quota.used: + enabled: false + description: The usage for a particular resource in a specific namespace. Will only be sent if a quota is specified. CPU requests/limits will be sent as millicores + unit: "{resource}" + gauge: + value_type: int + attributes: + - resource \ No newline at end of file diff --git a/receiver/k8sclusterreceiver/mock_resources_test.go b/receiver/k8sclusterreceiver/mock_resources_test.go index 3440cf569b27..1997e8259adc 100644 --- a/receiver/k8sclusterreceiver/mock_resources_test.go +++ b/receiver/k8sclusterreceiver/mock_resources_test.go @@ -16,8 +16,13 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/types" + fakeDynamic "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes/fake" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvk" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvr" ) func createPods(t *testing.T, client *fake.Clientset, numPods int) []*corev1.Pod { @@ -100,3 +105,27 @@ func createClusterQuota(t *testing.T, client *fakeQuota.Clientset, numQuotas int time.Sleep(2 * time.Millisecond) } } + +func createHrq(t *testing.T, client *fakeDynamic.FakeDynamicClient) { + namespace := "test-namespace" + unstructuredObject := &unstructured.Unstructured{ + Object: map[string]any{ + "apiVersion": gvk.HierarchicalResourceQuota.Version, + "kind": gvk.HierarchicalResourceQuota.Kind, + "metadata": map[string]any{ + "namespace": namespace, + "name": "test-hrq", + "uid": "test-hrq-uid", + }, + "status": map[string]any{ + "hard": map[string]any{ + "requests.cpu": "1", + "requests.memory": "1Gi", + }, + }, + }, + } + _, err := client.Resource(gvr.HierarchicalResourceQuota).Namespace(namespace).Create(context.Background(), unstructuredObject, v1.CreateOptions{}) + require.NoError(t, err, "error creating HierarchicalResourceQuota") + time.Sleep(2 * time.Millisecond) +} diff --git a/receiver/k8sclusterreceiver/receiver_test.go b/receiver/k8sclusterreceiver/receiver_test.go index f7d1713a373c..bc7956e7fc78 100644 --- a/receiver/k8sclusterreceiver/receiver_test.go +++ b/receiver/k8sclusterreceiver/receiver_test.go @@ -20,12 +20,16 @@ import ( "go.opentelemetry.io/collector/receiver" corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + fakeDynamic "k8s.io/client-go/dynamic/fake" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/fake" "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvk" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvr" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" ) @@ -37,26 +41,29 @@ func TestReceiver(t *testing.T) { }() client := newFakeClientWithAllResources() + dynamicClient := newFakeDynamicClient() osQuotaClient := fakeQuota.NewSimpleClientset() sink := new(consumertest.MetricsSink) - r := setupReceiver(client, osQuotaClient, sink, nil, 10*time.Second, tt) + r := setupReceiver(client, dynamicClient, osQuotaClient, sink, nil, 10*time.Second, tt) // Setup k8s resources. numPods := 2 numNodes := 1 numQuotas := 2 numClusterQuotaMetrics := numQuotas * 4 + numHrqMetrics := 2 createPods(t, client, numPods) createNodes(t, client, numNodes) createClusterQuota(t, osQuotaClient, 2) + createHrq(t, dynamicClient) ctx := context.Background() require.NoError(t, r.Start(ctx, componenttest.NewNopHost())) // Expects metric data from nodes and pods where each metric data // struct corresponds to one resource. - expectedNumMetrics := numPods + numNodes + numClusterQuotaMetrics + expectedNumMetrics := numPods + numNodes + numClusterQuotaMetrics + numHrqMetrics var initialDataPointCount int require.Eventually(t, func() bool { initialDataPointCount = sink.DataPointCount() @@ -68,7 +75,7 @@ func TestReceiver(t *testing.T) { deletePods(t, client, numPodsToDelete) // Expects metric data from a node, since other resources were deleted. - expectedNumMetrics = (numPods - numPodsToDelete) + numNodes + numClusterQuotaMetrics + expectedNumMetrics = (numPods - numPodsToDelete) + numNodes + numClusterQuotaMetrics + numHrqMetrics var metricsCountDelta int require.Eventually(t, func() bool { metricsCountDelta = sink.DataPointCount() - initialDataPointCount @@ -88,7 +95,7 @@ func TestReceiverTimesOutAfterStartup(t *testing.T) { client := newFakeClientWithAllResources() // Mock initial cache sync timing out, using a small timeout. - r := setupReceiver(client, nil, consumertest.NewNop(), nil, 1*time.Millisecond, tt) + r := setupReceiver(client, nil, nil, consumertest.NewNop(), nil, 1*time.Millisecond, tt) createPods(t, client, 1) @@ -108,10 +115,11 @@ func TestReceiverWithManyResources(t *testing.T) { }() client := newFakeClientWithAllResources() + dynamicClient := newFakeDynamicClient() osQuotaClient := fakeQuota.NewSimpleClientset() sink := new(consumertest.MetricsSink) - r := setupReceiver(client, osQuotaClient, sink, nil, 10*time.Second, tt) + r := setupReceiver(client, dynamicClient, osQuotaClient, sink, nil, 10*time.Second, tt) numPods := 1000 numQuotas := 2 @@ -151,7 +159,7 @@ func TestReceiverWithMetadata(t *testing.T) { logsConsumer := new(consumertest.LogsSink) - r := setupReceiver(client, nil, metricsConsumer, logsConsumer, 10*time.Second, tt) + r := setupReceiver(client, nil, nil, metricsConsumer, logsConsumer, 10*time.Second, tt) r.config.MetadataExporters = []string{"nop/withmetadata"} // Setup k8s resources. @@ -207,6 +215,7 @@ func getUpdatedPod(pod *corev1.Pod) any { func setupReceiver( client *fake.Clientset, + dymamicClient *fakeDynamic.FakeDynamicClient, osQuotaClient quotaclientset.Interface, metricsConsumer consumer.Metrics, logsConsumer consumer.Logs, @@ -218,12 +227,17 @@ func setupReceiver( distribution = distributionOpenShift } + mbc := metadata.DefaultMetricsBuilderConfig() + mbc.Metrics.K8sHierarchicalResourceQuotaHardLimit.Enabled = true + mbc.Metrics.K8sHierarchicalResourceQuotaUsed.Enabled = true + mbc.ResourceAttributes.K8sHierarchicalresourcequotaName.Enabled = true + mbc.ResourceAttributes.K8sHierarchicalresourcequotaUID.Enabled = true config := &Config{ CollectionInterval: 1 * time.Second, NodeConditionTypesToReport: []string{"Ready"}, AllocatableTypesToReport: []string{"cpu", "memory"}, Distribution: distribution, - MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(), + MetricsBuilderConfig: mbc, } r, _ := newReceiver(context.Background(), receiver.CreateSettings{ID: component.NewID(metadata.Type), TelemetrySettings: tt.TelemetrySettings, BuildInfo: component.NewDefaultBuildInfo()}, config) @@ -232,6 +246,9 @@ func setupReceiver( kr.resourceWatcher.makeClient = func(_ k8sconfig.APIConfig) (kubernetes.Interface, error) { return client, nil } + kr.resourceWatcher.makeDynamicClient = func(_ k8sconfig.APIConfig) (dynamic.Interface, error) { + return dymamicClient, nil + } kr.resourceWatcher.makeOpenShiftQuotaClient = func(_ k8sconfig.APIConfig) (quotaclientset.Interface, error) { return osQuotaClient, nil } @@ -276,10 +293,21 @@ func newFakeClientWithAllResources() *fake.Clientset { gvkToAPIResource(gvk.HorizontalPodAutoscaler), }, }, + { + GroupVersion: "hnc.x-k8s.io/v1alpha2", + APIResources: []v1.APIResource{ + gvkToAPIResource(gvk.HierarchicalResourceQuota), + }, + }, } return client } +func newFakeDynamicClient() *fakeDynamic.FakeDynamicClient { + gvrToListKind := map[schema.GroupVersionResource]string{gvr.HierarchicalResourceQuota: "HierarchicalResourceQuotaList"} + return fakeDynamic.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind) +} + func gvkToAPIResource(gvk schema.GroupVersionKind) v1.APIResource { return v1.APIResource{ Group: gvk.Group, diff --git a/receiver/k8sclusterreceiver/watcher.go b/receiver/k8sclusterreceiver/watcher.go index 93853122a583..cfa48e12185f 100644 --- a/receiver/k8sclusterreceiver/watcher.go +++ b/receiver/k8sclusterreceiver/watcher.go @@ -22,7 +22,10 @@ import ( batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/dynamic/dynamicinformer" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" @@ -33,6 +36,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/demonset" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/deployment" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvk" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvr" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/hpa" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/jobs" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" @@ -49,8 +53,20 @@ type sharedInformer interface { WaitForCacheSync(<-chan struct{}) map[reflect.Type]bool } +type dynamicSharedInformerFactory struct { + dynamicinformer.DynamicSharedInformerFactory +} + +func (df *dynamicSharedInformerFactory) WaitForCacheSync(channel <-chan struct{}) map[reflect.Type]bool { + cache := df.DynamicSharedInformerFactory.WaitForCacheSync(channel) + return map[reflect.Type]bool{ + reflect.TypeOf(&unstructured.Unstructured{}): cache[gvr.HierarchicalResourceQuota], + } +} + type resourceWatcher struct { client kubernetes.Interface + dynamicClient dynamic.Interface osQuotaClient quotaclientset.Interface informerFactories []sharedInformer metadataStore *metadata.Store @@ -64,6 +80,7 @@ type resourceWatcher struct { // For mocking. makeClient func(apiConf k8sconfig.APIConfig) (kubernetes.Interface, error) + makeDynamicClient func(apiConf k8sconfig.APIConfig) (dynamic.Interface, error) makeOpenShiftQuotaClient func(apiConf k8sconfig.APIConfig) (quotaclientset.Interface, error) } @@ -79,6 +96,7 @@ func newResourceWatcher(set receiver.CreateSettings, cfg *Config, metadataStore initialTimeout: defaultInitialSyncTimeout, config: cfg, makeClient: k8sconfig.MakeClient, + makeDynamicClient: k8sconfig.MakeDynamicClient, makeOpenShiftQuotaClient: k8sconfig.MakeOpenShiftQuotaClient, } } @@ -90,6 +108,12 @@ func (rw *resourceWatcher) initialize() error { } rw.client = client + dynamicClient, err := rw.makeDynamicClient(rw.config.APIConfig) + if err != nil { + return fmt.Errorf("Failed to create Kubernetes client: %w", err) + } + rw.dynamicClient = dynamicClient + if rw.config.Distribution == distributionOpenShift { rw.osQuotaClient, err = rw.makeOpenShiftQuotaClient(rw.config.APIConfig) if err != nil { @@ -153,6 +177,22 @@ func (rw *resourceWatcher) prepareSharedInformerFactory() error { } rw.informerFactories = append(rw.informerFactories, factory) + isSupported, err := rw.isKindSupported(gvk.HierarchicalResourceQuota) + switch { + case err != nil: + return err + case !isSupported: + rw.logger.Info("Server doesn't support any of the group versions defined for the kind", + zap.String("kind", gvk.HierarchicalResourceQuota.Kind)) + default: + dynamicFactory := dynamicinformer.NewDynamicSharedInformerFactory(rw.dynamicClient, rw.config.MetadataCollectionInterval) + dynamicInformer := dynamicFactory.ForResource(gvr.HierarchicalResourceQuota) + rw.setupInformer(gvk.HierarchicalResourceQuota, dynamicInformer.Informer()) + rw.informerFactories = append(rw.informerFactories, &(dynamicSharedInformerFactory{ + DynamicSharedInformerFactory: dynamicFactory, + })) + } + return nil }