From 109d2133df6e39fceb5520c62c8a329bdf631f41 Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 09:50:33 -0700 Subject: [PATCH 1/9] remove kubeletstats config converter --- ...ve-kubeletstats-utilization-converter.yaml | 30 ++++ CHANGELOG.md | 3 + .../disable_kubelet_utilization_metrics.go | 144 ------------------ ...isable_kubelet_utilization_metrics_test.go | 84 ---------- ...metrics_included_in_signalfx_exporter.yaml | 30 ---- .../disable_all_metrics_input.yaml | 27 ---- .../disable_all_metrics_output.yaml | 34 ----- .../do_not_change_enabled_metrics_input.yaml | 30 ---- .../do_not_change_enabled_metrics_output.yaml | 34 ----- .../no_kubeletstats_receiver.yaml | 25 --- .../no_signalfx_exporter.yaml | 28 ---- ...y_excluded_in_signalfx_exporter_input.yaml | 29 ---- ..._excluded_in_signalfx_exporter_output.yaml | 34 ----- ...y_included_in_signalfx_exporter_input.yaml | 29 ---- ..._included_in_signalfx_exporter_output.yaml | 34 ----- .../utilization_metrics_disabled.yaml | 35 ----- internal/settings/settings.go | 1 - 17 files changed, 33 insertions(+), 598 deletions(-) create mode 100644 .chloggen/remove-kubeletstats-utilization-converter.yaml delete mode 100644 internal/configconverter/disable_kubelet_utilization_metrics.go delete mode 100644 internal/configconverter/disable_kubelet_utilization_metrics_test.go delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/all_metrics_included_in_signalfx_exporter.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/disable_all_metrics_input.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/disable_all_metrics_output.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_input.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_output.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/no_kubeletstats_receiver.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/no_signalfx_exporter.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_input.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_output.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_input.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_output.yaml delete mode 100644 internal/configconverter/testdata/disable_kubelet_utilization_metrics/utilization_metrics_disabled.yaml diff --git a/.chloggen/remove-kubeletstats-utilization-converter.yaml b/.chloggen/remove-kubeletstats-utilization-converter.yaml new file mode 100644 index 0000000000..ec2a8a39cf --- /dev/null +++ b/.chloggen/remove-kubeletstats-utilization-converter.yaml @@ -0,0 +1,30 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: breaking + +# The name of the component, or a single word describing the area of concern, (e.g. crosslink) +component: receiver/kubeletstats + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Remove deprecated kubeletstats CPU utilization metrics from the collector's config converter + +# One or more tracking issues related to the change +issues: [] + +# (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: | + The kubeletstats receiver deprecated CPU utilization metrics have been completely removed in + OpenTelemetry Collector Contrib v0.136.0, along with the `receiver.kubeletstats.enableCPUUsageMetrics` + feature gate. Users who were explicitly including these metrics in their SignalFx exporter + `include_metrics` configuration need to update their configs: + + - `container.cpu.utilization` → `container.cpu.usage` + - `k8s.node.cpu.utilization` → `k8s.node.cpu.usage` + - `k8s.pod.cpu.utilization` → `k8s.pod.cpu.usage` + + Note: The new `.cpu.usage` metrics show usage counted in CPUs (not a percentage of used resources). + The previous metrics were incorrectly named using the "utilization" term. + + The Splunk distribution's config converter that automatically disabled these deprecated + metrics has been removed to prevent collector startup failures. diff --git a/CHANGELOG.md b/CHANGELOG.md index 4054e29c32..6985ebebd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ and the [opentelemetry-collector-contrib v0.136.0](https://github.com/open-telem - (Contrib) `pkg/ottl`: Upgrade profiles proto to 1.8.0 ([#42526](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/42526)) See [proto changelog](https://github.com/open-telemetry/opentelemetry-proto/blob/main/CHANGELOG.md#180---2025-09-02). - (Contrib) `transformprocessor`: Upgrade profiles proto to 1.8.0 ([#42526](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/42526)) +- (Contrib) `kubeletstatsreceiver`: Remove deprecated CPU utilization metrics and feature gate ([#42727](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/42727)) + The deprecated `*.cpu.utilization` metrics and `receiver.kubeletstats.enableCPUUsageMetrics` feature gate + have been removed. Use the corresponding `*.cpu.usage` metrics instead. ### 💡 Enhancements 💡 diff --git a/internal/configconverter/disable_kubelet_utilization_metrics.go b/internal/configconverter/disable_kubelet_utilization_metrics.go deleted file mode 100644 index 7192b5355e..0000000000 --- a/internal/configconverter/disable_kubelet_utilization_metrics.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright Splunk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configconverter - -import ( - "context" - "fmt" - "regexp" - - sfxpb "github.com/signalfx/com_signalfx_metrics_protobuf/model" - "go.opentelemetry.io/collector/confmap" - - "github.com/signalfx/splunk-otel-collector/internal/configconverter/dpfilters" -) - -// Metrics deprecated by kubeletstats receiver that need to be disabled if not explicitly included in signalfx exporter. -const ( - k8sNodeCPUUtilization = "k8s.node.cpu.utilization" - k8sPodCPUUtilization = "k8s.pod.cpu.utilization" - containerCPUUtilization = "container.cpu.utilization" -) - -// signalfxExporterConfig is the configuration for the signalfx exporter that contains the metrics filters. -type signalfxExporterConfig struct { - ExcludeMetrics []dpfilters.MetricFilter `mapstructure:"exclude_metrics"` - IncludeMetrics []dpfilters.MetricFilter `mapstructure:"include_metrics"` -} - -// DisableKubeletUtilizationMetrics disables the following deprecated metrics: -// - `k8s.node.cpu.utilization` -// - `k8s.pod.cpu.utilization` -// - `container.cpu.utilization` -// The converter disables the metrics at the receiver level to avoid showing users a warning message because -// they are excluded in signalfx exporter by default. -// We don't disable them in case if users explicitly include them in signalfx exporter. -func DisableKubeletUtilizationMetrics(_ context.Context, cfgMap *confmap.Conf) error { - if cfgMap == nil { - return fmt.Errorf("cannot DisableKubeletUtilizationMetrics on nil *confmap.Conf") - } - - receivers, err := cfgMap.Sub("receivers") - if err != nil { - return nil // Ignore invalid config. Rely on the config validation to catch this. - } - kubeletReceiverConfigs := map[string]map[string]any{} - for receiverName, receiverCfg := range receivers.ToStringMap() { - if regexp.MustCompile(`kubeletstats(/\w+)?`).MatchString(receiverName) { - if v, ok := receiverCfg.(map[string]any); ok { - kubeletReceiverConfigs[receiverName] = v - } - } - } - - exporters, err := cfgMap.Sub("exporters") - if err != nil { - return nil // Ignore invalid config. Rely on the config validation to catch this. - } - sfxExporterConfigs := map[string]map[string]any{} - for exporterName, exporterCfg := range exporters.ToStringMap() { - if regexp.MustCompile(`signalfx(/\w+)?`).MatchString(exporterName) { - if v, ok := exporterCfg.(map[string]any); ok { - sfxExporterConfigs[exporterName] = v - } - } - } - - // If there is no signalfx exporter or kubeletstats receiver, there is nothing to do. - if len(kubeletReceiverConfigs) == 0 || len(sfxExporterConfigs) == 0 { - return nil - } - - disableMetrics := map[string]bool{ - k8sNodeCPUUtilization: true, - k8sPodCPUUtilization: true, - containerCPUUtilization: true, - } - - // Check if the metrics are explicitly included in signalfx exporter. - // If they are not included, we will disable them in kubeletstats receiver. - for _, cm := range sfxExporterConfigs { - cfg := signalfxExporterConfig{} - err = confmap.NewFromStringMap(cm).Unmarshal(&cfg, confmap.WithIgnoreUnused()) - if err != nil { - return nil // Ignore invalid config. Rely on the config validation to catch this. - } - if len(cfg.ExcludeMetrics) == 0 { - // Apply default excluded metrics if not explicitly set. - // https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/f2d8efe507083b0f38b6567f8dba3f37053bfa86/exporter/signalfxexporter/internal/translation/default_metrics.go#L133 - cfg.ExcludeMetrics = []dpfilters.MetricFilter{ - {MetricNames: []string{"/^(?i:(container)|(k8s\\.node)|(k8s\\.pod))\\.cpu\\.utilization$/"}}, - } - } - - var filter *dpfilters.FilterSet - filter, err = dpfilters.NewFilterSet(cfg.ExcludeMetrics, cfg.IncludeMetrics) - if err != nil { - return nil // Ignore invalid config. Rely on the config validation to catch this. - } - for metricName := range disableMetrics { - if !filter.Matches(&sfxpb.DataPoint{Metric: metricName}) { - disableMetrics[metricName] = false - } - } - } - - // Disable the metrics in kubeletstats receiver. - for receiverName, cfg := range kubeletReceiverConfigs { - metricsCfg := map[string]any{} - if cfg["metrics"] != nil { - if v, ok := cfg["metrics"].(map[string]any); ok { - metricsCfg = v - } - } - if _, ok := metricsCfg[k8sNodeCPUUtilization]; !ok && disableMetrics[k8sNodeCPUUtilization] { - metricsCfg[k8sNodeCPUUtilization] = map[string]any{"enabled": false} - } - if _, ok := metricsCfg[k8sPodCPUUtilization]; !ok && disableMetrics[k8sPodCPUUtilization] { - metricsCfg[k8sPodCPUUtilization] = map[string]any{"enabled": false} - } - if _, ok := metricsCfg[containerCPUUtilization]; !ok && disableMetrics[containerCPUUtilization] { - metricsCfg[containerCPUUtilization] = map[string]any{"enabled": false} - } - metricsCfgKey := fmt.Sprintf("receivers::%s::metrics", receiverName) - if len(metricsCfg) > 0 { - if err = cfgMap.Merge(confmap.NewFromStringMap(map[string]any{metricsCfgKey: metricsCfg})); err != nil { - return err - } - } - } - - return nil -} diff --git a/internal/configconverter/disable_kubelet_utilization_metrics_test.go b/internal/configconverter/disable_kubelet_utilization_metrics_test.go deleted file mode 100644 index 849813d4ae..0000000000 --- a/internal/configconverter/disable_kubelet_utilization_metrics_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright Splunk, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package configconverter - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/confmap/confmaptest" -) - -func TestDisableKubeletUtilizationMetrics(t *testing.T) { - tests := []struct { - name string - input string - wantOutput string - }{ - { - name: "no_kubeletstats_receiver", - input: "testdata/disable_kubelet_utilization_metrics/no_kubeletstats_receiver.yaml", - wantOutput: "testdata/disable_kubelet_utilization_metrics/no_kubeletstats_receiver.yaml", - }, - { - name: "no_signalfx_exporter", - input: "testdata/disable_kubelet_utilization_metrics/no_signalfx_exporter.yaml", - wantOutput: "testdata/disable_kubelet_utilization_metrics/no_signalfx_exporter.yaml", - }, - { - name: "disable_all_metrics", - input: "testdata/disable_kubelet_utilization_metrics/disable_all_metrics_input.yaml", - wantOutput: "testdata/disable_kubelet_utilization_metrics/disable_all_metrics_output.yaml", - }, - { - name: "do_not_change_enabled_metrics", - input: "testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_input.yaml", - wantOutput: "testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_output.yaml", - }, - { - name: "all_metrics_included_in_signalfx_exporter", - input: "testdata/disable_kubelet_utilization_metrics/all_metrics_included_in_signalfx_exporter.yaml", - wantOutput: "testdata/disable_kubelet_utilization_metrics/all_metrics_included_in_signalfx_exporter.yaml", - }, - { - name: "partially_excluded_in_signalfx_exporter", - input: "testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_input.yaml", - wantOutput: "testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_output.yaml", - }, - { - name: "partially_included_in_signalfx_exporter", - input: "testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_input.yaml", - wantOutput: "testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_output.yaml", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - expectedCfgMap, err := confmaptest.LoadConf(tt.wantOutput) - require.NoError(t, err) - require.NotNil(t, expectedCfgMap) - - cfgMap, err := confmaptest.LoadConf(tt.input) - require.NoError(t, err) - require.NotNil(t, cfgMap) - - err = DisableKubeletUtilizationMetrics(context.Background(), cfgMap) - require.NoError(t, err) - - assert.Equal(t, expectedCfgMap, cfgMap) - }) - } -} diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/all_metrics_included_in_signalfx_exporter.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/all_metrics_included_in_signalfx_exporter.yaml deleted file mode 100644 index f01060d60b..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/all_metrics_included_in_signalfx_exporter.yaml +++ /dev/null @@ -1,30 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM - include_metrics: - - metric_names: - - /^(?i:(container)|(k8s\.node)|(k8s\.pod))\.cpu\.utilization$/ -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - signalfx diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/disable_all_metrics_input.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/disable_all_metrics_input.yaml deleted file mode 100644 index 8b54e6307e..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/disable_all_metrics_input.yaml +++ /dev/null @@ -1,27 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - signalfx diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/disable_all_metrics_output.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/disable_all_metrics_output.yaml deleted file mode 100644 index 890a1b35de..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/disable_all_metrics_output.yaml +++ /dev/null @@ -1,34 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s - metrics: - k8s.node.cpu.utilization: - enabled: false - k8s.pod.cpu.utilization: - enabled: false - container.cpu.utilization: - enabled: false -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - signalfx diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_input.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_input.yaml deleted file mode 100644 index 547a5fa47c..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_input.yaml +++ /dev/null @@ -1,30 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s - metrics: - k8s.pod.cpu.utilization: - enabled: true -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - signalfx diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_output.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_output.yaml deleted file mode 100644 index 2b01b29472..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/do_not_change_enabled_metrics_output.yaml +++ /dev/null @@ -1,34 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s - metrics: - k8s.node.cpu.utilization: - enabled: false - k8s.pod.cpu.utilization: - enabled: true - container.cpu.utilization: - enabled: false -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - signalfx diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/no_kubeletstats_receiver.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/no_kubeletstats_receiver.yaml deleted file mode 100644 index 6774f36ee5..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/no_kubeletstats_receiver.yaml +++ /dev/null @@ -1,25 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - debug: - verbosity: normal - sampling_initial: 2 - sampling_thereafter: 500 -service: - pipelines: - metrics: - receivers: - - hostmetrics - processors: - - memory_limiter - exporters: - - logging diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/no_signalfx_exporter.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/no_signalfx_exporter.yaml deleted file mode 100644 index 757c0938db..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/no_signalfx_exporter.yaml +++ /dev/null @@ -1,28 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - debug: - verbosity: normal - sampling_initial: 2 - sampling_thereafter: 500 -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - logging diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_input.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_input.yaml deleted file mode 100644 index 49f5864e52..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_input.yaml +++ /dev/null @@ -1,29 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM - exclude_metrics: - - metric_name: /^k8s\.(node|pod)\.cpu\.utilization$/ -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - signalfx diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_output.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_output.yaml deleted file mode 100644 index df88ed3ac0..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_excluded_in_signalfx_exporter_output.yaml +++ /dev/null @@ -1,34 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s - metrics: - k8s.node.cpu.utilization: - enabled: false - k8s.pod.cpu.utilization: - enabled: false -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM - exclude_metrics: - - metric_name: /^k8s\.(node|pod)\.cpu\.utilization$/ -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - signalfx diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_input.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_input.yaml deleted file mode 100644 index 5813c4b56d..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_input.yaml +++ /dev/null @@ -1,29 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats/bar: - collection_interval: 2s -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx/foo: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM - include_metrics: - - metric_name: container.cpu.utilization -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats/bar - processors: - - memory_limiter - exporters: - - signalfx/foo diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_output.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_output.yaml deleted file mode 100644 index c7be543cbd..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/partially_included_in_signalfx_exporter_output.yaml +++ /dev/null @@ -1,34 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats/bar: - collection_interval: 2s - metrics: - k8s.node.cpu.utilization: - enabled: false - k8s.pod.cpu.utilization: - enabled: false -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - signalfx/foo: - access_token: MY_SIGNALFX_API_TOKEN - realm: MY_SIGNALFX_REALM - include_metrics: - - metric_name: container.cpu.utilization -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats/bar - processors: - - memory_limiter - exporters: - - signalfx/foo diff --git a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/utilization_metrics_disabled.yaml b/internal/configconverter/testdata/disable_kubelet_utilization_metrics/utilization_metrics_disabled.yaml deleted file mode 100644 index 2f09ebcbae..0000000000 --- a/internal/configconverter/testdata/disable_kubelet_utilization_metrics/utilization_metrics_disabled.yaml +++ /dev/null @@ -1,35 +0,0 @@ -receivers: - hostmetrics: - collection_interval: 1s - scrapers: - cpu: - kubeletstats: - collection_interval: 2s - metrics: - k8s.node.cpu.utilization: - enabled: false - k8s.pod.cpu.utilization: - enabled: false - container.cpu.utilization: - enabled: false -processors: - memory_limiter: - check_interval: 1s - limit_mib: 4000 - spike_limit_mib: 800 - ballast_size_mib: 64 -exporters: - debug: - verbosity: normal - sampling_initial: 2 - sampling_thereafter: 500 -service: - pipelines: - metrics: - receivers: - - hostmetrics - - kubeletstats - processors: - - memory_limiter - exporters: - - logging diff --git a/internal/settings/settings.go b/internal/settings/settings.go index e160a0432a..6078415baa 100644 --- a/internal/settings/settings.go +++ b/internal/settings/settings.go @@ -213,7 +213,6 @@ func (s *Settings) ConfMapConverterFactories() []confmap.ConverterFactory { if !s.noConvertConfig { confMapConverterFactories = append( confMapConverterFactories, - configconverter.ConverterFactoryFromFunc(configconverter.DisableKubeletUtilizationMetrics), configconverter.ConverterFactoryFromFunc(configconverter.DisableExcessiveInternalMetrics), configconverter.ConverterFactoryFromFunc(configconverter.AddOTLPHistogramAttr), ) From 1a7965daf1644038a806e8c09a17a7c2113c1477 Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 09:54:43 -0700 Subject: [PATCH 2/9] add chlog pr ref --- .chloggen/remove-kubeletstats-utilization-converter.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/remove-kubeletstats-utilization-converter.yaml b/.chloggen/remove-kubeletstats-utilization-converter.yaml index ec2a8a39cf..50a74e545e 100644 --- a/.chloggen/remove-kubeletstats-utilization-converter.yaml +++ b/.chloggen/remove-kubeletstats-utilization-converter.yaml @@ -8,7 +8,7 @@ component: receiver/kubeletstats note: Remove deprecated kubeletstats CPU utilization metrics from the collector's config converter # One or more tracking issues related to the change -issues: [] +issues: [6815] # (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. From 72d8fd7e4f22696ac5e86b9b7c72e41ff2c8e047 Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 10:29:51 -0700 Subject: [PATCH 3/9] fix tests --- internal/settings/settings_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/settings/settings_test.go b/internal/settings/settings_test.go index 6f568be2ca..4ac4aa34ac 100644 --- a/internal/settings/settings_test.go +++ b/internal/settings/settings_test.go @@ -156,7 +156,7 @@ func TestNewSettingsConvertConfig(t *testing.T) { require.Equal(t, []string(nil), settings.discoveryProperties) require.Equal(t, []string{configPath, anotherConfigPath}, settings.ResolverURIs()) - require.Equal(t, 5, len(settings.ConfMapConverterFactories())) + require.Equal(t, 4, len(settings.ConfMapConverterFactories())) require.Equal(t, []string{"--feature-gates", "foo", "--feature-gates", "-bar"}, settings.ColCoreArgs()) } From 4defdb00146bac50927c25b26d450b4b62518ee8 Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 10:45:21 -0700 Subject: [PATCH 4/9] rm unused code --- .../configconverter/dpfilters/datapoint.go | 56 --- .../configconverter/dpfilters/dimensions.go | 64 --- .../dpfilters/dimensions_test.go | 134 ------ .../configconverter/dpfilters/filterset.go | 67 --- .../dpfilters/filterset_test.go | 408 ------------------ .../configconverter/dpfilters/matching.go | 64 --- .../configconverter/dpfilters/metricfilter.go | 37 -- internal/configconverter/dpfilters/string.go | 105 ----- .../configconverter/dpfilters/string_test.go | 162 ------- 9 files changed, 1097 deletions(-) delete mode 100644 internal/configconverter/dpfilters/datapoint.go delete mode 100644 internal/configconverter/dpfilters/dimensions.go delete mode 100644 internal/configconverter/dpfilters/dimensions_test.go delete mode 100644 internal/configconverter/dpfilters/filterset.go delete mode 100644 internal/configconverter/dpfilters/filterset_test.go delete mode 100644 internal/configconverter/dpfilters/matching.go delete mode 100644 internal/configconverter/dpfilters/metricfilter.go delete mode 100644 internal/configconverter/dpfilters/string.go delete mode 100644 internal/configconverter/dpfilters/string_test.go diff --git a/internal/configconverter/dpfilters/datapoint.go b/internal/configconverter/dpfilters/datapoint.go deleted file mode 100644 index 4ae5900148..0000000000 --- a/internal/configconverter/dpfilters/datapoint.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation/dpfilters" - -import ( - "errors" - - sfxpb "github.com/signalfx/com_signalfx_metrics_protobuf/model" -) - -type dataPointFilter struct { - metricFilter *StringFilter - dimensionsFilter *dimensionsFilter -} - -// newDataPointFilter returns a new dataPointFilter filter with the given configuration. -func newDataPointFilter(metricNames []string, dimSet map[string][]string) (*dataPointFilter, error) { - var metricFilter *StringFilter - if len(metricNames) > 0 { - var err error - metricFilter, err = NewStringFilter(metricNames) - if err != nil { - return nil, err - } - } - - var dimensionsFilter *dimensionsFilter - if len(dimSet) > 0 { - var err error - dimensionsFilter, err = newDimensionsFilter(dimSet) - if err != nil { - return nil, err - } - } - - if metricFilter == nil && dimensionsFilter == nil { - return nil, errors.New("metric filter must have at least one metric or dimension defined on it") - } - - return &dataPointFilter{ - metricFilter: metricFilter, - dimensionsFilter: dimensionsFilter, - }, nil -} - -// Matches tests a datapoint to see whether it is excluded by this -func (f *dataPointFilter) Matches(dp *sfxpb.DataPoint) bool { - metricNameMatched := f.metricFilter == nil || f.metricFilter.Matches(dp.Metric) - if metricNameMatched { - return f.dimensionsFilter == nil || f.dimensionsFilter.Matches(dp.Dimensions) - } - return false - -} diff --git a/internal/configconverter/dpfilters/dimensions.go b/internal/configconverter/dpfilters/dimensions.go deleted file mode 100644 index 38267efe04..0000000000 --- a/internal/configconverter/dpfilters/dimensions.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation/dpfilters" - -import ( - "errors" - - sfxpb "github.com/signalfx/com_signalfx_metrics_protobuf/model" -) - -type dimensionsFilter struct { - filterMap map[string]*StringFilter -} - -// newDimensionsFilter returns a filter that matches against a -// sfxpb.Dimension slice. The filter will return false if there's -// at least one dimension in the slice that fails to match. In case` -// there are no filters for any of the dimension keys in the slice, -// the filter will return false. -func newDimensionsFilter(m map[string][]string) (*dimensionsFilter, error) { - filterMap := map[string]*StringFilter{} - for k := range m { - if len(m[k]) == 0 { - return nil, errors.New("string map value in filter cannot be empty") - } - - var err error - filterMap[k], err = NewStringFilter(m[k]) - if err != nil { - return nil, err - } - } - - return &dimensionsFilter{ - filterMap: filterMap, - }, nil -} - -func (f *dimensionsFilter) Matches(dimensions []*sfxpb.Dimension) bool { - if len(dimensions) == 0 { - return false - } - - var atLeastOneMatchedDimension bool - for _, dim := range dimensions { - dimF := f.filterMap[dim.Key] - // Skip if there are no filters associated with current dimension key. - if dimF == nil { - continue - } - - if !dimF.Matches(dim.Value) { - return false - } - - if !atLeastOneMatchedDimension { - atLeastOneMatchedDimension = true - } - } - - return atLeastOneMatchedDimension -} diff --git a/internal/configconverter/dpfilters/dimensions_test.go b/internal/configconverter/dpfilters/dimensions_test.go deleted file mode 100644 index cc3cc0b36b..0000000000 --- a/internal/configconverter/dpfilters/dimensions_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters - -import ( - "testing" - - sfxpb "github.com/signalfx/com_signalfx_metrics_protobuf/model" - "github.com/stretchr/testify/require" -) - -func TestDimensionsFilter(t *testing.T) { - tests := []struct { - name string - filter map[string][]string - input []*sfxpb.Dimension - shouldMatch bool - shouldError bool - }{ - { - name: "Empty filter does not empty slice of dimensions", - filter: map[string][]string{}, - input: []*sfxpb.Dimension{}, - shouldMatch: false, - }, - { - name: "Non-empty filter does not match empty slice of dimensions", - filter: map[string][]string{ - "app": {"test"}, - }, - input: []*sfxpb.Dimension{}, - shouldMatch: false, - }, - { - name: "Filter does not match different dimension", - filter: map[string][]string{ - "app": {"test"}, - }, - input: []*sfxpb.Dimension{ - { - Key: "version", - Value: "latest", - }, - }, - shouldMatch: false, - }, - { - name: "Filter matches on exact match of a dimension", - filter: map[string][]string{ - "app": {"test"}, - "version": {"*"}, - }, - input: []*sfxpb.Dimension{ - { - Key: "app", - Value: "test", - }, - }, - shouldMatch: true, - }, - { - name: "Filter matches on exact match with multiple dimensions in input slice", - filter: map[string][]string{ - "app": {"test"}, - }, - input: []*sfxpb.Dimension{ - { - Key: "app", - Value: "test", - }, - { - Key: "version", - Value: "2.0", - }, - }, - shouldMatch: true, - }, - { - name: "Filter matches on regex with multiple dimensions in input slice", - filter: map[string][]string{ - "version": {`/\d+\.\d+/`}, - }, - input: []*sfxpb.Dimension{ - { - Key: "app", - Value: "test", - }, - { - Key: "version", - Value: "2.0", - }, - }, - shouldMatch: true, - }, - { - name: "Filter does not match on regex", - filter: map[string][]string{ - "version": {`/\d+\.\d+/`}, - }, - input: []*sfxpb.Dimension{ - { - Key: "app", - Value: "test", - }, - { - Key: "version", - Value: "bad", - }, - }, - shouldMatch: false, - }, - { - name: "Error creating filter with no dimension values", - filter: map[string][]string{ - "version": {}, - }, - shouldError: true, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - f, err := newDimensionsFilter(test.filter) - if test.shouldError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - - require.Equal(t, test.shouldMatch, f.Matches(test.input)) - }) - } -} diff --git a/internal/configconverter/dpfilters/filterset.go b/internal/configconverter/dpfilters/filterset.go deleted file mode 100644 index 216d1210ab..0000000000 --- a/internal/configconverter/dpfilters/filterset.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation/dpfilters" - -import sfxpb "github.com/signalfx/com_signalfx_metrics_protobuf/model" - -// FilterSet is a collection of datapont filters, any one of which must match -// for a datapoint to be matched. -type FilterSet struct { - excludeFilters []*dataPointFilter - includeFilters []*dataPointFilter -} - -// Matches sends a datapoint through each of the filters in the set and returns -// true if at least one of them matches the datapoint. -func (fs *FilterSet) Matches(dp *sfxpb.DataPoint) bool { - for _, ex := range fs.excludeFilters { - if ex.Matches(dp) { - // If we match an exclusionary filter, run through each inclusion - // filter and see if anything includes the metrics. - for _, in := range fs.includeFilters { - if in.Matches(dp) { - return false - } - } - return true - } - } - return false -} - -func NewFilterSet(excludes []MetricFilter, includes []MetricFilter) (*FilterSet, error) { - excludeSet, err := getDataPointFilters(excludes) - if err != nil { - return nil, err - } - - includeSet, err := getDataPointFilters(includes) - if err != nil { - return nil, err - } - - return &FilterSet{ - excludeFilters: excludeSet, - includeFilters: includeSet, - }, nil -} - -func getDataPointFilters(metricFilters []MetricFilter) ([]*dataPointFilter, error) { - out := make([]*dataPointFilter, len(metricFilters)) - for i, f := range metricFilters { - dimSet, err := f.normalize() - if err != nil { - return nil, err - } - - dpf, err := newDataPointFilter(f.MetricNames, dimSet) - if err != nil { - return nil, err - } - - out[i] = dpf - } - return out, nil -} diff --git a/internal/configconverter/dpfilters/filterset_test.go b/internal/configconverter/dpfilters/filterset_test.go deleted file mode 100644 index 9b0ff73434..0000000000 --- a/internal/configconverter/dpfilters/filterset_test.go +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters - -import ( - "testing" - - sfxpb "github.com/signalfx/com_signalfx_metrics_protobuf/model" - "github.com/stretchr/testify/require" -) - -func TestFilterSet(t *testing.T) { - tests := []struct { - name string - wantErrMsg string - excludes []MetricFilter - includes []MetricFilter - expectedMatches []*sfxpb.DataPoint - expectedNonMatches []*sfxpb.DataPoint - wantErr bool - }{ - { - name: "Match based on simple metric name as string", - excludes: []MetricFilter{{MetricName: "cpu.utilization"}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "memory.utilization", - }, - }, - }, - { - name: "Match based on simple metric name", - excludes: []MetricFilter{{MetricNames: []string{"cpu.utilization"}}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "memory.utilization", - }, - }, - }, - { - name: "Match based on multiple metric names", - excludes: []MetricFilter{{MetricNames: []string{"cpu.utilization", "memory.utilization"}}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - { - Metric: "memory.utilization", - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - }, - }, - }, - { - name: "Match based on regex metric name", - excludes: []MetricFilter{{MetricNames: []string{`/cpu\..*/`}}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - }, - }, - }, - { - name: "Match based on glob metric name", - excludes: []MetricFilter{{MetricNames: []string{`cpu.util*`, "memor*"}}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - { - Metric: "memory.utilization", - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - }, - }, - }, - { - name: "Match based on dimension name as string", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "container_name": "PO", - }}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "PO"}}, - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "test"}}, - }, - }, - }, - { - name: "Match based on dimension name", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "container_name": []any{"PO"}, - }}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "PO"}}, - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "test"}}, - }, - }, - }, - { - name: "Match based on dimension name regex", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "container_name": []any{`/^[A-Z][A-Z]$/`}, - }}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "PO"}}, - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "test"}}, - }, - }, - }, - { - name: "Match based on dimension presence", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "container_name": []any{`/.+/`}, - }}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "test"}}, - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "host", Value: "localhost"}}, - }, - }, - }, - { - name: "Match based on dimension name glob", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "container_name": []any{`*O*`}, - }}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "POD"}}, - }, - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "POD123"}}, - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "test"}}, - }, - }, - }, - { - name: "Match based on conjunction of both dimensions and metric name", - excludes: []MetricFilter{{ - MetricNames: []string{"*.utilization"}, - Dimensions: map[string]any{ - "container_name": []any{"test"}, - }, - }}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "test"}}, - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "not matching"}}, - }, { - Metric: "disk.usage", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "test"}}, - }, - }, - }, - { - name: "Doesn't match if no dimension filter specified", - excludes: []MetricFilter{{MetricNames: []string{"cpu.utilization"}}}, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "disk.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "test"}}, - }, - }, - }, - { - name: "Doesn't match if no metric name filter specified", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "container_name": []any{"mycontainer"}, - }}}, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - }, - }, - { - name: "Doesn't match metric when no (matching) dimensions exist", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "host": []any{"localhost"}, - "system": []any{"r4"}, - }}}, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{ - {Key: "Host", Value: "localhost"}, - }, - }, - }, - }, - { - name: "Matches on at least one dimension", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "host": []any{"localhost"}, - "system": []any{"r4"}, - }}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{ - {Key: "host", Value: "localhost"}, - }, - }, - }, - }, - { - name: "Matches against all dimension pairs", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "host": []any{"localhost"}, - "system": []any{"r4"}, - }}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{ - {Key: "host", Value: "localhost"}, - {Key: "system", Value: "r4"}, - }, - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{ - {Key: "host", Value: "localhost"}, - {Key: "system", Value: "r3"}, - }, - }, - }, - }, - { - name: "Negated dim values take precedent", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "container_name": []any{"*", "!pause", "!/.*idle/"}, - }}}, - expectedMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "mycontainer"}}, - }, - }, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "pause"}}, - }, - { - Metric: "cpu.utilization", - Dimensions: []*sfxpb.Dimension{{Key: "container_name", Value: "is_idle"}}, - }, - }, - }, - { - name: "Error creating exclude empty filter", - excludes: []MetricFilter{{}}, - wantErr: true, - wantErrMsg: "metric filter must have at least one metric or dimension defined on it", - }, - { - name: "Error creating include empty filter", - includes: []MetricFilter{{}}, - wantErr: true, - wantErrMsg: "metric filter must have at least one metric or dimension defined on it", - }, - { - name: "Error creating filter with empty dimension list", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "dim": []any{}, - }}}, - wantErr: true, - wantErrMsg: "string map value in filter cannot be empty", - }, - { - name: "Error creating filter with invalid glob", - excludes: []MetricFilter{{MetricNames: []string{"cpu.*["}}}, - wantErr: true, - wantErrMsg: "unexpected end of input", - }, - { - name: "Error creating filter with invalid glob in dimensions", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "container_name": []any{"cpu.*["}, - }}}, - wantErr: true, - wantErrMsg: "unexpected end of input", - }, - { - name: "Error on invalid dimensions input", - excludes: []MetricFilter{{ - Dimensions: map[string]any{ - "host": 1, - }}}, - wantErr: true, - wantErrMsg: "1 should be either a string or string list", - }, - { - name: "Match in include filters correctly overrides exclude", - excludes: []MetricFilter{{MetricNames: []string{"cpu.utilization"}}}, - includes: []MetricFilter{{MetricNames: []string{"cpu.utilization"}}}, - expectedNonMatches: []*sfxpb.DataPoint{ - { - Metric: "cpu.utilization", - }, - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - f, err := NewFilterSet(test.excludes, test.includes) - if test.wantErr { - require.EqualError(t, err, test.wantErrMsg) - require.Nil(t, f) - return - } - require.NoError(t, err) - - for _, metric := range test.expectedMatches { - require.True(t, f.Matches(metric)) - } - - for _, metric := range test.expectedNonMatches { - require.False(t, f.Matches(metric)) - } - }) - } -} diff --git a/internal/configconverter/dpfilters/matching.go b/internal/configconverter/dpfilters/matching.go deleted file mode 100644 index 31a4cbf0c5..0000000000 --- a/internal/configconverter/dpfilters/matching.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation/dpfilters" - -import ( - "regexp" - "strings" - - "github.com/gobwas/glob" -) - -// Contains all of the logic for glob and regex based filtering. - -func isGlobbed(s string) bool { - return strings.ContainsAny(s, "*?[]{}!") -} - -func isRegex(s string) bool { - return len(s) > 2 && s[0] == '/' && s[len(s)-1] == '/' -} - -// remove the bracketing slashes for a regex. -func stripSlashes(s string) string { - return s[1 : len(s)-1] -} - -// stripNegation checks if a string is prefixed with "!" -// and will returned the stripped string and true if so -// else, return original value and false. -func stripNegation(value string) (string, bool) { - if strings.HasPrefix(value, "!") { - return value[1:], true - } - return value, false -} - -type matcher interface { - // Returns whether the string matched and whether it was a negated match. - Matches(s string) (bool, bool) -} - -type regexMatcher struct { - re *regexp.Regexp - negated bool -} - -var _ matcher = (*regexMatcher)(nil) - -func (m *regexMatcher) Matches(s string) (bool, bool) { - return m.re.MatchString(s), m.negated -} - -type globMatcher struct { - glob glob.Glob - negated bool -} - -var _ matcher = &globMatcher{} - -func (m *globMatcher) Matches(s string) (bool, bool) { - return m.glob.Match(s), m.negated -} diff --git a/internal/configconverter/dpfilters/metricfilter.go b/internal/configconverter/dpfilters/metricfilter.go deleted file mode 100644 index 2fe7682395..0000000000 --- a/internal/configconverter/dpfilters/metricfilter.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation/dpfilters" - -import "fmt" - -type MetricFilter struct { - Dimensions map[string]any `mapstructure:"dimensions"` - MetricName string `mapstructure:"metric_name"` - MetricNames []string `mapstructure:"metric_names"` -} - -func (mf *MetricFilter) normalize() (map[string][]string, error) { - if mf.MetricName != "" { - mf.MetricNames = append(mf.MetricNames, mf.MetricName) - } - - dimSet := map[string][]string{} - for k, v := range mf.Dimensions { - switch s := v.(type) { - case []any: - var newSet []string - for _, iv := range s { - newSet = append(newSet, fmt.Sprintf("%v", iv)) - } - dimSet[k] = newSet - case string: - dimSet[k] = []string{s} - default: - return nil, fmt.Errorf("%v should be either a string or string list", v) - } - } - - return dimSet, nil -} diff --git a/internal/configconverter/dpfilters/string.go b/internal/configconverter/dpfilters/string.go deleted file mode 100644 index 9a06453da7..0000000000 --- a/internal/configconverter/dpfilters/string.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation/dpfilters" - -import ( - "regexp" - - "github.com/gobwas/glob" -) - -// StringFilter will match if any one of the given strings is a match. -type StringFilter struct { - staticSet map[string]bool - regexps []regexMatcher - globs []globMatcher - anyStaticNegated bool -} - -// NewStringFilter returns a filter that can match against the provided items. -func NewStringFilter(items []string) (*StringFilter, error) { - staticSet := make(map[string]bool) - var regexps []regexMatcher - var globs []globMatcher - - anyStaticNegated := false - for _, i := range items { - m, negated := stripNegation(i) - switch { - case isRegex(m): - var re *regexp.Regexp - var err error - - reText := stripSlashes(m) - re, err = regexp.Compile(reText) - - if err != nil { - return nil, err - } - - regexps = append(regexps, regexMatcher{re: re, negated: negated}) - case isGlobbed(m): - g, err := glob.Compile(m) - if err != nil { - return nil, err - } - - globs = append(globs, globMatcher{glob: g, negated: negated}) - default: - staticSet[m] = negated - if negated { - anyStaticNegated = true - } - } - } - - return &StringFilter{ - staticSet: staticSet, - regexps: regexps, - globs: globs, - anyStaticNegated: anyStaticNegated, - }, nil -} - -// Matches if s is positively matched by the filter items OR -// if it is positively matched by a non-glob/regex pattern exactly -// and is negated as well. See the unit tests for examples. -func (f *StringFilter) Matches(s string) bool { - if f == nil { - return true - } - negated, matched := f.staticSet[s] - // If a metric is negated and it matched it won't match anything else by - // definition. - if matched && negated { - return false - } - - for _, reMatch := range f.regexps { - reMatched, negated := reMatch.Matches(s) - if reMatched && negated { - return false - } - matched = matched || reMatched - } - - for _, globMatcher := range f.globs { - globMatched, negated := globMatcher.Matches(s) - if globMatched && negated { - return false - } - matched = matched || globMatched - } - return matched -} - -func (f *StringFilter) UnmarshalText(in []byte) error { - sf, err := NewStringFilter([]string{string(in)}) - if err != nil { - return err - } - *f = *sf - return nil -} diff --git a/internal/configconverter/dpfilters/string_test.go b/internal/configconverter/dpfilters/string_test.go deleted file mode 100644 index ad74b3dd0d..0000000000 --- a/internal/configconverter/dpfilters/string_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 -// Temporary copied from the SignalFx exporter to be used in DisableKubeletUtilizationMetrics converter. - -package dpfilters - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestStringFilter(t *testing.T) { - tests := []struct { - name string - filter []string - inputs []string - shouldMatch []bool - shouldError bool - }{ - { - filter: []string{}, - inputs: []string{"process_", "", "asdf"}, - shouldMatch: []bool{false, false, false}, - }, - { - filter: []string{ - "*", - }, - inputs: []string{"app", "asdf", "", "*"}, - shouldMatch: []bool{true, true, true, true}, - }, - { - filter: []string{ - "!app", - }, - inputs: []string{"app", "other"}, - shouldMatch: []bool{false, false}, - }, - { - filter: []string{ - "app", - "!app", - }, - inputs: []string{"app", "other"}, - shouldMatch: []bool{false, false}, - }, - { - filter: []string{ - "other", - "!app", - }, - inputs: []string{"other", "something", "app"}, - shouldMatch: []bool{true, false, false}, - }, - { - filter: []string{ - "/^process_/", - "/^node_/", - }, - inputs: []string{"process_", "node_", "process_asdf", "other"}, - shouldMatch: []bool{true, true, true, false}, - }, - { - filter: []string{ - "!/^process_/", - }, - inputs: []string{"process_", "other"}, - shouldMatch: []bool{false, false}, - }, - { - filter: []string{ - "app", - "!/^process_/", - "process_", - }, - inputs: []string{"other", "app", "process_cpu", "process_"}, - shouldMatch: []bool{false, true, false, false}, - }, - { - filter: []string{ - "asdfdfasdf", - "/^node_/", - }, - inputs: []string{"node_test"}, - shouldMatch: []bool{true}, - }, - { - filter: []string{ - "process_*", - "!process_cpu", - }, - inputs: []string{"process_mem", "process_cpu", "asdf"}, - shouldMatch: []bool{true, false, false}, - }, - { - filter: []string{ - "*", - "!process_cpu", - }, - inputs: []string{"process_mem", "process_cpu", "asdf"}, - shouldMatch: []bool{true, false, true}, - }, - { - filter: []string{ - "metric_?", - "!metric_a", - "!metric_b", - "random", - }, - inputs: []string{"metric_a", "metric_b", "metric_c", "asdf", "random"}, - shouldMatch: []bool{false, false, true, false, true}, - }, - { - filter: []string{ - "!process_cpu", - // Order doesn't matter - "*", - }, - inputs: []string{"process_mem", "process_cpu", "asdf"}, - shouldMatch: []bool{true, false, true}, - }, - { - filter: []string{ - "/a.*/", - "!/.*z/", - "b", - // Static match should not override the negated regex above - "alz", - }, - inputs: []string{"", "asdf", "asdz", "b", "wrong", "alz"}, - shouldMatch: []bool{false, true, false, true, false, false}, - }, - { - filter: []string{"!memory*"}, - inputs: []string{"cpu.utilization", "memory.utilization"}, - shouldMatch: []bool{false, false}, - }, - { - filter: []string{"/!memor*(/"}, - shouldError: true, - }, - { - filter: nil, - inputs: []string{"cpu.utilization", "memory.utilization"}, - shouldMatch: []bool{false, false}, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - f, err := NewStringFilter(test.filter) - if test.shouldError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - for i := range test.inputs { - assert.Equal(t, test.shouldMatch[i], f.Matches(test.inputs[i])) - } - }) - } -} From 538cc830baf3573a42cb03688d567d305bc72fa5 Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 10:49:34 -0700 Subject: [PATCH 5/9] tidy --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d24356b98d..e8a548c5e1 100644 --- a/go.mod +++ b/go.mod @@ -580,7 +580,7 @@ require ( github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-test/deep v1.1.1 // indirect - github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe + github.com/gobwas/glob v0.2.4-0.20181002190808-e7a84e9525fe // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/googleapis v1.4.1 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect @@ -707,7 +707,7 @@ require ( github.com/rs/cors v1.11.1 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/shirou/gopsutil/v3 v3.24.5 // indirect - github.com/signalfx/com_signalfx_metrics_protobuf v0.0.3 + github.com/signalfx/com_signalfx_metrics_protobuf v0.0.3 // indirect github.com/signalfx/defaults v1.2.2-0.20180531161417-70562fe60657 // indirect github.com/signalfx/gohistogram v0.0.0-20160107210732-1ccfd2ff5083 // indirect github.com/signalfx/ingest-protocols v0.4.1 // indirect From 5afb519627a51e29d41b15d8982457452aa0a4f1 Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 11:34:37 -0700 Subject: [PATCH 6/9] Update .chloggen/remove-kubeletstats-utilization-converter.yaml Co-authored-by: Curtis Robert --- .chloggen/remove-kubeletstats-utilization-converter.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.chloggen/remove-kubeletstats-utilization-converter.yaml b/.chloggen/remove-kubeletstats-utilization-converter.yaml index 50a74e545e..9bdd8fb2a8 100644 --- a/.chloggen/remove-kubeletstats-utilization-converter.yaml +++ b/.chloggen/remove-kubeletstats-utilization-converter.yaml @@ -14,7 +14,7 @@ issues: [6815] # These lines will be padded with 2 spaces and then inserted directly into the document. # Use pipe (|) for multiline entries. subtext: | - The kubeletstats receiver deprecated CPU utilization metrics have been completely removed in + The kubeletstats receiver's deprecated CPU utilization metrics have been completely removed in OpenTelemetry Collector Contrib v0.136.0, along with the `receiver.kubeletstats.enableCPUUsageMetrics` feature gate. Users who were explicitly including these metrics in their SignalFx exporter `include_metrics` configuration need to update their configs: From e0373f37489a3ec737584f80d27574d5147eef2e Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 12:10:08 -0700 Subject: [PATCH 7/9] reword changelog --- ...ve-kubeletstats-utilization-converter.yaml | 20 ++++--------------- CHANGELOG.md | 4 +--- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/.chloggen/remove-kubeletstats-utilization-converter.yaml b/.chloggen/remove-kubeletstats-utilization-converter.yaml index 9bdd8fb2a8..96b59c515b 100644 --- a/.chloggen/remove-kubeletstats-utilization-converter.yaml +++ b/.chloggen/remove-kubeletstats-utilization-converter.yaml @@ -1,11 +1,11 @@ # One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' -change_type: breaking +change_type: bug_fix # The name of the component, or a single word describing the area of concern, (e.g. crosslink) component: receiver/kubeletstats # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Remove deprecated kubeletstats CPU utilization metrics from the collector's config converter +note: Remove support for no-op configuration sections for disabled kubeletstats CPU utilization metrics # One or more tracking issues related to the change issues: [6815] @@ -14,17 +14,5 @@ issues: [6815] # These lines will be padded with 2 spaces and then inserted directly into the document. # Use pipe (|) for multiline entries. subtext: | - The kubeletstats receiver's deprecated CPU utilization metrics have been completely removed in - OpenTelemetry Collector Contrib v0.136.0, along with the `receiver.kubeletstats.enableCPUUsageMetrics` - feature gate. Users who were explicitly including these metrics in their SignalFx exporter - `include_metrics` configuration need to update their configs: - - - `container.cpu.utilization` → `container.cpu.usage` - - `k8s.node.cpu.utilization` → `k8s.node.cpu.usage` - - `k8s.pod.cpu.utilization` → `k8s.pod.cpu.usage` - - Note: The new `.cpu.usage` metrics show usage counted in CPUs (not a percentage of used resources). - The previous metrics were incorrectly named using the "utilization" term. - - The Splunk distribution's config converter that automatically disabled these deprecated - metrics has been removed to prevent collector startup failures. + Removed the Splunk distribution's config converter that handled no-op configuration + sections for CPU utilization metrics that were already disabled. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6985ebebd8..f9b6a92132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,7 @@ and the [opentelemetry-collector-contrib v0.136.0](https://github.com/open-telem - (Contrib) `pkg/ottl`: Upgrade profiles proto to 1.8.0 ([#42526](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/42526)) See [proto changelog](https://github.com/open-telemetry/opentelemetry-proto/blob/main/CHANGELOG.md#180---2025-09-02). - (Contrib) `transformprocessor`: Upgrade profiles proto to 1.8.0 ([#42526](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/42526)) -- (Contrib) `kubeletstatsreceiver`: Remove deprecated CPU utilization metrics and feature gate ([#42727](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/42727)) - The deprecated `*.cpu.utilization` metrics and `receiver.kubeletstats.enableCPUUsageMetrics` feature gate - have been removed. Use the corresponding `*.cpu.usage` metrics instead. +- (Contrib) `kubeletstatsreceiver`: Remove support for no-op config sections for disabled CPU utilization metrics ([#42727](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/42727)) ### 💡 Enhancements 💡 From 2730478b6a39c814040c86805811bf48b65cb1d1 Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 12:28:30 -0700 Subject: [PATCH 8/9] fix changelog --- .chloggen/remove-kubeletstats-utilization-converter.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.chloggen/remove-kubeletstats-utilization-converter.yaml b/.chloggen/remove-kubeletstats-utilization-converter.yaml index 96b59c515b..da799b3a9f 100644 --- a/.chloggen/remove-kubeletstats-utilization-converter.yaml +++ b/.chloggen/remove-kubeletstats-utilization-converter.yaml @@ -5,7 +5,7 @@ change_type: bug_fix component: receiver/kubeletstats # A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). -note: Remove support for no-op configuration sections for disabled kubeletstats CPU utilization metrics +note: Removed the Splunk distribution's config converter that handled no-op configuration sections for CPU utilization metrics that were already disabled # One or more tracking issues related to the change issues: [6815] @@ -13,6 +13,4 @@ issues: [6815] # (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: | - Removed the Splunk distribution's config converter that handled no-op configuration - sections for CPU utilization metrics that were already disabled. +subtext: From 5fd797f406eb8449a465f87731d259839f081716 Mon Sep 17 00:00:00 2001 From: Jina Jain Date: Tue, 30 Sep 2025 14:23:05 -0700 Subject: [PATCH 9/9] add disk cleanup --- .github/workflows/integration-test.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index eb165cd254..9bf96e5d91 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -340,6 +340,10 @@ jobs: matrix: ${{ fromJSON(needs.integration-test-discovery-matrix.outputs.matrix) }} fail-fast: false steps: + - name: Free up disk space for next step + uses: jlumbroso/free-disk-space@v1.3.1 + if: runner.os == 'Linux' + continue-on-error: true - uses: actions/checkout@v5 with: fetch-depth: 0 @@ -403,6 +407,10 @@ jobs: matrix: ${{ fromJSON(needs.integration-test-discovery-k8s-matrix.outputs.matrix) }} fail-fast: false steps: + - name: Free up disk space for next step + uses: jlumbroso/free-disk-space@v1.3.1 + if: runner.os == 'Linux' + continue-on-error: true - uses: actions/checkout@v5 with: fetch-depth: 0