Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ aliases:
* BUGFIX: [helm-converter](https://docs.victoriametrics.com/operator/helm-converter/): convert the `victoria-metrics-alert` chart's legacy values the same way the chart does: `server.remote.read`, `server.remote.write`, `server.notifier.alertmanager`, nested `basicAuth`/`bearer` auth keys and a list-valued `server.notifier.url`. Previously, they were silently dropped, so the generated `VMAlert` lost its remote storage, notifiers and credentials. Plaintext `server.remoteWrite` credentials, legacy or not, are now moved into a generated Secret like the other blocks; before, they were dropped too. The bug has existed since the converter was added in [v0.69.0](https://github.com/VictoriaMetrics/operator/releases/tag/v0.69.0). See [#2601](https://github.com/VictoriaMetrics/operator/issues/2601).
* BUGFIX: [helm-converter](https://docs.victoriametrics.com/operator/helm-converter/): omit `spec.notifier` for the `victoria-metrics-alert` chart when no notifier URL is set. Previously, the converter generated `spec.notifier: {}`, which the operator webhook rejects, so the output could not be applied. The conversion now fails with an explicit error when the values contain alerting rules but no notifier, since the operator would silently ignore those rules, and when `alertmanager.enabled: true` would make the chart use its bundled Alertmanager, which has no CR equivalent. See [#2601](https://github.com/VictoriaMetrics/operator/issues/2601).
* BUGFIX: [helm-converter](https://docs.victoriametrics.com/operator/helm-converter/): convert the charts' `license` and `global.license` blocks into `spec.license` of `VMSingle`, `VMCluster`, `VMAgent`, `VMAlert`, `VMAuth`, `VMAnomaly`, `VLCluster` and `VLAgent` (from both `victoria-logs-agent` and `victoria-logs-collector`). Previously, they were dropped, so the generated CRs started without the enterprise license. The precedence matches the charts: a plaintext key (`license.key`, then `global.license.key`) wins over any Secret reference, which is taken from `license.secret`, `global.license.secret`, `license.keyRef` and `global.license.keyRef`, in that order. See [#2601](https://github.com/VictoriaMetrics/operator/issues/2601).
* BUGFIX: [vmprobe](https://docs.victoriametrics.com/operator/resources/vmprobe/): apply `relabelConfigs` and `metricRelabelConfigs` of the referenced `scrapeClass` to the scrape config of a `VMProbe`, as it is done for the other scrape resources. Previously only the authentication settings of the scrape class were applied. See [#2738](https://github.com/VictoriaMetrics/operator/pull/2738).

## [v0.75.0](https://github.com/VictoriaMetrics/operator/releases/tag/v0.75.0)
**Release date:** 25 September 2026
Expand Down
8 changes: 8 additions & 0 deletions internal/controller/operator/factory/vmscrapes/probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package vmscrapes
import (
"context"
"fmt"
"slices"

"gopkg.in/yaml.v2"

Expand Down Expand Up @@ -65,6 +66,7 @@ func generateProbeConfig(
scrapeClass := getScrapeClass(spec.ScrapeClassName, sp)
if scrapeClass != nil {
mergeEndpointAuthWithScrapeClass(&spec.EndpointAuth, scrapeClass)
spec.MetricRelabelConfigs = slices.Concat(scrapeClass.MetricRelabelConfigs, spec.MetricRelabelConfigs)

@cubic-dev-ai cubic-dev-ai Bot Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: generateProbeConfig mutates sc.Spec.MetricRelabelConfigs, so repeated generation of the same VMProbe prefixes the class rules again. Keep the merged list local and pass that list to addMetricRelabelingsTo instead of modifying the input spec.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At internal/controller/operator/factory/vmscrapes/probe.go, line 69:

<comment>`generateProbeConfig` mutates `sc.Spec.MetricRelabelConfigs`, so repeated generation of the same `VMProbe` prefixes the class rules again. Keep the merged list local and pass that list to `addMetricRelabelingsTo` instead of modifying the input spec.</comment>

<file context>
@@ -65,6 +66,7 @@ func generateProbeConfig(
 	scrapeClass := getScrapeClass(spec.ScrapeClassName, sp)
 	if scrapeClass != nil {
 		mergeEndpointAuthWithScrapeClass(&spec.EndpointAuth, scrapeClass)
+		spec.MetricRelabelConfigs = slices.Concat(scrapeClass.MetricRelabelConfigs, spec.MetricRelabelConfigs)
 	}
 
</file context>
Fix with cubic

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each config generation lists the probes again (selectProbes reads a new VMProbeList) and calls generateProbeConfig once per probe, so the class rules are added once. The other scrape objects merge the class lists into their spec the same way (mergeEndpointRelabelingsWithScrapeClass), and this function already writes the path and module defaults into the spec, so I kept that pattern.

}

// add defaults
Expand All @@ -89,6 +91,12 @@ func generateProbeConfig(

var relabelings []yaml.MapSlice

if scrapeClass != nil {
for _, r := range scrapeClass.RelabelConfigs {
relabelings = append(relabelings, generateRelabelConfig(r))
}
}

staticTarget := spec.Targets.StaticConfig
if spec.Targets.Static != nil {
staticTarget = spec.Targets.Static
Expand Down
61 changes: 61 additions & 0 deletions internal/controller/operator/factory/vmscrapes/probe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,67 @@ relabel_configs:
target_label: instance
- target_label: __address__
replacement: blackbox:9115
`,
})

// scrapeClass relabelings and metric relabelings
f(opts{
cr: &vmv1beta1.VMAgent{
ObjectMeta: metav1.ObjectMeta{Name: "default-vmagent", Namespace: "default"},
Spec: vmv1beta1.VMAgentSpec{
CommonScrapeParams: vmv1beta1.CommonScrapeParams{
ScrapeClasses: []vmv1beta1.ScrapeClass{
{
Name: "probe-class",
EndpointRelabelings: vmv1beta1.EndpointRelabelings{
RelabelConfigs: []*vmv1beta1.RelabelConfig{{TargetLabel: "env", Replacement: ptr.To("prod")}},
MetricRelabelConfigs: []*vmv1beta1.RelabelConfig{{SourceLabels: []string{"__name__"}, Regex: vmv1beta1.StringOrArray{"probe_.*"}, Action: "keep"}},
},
},
},
},
},
},
sc: &vmv1beta1.VMProbe{
ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "class-probe"},
Spec: vmv1beta1.VMProbeSpec{
ScrapeClassName: ptr.To("probe-class"),
VMProberSpec: vmv1beta1.VMProberSpec{URL: "blackbox:9115"},
Targets: vmv1beta1.VMProbeTargets{
Static: &vmv1beta1.VMProbeTargetStatic{
Targets: []string{"host-1"},
RelabelConfigs: []*vmv1beta1.RelabelConfig{{TargetLabel: "env", Replacement: ptr.To("user")}},
},
},
MetricRelabelConfigs: []*vmv1beta1.RelabelConfig{{TargetLabel: "probe", Replacement: ptr.To("class-probe")}},
},
},
want: `job_name: probe/default/class-probe
honor_labels: false
metrics_path: /probe
static_configs:
- targets:
- host-1
relabel_configs:
- target_label: env
replacement: prod
- source_labels:
- __address__
target_label: __param_target
- target_label: env
replacement: user
- source_labels:
- __param_target
target_label: instance
- target_label: __address__
replacement: blackbox:9115
metric_relabel_configs:
- source_labels:
- __name__
regex: probe_.*
action: keep
- target_label: probe
replacement: class-probe
`,
})
}
Loading