Skip to content

Commit b17b9c9

Browse files
committed
chore: relocate version change validation to preflight check
Previously, the check was executed at the end of reconciliation and in multiple instances. By moving it to the beginning, the process is halted when installed and desired versions match, streamlining the reconciliation workflow.
1 parent e61a860 commit b17b9c9

File tree

4 files changed

+70
-61
lines changed

4 files changed

+70
-61
lines changed

internal/controller/genericprovider_controller.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,17 @@ func (r *GenericProviderReconciler) Reconcile(ctx context.Context, req reconcile
108108
}
109109

110110
// Run preflight checks to ensure that we can proceed with given specification
111-
res, err := preflightchecks.PreflightChecks(ctx, r.Client, typedProvider, typedProviderList)
111+
res, halt, err := preflightchecks.PreflightChecks(ctx, r.Client, typedProvider, typedProviderList)
112112
if !res.IsZero() || err != nil {
113-
// the steps are sequential, so we must be complete before progressing.
114113
return res, err
115114
}
116115

116+
if halt {
117+
log.Info("No changes detected, skipping further steps")
118+
119+
return res, nil
120+
}
121+
117122
return r.reconcile(ctx, typedProvider, typedProviderList)
118123
}
119124

internal/controller/phases.go

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -345,59 +345,15 @@ func (p *phaseReconciler) fetch(ctx context.Context) (reconcile.Result, error) {
345345
func (p *phaseReconciler) preInstall(ctx context.Context) (reconcile.Result, error) {
346346
log := ctrl.LoggerFrom(ctx)
347347

348-
needPreDelete, err := p.versionChanged()
349-
if err != nil {
350-
return reconcile.Result{}, wrapPhaseError(err, "failed getting clusterctl Provider version")
351-
}
352-
353-
// we need to delete existing components only if their version changes and something has already been installed.
354-
if !needPreDelete || p.provider.GetStatus().InstalledVersion == nil {
355-
return reconcile.Result{}, nil
356-
}
357-
358348
log.Info("Upgrade detected, deleting existing components")
359349

360350
return p.delete(ctx)
361351
}
362352

363-
// versionChanged try to get installed version from provider status and decide if it has changed.
364-
func (p *phaseReconciler) versionChanged() (bool, error) {
365-
installedVersion := p.provider.GetStatus().InstalledVersion
366-
if installedVersion == nil {
367-
return true, nil
368-
}
369-
370-
currentVersion, err := versionutil.ParseSemantic(*installedVersion)
371-
if err != nil {
372-
return false, err
373-
}
374-
375-
res, err := currentVersion.Compare(p.components.Version())
376-
if err != nil {
377-
return false, err
378-
}
379-
380-
// we need to delete installed components if versions are different
381-
versionChanged := res != 0
382-
383-
return versionChanged, nil
384-
}
385-
386353
// install installs the provider components using clusterctl library.
387354
func (p *phaseReconciler) install(ctx context.Context) (reconcile.Result, error) {
388355
log := ctrl.LoggerFrom(ctx)
389356

390-
versionChanged, err := p.versionChanged()
391-
if err != nil {
392-
return reconcile.Result{}, wrapPhaseError(err, "failed getting clusterctl Provider version")
393-
}
394-
395-
// skip installation if the version hasn't changed.
396-
if !versionChanged {
397-
log.Info("Provider already installed")
398-
return reconcile.Result{}, nil
399-
}
400-
401357
clusterClient := p.newClusterClient()
402358

403359
log.Info("Installing provider")

internal/controller/preflightchecks/preflight_checks.go

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ var (
5454
)
5555

5656
// PreflightChecks performs preflight checks before installing provider.
57-
func PreflightChecks(ctx context.Context, c client.Client, provider genericprovider.GenericProvider, providerList genericprovider.GenericProviderList) (ctrl.Result, error) {
57+
func PreflightChecks(ctx context.Context, c client.Client, provider genericprovider.GenericProvider, providerList genericprovider.GenericProviderList) (ctrl.Result, bool, error) {
5858
log := ctrl.LoggerFrom(ctx)
5959

6060
log.Info("Performing preflight checks")
@@ -71,7 +71,7 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
7171
emptyVersionMessage,
7272
))
7373

74-
return ctrl.Result{}, fmt.Errorf("version can't be empty for provider %s", provider.GetName())
74+
return ctrl.Result{}, true, fmt.Errorf("version can't be empty for provider %s", provider.GetName())
7575
}
7676

7777
// Check that provider version contains a valid value.
@@ -84,13 +84,23 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
8484
err.Error(),
8585
))
8686

