Skip to content

Commit 280e27f

Browse files
committed
WIP
1 parent 0c045a3 commit 280e27f

File tree

1 file changed

+80
-20
lines changed

1 file changed

+80
-20
lines changed

pkg/start/start.go

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -189,17 +189,42 @@ func (o *Options) Run(ctx context.Context) error {
189189
return err
190190
}
191191

192+
clusterVersionConfigInformerFactory, configInformerFactory := o.prepareConfigInformerFactories(cb)
193+
_, _, err = o.processInitialFeatureGate(ctx, configInformerFactory)
194+
if err != nil {
195+
return fmt.Errorf("error processing feature gates: %w", err)
196+
}
197+
198+
// initialize the controllers and attempt to load the payload information
199+
controllerCtx, err := o.NewControllerContext(cb, clusterVersionConfigInformerFactory, configInformerFactory)
200+
if err != nil {
201+
return err
202+
}
203+
o.leaderElection = getLeaderElectionConfig(ctx, cb.RestConfig(defaultQPS))
204+
o.run(ctx, controllerCtx, lock, cb.RestConfig(defaultQPS), cb.RestConfig(highQPS))
205+
return nil
206+
}
207+
208+
func (o *Options) prepareConfigInformerFactories(cb *ClientBuilder) (configinformers.SharedInformerFactory, configinformers.SharedInformerFactory) {
209+
client := cb.ClientOrDie("shared-informer")
210+
filterByName := func(opts *metav1.ListOptions) { opts.FieldSelector = fmt.Sprintf("metadata.name=%s", o.Name) }
211+
clusterVersionConfigInformerFactory := configinformers.NewSharedInformerFactoryWithOptions(client, resyncPeriod(o.ResyncInterval), configinformers.WithTweakListOptions(filterByName))
212+
configInformerFactory := configinformers.NewSharedInformerFactory(client, resyncPeriod(o.ResyncInterval))
213+
214+
return clusterVersionConfigInformerFactory, configInformerFactory
215+
}
216+
217+
// getOcpVersion peeks at the local release metadata to determine the version of OCP this CVO belongs to. This assumes
218+
// the CVO is executing in a container from the payload image. This does not and should not fully load whole payload
219+
// content, that is only loaded later once leader lease is acquired. Here we should only read as little data as possible
220+
// to determine the version so we can establish enabled feature gate checker for all following code.
221+
func (o *Options) getOcpVersion() string {
192222
payloadRoot := payload.DefaultRootPath
193223
if o.PayloadOverride != "" {
194224
payloadRoot = payload.RootPath(o.PayloadOverride)
195225
}
196226

197227
cvoOcpVersion := "0.0.1-snapshot"
198-
// Peek at the local release metadata to determine the version of OCP this CVO belongs to. This assumes the CVO is
199-
// executing in a container from the payload image. Full payload content is only read later once leader lease is
200-
// acquired, and here we should only read as little data as possible to determine the version so we can establish
201-
// enabled feature gate checker for all following code.
202-
//
203228
// We cannot refuse to start CVO if for some reason we cannot determine the OCP version on startup from the local
204229
// release metadata. The only consequence is we fail to determine enabled/disabled feature gates and will have to use
205230
// some defaults.
@@ -214,25 +239,60 @@ func (o *Options) Run(ctx context.Context) error {
214239
klog.Infof("Determined OCP version for this CVO: %q", cvoOcpVersion)
215240
}
216241

217-
clusterVersionConfigInformerFactory, configInformerFactory := o.prepareConfigInformerFactories(cb)
242+
return cvoOcpVersion
243+
}
218244

