Skip to content

Commit 6fe7c3c

Browse files
(feat): [Boxcutter] Use ClusterExtension ServiceAccount for revision operations
Implement serviceAccount-scoped token-based authentication for ClusterExtensionRevision controller using annotation-based configuration. - Add RevisionEngineFactory with CreateRevisionEngine(ctx, rev) interface - Read ServiceAccount from annotations (no ClusterExtension dependency) - Token-based auth using TokenInjectingRoundTripper - ServiceAccount name and namespace in annotations for observability - TrackingCache uses global client for caching/cleanup - Comprehensive error path tests ClusterExtensionRevision can exist independently. Easy mode impersonation deferred until API is finalized. Assisted-by: Cursor
1 parent 1fa4169 commit 6fe7c3c

File tree

7 files changed

+405
-60
lines changed

7 files changed

+405
-60
lines changed

cmd/operator-controller/main.go

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,7 @@ import (
4242
_ "k8s.io/client-go/plugin/pkg/client/auth"
4343
"k8s.io/klog/v2"
4444
"k8s.io/utils/ptr"
45-
"pkg.package-operator.run/boxcutter/machinery"
4645
"pkg.package-operator.run/boxcutter/managedcache"
47-
"pkg.package-operator.run/boxcutter/ownerhandling"
48-
"pkg.package-operator.run/boxcutter/validation"
4946
ctrl "sigs.k8s.io/controller-runtime"
5047
crcache "sigs.k8s.io/controller-runtime/pkg/cache"
5148
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
@@ -653,21 +650,26 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl
653650
return fmt.Errorf("unable to add tracking cache to manager: %v", err)
654651
}
655652

653+
cerCoreClient, err := corev1client.NewForConfig(c.mgr.GetConfig())
654+
if err != nil {
655+
return fmt.Errorf("unable to create client for ClusterExtensionRevision controller: %w", err)
656+
}
657+
cerTokenGetter := authentication.NewTokenGetter(cerCoreClient, authentication.WithExpirationDuration(1*time.Hour))
658+
659+
revisionEngineFactory := controllers.NewDefaultRevisionEngineFactory(
660+
c.mgr.GetScheme(),
661+
trackingCache,
662+
discoveryClient,
663+
c.mgr.GetRESTMapper(),
664+
fieldOwnerPrefix,
665+
c.mgr.GetConfig(),
666+
cerTokenGetter,
667+
)
668+
656669
if err = (&controllers.ClusterExtensionRevisionReconciler{
657-
Client: c.mgr.GetClient(),
658-
RevisionEngine: machinery.NewRevisionEngine(
659-
machinery.NewPhaseEngine(
660-
machinery.NewObjectEngine(
661-
c.mgr.GetScheme(), trackingCache, c.mgr.GetClient(),
662-
ownerhandling.NewNative(c.mgr.GetScheme()),
663-
machinery.NewComparator(ownerhandling.NewNative(c.mgr.GetScheme()), discoveryClient, c.mgr.GetScheme(), fieldOwnerPrefix),
664-
fieldOwnerPrefix, fieldOwnerPrefix,
665-
),
666-
validation.NewClusterPhaseValidator(c.mgr.GetRESTMapper(), c.mgr.GetClient()),
667-
),
668-
validation.NewRevisionValidator(), c.mgr.GetClient(),
669-
),
670-
TrackingCache: trackingCache,
670+
Client: c.mgr.GetClient(),
671+
RevisionEngineFactory: revisionEngineFactory,
672+
TrackingCache: trackingCache,
671673
}).SetupWithManager(c.mgr); err != nil {
672674
return fmt.Errorf("unable to setup ClusterExtensionRevision controller: %w", err)
673675
}