87-
return ctrl.Result{}, fmt.Errorf("version contains invalid value for provider %s", provider.GetName())
87+
return ctrl.Result{}, true, fmt.Errorf("version contains invalid value for provider %s", provider.GetName())
88+
}
89+
90+
// If desired and installed versions are the same - halt reconciliation
91+
changed, err := versionChanged(provider)
92+
if err != nil {
93+
return ctrl.Result{}, true, fmt.Errorf("failed to parse installed version: %w", err)
94+
}
95+
96+
if !changed {
97+
return ctrl.Result{}, true, nil
8898
}
8999

90100
// Check that if a predefined provider is being installed, and if it's not - ensure that FetchConfig is specified.
91101
isPredefinedProvider, err := isPredefinedProvider(provider.GetName(), util.ClusterctlProviderType(provider))
92102
if err != nil {
93-
return ctrl.Result{}, fmt.Errorf("failed to generate a list of predefined providers: %w", err)
103+
return ctrl.Result{}, true, fmt.Errorf("failed to generate a list of predefined providers: %w", err)
94104
}
95105

96106
if !isPredefinedProvider {
@@ -102,7 +112,7 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
102112
"Either Selector or URL must be provided for a not predefined provider",
103113
))
104114

105-
return ctrl.Result{}, fmt.Errorf("either selector or URL must be provided for a not predefined provider %s", provider.GetName())
115+
return ctrl.Result{}, true, fmt.Errorf("either selector or URL must be provided for a not predefined provider %s", provider.GetName())
106116
}
107117
}
108118

@@ -115,7 +125,7 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
115125
"Only one of Selector and URL must be provided, not both",
116126
))
117127

118-
return ctrl.Result{}, fmt.Errorf("only one of Selector and URL must be provided for provider %s", provider.GetName())
128+
return ctrl.Result{}, true, fmt.Errorf("only one of Selector and URL must be provided for provider %s", provider.GetName())
119129
}
120130

121131
// Validate that provided github token works and has repository access.
@@ -124,7 +134,7 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
124134
key := types.NamespacedName{Namespace: provider.GetNamespace(), Name: provider.GetSpec().SecretName}
125135

126136
if err := c.Get(ctx, key, secret); err != nil {
127-
return ctrl.Result{}, fmt.Errorf("failed to get providers secret: %w", err)
137+
return ctrl.Result{}, true, fmt.Errorf("failed to get providers secret: %w", err)
128138
}
129139

130140
if token, ok := secret.Data[configclient.GitHubTokenVariable]; ok {
@@ -139,13 +149,13 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
139149
invalidGithubTokenMessage,
140150
))
141151

142-
return ctrl.Result{}, fmt.Errorf("failed to validate provided github token: %w", err)
152+
return ctrl.Result{}, true, fmt.Errorf("failed to validate provided github token: %w", err)
143153
}
144154
}
145155
}
146156

147157
if err := c.List(ctx, providerList.GetObject()); err != nil {
148-
return ctrl.Result{}, fmt.Errorf("failed to list providers: %w", err)
158+
return ctrl.Result{}, true, fmt.Errorf("failed to list providers: %w", err)
149159
}
150160

