Skip to content
Closed
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 @@ -39,6 +39,7 @@ aliases:
* BUGFIX: [vmauth](https://docs.victoriametrics.com/operator/resources/vmauth/): allow `spec.unauthorizedUserAccessSpec` with only `access_log` set, without requiring `url_map`, `url_prefix`, or `targetRefs`. See [#2551](https://github.com/VictoriaMetrics/operator/issues/2551).
* BUGFIX: [vmoperator](https://docs.victoriametrics.com/operator/): a `VolumeClaimTemplate` size decrease, which Kubernetes cannot apply in-place to a bound `PersistentVolumeClaim`, was only logged and otherwise reported as a fully successful, `operational` reconcile. It now surfaces as a failed reconcile with the decline reason in `status.reason`, so the divergence between spec and actual PVC size is queryable and can be alerted on. See [#2512](https://github.com/VictoriaMetrics/operator/issues/2512).
* BUGFIX: [vmanomaly](https://docs.victoriametrics.com/operator/resources/vmanomaly/): pass previously skipped spec.extraEnvsFrom to anomaly pods. See [#2567](https://github.com/VictoriaMetrics/operator/issues/2567).
* BUGFIX: [vmnodescrape](https://docs.victoriametrics.com/operator/resources/vmnodescrape/), [vmservicescrape](https://docs.victoriametrics.com/operator/resources/vmservicescrape/), [vmpodscrape](https://docs.victoriametrics.com/operator/resources/vmpodscrape/), [vmprobe](https://docs.victoriametrics.com/operator/resources/vmprobe/), [vmscrapeconfig](https://docs.victoriametrics.com/operator/resources/vmscrapeconfig/), [vmstaticscrape](https://docs.victoriametrics.com/operator/resources/vmstaticscrape/), [vmrule](https://docs.victoriametrics.com/operator/resources/vmrule/), [vmuser](https://docs.victoriametrics.com/operator/resources/vmuser/), [vmalertmanagerconfig](https://docs.victoriametrics.com/operator/resources/vmalertmanagerconfig/), [vmanomalyconfig](https://docs.victoriametrics.com/operator/resources/vmanomalyconfig/): populate `status.updateStatus` (`Operational`/`Ignored`/`Failed`) after every reconcile instead of leaving it permanently unset, which was causing ArgoCD's health check to report these resources as stuck `Progressing` forever. ArgoCD's health check also needs to recognize the new `Ignored` value, see [argo-cd#29351](https://github.com/argoproj/argo-cd/pull/29351). See [#1181](https://github.com/VictoriaMetrics/operator/issues/1181).

## [v0.74.1](https://github.com/VictoriaMetrics/operator/releases/tag/v0.74.1)
**Release date:** 04 Aug 2026
Expand Down
45 changes: 45 additions & 0 deletions internal/controller/operator/controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"time"
"uuid"

"github.com/go-logr/logr"
"github.com/prometheus/client_golang/prometheus"
corev1 "k8s.io/api/core/v1"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
Expand All @@ -25,6 +26,8 @@ import (
k8sreconcile "sigs.k8s.io/controller-runtime/pkg/reconcile"

vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1"
"github.com/VictoriaMetrics/operator/internal/config"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/reconcile"
Expand Down Expand Up @@ -404,3 +407,45 @@ func reconcileAndTrackStatus[T client.Object, ST reconcile.StatusWithMetadata[ST
}
return result, nil
}

// releaseScrapeChildStatuses releases parentObject's Applied condition from every
// VMServiceScrape/VMPodScrape/VMNodeScrape/VMProbe/VMStaticScrape/VMScrapeConfig still
// carrying it, for use on VMAgent/VMSingle deletion, since no further reconcile of the
// deleted parent will ever release these otherwise.
func releaseScrapeChildStatuses(ctx context.Context, rclient client.Client, parentObject string) error {
var errs []error
if !build.IsControllerDisabled("VMServiceScrape") {
errs = append(errs, reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMServiceScrape(nil)))
}
if !build.IsControllerDisabled("VMPodScrape") {
errs = append(errs, reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMPodScrape(nil)))
}
if !build.IsControllerDisabled("VMNodeScrape") {
errs = append(errs, reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMNodeScrape(nil)))
}
if !build.IsControllerDisabled("VMProbe") {
errs = append(errs, reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMProbe(nil)))
}
if !build.IsControllerDisabled("VMStaticScrape") {
errs = append(errs, reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMStaticScrape(nil)))
}
if !build.IsControllerDisabled("VMScrapeConfig") {
errs = append(errs, reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMScrapeConfig(nil)))
}
return errors.Join(errs...)
}

