Skip to content

Commit e3677e0

Browse files
Merge pull request #1032 from petr-muller/ota-1159-result-of-work-rri-condition
OTA-1159: [2/3] Maintain `ReconciliationIssues` condition
2 parents 8f4a120 + 1604448 commit e3677e0

13 files changed

+519
-137
lines changed

install/0000_00_cluster-version-operator_03_deployment.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ spec:
5656
name: kube-api-access
5757
readOnly: true
5858
env:
59+
# Unfortunately the placeholder is not replaced, reported as OCPBUGS-30080
60+
- name: OPERATOR_IMAGE_VERSION
61+
value: "0.0.1-snapshot"
5962
- name: KUBERNETES_SERVICE_PORT # allows CVO to communicate with apiserver directly on same host. Is substituted with port from infrastructures.status.apiServerInternalURL if available.
6063
value: "6443"
6164
- name: KUBERNETES_SERVICE_HOST # allows CVO to communicate with apiserver directly on same host. Is substituted with hostname from infrastructures.status.apiServerInternalURL if available.

pkg/cvo/cvo.go

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
"github.com/openshift/cluster-version-operator/pkg/customsignaturestore"
4646
cvointernal "github.com/openshift/cluster-version-operator/pkg/cvo/internal"
4747
"github.com/openshift/cluster-version-operator/pkg/cvo/internal/dynamicclient"
48+
"github.com/openshift/cluster-version-operator/pkg/featuregates"
4849
"github.com/openshift/cluster-version-operator/pkg/internal"
4950
"github.com/openshift/cluster-version-operator/pkg/payload"
5051
"github.com/openshift/cluster-version-operator/pkg/payload/precondition"
@@ -164,9 +165,7 @@ type Operator struct {
164165
// via annotation
165166
exclude string
166167

167-
// requiredFeatureSet is set the value of featuregates.config.openshift.io|.spec.featureSet. It's a very slow
168-
// moving resource, so it is not re-detected live.
169-
requiredFeatureSet string
168+
enabledFeatureGates featuregates.CvoGateChecker
170169

171170
clusterProfile string
172171
uid types.UID
@@ -187,7 +186,6 @@ func New(
187186
client clientset.Interface,
188187
kubeClient kubernetes.Interface,
189188
exclude string,
190-
requiredFeatureSet string,
191189
clusterProfile string,
192190
promqlTarget clusterconditions.PromQLTarget,
193191
injectClusterIdIntoPromQL bool,
@@ -219,10 +217,14 @@ func New(
219217
upgradeableQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "upgradeable"),
220218

221219
exclude: exclude,
222-
requiredFeatureSet: requiredFeatureSet,
223220
clusterProfile: clusterProfile,
224221
conditionRegistry: standard.NewConditionRegistry(promqlTarget),
225222
injectClusterIdIntoPromQL: injectClusterIdIntoPromQL,
223+
224+
// Because of OCPBUGS-30080, we can only detect the enabled feature gates after Operator loads the initial payload
225+
// from disk via LoadInitialPayload. We must not have any gate-checking code until that happens, so we initialize
226+
// this field with a checker that panics when used.
227+
enabledFeatureGates: featuregates.PanicOnUsageBeforeInitialization,
226228
}
227229

228230
if _, err := cvInformer.Informer().AddEventHandler(optr.clusterVersionEventHandler()); err != nil {
@@ -254,10 +256,9 @@ func New(
254256
return optr, nil
255257
}
256258

257-
// InitializeFromPayload waits until a ClusterVersion object exists. It then retrieves the payload contents and verifies the
258-
// initial state, then configures the controller that loads and applies content to the cluster. It returns an error if the
259-
// payload appears to be in error rather than continuing.
260-
func (optr *Operator) InitializeFromPayload(ctx context.Context, restConfig *rest.Config, burstRestConfig *rest.Config) error {
259+
// LoadInitialPayload waits until a ClusterVersion object exists. It then retrieves the payload contents, verifies the
260+
// initial state and returns it. If the payload is invalid, an error is returned.
261+
func (optr *Operator) LoadInitialPayload(ctx context.Context, startingRequiredFeatureSet configv1.FeatureSet, restConfig *rest.Config) (*payload.Update, error) {
261262

262263
// wait until cluster version object exists
263264
if err := wait.PollUntilContextCancel(ctx, 3*time.Second, true, func(ctx context.Context) (bool, error) {
@@ -274,24 +275,19 @@ func (optr *Operator) InitializeFromPayload(ctx context.Context, restConfig *res
274275
}
275276
return true, nil
276277
}); err != nil {
277-
return fmt.Errorf("Error when attempting to get cluster version object: %w", err)
278+
return nil, fmt.Errorf("Error when attempting to get cluster version object: %w", err)
278279
}
279280

280-
update, err := payload.LoadUpdate(optr.defaultPayloadDir(), optr.release.Image, optr.exclude, optr.requiredFeatureSet,
281+
update, err := payload.LoadUpdate(optr.defaultPayloadDir(), optr.release.Image, optr.exclude, string(startingRequiredFeatureSet),
281282
optr.clusterProfile, capability.GetKnownCapabilities())
282283

283284
if err != nil {
284-
return fmt.Errorf("the local release contents are invalid - no current version can be determined from disk: %v", err)
285+
return nil, fmt.Errorf("the local release contents are invalid - no current version can be determined from disk: %v", err)
285286
}
286-
287-
optr.release = update.Release
288-
optr.releaseCreated = update.ImageRef.CreationTimestamp.Time
289-
optr.SetArchitecture(update.Architecture)
290-
291287
httpClientConstructor := sigstore.NewCachedHTTPClientConstructor(optr.HTTPClient, nil)
292288
configClient, err := coreclientsetv1.NewForConfig(restConfig)
293289
if err != nil {
294-
return fmt.Errorf("unable to create a configuration client: %v", err)
290+
return nil, fmt.Errorf("unable to create a configuration client: %v", err)
295291
}
296292

297293
customSignatureStore := &customsignaturestore.Store{
@@ -303,7 +299,7 @@ func (optr *Operator) InitializeFromPayload(ctx context.Context, restConfig *res
303299
// attempt to load a verifier as defined in the payload
304300
verifier, signatureStore, err := loadConfigMapVerifierDataFromUpdate(update, httpClientConstructor.HTTPClient, configClient, customSignatureStore)
305301
if err != nil {
306-
return err
302+
return nil, err
307303
}
308304
if verifier != nil {
309305
klog.Infof("Verifying release authenticity: %v", verifier)
@@ -313,6 +309,16 @@ func (optr *Operator) InitializeFromPayload(ctx context.Context, restConfig *res
313309
}
314310
optr.verifier = verifier
315311
optr.signatureStore = signatureStore
312+
return update, nil
313+
}
314+
315+
// InitializeFromPayload configures the controller that loads and applies content to the cluster given an initial payload
316+
// and feature gate data.
317+
func (optr *Operator) InitializeFromPayload(update *payload.Update, requiredFeatureSet configv1.FeatureSet, cvoFlags featuregates.CvoGateChecker, restConfig *rest.Config, burstRestConfig *rest.Config) {
318+
optr.enabledFeatureGates = cvoFlags
319+
optr.release = update.Release
320+
optr.releaseCreated = update.ImageRef.CreationTimestamp.Time
321+
optr.SetArchitecture(update.Architecture)
316322

317323
// after the verifier has been loaded, initialize the sync worker with a payload retriever
318324
// which will consume the verifier
@@ -328,12 +334,10 @@ func (optr *Operator) InitializeFromPayload(ctx context.Context, restConfig *res
328334
Cap: time.Second * 15,
329335
},
330336
optr.exclude,
331-
optr.requiredFeatureSet,
337+
requiredFeatureSet,
332338
optr.eventRecorder,
333339
optr.clusterProfile,
334340
)
335-
336-
return nil
337341
}
338342

339343
// ownerReferenceModifier sets the owner reference to the current CV resource if no other reference exists. It also resets

pkg/cvo/cvo_scenarios_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ import (
2626

2727
configv1 "github.com/openshift/api/config/v1"
2828
"github.com/openshift/client-go/config/clientset/versioned/fake"
29+
"github.com/openshift/library-go/pkg/manifest"
2930

31+
"github.com/openshift/cluster-version-operator/pkg/featuregates"
3032
"github.com/openshift/cluster-version-operator/pkg/payload"
3133
"github.com/openshift/cluster-version-operator/pkg/payload/precondition"
32-
"github.com/openshift/library-go/pkg/manifest"
3334
)
3435

3536
var architecture string
@@ -108,14 +109,15 @@ func setupCVOTest(payloadDir string) (*Operator, map[string]apiruntime.Object, *
108109
}
109110

110111
o := &Operator{
111-
namespace: "test",
112-
name: "version",
113-
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "cvo-loop-test"),
114-
client: client,
115-
cvLister: &clientCVLister{client: client},
116-
exclude: "exclude-test",
117-
eventRecorder: record.NewFakeRecorder(100),
118-
clusterProfile: payload.DefaultClusterProfile,
112+
namespace: "test",
113+
name: "version",
114+
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "cvo-loop-test"),
115+
client: client,
116+
enabledFeatureGates: featuregates.DefaultCvoGates("version"),
117+
cvLister: &clientCVLister{client: client},
118+
exclude: "exclude-test",
119+
eventRecorder: record.NewFakeRecorder(100),
120+
clusterProfile: payload.DefaultClusterProfile,
119121
}
120122

121123
dynamicScheme := apiruntime.NewScheme()

pkg/cvo/cvo_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ import (
3939
configv1 "github.com/openshift/api/config/v1"
4040
clientset "github.com/openshift/client-go/config/clientset/versioned"
4141
"github.com/openshift/client-go/config/clientset/versioned/fake"
42-
43-
"github.com/openshift/cluster-version-operator/pkg/payload"
4442
"github.com/openshift/library-go/pkg/manifest"
4543
"github.com/openshift/library-go/pkg/verify/store/serial"
4644
"github.com/openshift/library-go/pkg/verify/store/sigstore"
45+
46+
"github.com/openshift/cluster-version-operator/pkg/featuregates"
47+
"github.com/openshift/cluster-version-operator/pkg/payload"
4748
)
4849

4950
var (
@@ -2273,6 +2274,7 @@ func TestOperator_sync(t *testing.T) {
22732274
optr.configSync = &fakeSyncRecorder{Returns: expectStatus}
22742275
}
22752276
optr.eventRecorder = record.NewFakeRecorder(100)
2277+
optr.enabledFeatureGates = featuregates.DefaultCvoGates("version")
22762278

22772279
ctx := context.Background()
22782280
err := optr.sync(ctx, optr.queueKey())

pkg/cvo/reconciliation_issues.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package cvo
2+
3+
import v1 "github.com/openshift/api/config/v1"
4+
5+
const (
6+
reconciliationIssuesConditionType v1.ClusterStatusConditionType = "ReconciliationIssues"
7+
8+
noReconciliationIssuesReason string = "NoIssues"
9+
noReconciliationIssuesMessage string = "No issues found during reconciliation"
10+
11+
reconciliationIssuesFoundReason string = "IssuesFound"
12+
reconciliationIssuesFoundMessage string = "Issues found during reconciliation"
13+
)

pkg/cvo/status.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
configclientv1 "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
2424

2525
"github.com/openshift/cluster-version-operator/lib/resourcemerge"
26+
"github.com/openshift/cluster-version-operator/pkg/featuregates"
2627
"github.com/openshift/cluster-version-operator/pkg/payload"
2728
)
2829

@@ -198,7 +199,7 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1
198199
original = config.DeepCopy()
199200
}
200201

201-
updateClusterVersionStatus(&config.Status, status, optr.release, optr.getAvailableUpdates, validationErrs)
202+
updateClusterVersionStatus(&config.Status, status, optr.release, optr.getAvailableUpdates, optr.enabledFeatureGates, validationErrs)
202203

203204
if klog.V(6).Enabled() {
204205
klog.Infof("Apply config: %s", diff.ObjectReflectDiff(original, config))
@@ -210,7 +211,8 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1
210211

211212
// updateClusterVersionStatus updates the passed cvStatus with the latest status information
212213
func updateClusterVersionStatus(cvStatus *configv1.ClusterVersionStatus, status *SyncWorkerStatus,
213-
release configv1.Release, getAvailableUpdates func() *availableUpdates, validationErrs field.ErrorList) {
214+
release configv1.Release, getAvailableUpdates func() *availableUpdates, enabledGates featuregates.CvoGateChecker,
215+
validationErrs field.ErrorList) {
214216

215217
cvStatus.ObservedGeneration = status.Generation
216218
if len(status.VersionHash) > 0 {
@@ -379,6 +381,24 @@ func updateClusterVersionStatus(cvStatus *configv1.ClusterVersionStatus, status
379381
}
380382
}
381383

384+
oldRiCondition := resourcemerge.FindOperatorStatusCondition(cvStatus.Conditions, reconciliationIssuesConditionType)
385+
if enabledGates.ReconciliationIssuesCondition() || (oldRiCondition != nil && enabledGates.UnknownVersion()) {
386+
riCondition := configv1.ClusterOperatorStatusCondition{
387+
Type: reconciliationIssuesConditionType,
388+
Status: configv1.ConditionFalse,
389+
Reason: noReconciliationIssuesReason,
390+
Message: noReconciliationIssuesMessage,
391+
}
392+
if status.Failure != nil {
393+
riCondition.Status = configv1.ConditionTrue
394+
riCondition.Reason = reconciliationIssuesFoundReason
395+
riCondition.Message = fmt.Sprintf("%s: %s", reconciliationIssuesFoundMessage, status.Failure.Error())
396+
}
397+
resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, riCondition)
398+
} else if oldRiCondition != nil {
399+
resourcemerge.RemoveOperatorStatusCondition(&cvStatus.Conditions, reconciliationIssuesConditionType)
400+
}
401+
382402
// default retrieved updates if it is not set
383403
if resourcemerge.FindOperatorStatusCondition(cvStatus.Conditions, configv1.RetrievedUpdates) == nil {
384404
resourcemerge.SetOperatorStatusCondition(&cvStatus.Conditions, configv1.ClusterOperatorStatusCondition{

0 commit comments

Comments
 (0)