Support multiple cases for VMAlertmanagerConfig and scrape configs - #2233
Support multiple cases for VMAlertmanagerConfig and scrape configs#2233AndrewChubatiuk wants to merge 1 commit into
Conversation
93e3461 to
ee74e84
Compare
There was a problem hiding this comment.
All reported issues were addressed across 42 files
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
bdbd13d to
8007250
Compare
|
@cubic-ai-dev |
@AndrewChubatiuk I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
2 issues found across 35 files
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
ae83fb0 to
04bc75c
Compare
45cca9d to
7dc2589
Compare
7dc2589 to
ff18a1d
Compare
There was a problem hiding this comment.
4 issues found across 49 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="api/operator/v1beta1/vmalert_types.go">
<violation number="1" location="api/operator/v1beta1/vmalert_types.go:205">
P2: When a VMAlert spec contains an unknown field, the v2 decoder error is not recognized by `HasUnknownFields`, so `IsUnmanaged` treats the CR as fatally malformed instead of continuing to manage it. Update unknown-field detection for the v2 error format or normalize this error before storing it.</violation>
</file>
<file name="api/operator/v1beta1/vmstaticscrape_types.go">
<violation number="1" location="api/operator/v1beta1/vmstaticscrape_types.go:104">
P2: When a VMStaticScrape spec contains an unknown member, this v2 rejection is not recognized by `HasUnknownFields`. Consequently, downgrade-preservation updates for these CRs are rejected instead of being allowed; update `HasUnknownFields` to recognize the v2 error format.</violation>
</file>
<file name="go.mod">
<violation number="1" location="go.mod:3">
P2: go.mod bumps the go directive to 1.27.0, but the build is still on golang:1.26.6 (Dockerfile `FROM golang:1.26.6 AS builder`, which .github/actions/setup-build-env also derives the build image from). A go 1.26.6 toolchain refuses to build a module whose go directive is 1.27.0, so the docker build now depends on GOTOOLCHAIN auto-downloading go1.27.0 at build time and fails if that download is unavailable/restricted. Update the Dockerfile builder image (and any pinned go version) to 1.27.0 to keep it in sync with this bump, or drop the go directive change if it is not required.</violation>
</file>
<file name="api/operator/v1beta1/vmsingle_types.go">
<violation number="1" location="api/operator/v1beta1/vmsingle_types.go:250">
P1: When a VMSingle contains fields from a newer operator version, this v2 decoder records `unknown object member name`, but `HasUnknownFields` does not recognize it. Update `HasUnknownFields` to recognize the v2 unknown-member error so downgrade compatibility remains intact.</violation>
</file>
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| } | ||
| if len(s.Spec) > 0 { | ||
| if err := UnmarshalSpecStrict(s.Spec, &cr.Spec); err != nil { | ||
| if err := json.Unmarshal(s.Spec, &cr.Spec, json.MatchCaseInsensitiveNames(true), json.RejectUnknownMembers(true)); err != nil { |
There was a problem hiding this comment.
P1: When a VMSingle contains fields from a newer operator version, this v2 decoder records unknown object member name, but HasUnknownFields does not recognize it. Update HasUnknownFields to recognize the v2 unknown-member error so downgrade compatibility remains intact.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/operator/v1beta1/vmsingle_types.go, line 250:
<comment>When a VMSingle contains fields from a newer operator version, this v2 decoder records `unknown object member name`, but `HasUnknownFields` does not recognize it. Update `HasUnknownFields` to recognize the v2 unknown-member error so downgrade compatibility remains intact.</comment>
<file context>
@@ -239,14 +240,14 @@ func (cr *VMSingle) UnmarshalJSON(src []byte) error {
}
if len(s.Spec) > 0 {
- if err := UnmarshalSpecStrict(s.Spec, &cr.Spec); err != nil {
+ if err := json.Unmarshal(s.Spec, &cr.Spec, json.MatchCaseInsensitiveNames(true), json.RejectUnknownMembers(true)); err != nil {
cr.Status.ParsingSpecError = fmt.Sprintf("cannot parse VMSingleSpec: %s, err: %s", string(s.Spec), err)
}
</file context>
| } | ||
| if len(s.Spec) > 0 { | ||
| if err := UnmarshalSpecStrict(s.Spec, &cr.Spec); err != nil { | ||
| if err := json.Unmarshal(s.Spec, &cr.Spec, json.MatchCaseInsensitiveNames(true), json.RejectUnknownMembers(true)); err != nil { |
There was a problem hiding this comment.
P2: When a VMAlert spec contains an unknown field, the v2 decoder error is not recognized by HasUnknownFields, so IsUnmanaged treats the CR as fatally malformed instead of continuing to manage it. Update unknown-field detection for the v2 error format or normalize this error before storing it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/operator/v1beta1/vmalert_types.go, line 205:
<comment>When a VMAlert spec contains an unknown field, the v2 decoder error is not recognized by `HasUnknownFields`, so `IsUnmanaged` treats the CR as fatally malformed instead of continuing to manage it. Update unknown-field detection for the v2 error format or normalize this error before storing it.</comment>
<file context>
@@ -194,14 +195,14 @@ func (cr *VMAlert) UnmarshalJSON(src []byte) error {
}
if len(s.Spec) > 0 {
- if err := UnmarshalSpecStrict(s.Spec, &cr.Spec); err != nil {
+ if err := json.Unmarshal(s.Spec, &cr.Spec, json.MatchCaseInsensitiveNames(true), json.RejectUnknownMembers(true)); err != nil {
cr.Status.ParsingSpecError = fmt.Sprintf("cannot parse VMAlertSpec: %s, err: %s", string(s.Spec), err)
}
</file context>
2b1f823 to
317ae06
Compare
There was a problem hiding this comment.
2 existing issues remain and 2 new issues found across 55 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="api/operator/v1beta1/common_scrapeparams.go">
<violation number="1" location="api/operator/v1beta1/common_scrapeparams.go:225">
P2: A typo in any relabel key now survives CRD admission and is silently ignored by `RelabelConfig.UnmarshalJSON`, so the requested relabel rule is not applied without an error. Preserve only supported aliases through explicit schema fields or validate unknown members instead of preserving the entire relabel object.</violation>
</file>
<file name="Makefile">
<violation number="1" location="Makefile:168">
P3: Under errexit (`.SHELLFLAGS = -ec`), the shell terminates as soon as `wait $$P1` returns non-zero, so `S1=$$?` (and `S2=$$?`) is never assigned on a linter failure. The final `[ $$S1 -eq 0 ] && [ $$S2 -eq 0 ]` only runs when both linters already succeeded, so the newly added status-capture logic is dead and the target still fails only because errexit aborts the recipe. Since errexit already guarantees the correct pass/fail behavior, revert to the simpler `wait $$P1; wait $$P2` in both `lint` and `lint-fix`, or restructure the capture (e.g. `wait $$P1 || S1=1; wait $$P2 || S1=$$((S1|1))`) if you intend to keep both runs.</violation>
</file>
Requires human review: Auto-approval blocked because this review re-detected 3 unresolved issues already reported by Cubic.
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| // RelabelConfig allows dynamic rewriting of the label set | ||
| // More info: https://docs.victoriametrics.com/victoriametrics/#relabeling | ||
| // +k8s:openapi-gen=true | ||
| // +kubebuilder:pruning:PreserveUnknownFields |
There was a problem hiding this comment.
P2: A typo in any relabel key now survives CRD admission and is silently ignored by RelabelConfig.UnmarshalJSON, so the requested relabel rule is not applied without an error. Preserve only supported aliases through explicit schema fields or validate unknown members instead of preserving the entire relabel object.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/operator/v1beta1/common_scrapeparams.go, line 225:
<comment>A typo in any relabel key now survives CRD admission and is silently ignored by `RelabelConfig.UnmarshalJSON`, so the requested relabel rule is not applied without an error. Preserve only supported aliases through explicit schema fields or validate unknown members instead of preserving the entire relabel object.</comment>
<file context>
@@ -222,32 +222,20 @@ func (ac *Authorization) validate() error {
// RelabelConfig allows dynamic rewriting of the label set
// More info: https://docs.victoriametrics.com/victoriametrics/#relabeling
// +k8s:openapi-gen=true
+// +kubebuilder:pruning:PreserveUnknownFields
type RelabelConfig struct {
- // UnderScoreSourceLabels - additional form of source labels source_labels
</file context>
| cd api && $(GOLANGCI_LINT) run operator/... & P1=$$!; \ | ||
| $(GOLANGCI_LINT) run & P2=$$!; \ | ||
| wait $$P1; wait $$P2 | ||
| wait $$P1; S1=$$?; \ |
There was a problem hiding this comment.
P3: Under errexit (.SHELLFLAGS = -ec), the shell terminates as soon as wait $$P1 returns non-zero, so S1=$$? (and S2=$$?) is never assigned on a linter failure. The final [ $$S1 -eq 0 ] && [ $$S2 -eq 0 ] only runs when both linters already succeeded, so the newly added status-capture logic is dead and the target still fails only because errexit aborts the recipe. Since errexit already guarantees the correct pass/fail behavior, revert to the simpler wait $$P1; wait $$P2 in both lint and lint-fix, or restructure the capture (e.g. wait $$P1 || S1=1; wait $$P2 || S1=$$((S1|1))) if you intend to keep both runs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Makefile, line 168:
<comment>Under errexit (`.SHELLFLAGS = -ec`), the shell terminates as soon as `wait $$P1` returns non-zero, so `S1=$$?` (and `S2=$$?`) is never assigned on a linter failure. The final `[ $$S1 -eq 0 ] && [ $$S2 -eq 0 ]` only runs when both linters already succeeded, so the newly added status-capture logic is dead and the target still fails only because errexit aborts the recipe. Since errexit already guarantees the correct pass/fail behavior, revert to the simpler `wait $$P1; wait $$P2` in both `lint` and `lint-fix`, or restructure the capture (e.g. `wait $$P1 || S1=1; wait $$P2 || S1=$$((S1|1))`) if you intend to keep both runs.</comment>
<file context>
@@ -165,13 +165,17 @@ test: manifests generate fmt vet envtest ## Run tests.
cd api && $(GOLANGCI_LINT) run operator/... & P1=$$!; \
$(GOLANGCI_LINT) run & P2=$$!; \
- wait $$P1; wait $$P2
+ wait $$P1; S1=$$?; \
+ wait $$P2; S2=$$?; \
+ [ $$S1 -eq 0 ] && [ $$S2 -eq 0 ]
</file context>
317ae06 to
c595ad7
Compare
There was a problem hiding this comment.
2 issues found across 57 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="api/operator/v1beta1/common_scrapeparams.go">
<violation number="1" location="api/operator/v1beta1/common_scrapeparams.go:234">
P1: Scrape configs using Prometheus' `source_labels`/`target_label` spelling now lose their relabeling: `case:ignore` ignores letter case but does not remove underscores, and the removed compatibility fields handled these keys. Decode both explicit JSON spellings in `RelabelConfig.UnmarshalJSON` while preserving the existing priority rule.</violation>
</file>
<file name="api/operator/v1beta1/vmextra_types.go">
<violation number="1" location="api/operator/v1beta1/vmextra_types.go:1457">
P2: Embedding `CommonAppsParams` causes this pruning marker to propagate to entire containing specs, not just the `host_aliases` compatibility field. The API server will retain arbitrary misspelled or obsolete `spec` members across these resources; preserve unknown fields on a narrowly scoped field or keep an explicit alias/custom conversion instead.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| // for the replace, keep, and drop actions. | ||
| // +optional | ||
| SourceLabels []string `json:"sourceLabels,omitempty" yaml:"-"` | ||
| SourceLabels []string `json:"sourceLabels,omitempty,case:ignore" yaml:"source_labels,omitempty"` |
There was a problem hiding this comment.
P1: Scrape configs using Prometheus' source_labels/target_label spelling now lose their relabeling: case:ignore ignores letter case but does not remove underscores, and the removed compatibility fields handled these keys. Decode both explicit JSON spellings in RelabelConfig.UnmarshalJSON while preserving the existing priority rule.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/operator/v1beta1/common_scrapeparams.go, line 234:
<comment>Scrape configs using Prometheus' `source_labels`/`target_label` spelling now lose their relabeling: `case:ignore` ignores letter case but does not remove underscores, and the removed compatibility fields handled these keys. Decode both explicit JSON spellings in `RelabelConfig.UnmarshalJSON` while preserving the existing priority rule.</comment>
<file context>
@@ -222,32 +225,20 @@ func (ac *Authorization) validate() error {
// for the replace, keep, and drop actions.
// +optional
- SourceLabels []string `json:"sourceLabels,omitempty" yaml:"-"`
+ SourceLabels []string `json:"sourceLabels,omitempty,case:ignore" yaml:"source_labels,omitempty"`
// Separator placed between concatenated source label values. default is ';'.
// +optional
</file context>
|
|
||
| // CommonAppsParams defines common params | ||
| // for deployment and statefulset specifications | ||
| // +kubebuilder:pruning:PreserveUnknownFields |
There was a problem hiding this comment.
P2: Embedding CommonAppsParams causes this pruning marker to propagate to entire containing specs, not just the host_aliases compatibility field. The API server will retain arbitrary misspelled or obsolete spec members across these resources; preserve unknown fields on a narrowly scoped field or keep an explicit alias/custom conversion instead.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/operator/v1beta1/vmextra_types.go, line 1457:
<comment>Embedding `CommonAppsParams` causes this pruning marker to propagate to entire containing specs, not just the `host_aliases` compatibility field. The API server will retain arbitrary misspelled or obsolete `spec` members across these resources; preserve unknown fields on a narrowly scoped field or keep an explicit alias/custom conversion instead.</comment>
<file context>
@@ -1457,6 +1454,7 @@ type CommonConfigReloaderParams struct {
// CommonAppsParams defines common params
// for deployment and statefulset specifications
+// +kubebuilder:pruning:PreserveUnknownFields
type CommonAppsParams struct {
// Affinity If specified, the pod's scheduling constraints.
</file context>
c595ad7 to
01853ea
Compare
There was a problem hiding this comment.
1 issue found across 188 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="go.mod">
<violation number="1" location="go.mod:30">
P2: The k8s.io libraries (api, apimachinery, client-go, apiextensions-apiserver) are bumped to v0.37.0, but sigs.k8s.io/controller-runtime is left at v0.24.1. The prior go.mod paired controller-runtime v0.24.1 with k8s v0.36.3, so controller-runtime v0.24.1 is the build/tested against the 0.36 API surface; mixing it with client-go/apimachinery 0.37.0 creates a k8s-library version skew that the Kubernetes project's own convention requires keeping in lockstep. If the 0.37 bump is needed for the new VMAlertmanagerConfig/ScrapeConfig features, bump controller-runtime to the version aligned with k8s 0.37 as part of the same change rather than leaving it behind, and verify the operator still builds/tests against the new API.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| k8s.io/apimachinery v0.37.0 | ||
| k8s.io/autoscaler/vertical-pod-autoscaler v1.7.1 | ||
| k8s.io/client-go v0.36.3 | ||
| k8s.io/client-go v0.37.0 |
There was a problem hiding this comment.
P2: The k8s.io libraries (api, apimachinery, client-go, apiextensions-apiserver) are bumped to v0.37.0, but sigs.k8s.io/controller-runtime is left at v0.24.1. The prior go.mod paired controller-runtime v0.24.1 with k8s v0.36.3, so controller-runtime v0.24.1 is the build/tested against the 0.36 API surface; mixing it with client-go/apimachinery 0.37.0 creates a k8s-library version skew that the Kubernetes project's own convention requires keeping in lockstep. If the 0.37 bump is needed for the new VMAlertmanagerConfig/ScrapeConfig features, bump controller-runtime to the version aligned with k8s 0.37 as part of the same change rather than leaving it behind, and verify the operator still builds/tests against the new API.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At go.mod, line 30:
<comment>The k8s.io libraries (api, apimachinery, client-go, apiextensions-apiserver) are bumped to v0.37.0, but sigs.k8s.io/controller-runtime is left at v0.24.1. The prior go.mod paired controller-runtime v0.24.1 with k8s v0.36.3, so controller-runtime v0.24.1 is the build/tested against the 0.36 API surface; mixing it with client-go/apimachinery 0.37.0 creates a k8s-library version skew that the Kubernetes project's own convention requires keeping in lockstep. If the 0.37 bump is needed for the new VMAlertmanagerConfig/ScrapeConfig features, bump controller-runtime to the version aligned with k8s 0.37 as part of the same change rather than leaving it behind, and verify the operator still builds/tests against the new API.</comment>
<file context>
@@ -23,11 +23,11 @@ require (
+ k8s.io/apimachinery v0.37.0
k8s.io/autoscaler/vertical-pod-autoscaler v1.7.1
- k8s.io/client-go v0.36.3
+ k8s.io/client-go v0.37.0
k8s.io/klog/v2 v2.140.0
k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3
</file context>
01853ea to
04252b3
Compare
There was a problem hiding this comment.
1 existing issue remains and 3 new issues found across 52 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="api/operator/v1beta1/vmpodscrape_types.go">
<violation number="1" location="api/operator/v1beta1/vmpodscrape_types.go:156">
P2: When a CR uses a non-lowercase top-level `Spec` key, `encoding/json/v2` does not populate `s.Spec`, so the resource can proceed with an empty `VMPodScrapeSpec` instead of preserving the previous case-insensitive behavior. Mark this shadow field with `case:ignore` (or enable case-insensitive matching for the outer decode).</violation>
</file>
<file name="go.mod">
<violation number="1" location="go.mod:3">
P3: This Go version bump (go 1.26.6 -> 1.27.0) is unrelated to the PR's purpose (multiple cases in VMAlertmanagerConfig/scrape configs) and has no CHANGELOG entry, unlike prior Go toolchain bumps which were recorded. Consider splitting it into its own PR or noting the toolchain change in docs/CHANGELOG.md.</violation>
</file>
<file name="api/operator/v1beta1/vmalertmanagerconfig_types.go">
<violation number="1" location="api/operator/v1beta1/vmalertmanagerconfig_types.go:1915">
P2: When a VMAlertmanagerConfig uses the camelCase `httpHeaders` spelling, the new strict decoder still rejects it because `HTTPHeaders` lacks `case:ignore`. Add `case:ignore` to the `HTTPHeaders` JSON tag so both `http_headers` and `httpHeaders` are accepted.</violation>
</file>
Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| type shadow struct { | ||
| *pcr | ||
| Spec json.RawMessage `json:"spec"` | ||
| Spec jsontext.Value `json:"spec"` |
There was a problem hiding this comment.
P2: When a CR uses a non-lowercase top-level Spec key, encoding/json/v2 does not populate s.Spec, so the resource can proceed with an empty VMPodScrapeSpec instead of preserving the previous case-insensitive behavior. Mark this shadow field with case:ignore (or enable case-insensitive matching for the outer decode).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/operator/v1beta1/vmpodscrape_types.go, line 156:
<comment>When a CR uses a non-lowercase top-level `Spec` key, `encoding/json/v2` does not populate `s.Spec`, so the resource can proceed with an empty `VMPodScrapeSpec` instead of preserving the previous case-insensitive behavior. Mark this shadow field with `case:ignore` (or enable case-insensitive matching for the outer decode).</comment>
<file context>
@@ -152,7 +153,7 @@ func (cr *VMPodScrape) UnmarshalJSON(src []byte) error {
type shadow struct {
*pcr
- Spec json.RawMessage `json:"spec"`
+ Spec jsontext.Value `json:"spec"`
}
s := shadow{pcr: (*pcr)(cr)}
</file context>
| Spec jsontext.Value `json:"spec"` | |
| Spec jsontext.Value `json:"spec,case:ignore"` |
| } | ||
|
|
||
| return nil | ||
| return UnmarshalSpecStrict(data, (*pc)(c)) |
There was a problem hiding this comment.
P2: When a VMAlertmanagerConfig uses the camelCase httpHeaders spelling, the new strict decoder still rejects it because HTTPHeaders lacks case:ignore. Add case:ignore to the HTTPHeaders JSON tag so both http_headers and httpHeaders are accepted.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At api/operator/v1beta1/vmalertmanagerconfig_types.go, line 1915:
<comment>When a VMAlertmanagerConfig uses the camelCase `httpHeaders` spelling, the new strict decoder still rejects it because `HTTPHeaders` lacks `case:ignore`. Add `case:ignore` to the `HTTPHeaders` JSON tag so both `http_headers` and `httpHeaders` are accepted.</comment>
<file context>
@@ -1849,35 +1890,29 @@ func (c *HTTPConfig) validateArbitraryFSAccess() error {
- }
-
- return nil
+ return UnmarshalSpecStrict(data, (*pc)(c))
}
</file context>
| module github.com/VictoriaMetrics/operator | ||
|
|
||
| go 1.26.6 | ||
| go 1.27.0 |
There was a problem hiding this comment.
P3: This Go version bump (go 1.26.6 -> 1.27.0) is unrelated to the PR's purpose (multiple cases in VMAlertmanagerConfig/scrape configs) and has no CHANGELOG entry, unlike prior Go toolchain bumps which were recorded. Consider splitting it into its own PR or noting the toolchain change in docs/CHANGELOG.md.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At go.mod, line 3:
<comment>This Go version bump (go 1.26.6 -> 1.27.0) is unrelated to the PR's purpose (multiple cases in VMAlertmanagerConfig/scrape configs) and has no CHANGELOG entry, unlike prior Go toolchain bumps which were recorded. Consider splitting it into its own PR or noting the toolchain change in docs/CHANGELOG.md.</comment>
<file context>
@@ -1,6 +1,6 @@
module github.com/VictoriaMetrics/operator
-go 1.26.6
+go 1.27.0
require (
</file context>
04252b3 to
6c7ed82
Compare
fixes #1146
Summary by cubic
Adds case-insensitive support for both snake_case and camelCase in scrape CRDs and
VMAlertmanagerConfig, with strict spec validation and clearer errors. Preserves unknown fields inVMAlertmanagerConfig.specand removes underscore-only aliases across the API and controller.New Features
case:ignoretags on scrape params andjson.MatchCaseInsensitiveNames(true)+json.RejectUnknownMembers(true)during spec unmarshalling; errors now say “unknown object member name”.VMAlertmanagerConfig.spec(x-kubernetes-preserve-unknown-fields: true,+kubebuilder:pruning:PreserveUnknownFields).host_aliases,UnderScoreSourceLabels,UnderScoreTargetLabel); CRD schemas, controller logic, docs, and tests updated. API docs show both accepted aliases.Dependencies
github.com/go-json-experiment/json.Written for commit 7dc2589. Summary will update on new commits.