219-
// initialize the controllers and attempt to load the payload information
220-
controllerCtx, err := o.NewControllerContext(cb, clusterVersionConfigInformerFactory, configInformerFactory)
221-
if err != nil {
222-
return err
245+
func (o *Options) processInitialFeatureGate(ctx context.Context, configInformerFactory configinformers.SharedInformerFactory) (configv1.FeatureSet, *featuregates.CvoGates, error) {
246+
featureGates := configInformerFactory.Config().V1().FeatureGates().Lister()
247+
configInformerFactory.Start(ctx.Done())
248+
configInformerFactory.WaitForCacheSync(ctx.Done())
249+
250+
var startingFeatureSet configv1.FeatureSet
251+
var clusterFeatureGate *configv1.FeatureGate
252+
253+
cvoOcpVersion := o.getOcpVersion()
254+
cvoGates := featuregates.DefaultCvoGates(cvoOcpVersion)
255+
256+
// client-go automatically retries some network blip errors on GETs for 30s by default, and we want to
257+
// retry the remaining ones ourselves. If we fail longer than that, the operator won't be able to do work
258+
// anyway. Return the error and crashloop.
259+
//
260+
// We implement the timeout with a context because the timeout in PollImmediateWithContext does not behave
261+
// well when ConditionFunc takes longer time to execute, like here where the GET can be retried by client-go
262+
var lastError error
263+
if err := wait.PollUntilContextTimeout(context.Background(), 2*time.Second, 25*time.Second, true, func(ctx context.Context) (bool, error) {
264+
gate, fgErr := featureGates.Get("cluster")
265+
switch {
266+
case apierrors.IsNotFound(fgErr):
267+
// if we have no featuregates, then the cluster is using the default featureset, which is "".
268+
// This excludes everything that could possibly depend on a different feature set.
269+
startingFeatureSet = ""
270+
klog.Infof("FeatureGate not found in cluster, will assume default feature set %q at startup", startingFeatureSet)
271+
return true, nil
272+
case fgErr != nil:
273+
lastError = fgErr
274+
klog.Warningf("Failed to get FeatureGate from cluster: %v", fgErr)
275+
return false, nil
276+
default:
277+
clusterFeatureGate = gate
278+
startingFeatureSet = gate.Spec.FeatureSet
279+
cvoGates = featuregates.CvoGatesFromFeatureGate(clusterFeatureGate, cvoOcpVersion)
280+
klog.Infof("FeatureGate found in cluster, using its feature set %q at startup", startingFeatureSet)
281+
return true, nil
282+
}
283+
}); err != nil {
284+
if lastError != nil {
285+
return "", nil, lastError
286+
}
287+
return "", nil, err
223288
}
224-
o.leaderElection = getLeaderElectionConfig(ctx, cb.RestConfig(defaultQPS))
225-
o.run(ctx, controllerCtx, lock, cb.RestConfig(defaultQPS), cb.RestConfig(highQPS))
226-
return nil
227-
}
228289

229-
func (o *Options) prepareConfigInformerFactories(cb *ClientBuilder) (configinformers.SharedInformerFactory, configinformers.SharedInformerFactory) {
230-
client := cb.ClientOrDie("shared-informer")
231-
filterByName := func(opts *metav1.ListOptions) { opts.FieldSelector = fmt.Sprintf("metadata.name=%s", o.Name) }
232-
clusterVersionConfigInformerFactory := configinformers.NewSharedInformerFactoryWithOptions(client, resyncPeriod(o.ResyncInterval), configinformers.WithTweakListOptions(filterByName))
233-
configInformerFactory := configinformers.NewSharedInformerFactory(client, resyncPeriod(o.ResyncInterval))
290+
if cvoGates.UnknownVersion() {
291+
klog.Warningf("CVO features for version %s could not be detected from FeatureGate; will use defaults plus special UnknownVersion feature gate", cvoOcpVersion)
292+
}
293+
klog.Infof("CVO features for version %s enabled at startup: %+v", cvoOcpVersion, cvoGates)
234294

235-
return clusterVersionConfigInformerFactory, configInformerFactory
295+
return startingFeatureSet, &cvoGates, nil
236296
}
237297

238298
// run launches a number of goroutines to handle manifest application,

0 commit comments

Comments
 (0)