From 370fec91b5f10ae452bdcb01a7271227ce057822 Mon Sep 17 00:00:00 2001 From: Mahendra Paipuri Date: Fri, 4 Sep 2026 16:58:55 +0200 Subject: [PATCH 1/3] refactor: Check emission factor series in rules generation * When operators are running multiple instances of emission collectors, the current rules will silently fail due to many-to-many relationships. This commit checks for number of instances of emission factor series and when multiple found, ceems_tool will emit a warning. Additionally we only take the first instance of these multiple emission factor series while generating the recording rules Signed-off-by: Mahendra Paipuri --- cmd/ceems_tool/rules.go | 40 ++++++++++++++- cmd/ceems_tool/rules/cpu-cray.rules | 10 +++- .../rules/cpu-ipmi-redfish-hwmon.rules | 10 +++- cmd/ceems_tool/rules/cpu-rapl.rules | 10 +++- .../rules/gpu-amd-device-metrics.rules | 10 +++- cmd/ceems_tool/rules/gpu-amd-smi.rules | 8 +++ cmd/ceems_tool/rules/gpu-nvidia.rules | 8 +++ .../e2e-test-recording-rules-output.txt | 50 +++++++++---------- scripts/e2e-test.sh | 1 + website/docs/deployments/guide.md | 8 +++ 10 files changed, 123 insertions(+), 32 deletions(-) diff --git a/cmd/ceems_tool/rules.go b/cmd/ceems_tool/rules.go index 40291c3c..4b9cc6fa 100644 --- a/cmd/ceems_tool/rules.go +++ b/cmd/ceems_tool/rules.go @@ -41,6 +41,10 @@ const ( amdDevExporterPkgPowerMetric = "gpu_package_power" ) +const ( + emissionsMetric = "ceems_emissions_gCo2_kWh" +) + var ( seriesNames = []string{ "ceems_compute_unit_cpu_user_seconds_total", @@ -51,7 +55,7 @@ var ( redfishPowerMetric, crayPowerMetric, hwmonPowerMetric, - "ceems_emissions_gCo2_kWh", + emissionsMetric, dcgmPowerMetric, dcgmInstantPowerMetric, amdSMIPowerMetric, @@ -129,6 +133,7 @@ type rulesTemplateData struct { EmissionFactor EmissionFactor Providers model.LabelValues CountryCode string + Instance string RateInterval string EvaluationInterval string } @@ -229,6 +234,8 @@ func CreatePromRecordingRules( var providers model.LabelValues + var emissionCollectorInstance string + if emissionFactorValue == 0 { // If no emission factor value has been passed, attempt to get from time series or // static OWID data @@ -244,6 +251,9 @@ func CreatePromRecordingRules( } } } + + // Get instance of emission collector + emissionCollectorInstance = efInstance(ctx, api, stime, etime) } else { emissionFactor = EmissionFactor{Provider: "custom", Value: emissionFactorValue} } @@ -387,6 +397,7 @@ func CreatePromRecordingRules( EmissionFactor: emissionFactor, Providers: providers, CountryCode: countryCode, + Instance: emissionCollectorInstance, RateInterval: rateInterval.String(), EvaluationInterval: evalInterval.String(), } @@ -428,10 +439,35 @@ func scrapeIntervals(ctx context.Context, api v1.API) (map[string]time.Duration, return scrapeIntervals, nil } +// efInstance returns first found instance of emission factor metric. In case when operators run emissions collector on +// multiple instances, it comes handy. +func efInstance(ctx context.Context, api v1.API, start time.Time, end time.Time) string { + // First fetch all the available series of emissions + series, _, err := api.Series(ctx, []string{emissionsMetric}, start, end) + if err != nil || len(series) == 0 { + return "" + } + + var instances []string + for _, s := range series { + instances = append(instances, string(s["instance"])) + } + + // Remove duplicates and if more than one instance is found, it means multiple + // instances of emissions collectors are running which is not ideal. Emit a warning + instances = slices.Compact(instances) + slices.Sort(instances) + if len(instances) > 1 { + fmt.Fprintln(os.Stderr, `WARNING: multiple instances of emissions collectors are detected. The generated recording rules might not work in this case. Ensure there is only one single instance of emissions collector running in the cluster.`) + } + + return instances[0] +} + // efProviders returns a slice of available emission factor providers. func efProviders(ctx context.Context, api v1.API, start time.Time, end time.Time, countryCode string, disableProviders bool) (model.LabelValues, error) { // Run query to get label values. - matcher := fmt.Sprintf(`ceems_emissions_gCo2_kWh{country_code="%s"}`, countryCode) + matcher := fmt.Sprintf(`%s{country_code="%s"}`, emissionsMetric, countryCode) providers, _, err := api.LabelValues(ctx, "provider", []string{matcher}, start, end) // Ignoring warnings for now. if err != nil { diff --git a/cmd/ceems_tool/rules/cpu-cray.rules b/cmd/ceems_tool/rules/cpu-cray.rules index 42225d99..c0a3769f 100644 --- a/cmd/ceems_tool/rules/cpu-cray.rules +++ b/cmd/ceems_tool/rules/cpu-cray.rules @@ -108,7 +108,11 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- if ne $.Instance "" }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}",instance="{{$.Instance}}"} +{{- else }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- end }} {{ end }} {{- else }} {{- if gt $.EmissionFactor.Value 0.0 }} @@ -192,7 +196,11 @@ groups: ( job:ceems_host_power_watts:pue{job="{{$.Job}}"} / 3.6e+06 * on (job) group_right () +{{- if ne $.Instance "" }} + label_replace(ceems_emissions_gCo2_kWh{instance="{{$.Instance}}"}, "job", "{{$.Job}}", "instance", "(.*)") +{{- else }} label_replace(ceems_emissions_gCo2_kWh, "job", "{{$.Job}}", "instance", "(.*)") +{{- end }} ) ) {{- else }} diff --git a/cmd/ceems_tool/rules/cpu-ipmi-redfish-hwmon.rules b/cmd/ceems_tool/rules/cpu-ipmi-redfish-hwmon.rules index de17d99d..42cd3ce0 100644 --- a/cmd/ceems_tool/rules/cpu-ipmi-redfish-hwmon.rules +++ b/cmd/ceems_tool/rules/cpu-ipmi-redfish-hwmon.rules @@ -142,7 +142,11 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- if ne $.Instance "" }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}",instance="{{$.Instance}}"} +{{- else }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- end }} {{ end }} {{- else }} {{- if gt $.EmissionFactor.Value 0.0 }} @@ -218,7 +222,11 @@ groups: ( job:ceems_host_power_watts:pue{job="{{$.Job}}"} / 3.6e+06 * on (job) group_right () +{{- if ne $.Instance "" }} + label_replace(ceems_emissions_gCo2_kWh{instance="{{$.Instance}}"}, "job", "{{$.Job}}", "instance", "(.*)") +{{- else }} label_replace(ceems_emissions_gCo2_kWh, "job", "{{$.Job}}", "instance", "(.*)") +{{- end }} ) ) {{- else }} diff --git a/cmd/ceems_tool/rules/cpu-rapl.rules b/cmd/ceems_tool/rules/cpu-rapl.rules index cb44d759..0628683e 100644 --- a/cmd/ceems_tool/rules/cpu-rapl.rules +++ b/cmd/ceems_tool/rules/cpu-rapl.rules @@ -95,7 +95,11 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- if ne $.Instance "" }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}",instance="{{$.Instance}}"} +{{- else }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- end }} {{ end }} {{- else }} {{- if gt $.EmissionFactor.Value 0.0 }} @@ -179,7 +183,11 @@ groups: ( job:ceems_host_power_watts:pue{job="{{$.Job}}"} / 3.6e+06 * on (job) group_right () +{{- if ne $.Instance "" }} + label_replace(ceems_emissions_gCo2_kWh{instance="{{$.Instance}}"}, "job", "{{$.Job}}", "instance", "(.*)") +{{- else }} label_replace(ceems_emissions_gCo2_kWh, "job", "{{$.Job}}", "instance", "(.*)") +{{- end }} ) ) {{- else }} diff --git a/cmd/ceems_tool/rules/gpu-amd-device-metrics.rules b/cmd/ceems_tool/rules/gpu-amd-device-metrics.rules index 29421cd5..c289fa67 100644 --- a/cmd/ceems_tool/rules/gpu-amd-device-metrics.rules +++ b/cmd/ceems_tool/rules/gpu-amd-device-metrics.rules @@ -95,7 +95,11 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- if ne $.Instance "" }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}",instance="{{$.Instance}}"} +{{- else }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- end }} {{ end }} {{- else }} {{- if gt $.EmissionFactor.Value 0.0 }} @@ -178,7 +182,11 @@ groups: ( job:ceems_gpu_power_watts:pue{job="{{$.GPUJob}}"} / 3.6e+06 * on (job) group_right () +{{- if ne $.Instance "" }} + label_replace(ceems_emissions_gCo2_kWh{instance="{{$.Instance}}"}, "job", "{{$.GPUJob}}", "instance", "(.*)") +{{- else }} label_replace(ceems_emissions_gCo2_kWh, "job", "{{$.GPUJob}}", "instance", "(.*)") +{{- end }} ) ) {{- else }} diff --git a/cmd/ceems_tool/rules/gpu-amd-smi.rules b/cmd/ceems_tool/rules/gpu-amd-smi.rules index d7ef9039..db73f566 100644 --- a/cmd/ceems_tool/rules/gpu-amd-smi.rules +++ b/cmd/ceems_tool/rules/gpu-amd-smi.rules @@ -63,7 +63,11 @@ groups: "(.*)" ) * on (provider) group_left () +{{- if ne $.Instance "" }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}",instance="{{$.Instance}}"} +{{- else }} ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- end }} {{ end }} {{- else }} {{- if gt $.EmissionFactor.Value 0.0 }} @@ -116,7 +120,11 @@ groups: ( job:ceems_gpu_power_watts:pue{job="{{$.GPUJob}}"} / 3.6e+06 * on (job) group_right () +{{- if ne $.Instance "" }} + label_replace(ceems_emissions_gCo2_kWh{instance="{{$.Instance}}"}, "job", "{{$.GPUJob}}", "instance", "(.*)") +{{- else }} label_replace(ceems_emissions_gCo2_kWh, "job", "{{$.GPUJob}}", "instance", "(.*)") +{{- end }} ) ) {{- else }} diff --git a/cmd/ceems_tool/rules/gpu-nvidia.rules b/cmd/ceems_tool/rules/gpu-nvidia.rules index 0ad0fed2..dce491f6 100644 --- a/cmd/ceems_tool/rules/gpu-nvidia.rules +++ b/cmd/ceems_tool/rules/gpu-nvidia.rules @@ -110,7 +110,11 @@ groups: "(.*)" ) * on (provider) group_left () +{{- if ne $.Instance "" }} + ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}",instance="{{$.Instance}}"} +{{- else }} ceems_emissions_gCo2_kWh{country_code="{{$.CountryCode}}",provider="{{$v}}"} +{{- end }} {{ end }} {{- else }} {{- if gt $.EmissionFactor.Value 0.0 }} @@ -189,7 +193,11 @@ groups: ( job:ceems_gpu_power_watts:pue{job="{{$.GPUJob}}"} / 3.6e+06 * on (job) group_right () +{{- if ne $.Instance "" }} + label_replace(ceems_emissions_gCo2_kWh{instance="{{$.Instance}}"}, "job", "{{$.GPUJob}}", "instance", "(.*)") +{{- else }} label_replace(ceems_emissions_gCo2_kWh, "job", "{{$.GPUJob}}", "instance", "(.*)") +{{- end }} ) ) {{- else }} diff --git a/cmd/ceems_tool/testdata/output/e2e-test-recording-rules-output.txt b/cmd/ceems_tool/testdata/output/e2e-test-recording-rules-output.txt index 7bbde2e3..9571a07f 100644 --- a/cmd/ceems_tool/testdata/output/e2e-test-recording-rules-output.txt +++ b/cmd/ceems_tool/testdata/output/e2e-test-recording-rules-output.txt @@ -1,5 +1,5 @@ -Number of series found for job series are: 91 -Number of series found for uuid series are: 728 +Number of series found for job series are: 80 +Number of series found for uuid series are: 592 amd-device-metrics-gpu-gpu.rules --- # Recording rules for AMD GPUs scrape job amd-device-metrics-gpu. @@ -94,7 +94,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # Profiling metrics @@ -188,7 +188,7 @@ groups: ( job:ceems_gpu_power_watts:pue{job="amd-device-metrics-gpu"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "amd-device-metrics-gpu", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "amd-device-metrics-gpu", "instance", "(.*)") ) ) amd-smi-gpu-gpu.rules @@ -253,7 +253,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # The following recording rules estimate the average GPU, GPU memory usages and @@ -285,7 +285,7 @@ groups: ( job:ceems_gpu_power_watts:pue{job="amd-smi-gpu"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "amd-smi-gpu", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "amd-smi-gpu", "instance", "(.*)") ) ) cpu-cray-amd-gpu.rules @@ -397,7 +397,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # The following recording rules estimate the average CPU, CPU memory usages and @@ -460,7 +460,7 @@ groups: ( job:ceems_host_power_watts:pue{job="cpu-cray-amd-gpu"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "cpu-cray-amd-gpu", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "cpu-cray-amd-gpu", "instance", "(.*)") ) ) @@ -726,7 +726,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # The following recording rules estimate the average CPU, CPU memory usages and @@ -781,7 +781,7 @@ groups: ( job:ceems_host_power_watts:pue{job="cpu-hwmon-amd-gpu"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "cpu-hwmon-amd-gpu", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "cpu-hwmon-amd-gpu", "instance", "(.*)") ) ) @@ -1013,7 +1013,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # The following recording rules estimate the average CPU, CPU memory usages and @@ -1068,7 +1068,7 @@ groups: ( job:ceems_host_power_watts:pue{job="cpu-ipmi-nvidia-gpu"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "cpu-ipmi-nvidia-gpu", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "cpu-ipmi-nvidia-gpu", "instance", "(.*)") ) ) @@ -1332,7 +1332,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # The following recording rules estimate the average CPU, CPU memory usages and @@ -1387,7 +1387,7 @@ groups: ( job:ceems_host_power_watts:pue{job="cpu-only-ipmi"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "cpu-only-ipmi", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "cpu-only-ipmi", "instance", "(.*)") ) ) @@ -1608,7 +1608,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # The following recording rules estimate the average CPU, CPU memory usages and @@ -1671,7 +1671,7 @@ groups: ( job:ceems_host_power_watts:pue{job="cpu-only-rapl"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "cpu-only-rapl", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "cpu-only-rapl", "instance", "(.*)") ) ) @@ -1937,7 +1937,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # The following recording rules estimate the average CPU, CPU memory usages and @@ -1992,7 +1992,7 @@ groups: ( job:ceems_host_power_watts:pue{job="cpu-only-redfish"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "cpu-only-redfish", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "cpu-only-redfish", "instance", "(.*)") ) ) @@ -2256,7 +2256,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # The following recording rules estimate the average CPU, CPU memory usages and @@ -2311,7 +2311,7 @@ groups: ( job:ceems_host_power_watts:pue{job="cpu-redfish-nvidia-gpu"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "cpu-redfish-nvidia-gpu", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "cpu-redfish-nvidia-gpu", "instance", "(.*)") ) ) @@ -2527,7 +2527,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # Profiling metrics @@ -2640,7 +2640,7 @@ groups: ( job:ceems_gpu_power_watts:pue{job="ipmi-nvidia-gpu"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "ipmi-nvidia-gpu", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "ipmi-nvidia-gpu", "instance", "(.*)") ) ) nvidia-gpu-gpu.rules @@ -2734,7 +2734,7 @@ groups: "(.*)" ) * on (provider) group_left () - ceems_emissions_gCo2_kWh{country_code="FR",provider="owid"} + ceems_emissions_gCo2_kWh{country_code="FR",provider="owid",instance="compute-emissions:9017"} # Profiling metrics @@ -2847,7 +2847,7 @@ groups: ( job:ceems_gpu_power_watts:pue{job="nvidia-gpu"} / 3.6e+06 * on (job) group_right () - label_replace(ceems_emissions_gCo2_kWh, "job", "nvidia-gpu", "instance", "(.*)") + label_replace(ceems_emissions_gCo2_kWh{instance="compute-emissions:9017"}, "job", "nvidia-gpu", "instance", "(.*)") ) ) amd-device-metrics-gpu-gpu.rules @@ -8682,7 +8682,6 @@ queries: total_cpu_emissions_gms: - owid_total: sum_over_time(sum by (uuid) (uuid:ceems_host_emissions_g_s:pue{uuid=~`{{.UUIDs}}`,provider=`owid`} > 0 < inf)[{{.Range}}:{{.ScrapeInterval}}]) * {{.ScrapeIntervalMilli}} / 1e3 @@ -8693,7 +8692,6 @@ queries: total_gpu_emissions_gms: - owid_total: sum_over_time(sum by (uuid) (uuid:ceems_gpu_emissions_g_s:pue{uuid=~`{{.UUIDs}}`,provider=`owid`} > 0 < inf)[{{.Range}}:{{.ScrapeInterval}}]) * {{.ScrapeIntervalMilli}} / 1e3 diff --git a/scripts/e2e-test.sh b/scripts/e2e-test.sh index 21622c92..4c408e5e 100755 --- a/scripts/e2e-test.sh +++ b/scripts/e2e-test.sh @@ -1835,6 +1835,7 @@ then --collector.ipmi.test-mode \ --collector.ipmi.dcmi.cmd="pkg/collector/testdata/ipmi/openipmi/ipmitool" \ --collector.rapl \ + --collector.emissions \ --collector.force-hostname="testhost-1" \ --web.listen-address "127.0.0.1:9016" \ --web.disable-exporter-metrics \ diff --git a/website/docs/deployments/guide.md b/website/docs/deployments/guide.md index e1beccd3..5f9b7853 100644 --- a/website/docs/deployments/guide.md +++ b/website/docs/deployments/guide.md @@ -235,6 +235,14 @@ API before using it in their production environment. ::: +:::tip[TIP] + +Avoid enabling emissions collector on the CEEMS exporter instances running on compute nodes. +At any given time ensure that there is only a **single** instance of emissions collector to +avoid duplicating the metrics on the Prometheus server. + +::: + CEEMS package supports static emission factors from historical data provided by [OWID](https://ourworldindata.org/co2-and-greenhouse-gas-emissions). To estimate emissions using this static factor, there is no need to deploy the above instance of CEEMS exporter and emissions will From 6e5533acadb2451735dfdf4d981b1bfea9124734 Mon Sep 17 00:00:00 2001 From: Mahendra Paipuri Date: Fri, 4 Sep 2026 16:59:32 +0200 Subject: [PATCH 2/3] ci: Deploye docs for only tags * This ensures that docs are always showing last stable version Signed-off-by: Mahendra Paipuri --- .github/workflows/step_docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/step_docs.yml b/.github/workflows/step_docs.yml index 4904a1ed..6c3c3d95 100644 --- a/.github/workflows/step_docs.yml +++ b/.github/workflows/step_docs.yml @@ -54,7 +54,8 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build-docs - if: github.ref == 'refs/heads/main' || github.ref_type == 'tag' + # Update docs only for tagged versions + if: github.ref_type == 'tag' steps: - name: Deploy to GitHub Pages id: deployment From 70f91bb43d7cfe01f85a9e0d5d532417837c14cd Mon Sep 17 00:00:00 2001 From: Mahendra Paipuri Date: Fri, 4 Sep 2026 18:32:09 +0200 Subject: [PATCH 3/3] style: Fix lint issues Signed-off-by: Mahendra Paipuri --- cmd/ceems_tool/rules.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/ceems_tool/rules.go b/cmd/ceems_tool/rules.go index 4b9cc6fa..e8d09d1a 100644 --- a/cmd/ceems_tool/rules.go +++ b/cmd/ceems_tool/rules.go @@ -457,6 +457,7 @@ func efInstance(ctx context.Context, api v1.API, start time.Time, end time.Time) // instances of emissions collectors are running which is not ideal. Emit a warning instances = slices.Compact(instances) slices.Sort(instances) + if len(instances) > 1 { fmt.Fprintln(os.Stderr, `WARNING: multiple instances of emissions collectors are detected. The generated recording rules might not work in this case. Ensure there is only one single instance of emissions collector running in the cluster.`) }