Skip to content

Commit ade4443

Browse files
committed
refactor Applier interface and improve status reporting
Signed-off-by: Joe Lanford <[email protected]>
1 parent a228dc0 commit ade4443

File tree

6 files changed

+184
-171
lines changed

6 files changed

+184
-171
lines changed

api/v1/common_types.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ const (
2020
TypeInstalled = "Installed"
2121
TypeProgressing = "Progressing"
2222

23+
// Installed reasons
24+
ReasonAbsent = "Absent"
25+
2326
// Progressing reasons
24-
ReasonSucceeded = "Succeeded"
25-
ReasonRetrying = "Retrying"
26-
ReasonBlocked = "Blocked"
27+
ReasonRolloutInProgress = "RolloutInProgress"
28+
ReasonRetrying = "Retrying"
29+
ReasonBlocked = "Blocked"
2730

28-
// Terminal reasons
31+
// Deprecation reasons
2932
ReasonDeprecated = "Deprecated"
30-
ReasonFailed = "Failed"
33+
34+
// Common reasons
35+
ReasonSucceeded = "Succeeded"
36+
ReasonFailed = "Failed"
3137
)

cmd/operator-controller/main.go

Lines changed: 82 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -317,38 +317,6 @@ func run() error {
317317
return err
318318
}
319319

320-
coreClient, err := corev1client.NewForConfig(mgr.GetConfig())
321-
if err != nil {
322-
setupLog.Error(err, "unable to create core client")
323-
return err
324-
}
325-
tokenGetter := authentication.NewTokenGetter(coreClient, authentication.WithExpirationDuration(1*time.Hour))
326-
clientRestConfigMapper := action.ServiceAccountRestConfigMapper(tokenGetter)
327-
if features.OperatorControllerFeatureGate.Enabled(features.SyntheticPermissions) {
328-
clientRestConfigMapper = action.SyntheticUserRestConfigMapper(clientRestConfigMapper)
329-
}
330-
331-
cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(),
332-
helmclient.StorageDriverMapper(action.ChunkedStorageDriverMapper(coreClient, mgr.GetAPIReader(), cfg.systemNamespace)),
333-
helmclient.ClientNamespaceMapper(func(obj client.Object) (string, error) {
334-
ext := obj.(*ocv1.ClusterExtension)
335-
return ext.Spec.Namespace, nil
336-
}),
337-
helmclient.ClientRestConfigMapper(clientRestConfigMapper),
338-
)
339-
if err != nil {
340-
setupLog.Error(err, "unable to config for creating helm client")
341-
return err
342-
}
343-
344-
acg, err := action.NewWrappedActionClientGetter(cfgGetter,
345-
helmclient.WithFailureRollbacks(false),
346-
)
347-
if err != nil {
348-
setupLog.Error(err, "unable to create helm client")
349-
return err
350-
}
351-
352320
certPoolWatcher, err := httputil.NewCertPoolWatcher(cfg.catalogdCasDir, ctrl.Log.WithName("cert-pool"))
353321
if err != nil {
354322
setupLog.Error(err, "unable to create CA certificate pool")
@@ -434,24 +402,30 @@ func run() error {
434402
crdupgradesafety.NewPreflight(aeClient.CustomResourceDefinitions()),
435403
}
436404

437-
// determine if PreAuthorizer should be enabled based on feature gate
438-
var preAuth authorization.PreAuthorizer
439-
if features.OperatorControllerFeatureGate.Enabled(features.PreflightPermissions) {
440-
preAuth = authorization.NewRBACPreAuthorizer(mgr.GetClient())
405+
var ctrlBuilderOpts []controllers.ControllerBuilderOption
406+
if features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime) {
407+
ctrlBuilderOpts = append(ctrlBuilderOpts, controllers.WithOwns(&ocv1.ClusterExtensionRevision{}))
408+
}
409+
410+
ceReconciler := &controllers.ClusterExtensionReconciler{
411+
Client: cl,
412+
Resolver: resolver,
413+
ImageCache: imageCache,
414+
ImagePuller: imagePuller,
415+
Finalizers: clusterExtensionFinalizers,
416+
}
417+
ceController, err := ceReconciler.SetupWithManager(mgr, ctrlBuilderOpts...)
418+
if err != nil {
419+
setupLog.Error(err, "unable to create controller", "controller", "ClusterExtension")
420+
return err
441421
}
442422

443-
// create applier
444-
var (
445-
ctrlBuilderOpts []controllers.ControllerBuilderOption
446-
extApplier controllers.Applier
447-
revisionStatesGetter controllers.RevisionStatesGetter
448-
)
449423
certProvider := getCertificateProvider()
450424
if features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime) {
451425
// TODO: add support for preflight checks
452426
// TODO: better scheme handling - which types do we want to support?
453427
_ = apiextensionsv1.AddToScheme(mgr.GetScheme())
454-
extApplier = &applier.Boxcutter{
428+
ceReconciler.Applier = &applier.Boxcutter{
455429
Client: mgr.GetClient(),
456430
Scheme: mgr.GetScheme(),
457431
RevisionGenerator: &applier.SimpleRevisionGenerator{
@@ -463,50 +437,8 @@ func run() error {
463437
},
464438
Preflights: preflights,
465439
}
466-
revisionStatesGetter = &controllers.BoxcutterRevisionStatesGetter{Reader: mgr.GetClient()}
467-
ctrlBuilderOpts = append(ctrlBuilderOpts, controllers.WithOwns(&ocv1.ClusterExtensionRevision{}))
468-
} else {
469-
// now initialize the helmApplier, assigning the potentially nil preAuth
470-
extApplier = &applier.Helm{
471-
ActionClientGetter: acg,
472-
Preflights: preflights,
473-
BundleToHelmChartConverter: &convert.BundleToHelmChartConverter{
474-
BundleRenderer: registryv1.Renderer,
475-
CertificateProvider: certProvider,
476-
IsWebhookSupportEnabled: certProvider != nil,
477-
},
478-
PreAuthorizer: preAuth,
479-
HelmReleaseToObjectsConverter: &applier.HelmReleaseToObjectsConverter{},
480-
}
481-
revisionStatesGetter = &controllers.HelmRevisionStatesGetter{ActionClientGetter: acg}
482-
}
483-
484-
cm := contentmanager.NewManager(clientRestConfigMapper, mgr.GetConfig(), mgr.GetRESTMapper())
485-
err = clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupContentManagerCacheFinalizer, finalizers.FinalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) {
486-
ext := obj.(*ocv1.ClusterExtension)
487-
err := cm.Delete(ext)
488-
return crfinalizer.Result{}, err
489-
}))
490-
if err != nil {
491-
setupLog.Error(err, "unable to register content manager cleanup finalizer")
492-
return err
493-
}
494-
495-
if err = (&controllers.ClusterExtensionReconciler{
496-
Client: cl,
497-
Resolver: resolver,
498-
ImageCache: imageCache,
499-
ImagePuller: imagePuller,
500-
Applier: extApplier,
501-
RevisionStatesGetter: revisionStatesGetter,
502-
Finalizers: clusterExtensionFinalizers,
503-
Manager: cm,
504-
}).SetupWithManager(mgr, ctrlBuilderOpts...); err != nil {
505-
setupLog.Error(err, "unable to create controller", "controller", "ClusterExtension")
506-
return err
507-
}
440+
ceReconciler.RevisionStatesGetter = &controllers.BoxcutterRevisionStatesGetter{Reader: mgr.GetClient()}
508441

