@@ -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
0 commit comments