Skip to content

Commit 000cb09

Browse files
review changes
1 parent 2fee08f commit 000cb09

File tree

3 files changed

+61
-36
lines changed

3 files changed

+61
-36
lines changed

cmd/operator-controller/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl
656656
}
657657
cerTokenGetter := authentication.NewTokenGetter(cerCoreClient, authentication.WithExpirationDuration(1*time.Hour))
658658

659-
revisionEngineFactory := controllers.NewDefaultRevisionEngineFactory(
659+
revisionEngineFactory, err := controllers.NewDefaultRevisionEngineFactory(
660660
c.mgr.GetScheme(),
661661
trackingCache,
662662
discoveryClient,
@@ -665,6 +665,9 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl
665665
c.mgr.GetConfig(),
666666
cerTokenGetter,
667667
)
668+
if err != nil {
669+
return fmt.Errorf("unable to create revision engine factory: %w", err)
670+
}
668671

669672
if err = (&controllers.ClusterExtensionRevisionReconciler{
670673
Client: c.mgr.GetClient(),

internal/operator-controller/controllers/clusterextensionrevision_controller.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"k8s.io/apimachinery/pkg/util/sets"
2222
"pkg.package-operator.run/boxcutter"
2323
"pkg.package-operator.run/boxcutter/machinery"
24-
machinerytypes "pkg.package-operator.run/boxcutter/machinery/types"
2524
"pkg.package-operator.run/boxcutter/probing"
2625
ctrl "sigs.k8s.io/controller-runtime"
2726
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -55,16 +54,6 @@ type trackingCache interface {
5554
Free(ctx context.Context, user client.Object) error
5655
}
5756

58-
type RevisionEngine interface {
59-
Teardown(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error)
60-
Reconcile(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error)
61-
}
62-
63-
// RevisionEngineFactory creates a RevisionEngine for a ClusterExtensionRevision.
64-
type RevisionEngineFactory interface {
65-
CreateRevisionEngine(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (RevisionEngine, error)
66-
}
67-
6857
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions,verbs=get;list;watch;update;patch;create;delete
6958
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions/status,verbs=update;patch
7059
//+kubebuilder:rbac:groups=olm.operatorframework.io,resources=clusterextensionrevisions/finalizers,verbs=update

internal/operator-controller/controllers/revision_engine_factory.go

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import (
1212
"fmt"
1313
"net/http"
1414
"strings"
15+
"sync"
1516

1617
"k8s.io/apimachinery/pkg/api/meta"
1718
"k8s.io/apimachinery/pkg/runtime"
1819
"k8s.io/apimachinery/pkg/types"
1920
"k8s.io/client-go/discovery"
2021
"k8s.io/client-go/rest"
2122
"pkg.package-operator.run/boxcutter/machinery"
23+
machinerytypes "pkg.package-operator.run/boxcutter/machinery/types"
2224
"pkg.package-operator.run/boxcutter/managedcache"
2325
"pkg.package-operator.run/boxcutter/ownerhandling"
2426
"pkg.package-operator.run/boxcutter/validation"
@@ -29,26 +31,50 @@ import (
2931
"github.com/operator-framework/operator-controller/internal/operator-controller/labels"
3032
)
3133

32-
// DefaultRevisionEngineFactory creates boxcutter RevisionEngines with serviceAccount-scoped clients.
33-
type DefaultRevisionEngineFactory struct {
34+
// RevisionEngine defines the interface for reconciling and tearing down revisions.
35+
type RevisionEngine interface {
36+
Teardown(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionTeardownOption) (machinery.RevisionTeardownResult, error)
37+
Reconcile(ctx context.Context, rev machinerytypes.Revision, opts ...machinerytypes.RevisionReconcileOption) (machinery.RevisionResult, error)
38+
}
39+
40+
// RevisionEngineFactory creates a RevisionEngine for a ClusterExtensionRevision.
41+
type RevisionEngineFactory interface {
42+
CreateRevisionEngine(ctx context.Context, rev *ocv1.ClusterExtensionRevision) (RevisionEngine, error)
43+
}
44+
45+
// defaultRevisionEngineFactory creates boxcutter RevisionEngines with serviceAccount-scoped clients.
46+
type defaultRevisionEngineFactory struct {
3447
Scheme *runtime.Scheme
3548
TrackingCache managedcache.TrackingCache
3649
DiscoveryClient discovery.CachedDiscoveryInterface
3750
RESTMapper meta.RESTMapper
3851
FieldOwnerPrefix string
3952
BaseConfig *rest.Config
4053
TokenGetter *authentication.TokenGetter
54+
55+
// engineCache caches RevisionEngines keyed by "namespace/serviceAccountName"
56+
engineCache sync.Map
4157
}
4258

4359
// CreateRevisionEngine constructs a boxcutter RevisionEngine for the given ClusterExtensionRevision.
44-
// It reads the ServiceAccount from annotations and creates a scoped client.
45-
func (f *DefaultRevisionEngineFactory) CreateRevisionEngine(_ context.Context, rev *ocv1.ClusterExtensionRevision) (RevisionEngine, error) {
46-
scopedClient, err := f.getScopedClient(rev)
60+
// It reads the ServiceAccount from annotations and returns a cached or new engine.
61+
func (f *defaultRevisionEngineFactory) CreateRevisionEngine(_ context.Context, rev *ocv1.ClusterExtensionRevision) (RevisionEngine, error) {
62+
cacheKey, err := f.getServiceAccountKey(rev)
4763
if err != nil {
4864
return nil, err
4965
}
5066

51-
return machinery.NewRevisionEngine(
67+
// Check cache first
68+
if cached, ok := f.engineCache.Load(cacheKey); ok {
69+
return cached.(RevisionEngine), nil
70+
}
71+
72+
scopedClient, err := f.createScopedClient(cacheKey)
73+
if err != nil {
74+
return nil, err
75+
}
76+
77+
engine := machinery.NewRevisionEngine(
5278
machinery.NewPhaseEngine(
5379
machinery.NewObjectEngine(
5480
f.Scheme, f.TrackingCache, scopedClient,
@@ -59,35 +85,36 @@ func (f *DefaultRevisionEngineFactory) CreateRevisionEngine(_ context.Context, r
5985
validation.NewClusterPhaseValidator(f.RESTMapper, scopedClient),
6086
),
6187
validation.NewRevisionValidator(), scopedClient,
62-
), nil
88+
)
89+
90+
// Store in cache (LoadOrStore handles race conditions)
91+
actual, _ := f.engineCache.LoadOrStore(cacheKey, engine)
92+
return actual.(RevisionEngine), nil
6393
}
6494

65-
func (f *DefaultRevisionEngineFactory) getScopedClient(rev *ocv1.ClusterExtensionRevision) (client.Client, error) {
95+
func (f *defaultRevisionEngineFactory) getServiceAccountKey(rev *ocv1.ClusterExtensionRevision) (string, error) {
6696
annotations := rev.GetAnnotations()
6797
if annotations == nil {
68-
return nil, fmt.Errorf("revision %q is missing required annotations", rev.Name)
98+
return "", fmt.Errorf("revision %q is missing required annotations", rev.Name)
6999
}
70100

71101
saName := strings.TrimSpace(annotations[labels.ServiceAccountNameKey])
72102
saNamespace := strings.TrimSpace(annotations[labels.ServiceAccountNamespaceKey])
73103

74104
if len(saName) == 0 {
75-
return nil, fmt.Errorf("revision %q is missing ServiceAccount name annotation", rev.Name)
105+
return "", fmt.Errorf("revision %q is missing ServiceAccount name annotation", rev.Name)
76106
}
77107
if len(saNamespace) == 0 {
78-
return nil, fmt.Errorf("revision %q is missing ServiceAccount namespace annotation", rev.Name)
108+
return "", fmt.Errorf("revision %q is missing ServiceAccount namespace annotation", rev.Name)
79109
}
80110

81-
return f.createScopedClient(saNamespace, saName)
111+
return saNamespace + "/" + saName, nil
82112
}
83113

84-
func (f *DefaultRevisionEngineFactory) createScopedClient(namespace, serviceAccountName string) (client.Client, error) {
85-
if f.TokenGetter == nil {
86-
return nil, fmt.Errorf("TokenGetter is required but not configured")
87-
}
88-
if f.BaseConfig == nil {
89-
return nil, fmt.Errorf("BaseConfig is required but not configured")
90-
}
114+
func (f *defaultRevisionEngineFactory) createScopedClient(serviceAccountKey string) (client.Client, error) {
115+
// Parse the key back to namespace/name
116+
parts := strings.SplitN(serviceAccountKey, "/", 2)
117+
namespace, serviceAccountName := parts[0], parts[1]
91118

92119
saConfig := rest.AnonymousClientConfig(f.BaseConfig)
93120
saConfig.Wrap(func(rt http.RoundTripper) http.RoundTripper {
@@ -105,13 +132,13 @@ func (f *DefaultRevisionEngineFactory) createScopedClient(namespace, serviceAcco
105132
Scheme: f.Scheme,
106133
})
107134
if err != nil {
108-
return nil, fmt.Errorf("failed to create client for ServiceAccount %s/%s: %w", namespace, serviceAccountName, err)
135+
return nil, fmt.Errorf("failed to create client for ServiceAccount %s: %w", serviceAccountKey, err)
109136
}
110137

111138
return scopedClient, nil
112139
}
113140

114-
// NewDefaultRevisionEngineFactory creates a new DefaultRevisionEngineFactory.
141+
// NewDefaultRevisionEngineFactory creates a new defaultRevisionEngineFactory.
115142
func NewDefaultRevisionEngineFactory(
116143
scheme *runtime.Scheme,
117144
trackingCache managedcache.TrackingCache,
@@ -120,14 +147,20 @@ func NewDefaultRevisionEngineFactory(
120147
fieldOwnerPrefix string,
121148
baseConfig *rest.Config,
122149
tokenGetter *authentication.TokenGetter,
123-
) *DefaultRevisionEngineFactory {
124-
return &DefaultRevisionEngineFactory{
150+
) (RevisionEngineFactory, error) {
151+
if baseConfig == nil {
152+
return nil, fmt.Errorf("baseConfig is required but not provided")
153+
}
154+
if tokenGetter == nil {
155+
return nil, fmt.Errorf("tokenGetter is required but not provided")
156+
}
157+
return &defaultRevisionEngineFactory{
125158
Scheme: scheme,
126159
TrackingCache: trackingCache,
127160
DiscoveryClient: discoveryClient,
128161
RESTMapper: restMapper,
129162
FieldOwnerPrefix: fieldOwnerPrefix,
130163
BaseConfig: baseConfig,
131164
TokenGetter: tokenGetter,
132-
}
165+
}, nil
133166
}

0 commit comments

Comments
 (0)