509-
if features.OperatorControllerFeatureGate.Enabled(features.BoxcutterRuntime) {
510442
// Boxcutter
511443
const (
512444
boxcutterSystemPrefixFieldOwner = "olm.operatorframework.io"
@@ -551,6 +483,70 @@ func run() error {
551483
setupLog.Error(err, "unable to create controller", "controller", "ClusterExtensionRevision")
552484
return err
553485
}
486+
} else {
487+
coreClient, err := corev1client.NewForConfig(mgr.GetConfig())
488+
if err != nil {
489+
setupLog.Error(err, "unable to create core client")
490+
return err
491+
}
492+
tokenGetter := authentication.NewTokenGetter(coreClient, authentication.WithExpirationDuration(1*time.Hour))
493+
clientRestConfigMapper := action.ServiceAccountRestConfigMapper(tokenGetter)
494+
if features.OperatorControllerFeatureGate.Enabled(features.SyntheticPermissions) {
495+
clientRestConfigMapper = action.SyntheticUserRestConfigMapper(clientRestConfigMapper)
496+
}
497+
498+
cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(),
499+
helmclient.StorageDriverMapper(action.ChunkedStorageDriverMapper(coreClient, mgr.GetAPIReader(), cfg.systemNamespace)),
500+
helmclient.ClientNamespaceMapper(func(obj client.Object) (string, error) {
501+
ext := obj.(*ocv1.ClusterExtension)
502+
return ext.Spec.Namespace, nil
503+
}),
504+
helmclient.ClientRestConfigMapper(clientRestConfigMapper),
505+
)
506+
if err != nil {
507+
setupLog.Error(err, "unable to config for creating helm client")
508+
return err
509+
}
510+
511+
acg, err := action.NewWrappedActionClientGetter(cfgGetter,
512+
helmclient.WithFailureRollbacks(false),
513+
)
514+
if err != nil {
515+
setupLog.Error(err, "unable to create helm client")
516+
return err
517+
}
518+
519+
// determine if PreAuthorizer should be enabled based on feature gate
520+
var preAuth authorization.PreAuthorizer
521+
if features.OperatorControllerFeatureGate.Enabled(features.PreflightPermissions) {
522+
preAuth = authorization.NewRBACPreAuthorizer(mgr.GetClient())
523+
}
524+
525+
cm := contentmanager.NewManager(clientRestConfigMapper, mgr.GetConfig(), mgr.GetRESTMapper())
526+
err = clusterExtensionFinalizers.Register(controllers.ClusterExtensionCleanupContentManagerCacheFinalizer, finalizers.FinalizerFunc(func(ctx context.Context, obj client.Object) (crfinalizer.Result, error) {
527+
ext := obj.(*ocv1.ClusterExtension)
528+
err := cm.Delete(ext)
529+
return crfinalizer.Result{}, err
530+
}))
531+
if err != nil {
532+
setupLog.Error(err, "unable to register content manager cleanup finalizer")
533+
return err
534+
}
535+
// now initialize the helmApplier, assigning the potentially nil preAuth
536+
ceReconciler.Applier = &applier.Helm{
537+
ActionClientGetter: acg,
538+
Preflights: preflights,
539+
BundleToHelmChartConverter: &convert.BundleToHelmChartConverter{
540+
BundleRenderer: registryv1.Renderer,
541+
CertificateProvider: certProvider,
542+
IsWebhookSupportEnabled: certProvider != nil,
543+
},
544+
HelmReleaseToObjectsConverter: &applier.HelmReleaseToObjectsConverter{},
545+
PreAuthorizer: preAuth,
546+
Watcher: ceController,
547+
Manager: cm,
548+
}
549+
ceReconciler.RevisionStatesGetter = &controllers.HelmRevisionStatesGetter{ActionClientGetter: acg}
554550
}
555551

