From 0f3bb17951583c6c9cd5acef1ba4c497d9e7b664 Mon Sep 17 00:00:00 2001 From: Petr Muller Date: Tue, 30 Jan 2024 18:29:48 +0100 Subject: [PATCH 1/4] status: extract status update logic to method I would like to unit-test this logic but `syncStatus` does apiserver operation so such tests would need a fake. Computing new status is enough logic to constitute a method, so we can extract it so that we can test it without a mock server. Extracted with an IDE refactor helper. --- pkg/cvo/status.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pkg/cvo/status.go b/pkg/cvo/status.go index 37e9427880..e1fa76af7e 100644 --- a/pkg/cvo/status.go +++ b/pkg/cvo/status.go @@ -198,6 +198,17 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1 original = config.DeepCopy() } + updateClusterVersionStatus(config, status, optr, validationErrs) + + if klog.V(6).Enabled() { + klog.Infof("Apply config: %s", diff.ObjectReflectDiff(original, config)) + } + updated, err := applyClusterVersionStatus(ctx, optr.client.ConfigV1(), config, original) + optr.rememberLastUpdate(updated) + return err +} + +func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWorkerStatus, optr *Operator, validationErrs field.ErrorList) { config.Status.ObservedGeneration = status.Generation if len(status.VersionHash) > 0 { config.Status.VersionHash = status.VersionHash @@ -373,13 +384,6 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1 LastTransitionTime: now, }) } - - if klog.V(6).Enabled() { - klog.Infof("Apply config: %s", diff.ObjectReflectDiff(original, config)) - } - updated, err := applyClusterVersionStatus(ctx, optr.client.ConfigV1(), config, original) - optr.rememberLastUpdate(updated) - return err } func setImplicitlyEnabledCapabilitiesCondition(config *configv1.ClusterVersion, implicitlyEnabled []configv1.ClusterVersionCapability, From f99d4a00dc0ba3a698b3ed4f0a9ec7fcf45f7e07 Mon Sep 17 00:00:00 2001 From: Petr Muller Date: Tue, 30 Jan 2024 18:39:16 +0100 Subject: [PATCH 2/4] status: uncouple `updateClusterVersionStatus` with `Operator` `Operator` is a pretty beefy object with access to half of the universe, but `updateClusterVersionStatus` only uses its member very little. It reads its `release` member and calls its `getAvailableUpdates` method. We can uncouple the long method by passing these two members as parameters. It is slightly awkward for the `getAvailableUpdates` method but I think the benefits outweight this. --- pkg/cvo/cvo.go | 6 +++--- pkg/cvo/cvo_test.go | 2 +- pkg/cvo/status.go | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/cvo/cvo.go b/pkg/cvo/cvo.go index 81f9c51419..134f2cafd5 100644 --- a/pkg/cvo/cvo.go +++ b/pkg/cvo/cvo.go @@ -811,18 +811,18 @@ func versionStringFromRelease(release configv1.Release) string { // are used as fallbacks for any properties not defined in the release // image itself. func (optr *Operator) currentVersion() configv1.Release { - return optr.mergeReleaseMetadata(optr.release) + return mergeReleaseMetadata(optr.release, optr.getAvailableUpdates) } // mergeReleaseMetadata returns a deep copy of the input release. // Values from any matching availableUpdates release are used as // fallbacks for any properties not defined in the input release. -func (optr *Operator) mergeReleaseMetadata(release configv1.Release) configv1.Release { +func mergeReleaseMetadata(release configv1.Release, getAvailableUpdates func() *availableUpdates) configv1.Release { merged := *release.DeepCopy() if merged.Version == "" || len(merged.URL) == 0 || merged.Channels == nil { // only fill in missing values from availableUpdates, to avoid clobbering data from payload.LoadUpdate. - availableUpdates := optr.getAvailableUpdates() + availableUpdates := getAvailableUpdates() if availableUpdates != nil { var update *configv1.Release if equalDigest(merged.Image, availableUpdates.Current.Image) { diff --git a/pkg/cvo/cvo_test.go b/pkg/cvo/cvo_test.go index cfe0ce386f..cb3d693a90 100644 --- a/pkg/cvo/cvo_test.go +++ b/pkg/cvo/cvo_test.go @@ -4343,7 +4343,7 @@ func TestOperator_mergeReleaseMetadata(t *testing.T) { } { t.Run(testCase.name, func(t *testing.T) { optr := Operator{availableUpdates: testCase.availableUpdates} - actual := optr.mergeReleaseMetadata(testCase.input) + actual := mergeReleaseMetadata(testCase.input, optr.getAvailableUpdates) if !reflect.DeepEqual(actual, testCase.expected) { t.Fatalf("unexpected: %s", diff.ObjectReflectDiff(testCase.expected, actual)) } diff --git a/pkg/cvo/status.go b/pkg/cvo/status.go index e1fa76af7e..4424d0a816 100644 --- a/pkg/cvo/status.go +++ b/pkg/cvo/status.go @@ -198,7 +198,7 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1 original = config.DeepCopy() } - updateClusterVersionStatus(config, status, optr, validationErrs) + updateClusterVersionStatus(config, status, optr.release, optr.getAvailableUpdates, validationErrs) if klog.V(6).Enabled() { klog.Infof("Apply config: %s", diff.ObjectReflectDiff(original, config)) @@ -208,7 +208,7 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1 return err } -func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWorkerStatus, optr *Operator, validationErrs field.ErrorList) { +func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWorkerStatus, release configv1.Release, getAvailableUpdates func() *availableUpdates, validationErrs field.ErrorList) { config.Status.ObservedGeneration = status.Generation if len(status.VersionHash) > 0 { config.Status.VersionHash = status.VersionHash @@ -216,19 +216,19 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor now := metav1.Now() version := versionStringFromRelease(status.Actual) - if status.Actual.Image == optr.release.Image { + if status.Actual.Image == release.Image { // backfill any missing information from the operator (payload). if status.Actual.Version == "" { - status.Actual.Version = optr.release.Version + status.Actual.Version = release.Version } if len(status.Actual.URL) == 0 { - status.Actual.URL = optr.release.URL + status.Actual.URL = release.URL } if status.Actual.Channels == nil { - status.Actual.Channels = append(optr.release.Channels[:0:0], optr.release.Channels...) // copy + status.Actual.Channels = append(release.Channels[:0:0], release.Channels...) // copy } } - desired := optr.mergeReleaseMetadata(status.Actual) + desired := mergeReleaseMetadata(status.Actual, getAvailableUpdates) risksMsg := "" if desired.Image == status.loadPayloadStatus.Update.Image { From 77a0ef3a779c6ccde9dbdcc2c3de9bbfb3e41750 Mon Sep 17 00:00:00 2001 From: Petr Muller Date: Tue, 30 Jan 2024 18:47:57 +0100 Subject: [PATCH 3/4] status: work with `ClusterVersionStatus` instead of `ClusterVersion` Status-computing methods can only read and modify `ClusterVersionStatus` instead of `ClusterVersion`, resulting in simpler code and lower chance of making a mistake. --- pkg/cvo/status.go | 92 +++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/pkg/cvo/status.go b/pkg/cvo/status.go index 4424d0a816..132403ab08 100644 --- a/pkg/cvo/status.go +++ b/pkg/cvo/status.go @@ -65,21 +65,21 @@ func mergeEqualVersions(current *configv1.UpdateHistory, desired configv1.Releas return false } -func mergeOperatorHistory(config *configv1.ClusterVersion, desired configv1.Release, verified bool, now metav1.Time, +func mergeOperatorHistory(cvStatus *configv1.ClusterVersionStatus, desired configv1.Release, verified bool, now metav1.Time, completed bool, acceptedRisksMsg string, localPayload bool) { // if we have no image, we cannot reproduce the update later and so it cannot be part of the history if len(desired.Image) == 0 { // make the array empty - if config.Status.History == nil { - config.Status.History = []configv1.UpdateHistory{} + if cvStatus.History == nil { + cvStatus.History = []configv1.UpdateHistory{} } return } - if len(config.Status.History) == 0 { + if len(cvStatus.History) == 0 { klog.V(2).Infof("initialize new history completed=%t desired=%#v", completed, desired) - config.Status.History = append(config.Status.History, configv1.UpdateHistory{ + cvStatus.History = append(cvStatus.History, configv1.UpdateHistory{ Version: desired.Version, Image: desired.Image, @@ -89,7 +89,7 @@ func mergeOperatorHistory(config *configv1.ClusterVersion, desired configv1.Rele }) } - last := &config.Status.History[0] + last := &cvStatus.History[0] if len(last.State) == 0 { last.State = configv1.PartialUpdate @@ -114,7 +114,7 @@ func mergeOperatorHistory(config *configv1.ClusterVersion, desired configv1.Rele last.CompletionTime = &now } if completed { - config.Status.History = append([]configv1.UpdateHistory{ + cvStatus.History = append([]configv1.UpdateHistory{ { Version: desired.Version, Image: desired.Image, @@ -124,9 +124,9 @@ func mergeOperatorHistory(config *configv1.ClusterVersion, desired configv1.Rele CompletionTime: &now, AcceptedRisks: acceptedRisksMsg, }, - }, config.Status.History...) + }, cvStatus.History...) } else { - config.Status.History = append([]configv1.UpdateHistory{ + cvStatus.History = append([]configv1.UpdateHistory{ { Version: desired.Version, Image: desired.Image, @@ -135,27 +135,27 @@ func mergeOperatorHistory(config *configv1.ClusterVersion, desired configv1.Rele StartedTime: now, AcceptedRisks: acceptedRisksMsg, }, - }, config.Status.History...) + }, cvStatus.History...) } } // leave this here in case we find other future history bugs and need to debug it - if klog.V(2).Enabled() && len(config.Status.History) > 1 { - if config.Status.History[0].Image == config.Status.History[1].Image && config.Status.History[0].Version == config.Status.History[1].Version { - data, _ := json.MarshalIndent(config.Status.History, "", " ") + if klog.V(2).Enabled() && len(cvStatus.History) > 1 { + if cvStatus.History[0].Image == cvStatus.History[1].Image && cvStatus.History[0].Version == cvStatus.History[1].Version { + data, _ := json.MarshalIndent(cvStatus.History, "", " ") panic(fmt.Errorf("tried to update cluster version history to contain duplicate image entries: %s", string(data))) } } // payloads can be verified during sync if verified { - config.Status.History[0].Verified = true + cvStatus.History[0].Verified = true } // Prune least informative history entry when at maxHistory. - config.Status.History = prune(config.Status.History, MaxHistory) + cvStatus.History = prune(cvStatus.History, MaxHistory) - config.Status.Desired = desired + cvStatus.Desired = desired } // ClusterVersionInvalid indicates that the cluster version has an error that prevents the server from @@ -198,7 +198,7 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1 original = config.DeepCopy() } - updateClusterVersionStatus(config, status, optr.release, optr.getAvailableUpdates, validationErrs) + updateClusterVersionStatus(&config.Status, status, optr.release, optr.getAvailableUpdates, validationErrs) if klog.V(6).Enabled() { klog.Infof("Apply config: %s", diff.ObjectReflectDiff(original, config)) @@ -208,10 +208,10 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1 return err } -func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWorkerStatus, release configv1.Release, getAvailableUpdates func() *availableUpdates, validationErrs field.ErrorList) { - config.Status.ObservedGeneration = status.Generation +func updateClusterVersionStatus(cvStatus *configv1.ClusterVersionStatus, status *SyncWorkerStatus, release configv1.Release, getAvailableUpdates func() *availableUpdates, validationErrs field.ErrorList) { + cvStatus.ObservedGeneration = status.Generation if len(status.VersionHash) > 0 { - config.Status.VersionHash = status.VersionHash + cvStatus.VersionHash = status.VersionHash } now := metav1.Now() @@ -235,9 +235,9 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor risksMsg = status.loadPayloadStatus.AcceptedRisks } - mergeOperatorHistory(config, desired, status.Verified, now, status.Completed > 0, risksMsg, status.loadPayloadStatus.Local) + mergeOperatorHistory(cvStatus, desired, status.Verified, now, status.Completed > 0, risksMsg, status.loadPayloadStatus.Local) - config.Status.Capabilities = status.CapabilitiesStatus.Status + cvStatus.Capabilities = status.CapabilitiesStatus.Status // update validation errors var reason string @@ -253,7 +253,7 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor } reason = "InvalidClusterVersion" - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: ClusterVersionInvalid, Status: configv1.ConditionTrue, Reason: reason, @@ -261,18 +261,18 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor LastTransitionTime: now, }) } else { - resourcemerge.RemoveOperatorStatusCondition(&config.Status.Conditions, ClusterVersionInvalid) + resourcemerge.RemoveOperatorStatusCondition(&cvStatus.Conditions, ClusterVersionInvalid) } // set the implicitly enabled capabilities condition - setImplicitlyEnabledCapabilitiesCondition(config, status.CapabilitiesStatus.ImplicitlyEnabledCaps, now) + setImplicitlyEnabledCapabilitiesCondition(cvStatus, status.CapabilitiesStatus.ImplicitlyEnabledCaps, now) // set the desired release accepted condition - setDesiredReleaseAcceptedCondition(config, status.loadPayloadStatus, now) + setDesiredReleaseAcceptedCondition(cvStatus, status.loadPayloadStatus, now) // set the available condition if status.Completed > 0 { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: configv1.OperatorAvailable, Status: configv1.ConditionTrue, Message: fmt.Sprintf("Done applying %s", version), @@ -280,15 +280,15 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor }) } // default the available condition if not set - if resourcemerge.FindOperatorStatusCondition(config.Status.Conditions, configv1.OperatorAvailable) == nil { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + if resourcemerge.FindOperatorStatusCondition(cvStatus.Conditions, configv1.OperatorAvailable) == nil { + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: configv1.OperatorAvailable, Status: configv1.ConditionFalse, LastTransitionTime: now, }) } - progressReason, progressMessage, skipFailure := convertErrorToProgressing(config.Status.History, now.Time, status) + progressReason, progressMessage, skipFailure := convertErrorToProgressing(cvStatus.History, now.Time, status) if err := status.Failure; err != nil && !skipFailure { var reason string @@ -303,7 +303,7 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor } // set the failing condition - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: ClusterStatusFailing, Status: configv1.ConditionTrue, Reason: reason, @@ -313,7 +313,7 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor // update progressing if status.Reconciling { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: configv1.OperatorProgressing, Status: configv1.ConditionFalse, Reason: reason, @@ -321,7 +321,7 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor LastTransitionTime: now, }) } else { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: configv1.OperatorProgressing, Status: configv1.ConditionTrue, Reason: reason, @@ -332,7 +332,7 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor } else { // clear the failure condition - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{Type: ClusterStatusFailing, Status: configv1.ConditionFalse, LastTransitionTime: now}) + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{Type: ClusterStatusFailing, Status: configv1.ConditionFalse, LastTransitionTime: now}) // update progressing if status.Reconciling { @@ -340,7 +340,7 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor if len(validationErrs) > 0 { message = fmt.Sprintf("Stopped at %s: the cluster version is invalid", version) } - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: configv1.OperatorProgressing, Status: configv1.ConditionFalse, Reason: reason, @@ -366,7 +366,7 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor default: message = fmt.Sprintf("Working towards %s", version) } - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: configv1.OperatorProgressing, Status: configv1.ConditionTrue, Reason: reason, @@ -377,8 +377,8 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor } // default retrieved updates if it is not set - if resourcemerge.FindOperatorStatusCondition(config.Status.Conditions, configv1.RetrievedUpdates) == nil { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + if resourcemerge.FindOperatorStatusCondition(cvStatus.Conditions, configv1.RetrievedUpdates) == nil { + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: configv1.RetrievedUpdates, Status: configv1.ConditionFalse, LastTransitionTime: now, @@ -386,7 +386,7 @@ func updateClusterVersionStatus(config *configv1.ClusterVersion, status *SyncWor } } -func setImplicitlyEnabledCapabilitiesCondition(config *configv1.ClusterVersion, implicitlyEnabled []configv1.ClusterVersionCapability, +func setImplicitlyEnabledCapabilitiesCondition(cvStatus *configv1.ClusterVersionStatus, implicitlyEnabled []configv1.ClusterVersionCapability, now metav1.Time) { if len(implicitlyEnabled) > 0 { @@ -398,7 +398,7 @@ func setImplicitlyEnabledCapabilitiesCondition(config *configv1.ClusterVersion, sort.Strings(caps) message = message + strings.Join([]string(caps), ", ") - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: ImplicitlyEnabledCapabilities, Status: configv1.ConditionTrue, Reason: "CapabilitiesImplicitlyEnabled", @@ -406,7 +406,7 @@ func setImplicitlyEnabledCapabilitiesCondition(config *configv1.ClusterVersion, LastTransitionTime: now, }) } else { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: ImplicitlyEnabledCapabilities, Status: configv1.ConditionFalse, Reason: "AsExpected", @@ -416,9 +416,9 @@ func setImplicitlyEnabledCapabilitiesCondition(config *configv1.ClusterVersion, } } -func setDesiredReleaseAcceptedCondition(config *configv1.ClusterVersion, status LoadPayloadStatus, now metav1.Time) { +func setDesiredReleaseAcceptedCondition(cvStatus *configv1.ClusterVersionStatus, status LoadPayloadStatus, now metav1.Time) { if status.Step == "PayloadLoaded" { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: DesiredReleaseAccepted, Status: configv1.ConditionTrue, Reason: status.Step, @@ -427,7 +427,7 @@ func setDesiredReleaseAcceptedCondition(config *configv1.ClusterVersion, status }) } else if status.Step != "" { if status.Failure != nil { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: DesiredReleaseAccepted, Status: configv1.ConditionFalse, Reason: status.Step, @@ -435,7 +435,7 @@ func setDesiredReleaseAcceptedCondition(config *configv1.ClusterVersion, status LastTransitionTime: now, }) } else { - resourcemerge.SetOperatorStatusCondition(&config.Status.Conditions, configv1.ClusterOperatorStatusCondition{ + resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{ Type: DesiredReleaseAccepted, Status: configv1.ConditionUnknown, Reason: status.Step, @@ -538,7 +538,7 @@ func (optr *Operator) syncFailingStatus(ctx context.Context, original *configv1. LastTransitionTime: now, }) - mergeOperatorHistory(config, optr.currentVersion(), false, now, false, "", false) + mergeOperatorHistory(&config.Status, optr.currentVersion(), false, now, false, "", false) updated, err := applyClusterVersionStatus(ctx, optr.client.ConfigV1(), config, original) optr.rememberLastUpdate(updated) From 3ec244502664d8e53404ec01cdaef6c36dae7f14 Mon Sep 17 00:00:00 2001 From: Petr Muller Date: Tue, 30 Jan 2024 18:52:05 +0100 Subject: [PATCH 4/4] updateClusterVersionStatus: add godoc and linewrap signature --- pkg/cvo/status.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/cvo/status.go b/pkg/cvo/status.go index 132403ab08..25d7142b6a 100644 --- a/pkg/cvo/status.go +++ b/pkg/cvo/status.go @@ -208,7 +208,10 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1 return err } -func updateClusterVersionStatus(cvStatus *configv1.ClusterVersionStatus, status *SyncWorkerStatus, release configv1.Release, getAvailableUpdates func() *availableUpdates, validationErrs field.ErrorList) { +// updateClusterVersionStatus updates the passed cvStatus with the latest status information +func updateClusterVersionStatus(cvStatus *configv1.ClusterVersionStatus, status *SyncWorkerStatus, + release configv1.Release, getAvailableUpdates func() *availableUpdates, validationErrs field.ErrorList) { + cvStatus.ObservedGeneration = status.Generation if len(status.VersionHash) > 0 { cvStatus.VersionHash = status.VersionHash