Skip to content

Commit 95adca5

Browse files
authored
Merge pull request #826 from Danil-Grigorev/expose-internal-controllers
✨ Expose internal controllers
2 parents 594be36 + 0fe1d45 commit 95adca5

File tree

8 files changed

+200
-127
lines changed

8 files changed

+200
-127
lines changed

cmd/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchConfigSecretCh
244244
Client: mgr.GetClient(),
245245
Config: mgr.GetConfig(),
246246
WatchConfigSecretChanges: watchConfigSecretChanges,
247+
WatchCoreProviderChanges: true,
247248
}).SetupWithManager(ctx, mgr, concurrency(concurrencyNumber)); err != nil {
248249
setupLog.Error(err, "unable to create controller", "controller", "InfrastructureProvider")
249250
os.Exit(1)
@@ -255,6 +256,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchConfigSecretCh
255256
Client: mgr.GetClient(),
256257
Config: mgr.GetConfig(),
257258
WatchConfigSecretChanges: watchConfigSecretChanges,
259+
WatchCoreProviderChanges: true,
258260
}).SetupWithManager(ctx, mgr, concurrency(concurrencyNumber)); err != nil {
259261
setupLog.Error(err, "unable to create controller", "controller", "BootstrapProvider")
260262
os.Exit(1)
@@ -266,6 +268,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchConfigSecretCh
266268
Client: mgr.GetClient(),
267269
Config: mgr.GetConfig(),
268270
WatchConfigSecretChanges: watchConfigSecretChanges,
271+
WatchCoreProviderChanges: true,
269272
}).SetupWithManager(ctx, mgr, concurrency(concurrencyNumber)); err != nil {
270273
setupLog.Error(err, "unable to create controller", "controller", "ControlPlaneProvider")
271274
os.Exit(1)
@@ -277,6 +280,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchConfigSecretCh
277280
Client: mgr.GetClient(),
278281
Config: mgr.GetConfig(),
279282
WatchConfigSecretChanges: watchConfigSecretChanges,
283+
WatchCoreProviderChanges: true,
280284
}).SetupWithManager(ctx, mgr, concurrency(concurrencyNumber)); err != nil {
281285
setupLog.Error(err, "unable to create controller", "controller", "AddonProvider")
282286
os.Exit(1)
@@ -288,6 +292,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchConfigSecretCh
288292
Client: mgr.GetClient(),
289293
Config: mgr.GetConfig(),
290294
WatchConfigSecretChanges: watchConfigSecretChanges,
295+
WatchCoreProviderChanges: true,
291296
}).SetupWithManager(ctx, mgr, concurrency(concurrencyNumber)); err != nil {
292297
setupLog.Error(err, "unable to create controller", "controller", "IPAMProvider")
293298
os.Exit(1)
@@ -299,6 +304,7 @@ func setupReconcilers(ctx context.Context, mgr ctrl.Manager, watchConfigSecretCh
299304
Client: mgr.GetClient(),
300305
Config: mgr.GetConfig(),
301306
WatchConfigSecretChanges: watchConfigSecretChanges,
307+
WatchCoreProviderChanges: true,
302308
}).SetupWithManager(ctx, mgr, concurrency(concurrencyNumber)); err != nil {
303309
setupLog.Error(err, "unable to create controller", "controller", "RuntimeExtensionProvider")
304310
os.Exit(1)

controller/alias.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,28 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
/*
18+
Package controller provides aliases for internal controller types and functions
19+
to allow external users to interact with the core controller logic.
20+
*/
1721
package controller
1822

19-
import providercontroller "sigs.k8s.io/cluster-api-operator/internal/controller"
23+
import (
24+
providercontroller "sigs.k8s.io/cluster-api-operator/internal/controller"
25+
internalhealthcheck "sigs.k8s.io/cluster-api-operator/internal/controller/healthcheck"
26+
)
2027

28+
// GenericProviderReconciler wraps the internal GenericProviderReconciler.
2129
type GenericProviderReconciler = providercontroller.GenericProviderReconciler
30+
31+
// GenericProviderHealthCheckReconciler wraps the internal GenericProviderHealthCheckReconciler.
32+
type GenericProviderHealthCheckReconciler = internalhealthcheck.GenericProviderHealthCheckReconciler
33+
34+
// PhaseFn is an alias for the internal PhaseFn type.
35+
type PhaseFn = providercontroller.PhaseFn
36+
37+
// Result is an alias for the internal Result type.
38+
type Result = providercontroller.Result
39+
40+
// NewPhaseReconciler is an alias for the internal NewPhaseReconciler function.
41+
var NewPhaseReconciler = providercontroller.NewPhaseReconciler

internal/controller/genericprovider_controller.go

Lines changed: 70 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"errors"
2424
"fmt"
2525
"hash"
26-
"reflect"
2726

2827
corev1 "k8s.io/api/core/v1"
2928
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -49,23 +48,27 @@ type GenericProviderReconciler struct {
4948
Client client.Client
5049
Config *rest.Config
5150
WatchConfigSecretChanges bool
51+
WatchCoreProviderChanges bool
52+
53+
DeletePhases []PhaseFn
54+
ReconcilePhases []PhaseFn
5255
}
5356

5457
const (
5558
appliedSpecHashAnnotation = "operator.cluster.x-k8s.io/applied-spec-hash"
5659
)
5760

58-
func (r *GenericProviderReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
61+
func (r *GenericProviderReconciler) BuildWithManager(ctx context.Context, mgr ctrl.Manager) (*ctrl.Builder, error) {
5962
builder := ctrl.NewControllerManagedBy(mgr).
6063
For(r.Provider)
6164

6265
if r.WatchConfigSecretChanges {
6366
if err := mgr.GetFieldIndexer().IndexField(ctx, r.Provider, configSecretNameField, configSecretNameIndexFunc); err != nil {
64-
return err
67+
return nil, err
6568
}
6669

6770
if err := mgr.GetFieldIndexer().IndexField(ctx, r.Provider, configSecretNamespaceField, configSecretNamespaceIndexFunc); err != nil {
68-
return err
71+
return nil, err
6972
}
7073

7174
builder.Watches(
@@ -75,15 +78,40 @@ func (r *GenericProviderReconciler) SetupWithManager(ctx context.Context, mgr ct
7578
}
7679

7780
// We don't want to receive secondary events from the CoreProvider for itself.
78-
if reflect.TypeOf(r.Provider) != reflect.TypeOf(genericprovider.GenericProvider(&operatorv1.CoreProvider{})) {
81+
if r.WatchCoreProviderChanges {
7982
builder.Watches(
8083
&operatorv1.CoreProvider{},
8184
handler.EnqueueRequestsFromMapFunc(newCoreProviderToProviderFuncMapForProviderList(r.Client, r.ProviderList)),
8285
)
8386
}
8487

85-
return builder.WithOptions(options).
86-
Complete(r)
88+
reconciler := NewPhaseReconciler(*r, r.Provider, r.ProviderList)
89+
90+
r.ReconcilePhases = []PhaseFn{
91+
reconciler.PreflightChecks,
92+
reconciler.InitializePhaseReconciler,
93+
reconciler.DownloadManifests,
94+
reconciler.Load,
95+
reconciler.Fetch,
96+
reconciler.Upgrade,
97+
reconciler.Install,
98+
reconciler.ReportStatus,
99+
}
100+
101+
r.DeletePhases = []PhaseFn{
102+
reconciler.Delete,
103+
}
104+
105+
return builder, nil
106+
}
107+
108+
func (r *GenericProviderReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
109+
builder, err := r.BuildWithManager(ctx, mgr)
110+
if err != nil {
111+
return err
112+
}
113+
114+
return builder.WithOptions(options).Complete(r)
87115
}
88116

89117
func (r *GenericProviderReconciler) Reconcile(ctx context.Context, req reconcile.Request) (_ reconcile.Result, reterr error) {
@@ -128,7 +156,15 @@ func (r *GenericProviderReconciler) Reconcile(ctx context.Context, req reconcile
128156

129157
// Handle deletion reconciliation loop.
130158
if !r.Provider.GetDeletionTimestamp().IsZero() {
131-
return r.reconcileDelete(ctx, r.Provider)
159+
res, err := r.reconcileDelete(ctx, r.Provider)
160+
if err != nil {
161+
return reconcile.Result{}, err
162+
}
163+
164+
return ctrl.Result{
165+
Requeue: res.Requeue,
166+
RequeueAfter: res.RequeueAfter,
167+
}, nil
132168
}
133169

134170
// Check if spec hash stays the same and don't go further in this case.
@@ -142,7 +178,7 @@ func (r *GenericProviderReconciler) Reconcile(ctx context.Context, req reconcile
142178
return ctrl.Result{}, nil
143179
}
144180

145-
res, err := r.reconcile(ctx, r.Provider, r.ProviderList)
181+
res, err := r.reconcile(ctx)
146182

147183
annotations := r.Provider.GetAnnotations()
148184
if annotations == nil {
@@ -164,7 +200,10 @@ func (r *GenericProviderReconciler) Reconcile(ctx context.Context, req reconcile
164200

165201
r.Provider.SetAnnotations(annotations)
166202

167-
return res, ignoreCoreProviderWaitError(err)
203+
return ctrl.Result{
204+
Requeue: res.Requeue,
205+
RequeueAfter: res.RequeueAfter,
206+
}, ignoreCoreProviderWaitError(err)
168207
}
169208

170209
func patchProvider(ctx context.Context, provider operatorv1.GenericProvider, patchHelper *patch.Helper, options ...patch.Option) error {
@@ -178,57 +217,41 @@ func patchProvider(ctx context.Context, provider operatorv1.GenericProvider, pat
178217
return patchHelper.Patch(ctx, provider, options...)
179218
}
180219

181-
func (r *GenericProviderReconciler) reconcile(ctx context.Context, provider genericprovider.GenericProvider, genericProviderList genericprovider.GenericProviderList) (ctrl.Result, error) {
182-
reconciler := newPhaseReconciler(*r, provider, genericProviderList)
183-
phases := []reconcilePhaseFn{
184-
reconciler.preflightChecks,
185-
reconciler.initializePhaseReconciler,
186-
reconciler.downloadManifests,
187-
reconciler.load,
188-
reconciler.fetch,
189-
reconciler.upgrade,
190-
reconciler.install,
191-
reconciler.reportStatus,
192-
}
220+
func (r *GenericProviderReconciler) reconcile(ctx context.Context) (*Result, error) {
221+
var res Result
193222

194-
res := reconcile.Result{}
195-
196-
var err error
197-
198-
for _, phase := range phases {
199-
res, err = phase(ctx)
223+
for _, phase := range r.ReconcilePhases {
224+
res, err := phase(ctx)
200225
if err != nil {
201226
var pe *PhaseError
202227
if errors.As(err, &pe) {
203-
conditions.Set(provider, conditions.FalseCondition(pe.Type, pe.Reason, pe.Severity, "%s", err.Error()))
228+
conditions.Set(r.Provider, conditions.FalseCondition(pe.Type, pe.Reason, pe.Severity, "%s", err.Error()))
204229
}
205230
}
206231

207232
if !res.IsZero() || err != nil {
233+
// Stop the reconciliation if the phase was final
234+
if res.Completed {
235+
return &Result{}, nil
236+
}
237+
208238
// the steps are sequential, so we must be complete before progressing.
209239
return res, err
210240
}
211241
}
212242

213-
return res, nil
243+
return &res, nil
214244
}
215245

216-
func (r *GenericProviderReconciler) reconcileDelete(ctx context.Context, provider operatorv1.GenericProvider) (ctrl.Result, error) {
246+
func (r *GenericProviderReconciler) reconcileDelete(ctx context.Context, provider operatorv1.GenericProvider) (*Result, error) {
217247
log := ctrl.LoggerFrom(ctx)
218248

219249
log.Info("Deleting provider resources")
220250

221-
reconciler := newPhaseReconciler(*r, provider, nil)
222-
phases := []reconcilePhaseFn{
223-
reconciler.delete,
224-
}
225-
226-
res := reconcile.Result{}
251+
var res Result
227252

228-
var err error
229-
230-
for _, phase := range phases {
231-
res, err = phase(ctx)
253+
for _, phase := range r.DeletePhases {
254+
res, err := phase(ctx)
232255
if err != nil {
233256
var pe *PhaseError
234257
if errors.As(err, &pe) {
@@ -237,14 +260,19 @@ func (r *GenericProviderReconciler) reconcileDelete(ctx context.Context, provide
237260
}
238261

239262
if !res.IsZero() || err != nil {
263+
// Stop the reconciliation if the phase was final
264+
if res.Completed {
265+
return &Result{}, nil
266+
}
267+
240268
// the steps are sequential, so we must be complete before progressing.
241269
return res, err
242270
}
243271
}
244272

245273
controllerutil.RemoveFinalizer(provider, operatorv1.ProviderFinalizer)
246274

247-
return res, nil
275+
return &res, nil
248276
}
249277

250278
func addConfigSecretToHash(ctx context.Context, k8sClient client.Client, hash hash.Hash, provider genericprovider.GenericProvider) error {

internal/controller/manifests_downloader.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import (
3131
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
3232
ctrl "sigs.k8s.io/controller-runtime"
3333
"sigs.k8s.io/controller-runtime/pkg/client"
34-
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3534

3635
operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha2"
3736
"sigs.k8s.io/cluster-api-operator/util"
@@ -46,15 +45,15 @@ const (
4645
ociSource = "oci"
4746
)
4847

49-
// downloadManifests downloads CAPI manifests from a url.
50-
func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Result, error) {
48+
// DownloadManifests downloads CAPI manifests from a url.
49+
func (p *PhaseReconciler) DownloadManifests(ctx context.Context) (*Result, error) {
5150
log := ctrl.LoggerFrom(ctx)
5251

5352
// Return immediately if a custom config map is used instead of a url.
5453
if p.provider.GetSpec().FetchConfig != nil && p.provider.GetSpec().FetchConfig.Selector != nil {
5554
log.V(5).Info("Custom config map is used, skip downloading provider manifests")
5655

57-
return reconcile.Result{}, nil
56+
return &Result{}, nil
5857
}
5958

6059
// Check if manifests are already downloaded and stored in a configmap
@@ -64,13 +63,13 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
6463

6564
exists, err := p.checkConfigMapExists(ctx, labelSelector, p.provider.GetNamespace())
6665
if err != nil {
67-
return reconcile.Result{}, wrapPhaseError(err, "failed to check that config map with manifests exists", operatorv1.ProviderInstalledCondition)
66+
return &Result{}, wrapPhaseError(err, "failed to check that config map with manifests exists", operatorv1.ProviderInstalledCondition)
6867
}
6968

7069
if exists {
7170
log.V(5).Info("Config map with downloaded manifests already exists, skip downloading provider manifests")
7271

73-
return reconcile.Result{}, nil
72+
return &Result{}, nil
7473
}
7574

7675
log.Info("Downloading provider manifests")
@@ -80,7 +79,7 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
8079
if err != nil {
8180
err = fmt.Errorf("failed to create repo from provider url for provider %q: %w", p.provider.GetName(), err)
8281

83-
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
82+
return &Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
8483
}
8584
}
8685

@@ -100,26 +99,26 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
10099
if p.provider.GetSpec().FetchConfig != nil && p.provider.GetSpec().FetchConfig.OCI != "" {
101100
configMap, err = OCIConfigMap(ctx, p.provider, OCIAuthentication(p.configClient.Variables()))
102101
if err != nil {
103-
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
102+
return &Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
104103
}
105104
} else {
106105
configMap, err = RepositoryConfigMap(ctx, p.provider, p.repo)
107106
if err != nil {
108107
err = fmt.Errorf("failed to create config map for provider %q: %w", p.provider.GetName(), err)
109108

110-
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
109+
return &Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
111110
}
112111
}
113112

114113
if err := p.ctrlClient.Create(ctx, configMap); client.IgnoreAlreadyExists(err) != nil {
115-
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
114+
return &Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.ProviderInstalledCondition)
116115
}
117116

118-
return reconcile.Result{}, nil
117+
return &Result{}, nil
119118
}
120119

121120
// checkConfigMapExists checks if a config map exists in Kubernetes with the given LabelSelector.
122-
func (p *phaseReconciler) checkConfigMapExists(ctx context.Context, labelSelector metav1.LabelSelector, namespace string) (bool, error) {
121+
func (p *PhaseReconciler) checkConfigMapExists(ctx context.Context, labelSelector metav1.LabelSelector, namespace string) (bool, error) {
123122
labelSet := labels.Set(labelSelector.MatchLabels)
124123
listOpts := []client.ListOption{
125124
client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(labelSet)},
@@ -140,7 +139,7 @@ func (p *phaseReconciler) checkConfigMapExists(ctx context.Context, labelSelecto
140139
}
141140

142141
// prepareConfigMapLabels returns labels that identify a config map with downloaded manifests.
143-
func (p *phaseReconciler) prepareConfigMapLabels() map[string]string {
142+
func (p *PhaseReconciler) prepareConfigMapLabels() map[string]string {
144143
return ProviderLabels(p.provider)
145144
}
146145

0 commit comments

Comments
 (0)