556552
if err = (&controllers.ClusterCatalogReconciler{

internal/operator-controller/applier/boxcutter.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"slices"
1313

1414
"github.com/davecgh/go-spew/spew"
15+
"k8s.io/apimachinery/pkg/api/meta"
1516
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1617
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1718
"k8s.io/apimachinery/pkg/runtime"
@@ -104,9 +105,8 @@ type Boxcutter struct {
104105
Preflights []Preflight
105106
}
106107

107-
func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) ([]client.Object, string, error) {
108-
objs, err := bc.apply(ctx, contentFS, ext, objectLabels, revisionAnnotations)
109-
return objs, "", err
108+
func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) {
109+
return bc.apply(ctx, contentFS, ext, objectLabels, revisionAnnotations)
110110
}
111111

112112
func (bc *Boxcutter) getObjects(rev *ocv1.ClusterExtensionRevision) []client.Object {
@@ -119,17 +119,17 @@ func (bc *Boxcutter) getObjects(rev *ocv1.ClusterExtensionRevision) []client.Obj
119119
return objs
120120
}
121121

122-
func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) ([]client.Object, error) {
122+
func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) {
123123
// Generate desired revision
124124
desiredRevision, err := bc.RevisionGenerator.GenerateRevision(contentFS, ext, objectLabels, revisionAnnotations)
125125
if err != nil {
126-
return nil, err
126+
return false, "", err
127127
}
128128

129129
// List all existing revisions
130130
existingRevisions, err := bc.getExistingRevisions(ctx, ext.GetName())
131131
if err != nil {
132-
return nil, err
132+
return false, "", err
133133
}
134134
desiredHash := computeSHA256Hash(desiredRevision.Spec.Phases)
135135

@@ -159,12 +159,16 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
159159
case StateNeedsInstall:
160160
err := preflight.Install(ctx, plainObjs)
161161
if err != nil {
162-
return nil, err
162+
return false, "", err
163163
}
164+
// TODO: jlanford's IDE says that "StateNeedsUpgrade" condition is always true, but
165+
// it isn't immediately obvious why that is. Perhaps len(existingRevisions) is
166+
// always greater than 0 (seems unlikely), or shouldSkipPreflight always returns
167+
// true (and we continue) when state == StateNeedsInstall?
164168
case StateNeedsUpgrade:
165169
err := preflight.Upgrade(ctx, plainObjs)
166170
if err != nil {
167-
return nil, err
171+
return false, "", err
168172
}
169173
}
170174
}
@@ -189,19 +193,24 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
189193
}
190194

191195
if err := controllerutil.SetControllerReference(ext, newRevision, bc.Scheme); err != nil {
192-
return nil, fmt.Errorf("set ownerref: %w", err)
196+
return false, "", fmt.Errorf("set ownerref: %w", err)
193197
}
194198
if err := bc.Client.Create(ctx, newRevision); err != nil {
195-
return nil, fmt.Errorf("creating new Revision: %w", err)
199+
return false, "", fmt.Errorf("creating new Revision: %w", err)
196200
}
201+
currentRevision = newRevision
197202
}
198203

199204
// TODO: Delete archived previous revisions over a certain revision limit
200205

201-
// TODO: Read status from revision.
202-
203-
// Collect objects
204-
return bc.getObjects(desiredRevision), nil
206+
// TODO: Define constants for the ClusterExtensionRevision condition types.
207+
installedCondition := meta.FindStatusCondition(currentRevision.Status.Conditions, "Succeeded")
208+
if installedCondition == nil {
209+
return false, "New revision created", nil
210+
} else if installedCondition.Status != metav1.ConditionTrue {
211+
return false, installedCondition.Message, nil
212+
}
213+
return true, "", nil
205214
}
206215

207216
// getExistingRevisions returns the list of ClusterExtensionRevisions for a ClusterExtension with name extName in revision order (oldest to newest)

0 commit comments

Comments
 (0)