151161
// Check that no more than one instance of the provider is installed.
@@ -168,7 +178,7 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
168178
preflightFalseCondition.Message = moreThanOneCoreProviderInstanceExistsMessage
169179
conditions.Set(provider, preflightFalseCondition)
170180

171-
return ctrl.Result{}, fmt.Errorf("only one instance of CoreProvider is allowed")
181+
return ctrl.Result{}, true, fmt.Errorf("only one instance of CoreProvider is allowed")
172182
}
173183

174184
// For any other provider we should check that instances with similar name exist in any namespace
@@ -177,15 +187,15 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
177187
log.Info(preflightFalseCondition.Message)
178188
conditions.Set(provider, preflightFalseCondition)
179189

180-
return ctrl.Result{}, fmt.Errorf("only one %s provider is allowed in the cluster", p.GetName())
190+
return ctrl.Result{}, true, fmt.Errorf("only one %s provider is allowed in the cluster", p.GetName())
181191
}
182192
}
183193

184194
// Wait for core provider to be ready before we install other providers.
185195
if !util.IsCoreProvider(provider) {
186196
ready, err := coreProviderIsReady(ctx, c)
187197
if err != nil {
188-
return ctrl.Result{}, fmt.Errorf("failed to get coreProvider ready condition: %w", err)
198+
return ctrl.Result{}, true, fmt.Errorf("failed to get coreProvider ready condition: %w", err)
189199
}
190200

191201
if !ready {
@@ -197,15 +207,15 @@ func PreflightChecks(ctx context.Context, c client.Client, provider genericprovi
197207
waitingForCoreProviderReadyMessage,
198208
))
199209

200-
return ctrl.Result{RequeueAfter: preflightFailedRequeueAfter}, nil
210+
return ctrl.Result{RequeueAfter: preflightFailedRequeueAfter}, true, nil
201211
}
202212
}
203213

204214
conditions.Set(provider, conditions.TrueCondition(operatorv1.PreflightCheckCondition))
205215

206216
log.Info("Preflight checks passed")
207217

208-
return ctrl.Result{}, nil
218+
return ctrl.Result{}, false, nil
209219
}
210220

211221
// coreProviderIsReady returns true if the core provider is ready.
@@ -242,3 +252,26 @@ func isPredefinedProvider(providerName string, providerType clusterctlv1.Provide
242252

243253
return err == nil, nil
244254
}
255+
256+
// versionChanged try to get installed version from provider status and decide if it has changed.
257+
func versionChanged(provider genericprovider.GenericProvider) (bool, error) {
258+
installedVersion := provider.GetStatus().InstalledVersion
259+
if installedVersion == nil {
260+
return true, nil
261+
}
262+
263+
currentVersion, err := version.ParseSemantic(*installedVersion)
264+
if err != nil {
265+
return false, err
266+
}
267+
268+
res, err := currentVersion.Compare(provider.GetSpec().Version)
269+
if err != nil {
270+
return false, err
271+
}
272+
273+
// we need to delete installed components if versions are different
274+
versionChanged := res != 0
275+
276+
return versionChanged, nil
277+
}