internal/operator-controller/applier/boxcutter.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ func (r *SimpleRevisionGenerator) buildClusterExtensionRevision(
186186
ext *ocv1.ClusterExtension,
187187
annotations map[string]string,
188188
) *ocv1.ClusterExtensionRevision {
189+
if annotations == nil {
190+
annotations = make(map[string]string)
191+
}
192+
annotations[labels.ServiceAccountNameKey] = ext.Spec.ServiceAccount.Name
193+
annotations[labels.ServiceAccountNamespaceKey] = ext.Spec.Namespace
194+
189195
return &ocv1.ClusterExtensionRevision{
190196
ObjectMeta: metav1.ObjectMeta{
191197
Annotations: annotations,

internal/operator-controller/applier/boxcutter_test.go

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T)
6666
ObjectMeta: metav1.ObjectMeta{
6767
Name: "test-123",
6868
},
69+
Spec: ocv1.ClusterExtensionSpec{
70+
Namespace: "test-namespace",
71+
ServiceAccount: ocv1.ServiceAccountReference{
72+
Name: "test-sa",
73+
},
74+
},
6975
}
7076

7177
objectLabels := map[string]string{
@@ -79,10 +85,12 @@ func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T)
7985
ObjectMeta: metav1.ObjectMeta{
8086
Name: "test-123-1",
8187
Annotations: map[string]string{
82-
"olm.operatorframework.io/bundle-name": "my-bundle",
83-
"olm.operatorframework.io/bundle-reference": "bundle-ref",
84-
"olm.operatorframework.io/bundle-version": "1.2.0",
85-
"olm.operatorframework.io/package-name": "my-package",
88+
"olm.operatorframework.io/bundle-name": "my-bundle",
89+
"olm.operatorframework.io/bundle-reference": "bundle-ref",
90+
"olm.operatorframework.io/bundle-version": "1.2.0",
91+
"olm.operatorframework.io/package-name": "my-package",
92+
"olm.operatorframework.io/service-account-name": "test-sa",
93+
"olm.operatorframework.io/service-account-namespace": "test-namespace",
8694
},
8795
Labels: map[string]string{
8896
labels.OwnerNameKey: "test-123",
@@ -172,6 +180,12 @@ func Test_SimpleRevisionGenerator_GenerateRevision(t *testing.T) {
172180
ObjectMeta: metav1.ObjectMeta{
173181
Name: "test-extension",
174182
},
183+
Spec: ocv1.ClusterExtensionSpec{
184+
Namespace: "test-namespace",
185+
ServiceAccount: ocv1.ServiceAccountReference{
186+
Name: "test-sa",
187+
},
188+
},
175189
}
176190

177191
rev, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, ext, map[string]string{}, map[string]string{})
@@ -291,7 +305,12 @@ func Test_SimpleRevisionGenerator_AppliesObjectLabelsAndRevisionAnnotations(t *t
291305
"other": "value",
292306
}
293307

294-
rev, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, &ocv1.ClusterExtension{}, map[string]string{
308+
rev, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, &ocv1.ClusterExtension{
309+
Spec: ocv1.ClusterExtensionSpec{
310+
Namespace: "test-namespace",
311+
ServiceAccount: ocv1.ServiceAccountReference{Name: "test-sa"},
312+
},
313+
}, map[string]string{
295314
"some": "value",
296315
}, revAnnotations)
297316
require.NoError(t, err)
@@ -319,7 +338,12 @@ func Test_SimpleRevisionGenerator_Failure(t *testing.T) {
319338
ManifestProvider: r,
320339
}
321340

322-
rev, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, &ocv1.ClusterExtension{}, map[string]string{}, map[string]string{})
341+
rev, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, &ocv1.ClusterExtension{
342+
Spec: ocv1.ClusterExtensionSpec{
343+
Namespace: "test-namespace",
344+
ServiceAccount: ocv1.ServiceAccountReference{Name: "test-sa"},
345+
},
346+
}, map[string]string{}, map[string]string{})
323347
require.Nil(t, rev)
324348
t.Log("by checking rendering errors are propagated")
325349
require.Error(t, err)

internal/operator-controller/controllers/clusterextensionrevision_controller.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ const (
4343
// ClusterExtensionRevisionReconciler actions individual snapshots of ClusterExtensions,
4444
// as part of the boxcutter integration.
4545
type ClusterExtensionRevisionReconciler struct {
46-
Client client.Client
47-
RevisionEngine RevisionEngine
48-
TrackingCache trackingCache
46+
Client client.Client
47+
RevisionEngineFactory RevisionEngineFactory
48+
TrackingCache trackingCache
4949
}
5050

5151
type trackingCache interface {
@@ -60,6 +60,11 @@ type RevisionEngine interface {
6060
Reconcile(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error)
6161
}
6262

63+
// RevisionEngineFactory creates a RevisionEngine for a ClusterExtensionRevision.
64+
type RevisionEngineFactory interface {
65+
CreateRevisionEngine(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (RevisionEngine, error)
66+
}
67+
6368
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions,verbs=get;list;watch;update;patch;create;delete
6469
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions/status,verbs=update;patch
6570
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions/finalizers,verbs=update
@@ -139,7 +144,13 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
139144
return ctrl.Result{}, werr
140145
}
141146

142-
rres, err := c.RevisionEngine.Reconcile(ctx, *revision, opts...)
147+
revisionEngine, err := c.RevisionEngineFactory.CreateRevisionEngine(ctx, rev)
148+
if err != nil {
149+
setRetryingConditions(rev, err.Error())
150+
return ctrl.Result{}, fmt.Errorf("failed to create revision engine: %v", err)
151+
}
152+
153+
rres, err := revisionEngine.Reconcile(ctx, *revision, opts...)
143154
if err != nil {
144155
if rres != nil {
145156
// Log detailed reconcile reports only in debug mode (V(1)) to reduce verbosity.
@@ -253,7 +264,13 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
253264
func (c *ClusterExtensionRevisionReconciler) teardown(ctx context.Context, rev *ocv1.ClusterExtensionRevision, revision *boxcutter.Revision) (ctrl.Result, error) {
254265
l := log.FromContext(ctx)
255266

256-
tres, err := c.RevisionEngine.Teardown(ctx, *revision)
267+
revisionEngine, err := c.RevisionEngineFactory.CreateRevisionEngine(ctx, rev)
268+
if err != nil {
269+
markAsAvailableUnknown(rev, ocv1.ClusterExtensionRevisionReasonReconciling, err.Error())
270+
return ctrl.Result{}, fmt.Errorf("failed to create revision engine for teardown: %v", err)
271+
}
272+
273+
tres, err := revisionEngine.Teardown(ctx, *revision)
257274
if err != nil {
258275
if tres != nil {
259276
l.V(1).Info("teardown failure report", "report", tres.String())

0 commit comments

Comments
 (0)