// collectAndSyncScrapeChildStatus collects instance's VMAgent/VMSingle selection and syncs its aggregated Applied condition.
func collectAndSyncScrapeChildStatus[T any, PT interface {
*T
client.Object
GetStatusMetadata() *vmv1beta1.StatusMetadata
}](l logr.Logger, ctx context.Context, rclient client.Client, cfg *config.BaseOperatorConf, instance PT) error {
agentErr := collectVMAgentScrapes(l, ctx, rclient, cfg, instance)
singleErr := collectVMSingleScrapes(l, ctx, rclient, cfg, instance)
errs := []error{agentErr, singleErr}
if agentErr == nil && singleErr == nil {
errs = append(errs, reconcile.SyncAggregatedChildStatus(ctx, rclient, instance))
}
return errors.Join(errs...)
}
43 changes: 38 additions & 5 deletions internal/controller/operator/factory/reconcile/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ type objectWithStatus interface {
}

func childConditionType(parentObjectName string) string {
if len(strings.Split(parentObjectName, ".")) != 3 {
// < 3, not != 3: name or namespace may itself contain dots.
if len(strings.Split(parentObjectName, ".")) < 3 {
Comment thread
AndrewChubatiuk marked this conversation as resolved.
panic(fmt.Sprintf("BUG: unexpected format for parentObjectName=%q, want name.namespace.resource", parentObjectName))
}
return parentObjectName + vmv1beta1.ConditionDomainTypeAppliedSuffix
Expand Down Expand Up @@ -240,7 +241,7 @@ func releaseChildStatusCondition[T any, PT interface {

st.Conditions = removeConditionByType(st.Conditions, typeName)
st.ObservedGeneration = dst.GetGeneration()
writeAggregatedStatus(st, vmv1beta1.ConditionDomainTypeAppliedSuffix)
writeAggregatedStatus(st)
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Comment thread
AndrewChubatiuk marked this conversation as resolved.
if !reflect.DeepEqual(prevSt, st) {
if err := rclient.Status().Update(ctx, dst); err != nil {
if k8serrors.IsNotFound(err) {
Expand Down Expand Up @@ -285,7 +286,7 @@ func updateChildStatusConditions[T any, PT interface {
st.Conditions = setConditionTo(st.Conditions, currCond)
st.Conditions = removeStaleConditionsBySuffix(st.Conditions, vmv1beta1.ConditionDomainTypeAppliedSuffix)
st.ObservedGeneration = dst.GetGeneration()
writeAggregatedStatus(st, vmv1beta1.ConditionDomainTypeAppliedSuffix)
writeAggregatedStatus(st)
if !reflect.DeepEqual(prevSt, st) {
if err := rclient.Status().Update(ctx, dst); err != nil {
if k8serrors.IsNotFound(err) {
Expand Down Expand Up @@ -344,11 +345,11 @@ func removeStaleConditionsBySuffix(src []vmv1beta1.Condition, domainTypeSuffix s

// writeAggregatedStatus derives status from per-parent conditions; a child selected by
// multiple parents is only Failed if it fails on all of them, not just one.
func writeAggregatedStatus(stm *vmv1beta1.StatusMetadata, domainTypeSuffix string) {
func writeAggregatedStatus(stm *vmv1beta1.StatusMetadata) {
var appliedCount, failedCount int
var errorMessages []string
for _, c := range stm.Conditions {
if !strings.HasSuffix(c.Type, domainTypeSuffix) {
if !strings.HasSuffix(c.Type, vmv1beta1.ConditionDomainTypeAppliedSuffix) {
continue
}
if c.Status == "False" {
Expand All @@ -375,6 +376,38 @@ func writeAggregatedStatus(stm *vmv1beta1.StatusMetadata, domainTypeSuffix strin
}
}

// SyncAggregatedChildStatus recomputes status.updateStatus/reason for a config-selector child
// object (VMServiceScrape and friends) from its already-recorded per-parent conditions.
func SyncAggregatedChildStatus[T any, PT interface {
*T
objectWithStatus
}](ctx context.Context, rclient client.Client, instance PT) error {
nsn := types.NamespacedName{Namespace: instance.GetNamespace(), Name: instance.GetName()}
return retryOnConflict(func() error {
dst := PT(new(T))
if err := rclient.Get(ctx, nsn, dst); err != nil {
if k8serrors.IsNotFound(err) {
return nil
}
return err
}
st := dst.GetStatusMetadata()
prevSt := st.DeepCopy()
st.ObservedGeneration = dst.GetGeneration()
writeAggregatedStatus(st)
if reflect.DeepEqual(prevSt, st) {
return nil
}
if err := rclient.Status().Update(ctx, dst); err != nil {
if k8serrors.IsNotFound(err) {
return nil
}
return err
}
return nil
})
}

// adds 50% jitter to the given duration
func jitterForDuration(d time.Duration) time.Duration {
dv := d / 2
Expand Down
134 changes: 133 additions & 1 deletion internal/controller/operator/factory/reconcile/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,24 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"

vmv1 "github.com/VictoriaMetrics/operator/api/operator/v1"
vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/k8stools"
)

func TestChildConditionType_NameWithDots(t *testing.T) {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
var got string
assert.NotPanics(t, func() {
got = childConditionType("my.alert.ns.vmalert")
})
assert.Equal(t, "my.alert.ns.vmalert"+vmv1beta1.ConditionDomainTypeAppliedSuffix, got)
}

func TestWriteAggregatedStatus(t *testing.T) {
f := func(conditions []vmv1beta1.Condition, expectedStatus vmv1beta1.UpdateStatus, expectedReasonContains string) {
t.Helper()
stm := &vmv1beta1.StatusMetadata{Conditions: conditions}
writeAggregatedStatus(stm, vmv1beta1.ConditionDomainTypeAppliedSuffix)
writeAggregatedStatus(stm)
assert.Equal(t, expectedStatus, stm.UpdateStatus)
if expectedReasonContains == "" {
assert.Empty(t, stm.Reason)
Expand Down Expand Up @@ -212,3 +221,126 @@ func TestStatusForChildObjects_FallsBackWithoutIndexedClient(t *testing.T) {
assert.Equal(t, vmv1beta1.UpdateStatusIgnored, got.Status.UpdateStatus)
assert.Empty(t, got.Status.Conditions)
}

func TestSyncAggregatedChildStatus(t *testing.T) {
ctx := context.Background()
scrape := &vmv1beta1.VMServiceScrape{ObjectMeta: metav1.ObjectMeta{Name: "scrape", Namespace: "ns"}}
rclient := k8stools.GetTestClientWithObjects([]runtime.Object{scrape})

// no parent ever wrote a condition for it: must become Ignored
require.NoError(t, SyncAggregatedChildStatus(ctx, rclient, scrape))
var got vmv1beta1.VMServiceScrape
require.NoError(t, rclient.Get(ctx, types.NamespacedName{Namespace: "ns", Name: "scrape"}, &got))
assert.Equal(t, vmv1beta1.UpdateStatusIgnored, got.Status.UpdateStatus)

// a parent selects it and applies it successfully
require.NoError(t, StatusForChildObjects(ctx, rclient, "vmagent1.ns.vmagent", []*vmv1beta1.VMServiceScrape{scrape}))
require.NoError(t, SyncAggregatedChildStatus(ctx, rclient, scrape))
require.NoError(t, rclient.Get(ctx, types.NamespacedName{Namespace: "ns", Name: "scrape"}, &got))
assert.Equal(t, vmv1beta1.UpdateStatusOperational, got.Status.UpdateStatus)

// dropped by that same parent again: back to Ignored
require.NoError(t, StatusForChildObjects(ctx, rclient, "vmagent1.ns.vmagent", []*vmv1beta1.VMServiceScrape{}))
require.NoError(t, SyncAggregatedChildStatus(ctx, rclient, scrape))
require.NoError(t, rclient.Get(ctx, types.NamespacedName{Namespace: "ns", Name: "scrape"}, &got))
assert.Equal(t, vmv1beta1.UpdateStatusIgnored, got.Status.UpdateStatus)

// object gone: not found is not an error
require.NoError(t, SyncAggregatedChildStatus(ctx, rclient, &vmv1beta1.VMServiceScrape{
ObjectMeta: metav1.ObjectMeta{Name: "missing", Namespace: "ns"},
}))
}

// assertSyncAggregatedChildStatusRoundTrip exercises the same Ignored -> Operational -> Ignored
// cycle as TestSyncAggregatedChildStatus against a concrete child kind, to guard each controller
// that actually calls SyncAggregatedChildStatus (VMRule, VMUser, VMAlertmanagerConfig,
// VMAnomalyConfig, and the scrape kinds tested below).
func assertSyncAggregatedChildStatusRoundTrip[T any, PT interface {
*T
objectWithStatus
}](t *testing.T, obj PT, parent string) {
t.Helper()
ctx := context.Background()
rclient := k8stools.GetTestClientWithObjects([]runtime.Object{obj})
nsn := types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}

require.NoError(t, SyncAggregatedChildStatus(ctx, rclient, obj))
got := PT(new(T))
require.NoError(t, rclient.Get(ctx, nsn, got))
assert.Equal(t, vmv1beta1.UpdateStatusIgnored, got.GetStatusMetadata().UpdateStatus)

require.NoError(t, StatusForChildObjects(ctx, rclient, parent, []PT{obj}))
require.NoError(t, SyncAggregatedChildStatus(ctx, rclient, obj))
got = PT(new(T))
require.NoError(t, rclient.Get(ctx, nsn, got))
assert.Equal(t, vmv1beta1.UpdateStatusOperational, got.GetStatusMetadata().UpdateStatus)

corrupted := PT(new(T))
require.NoError(t, rclient.Get(ctx, nsn, corrupted))
corrupted.GetStatusMetadata().UpdateStatus = vmv1beta1.UpdateStatusFailed
require.NoError(t, rclient.Status().Update(ctx, corrupted))
require.NoError(t, SyncAggregatedChildStatus(ctx, rclient, obj))
got = PT(new(T))
require.NoError(t, rclient.Get(ctx, nsn, got))
assert.Equal(t, vmv1beta1.UpdateStatusOperational, got.GetStatusMetadata().UpdateStatus)

require.NoError(t, StatusForChildObjects(ctx, rclient, parent, []PT{}))
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
require.NoError(t, SyncAggregatedChildStatus(ctx, rclient, obj))
got = PT(new(T))
require.NoError(t, rclient.Get(ctx, nsn, got))
assert.Equal(t, vmv1beta1.UpdateStatusIgnored, got.GetStatusMetadata().UpdateStatus)
}

func TestSyncAggregatedChildStatus_VMRule(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1beta1.VMRule](t,
&vmv1beta1.VMRule{ObjectMeta: metav1.ObjectMeta{Name: "rule", Namespace: "ns"}},
"vmalert1.ns.vmalert")
}

func TestSyncAggregatedChildStatus_VMUser(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1beta1.VMUser](t,
&vmv1beta1.VMUser{ObjectMeta: metav1.ObjectMeta{Name: "user", Namespace: "ns"}},
"vmauth1.ns.vmauth")
}

func TestSyncAggregatedChildStatus_VMAlertmanagerConfig(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1beta1.VMAlertmanagerConfig](t,
&vmv1beta1.VMAlertmanagerConfig{ObjectMeta: metav1.ObjectMeta{Name: "cfg", Namespace: "ns"}},
"vmalertmanager1.ns.vmalertmanager")
}

func TestSyncAggregatedChildStatus_VMAnomalyConfig(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1.VMAnomalyConfig](t,
&vmv1.VMAnomalyConfig{ObjectMeta: metav1.ObjectMeta{Name: "cfg", Namespace: "ns"}},
"vmanomaly1.ns.vmanomaly")
}

func TestSyncAggregatedChildStatus_VMNodeScrape(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1beta1.VMNodeScrape](t,
&vmv1beta1.VMNodeScrape{ObjectMeta: metav1.ObjectMeta{Name: "scrape", Namespace: "ns"}},
"vmagent1.ns.vmagent")
}

func TestSyncAggregatedChildStatus_VMPodScrape(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1beta1.VMPodScrape](t,
&vmv1beta1.VMPodScrape{ObjectMeta: metav1.ObjectMeta{Name: "scrape", Namespace: "ns"}},
"vmagent1.ns.vmagent")
}

func TestSyncAggregatedChildStatus_VMProbe(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1beta1.VMProbe](t,
&vmv1beta1.VMProbe{ObjectMeta: metav1.ObjectMeta{Name: "probe", Namespace: "ns"}},
"vmagent1.ns.vmagent")
}

func TestSyncAggregatedChildStatus_VMScrapeConfig(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1beta1.VMScrapeConfig](t,
&vmv1beta1.VMScrapeConfig{ObjectMeta: metav1.ObjectMeta{Name: "scrapeconfig", Namespace: "ns"}},
"vmagent1.ns.vmagent")
}

func TestSyncAggregatedChildStatus_VMStaticScrape(t *testing.T) {
assertSyncAggregatedChildStatusRoundTrip[vmv1beta1.VMStaticScrape](t,
&vmv1beta1.VMStaticScrape{ObjectMeta: metav1.ObjectMeta{Name: "scrape", Namespace: "ns"}},
"vmagent1.ns.vmagent")
}
7 changes: 5 additions & 2 deletions internal/controller/operator/factory/vmalert/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ import (
// Alerting rules are dropped when hasNotifiers is false, since vmalert would have nowhere to
// send them; recording rules are unaffected.
func CreateOrUpdateRuleConfigMaps(ctx context.Context, rclient client.Client, cr *vmv1beta1.VMAlert, childCR *vmv1beta1.VMRule, hasNotifiers bool) ([]string, error) {
// fast path
if cr.IsUnmanaged() {
return nil, nil
if build.IsControllerDisabled("VMRule") {
return nil, nil
}
parentObject := fmt.Sprintf("%s.%s.vmalert", cr.Name, cr.Namespace)
return nil, reconcile.StatusForChildObjects(ctx, rclient, parentObject, []*vmv1beta1.VMRule(nil))
Comment thread
AndrewChubatiuk marked this conversation as resolved.
}
return reconcileVMAlertConfig(ctx, rclient, cr, childCR, hasNotifiers)
}
Expand Down
16 changes: 11 additions & 5 deletions internal/controller/operator/vmagent_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,23 @@ func (r *VMAgentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
return
}

if !instance.IsUnmanaged(nil) {
agentSync.RLock()
defer agentSync.RUnlock()
}

RegisterObjectStat(&instance, r.name)
if !instance.DeletionTimestamp.IsZero() {
agentSync.Lock()
defer agentSync.Unlock()
parentObject := fmt.Sprintf("%s.%s.vmagent", instance.Name, instance.Namespace)
if err = releaseScrapeChildStatuses(ctx, r.Client, parentObject); err != nil {
Comment thread
AndrewChubatiuk marked this conversation as resolved.
Comment thread
AndrewChubatiuk marked this conversation as resolved.
return
}
err = finalize.OnVMAgentDelete(ctx, r.Client, &instance)
return
}

if !instance.IsUnmanaged(nil) {
agentSync.RLock()
defer agentSync.RUnlock()
}

if instance.Status.ParsingSpecError != "" && !vmv1beta1.HasUnknownFields(instance.Status.ParsingSpecError) {
err = newParsingError(instance.Status.ParsingSpecError)
return
Expand Down
21 changes: 16 additions & 5 deletions internal/controller/operator/vmalert_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package operator

import (
"context"
"fmt"
"strings"
"sync"

Expand All @@ -31,9 +32,11 @@ import (

vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1"
"github.com/VictoriaMetrics/operator/internal/config"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/build"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/finalize"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/limiter"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/logger"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/reconcile"
"github.com/VictoriaMetrics/operator/internal/controller/operator/factory/vmalert"
)

Expand Down Expand Up @@ -87,17 +90,25 @@ func (r *VMAlertReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
return
}

if !instance.IsUnmanaged() {
alertSync.RLock()
defer alertSync.RUnlock()
}

RegisterObjectStat(&instance, r.name)
Comment thread
AndrewChubatiuk marked this conversation as resolved.
if !instance.DeletionTimestamp.IsZero() {
alertSync.Lock()
defer alertSync.Unlock()
if !build.IsControllerDisabled("VMRule") {
parentObject := fmt.Sprintf("%s.%s.vmalert", instance.Name, instance.Namespace)
if err = reconcile.StatusForChildObjects(ctx, r.Client, parentObject, []*vmv1beta1.VMRule(nil)); err != nil {
return
}
}
err = finalize.OnVMAlertDelete(ctx, r.Client, &instance)
return
}

if !instance.IsUnmanaged() {
alertSync.RLock()
defer alertSync.RUnlock()
}

if instance.Status.ParsingSpecError != "" && !vmv1beta1.HasUnknownFields(instance.Status.ParsingSpecError) {
err = newParsingError(instance.Status.ParsingSpecError)
return
Expand Down
Loading