internal/controller/preflightchecks/preflight_checks_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func TestPreflightChecks(t *testing.T) {
4646
providerList genericprovider.GenericProviderList
4747
expectedCondition clusterv1.Condition
4848
expectedError bool
49+
expectedHalt bool
4950
}{
5051
{
5152
name: "only one core provider exists, preflight check passed",
@@ -82,6 +83,7 @@ func TestPreflightChecks(t *testing.T) {
8283
{
8384
name: "two core providers were created, preflight check failed",
8485
expectedError: true,
86+
expectedHalt: true,
8587
providers: []genericprovider.GenericProvider{
8688
&genericprovider.CoreProviderWrapper{
8789
CoreProvider: &operatorv1.CoreProvider{
@@ -132,6 +134,7 @@ func TestPreflightChecks(t *testing.T) {
132134
{
133135
name: "two core providers in two different namespaces were created, preflight check failed",
134136
expectedError: true,
137+
expectedHalt: true,
135138
providers: []genericprovider.GenericProvider{
136139
&genericprovider.CoreProviderWrapper{
137140
CoreProvider: &operatorv1.CoreProvider{
@@ -387,6 +390,7 @@ func TestPreflightChecks(t *testing.T) {
387390
{
388391
name: "two similar infra provider exist in different namespaces, preflight check failed",
389392
expectedError: true,
393+
expectedHalt: true,
390394
providers: []genericprovider.GenericProvider{
391395
&genericprovider.InfrastructureProviderWrapper{
392396
InfrastructureProvider: &operatorv1.InfrastructureProvider{
@@ -437,6 +441,7 @@ func TestPreflightChecks(t *testing.T) {
437441
{
438442
name: "wrong version, preflight check failed",
439443
expectedError: true,
444+
expectedHalt: true,
440445
providers: []genericprovider.GenericProvider{
441446
&genericprovider.InfrastructureProviderWrapper{
442447
InfrastructureProvider: &operatorv1.InfrastructureProvider{
@@ -470,6 +475,7 @@ func TestPreflightChecks(t *testing.T) {
470475
{
471476
name: "missing version, preflight check failed",
472477
expectedError: true,
478+
expectedHalt: true,
473479
providers: []genericprovider.GenericProvider{
474480
&genericprovider.InfrastructureProviderWrapper{
475481
InfrastructureProvider: &operatorv1.InfrastructureProvider{
@@ -501,6 +507,7 @@ func TestPreflightChecks(t *testing.T) {
501507
{
502508
name: "incorrect fetchConfig, preflight check failed",
503509
expectedError: true,
510+
expectedHalt: true,
504511
providers: []genericprovider.GenericProvider{
505512
&genericprovider.InfrastructureProviderWrapper{
506513
InfrastructureProvider: &operatorv1.InfrastructureProvider{
@@ -569,6 +576,7 @@ func TestPreflightChecks(t *testing.T) {
569576
{
570577
name: "custom Core Provider without fetch config, preflight check failed",
571578
expectedError: true,
579+
expectedHalt: true,
572580
providers: []genericprovider.GenericProvider{
573581
&genericprovider.CoreProviderWrapper{
574582
CoreProvider: &operatorv1.CoreProvider{
@@ -602,6 +610,7 @@ func TestPreflightChecks(t *testing.T) {
602610
{
603611
name: "custom Core Provider with fetch config with empty values, preflight check failed",
604612
expectedError: true,
613+
expectedHalt: true,
605614
providers: []genericprovider.GenericProvider{
606615
&genericprovider.CoreProviderWrapper{
607616
CoreProvider: &operatorv1.CoreProvider{
@@ -648,13 +657,19 @@ func TestPreflightChecks(t *testing.T) {
648657
gs.Expect(fakeclient.Create(context.Background(), c.GetObject())).To(Succeed())
649658
}
650659

651-
_, err := PreflightChecks(context.Background(), fakeclient, tc.providers[0], tc.providerList)
660+
_, halt, err := PreflightChecks(context.Background(), fakeclient, tc.providers[0], tc.providerList)
652661
if tc.expectedError {
653662
gs.Expect(err).To(HaveOccurred())
654663
} else {
655664
gs.Expect(err).ToNot(HaveOccurred())
656665
}
657666

667+
if tc.expectedHalt {
668+
gs.Expect(halt).To(BeTrue())
669+
} else {
670+
gs.Expect(halt).To(BeFalse())
671+
}
672+
658673
// Check if proper condition is returned
659674
gs.Expect(tc.providers[0].GetStatus().Conditions).To(HaveLen(1))
660675
gs.Expect(tc.providers[0].GetStatus().Conditions[0].Type).To(Equal(tc.expectedCondition.Type))

0 commit comments

Comments
 (0)