From 04c4f6882979aa47987328f8f43c10db247fbcc1 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 9 Sep 2025 10:39:45 +0200 Subject: [PATCH 01/14] [receiver/k8scluster] Sync allocatable metric names with the latest semconv Signed-off-by: odubajDT --- .chloggen/k8scluster-allocatable.yaml | 27 ++++++ receiver/k8sclusterreceiver/README.md | 22 +++++ .../k8sclusterreceiver/internal/node/nodes.go | 11 +++ .../internal/node/nodes_test.go | 82 +++++++++++++++++ .../node/testdata/expected_allocatable.yaml | 87 +++++++++++++++++++ .../expected_optional_allocatable.yaml | 43 +++++++++ receiver/k8sclusterreceiver/metadata.yaml | 2 +- 7 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 .chloggen/k8scluster-allocatable.yaml create mode 100644 receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml create mode 100644 receiver/k8sclusterreceiver/internal/node/testdata/expected_optional_allocatable.yaml diff --git a/.chloggen/k8scluster-allocatable.yaml b/.chloggen/k8scluster-allocatable.yaml new file mode 100644 index 0000000000000..6cb9b00a9df76 --- /dev/null +++ b/.chloggen/k8scluster-allocatable.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: receiver/k8scluster + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Sync allocatable metric names with the latest semconv" + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [40708] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/receiver/k8sclusterreceiver/README.md b/receiver/k8sclusterreceiver/README.md index 7d01b823f41e0..7be4a10cdcca9 100644 --- a/receiver/k8sclusterreceiver/README.md +++ b/receiver/k8sclusterreceiver/README.md @@ -444,3 +444,25 @@ Add the following rules to your ClusterRole: - watch ``` +## Feature Gates + +### `k8scluster.allocatableNamespace.enabled` + +The `k8scluster.allocatableNamespace.enabled` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) enables the SemConv valid format of the node allocatable metrics reported by the receiver. +The feature gate is in `alpha` stage, which means it is disabled by default. + +If enabled (together with the `allocatable_types_to_report` config option), the SemConv valid format of the node allocatable metrics are reported: + +- `k8s.node.allocatable.cpu` +- `k8s.node.allocatable.ephemeral_storage` +- `k8s.node.allocatable.storage` +- `k8s.node.allocatable.memory` +- `k8s.node.allocatable.pods` + +instead of the original version: + +- `k8s.node.allocatable_cpu` +- `k8s.node.allocatable_ephemeral_storage` +- `k8s.node.allocatable_storage` +- `k8s.node.allocatable_memory` +- `k8s.node.allocatable_pods` diff --git a/receiver/k8sclusterreceiver/internal/node/nodes.go b/receiver/k8sclusterreceiver/internal/node/nodes.go index 41a7d2fd995a2..de86d8b14d107 100644 --- a/receiver/k8sclusterreceiver/internal/node/nodes.go +++ b/receiver/k8sclusterreceiver/internal/node/nodes.go @@ -9,6 +9,7 @@ import ( "time" "github.com/iancoleman/strcase" + "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver" @@ -27,6 +28,13 @@ const ( k8sNodeConditionPrefix = "k8s.node.condition" ) +var allowAllocatableNamespace = featuregate.GlobalRegistry().MustRegister( + "k8scluster.allocatableNamespace.enabled", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("When enabled, allocatable metrics are reported under allocatable namespace: with '.' instead of '_'"), + featuregate.WithRegisterFromVersion("v0.136.0"), +) + // Transform transforms the node to remove the fields that we don't use to reduce RAM utilization. // IMPORTANT: Make sure to update this function before using new node fields. func Transform(node *corev1.Node) *corev1.Node { @@ -218,5 +226,8 @@ func setNodeAllocatableValue(dp pmetric.NumberDataPoint, res corev1.ResourceName } func getNodeAllocatableMetric(nodeAllocatableTypeValue string) string { + if allowAllocatableNamespace.IsEnabled() { + return "k8s.node.allocatable." + strcase.ToSnake(nodeAllocatableTypeValue) + } return "k8s.node.allocatable_" + strcase.ToSnake(nodeAllocatableTypeValue) } diff --git a/receiver/k8sclusterreceiver/internal/node/nodes_test.go b/receiver/k8sclusterreceiver/internal/node/nodes_test.go index 7fb04725db08f..ce0537cf54c3b 100644 --- a/receiver/k8sclusterreceiver/internal/node/nodes_test.go +++ b/receiver/k8sclusterreceiver/internal/node/nodes_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver/receivertest" @@ -64,6 +65,49 @@ func TestNodeMetricsReportCPUMetrics(t *testing.T) { ) } +func TestNodeMetricsReportCPUMetricsAllocatableNamespace(t *testing.T) { + require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), true)) + defer func() { + require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), false)) + }() + n := testutils.NewNode("1") + rb := metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig()) + rm := CustomMetrics(receivertest.NewNopSettings(metadata.Type), rb, n, + []string{ + "Ready", + "MemoryPressure", + "DiskPressure", + "NetworkUnavailable", + "PIDPressure", + "OutOfDisk", + }, + []string{ + "cpu", + "memory", + "ephemeral-storage", + "storage", + "pods", + "hugepages-1Gi", + "hugepages-2Mi", + "not-present", + }, + pcommon.Timestamp(time.Now().UnixNano()), + ) + m := pmetric.NewMetrics() + rm.MoveTo(m.ResourceMetrics().AppendEmpty()) + + expected, err := golden.ReadMetrics(filepath.Join("testdata", "expected_allocatable.yaml")) + require.NoError(t, err) + require.NoError(t, pmetrictest.CompareMetrics(expected, m, + pmetrictest.IgnoreTimestamp(), + pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreResourceMetricsOrder(), + pmetrictest.IgnoreMetricsOrder(), + pmetrictest.IgnoreScopeMetricsOrder(), + ), + ) +} + func TestNodeOptionalMetrics(t *testing.T) { n := testutils.NewNode("2") rac := metadata.DefaultResourceAttributesConfig() @@ -98,6 +142,44 @@ func TestNodeOptionalMetrics(t *testing.T) { ) } +func TestNodeOptionalMetricsAllocatableNamespace(t *testing.T) { + require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), true)) + defer func() { + require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), false)) + }() + n := testutils.NewNode("2") + rac := metadata.DefaultResourceAttributesConfig() + rac.K8sKubeletVersion.Enabled = true + rac.ContainerRuntime.Enabled = true + rac.ContainerRuntimeVersion.Enabled = true + rac.OsType.Enabled = true + rac.OsDescription.Enabled = true + + rb := metadata.NewResourceBuilder(rac) + rm := CustomMetrics(receivertest.NewNopSettings(metadata.Type), rb, n, + []string{}, + []string{ + "cpu", + "memory", + }, + + pcommon.Timestamp(time.Now().UnixNano()), + ) + m := pmetric.NewMetrics() + rm.MoveTo(m.ResourceMetrics().AppendEmpty()) + + expected, err := golden.ReadMetrics(filepath.Join("testdata", "expected_optional_allocatable.yaml")) + require.NoError(t, err) + require.NoError(t, pmetrictest.CompareMetrics(expected, m, + pmetrictest.IgnoreTimestamp(), + pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreResourceMetricsOrder(), + pmetrictest.IgnoreMetricsOrder(), + pmetrictest.IgnoreScopeMetricsOrder(), + ), + ) +} + func TestNodeConditionValue(t *testing.T) { type args struct { node *corev1.Node diff --git a/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml b/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml new file mode 100644 index 0000000000000..a4ef9f84e6033 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml @@ -0,0 +1,87 @@ +resourceMetrics: + - resource: + attributes: + - key: k8s.node.name + value: + stringValue: test-node-1 + - key: k8s.node.uid + value: + stringValue: test-node-1-uid + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: Amount of cpu allocatable on the node + gauge: + dataPoints: + - asDouble: 0.123 + name: k8s.node.allocatable.cpu + unit: '{cpu}' + - description: Amount of ephemeral-storage allocatable on the node + gauge: + dataPoints: + - asInt: "1234" + name: k8s.node.allocatable.ephemeral_storage + unit: By + - description: Amount of memory allocatable on the node + gauge: + dataPoints: + - asInt: "456" + name: k8s.node.allocatable.memory + unit: By + - description: Amount of pods allocatable on the node + gauge: + dataPoints: + - asInt: "12" + name: k8s.node.allocatable.pods + unit: "{pod}" + - description: DiskPressure condition status of the node (true=1, false=0, unknown=-1) + gauge: + dataPoints: + - asInt: "0" + name: k8s.node.condition_disk_pressure + unit: "" + - description: MemoryPressure condition status of the node (true=1, false=0, unknown=-1) + gauge: + dataPoints: + - asInt: "0" + name: k8s.node.condition_memory_pressure + unit: "" + - description: NetworkUnavailable condition status of the node (true=1, false=0, unknown=-1) + gauge: + dataPoints: + - asInt: "0" + name: k8s.node.condition_network_unavailable + unit: "" + - description: PIDPressure condition status of the node (true=1, false=0, unknown=-1) + gauge: + dataPoints: + - asInt: "0" + name: k8s.node.condition_pid_pressure + unit: "" + - description: Ready condition status of the node (true=1, false=0, unknown=-1) + gauge: + dataPoints: + - asInt: "1" + name: k8s.node.condition_ready + unit: "" + - description: OutOfDisk condition status of the node (true=1, false=0, unknown=-1) + gauge: + dataPoints: + - asInt: "-1" + name: k8s.node.condition_out_of_disk + unit: "" + - description: Amount of hugepages-1Gi allocatable on the node + gauge: + dataPoints: + - asInt: "2" + name: k8s.node.allocatable.hugepages_1_gi + unit: "{hugepages-1Gi}" + - description: Amount of hugepages-2Mi allocatable on the node + gauge: + dataPoints: + - asInt: "2048" + name: k8s.node.allocatable.hugepages_2_mi + unit: "{hugepages-2Mi}" + scope: + name: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver + version: latest diff --git a/receiver/k8sclusterreceiver/internal/node/testdata/expected_optional_allocatable.yaml b/receiver/k8sclusterreceiver/internal/node/testdata/expected_optional_allocatable.yaml new file mode 100644 index 0000000000000..28518d3999bb0 --- /dev/null +++ b/receiver/k8sclusterreceiver/internal/node/testdata/expected_optional_allocatable.yaml @@ -0,0 +1,43 @@ +resourceMetrics: + - resource: + attributes: + - key: k8s.node.name + value: + stringValue: test-node-2 + - key: k8s.node.uid + value: + stringValue: test-node-2-uid + - key: k8s.kubelet.version + value: + stringValue: v1.25.3 + - key: container.runtime.version + value: + stringValue: "1.6.9" + - key: container.runtime + value: + stringValue: "containerd" + - key: os.description + value: + stringValue: Ubuntu 22.04.1 LTS + - key: os.type + value: + stringValue: "linux" + + schemaUrl: https://opentelemetry.io/schemas/1.18.0 + scopeMetrics: + - metrics: + - description: Amount of cpu allocatable on the node + gauge: + dataPoints: + - asDouble: 0.123 + name: k8s.node.allocatable.cpu + unit: '{cpu}' + - description: Amount of memory allocatable on the node + gauge: + dataPoints: + - asInt: "456" + name: k8s.node.allocatable.memory + unit: By + scope: + name: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver + version: latest diff --git a/receiver/k8sclusterreceiver/metadata.yaml b/receiver/k8sclusterreceiver/metadata.yaml index df2805d74f0f3..60ca07adbbf4e 100644 --- a/receiver/k8sclusterreceiver/metadata.yaml +++ b/receiver/k8sclusterreceiver/metadata.yaml @@ -540,7 +540,7 @@ metrics: # k8s.node.condition_* metrics (k8s.node.condition_ready, k8s.node.condition_memory_pressure, etc) are controlled # by node_conditions_to_report config option. By default, only k8s.node.condition_ready is enabled. - # k8s.node.allocatable_* metrics (k8s.node.allocatable_cpu, k8s.node.allocatable_memory, etc) are controlled + # k8s.node.allocatable_* or k8s.node.allocatable.* metrics (k8s.node.allocatable_cpu/k8s.node.allocatable.cpu, k8s.node.allocatable_memory/k8s.node.allocatable.memory, etc) are controlled # by allocatable_types_to_report config option. By default, none of them are reported. tests: From 5bf22ce115eda937a4e1252289d93bc9cd6b74e6 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 9 Sep 2025 10:50:51 +0200 Subject: [PATCH 02/14] go mod tidy Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/k8sclusterreceiver/go.mod b/receiver/k8sclusterreceiver/go.mod index 7d33050860b16..76e37427ea905 100644 --- a/receiver/k8sclusterreceiver/go.mod +++ b/receiver/k8sclusterreceiver/go.mod @@ -25,6 +25,7 @@ require ( go.opentelemetry.io/collector/confmap/xconfmap v0.135.1-0.20250911090542-8b3a08ca2a38 go.opentelemetry.io/collector/consumer v1.41.1-0.20250911090542-8b3a08ca2a38 go.opentelemetry.io/collector/consumer/consumertest v0.135.1-0.20250911090542-8b3a08ca2a38 + go.opentelemetry.io/collector/featuregate v1.41.1-0.20250911090542-8b3a08ca2a38 go.opentelemetry.io/collector/filter v0.135.1-0.20250911090542-8b3a08ca2a38 go.opentelemetry.io/collector/pdata v1.41.1-0.20250911090542-8b3a08ca2a38 go.opentelemetry.io/collector/pipeline v1.41.1-0.20250911090542-8b3a08ca2a38 @@ -111,7 +112,6 @@ require ( go.opentelemetry.io/collector/extension v1.41.1-0.20250911090542-8b3a08ca2a38 // indirect go.opentelemetry.io/collector/extension/extensionauth v1.41.1-0.20250911090542-8b3a08ca2a38 // indirect go.opentelemetry.io/collector/extension/extensionmiddleware v0.135.1-0.20250911090542-8b3a08ca2a38 // indirect - go.opentelemetry.io/collector/featuregate v1.41.1-0.20250911090542-8b3a08ca2a38 // indirect go.opentelemetry.io/collector/internal/sharedcomponent v0.135.1-0.20250911090542-8b3a08ca2a38 // indirect go.opentelemetry.io/collector/internal/telemetry v0.135.1-0.20250911090542-8b3a08ca2a38 // indirect go.opentelemetry.io/collector/pdata/pprofile v0.135.1-0.20250911090542-8b3a08ca2a38 // indirect From 559d6293b54cafd510a9de1465fc22d2ba90ae72 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 9 Sep 2025 12:04:37 +0200 Subject: [PATCH 03/14] use generated metrics Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/README.md | 2 +- receiver/k8sclusterreceiver/documentation.md | 40 +++ .../internal/metadata/generated_config.go | 20 ++ .../metadata/generated_config_test.go | 10 + .../internal/metadata/generated_metrics.go | 315 ++++++++++++++++++ .../metadata/generated_metrics_test.go | 90 +++++ .../internal/metadata/testdata/config.yaml | 20 ++ .../k8sclusterreceiver/internal/node/nodes.go | 59 ++-- .../internal/node/nodes_test.go | 73 +--- .../internal/node/testdata/expected.yaml | 6 + .../node/testdata/expected_allocatable.yaml | 63 +--- .../expected_optional_allocatable.yaml | 43 --- .../internal/testutils/objects.go | 1 + receiver/k8sclusterreceiver/metadata.yaml | 39 ++- 14 files changed, 601 insertions(+), 180 deletions(-) delete mode 100644 receiver/k8sclusterreceiver/internal/node/testdata/expected_optional_allocatable.yaml diff --git a/receiver/k8sclusterreceiver/README.md b/receiver/k8sclusterreceiver/README.md index 7be4a10cdcca9..a0ebb903a5610 100644 --- a/receiver/k8sclusterreceiver/README.md +++ b/receiver/k8sclusterreceiver/README.md @@ -451,7 +451,7 @@ Add the following rules to your ClusterRole: The `k8scluster.allocatableNamespace.enabled` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) enables the SemConv valid format of the node allocatable metrics reported by the receiver. The feature gate is in `alpha` stage, which means it is disabled by default. -If enabled (together with the `allocatable_types_to_report` config option), the SemConv valid format of the node allocatable metrics are reported: +If enabled the SemConv valid format of the node allocatable metrics are reported (if enabled): - `k8s.node.allocatable.cpu` - `k8s.node.allocatable.ephemeral_storage` diff --git a/receiver/k8sclusterreceiver/documentation.md b/receiver/k8sclusterreceiver/documentation.md index 8e38ca93ef168..c79f19fbc39a8 100644 --- a/receiver/k8sclusterreceiver/documentation.md +++ b/receiver/k8sclusterreceiver/documentation.md @@ -228,6 +228,46 @@ The current phase of namespaces (1 for active and 0 for terminating) | ---- | ----------- | ---------- | | | Gauge | Int | +### k8s.node.allocatable.cpu + +Amount of cpu allocatable on the node + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| {cpu} | Sum | Double | Unspecified | false | + +### k8s.node.allocatable.ephemeral_storage + +Amount of ephemeral-storage allocatable on the node + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| By | Sum | Int | Unspecified | false | + +### k8s.node.allocatable.memory + +Amount of memory allocatable on the node + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| By | Sum | Int | Unspecified | false | + +### k8s.node.allocatable.pods + +Amount of pods allocatable on the node + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| {pod} | Sum | Int | Unspecified | false | + +### k8s.node.allocatable.storage + +Amount of storage allocatable on the node + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| By | Sum | Int | Unspecified | false | + ### k8s.pod.phase Current phase of the pod (1 - Pending, 2 - Running, 3 - Succeeded, 4 - Failed, 5 - Unknown) diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_config.go b/receiver/k8sclusterreceiver/internal/metadata/generated_config.go index 560ab5da55f17..5d6e969c83455 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_config.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_config.go @@ -56,6 +56,11 @@ type MetricsConfig struct { K8sJobMaxParallelPods MetricConfig `mapstructure:"k8s.job.max_parallel_pods"` K8sJobSuccessfulPods MetricConfig `mapstructure:"k8s.job.successful_pods"` K8sNamespacePhase MetricConfig `mapstructure:"k8s.namespace.phase"` + K8sNodeAllocatableCPU MetricConfig `mapstructure:"k8s.node.allocatable.cpu"` + K8sNodeAllocatableEphemeralStorage MetricConfig `mapstructure:"k8s.node.allocatable.ephemeral_storage"` + K8sNodeAllocatableMemory MetricConfig `mapstructure:"k8s.node.allocatable.memory"` + K8sNodeAllocatablePods MetricConfig `mapstructure:"k8s.node.allocatable.pods"` + K8sNodeAllocatableStorage MetricConfig `mapstructure:"k8s.node.allocatable.storage"` K8sNodeCondition MetricConfig `mapstructure:"k8s.node.condition"` K8sPodPhase MetricConfig `mapstructure:"k8s.pod.phase"` K8sPodStatusReason MetricConfig `mapstructure:"k8s.pod.status_reason"` @@ -161,6 +166,21 @@ func DefaultMetricsConfig() MetricsConfig { K8sNamespacePhase: MetricConfig{ Enabled: true, }, + K8sNodeAllocatableCPU: MetricConfig{ + Enabled: true, + }, + K8sNodeAllocatableEphemeralStorage: MetricConfig{ + Enabled: true, + }, + K8sNodeAllocatableMemory: MetricConfig{ + Enabled: true, + }, + K8sNodeAllocatablePods: MetricConfig{ + Enabled: true, + }, + K8sNodeAllocatableStorage: MetricConfig{ + Enabled: true, + }, K8sNodeCondition: MetricConfig{ Enabled: false, }, diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go b/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go index 71347a50930de..86ea92908533e 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go @@ -55,6 +55,11 @@ func TestMetricsBuilderConfig(t *testing.T) { K8sJobMaxParallelPods: MetricConfig{Enabled: true}, K8sJobSuccessfulPods: MetricConfig{Enabled: true}, K8sNamespacePhase: MetricConfig{Enabled: true}, + K8sNodeAllocatableCPU: MetricConfig{Enabled: true}, + K8sNodeAllocatableEphemeralStorage: MetricConfig{Enabled: true}, + K8sNodeAllocatableMemory: MetricConfig{Enabled: true}, + K8sNodeAllocatablePods: MetricConfig{Enabled: true}, + K8sNodeAllocatableStorage: MetricConfig{Enabled: true}, K8sNodeCondition: MetricConfig{Enabled: true}, K8sPodPhase: MetricConfig{Enabled: true}, K8sPodStatusReason: MetricConfig{Enabled: true}, @@ -149,6 +154,11 @@ func TestMetricsBuilderConfig(t *testing.T) { K8sJobMaxParallelPods: MetricConfig{Enabled: false}, K8sJobSuccessfulPods: MetricConfig{Enabled: false}, K8sNamespacePhase: MetricConfig{Enabled: false}, + K8sNodeAllocatableCPU: MetricConfig{Enabled: false}, + K8sNodeAllocatableEphemeralStorage: MetricConfig{Enabled: false}, + K8sNodeAllocatableMemory: MetricConfig{Enabled: false}, + K8sNodeAllocatablePods: MetricConfig{Enabled: false}, + K8sNodeAllocatableStorage: MetricConfig{Enabled: false}, K8sNodeCondition: MetricConfig{Enabled: false}, K8sPodPhase: MetricConfig{Enabled: false}, K8sPodStatusReason: MetricConfig{Enabled: false}, diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go index a3ac841476423..0de145991e4d0 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go @@ -128,6 +128,21 @@ var MetricsInfo = metricsInfo{ K8sNamespacePhase: metricInfo{ Name: "k8s.namespace.phase", }, + K8sNodeAllocatableCPU: metricInfo{ + Name: "k8s.node.allocatable.cpu", + }, + K8sNodeAllocatableEphemeralStorage: metricInfo{ + Name: "k8s.node.allocatable.ephemeral_storage", + }, + K8sNodeAllocatableMemory: metricInfo{ + Name: "k8s.node.allocatable.memory", + }, + K8sNodeAllocatablePods: metricInfo{ + Name: "k8s.node.allocatable.pods", + }, + K8sNodeAllocatableStorage: metricInfo{ + Name: "k8s.node.allocatable.storage", + }, K8sNodeCondition: metricInfo{ Name: "k8s.node.condition", }, @@ -210,6 +225,11 @@ type metricsInfo struct { K8sJobMaxParallelPods metricInfo K8sJobSuccessfulPods metricInfo K8sNamespacePhase metricInfo + K8sNodeAllocatableCPU metricInfo + K8sNodeAllocatableEphemeralStorage metricInfo + K8sNodeAllocatableMemory metricInfo + K8sNodeAllocatablePods metricInfo + K8sNodeAllocatableStorage metricInfo K8sNodeCondition metricInfo K8sPodPhase metricInfo K8sPodStatusReason metricInfo @@ -1609,6 +1629,261 @@ func newMetricK8sNamespacePhase(cfg MetricConfig) metricK8sNamespacePhase { return m } +type metricK8sNodeAllocatableCPU struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.node.allocatable.cpu metric with initial data. +func (m *metricK8sNodeAllocatableCPU) init() { + m.data.SetName("k8s.node.allocatable.cpu") + m.data.SetDescription("Amount of cpu allocatable on the node") + m.data.SetUnit("{cpu}") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(false) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityUnspecified) +} + +func (m *metricK8sNodeAllocatableCPU) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetDoubleValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricK8sNodeAllocatableCPU) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sNodeAllocatableCPU) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sNodeAllocatableCPU(cfg MetricConfig) metricK8sNodeAllocatableCPU { + m := metricK8sNodeAllocatableCPU{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricK8sNodeAllocatableEphemeralStorage struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.node.allocatable.ephemeral_storage metric with initial data. +func (m *metricK8sNodeAllocatableEphemeralStorage) init() { + m.data.SetName("k8s.node.allocatable.ephemeral_storage") + m.data.SetDescription("Amount of ephemeral-storage allocatable on the node") + m.data.SetUnit("By") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(false) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityUnspecified) +} + +func (m *metricK8sNodeAllocatableEphemeralStorage) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().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 *metricK8sNodeAllocatableEphemeralStorage) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sNodeAllocatableEphemeralStorage) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sNodeAllocatableEphemeralStorage(cfg MetricConfig) metricK8sNodeAllocatableEphemeralStorage { + m := metricK8sNodeAllocatableEphemeralStorage{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricK8sNodeAllocatableMemory struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.node.allocatable.memory metric with initial data. +func (m *metricK8sNodeAllocatableMemory) init() { + m.data.SetName("k8s.node.allocatable.memory") + m.data.SetDescription("Amount of memory allocatable on the node") + m.data.SetUnit("By") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(false) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityUnspecified) +} + +func (m *metricK8sNodeAllocatableMemory) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().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 *metricK8sNodeAllocatableMemory) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sNodeAllocatableMemory) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sNodeAllocatableMemory(cfg MetricConfig) metricK8sNodeAllocatableMemory { + m := metricK8sNodeAllocatableMemory{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricK8sNodeAllocatablePods struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.node.allocatable.pods metric with initial data. +func (m *metricK8sNodeAllocatablePods) init() { + m.data.SetName("k8s.node.allocatable.pods") + m.data.SetDescription("Amount of pods allocatable on the node") + m.data.SetUnit("{pod}") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(false) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityUnspecified) +} + +func (m *metricK8sNodeAllocatablePods) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().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 *metricK8sNodeAllocatablePods) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sNodeAllocatablePods) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sNodeAllocatablePods(cfg MetricConfig) metricK8sNodeAllocatablePods { + m := metricK8sNodeAllocatablePods{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricK8sNodeAllocatableStorage struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.node.allocatable.storage metric with initial data. +func (m *metricK8sNodeAllocatableStorage) init() { + m.data.SetName("k8s.node.allocatable.storage") + m.data.SetDescription("Amount of storage allocatable on the node") + m.data.SetUnit("By") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(false) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityUnspecified) +} + +func (m *metricK8sNodeAllocatableStorage) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().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 *metricK8sNodeAllocatableStorage) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sNodeAllocatableStorage) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sNodeAllocatableStorage(cfg MetricConfig) metricK8sNodeAllocatableStorage { + m := metricK8sNodeAllocatableStorage{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricK8sNodeCondition struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -2496,6 +2771,11 @@ type MetricsBuilder struct { metricK8sJobMaxParallelPods metricK8sJobMaxParallelPods metricK8sJobSuccessfulPods metricK8sJobSuccessfulPods metricK8sNamespacePhase metricK8sNamespacePhase + metricK8sNodeAllocatableCPU metricK8sNodeAllocatableCPU + metricK8sNodeAllocatableEphemeralStorage metricK8sNodeAllocatableEphemeralStorage + metricK8sNodeAllocatableMemory metricK8sNodeAllocatableMemory + metricK8sNodeAllocatablePods metricK8sNodeAllocatablePods + metricK8sNodeAllocatableStorage metricK8sNodeAllocatableStorage metricK8sNodeCondition metricK8sNodeCondition metricK8sPodPhase metricK8sPodPhase metricK8sPodStatusReason metricK8sPodStatusReason @@ -2566,6 +2846,11 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, opt metricK8sJobMaxParallelPods: newMetricK8sJobMaxParallelPods(mbc.Metrics.K8sJobMaxParallelPods), metricK8sJobSuccessfulPods: newMetricK8sJobSuccessfulPods(mbc.Metrics.K8sJobSuccessfulPods), metricK8sNamespacePhase: newMetricK8sNamespacePhase(mbc.Metrics.K8sNamespacePhase), + metricK8sNodeAllocatableCPU: newMetricK8sNodeAllocatableCPU(mbc.Metrics.K8sNodeAllocatableCPU), + metricK8sNodeAllocatableEphemeralStorage: newMetricK8sNodeAllocatableEphemeralStorage(mbc.Metrics.K8sNodeAllocatableEphemeralStorage), + metricK8sNodeAllocatableMemory: newMetricK8sNodeAllocatableMemory(mbc.Metrics.K8sNodeAllocatableMemory), + metricK8sNodeAllocatablePods: newMetricK8sNodeAllocatablePods(mbc.Metrics.K8sNodeAllocatablePods), + metricK8sNodeAllocatableStorage: newMetricK8sNodeAllocatableStorage(mbc.Metrics.K8sNodeAllocatableStorage), metricK8sNodeCondition: newMetricK8sNodeCondition(mbc.Metrics.K8sNodeCondition), metricK8sPodPhase: newMetricK8sPodPhase(mbc.Metrics.K8sPodPhase), metricK8sPodStatusReason: newMetricK8sPodStatusReason(mbc.Metrics.K8sPodStatusReason), @@ -2924,6 +3209,11 @@ func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { mb.metricK8sJobMaxParallelPods.emit(ils.Metrics()) mb.metricK8sJobSuccessfulPods.emit(ils.Metrics()) mb.metricK8sNamespacePhase.emit(ils.Metrics()) + mb.metricK8sNodeAllocatableCPU.emit(ils.Metrics()) + mb.metricK8sNodeAllocatableEphemeralStorage.emit(ils.Metrics()) + mb.metricK8sNodeAllocatableMemory.emit(ils.Metrics()) + mb.metricK8sNodeAllocatablePods.emit(ils.Metrics()) + mb.metricK8sNodeAllocatableStorage.emit(ils.Metrics()) mb.metricK8sNodeCondition.emit(ils.Metrics()) mb.metricK8sPodPhase.emit(ils.Metrics()) mb.metricK8sPodStatusReason.emit(ils.Metrics()) @@ -3112,6 +3402,31 @@ func (mb *MetricsBuilder) RecordK8sNamespacePhaseDataPoint(ts pcommon.Timestamp, mb.metricK8sNamespacePhase.recordDataPoint(mb.startTime, ts, val) } +// RecordK8sNodeAllocatableCPUDataPoint adds a data point to k8s.node.allocatable.cpu metric. +func (mb *MetricsBuilder) RecordK8sNodeAllocatableCPUDataPoint(ts pcommon.Timestamp, val float64) { + mb.metricK8sNodeAllocatableCPU.recordDataPoint(mb.startTime, ts, val) +} + +// RecordK8sNodeAllocatableEphemeralStorageDataPoint adds a data point to k8s.node.allocatable.ephemeral_storage metric. +func (mb *MetricsBuilder) RecordK8sNodeAllocatableEphemeralStorageDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricK8sNodeAllocatableEphemeralStorage.recordDataPoint(mb.startTime, ts, val) +} + +// RecordK8sNodeAllocatableMemoryDataPoint adds a data point to k8s.node.allocatable.memory metric. +func (mb *MetricsBuilder) RecordK8sNodeAllocatableMemoryDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricK8sNodeAllocatableMemory.recordDataPoint(mb.startTime, ts, val) +} + +// RecordK8sNodeAllocatablePodsDataPoint adds a data point to k8s.node.allocatable.pods metric. +func (mb *MetricsBuilder) RecordK8sNodeAllocatablePodsDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricK8sNodeAllocatablePods.recordDataPoint(mb.startTime, ts, val) +} + +// RecordK8sNodeAllocatableStorageDataPoint adds a data point to k8s.node.allocatable.storage metric. +func (mb *MetricsBuilder) RecordK8sNodeAllocatableStorageDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricK8sNodeAllocatableStorage.recordDataPoint(mb.startTime, ts, val) +} + // RecordK8sNodeConditionDataPoint adds a data point to k8s.node.condition metric. func (mb *MetricsBuilder) RecordK8sNodeConditionDataPoint(ts pcommon.Timestamp, val int64, conditionAttributeValue string) { mb.metricK8sNodeCondition.recordDataPoint(mb.startTime, ts, val, conditionAttributeValue) diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go index 703ec9896b3db..38cc241bb22ea 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go @@ -179,6 +179,26 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordK8sNamespacePhaseDataPoint(ts, 1) + defaultMetricsCount++ + allMetricsCount++ + mb.RecordK8sNodeAllocatableCPUDataPoint(ts, 1) + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordK8sNodeAllocatableEphemeralStorageDataPoint(ts, 1) + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordK8sNodeAllocatableMemoryDataPoint(ts, 1) + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordK8sNodeAllocatablePodsDataPoint(ts, 1) + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordK8sNodeAllocatableStorageDataPoint(ts, 1) + allMetricsCount++ mb.RecordK8sNodeConditionDataPoint(ts, 1, "condition-val") @@ -649,6 +669,76 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) + case "k8s.node.allocatable.cpu": + assert.False(t, validatedMetrics["k8s.node.allocatable.cpu"], "Found a duplicate in the metrics slice: k8s.node.allocatable.cpu") + validatedMetrics["k8s.node.allocatable.cpu"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Amount of cpu allocatable on the node", ms.At(i).Description()) + assert.Equal(t, "{cpu}", ms.At(i).Unit()) + assert.False(t, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityUnspecified, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeDouble, dp.ValueType()) + assert.InDelta(t, float64(1), dp.DoubleValue(), 0.01) + case "k8s.node.allocatable.ephemeral_storage": + assert.False(t, validatedMetrics["k8s.node.allocatable.ephemeral_storage"], "Found a duplicate in the metrics slice: k8s.node.allocatable.ephemeral_storage") + validatedMetrics["k8s.node.allocatable.ephemeral_storage"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Amount of ephemeral-storage allocatable on the node", ms.At(i).Description()) + assert.Equal(t, "By", ms.At(i).Unit()) + assert.False(t, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityUnspecified, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().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 "k8s.node.allocatable.memory": + assert.False(t, validatedMetrics["k8s.node.allocatable.memory"], "Found a duplicate in the metrics slice: k8s.node.allocatable.memory") + validatedMetrics["k8s.node.allocatable.memory"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Amount of memory allocatable on the node", ms.At(i).Description()) + assert.Equal(t, "By", ms.At(i).Unit()) + assert.False(t, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityUnspecified, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().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 "k8s.node.allocatable.pods": + assert.False(t, validatedMetrics["k8s.node.allocatable.pods"], "Found a duplicate in the metrics slice: k8s.node.allocatable.pods") + validatedMetrics["k8s.node.allocatable.pods"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Amount of pods allocatable on the node", ms.At(i).Description()) + assert.Equal(t, "{pod}", ms.At(i).Unit()) + assert.False(t, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityUnspecified, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().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 "k8s.node.allocatable.storage": + assert.False(t, validatedMetrics["k8s.node.allocatable.storage"], "Found a duplicate in the metrics slice: k8s.node.allocatable.storage") + validatedMetrics["k8s.node.allocatable.storage"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Amount of storage allocatable on the node", ms.At(i).Description()) + assert.Equal(t, "By", ms.At(i).Unit()) + assert.False(t, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityUnspecified, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().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 "k8s.node.condition": assert.False(t, validatedMetrics["k8s.node.condition"], "Found a duplicate in the metrics slice: k8s.node.condition") validatedMetrics["k8s.node.condition"] = true diff --git a/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml b/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml index 743fca44736bf..6ba0c866ef549 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml @@ -57,6 +57,16 @@ all_set: enabled: true k8s.namespace.phase: enabled: true + k8s.node.allocatable.cpu: + enabled: true + k8s.node.allocatable.ephemeral_storage: + enabled: true + k8s.node.allocatable.memory: + enabled: true + k8s.node.allocatable.pods: + enabled: true + k8s.node.allocatable.storage: + enabled: true k8s.node.condition: enabled: true k8s.pod.phase: @@ -230,6 +240,16 @@ none_set: enabled: false k8s.namespace.phase: enabled: false + k8s.node.allocatable.cpu: + enabled: false + k8s.node.allocatable.ephemeral_storage: + enabled: false + k8s.node.allocatable.memory: + enabled: false + k8s.node.allocatable.pods: + enabled: false + k8s.node.allocatable.storage: + enabled: false k8s.node.condition: enabled: false k8s.pod.phase: diff --git a/receiver/k8sclusterreceiver/internal/node/nodes.go b/receiver/k8sclusterreceiver/internal/node/nodes.go index de86d8b14d107..d346ca79fb603 100644 --- a/receiver/k8sclusterreceiver/internal/node/nodes.go +++ b/receiver/k8sclusterreceiver/internal/node/nodes.go @@ -29,7 +29,7 @@ const ( ) var allowAllocatableNamespace = featuregate.GlobalRegistry().MustRegister( - "k8scluster.allocatableNamespace.enabled", + "receiver.k8scluster.allocatableNamespace.enabled", featuregate.StageAlpha, featuregate.WithRegisterDescription("When enabled, allocatable metrics are reported under allocatable namespace: with '.' instead of '_'"), featuregate.WithRegisterFromVersion("v0.136.0"), @@ -68,6 +68,28 @@ func RecordMetrics(mb *metadata.MetricsBuilder, node *corev1.Node, ts pcommon.Ti rb.SetK8sNodeName(node.Name) rb.SetK8sKubeletVersion(node.Status.NodeInfo.KubeletVersion) + if allowAllocatableNamespace.IsEnabled() { + if cpuVal, ok := node.Status.Allocatable[corev1.ResourceCPU]; ok { + mb.RecordK8sNodeAllocatableCPUDataPoint(ts, float64(cpuVal.MilliValue())/1000.0) + } + + if ephemeralMemoryVal, ok := node.Status.Allocatable[corev1.ResourceEphemeralStorage]; ok { + mb.RecordK8sNodeAllocatableEphemeralStorageDataPoint(ts, ephemeralMemoryVal.Value()) + } + + if storageVal, ok := node.Status.Allocatable[corev1.ResourceStorage]; ok { + mb.RecordK8sNodeAllocatableStorageDataPoint(ts, storageVal.Value()) + } + + if memoryVal, ok := node.Status.Allocatable[corev1.ResourceMemory]; ok { + mb.RecordK8sNodeAllocatableMemoryDataPoint(ts, memoryVal.Value()) + } + + if podVal, ok := node.Status.Allocatable[corev1.ResourcePods]; ok { + mb.RecordK8sNodeAllocatablePodsDataPoint(ts, podVal.Value()) + } + } + mb.EmitForResource(metadata.WithResource(rb.Emit())) } @@ -91,22 +113,24 @@ func CustomMetrics(set receiver.Settings, rb *metadata.ResourceBuilder, node *co } // Adding 'node allocatable type' metrics - for _, nodeAllocatableTypeValue := range allocatableTypesToReport { - v1NodeAllocatableTypeValue := corev1.ResourceName(nodeAllocatableTypeValue) - quantity, ok := node.Status.Allocatable[v1NodeAllocatableTypeValue] - if !ok { - set.Logger.Debug(fmt.Errorf("allocatable type %v not found in node %v", nodeAllocatableTypeValue, - node.GetName()).Error()) - continue + if !allowAllocatableNamespace.IsEnabled() { + for _, nodeAllocatableTypeValue := range allocatableTypesToReport { + v1NodeAllocatableTypeValue := corev1.ResourceName(nodeAllocatableTypeValue) + quantity, ok := node.Status.Allocatable[v1NodeAllocatableTypeValue] + if !ok { + set.Logger.Debug(fmt.Errorf("allocatable type %v not found in node %v", nodeAllocatableTypeValue, + node.GetName()).Error()) + continue + } + m := sm.Metrics().AppendEmpty() + m.SetName(getNodeAllocatableMetric(nodeAllocatableTypeValue)) + m.SetDescription(fmt.Sprintf("Amount of %v allocatable on the node", nodeAllocatableTypeValue)) + m.SetUnit(getNodeAllocatableUnit(v1NodeAllocatableTypeValue)) + g := m.SetEmptyGauge() + dp := g.DataPoints().AppendEmpty() + setNodeAllocatableValue(dp, v1NodeAllocatableTypeValue, quantity) + dp.SetTimestamp(ts) } - m := sm.Metrics().AppendEmpty() - m.SetName(getNodeAllocatableMetric(nodeAllocatableTypeValue)) - m.SetDescription(fmt.Sprintf("Amount of %v allocatable on the node", nodeAllocatableTypeValue)) - m.SetUnit(getNodeAllocatableUnit(v1NodeAllocatableTypeValue)) - g := m.SetEmptyGauge() - dp := g.DataPoints().AppendEmpty() - setNodeAllocatableValue(dp, v1NodeAllocatableTypeValue, quantity) - dp.SetTimestamp(ts) } if sm.Metrics().Len() == 0 { @@ -226,8 +250,5 @@ func setNodeAllocatableValue(dp pmetric.NumberDataPoint, res corev1.ResourceName } func getNodeAllocatableMetric(nodeAllocatableTypeValue string) string { - if allowAllocatableNamespace.IsEnabled() { - return "k8s.node.allocatable." + strcase.ToSnake(nodeAllocatableTypeValue) - } return "k8s.node.allocatable_" + strcase.ToSnake(nodeAllocatableTypeValue) } diff --git a/receiver/k8sclusterreceiver/internal/node/nodes_test.go b/receiver/k8sclusterreceiver/internal/node/nodes_test.go index ce0537cf54c3b..0b75e213769c7 100644 --- a/receiver/k8sclusterreceiver/internal/node/nodes_test.go +++ b/receiver/k8sclusterreceiver/internal/node/nodes_test.go @@ -71,30 +71,17 @@ func TestNodeMetricsReportCPUMetricsAllocatableNamespace(t *testing.T) { require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), false)) }() n := testutils.NewNode("1") - rb := metadata.NewResourceBuilder(metadata.DefaultResourceAttributesConfig()) - rm := CustomMetrics(receivertest.NewNopSettings(metadata.Type), rb, n, - []string{ - "Ready", - "MemoryPressure", - "DiskPressure", - "NetworkUnavailable", - "PIDPressure", - "OutOfDisk", - }, - []string{ - "cpu", - "memory", - "ephemeral-storage", - "storage", - "pods", - "hugepages-1Gi", - "hugepages-2Mi", - "not-present", - }, - pcommon.Timestamp(time.Now().UnixNano()), - ) - m := pmetric.NewMetrics() - rm.MoveTo(m.ResourceMetrics().AppendEmpty()) + + ts := pcommon.Timestamp(time.Now().UnixNano()) + mbc := metadata.DefaultMetricsBuilderConfig() + mbc.Metrics.K8sNodeAllocatableCPU.Enabled = true + mbc.Metrics.K8sNodeAllocatableMemory.Enabled = true + mbc.Metrics.K8sNodeAllocatableEphemeralStorage.Enabled = true + mbc.Metrics.K8sNodeAllocatableStorage.Enabled = true + mbc.Metrics.K8sNodeAllocatablePods.Enabled = true + mb := metadata.NewMetricsBuilder(mbc, receivertest.NewNopSettings(metadata.Type)) + RecordMetrics(mb, n, ts) + m := mb.Emit() expected, err := golden.ReadMetrics(filepath.Join("testdata", "expected_allocatable.yaml")) require.NoError(t, err) @@ -142,44 +129,6 @@ func TestNodeOptionalMetrics(t *testing.T) { ) } -func TestNodeOptionalMetricsAllocatableNamespace(t *testing.T) { - require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), true)) - defer func() { - require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), false)) - }() - n := testutils.NewNode("2") - rac := metadata.DefaultResourceAttributesConfig() - rac.K8sKubeletVersion.Enabled = true - rac.ContainerRuntime.Enabled = true - rac.ContainerRuntimeVersion.Enabled = true - rac.OsType.Enabled = true - rac.OsDescription.Enabled = true - - rb := metadata.NewResourceBuilder(rac) - rm := CustomMetrics(receivertest.NewNopSettings(metadata.Type), rb, n, - []string{}, - []string{ - "cpu", - "memory", - }, - - pcommon.Timestamp(time.Now().UnixNano()), - ) - m := pmetric.NewMetrics() - rm.MoveTo(m.ResourceMetrics().AppendEmpty()) - - expected, err := golden.ReadMetrics(filepath.Join("testdata", "expected_optional_allocatable.yaml")) - require.NoError(t, err) - require.NoError(t, pmetrictest.CompareMetrics(expected, m, - pmetrictest.IgnoreTimestamp(), - pmetrictest.IgnoreStartTimestamp(), - pmetrictest.IgnoreResourceMetricsOrder(), - pmetrictest.IgnoreMetricsOrder(), - pmetrictest.IgnoreScopeMetricsOrder(), - ), - ) -} - func TestNodeConditionValue(t *testing.T) { type args struct { node *corev1.Node diff --git a/receiver/k8sclusterreceiver/internal/node/testdata/expected.yaml b/receiver/k8sclusterreceiver/internal/node/testdata/expected.yaml index 7e63aac6f972d..904e6e8ea1120 100644 --- a/receiver/k8sclusterreceiver/internal/node/testdata/expected.yaml +++ b/receiver/k8sclusterreceiver/internal/node/testdata/expected.yaml @@ -22,6 +22,12 @@ resourceMetrics: - asInt: "1234" name: k8s.node.allocatable_ephemeral_storage unit: By + - description: Amount of storage allocatable on the node + gauge: + dataPoints: + - asInt: "34" + name: k8s.node.allocatable_storage + unit: By - description: Amount of memory allocatable on the node gauge: dataPoints: diff --git a/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml b/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml index a4ef9f84e6033..b981417e6c78e 100644 --- a/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml +++ b/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml @@ -11,77 +11,36 @@ resourceMetrics: scopeMetrics: - metrics: - description: Amount of cpu allocatable on the node - gauge: + sum: dataPoints: - asDouble: 0.123 name: k8s.node.allocatable.cpu unit: '{cpu}' - description: Amount of ephemeral-storage allocatable on the node - gauge: + sum: dataPoints: - asInt: "1234" name: k8s.node.allocatable.ephemeral_storage unit: By + - description: Amount of storage allocatable on the node + sum: + dataPoints: + - asInt: "34" + name: k8s.node.allocatable.storage + unit: By - description: Amount of memory allocatable on the node - gauge: + sum: dataPoints: - asInt: "456" name: k8s.node.allocatable.memory unit: By - description: Amount of pods allocatable on the node - gauge: + sum: dataPoints: - asInt: "12" name: k8s.node.allocatable.pods unit: "{pod}" - - description: DiskPressure condition status of the node (true=1, false=0, unknown=-1) - gauge: - dataPoints: - - asInt: "0" - name: k8s.node.condition_disk_pressure - unit: "" - - description: MemoryPressure condition status of the node (true=1, false=0, unknown=-1) - gauge: - dataPoints: - - asInt: "0" - name: k8s.node.condition_memory_pressure - unit: "" - - description: NetworkUnavailable condition status of the node (true=1, false=0, unknown=-1) - gauge: - dataPoints: - - asInt: "0" - name: k8s.node.condition_network_unavailable - unit: "" - - description: PIDPressure condition status of the node (true=1, false=0, unknown=-1) - gauge: - dataPoints: - - asInt: "0" - name: k8s.node.condition_pid_pressure - unit: "" - - description: Ready condition status of the node (true=1, false=0, unknown=-1) - gauge: - dataPoints: - - asInt: "1" - name: k8s.node.condition_ready - unit: "" - - description: OutOfDisk condition status of the node (true=1, false=0, unknown=-1) - gauge: - dataPoints: - - asInt: "-1" - name: k8s.node.condition_out_of_disk - unit: "" - - description: Amount of hugepages-1Gi allocatable on the node - gauge: - dataPoints: - - asInt: "2" - name: k8s.node.allocatable.hugepages_1_gi - unit: "{hugepages-1Gi}" - - description: Amount of hugepages-2Mi allocatable on the node - gauge: - dataPoints: - - asInt: "2048" - name: k8s.node.allocatable.hugepages_2_mi - unit: "{hugepages-2Mi}" + scope: name: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver version: latest diff --git a/receiver/k8sclusterreceiver/internal/node/testdata/expected_optional_allocatable.yaml b/receiver/k8sclusterreceiver/internal/node/testdata/expected_optional_allocatable.yaml deleted file mode 100644 index 28518d3999bb0..0000000000000 --- a/receiver/k8sclusterreceiver/internal/node/testdata/expected_optional_allocatable.yaml +++ /dev/null @@ -1,43 +0,0 @@ -resourceMetrics: - - resource: - attributes: - - key: k8s.node.name - value: - stringValue: test-node-2 - - key: k8s.node.uid - value: - stringValue: test-node-2-uid - - key: k8s.kubelet.version - value: - stringValue: v1.25.3 - - key: container.runtime.version - value: - stringValue: "1.6.9" - - key: container.runtime - value: - stringValue: "containerd" - - key: os.description - value: - stringValue: Ubuntu 22.04.1 LTS - - key: os.type - value: - stringValue: "linux" - - schemaUrl: https://opentelemetry.io/schemas/1.18.0 - scopeMetrics: - - metrics: - - description: Amount of cpu allocatable on the node - gauge: - dataPoints: - - asDouble: 0.123 - name: k8s.node.allocatable.cpu - unit: '{cpu}' - - description: Amount of memory allocatable on the node - gauge: - dataPoints: - - asInt: "456" - name: k8s.node.allocatable.memory - unit: By - scope: - name: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver - version: latest diff --git a/receiver/k8sclusterreceiver/internal/testutils/objects.go b/receiver/k8sclusterreceiver/internal/testutils/objects.go index 3161879bbd832..c908fd93f8da2 100644 --- a/receiver/k8sclusterreceiver/internal/testutils/objects.go +++ b/receiver/k8sclusterreceiver/internal/testutils/objects.go @@ -202,6 +202,7 @@ func NewNode(id string) *corev1.Node { corev1.ResourceCPU: *resource.NewMilliQuantity(123, resource.DecimalSI), corev1.ResourceMemory: *resource.NewQuantity(456, resource.DecimalSI), corev1.ResourceEphemeralStorage: *resource.NewQuantity(1234, resource.DecimalSI), + corev1.ResourceStorage: *resource.NewQuantity(34, resource.DecimalSI), corev1.ResourcePods: *resource.NewQuantity(12, resource.DecimalSI), "hugepages-1Gi": *resource.NewQuantity(2, resource.DecimalSI), "hugepages-2Mi": *resource.NewQuantity(2048, resource.DecimalSI), diff --git a/receiver/k8sclusterreceiver/metadata.yaml b/receiver/k8sclusterreceiver/metadata.yaml index 60ca07adbbf4e..b299cfcc804e1 100644 --- a/receiver/k8sclusterreceiver/metadata.yaml +++ b/receiver/k8sclusterreceiver/metadata.yaml @@ -537,12 +537,45 @@ metrics: value_type: int attributes: - condition + k8s.node.allocatable.cpu: + enabled: true + description: Amount of cpu allocatable on the node + unit: "{cpu}" + sum: + monotonic: false + value_type: double + k8s.node.allocatable.ephemeral_storage: + enabled: true + description: Amount of ephemeral-storage allocatable on the node + unit: "By" + sum: + monotonic: false + value_type: int + k8s.node.allocatable.storage: + enabled: true + description: Amount of storage allocatable on the node + unit: "By" + sum: + monotonic: false + value_type: int + k8s.node.allocatable.memory: + enabled: true + description: Amount of memory allocatable on the node + unit: "By" + sum: + monotonic: false + value_type: int + k8s.node.allocatable.pods: + enabled: true + description: Amount of pods allocatable on the node + unit: "{pod}" + sum: + monotonic: false + value_type: int + # k8s.node.condition_* metrics (k8s.node.condition_ready, k8s.node.condition_memory_pressure, etc) are controlled # by node_conditions_to_report config option. By default, only k8s.node.condition_ready is enabled. - # k8s.node.allocatable_* or k8s.node.allocatable.* metrics (k8s.node.allocatable_cpu/k8s.node.allocatable.cpu, k8s.node.allocatable_memory/k8s.node.allocatable.memory, etc) are controlled - # by allocatable_types_to_report config option. By default, none of them are reported. - tests: config: skip_lifecycle: true From e475da8bdc774fda7ac39344e9505f94ae5c17c0 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 9 Sep 2025 13:13:15 +0200 Subject: [PATCH 04/14] remove storage Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/documentation.md | 8 --- .../internal/metadata/generated_config.go | 4 -- .../metadata/generated_config_test.go | 2 - .../internal/metadata/generated_metrics.go | 63 ------------------- .../metadata/generated_metrics_test.go | 18 ------ .../internal/metadata/testdata/config.yaml | 4 -- .../k8sclusterreceiver/internal/node/nodes.go | 4 -- .../internal/node/nodes_test.go | 3 +- .../internal/node/testdata/expected.yaml | 6 -- .../node/testdata/expected_allocatable.yaml | 6 -- .../internal/testutils/objects.go | 1 - receiver/k8sclusterreceiver/metadata.yaml | 7 --- 12 files changed, 1 insertion(+), 125 deletions(-) diff --git a/receiver/k8sclusterreceiver/documentation.md b/receiver/k8sclusterreceiver/documentation.md index c79f19fbc39a8..f0602958fa1f9 100644 --- a/receiver/k8sclusterreceiver/documentation.md +++ b/receiver/k8sclusterreceiver/documentation.md @@ -260,14 +260,6 @@ Amount of pods allocatable on the node | ---- | ----------- | ---------- | ----------------------- | --------- | | {pod} | Sum | Int | Unspecified | false | -### k8s.node.allocatable.storage - -Amount of storage allocatable on the node - -| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | -| ---- | ----------- | ---------- | ----------------------- | --------- | -| By | Sum | Int | Unspecified | false | - ### k8s.pod.phase Current phase of the pod (1 - Pending, 2 - Running, 3 - Succeeded, 4 - Failed, 5 - Unknown) diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_config.go b/receiver/k8sclusterreceiver/internal/metadata/generated_config.go index 5d6e969c83455..aa501bc0686b7 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_config.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_config.go @@ -60,7 +60,6 @@ type MetricsConfig struct { K8sNodeAllocatableEphemeralStorage MetricConfig `mapstructure:"k8s.node.allocatable.ephemeral_storage"` K8sNodeAllocatableMemory MetricConfig `mapstructure:"k8s.node.allocatable.memory"` K8sNodeAllocatablePods MetricConfig `mapstructure:"k8s.node.allocatable.pods"` - K8sNodeAllocatableStorage MetricConfig `mapstructure:"k8s.node.allocatable.storage"` K8sNodeCondition MetricConfig `mapstructure:"k8s.node.condition"` K8sPodPhase MetricConfig `mapstructure:"k8s.pod.phase"` K8sPodStatusReason MetricConfig `mapstructure:"k8s.pod.status_reason"` @@ -178,9 +177,6 @@ func DefaultMetricsConfig() MetricsConfig { K8sNodeAllocatablePods: MetricConfig{ Enabled: true, }, - K8sNodeAllocatableStorage: MetricConfig{ - Enabled: true, - }, K8sNodeCondition: MetricConfig{ Enabled: false, }, diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go b/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go index 86ea92908533e..8f308ec83bea9 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_config_test.go @@ -59,7 +59,6 @@ func TestMetricsBuilderConfig(t *testing.T) { K8sNodeAllocatableEphemeralStorage: MetricConfig{Enabled: true}, K8sNodeAllocatableMemory: MetricConfig{Enabled: true}, K8sNodeAllocatablePods: MetricConfig{Enabled: true}, - K8sNodeAllocatableStorage: MetricConfig{Enabled: true}, K8sNodeCondition: MetricConfig{Enabled: true}, K8sPodPhase: MetricConfig{Enabled: true}, K8sPodStatusReason: MetricConfig{Enabled: true}, @@ -158,7 +157,6 @@ func TestMetricsBuilderConfig(t *testing.T) { K8sNodeAllocatableEphemeralStorage: MetricConfig{Enabled: false}, K8sNodeAllocatableMemory: MetricConfig{Enabled: false}, K8sNodeAllocatablePods: MetricConfig{Enabled: false}, - K8sNodeAllocatableStorage: MetricConfig{Enabled: false}, K8sNodeCondition: MetricConfig{Enabled: false}, K8sPodPhase: MetricConfig{Enabled: false}, K8sPodStatusReason: MetricConfig{Enabled: false}, diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go index 0de145991e4d0..fb52ca1fd56dc 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics.go @@ -140,9 +140,6 @@ var MetricsInfo = metricsInfo{ K8sNodeAllocatablePods: metricInfo{ Name: "k8s.node.allocatable.pods", }, - K8sNodeAllocatableStorage: metricInfo{ - Name: "k8s.node.allocatable.storage", - }, K8sNodeCondition: metricInfo{ Name: "k8s.node.condition", }, @@ -229,7 +226,6 @@ type metricsInfo struct { K8sNodeAllocatableEphemeralStorage metricInfo K8sNodeAllocatableMemory metricInfo K8sNodeAllocatablePods metricInfo - K8sNodeAllocatableStorage metricInfo K8sNodeCondition metricInfo K8sPodPhase metricInfo K8sPodStatusReason metricInfo @@ -1833,57 +1829,6 @@ func newMetricK8sNodeAllocatablePods(cfg MetricConfig) metricK8sNodeAllocatableP return m } -type metricK8sNodeAllocatableStorage struct { - data pmetric.Metric // data buffer for generated metric. - config MetricConfig // metric config provided by user. - capacity int // max observed number of data points added to the metric. -} - -// init fills k8s.node.allocatable.storage metric with initial data. -func (m *metricK8sNodeAllocatableStorage) init() { - m.data.SetName("k8s.node.allocatable.storage") - m.data.SetDescription("Amount of storage allocatable on the node") - m.data.SetUnit("By") - m.data.SetEmptySum() - m.data.Sum().SetIsMonotonic(false) - m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityUnspecified) -} - -func (m *metricK8sNodeAllocatableStorage) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { - if !m.config.Enabled { - return - } - dp := m.data.Sum().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 *metricK8sNodeAllocatableStorage) updateCapacity() { - if m.data.Sum().DataPoints().Len() > m.capacity { - m.capacity = m.data.Sum().DataPoints().Len() - } -} - -// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. -func (m *metricK8sNodeAllocatableStorage) emit(metrics pmetric.MetricSlice) { - if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { - m.updateCapacity() - m.data.MoveTo(metrics.AppendEmpty()) - m.init() - } -} - -func newMetricK8sNodeAllocatableStorage(cfg MetricConfig) metricK8sNodeAllocatableStorage { - m := metricK8sNodeAllocatableStorage{config: cfg} - if cfg.Enabled { - m.data = pmetric.NewMetric() - m.init() - } - return m -} - type metricK8sNodeCondition struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -2775,7 +2720,6 @@ type MetricsBuilder struct { metricK8sNodeAllocatableEphemeralStorage metricK8sNodeAllocatableEphemeralStorage metricK8sNodeAllocatableMemory metricK8sNodeAllocatableMemory metricK8sNodeAllocatablePods metricK8sNodeAllocatablePods - metricK8sNodeAllocatableStorage metricK8sNodeAllocatableStorage metricK8sNodeCondition metricK8sNodeCondition metricK8sPodPhase metricK8sPodPhase metricK8sPodStatusReason metricK8sPodStatusReason @@ -2850,7 +2794,6 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, opt metricK8sNodeAllocatableEphemeralStorage: newMetricK8sNodeAllocatableEphemeralStorage(mbc.Metrics.K8sNodeAllocatableEphemeralStorage), metricK8sNodeAllocatableMemory: newMetricK8sNodeAllocatableMemory(mbc.Metrics.K8sNodeAllocatableMemory), metricK8sNodeAllocatablePods: newMetricK8sNodeAllocatablePods(mbc.Metrics.K8sNodeAllocatablePods), - metricK8sNodeAllocatableStorage: newMetricK8sNodeAllocatableStorage(mbc.Metrics.K8sNodeAllocatableStorage), metricK8sNodeCondition: newMetricK8sNodeCondition(mbc.Metrics.K8sNodeCondition), metricK8sPodPhase: newMetricK8sPodPhase(mbc.Metrics.K8sPodPhase), metricK8sPodStatusReason: newMetricK8sPodStatusReason(mbc.Metrics.K8sPodStatusReason), @@ -3213,7 +3156,6 @@ func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { mb.metricK8sNodeAllocatableEphemeralStorage.emit(ils.Metrics()) mb.metricK8sNodeAllocatableMemory.emit(ils.Metrics()) mb.metricK8sNodeAllocatablePods.emit(ils.Metrics()) - mb.metricK8sNodeAllocatableStorage.emit(ils.Metrics()) mb.metricK8sNodeCondition.emit(ils.Metrics()) mb.metricK8sPodPhase.emit(ils.Metrics()) mb.metricK8sPodStatusReason.emit(ils.Metrics()) @@ -3422,11 +3364,6 @@ func (mb *MetricsBuilder) RecordK8sNodeAllocatablePodsDataPoint(ts pcommon.Times mb.metricK8sNodeAllocatablePods.recordDataPoint(mb.startTime, ts, val) } -// RecordK8sNodeAllocatableStorageDataPoint adds a data point to k8s.node.allocatable.storage metric. -func (mb *MetricsBuilder) RecordK8sNodeAllocatableStorageDataPoint(ts pcommon.Timestamp, val int64) { - mb.metricK8sNodeAllocatableStorage.recordDataPoint(mb.startTime, ts, val) -} - // RecordK8sNodeConditionDataPoint adds a data point to k8s.node.condition metric. func (mb *MetricsBuilder) RecordK8sNodeConditionDataPoint(ts pcommon.Timestamp, val int64, conditionAttributeValue string) { mb.metricK8sNodeCondition.recordDataPoint(mb.startTime, ts, val, conditionAttributeValue) diff --git a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go index 38cc241bb22ea..279bcbe61957d 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/k8sclusterreceiver/internal/metadata/generated_metrics_test.go @@ -195,10 +195,6 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordK8sNodeAllocatablePodsDataPoint(ts, 1) - defaultMetricsCount++ - allMetricsCount++ - mb.RecordK8sNodeAllocatableStorageDataPoint(ts, 1) - allMetricsCount++ mb.RecordK8sNodeConditionDataPoint(ts, 1, "condition-val") @@ -725,20 +721,6 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) - case "k8s.node.allocatable.storage": - assert.False(t, validatedMetrics["k8s.node.allocatable.storage"], "Found a duplicate in the metrics slice: k8s.node.allocatable.storage") - validatedMetrics["k8s.node.allocatable.storage"] = true - assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) - assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) - assert.Equal(t, "Amount of storage allocatable on the node", ms.At(i).Description()) - assert.Equal(t, "By", ms.At(i).Unit()) - assert.False(t, ms.At(i).Sum().IsMonotonic()) - assert.Equal(t, pmetric.AggregationTemporalityUnspecified, ms.At(i).Sum().AggregationTemporality()) - dp := ms.At(i).Sum().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 "k8s.node.condition": assert.False(t, validatedMetrics["k8s.node.condition"], "Found a duplicate in the metrics slice: k8s.node.condition") validatedMetrics["k8s.node.condition"] = true diff --git a/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml b/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml index 6ba0c866ef549..ebde0f1c8a793 100644 --- a/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/k8sclusterreceiver/internal/metadata/testdata/config.yaml @@ -65,8 +65,6 @@ all_set: enabled: true k8s.node.allocatable.pods: enabled: true - k8s.node.allocatable.storage: - enabled: true k8s.node.condition: enabled: true k8s.pod.phase: @@ -248,8 +246,6 @@ none_set: enabled: false k8s.node.allocatable.pods: enabled: false - k8s.node.allocatable.storage: - enabled: false k8s.node.condition: enabled: false k8s.pod.phase: diff --git a/receiver/k8sclusterreceiver/internal/node/nodes.go b/receiver/k8sclusterreceiver/internal/node/nodes.go index d346ca79fb603..fb4c48e75ff18 100644 --- a/receiver/k8sclusterreceiver/internal/node/nodes.go +++ b/receiver/k8sclusterreceiver/internal/node/nodes.go @@ -77,10 +77,6 @@ func RecordMetrics(mb *metadata.MetricsBuilder, node *corev1.Node, ts pcommon.Ti mb.RecordK8sNodeAllocatableEphemeralStorageDataPoint(ts, ephemeralMemoryVal.Value()) } - if storageVal, ok := node.Status.Allocatable[corev1.ResourceStorage]; ok { - mb.RecordK8sNodeAllocatableStorageDataPoint(ts, storageVal.Value()) - } - if memoryVal, ok := node.Status.Allocatable[corev1.ResourceMemory]; ok { mb.RecordK8sNodeAllocatableMemoryDataPoint(ts, memoryVal.Value()) } diff --git a/receiver/k8sclusterreceiver/internal/node/nodes_test.go b/receiver/k8sclusterreceiver/internal/node/nodes_test.go index 0b75e213769c7..74b173e163468 100644 --- a/receiver/k8sclusterreceiver/internal/node/nodes_test.go +++ b/receiver/k8sclusterreceiver/internal/node/nodes_test.go @@ -65,7 +65,7 @@ func TestNodeMetricsReportCPUMetrics(t *testing.T) { ) } -func TestNodeMetricsReportCPUMetricsAllocatableNamespace(t *testing.T) { +func TestNodeAllocatableNamespaceMetrics(t *testing.T) { require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), true)) defer func() { require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), false)) @@ -77,7 +77,6 @@ func TestNodeMetricsReportCPUMetricsAllocatableNamespace(t *testing.T) { mbc.Metrics.K8sNodeAllocatableCPU.Enabled = true mbc.Metrics.K8sNodeAllocatableMemory.Enabled = true mbc.Metrics.K8sNodeAllocatableEphemeralStorage.Enabled = true - mbc.Metrics.K8sNodeAllocatableStorage.Enabled = true mbc.Metrics.K8sNodeAllocatablePods.Enabled = true mb := metadata.NewMetricsBuilder(mbc, receivertest.NewNopSettings(metadata.Type)) RecordMetrics(mb, n, ts) diff --git a/receiver/k8sclusterreceiver/internal/node/testdata/expected.yaml b/receiver/k8sclusterreceiver/internal/node/testdata/expected.yaml index 904e6e8ea1120..7e63aac6f972d 100644 --- a/receiver/k8sclusterreceiver/internal/node/testdata/expected.yaml +++ b/receiver/k8sclusterreceiver/internal/node/testdata/expected.yaml @@ -22,12 +22,6 @@ resourceMetrics: - asInt: "1234" name: k8s.node.allocatable_ephemeral_storage unit: By - - description: Amount of storage allocatable on the node - gauge: - dataPoints: - - asInt: "34" - name: k8s.node.allocatable_storage - unit: By - description: Amount of memory allocatable on the node gauge: dataPoints: diff --git a/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml b/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml index b981417e6c78e..ce2d207fc67b7 100644 --- a/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml +++ b/receiver/k8sclusterreceiver/internal/node/testdata/expected_allocatable.yaml @@ -22,12 +22,6 @@ resourceMetrics: - asInt: "1234" name: k8s.node.allocatable.ephemeral_storage unit: By - - description: Amount of storage allocatable on the node - sum: - dataPoints: - - asInt: "34" - name: k8s.node.allocatable.storage - unit: By - description: Amount of memory allocatable on the node sum: dataPoints: diff --git a/receiver/k8sclusterreceiver/internal/testutils/objects.go b/receiver/k8sclusterreceiver/internal/testutils/objects.go index c908fd93f8da2..3161879bbd832 100644 --- a/receiver/k8sclusterreceiver/internal/testutils/objects.go +++ b/receiver/k8sclusterreceiver/internal/testutils/objects.go @@ -202,7 +202,6 @@ func NewNode(id string) *corev1.Node { corev1.ResourceCPU: *resource.NewMilliQuantity(123, resource.DecimalSI), corev1.ResourceMemory: *resource.NewQuantity(456, resource.DecimalSI), corev1.ResourceEphemeralStorage: *resource.NewQuantity(1234, resource.DecimalSI), - corev1.ResourceStorage: *resource.NewQuantity(34, resource.DecimalSI), corev1.ResourcePods: *resource.NewQuantity(12, resource.DecimalSI), "hugepages-1Gi": *resource.NewQuantity(2, resource.DecimalSI), "hugepages-2Mi": *resource.NewQuantity(2048, resource.DecimalSI), diff --git a/receiver/k8sclusterreceiver/metadata.yaml b/receiver/k8sclusterreceiver/metadata.yaml index b299cfcc804e1..ea86ef41b446e 100644 --- a/receiver/k8sclusterreceiver/metadata.yaml +++ b/receiver/k8sclusterreceiver/metadata.yaml @@ -551,13 +551,6 @@ metrics: sum: monotonic: false value_type: int - k8s.node.allocatable.storage: - enabled: true - description: Amount of storage allocatable on the node - unit: "By" - sum: - monotonic: false - value_type: int k8s.node.allocatable.memory: enabled: true description: Amount of memory allocatable on the node From 894ee22b058654f343e12aa905f667dc4478f8fc Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 9 Sep 2025 13:27:26 +0200 Subject: [PATCH 05/14] adapt readme Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/receiver/k8sclusterreceiver/README.md b/receiver/k8sclusterreceiver/README.md index a0ebb903a5610..85129a18d3c40 100644 --- a/receiver/k8sclusterreceiver/README.md +++ b/receiver/k8sclusterreceiver/README.md @@ -60,6 +60,9 @@ The following allocatable resource types are available. - ephemeral-storage - storage - pods + Note that with the introduction of the [receiver.k8scluster.allocatableNamespace.enabled](#receiverk8sclusterallocatablenamespaceenabled) feature gate, the metrics for the allocatable resource types + (`k8s.node.allocatable.cpu`, `k8s.node.allocatable.ephemeral_storage`, `k8s.node.allocatable.memory`, `k8s.node.allocatable.pods`) are enabled/disabled via the metrics section, and are represented by up/down counters, rather than gauges. + To activate the feature flag, start the collector with `--feature-gates receiver.k8scluster.allocatableNamespace.enabled`. - `metrics`: Allows to enable/disable metrics. - `resource_attributes`: Allows to enable/disable resource attributes. - `namespace` (deprecated, use `namespaces` instead): Allows to observe resources for a particular namespace only. If this option is set to a non-empty string, `Nodes`, `Namespaces` and `ClusterResourceQuotas` will not be observed. @@ -446,23 +449,21 @@ Add the following rules to your ClusterRole: ## Feature Gates -### `k8scluster.allocatableNamespace.enabled` +### `receiver.k8scluster.allocatableNamespace.enabled` -The `k8scluster.allocatableNamespace.enabled` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) enables the SemConv valid format of the node allocatable metrics reported by the receiver. +The `receiver.k8scluster.allocatableNamespace.enabled` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) enables the SemConv valid format of the node allocatable metrics reported by the receiver. The feature gate is in `alpha` stage, which means it is disabled by default. -If enabled the SemConv valid format of the node allocatable metrics are reported (if enabled): +If enabled the SemConv valid format of the node allocatable metrics are reported (if enabled via the metrics section): - `k8s.node.allocatable.cpu` - `k8s.node.allocatable.ephemeral_storage` -- `k8s.node.allocatable.storage` - `k8s.node.allocatable.memory` - `k8s.node.allocatable.pods` -instead of the original version: +instead, the old metrics are disabled and not reported (even if `allocatable_types_to_report` config option is set): - `k8s.node.allocatable_cpu` - `k8s.node.allocatable_ephemeral_storage` -- `k8s.node.allocatable_storage` - `k8s.node.allocatable_memory` - `k8s.node.allocatable_pods` From 8989cc25f99877e2e693d297e5039e7768a5a87f Mon Sep 17 00:00:00 2001 From: odubajDT Date: Wed, 17 Sep 2025 09:06:52 +0200 Subject: [PATCH 06/14] go mod tidy Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/k8sclusterreceiver/go.mod b/receiver/k8sclusterreceiver/go.mod index 5583d607ecea9..fee4749f5329d 100644 --- a/receiver/k8sclusterreceiver/go.mod +++ b/receiver/k8sclusterreceiver/go.mod @@ -25,7 +25,7 @@ require ( go.opentelemetry.io/collector/confmap/xconfmap v0.135.1-0.20250911155607-37a3ace6274c go.opentelemetry.io/collector/consumer v1.41.1-0.20250911155607-37a3ace6274c go.opentelemetry.io/collector/consumer/consumertest v0.135.1-0.20250911155607-37a3ace6274c - go.opentelemetry.io/collector/featuregate v1.41.1-0.20250911155607-37a3ace6274c + go.opentelemetry.io/collector/featuregate v1.41.1-0.20250911155607-37a3ace6274c go.opentelemetry.io/collector/filter v0.135.1-0.20250911155607-37a3ace6274c go.opentelemetry.io/collector/pdata v1.41.1-0.20250911155607-37a3ace6274c go.opentelemetry.io/collector/pipeline v1.41.1-0.20250911155607-37a3ace6274c From ed865a80ad18a92de5696f4de42bd63fb51c9264 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Wed, 24 Sep 2025 15:13:49 +0200 Subject: [PATCH 07/14] fix Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/k8sclusterreceiver/go.mod b/receiver/k8sclusterreceiver/go.mod index 3fcaf9b3b8acb..652082991d13c 100644 --- a/receiver/k8sclusterreceiver/go.mod +++ b/receiver/k8sclusterreceiver/go.mod @@ -25,7 +25,7 @@ require ( go.opentelemetry.io/collector/confmap/xconfmap v0.136.0 go.opentelemetry.io/collector/consumer v1.42.0 go.opentelemetry.io/collector/consumer/consumertest v0.136.0 - go.opentelemetry.io/collector/featuregate v1.42.0 + go.opentelemetry.io/collector/featuregate v1.42.0 go.opentelemetry.io/collector/filter v0.136.0 go.opentelemetry.io/collector/pdata v1.42.0 go.opentelemetry.io/collector/pipeline v1.42.0 From 6a400d6691d203f2817e76cf2ad17d8be31af0da Mon Sep 17 00:00:00 2001 From: odubajDT <93584209+odubajDT@users.noreply.github.com> Date: Mon, 13 Oct 2025 13:27:17 +0200 Subject: [PATCH 08/14] Apply suggestions from code review --- receiver/k8sclusterreceiver/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/k8sclusterreceiver/go.mod b/receiver/k8sclusterreceiver/go.mod index cc37ec3cb467c..933708d7d5b3b 100644 --- a/receiver/k8sclusterreceiver/go.mod +++ b/receiver/k8sclusterreceiver/go.mod @@ -25,7 +25,7 @@ require ( go.opentelemetry.io/collector/confmap/xconfmap v0.137.0 go.opentelemetry.io/collector/consumer v1.43.0 go.opentelemetry.io/collector/consumer/consumertest v0.137.0 - go.opentelemetry.io/collector/featuregate v1.43.0 + go.opentelemetry.io/collector/featuregate v1.43.0 go.opentelemetry.io/collector/filter v0.137.0 go.opentelemetry.io/collector/pdata v1.43.0 go.opentelemetry.io/collector/pipeline v1.43.0 From 937fc2245be655de29cea100055d3b922316566e Mon Sep 17 00:00:00 2001 From: odubajDT <93584209+odubajDT@users.noreply.github.com> Date: Tue, 21 Oct 2025 07:47:38 +0200 Subject: [PATCH 09/14] Update receiver/k8sclusterreceiver/metadata.yaml --- receiver/k8sclusterreceiver/metadata.yaml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/receiver/k8sclusterreceiver/metadata.yaml b/receiver/k8sclusterreceiver/metadata.yaml index 2dd7a9cc09f32..26f713c30f5ce 100644 --- a/receiver/k8sclusterreceiver/metadata.yaml +++ b/receiver/k8sclusterreceiver/metadata.yaml @@ -659,16 +659,6 @@ metrics: value_type: int attributes: - resource - k8s.node.condition: - enabled: false - description: The condition of a particular Node. - unit: "{condition}" - stability: - level: development - gauge: - value_type: int - attributes: - - condition k8s.node.allocatable.cpu: enabled: true description: Amount of cpu allocatable on the node From 207037a6a3d1977c8306ca9e4537c2fa90f94b93 Mon Sep 17 00:00:00 2001 From: odubajDT <93584209+odubajDT@users.noreply.github.com> Date: Tue, 21 Oct 2025 07:52:14 +0200 Subject: [PATCH 10/14] Update receiver/k8sclusterreceiver/go.mod --- receiver/k8sclusterreceiver/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/k8sclusterreceiver/go.mod b/receiver/k8sclusterreceiver/go.mod index 55462bc9a95ff..1d70e14e4aa5f 100644 --- a/receiver/k8sclusterreceiver/go.mod +++ b/receiver/k8sclusterreceiver/go.mod @@ -25,7 +25,7 @@ require ( go.opentelemetry.io/collector/confmap/xconfmap v0.138.0 go.opentelemetry.io/collector/consumer v1.44.0 go.opentelemetry.io/collector/consumer/consumertest v0.138.0 - go.opentelemetry.io/collector/featuregate v1.44.0 + go.opentelemetry.io/collector/featuregate v1.44.0 go.opentelemetry.io/collector/filter v0.138.0 go.opentelemetry.io/collector/pdata v1.44.0 go.opentelemetry.io/collector/pipeline v1.44.0 From e0c2b3ced04876104984c81bf254f11caa158a1f Mon Sep 17 00:00:00 2001 From: odubajDT <93584209+odubajDT@users.noreply.github.com> Date: Tue, 21 Oct 2025 07:54:06 +0200 Subject: [PATCH 11/14] Update .chloggen/k8scluster-allocatable.yaml --- .chloggen/k8scluster-allocatable.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/k8scluster-allocatable.yaml b/.chloggen/k8scluster-allocatable.yaml index 6cb9b00a9df76..e6215bc8e1de9 100644 --- a/.chloggen/k8scluster-allocatable.yaml +++ b/.chloggen/k8scluster-allocatable.yaml @@ -4,7 +4,7 @@ change_type: enhancement # The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) -component: receiver/k8scluster +component: receiver/k8s_cluster # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). note: "Sync allocatable metric names with the latest semconv" From 64c29d493524085fa1f61d6404919177dee53e27 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 21 Oct 2025 08:00:21 +0200 Subject: [PATCH 12/14] go mod tidy Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/receiver/k8sclusterreceiver/go.mod b/receiver/k8sclusterreceiver/go.mod index 1d70e14e4aa5f..88c2b646b08c4 100644 --- a/receiver/k8sclusterreceiver/go.mod +++ b/receiver/k8sclusterreceiver/go.mod @@ -25,7 +25,7 @@ require ( go.opentelemetry.io/collector/confmap/xconfmap v0.138.0 go.opentelemetry.io/collector/consumer v1.44.0 go.opentelemetry.io/collector/consumer/consumertest v0.138.0 - go.opentelemetry.io/collector/featuregate v1.44.0 + go.opentelemetry.io/collector/featuregate v1.44.0 go.opentelemetry.io/collector/filter v0.138.0 go.opentelemetry.io/collector/pdata v1.44.0 go.opentelemetry.io/collector/pipeline v1.44.0 From 317b27af639501728bad4d1cf97fcbb235b43667 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Tue, 21 Oct 2025 08:42:09 +0200 Subject: [PATCH 13/14] sort metadata.yaml Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/metadata.yaml | 63 ++++++++++++----------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/receiver/k8sclusterreceiver/metadata.yaml b/receiver/k8sclusterreceiver/metadata.yaml index 26f713c30f5ce..6882cb3077e8f 100644 --- a/receiver/k8sclusterreceiver/metadata.yaml +++ b/receiver/k8sclusterreceiver/metadata.yaml @@ -492,6 +492,38 @@ metrics: level: development gauge: value_type: int + + k8s.node.allocatable.cpu: + enabled: true + description: Amount of cpu allocatable on the node + unit: "{cpu}" + sum: + monotonic: false + value_type: double + k8s.node.allocatable.ephemeral_storage: + enabled: true + description: Amount of ephemeral-storage allocatable on the node + unit: "By" + sum: + monotonic: false + value_type: int + k8s.node.allocatable.memory: + enabled: true + description: Amount of memory allocatable on the node + unit: "By" + sum: + monotonic: false + value_type: int + k8s.node.allocatable.pods: + enabled: true + description: Amount of pods allocatable on the node + unit: "{pod}" + sum: + monotonic: false + value_type: int + + # k8s.node.condition_* metrics (k8s.node.condition_ready, k8s.node.condition_memory_pressure, etc) are controlled + # by node_conditions_to_report config option. By default, only k8s.node.condition_ready is enabled. k8s.node.condition: enabled: false @@ -659,37 +691,6 @@ metrics: value_type: int attributes: - resource - k8s.node.allocatable.cpu: - enabled: true - description: Amount of cpu allocatable on the node - unit: "{cpu}" - sum: - monotonic: false - value_type: double - k8s.node.allocatable.ephemeral_storage: - enabled: true - description: Amount of ephemeral-storage allocatable on the node - unit: "By" - sum: - monotonic: false - value_type: int - k8s.node.allocatable.memory: - enabled: true - description: Amount of memory allocatable on the node - unit: "By" - sum: - monotonic: false - value_type: int - k8s.node.allocatable.pods: - enabled: true - description: Amount of pods allocatable on the node - unit: "{pod}" - sum: - monotonic: false - value_type: int - - # k8s.node.condition_* metrics (k8s.node.condition_ready, k8s.node.condition_memory_pressure, etc) are controlled - # by node_conditions_to_report config option. By default, only k8s.node.condition_ready is enabled. tests: config: From 679c2dd4313e49223d15f67ecb0cc6b55fb46ef6 Mon Sep 17 00:00:00 2001 From: odubajDT Date: Thu, 23 Oct 2025 09:21:34 +0200 Subject: [PATCH 14/14] use semconv feature gates Signed-off-by: odubajDT --- receiver/k8sclusterreceiver/README.md | 18 ++++++++----- .../k8sclusterreceiver/internal/node/nodes.go | 13 +++------- .../internal/node/nodes_test.go | 5 ++-- .../k8sclusterreceiver/internal/utils/set.go | 16 ++++++++++++ receiver/k8sclusterreceiver/receiver.go | 5 ++++ receiver/k8sclusterreceiver/receiver_test.go | 26 +++++++++++++++++++ 6 files changed, 65 insertions(+), 18 deletions(-) diff --git a/receiver/k8sclusterreceiver/README.md b/receiver/k8sclusterreceiver/README.md index 64ba2392da40c..c213dc765bae1 100644 --- a/receiver/k8sclusterreceiver/README.md +++ b/receiver/k8sclusterreceiver/README.md @@ -93,9 +93,10 @@ Example: The full list of settings exposed for this receiver are documented in [config.go](./config.go) with detailed sample configurations in [testdata/config.yaml](./testdata/config.yaml). -**Note** that with the introduction of the [receiver.k8scluster.allocatableNamespace.enabled](#receiverk8sclusterallocatablenamespaceenabled) feature gate, the metrics for the allocatable resource types +**Note** that with the introduction of the [semconv.k8s.enableStable](#semconvk8senablestable) feature gate, the metrics for the allocatable resource types (`k8s.node.allocatable.cpu`, `k8s.node.allocatable.ephemeral_storage`, `k8s.node.allocatable.memory`, `k8s.node.allocatable.pods`) are enabled/disabled via the metrics section, and are represented by up/down counters, rather than gauges. -To activate the feature flag, start the collector with `--feature-gates receiver.k8scluster.allocatableNamespace.enabled`. +To activate the feature flag, start the collector with `--feature-gates=+semconv.k8s.enableStable`. +To disable the old representation of the allocatable metrics (`k8s.node.allocatable_cpu`, `k8s.node.allocatable_ephemeral_storage`, `k8s.node.allocatable_memory`, `k8s.node.allocatable_pods`) disable the [semconv.k8s.disableLegacy](#semconvk8sdisablelegacy) feature flag with `--feature-gates=-semconv.k8s.disableLegacy` ### k8s_leader_elector Provide name of the k8s leader elector extension defined in config. This allows multiple instances of k8s cluster @@ -459,19 +460,24 @@ Add the following rules to your ClusterRole: ## Feature Gates -### `receiver.k8scluster.allocatableNamespace.enabled` +### `semconv.k8s.enableStable` -The `receiver.k8scluster.allocatableNamespace.enabled` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) enables the SemConv valid format of the node allocatable metrics reported by the receiver. +The `semconv.k8s.enableStable` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) enables the SemConv valid format of the node allocatable metrics reported by the receiver. The feature gate is in `alpha` stage, which means it is disabled by default. -If enabled the SemConv valid format of the node allocatable metrics are reported (if enabled via the metrics section): +If enabled, the SemConv valid format of the node allocatable metrics are reported (if enabled via the metrics section): - `k8s.node.allocatable.cpu` - `k8s.node.allocatable.ephemeral_storage` - `k8s.node.allocatable.memory` - `k8s.node.allocatable.pods` -instead, the old metrics are disabled and not reported (even if `allocatable_types_to_report` config option is set): +### `semconv.k8s.disableLegacy` + +The `semconv.k8s.disableLegacy` [feature gate](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) disables the old, non-SemConv valid format of the node allocatable metrics reported by the receiver. +The feature gate is in `alpha` stage, which means it is disabled by default. + +If disabled, the old format of the node allocatable metrics are reported: - `k8s.node.allocatable_cpu` - `k8s.node.allocatable_ephemeral_storage` diff --git a/receiver/k8sclusterreceiver/internal/node/nodes.go b/receiver/k8sclusterreceiver/internal/node/nodes.go index fb4c48e75ff18..c681c8feb75a5 100644 --- a/receiver/k8sclusterreceiver/internal/node/nodes.go +++ b/receiver/k8sclusterreceiver/internal/node/nodes.go @@ -9,7 +9,6 @@ import ( "time" "github.com/iancoleman/strcase" - "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver" @@ -20,6 +19,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/maps" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/utils" ) const ( @@ -28,13 +28,6 @@ const ( k8sNodeConditionPrefix = "k8s.node.condition" ) -var allowAllocatableNamespace = featuregate.GlobalRegistry().MustRegister( - "receiver.k8scluster.allocatableNamespace.enabled", - featuregate.StageAlpha, - featuregate.WithRegisterDescription("When enabled, allocatable metrics are reported under allocatable namespace: with '.' instead of '_'"), - featuregate.WithRegisterFromVersion("v0.136.0"), -) - // Transform transforms the node to remove the fields that we don't use to reduce RAM utilization. // IMPORTANT: Make sure to update this function before using new node fields. func Transform(node *corev1.Node) *corev1.Node { @@ -68,7 +61,7 @@ func RecordMetrics(mb *metadata.MetricsBuilder, node *corev1.Node, ts pcommon.Ti rb.SetK8sNodeName(node.Name) rb.SetK8sKubeletVersion(node.Status.NodeInfo.KubeletVersion) - if allowAllocatableNamespace.IsEnabled() { + if utils.EnableStableMetrics.IsEnabled() { if cpuVal, ok := node.Status.Allocatable[corev1.ResourceCPU]; ok { mb.RecordK8sNodeAllocatableCPUDataPoint(ts, float64(cpuVal.MilliValue())/1000.0) } @@ -109,7 +102,7 @@ func CustomMetrics(set receiver.Settings, rb *metadata.ResourceBuilder, node *co } // Adding 'node allocatable type' metrics - if !allowAllocatableNamespace.IsEnabled() { + if !utils.DisableLegacyMetrics.IsEnabled() { for _, nodeAllocatableTypeValue := range allocatableTypesToReport { v1NodeAllocatableTypeValue := corev1.ResourceName(nodeAllocatableTypeValue) quantity, ok := node.Status.Allocatable[v1NodeAllocatableTypeValue] diff --git a/receiver/k8sclusterreceiver/internal/node/nodes_test.go b/receiver/k8sclusterreceiver/internal/node/nodes_test.go index 74b173e163468..fc9be5a9189a7 100644 --- a/receiver/k8sclusterreceiver/internal/node/nodes_test.go +++ b/receiver/k8sclusterreceiver/internal/node/nodes_test.go @@ -24,6 +24,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/testutils" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/utils" ) func TestNodeMetricsReportCPUMetrics(t *testing.T) { @@ -66,9 +67,9 @@ func TestNodeMetricsReportCPUMetrics(t *testing.T) { } func TestNodeAllocatableNamespaceMetrics(t *testing.T) { - require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), true)) + require.NoError(t, featuregate.GlobalRegistry().Set(utils.EnableStableMetrics.ID(), true)) defer func() { - require.NoError(t, featuregate.GlobalRegistry().Set(allowAllocatableNamespace.ID(), false)) + require.NoError(t, featuregate.GlobalRegistry().Set(utils.EnableStableMetrics.ID(), false)) }() n := testutils.NewNode("1") diff --git a/receiver/k8sclusterreceiver/internal/utils/set.go b/receiver/k8sclusterreceiver/internal/utils/set.go index 2758d5792b4c3..f233caa7b24bb 100644 --- a/receiver/k8sclusterreceiver/internal/utils/set.go +++ b/receiver/k8sclusterreceiver/internal/utils/set.go @@ -3,6 +3,22 @@ package utils // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/utils" +import "go.opentelemetry.io/collector/featuregate" + +var EnableStableMetrics = featuregate.GlobalRegistry().MustRegister( + "semconv.k8s.enableStable", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("When enabled, semconv stable metrics are enabled."), + featuregate.WithRegisterFromVersion("v0.139.0"), +) + +var DisableLegacyMetrics = featuregate.GlobalRegistry().MustRegister( + "semconv.k8s.disableLegacy", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("When enabled, semconv legacy metrics are disabled."), + featuregate.WithRegisterFromVersion("v0.139.0"), +) + // StringSliceToMap converts a slice of strings into a map with keys from the slice func StringSliceToMap(strings []string) map[string]bool { ret := map[string]bool{} diff --git a/receiver/k8sclusterreceiver/receiver.go b/receiver/k8sclusterreceiver/receiver.go index d06cb6a9c513c..fefda9a5a550e 100644 --- a/receiver/k8sclusterreceiver/receiver.go +++ b/receiver/k8sclusterreceiver/receiver.go @@ -19,6 +19,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/extension/k8sleaderelector" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/collection" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/utils" ) const ( @@ -103,6 +104,10 @@ func (kr *kubernetesReceiver) startReceiver(ctx context.Context, host component. func (kr *kubernetesReceiver) Start(ctx context.Context, host component.Host) error { ctx, kr.cancel = context.WithCancel(ctx) + if utils.DisableLegacyMetrics.IsEnabled() && !utils.EnableStableMetrics.IsEnabled() { + return errors.New("cannot disable legacy metrics without enabling stable metrics") + } + // if extension is defined start with k8s leader elector if kr.config.K8sLeaderElector != nil { kr.settings.Logger.Info("Starting k8sClusterReceiver with leader election") diff --git a/receiver/k8sclusterreceiver/receiver_test.go b/receiver/k8sclusterreceiver/receiver_test.go index 94b666122d1a0..3e879a13f83f4 100644 --- a/receiver/k8sclusterreceiver/receiver_test.go +++ b/receiver/k8sclusterreceiver/receiver_test.go @@ -17,6 +17,7 @@ import ( "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/featuregate" "go.opentelemetry.io/collector/pipeline" "go.opentelemetry.io/collector/receiver" semconv "go.opentelemetry.io/otel/semconv/v1.27.0" @@ -30,6 +31,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/gvk" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/metadata" + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8sclusterreceiver/internal/utils" ) type nopHost struct { @@ -231,6 +233,30 @@ func TestNamespacedReceiverWithMultipleNamespaces(t *testing.T) { require.NoError(t, r.Shutdown(ctx)) } +func TestReceiverFeatureGatesCombination(t *testing.T) { + require.NoError(t, featuregate.GlobalRegistry().Set(utils.DisableLegacyMetrics.ID(), true)) + defer func() { + require.NoError(t, featuregate.GlobalRegistry().Set(utils.DisableLegacyMetrics.ID(), false)) + }() + + tt := componenttest.NewTelemetry() + defer func() { + require.NoError(t, tt.Shutdown(t.Context())) + }() + + client := newFakeClientWithAllResources() + osQuotaClient := fakeQuota.NewSimpleClientset() + sink := new(consumertest.MetricsSink) + + observedNamespaces := []string{"test-0", "test-1"} + r := setupReceiver(client, osQuotaClient, sink, nil, 10*time.Second, tt, observedNamespaces, component.MustNewID("foo")) + + ctx := t.Context() + require.ErrorContains(t, r.Start(ctx, newNopHost()), "cannot disable legacy metrics without enabling stable metrics") + + require.NoError(t, r.Shutdown(ctx)) +} + func TestReceiverTimesOutAfterStartup(t *testing.T) { tt := componenttest.NewTelemetry() defer func() {