From 0fb6dc5293767fada5c28f819e2c94af0b4adc13 Mon Sep 17 00:00:00 2001 From: Curtis Robert Date: Wed, 19 Jul 2023 15:45:51 -0700 Subject: [PATCH 1/8] [receiver/k8scluster] Convert clusterresourcequota to use pdata This converts the clusterresourcequota metrics to use the pdata format instead of opencensus. --- ...cluster-receiver-clusterresourcequota.yaml | 21 + .../clusteresourcequotas.go | 114 ------ .../clusterresourcequotas.go | 62 +++ .../clusterresourcequotas_test.go | 136 +----- .../internal/clusterresourcequota/doc.go | 6 + .../clusterresourcequota/documentation.md | 56 +++ .../internal/metadata/generated_config.go | 92 +++++ .../metadata/generated_config_test.go | 78 ++++ .../internal/metadata/generated_metrics.go | 386 ++++++++++++++++++ .../metadata/generated_metrics_test.go | 178 ++++++++ .../internal/metadata/testdata/config.yaml | 39 ++ .../clusterresourcequota/metadata.yaml | 52 +++ .../testdata/expected.yaml | 151 +++++++ .../internal/collection/collector.go | 2 +- .../internal/testutils/objects.go | 45 ++ 15 files changed, 1184 insertions(+), 234 deletions(-) create mode 100644 .chloggen/k8s-cluster-receiver-clusterresourcequota.yaml delete mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/clusteresourcequotas.go create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/doc.go create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/documentation.md create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config.go create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config_test.go create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics.go create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/testdata/config.yaml create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/metadata.yaml create mode 100644 receiver/k8sclusterreceiver/internal/clusterresourcequota/testdata/expected.yaml diff --git a/.chloggen/k8s-cluster-receiver-clusterresourcequota.yaml b/.chloggen/k8s-cluster-receiver-clusterresourcequota.yaml new file mode 100644 index 000000000000..9eed9d57be7d --- /dev/null +++ b/.chloggen/k8s-cluster-receiver-clusterresourcequota.yaml @@ -0,0 +1,21 @@ + +# Use this changelog template to create an entry for release notes. +# If your change doesn't affect end users, such as a test fix or a tooling change, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. + +# 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: "Change k8s.clusterresourcequota metrics to use mdatagen" + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [10553, 4367] + +# (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: \ No newline at end of file diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusteresourcequotas.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusteresourcequotas.go deleted file mode 100644 index dc77c0d5f5d4..000000000000 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusteresourcequotas.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package clusterresourcequota // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota" - -import ( - "strings" - - agentmetricspb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/metrics/v1" - metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1" - resourcepb "github.com/census-instrumentation/opencensus-proto/gen-go/resource/v1" - quotav1 "github.com/openshift/api/quota/v1" - conventions "go.opentelemetry.io/collector/semconv/v1.6.1" - corev1 "k8s.io/api/core/v1" - - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/constants" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/utils" -) - -var clusterResourceQuotaLimitMetric = &metricspb.MetricDescriptor{ - Name: "openshift.clusterquota.limit", - Description: "The configured upper limit for a particular resource.", - Type: metricspb.MetricDescriptor_GAUGE_INT64, - LabelKeys: []*metricspb.LabelKey{{ - Key: "resource", - }}, -} - -var clusterResourceQuotaUsedMetric = &metricspb.MetricDescriptor{ - Name: "openshift.clusterquota.used", - Description: "The usage for a particular resource with a configured limit.", - Type: metricspb.MetricDescriptor_GAUGE_INT64, - LabelKeys: []*metricspb.LabelKey{{ - Key: "resource", - }}, -} - -var appliedClusterResourceQuotaLimitMetric = &metricspb.MetricDescriptor{ - Name: "openshift.appliedclusterquota.limit", - Description: "The upper limit for a particular resource in a specific namespace.", - Type: metricspb.MetricDescriptor_GAUGE_INT64, - LabelKeys: []*metricspb.LabelKey{ - { - Key: "resource", - }, - { - Key: conventions.AttributeK8SNamespaceName, - }, - }, -} - -var appliedClusterResourceQuotaUsedMetric = &metricspb.MetricDescriptor{ - Name: "openshift.appliedclusterquota.used", - Description: "The usage for a particular resource in a specific namespace.", - Type: metricspb.MetricDescriptor_GAUGE_INT64, - LabelKeys: []*metricspb.LabelKey{ - { - Key: "resource", - }, - { - Key: conventions.AttributeK8SNamespaceName, - }, - }, -} - -func GetMetrics(rq *quotav1.ClusterResourceQuota) []*agentmetricspb.ExportMetricsServiceRequest { - var metrics []*metricspb.Metric - - metrics = appendClusterQuotaMetrics(metrics, clusterResourceQuotaLimitMetric, rq.Status.Total.Hard, "") - metrics = appendClusterQuotaMetrics(metrics, clusterResourceQuotaUsedMetric, rq.Status.Total.Used, "") - for _, ns := range rq.Status.Namespaces { - metrics = appendClusterQuotaMetrics(metrics, appliedClusterResourceQuotaLimitMetric, ns.Status.Hard, ns.Namespace) - metrics = appendClusterQuotaMetrics(metrics, appliedClusterResourceQuotaUsedMetric, ns.Status.Used, ns.Namespace) - } - return []*agentmetricspb.ExportMetricsServiceRequest{ - { - Resource: getResource(rq), - Metrics: metrics, - }, - } -} - -func appendClusterQuotaMetrics(metrics []*metricspb.Metric, metric *metricspb.MetricDescriptor, rl corev1.ResourceList, namespace string) []*metricspb.Metric { - for k, v := range rl { - val := v.Value() - if strings.HasSuffix(string(k), ".cpu") { - val = v.MilliValue() - } - - labels := []*metricspb.LabelValue{{Value: string(k), HasValue: true}} - if namespace != "" { - labels = append(labels, &metricspb.LabelValue{Value: namespace, HasValue: true}) - } - metrics = append(metrics, - &metricspb.Metric{ - MetricDescriptor: metric, - Timeseries: []*metricspb.TimeSeries{ - utils.GetInt64TimeSeriesWithLabels(val, labels), - }, - }, - ) - } - return metrics -} - -func getResource(rq *quotav1.ClusterResourceQuota) *resourcepb.Resource { - return &resourcepb.Resource{ - Type: constants.K8sType, - Labels: map[string]string{ - constants.K8sKeyClusterResourceQuotaUID: string(rq.UID), - constants.K8sKeyClusterResourceQuotaName: rq.Name, - }, - } -} diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go new file mode 100644 index 000000000000..be239b79f88c --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package clusterresourcequota // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota" + +import ( + "strings" + "time" + + quotav1 "github.com/openshift/api/quota/v1" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver" + v1 "k8s.io/api/core/v1" + + imetadataphase "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata" +) + +func GetMetrics(set receiver.CreateSettings, crq *quotav1.ClusterResourceQuota) pmetric.Metrics { + mbphase := imetadataphase.NewMetricsBuilder(imetadataphase.DefaultMetricsBuilderConfig(), set) + ts := pcommon.NewTimestampFromTime(time.Now()) + + emitCRQMetrics(mbphase, crq, crq.Status.Total.Hard, "openshift.clusterquota.limit", ts, "") + emitCRQMetrics(mbphase, crq, crq.Status.Total.Used, "openshift.clusterquota.used", ts, "") + + for _, ns := range crq.Status.Namespaces { + emitCRQMetrics(mbphase, crq, ns.Status.Hard, "openshift.appliedclusterquota.limit", ts, ns.Namespace) + emitCRQMetrics(mbphase, crq, ns.Status.Hard, "openshift.appliedclusterquota.used", ts, ns.Namespace) + + } + + return mbphase.Emit() +} + +func emitCRQMetrics(mbphase *imetadataphase.MetricsBuilder, crq *quotav1.ClusterResourceQuota, rl v1.ResourceList, metricName string, ts pcommon.Timestamp, namespace string) { + for k, v := range rl { + val := v.Value() + if strings.HasSuffix(string(k), ".cpu") { + val = v.MilliValue() + } + + switch metricName { + case "openshift.clusterquota.limit": + mbphase.RecordOpenshiftClusterquotaLimitDataPoint(ts, val) + case "openshift.clusterquota.used": + mbphase.RecordOpenshiftClusterquotaUsedDataPoint(ts, val) + case "openshift.appliedclusterquota.limit": + mbphase.RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts, val) + case "openshift.appliedclusterquota.used": + mbphase.RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts, val) + default: + return + } + + if namespace != "" { + mbphase.EmitForResource(imetadataphase.WithK8sNamespaceName(namespace), imetadataphase.WithOpenshiftClusterquotaUID(crq.Name), imetadataphase.WithOpenshiftClusterquotaName(string(crq.UID)), imetadataphase.WithOpencensusResourcetype(string(k))) + } else { + mbphase.EmitForResource(imetadataphase.WithOpenshiftClusterquotaUID(crq.Name), imetadataphase.WithOpenshiftClusterquotaName(string(crq.UID)), imetadataphase.WithOpencensusResourcetype(string(k))) + } + + } +} diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas_test.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas_test.go index d3bdb6b56608..e0edf7110919 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas_test.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas_test.go @@ -4,132 +4,30 @@ package clusterresourcequota import ( + "path/filepath" "testing" - metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1" - quotav1 "github.com/openshift/api/quota/v1" "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" + "go.opentelemetry.io/collector/receiver/receivertest" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/constants" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/testutils" ) func TestClusterRequestQuotaMetrics(t *testing.T) { - rq := newMockClusterResourceQuota("1") - - actualResourceMetrics := GetMetrics(rq) - - require.Equal(t, 1, len(actualResourceMetrics)) - - metrics := actualResourceMetrics[0].Metrics - require.Equal(t, 6, len(metrics)) - testutils.AssertResource(t, actualResourceMetrics[0].Resource, constants.K8sType, - map[string]string{ - "openshift.clusterquota.uid": "test-clusterquota-1-uid", - "openshift.clusterquota.name": "test-clusterquota-1", - }, + crq := testutils.NewClusterResourceQuota("1") + + m := GetMetrics(receivertest.NewNopCreateSettings(), crq) + + 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(), + ), ) - - for i, tc := range []struct { - name string - value int64 - labels map[string]string - }{ - { - "openshift.clusterquota.limit", - 10000, - map[string]string{ - "resource": "requests.cpu", - }, - }, - { - "openshift.clusterquota.used", - 6000, - map[string]string{ - "resource": "requests.cpu", - }, - }, - { - "openshift.appliedclusterquota.limit", - 6000, - map[string]string{ - "resource": "requests.cpu", - "k8s.namespace.name": "ns1", - }, - }, - { - "openshift.appliedclusterquota.used", - 1000, - map[string]string{ - "resource": "requests.cpu", - "k8s.namespace.name": "ns1", - }, - }, - { - "openshift.appliedclusterquota.limit", - 4000, - map[string]string{ - "resource": "requests.cpu", - "k8s.namespace.name": "ns2", - }, - }, - { - "openshift.appliedclusterquota.used", - 5000, - map[string]string{ - "resource": "requests.cpu", - "k8s.namespace.name": "ns2", - }, - }, - } { - testutils.AssertMetricsWithLabels(t, metrics[i], tc.name, - metricspb.MetricDescriptor_GAUGE_INT64, tc.labels, tc.value) - } -} - -func newMockClusterResourceQuota(id string) *quotav1.ClusterResourceQuota { - return "av1.ClusterResourceQuota{ - ObjectMeta: v1.ObjectMeta{ - Name: "test-clusterquota-" + id, - UID: types.UID("test-clusterquota-" + id + "-uid"), - }, - Status: quotav1.ClusterResourceQuotaStatus{ - Total: corev1.ResourceQuotaStatus{ - Hard: corev1.ResourceList{ - "requests.cpu": *resource.NewQuantity(10, resource.DecimalSI), - }, - Used: corev1.ResourceList{ - "requests.cpu": *resource.NewQuantity(6, resource.DecimalSI), - }, - }, - Namespaces: quotav1.ResourceQuotasStatusByNamespace{ - { - Namespace: "ns1", - Status: corev1.ResourceQuotaStatus{ - Hard: corev1.ResourceList{ - "requests.cpu": *resource.NewQuantity(6, resource.DecimalSI), - }, - Used: corev1.ResourceList{ - "requests.cpu": *resource.NewQuantity(1, resource.DecimalSI), - }, - }, - }, - { - Namespace: "ns2", - Status: corev1.ResourceQuotaStatus{ - Hard: corev1.ResourceList{ - "requests.cpu": *resource.NewQuantity(4, resource.DecimalSI), - }, - Used: corev1.ResourceList{ - "requests.cpu": *resource.NewQuantity(5, resource.DecimalSI), - }, - }, - }, - }, - }, - } } diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/doc.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/doc.go new file mode 100644 index 000000000000..a30fe432c28d --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:generate mdatagen metadata.yaml + +package clusterresourcequota // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota" diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/documentation.md b/receiver/k8sclusterreceiver/internal/clusterresourcequota/documentation.md new file mode 100644 index 000000000000..e3aec0b5a901 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/documentation.md @@ -0,0 +1,56 @@ +[comment]: <> (Code generated by mdatagen. DO NOT EDIT.) + +# k8s/clusterresourcequota + +**Parent Component:** k8s_cluster + +## Default Metrics + +The following metrics are emitted by default. Each of them can be disabled by applying the following configuration: + +```yaml +metrics: + : + enabled: false +``` + +### openshift.appliedclusterquota.limit + +The upper limit for a particular resource in a specific namespace. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| 1 | Gauge | Int | + +### openshift.appliedclusterquota.used + +The usage for a particular resource in a specific namespace. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| 1 | Gauge | Int | + +### openshift.clusterquota.limit + +The configured upper limit for a particular resource. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| 1 | Gauge | Int | + +### openshift.clusterquota.used + +The usage for a particular resource with a configured limit. + +| Unit | Metric Type | Value Type | +| ---- | ----------- | ---------- | +| 1 | Gauge | Int | + +## Resource Attributes + +| Name | Description | Values | Enabled | +| ---- | ----------- | ------ | ------- | +| k8s.namespace.name | The k8s namespace name. | Any Str | true | +| opencensus.resourcetype | The OpenCensus resource type. | Any Str | true | +| openshift.clusterquota.name | The k8s ClusterResourceQuota name. | Any Str | true | +| openshift.clusterquota.uid | The k8s ClusterResourceQuota uid. | Any Str | true | diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config.go new file mode 100644 index 000000000000..d2b789229d51 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config.go @@ -0,0 +1,92 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import "go.opentelemetry.io/collector/confmap" + +// MetricConfig provides common config for a particular metric. +type MetricConfig struct { + Enabled bool `mapstructure:"enabled"` + + enabledSetByUser bool +} + +func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { + if parser == nil { + return nil + } + err := parser.Unmarshal(ms, confmap.WithErrorUnused()) + if err != nil { + return err + } + ms.enabledSetByUser = parser.IsSet("enabled") + return nil +} + +// MetricsConfig provides config for k8s/clusterresourcequota metrics. +type MetricsConfig struct { + 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 { + return MetricsConfig{ + OpenshiftAppliedclusterquotaLimit: MetricConfig{ + Enabled: true, + }, + OpenshiftAppliedclusterquotaUsed: MetricConfig{ + Enabled: true, + }, + OpenshiftClusterquotaLimit: MetricConfig{ + Enabled: true, + }, + OpenshiftClusterquotaUsed: MetricConfig{ + Enabled: true, + }, + } +} + +// ResourceAttributeConfig provides common config for a particular resource attribute. +type ResourceAttributeConfig struct { + Enabled bool `mapstructure:"enabled"` +} + +// ResourceAttributesConfig provides config for k8s/clusterresourcequota resource attributes. +type ResourceAttributesConfig struct { + K8sNamespaceName ResourceAttributeConfig `mapstructure:"k8s.namespace.name"` + OpencensusResourcetype ResourceAttributeConfig `mapstructure:"opencensus.resourcetype"` + OpenshiftClusterquotaName ResourceAttributeConfig `mapstructure:"openshift.clusterquota.name"` + OpenshiftClusterquotaUID ResourceAttributeConfig `mapstructure:"openshift.clusterquota.uid"` +} + +func DefaultResourceAttributesConfig() ResourceAttributesConfig { + return ResourceAttributesConfig{ + K8sNamespaceName: ResourceAttributeConfig{ + Enabled: true, + }, + OpencensusResourcetype: ResourceAttributeConfig{ + Enabled: true, + }, + OpenshiftClusterquotaName: ResourceAttributeConfig{ + Enabled: true, + }, + OpenshiftClusterquotaUID: ResourceAttributeConfig{ + Enabled: true, + }, + } +} + +// MetricsBuilderConfig is a configuration for k8s/clusterresourcequota metrics builder. +type MetricsBuilderConfig struct { + Metrics MetricsConfig `mapstructure:"metrics"` + ResourceAttributes ResourceAttributesConfig `mapstructure:"resource_attributes"` +} + +func DefaultMetricsBuilderConfig() MetricsBuilderConfig { + return MetricsBuilderConfig{ + Metrics: DefaultMetricsConfig(), + ResourceAttributes: DefaultResourceAttributesConfig(), + } +} diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config_test.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config_test.go new file mode 100644 index 000000000000..36a97143d165 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config_test.go @@ -0,0 +1,78 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap/confmaptest" +) + +func TestMetricsBuilderConfig(t *testing.T) { + tests := []struct { + name string + want MetricsBuilderConfig + }{ + { + name: "default", + want: DefaultMetricsBuilderConfig(), + }, + { + name: "all_set", + want: MetricsBuilderConfig{ + Metrics: MetricsConfig{ + OpenshiftAppliedclusterquotaLimit: MetricConfig{Enabled: true}, + OpenshiftAppliedclusterquotaUsed: MetricConfig{Enabled: true}, + OpenshiftClusterquotaLimit: MetricConfig{Enabled: true}, + OpenshiftClusterquotaUsed: MetricConfig{Enabled: true}, + }, + ResourceAttributes: ResourceAttributesConfig{ + K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, + OpencensusResourcetype: ResourceAttributeConfig{Enabled: true}, + OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: true}, + OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: true}, + }, + }, + }, + { + name: "none_set", + want: MetricsBuilderConfig{ + Metrics: MetricsConfig{ + OpenshiftAppliedclusterquotaLimit: MetricConfig{Enabled: false}, + OpenshiftAppliedclusterquotaUsed: MetricConfig{Enabled: false}, + OpenshiftClusterquotaLimit: MetricConfig{Enabled: false}, + OpenshiftClusterquotaUsed: MetricConfig{Enabled: false}, + }, + ResourceAttributes: ResourceAttributesConfig{ + K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, + OpencensusResourcetype: ResourceAttributeConfig{Enabled: false}, + OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: false}, + OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: false}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadMetricsBuilderConfig(t, tt.name) + if diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(MetricConfig{}, ResourceAttributeConfig{})); diff != "" { + t.Errorf("Config mismatch (-expected +actual):\n%s", diff) + } + }) + } +} + +func loadMetricsBuilderConfig(t *testing.T, name string) MetricsBuilderConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + cfg := DefaultMetricsBuilderConfig() + require.NoError(t, component.UnmarshalConfig(sub, &cfg)) + return cfg +} diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics.go new file mode 100644 index 000000000000..14fd8686bc78 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics.go @@ -0,0 +1,386 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver" + conventions "go.opentelemetry.io/collector/semconv/v1.18.0" +) + +type metricOpenshiftAppliedclusterquotaLimit 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 openshift.appliedclusterquota.limit metric with initial data. +func (m *metricOpenshiftAppliedclusterquotaLimit) init() { + m.data.SetName("openshift.appliedclusterquota.limit") + m.data.SetDescription("The upper limit for a particular resource in a specific namespace.") + m.data.SetUnit("1") + m.data.SetEmptyGauge() +} + +func (m *metricOpenshiftAppliedclusterquotaLimit) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricOpenshiftAppliedclusterquotaLimit) 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 *metricOpenshiftAppliedclusterquotaLimit) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricOpenshiftAppliedclusterquotaLimit(cfg MetricConfig) metricOpenshiftAppliedclusterquotaLimit { + m := metricOpenshiftAppliedclusterquotaLimit{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricOpenshiftAppliedclusterquotaUsed 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 openshift.appliedclusterquota.used metric with initial data. +func (m *metricOpenshiftAppliedclusterquotaUsed) init() { + m.data.SetName("openshift.appliedclusterquota.used") + m.data.SetDescription("The usage for a particular resource in a specific namespace.") + m.data.SetUnit("1") + m.data.SetEmptyGauge() +} + +func (m *metricOpenshiftAppliedclusterquotaUsed) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricOpenshiftAppliedclusterquotaUsed) 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 *metricOpenshiftAppliedclusterquotaUsed) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricOpenshiftAppliedclusterquotaUsed(cfg MetricConfig) metricOpenshiftAppliedclusterquotaUsed { + m := metricOpenshiftAppliedclusterquotaUsed{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricOpenshiftClusterquotaLimit 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 openshift.clusterquota.limit metric with initial data. +func (m *metricOpenshiftClusterquotaLimit) init() { + m.data.SetName("openshift.clusterquota.limit") + m.data.SetDescription("The configured upper limit for a particular resource.") + m.data.SetUnit("1") + m.data.SetEmptyGauge() +} + +func (m *metricOpenshiftClusterquotaLimit) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricOpenshiftClusterquotaLimit) 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 *metricOpenshiftClusterquotaLimit) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricOpenshiftClusterquotaLimit(cfg MetricConfig) metricOpenshiftClusterquotaLimit { + m := metricOpenshiftClusterquotaLimit{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricOpenshiftClusterquotaUsed 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 openshift.clusterquota.used metric with initial data. +func (m *metricOpenshiftClusterquotaUsed) init() { + m.data.SetName("openshift.clusterquota.used") + m.data.SetDescription("The usage for a particular resource with a configured limit.") + m.data.SetUnit("1") + m.data.SetEmptyGauge() +} + +func (m *metricOpenshiftClusterquotaUsed) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricOpenshiftClusterquotaUsed) 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 *metricOpenshiftClusterquotaUsed) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricOpenshiftClusterquotaUsed(cfg MetricConfig) metricOpenshiftClusterquotaUsed { + m := metricOpenshiftClusterquotaUsed{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +// 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 { + startTime pcommon.Timestamp // start time that will be applied to all recorded data points. + metricsCapacity int // maximum observed number of metrics per resource. + resourceCapacity int // maximum observed number of resource attributes. + metricsBuffer pmetric.Metrics // accumulates metrics data before emitting. + buildInfo component.BuildInfo // contains version information + resourceAttributesConfig ResourceAttributesConfig + metricOpenshiftAppliedclusterquotaLimit metricOpenshiftAppliedclusterquotaLimit + metricOpenshiftAppliedclusterquotaUsed metricOpenshiftAppliedclusterquotaUsed + metricOpenshiftClusterquotaLimit metricOpenshiftClusterquotaLimit + metricOpenshiftClusterquotaUsed metricOpenshiftClusterquotaUsed +} + +// metricBuilderOption applies changes to default metrics builder. +type metricBuilderOption func(*MetricsBuilder) + +// WithStartTime sets startTime on the metrics builder. +func WithStartTime(startTime pcommon.Timestamp) metricBuilderOption { + return func(mb *MetricsBuilder) { + mb.startTime = startTime + } +} + +func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSettings, options ...metricBuilderOption) *MetricsBuilder { + mb := &MetricsBuilder{ + startTime: pcommon.NewTimestampFromTime(time.Now()), + metricsBuffer: pmetric.NewMetrics(), + buildInfo: settings.BuildInfo, + resourceAttributesConfig: mbc.ResourceAttributes, + 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) + } + return mb +} + +// updateCapacity updates max length of metrics and resource attributes that will be used for the slice capacity. +func (mb *MetricsBuilder) updateCapacity(rm pmetric.ResourceMetrics) { + if mb.metricsCapacity < rm.ScopeMetrics().At(0).Metrics().Len() { + mb.metricsCapacity = rm.ScopeMetrics().At(0).Metrics().Len() + } + if mb.resourceCapacity < rm.Resource().Attributes().Len() { + mb.resourceCapacity = rm.Resource().Attributes().Len() + } +} + +// ResourceMetricsOption applies changes to provided resource metrics. +type ResourceMetricsOption func(ResourceAttributesConfig, pmetric.ResourceMetrics) + +// WithK8sNamespaceName sets provided value as "k8s.namespace.name" attribute for current resource. +func WithK8sNamespaceName(val string) ResourceMetricsOption { + return func(rac ResourceAttributesConfig, rm pmetric.ResourceMetrics) { + if rac.K8sNamespaceName.Enabled { + rm.Resource().Attributes().PutStr("k8s.namespace.name", val) + } + } +} + +// WithOpencensusResourcetype sets provided value as "opencensus.resourcetype" attribute for current resource. +func WithOpencensusResourcetype(val string) ResourceMetricsOption { + return func(rac ResourceAttributesConfig, rm pmetric.ResourceMetrics) { + if rac.OpencensusResourcetype.Enabled { + rm.Resource().Attributes().PutStr("opencensus.resourcetype", val) + } + } +} + +// WithOpenshiftClusterquotaName sets provided value as "openshift.clusterquota.name" attribute for current resource. +func WithOpenshiftClusterquotaName(val string) ResourceMetricsOption { + return func(rac ResourceAttributesConfig, rm pmetric.ResourceMetrics) { + if rac.OpenshiftClusterquotaName.Enabled { + rm.Resource().Attributes().PutStr("openshift.clusterquota.name", val) + } + } +} + +// WithOpenshiftClusterquotaUID sets provided value as "openshift.clusterquota.uid" attribute for current resource. +func WithOpenshiftClusterquotaUID(val string) ResourceMetricsOption { + return func(rac ResourceAttributesConfig, rm pmetric.ResourceMetrics) { + if rac.OpenshiftClusterquotaUID.Enabled { + rm.Resource().Attributes().PutStr("openshift.clusterquota.uid", val) + } + } +} + +// WithStartTimeOverride overrides start time for all the resource metrics data points. +// This option should be only used if different start time has to be set on metrics coming from different resources. +func WithStartTimeOverride(start pcommon.Timestamp) ResourceMetricsOption { + return func(_ ResourceAttributesConfig, rm pmetric.ResourceMetrics) { + var dps pmetric.NumberDataPointSlice + metrics := rm.ScopeMetrics().At(0).Metrics() + for i := 0; i < metrics.Len(); i++ { + switch metrics.At(i).Type() { + case pmetric.MetricTypeGauge: + dps = metrics.At(i).Gauge().DataPoints() + case pmetric.MetricTypeSum: + dps = metrics.At(i).Sum().DataPoints() + } + for j := 0; j < dps.Len(); j++ { + dps.At(j).SetStartTimestamp(start) + } + } + } +} + +// EmitForResource saves all the generated metrics under a new resource and updates the internal state to be ready for +// recording another set of data points as part of another resource. This function can be helpful when one scraper +// needs to emit metrics from several resources. Otherwise calling this function is not required, +// just `Emit` function can be called instead. +// Resource attributes should be provided as ResourceMetricsOption arguments. +func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { + rm := pmetric.NewResourceMetrics() + rm.SetSchemaUrl(conventions.SchemaURL) + rm.Resource().Attributes().EnsureCapacity(mb.resourceCapacity) + ils := rm.ScopeMetrics().AppendEmpty() + ils.Scope().SetName("otelcol/k8sclusterreceiver") + ils.Scope().SetVersion(mb.buildInfo.Version) + ils.Metrics().EnsureCapacity(mb.metricsCapacity) + mb.metricOpenshiftAppliedclusterquotaLimit.emit(ils.Metrics()) + mb.metricOpenshiftAppliedclusterquotaUsed.emit(ils.Metrics()) + mb.metricOpenshiftClusterquotaLimit.emit(ils.Metrics()) + mb.metricOpenshiftClusterquotaUsed.emit(ils.Metrics()) + + for _, op := range rmo { + op(mb.resourceAttributesConfig, rm) + } + if ils.Metrics().Len() > 0 { + mb.updateCapacity(rm) + rm.MoveTo(mb.metricsBuffer.ResourceMetrics().AppendEmpty()) + } +} + +// Emit returns all the metrics accumulated by the metrics builder and updates the internal state to be ready for +// recording another set of metrics. This function will be responsible for applying all the transformations required to +// produce metric representation defined in metadata and user config, e.g. delta or cumulative. +func (mb *MetricsBuilder) Emit(rmo ...ResourceMetricsOption) pmetric.Metrics { + mb.EmitForResource(rmo...) + metrics := mb.metricsBuffer + mb.metricsBuffer = pmetric.NewMetrics() + return metrics +} + +// RecordOpenshiftAppliedclusterquotaLimitDataPoint adds a data point to openshift.appliedclusterquota.limit metric. +func (mb *MetricsBuilder) RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricOpenshiftAppliedclusterquotaLimit.recordDataPoint(mb.startTime, ts, val) +} + +// RecordOpenshiftAppliedclusterquotaUsedDataPoint adds a data point to openshift.appliedclusterquota.used metric. +func (mb *MetricsBuilder) RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricOpenshiftAppliedclusterquotaUsed.recordDataPoint(mb.startTime, ts, val) +} + +// RecordOpenshiftClusterquotaLimitDataPoint adds a data point to openshift.clusterquota.limit metric. +func (mb *MetricsBuilder) RecordOpenshiftClusterquotaLimitDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricOpenshiftClusterquotaLimit.recordDataPoint(mb.startTime, ts, val) +} + +// RecordOpenshiftClusterquotaUsedDataPoint adds a data point to openshift.clusterquota.used metric. +func (mb *MetricsBuilder) RecordOpenshiftClusterquotaUsedDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricOpenshiftClusterquotaUsed.recordDataPoint(mb.startTime, ts, val) +} + +// Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, +// and metrics builder should update its startTime and reset it's internal state accordingly. +func (mb *MetricsBuilder) Reset(options ...metricBuilderOption) { + mb.startTime = pcommon.NewTimestampFromTime(time.Now()) + for _, op := range options { + op(mb) + } +} diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go new file mode 100644 index 000000000000..d29122aeb09a --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go @@ -0,0 +1,178 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver/receivertest" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +type testConfigCollection int + +const ( + testSetDefault testConfigCollection = iota + testSetAll + testSetNone +) + +func TestMetricsBuilder(t *testing.T) { + tests := []struct { + name string + configSet testConfigCollection + }{ + { + name: "default", + configSet: testSetDefault, + }, + { + name: "all_set", + configSet: testSetAll, + }, + { + name: "none_set", + configSet: testSetNone, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + start := pcommon.Timestamp(1_000_000_000) + ts := pcommon.Timestamp(1_000_001_000) + observedZapCore, observedLogs := observer.New(zap.WarnLevel) + settings := receivertest.NewNopCreateSettings() + settings.Logger = zap.New(observedZapCore) + mb := NewMetricsBuilder(loadMetricsBuilderConfig(t, test.name), settings, WithStartTime(start)) + + expectedWarnings := 0 + assert.Equal(t, expectedWarnings, observedLogs.Len()) + + defaultMetricsCount := 0 + allMetricsCount := 0 + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts, 1) + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts, 1) + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordOpenshiftClusterquotaLimitDataPoint(ts, 1) + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordOpenshiftClusterquotaUsedDataPoint(ts, 1) + + metrics := mb.Emit(WithK8sNamespaceName("attr-val"), WithOpencensusResourcetype("attr-val"), WithOpenshiftClusterquotaName("attr-val"), WithOpenshiftClusterquotaUID("attr-val")) + + if test.configSet == testSetNone { + assert.Equal(t, 0, metrics.ResourceMetrics().Len()) + return + } + + assert.Equal(t, 1, metrics.ResourceMetrics().Len()) + rm := metrics.ResourceMetrics().At(0) + attrCount := 0 + enabledAttrCount := 0 + attrVal, ok := rm.Resource().Attributes().Get("k8s.namespace.name") + attrCount++ + assert.Equal(t, mb.resourceAttributesConfig.K8sNamespaceName.Enabled, ok) + if mb.resourceAttributesConfig.K8sNamespaceName.Enabled { + enabledAttrCount++ + assert.EqualValues(t, "attr-val", attrVal.Str()) + } + attrVal, ok = rm.Resource().Attributes().Get("opencensus.resourcetype") + attrCount++ + assert.Equal(t, mb.resourceAttributesConfig.OpencensusResourcetype.Enabled, ok) + if mb.resourceAttributesConfig.OpencensusResourcetype.Enabled { + enabledAttrCount++ + assert.EqualValues(t, "attr-val", attrVal.Str()) + } + attrVal, ok = rm.Resource().Attributes().Get("openshift.clusterquota.name") + attrCount++ + assert.Equal(t, mb.resourceAttributesConfig.OpenshiftClusterquotaName.Enabled, ok) + if mb.resourceAttributesConfig.OpenshiftClusterquotaName.Enabled { + enabledAttrCount++ + assert.EqualValues(t, "attr-val", attrVal.Str()) + } + attrVal, ok = rm.Resource().Attributes().Get("openshift.clusterquota.uid") + attrCount++ + assert.Equal(t, mb.resourceAttributesConfig.OpenshiftClusterquotaUID.Enabled, ok) + if mb.resourceAttributesConfig.OpenshiftClusterquotaUID.Enabled { + enabledAttrCount++ + assert.EqualValues(t, "attr-val", attrVal.Str()) + } + assert.Equal(t, enabledAttrCount, rm.Resource().Attributes().Len()) + assert.Equal(t, attrCount, 4) + + assert.Equal(t, 1, rm.ScopeMetrics().Len()) + ms := rm.ScopeMetrics().At(0).Metrics() + if test.configSet == testSetDefault { + assert.Equal(t, defaultMetricsCount, ms.Len()) + } + if test.configSet == testSetAll { + assert.Equal(t, allMetricsCount, ms.Len()) + } + validatedMetrics := make(map[string]bool) + for i := 0; i < ms.Len(); i++ { + switch ms.At(i).Name() { + case "openshift.appliedclusterquota.limit": + assert.False(t, validatedMetrics["openshift.appliedclusterquota.limit"], "Found a duplicate in the metrics slice: openshift.appliedclusterquota.limit") + validatedMetrics["openshift.appliedclusterquota.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.", ms.At(i).Description()) + assert.Equal(t, "1", 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()) + case "openshift.appliedclusterquota.used": + assert.False(t, validatedMetrics["openshift.appliedclusterquota.used"], "Found a duplicate in the metrics slice: openshift.appliedclusterquota.used") + validatedMetrics["openshift.appliedclusterquota.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.", ms.At(i).Description()) + assert.Equal(t, "1", 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()) + case "openshift.clusterquota.limit": + assert.False(t, validatedMetrics["openshift.clusterquota.limit"], "Found a duplicate in the metrics slice: openshift.clusterquota.limit") + validatedMetrics["openshift.clusterquota.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 configured upper limit for a particular resource.", ms.At(i).Description()) + assert.Equal(t, "1", 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()) + case "openshift.clusterquota.used": + assert.False(t, validatedMetrics["openshift.clusterquota.used"], "Found a duplicate in the metrics slice: openshift.clusterquota.used") + validatedMetrics["openshift.clusterquota.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 with a configured limit.", ms.At(i).Description()) + assert.Equal(t, "1", 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()) + } + } + }) + } +} diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/testdata/config.yaml b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/testdata/config.yaml new file mode 100644 index 000000000000..7415ae508568 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/testdata/config.yaml @@ -0,0 +1,39 @@ +default: +all_set: + metrics: + openshift.appliedclusterquota.limit: + enabled: true + openshift.appliedclusterquota.used: + enabled: true + openshift.clusterquota.limit: + enabled: true + openshift.clusterquota.used: + enabled: true + resource_attributes: + k8s.namespace.name: + enabled: true + opencensus.resourcetype: + enabled: true + openshift.clusterquota.name: + enabled: true + openshift.clusterquota.uid: + enabled: true +none_set: + metrics: + openshift.appliedclusterquota.limit: + enabled: false + openshift.appliedclusterquota.used: + enabled: false + openshift.clusterquota.limit: + enabled: false + openshift.clusterquota.used: + enabled: false + resource_attributes: + k8s.namespace.name: + enabled: false + opencensus.resourcetype: + enabled: false + openshift.clusterquota.name: + enabled: false + openshift.clusterquota.uid: + enabled: false diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/metadata.yaml b/receiver/k8sclusterreceiver/internal/clusterresourcequota/metadata.yaml new file mode 100644 index 000000000000..d647708fda05 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/metadata.yaml @@ -0,0 +1,52 @@ +type: k8s/clusterresourcequota + +sem_conv_version: 1.18.0 + +parent: k8s_cluster + +resource_attributes: + k8s.namespace.name: + description: The k8s namespace name. + type: string + enabled: true + + openshift.clusterquota.uid: + description: The k8s ClusterResourceQuota uid. + type: string + enabled: true + + openshift.clusterquota.name: + description: The k8s ClusterResourceQuota name. + type: string + enabled: true + + opencensus.resourcetype: + description: The OpenCensus resource type. + type: string + enabled: true + +metrics: + openshift.clusterquota.limit: + enabled: true + description: The configured upper limit for a particular resource. + unit: "1" + gauge: + value_type: int + openshift.clusterquota.used: + enabled: true + description: The usage for a particular resource with a configured limit. + unit: "1" + gauge: + value_type: int + openshift.appliedclusterquota.limit: + enabled: true + description: The upper limit for a particular resource in a specific namespace. + unit: "1" + gauge: + value_type: int + openshift.appliedclusterquota.used: + enabled: true + description: The usage for a particular resource in a specific namespace. + unit: "1" + gauge: + value_type: int diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/testdata/expected.yaml b/receiver/k8sclusterreceiver/internal/clusterresourcequota/testdata/expected.yaml new file mode 100644 index 000000000000..0da1aaad3c00 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/testdata/expected.yaml @@ -0,0 +1,151 @@ +resourceMetrics: + - resource: + attributes: + - key: openshift.clusterquota.name + value: + stringValue: test-clusterquota-1-uid + - key: openshift.clusterquota.uid + value: + stringValue: test-clusterquota-1 + - key: opencensus.resourcetype + value: + stringValue: requests.cpu + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: The configured upper limit for a particular resource. + gauge: + dataPoints: + - asInt: "10000" + name: openshift.clusterquota.limit + unit: "1" + scope: + name: otelcol/k8sclusterreceiver + version: latest + - resource: + attributes: + - key: openshift.clusterquota.name + value: + stringValue: test-clusterquota-1-uid + - key: openshift.clusterquota.uid + value: + stringValue: test-clusterquota-1 + - key: opencensus.resourcetype + value: + stringValue: requests.cpu + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: The usage for a particular resource with a configured limit. + gauge: + dataPoints: + - asInt: "6000" + name: openshift.clusterquota.used + unit: "1" + scope: + name: otelcol/k8sclusterreceiver + version: latest + - resource: + attributes: + - key: openshift.clusterquota.name + value: + stringValue: test-clusterquota-1-uid + - key: openshift.clusterquota.uid + value: + stringValue: test-clusterquota-1 + - key: opencensus.resourcetype + value: + stringValue: requests.cpu + - key: k8s.namespace.name + value: + stringValue: ns1 + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: The upper limit for a particular resource in a specific namespace. + gauge: + dataPoints: + - asInt: "6000" + name: openshift.appliedclusterquota.limit + unit: "1" + scope: + name: otelcol/k8sclusterreceiver + version: latest + - resource: + attributes: + - key: openshift.clusterquota.name + value: + stringValue: test-clusterquota-1-uid + - key: openshift.clusterquota.uid + value: + stringValue: test-clusterquota-1 + - key: opencensus.resourcetype + value: + stringValue: requests.cpu + - key: k8s.namespace.name + value: + stringValue: ns1 + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: The usage for a particular resource in a specific namespace. + gauge: + dataPoints: + - asInt: "6000" + name: openshift.appliedclusterquota.used + unit: "1" + scope: + name: otelcol/k8sclusterreceiver + version: latest + - resource: + attributes: + - key: openshift.clusterquota.name + value: + stringValue: test-clusterquota-1-uid + - key: openshift.clusterquota.uid + value: + stringValue: test-clusterquota-1 + - key: opencensus.resourcetype + value: + stringValue: requests.cpu + - key: k8s.namespace.name + value: + stringValue: ns2 + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: The upper limit for a particular resource in a specific namespace. + gauge: + dataPoints: + - asInt: "4000" + name: openshift.appliedclusterquota.limit + unit: "1" + scope: + name: otelcol/k8sclusterreceiver + version: latest + - resource: + attributes: + - key: openshift.clusterquota.name + value: + stringValue: test-clusterquota-1-uid + - key: openshift.clusterquota.uid + value: + stringValue: test-clusterquota-1 + - key: opencensus.resourcetype + value: + stringValue: requests.cpu + - key: k8s.namespace.name + value: + stringValue: ns2 + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: The usage for a particular resource in a specific namespace. + gauge: + dataPoints: + - asInt: "4000" + name: openshift.appliedclusterquota.used + unit: "1" + scope: + name: otelcol/k8sclusterreceiver + version: latest \ No newline at end of file diff --git a/receiver/k8sclusterreceiver/internal/collection/collector.go b/receiver/k8sclusterreceiver/internal/collection/collector.go index 008d25bfb73b..c15455d15b89 100644 --- a/receiver/k8sclusterreceiver/internal/collection/collector.go +++ b/receiver/k8sclusterreceiver/internal/collection/collector.go @@ -132,7 +132,7 @@ func (dc *DataCollector) SyncMetrics(obj interface{}) { case *autoscalingv2beta2.HorizontalPodAutoscaler: md = hpa.GetMetricsBeta(dc.settings, o) case *quotav1.ClusterResourceQuota: - md = ocsToMetrics(clusterresourcequota.GetMetrics(o)) + md = clusterresourcequota.GetMetrics(dc.settings, o) default: return } diff --git a/receiver/k8sclusterreceiver/internal/testutils/objects.go b/receiver/k8sclusterreceiver/internal/testutils/objects.go index 17c33651a48f..f8f67d8a0eda 100644 --- a/receiver/k8sclusterreceiver/internal/testutils/objects.go +++ b/receiver/k8sclusterreceiver/internal/testutils/objects.go @@ -4,6 +4,7 @@ package testutils // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/testutils" import ( + quotav1 "github.com/openshift/api/quota/v1" appsv1 "k8s.io/api/apps/v1" autoscalingv2 "k8s.io/api/autoscaling/v2" autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2" @@ -77,6 +78,50 @@ func NewJob(id string) *batchv1.Job { } } +func NewClusterResourceQuota(id string) *quotav1.ClusterResourceQuota { + return "av1.ClusterResourceQuota{ + ObjectMeta: v1.ObjectMeta{ + Name: "test-clusterquota-" + id, + Namespace: "test-namespace", + UID: types.UID("test-clusterquota-" + id + "-uid"), + }, + Status: quotav1.ClusterResourceQuotaStatus{ + Total: corev1.ResourceQuotaStatus{ + Hard: corev1.ResourceList{ + "requests.cpu": *resource.NewQuantity(10, resource.DecimalSI), + }, + Used: corev1.ResourceList{ + "requests.cpu": *resource.NewQuantity(6, resource.DecimalSI), + }, + }, + Namespaces: quotav1.ResourceQuotasStatusByNamespace{ + { + Namespace: "ns1", + Status: corev1.ResourceQuotaStatus{ + Hard: corev1.ResourceList{ + "requests.cpu": *resource.NewQuantity(6, resource.DecimalSI), + }, + Used: corev1.ResourceList{ + "requests.cpu": *resource.NewQuantity(1, resource.DecimalSI), + }, + }, + }, + { + Namespace: "ns2", + Status: corev1.ResourceQuotaStatus{ + Hard: corev1.ResourceList{ + "requests.cpu": *resource.NewQuantity(4, resource.DecimalSI), + }, + Used: corev1.ResourceList{ + "requests.cpu": *resource.NewQuantity(5, resource.DecimalSI), + }, + }, + }, + }, + }, + } +} + func NewDaemonset(id string) *appsv1.DaemonSet { return &appsv1.DaemonSet{ ObjectMeta: v1.ObjectMeta{ From 7255c3f6720dc6542d90149cd1d1d85a2f3ae354 Mon Sep 17 00:00:00 2001 From: Curtis Robert Date: Thu, 20 Jul 2023 11:09:29 -0700 Subject: [PATCH 2/8] Run make generate --- .../internal/metadata/generated_metrics_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go index d29122aeb09a..36d001b7b16c 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go @@ -70,7 +70,7 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordOpenshiftClusterquotaUsedDataPoint(ts, 1) - metrics := mb.Emit(WithK8sNamespaceName("attr-val"), WithOpencensusResourcetype("attr-val"), WithOpenshiftClusterquotaName("attr-val"), WithOpenshiftClusterquotaUID("attr-val")) + metrics := mb.Emit(WithK8sNamespaceName("k8s.namespace.name-val"), WithOpencensusResourcetype("opencensus.resourcetype-val"), WithOpenshiftClusterquotaName("openshift.clusterquota.name-val"), WithOpenshiftClusterquotaUID("openshift.clusterquota.uid-val")) if test.configSet == testSetNone { assert.Equal(t, 0, metrics.ResourceMetrics().Len()) @@ -86,28 +86,28 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, mb.resourceAttributesConfig.K8sNamespaceName.Enabled, ok) if mb.resourceAttributesConfig.K8sNamespaceName.Enabled { enabledAttrCount++ - assert.EqualValues(t, "attr-val", attrVal.Str()) + assert.EqualValues(t, "k8s.namespace.name-val", attrVal.Str()) } attrVal, ok = rm.Resource().Attributes().Get("opencensus.resourcetype") attrCount++ assert.Equal(t, mb.resourceAttributesConfig.OpencensusResourcetype.Enabled, ok) if mb.resourceAttributesConfig.OpencensusResourcetype.Enabled { enabledAttrCount++ - assert.EqualValues(t, "attr-val", attrVal.Str()) + assert.EqualValues(t, "opencensus.resourcetype-val", attrVal.Str()) } attrVal, ok = rm.Resource().Attributes().Get("openshift.clusterquota.name") attrCount++ assert.Equal(t, mb.resourceAttributesConfig.OpenshiftClusterquotaName.Enabled, ok) if mb.resourceAttributesConfig.OpenshiftClusterquotaName.Enabled { enabledAttrCount++ - assert.EqualValues(t, "attr-val", attrVal.Str()) + assert.EqualValues(t, "openshift.clusterquota.name-val", attrVal.Str()) } attrVal, ok = rm.Resource().Attributes().Get("openshift.clusterquota.uid") attrCount++ assert.Equal(t, mb.resourceAttributesConfig.OpenshiftClusterquotaUID.Enabled, ok) if mb.resourceAttributesConfig.OpenshiftClusterquotaUID.Enabled { enabledAttrCount++ - assert.EqualValues(t, "attr-val", attrVal.Str()) + assert.EqualValues(t, "openshift.clusterquota.uid-val", attrVal.Str()) } assert.Equal(t, enabledAttrCount, rm.Resource().Attributes().Len()) assert.Equal(t, attrCount, 4) From 1817cf4c066649c41ebd28b86552a607ba5d35b5 Mon Sep 17 00:00:00 2001 From: Curtis Robert Date: Fri, 21 Jul 2023 14:34:03 -0700 Subject: [PATCH 3/8] Make changes requested - resource should be an attribute of the metric, not a resource attribute. - k8s namespace needs to also be an attribute of the metric and not a resource attribute - Remove unecessary extra method - Update test to match original format of metrics --- .../clusterresourcequotas.go | 58 ++++--- .../clusterresourcequota/documentation.md | 27 ++- .../internal/metadata/generated_config.go | 4 - .../metadata/generated_config_test.go | 2 - .../internal/metadata/generated_metrics.go | 43 ++--- .../metadata/generated_metrics_test.go | 39 +++-- .../internal/metadata/testdata/config.yaml | 4 - .../clusterresourcequota/metadata.yaml | 25 ++- .../testdata/expected.yaml | 154 +++++------------- 9 files changed, 163 insertions(+), 193 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go index be239b79f88c..61c369773c5f 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go @@ -7,56 +7,54 @@ import ( "strings" "time" + imetadataphase "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata" quotav1 "github.com/openshift/api/quota/v1" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver" - v1 "k8s.io/api/core/v1" - - imetadataphase "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata" ) func GetMetrics(set receiver.CreateSettings, crq *quotav1.ClusterResourceQuota) pmetric.Metrics { mbphase := imetadataphase.NewMetricsBuilder(imetadataphase.DefaultMetricsBuilderConfig(), set) ts := pcommon.NewTimestampFromTime(time.Now()) - emitCRQMetrics(mbphase, crq, crq.Status.Total.Hard, "openshift.clusterquota.limit", ts, "") - emitCRQMetrics(mbphase, crq, crq.Status.Total.Used, "openshift.clusterquota.used", ts, "") - - for _, ns := range crq.Status.Namespaces { - emitCRQMetrics(mbphase, crq, ns.Status.Hard, "openshift.appliedclusterquota.limit", ts, ns.Namespace) - emitCRQMetrics(mbphase, crq, ns.Status.Hard, "openshift.appliedclusterquota.used", ts, ns.Namespace) + for k, v := range crq.Status.Total.Hard { + val := v.Value() + if strings.HasSuffix(string(k), ".cpu") { + val = v.MilliValue() + } + mbphase.RecordOpenshiftClusterquotaLimitDataPoint(ts, val, string(k)) } - return mbphase.Emit() -} - -func emitCRQMetrics(mbphase *imetadataphase.MetricsBuilder, crq *quotav1.ClusterResourceQuota, rl v1.ResourceList, metricName string, ts pcommon.Timestamp, namespace string) { - for k, v := range rl { + for k, v := range crq.Status.Total.Used { val := v.Value() if strings.HasSuffix(string(k), ".cpu") { val = v.MilliValue() } - switch metricName { - case "openshift.clusterquota.limit": - mbphase.RecordOpenshiftClusterquotaLimitDataPoint(ts, val) - case "openshift.clusterquota.used": - mbphase.RecordOpenshiftClusterquotaUsedDataPoint(ts, val) - case "openshift.appliedclusterquota.limit": - mbphase.RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts, val) - case "openshift.appliedclusterquota.used": - mbphase.RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts, val) - default: - return - } + mbphase.RecordOpenshiftClusterquotaUsedDataPoint(ts, val, string(k)) + } - if namespace != "" { - mbphase.EmitForResource(imetadataphase.WithK8sNamespaceName(namespace), imetadataphase.WithOpenshiftClusterquotaUID(crq.Name), imetadataphase.WithOpenshiftClusterquotaName(string(crq.UID)), imetadataphase.WithOpencensusResourcetype(string(k))) - } else { - mbphase.EmitForResource(imetadataphase.WithOpenshiftClusterquotaUID(crq.Name), imetadataphase.WithOpenshiftClusterquotaName(string(crq.UID)), imetadataphase.WithOpencensusResourcetype(string(k))) + for _, ns := range crq.Status.Namespaces { + for k, v := range ns.Status.Hard { + val := v.Value() + if strings.HasSuffix(string(k), ".cpu") { + val = v.MilliValue() + } + + mbphase.RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts, val, ns.Namespace, string(k)) } + for k, v := range ns.Status.Used { + val := v.Value() + if strings.HasSuffix(string(k), ".cpu") { + val = v.MilliValue() + } + + mbphase.RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts, val, ns.Namespace, string(k)) + } } + + return mbphase.Emit(imetadataphase.WithOpenshiftClusterquotaName(crq.Name), imetadataphase.WithOpenshiftClusterquotaUID(string(crq.UID)), imetadataphase.WithOpencensusResourcetype("k8s")) } diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/documentation.md b/receiver/k8sclusterreceiver/internal/clusterresourcequota/documentation.md index e3aec0b5a901..cc54ab308380 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/documentation.md +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/documentation.md @@ -22,6 +22,13 @@ The upper limit for a particular resource in a specific namespace. | ---- | ----------- | ---------- | | 1 | Gauge | Int | +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| k8s.namespace.name | The k8s namespace name. | Any Str | +| resource | The name of the resource on which the cluster quota is applied | Any Str | + ### openshift.appliedclusterquota.used The usage for a particular resource in a specific namespace. @@ -30,6 +37,13 @@ The usage for a particular resource in a specific namespace. | ---- | ----------- | ---------- | | 1 | Gauge | Int | +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| k8s.namespace.name | The k8s namespace name. | Any Str | +| resource | The name of the resource on which the cluster quota is applied | Any Str | + ### openshift.clusterquota.limit The configured upper limit for a particular resource. @@ -38,6 +52,12 @@ The configured upper limit for a particular resource. | ---- | ----------- | ---------- | | 1 | Gauge | Int | +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| resource | The name of the resource on which the cluster quota is applied | Any Str | + ### openshift.clusterquota.used The usage for a particular resource with a configured limit. @@ -46,11 +66,16 @@ The usage for a particular resource with a configured limit. | ---- | ----------- | ---------- | | 1 | Gauge | Int | +#### Attributes + +| Name | Description | Values | +| ---- | ----------- | ------ | +| resource | The name of the resource on which the cluster quota is applied | Any Str | + ## Resource Attributes | Name | Description | Values | Enabled | | ---- | ----------- | ------ | ------- | -| k8s.namespace.name | The k8s namespace name. | Any Str | true | | opencensus.resourcetype | The OpenCensus resource type. | Any Str | true | | openshift.clusterquota.name | The k8s ClusterResourceQuota name. | Any Str | true | | openshift.clusterquota.uid | The k8s ClusterResourceQuota uid. | Any Str | true | diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config.go index d2b789229d51..cee0b047101b 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config.go @@ -55,7 +55,6 @@ type ResourceAttributeConfig struct { // ResourceAttributesConfig provides config for k8s/clusterresourcequota resource attributes. type ResourceAttributesConfig struct { - K8sNamespaceName ResourceAttributeConfig `mapstructure:"k8s.namespace.name"` OpencensusResourcetype ResourceAttributeConfig `mapstructure:"opencensus.resourcetype"` OpenshiftClusterquotaName ResourceAttributeConfig `mapstructure:"openshift.clusterquota.name"` OpenshiftClusterquotaUID ResourceAttributeConfig `mapstructure:"openshift.clusterquota.uid"` @@ -63,9 +62,6 @@ type ResourceAttributesConfig struct { func DefaultResourceAttributesConfig() ResourceAttributesConfig { return ResourceAttributesConfig{ - K8sNamespaceName: ResourceAttributeConfig{ - Enabled: true, - }, OpencensusResourcetype: ResourceAttributeConfig{ Enabled: true, }, diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config_test.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config_test.go index 36a97143d165..5758ce83ebc8 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config_test.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_config_test.go @@ -32,7 +32,6 @@ func TestMetricsBuilderConfig(t *testing.T) { OpenshiftClusterquotaUsed: MetricConfig{Enabled: true}, }, ResourceAttributes: ResourceAttributesConfig{ - K8sNamespaceName: ResourceAttributeConfig{Enabled: true}, OpencensusResourcetype: ResourceAttributeConfig{Enabled: true}, OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: true}, OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: true}, @@ -49,7 +48,6 @@ func TestMetricsBuilderConfig(t *testing.T) { OpenshiftClusterquotaUsed: MetricConfig{Enabled: false}, }, ResourceAttributes: ResourceAttributesConfig{ - K8sNamespaceName: ResourceAttributeConfig{Enabled: false}, OpencensusResourcetype: ResourceAttributeConfig{Enabled: false}, OpenshiftClusterquotaName: ResourceAttributeConfig{Enabled: false}, OpenshiftClusterquotaUID: ResourceAttributeConfig{Enabled: false}, diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics.go index 14fd8686bc78..08e3431d9572 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics.go @@ -24,9 +24,10 @@ func (m *metricOpenshiftAppliedclusterquotaLimit) init() { m.data.SetDescription("The upper limit for a particular resource in a specific namespace.") m.data.SetUnit("1") m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricOpenshiftAppliedclusterquotaLimit) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { +func (m *metricOpenshiftAppliedclusterquotaLimit) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, k8sNamespaceNameAttributeValue string, resourceAttributeValue string) { if !m.config.Enabled { return } @@ -34,6 +35,8 @@ func (m *metricOpenshiftAppliedclusterquotaLimit) recordDataPoint(start pcommon. dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) + dp.Attributes().PutStr("k8s.namespace.name", k8sNamespaceNameAttributeValue) + dp.Attributes().PutStr("resource", resourceAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -73,9 +76,10 @@ func (m *metricOpenshiftAppliedclusterquotaUsed) init() { m.data.SetDescription("The usage for a particular resource in a specific namespace.") m.data.SetUnit("1") m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricOpenshiftAppliedclusterquotaUsed) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { +func (m *metricOpenshiftAppliedclusterquotaUsed) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, k8sNamespaceNameAttributeValue string, resourceAttributeValue string) { if !m.config.Enabled { return } @@ -83,6 +87,8 @@ func (m *metricOpenshiftAppliedclusterquotaUsed) recordDataPoint(start pcommon.T dp.SetStartTimestamp(start) dp.SetTimestamp(ts) dp.SetIntValue(val) + dp.Attributes().PutStr("k8s.namespace.name", k8sNamespaceNameAttributeValue) + dp.Attributes().PutStr("resource", resourceAttributeValue) } // updateCapacity saves max length of data point slices that will be used for the slice capacity. @@ -122,9 +128,10 @@ func (m *metricOpenshiftClusterquotaLimit) init() { m.data.SetDescription("The configured upper limit for a particular resource.") m.data.SetUnit("1") m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricOpenshiftClusterquotaLimit) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { +func (m *metricOpenshiftClusterquotaLimit) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, resourceAttributeValue string) { if !m.config.Enabled { return } @@ -132,6 +139,7 @@ func (m *metricOpenshiftClusterquotaLimit) recordDataPoint(start pcommon.Timesta 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. @@ -171,9 +179,10 @@ func (m *metricOpenshiftClusterquotaUsed) init() { m.data.SetDescription("The usage for a particular resource with a configured limit.") m.data.SetUnit("1") m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) } -func (m *metricOpenshiftClusterquotaUsed) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { +func (m *metricOpenshiftClusterquotaUsed) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, resourceAttributeValue string) { if !m.config.Enabled { return } @@ -181,6 +190,7 @@ func (m *metricOpenshiftClusterquotaUsed) recordDataPoint(start pcommon.Timestam 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. @@ -263,15 +273,6 @@ func (mb *MetricsBuilder) updateCapacity(rm pmetric.ResourceMetrics) { // ResourceMetricsOption applies changes to provided resource metrics. type ResourceMetricsOption func(ResourceAttributesConfig, pmetric.ResourceMetrics) -// WithK8sNamespaceName sets provided value as "k8s.namespace.name" attribute for current resource. -func WithK8sNamespaceName(val string) ResourceMetricsOption { - return func(rac ResourceAttributesConfig, rm pmetric.ResourceMetrics) { - if rac.K8sNamespaceName.Enabled { - rm.Resource().Attributes().PutStr("k8s.namespace.name", val) - } - } -} - // WithOpencensusResourcetype sets provided value as "opencensus.resourcetype" attribute for current resource. func WithOpencensusResourcetype(val string) ResourceMetricsOption { return func(rac ResourceAttributesConfig, rm pmetric.ResourceMetrics) { @@ -357,23 +358,23 @@ func (mb *MetricsBuilder) Emit(rmo ...ResourceMetricsOption) pmetric.Metrics { } // RecordOpenshiftAppliedclusterquotaLimitDataPoint adds a data point to openshift.appliedclusterquota.limit metric. -func (mb *MetricsBuilder) RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts pcommon.Timestamp, val int64) { - mb.metricOpenshiftAppliedclusterquotaLimit.recordDataPoint(mb.startTime, ts, val) +func (mb *MetricsBuilder) RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts pcommon.Timestamp, val int64, k8sNamespaceNameAttributeValue string, resourceAttributeValue string) { + mb.metricOpenshiftAppliedclusterquotaLimit.recordDataPoint(mb.startTime, ts, val, k8sNamespaceNameAttributeValue, resourceAttributeValue) } // RecordOpenshiftAppliedclusterquotaUsedDataPoint adds a data point to openshift.appliedclusterquota.used metric. -func (mb *MetricsBuilder) RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts pcommon.Timestamp, val int64) { - mb.metricOpenshiftAppliedclusterquotaUsed.recordDataPoint(mb.startTime, ts, val) +func (mb *MetricsBuilder) RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts pcommon.Timestamp, val int64, k8sNamespaceNameAttributeValue string, resourceAttributeValue string) { + mb.metricOpenshiftAppliedclusterquotaUsed.recordDataPoint(mb.startTime, ts, val, k8sNamespaceNameAttributeValue, resourceAttributeValue) } // RecordOpenshiftClusterquotaLimitDataPoint adds a data point to openshift.clusterquota.limit metric. -func (mb *MetricsBuilder) RecordOpenshiftClusterquotaLimitDataPoint(ts pcommon.Timestamp, val int64) { - mb.metricOpenshiftClusterquotaLimit.recordDataPoint(mb.startTime, ts, val) +func (mb *MetricsBuilder) RecordOpenshiftClusterquotaLimitDataPoint(ts pcommon.Timestamp, val int64, resourceAttributeValue string) { + mb.metricOpenshiftClusterquotaLimit.recordDataPoint(mb.startTime, ts, val, resourceAttributeValue) } // RecordOpenshiftClusterquotaUsedDataPoint adds a data point to openshift.clusterquota.used metric. -func (mb *MetricsBuilder) RecordOpenshiftClusterquotaUsedDataPoint(ts pcommon.Timestamp, val int64) { - mb.metricOpenshiftClusterquotaUsed.recordDataPoint(mb.startTime, ts, val) +func (mb *MetricsBuilder) RecordOpenshiftClusterquotaUsedDataPoint(ts pcommon.Timestamp, val int64, resourceAttributeValue string) { + mb.metricOpenshiftClusterquotaUsed.recordDataPoint(mb.startTime, ts, val, resourceAttributeValue) } // Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go index 36d001b7b16c..88afbd526cc3 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/generated_metrics_test.go @@ -56,21 +56,21 @@ func TestMetricsBuilder(t *testing.T) { defaultMetricsCount++ allMetricsCount++ - mb.RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts, 1) + mb.RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts, 1, "k8s.namespace.name-val", "resource-val") defaultMetricsCount++ allMetricsCount++ - mb.RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts, 1) + mb.RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts, 1, "k8s.namespace.name-val", "resource-val") defaultMetricsCount++ allMetricsCount++ - mb.RecordOpenshiftClusterquotaLimitDataPoint(ts, 1) + mb.RecordOpenshiftClusterquotaLimitDataPoint(ts, 1, "resource-val") defaultMetricsCount++ allMetricsCount++ - mb.RecordOpenshiftClusterquotaUsedDataPoint(ts, 1) + mb.RecordOpenshiftClusterquotaUsedDataPoint(ts, 1, "resource-val") - metrics := mb.Emit(WithK8sNamespaceName("k8s.namespace.name-val"), WithOpencensusResourcetype("opencensus.resourcetype-val"), WithOpenshiftClusterquotaName("openshift.clusterquota.name-val"), WithOpenshiftClusterquotaUID("openshift.clusterquota.uid-val")) + metrics := mb.Emit(WithOpencensusResourcetype("opencensus.resourcetype-val"), WithOpenshiftClusterquotaName("openshift.clusterquota.name-val"), WithOpenshiftClusterquotaUID("openshift.clusterquota.uid-val")) if test.configSet == testSetNone { assert.Equal(t, 0, metrics.ResourceMetrics().Len()) @@ -81,14 +81,7 @@ func TestMetricsBuilder(t *testing.T) { rm := metrics.ResourceMetrics().At(0) attrCount := 0 enabledAttrCount := 0 - attrVal, ok := rm.Resource().Attributes().Get("k8s.namespace.name") - attrCount++ - assert.Equal(t, mb.resourceAttributesConfig.K8sNamespaceName.Enabled, ok) - if mb.resourceAttributesConfig.K8sNamespaceName.Enabled { - enabledAttrCount++ - assert.EqualValues(t, "k8s.namespace.name-val", attrVal.Str()) - } - attrVal, ok = rm.Resource().Attributes().Get("opencensus.resourcetype") + attrVal, ok := rm.Resource().Attributes().Get("opencensus.resourcetype") attrCount++ assert.Equal(t, mb.resourceAttributesConfig.OpencensusResourcetype.Enabled, ok) if mb.resourceAttributesConfig.OpencensusResourcetype.Enabled { @@ -110,7 +103,7 @@ func TestMetricsBuilder(t *testing.T) { assert.EqualValues(t, "openshift.clusterquota.uid-val", attrVal.Str()) } assert.Equal(t, enabledAttrCount, rm.Resource().Attributes().Len()) - assert.Equal(t, attrCount, 4) + assert.Equal(t, attrCount, 3) assert.Equal(t, 1, rm.ScopeMetrics().Len()) ms := rm.ScopeMetrics().At(0).Metrics() @@ -135,6 +128,12 @@ 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()) + attrVal, ok := dp.Attributes().Get("k8s.namespace.name") + assert.True(t, ok) + assert.EqualValues(t, "k8s.namespace.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("resource") + assert.True(t, ok) + assert.EqualValues(t, "resource-val", attrVal.Str()) case "openshift.appliedclusterquota.used": assert.False(t, validatedMetrics["openshift.appliedclusterquota.used"], "Found a duplicate in the metrics slice: openshift.appliedclusterquota.used") validatedMetrics["openshift.appliedclusterquota.used"] = true @@ -147,6 +146,12 @@ 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()) + attrVal, ok := dp.Attributes().Get("k8s.namespace.name") + assert.True(t, ok) + assert.EqualValues(t, "k8s.namespace.name-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("resource") + assert.True(t, ok) + assert.EqualValues(t, "resource-val", attrVal.Str()) case "openshift.clusterquota.limit": assert.False(t, validatedMetrics["openshift.clusterquota.limit"], "Found a duplicate in the metrics slice: openshift.clusterquota.limit") validatedMetrics["openshift.clusterquota.limit"] = true @@ -159,6 +164,9 @@ 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()) + attrVal, ok := dp.Attributes().Get("resource") + assert.True(t, ok) + assert.EqualValues(t, "resource-val", attrVal.Str()) case "openshift.clusterquota.used": assert.False(t, validatedMetrics["openshift.clusterquota.used"], "Found a duplicate in the metrics slice: openshift.clusterquota.used") validatedMetrics["openshift.clusterquota.used"] = true @@ -171,6 +179,9 @@ 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()) + attrVal, ok := dp.Attributes().Get("resource") + assert.True(t, ok) + assert.EqualValues(t, "resource-val", attrVal.Str()) } } }) diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/testdata/config.yaml b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/testdata/config.yaml index 7415ae508568..385232b2f6a2 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/testdata/config.yaml +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata/testdata/config.yaml @@ -10,8 +10,6 @@ all_set: openshift.clusterquota.used: enabled: true resource_attributes: - k8s.namespace.name: - enabled: true opencensus.resourcetype: enabled: true openshift.clusterquota.name: @@ -29,8 +27,6 @@ none_set: openshift.clusterquota.used: enabled: false resource_attributes: - k8s.namespace.name: - enabled: false opencensus.resourcetype: enabled: false openshift.clusterquota.name: diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/metadata.yaml b/receiver/k8sclusterreceiver/internal/clusterresourcequota/metadata.yaml index d647708fda05..10cd59e40044 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/metadata.yaml +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/metadata.yaml @@ -5,11 +5,6 @@ sem_conv_version: 1.18.0 parent: k8s_cluster resource_attributes: - k8s.namespace.name: - description: The k8s namespace name. - type: string - enabled: true - openshift.clusterquota.uid: description: The k8s ClusterResourceQuota uid. type: string @@ -25,6 +20,16 @@ resource_attributes: type: string enabled: true +attributes: + k8s.namespace.name: + description: The k8s namespace name. + type: string + enabled: true + resource: + description: The name of the resource on which the cluster quota is applied + type: string + enabled: true + metrics: openshift.clusterquota.limit: enabled: true @@ -32,21 +37,31 @@ metrics: unit: "1" gauge: value_type: int + attributes: + - resource openshift.clusterquota.used: enabled: true description: The usage for a particular resource with a configured limit. unit: "1" gauge: value_type: int + attributes: + - resource openshift.appliedclusterquota.limit: enabled: true description: The upper limit for a particular resource in a specific namespace. unit: "1" gauge: value_type: int + attributes: + - k8s.namespace.name + - resource openshift.appliedclusterquota.used: enabled: true description: The usage for a particular resource in a specific namespace. unit: "1" gauge: value_type: int + attributes: + - k8s.namespace.name + - resource diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/testdata/expected.yaml b/receiver/k8sclusterreceiver/internal/clusterresourcequota/testdata/expected.yaml index 0da1aaad3c00..1fdcd0580e33 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/testdata/expected.yaml +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/testdata/expected.yaml @@ -3,13 +3,13 @@ resourceMetrics: attributes: - key: openshift.clusterquota.name value: - stringValue: test-clusterquota-1-uid + stringValue: test-clusterquota-1 - key: openshift.clusterquota.uid value: - stringValue: test-clusterquota-1 + stringValue: test-clusterquota-1-uid - key: opencensus.resourcetype value: - stringValue: requests.cpu + stringValue: k8s schemaUrl: https://opentelemetry.io/schemas/1.18.0 scopeMetrics: - metrics: @@ -17,133 +17,63 @@ resourceMetrics: gauge: dataPoints: - asInt: "10000" + attributes: + - key: resource + value: + stringValue: requests.cpu name: openshift.clusterquota.limit unit: "1" - scope: - name: otelcol/k8sclusterreceiver - version: latest - - resource: - attributes: - - key: openshift.clusterquota.name - value: - stringValue: test-clusterquota-1-uid - - key: openshift.clusterquota.uid - value: - stringValue: test-clusterquota-1 - - key: opencensus.resourcetype - value: - stringValue: requests.cpu - schemaUrl: https://opentelemetry.io/schemas/1.18.0 - scopeMetrics: - - metrics: - description: The usage for a particular resource with a configured limit. gauge: dataPoints: - asInt: "6000" + attributes: + - key: resource + value: + stringValue: requests.cpu name: openshift.clusterquota.used unit: "1" - scope: - name: otelcol/k8sclusterreceiver - version: latest - - resource: - attributes: - - key: openshift.clusterquota.name - value: - stringValue: test-clusterquota-1-uid - - key: openshift.clusterquota.uid - value: - stringValue: test-clusterquota-1 - - key: opencensus.resourcetype - value: - stringValue: requests.cpu - - key: k8s.namespace.name - value: - stringValue: ns1 - schemaUrl: https://opentelemetry.io/schemas/1.18.0 - scopeMetrics: - - metrics: - description: The upper limit for a particular resource in a specific namespace. gauge: dataPoints: - asInt: "6000" - name: openshift.appliedclusterquota.limit - unit: "1" - scope: - name: otelcol/k8sclusterreceiver - version: latest - - resource: - attributes: - - key: openshift.clusterquota.name - value: - stringValue: test-clusterquota-1-uid - - key: openshift.clusterquota.uid - value: - stringValue: test-clusterquota-1 - - key: opencensus.resourcetype - value: - stringValue: requests.cpu - - key: k8s.namespace.name - value: - stringValue: ns1 - schemaUrl: https://opentelemetry.io/schemas/1.18.0 - scopeMetrics: - - metrics: - - description: The usage for a particular resource in a specific namespace. - gauge: - dataPoints: - - asInt: "6000" - name: openshift.appliedclusterquota.used - unit: "1" - scope: - name: otelcol/k8sclusterreceiver - version: latest - - resource: - attributes: - - key: openshift.clusterquota.name - value: - stringValue: test-clusterquota-1-uid - - key: openshift.clusterquota.uid - value: - stringValue: test-clusterquota-1 - - key: opencensus.resourcetype - value: - stringValue: requests.cpu - - key: k8s.namespace.name - value: - stringValue: ns2 - schemaUrl: https://opentelemetry.io/schemas/1.18.0 - scopeMetrics: - - metrics: - - description: The upper limit for a particular resource in a specific namespace. - gauge: - dataPoints: + attributes: + - key: resource + value: + stringValue: requests.cpu + - key: k8s.namespace.name + value: + stringValue: "ns1" - asInt: "4000" + attributes: + - key: resource + value: + stringValue: requests.cpu + - key: k8s.namespace.name + value: + stringValue: "ns2" name: openshift.appliedclusterquota.limit unit: "1" - scope: - name: otelcol/k8sclusterreceiver - version: latest - - resource: - attributes: - - key: openshift.clusterquota.name - value: - stringValue: test-clusterquota-1-uid - - key: openshift.clusterquota.uid - value: - stringValue: test-clusterquota-1 - - key: opencensus.resourcetype - value: - stringValue: requests.cpu - - key: k8s.namespace.name - value: - stringValue: ns2 - schemaUrl: https://opentelemetry.io/schemas/1.18.0 - scopeMetrics: - - metrics: - description: The usage for a particular resource in a specific namespace. gauge: dataPoints: - - asInt: "4000" + - asInt: "1000" + attributes: + - key: resource + value: + stringValue: requests.cpu + - key: k8s.namespace.name + value: + stringValue: "ns1" + - asInt: "5000" + attributes: + - key: resource + value: + stringValue: requests.cpu + - key: + k8s.namespace.name + value: + stringValue: "ns2" name: openshift.appliedclusterquota.used unit: "1" scope: From 5a06b93401aa8c705a4ce3901f34182b6f7e4112 Mon Sep 17 00:00:00 2001 From: Curtis Robert Date: Fri, 21 Jul 2023 14:48:51 -0700 Subject: [PATCH 4/8] Fix linting failure --- .../internal/clusterresourcequota/clusterresourcequotas.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go index 61c369773c5f..0c50e19393d9 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go @@ -7,11 +7,12 @@ import ( "strings" "time" - imetadataphase "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata" quotav1 "github.com/openshift/api/quota/v1" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver" + + imetadataphase "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata" ) func GetMetrics(set receiver.CreateSettings, crq *quotav1.ClusterResourceQuota) pmetric.Metrics { From 221c08c90bb06f27536e3c14be55dd177dc85d12 Mon Sep 17 00:00:00 2001 From: Curtis Robert Date: Fri, 21 Jul 2023 15:05:32 -0700 Subject: [PATCH 5/8] Move re-used functionality into separate method --- .../clusterresourcequotas.go | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go index 0c50e19393d9..948e5b51126d 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go @@ -4,6 +4,8 @@ package clusterresourcequota // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota" import ( + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" "strings" "time" @@ -20,42 +22,34 @@ func GetMetrics(set receiver.CreateSettings, crq *quotav1.ClusterResourceQuota) ts := pcommon.NewTimestampFromTime(time.Now()) for k, v := range crq.Status.Total.Hard { - val := v.Value() - if strings.HasSuffix(string(k), ".cpu") { - val = v.MilliValue() - } - + val := extractValue(k, v) mbphase.RecordOpenshiftClusterquotaLimitDataPoint(ts, val, string(k)) } for k, v := range crq.Status.Total.Used { - val := v.Value() - if strings.HasSuffix(string(k), ".cpu") { - val = v.MilliValue() - } - + val := extractValue(k, v) mbphase.RecordOpenshiftClusterquotaUsedDataPoint(ts, val, string(k)) } for _, ns := range crq.Status.Namespaces { for k, v := range ns.Status.Hard { - val := v.Value() - if strings.HasSuffix(string(k), ".cpu") { - val = v.MilliValue() - } - + val := extractValue(k, v) mbphase.RecordOpenshiftAppliedclusterquotaLimitDataPoint(ts, val, ns.Namespace, string(k)) } for k, v := range ns.Status.Used { - val := v.Value() - if strings.HasSuffix(string(k), ".cpu") { - val = v.MilliValue() - } - + val := extractValue(k, v) mbphase.RecordOpenshiftAppliedclusterquotaUsedDataPoint(ts, val, ns.Namespace, string(k)) } } return mbphase.Emit(imetadataphase.WithOpenshiftClusterquotaName(crq.Name), imetadataphase.WithOpenshiftClusterquotaUID(string(crq.UID)), imetadataphase.WithOpencensusResourcetype("k8s")) } + +func extractValue(k v1.ResourceName, v resource.Quantity) int64 { + val := v.Value() + if strings.HasSuffix(string(k), ".cpu") { + val = v.MilliValue() + } + return val +} From a5bc284c56ac4e6bd982f68b07be6eecbae9cc1a Mon Sep 17 00:00:00 2001 From: Curtis Robert Date: Fri, 21 Jul 2023 15:12:04 -0700 Subject: [PATCH 6/8] Fix linter --- .../internal/clusterresourcequota/clusterresourcequotas.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go index 948e5b51126d..1707dec8c453 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go @@ -4,11 +4,12 @@ package clusterresourcequota // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota" import ( - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" "strings" "time" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + quotav1 "github.com/openshift/api/quota/v1" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" From ae10eb00d583a40f96b16973ec9e38fb3984bafe Mon Sep 17 00:00:00 2001 From: Curtis Robert Date: Fri, 21 Jul 2023 15:55:32 -0700 Subject: [PATCH 7/8] Fix import organization --- .../internal/clusterresourcequota/clusterresourcequotas.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go index 1707dec8c453..687dac5c6cb4 100644 --- a/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go +++ b/receiver/k8sclusterreceiver/internal/clusterresourcequota/clusterresourcequotas.go @@ -7,13 +7,12 @@ import ( "strings" "time" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" - quotav1 "github.com/openshift/api/quota/v1" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" imetadataphase "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/clusterresourcequota/internal/metadata" ) From 656230c9d417b56ec113f6689b34ba6f0dbdfb8e Mon Sep 17 00:00:00 2001 From: Curtis Robert Date: Mon, 24 Jul 2023 08:39:42 -0700 Subject: [PATCH 8/8] Remove issue #10553 from CHANGELOG --- .chloggen/k8s-cluster-receiver-clusterresourcequota.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/k8s-cluster-receiver-clusterresourcequota.yaml b/.chloggen/k8s-cluster-receiver-clusterresourcequota.yaml index 9eed9d57be7d..3b7e69e75a8c 100644 --- a/.chloggen/k8s-cluster-receiver-clusterresourcequota.yaml +++ b/.chloggen/k8s-cluster-receiver-clusterresourcequota.yaml @@ -13,7 +13,7 @@ component: "k8sclusterreceiver" note: "Change k8s.clusterresourcequota metrics to use mdatagen" # Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. -issues: [10553, 4367] +issues: [4367] # (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.