Skip to content

Commit 978685b

Browse files
committed
feat: set latest provider version if spec doesn't define it
1 parent 382f2b2 commit 978685b

File tree

4 files changed

+85
-54
lines changed

4 files changed

+85
-54
lines changed

internal/controller/manifests_downloader.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,22 +75,36 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
7575
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
7676
}
7777

78+
version := p.provider.GetSpec().Version
79+
if version == "" {
80+
// User didn't set the version, so we need to find the latest one from the provided URL.
81+
repoVersions, err := repo.GetVersions()
82+
if err != nil {
83+
return reconcile.Result{}, wrapPhaseError(err, fmt.Sprintf("failed to get a list of available versions for provider %q", p.provider.GetName()))
84+
}
85+
86+
version, err = getLatestVersion(repoVersions)
87+
if err != nil {
88+
return reconcile.Result{}, wrapPhaseError(err, fmt.Sprintf("failed to get the latest version for provider %q", p.provider.GetName()))
89+
}
90+
}
91+
7892
// Fetch the provider metadata and components yaml files from the provided repository GitHub/GitLab.
79-
metadataFile, err := repo.GetFile(p.options.Version, metadataFile)
93+
metadataFile, err := repo.GetFile(version, metadataFile)
8094
if err != nil {
8195
err = fmt.Errorf("failed to read %q from the repository for provider %q: %w", metadataFile, p.provider.GetName(), err)
8296

8397
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
8498
}
8599

86-
componentsFile, err := repo.GetFile(p.options.Version, repo.ComponentsPath())
100+
componentsFile, err := repo.GetFile(version, repo.ComponentsPath())
87101
if err != nil {
88102
err = fmt.Errorf("failed to read %q from the repository for provider %q: %w", componentsFile, p.provider.GetName(), err)
89103

90104
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
91105
}
92106

93-
if err := p.createManifestsConfigMap(ctx, string(metadataFile), string(componentsFile)); err != nil {
107+
if err := p.createManifestsConfigMap(ctx, string(metadataFile), string(componentsFile), version); err != nil {
94108
err = fmt.Errorf("failed to create config map for provider %q: %w", p.provider.GetName(), err)
95109

96110
return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
@@ -130,8 +144,8 @@ func (p *phaseReconciler) prepareConfigMapLabels() map[string]string {
130144
}
131145

132146
// createManifestsConfigMap creates a config map with downloaded manifests.
133-
func (p *phaseReconciler) createManifestsConfigMap(ctx context.Context, metadata, components string) error {
134-
configMapName := fmt.Sprintf("%s-%s-%s", p.provider.GetType(), p.provider.GetName(), p.provider.GetSpec().Version)
147+
func (p *phaseReconciler) createManifestsConfigMap(ctx context.Context, metadata, components, version string) error {
148+
configMapName := fmt.Sprintf("%s-%s-%s", p.provider.GetType(), p.provider.GetName(), version)
135149

136150
configMap := &corev1.ConfigMap{
137151
ObjectMeta: metav1.ObjectMeta{

internal/controller/phases.go

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,6 @@ func (p *phaseReconciler) initializePhaseReconciler(ctx context.Context) (reconc
131131
return reconcile.Result{}, wrapPhaseError(err, operatorv1.UnknownProviderReason)
132132
}
133133

134-
spec := p.provider.GetSpec()
135-
136-
// Store some provider specific inputs for passing it to clusterctl library
137-
p.options = repository.ComponentsOptions{
138-
TargetNamespace: p.provider.GetNamespace(),
139-
SkipTemplateProcess: false,
140-
Version: spec.Version,
141-
}
142-
143134
return reconcile.Result{}, nil
144135
}
145136

@@ -167,11 +158,25 @@ func (p *phaseReconciler) load(ctx context.Context) (reconcile.Result, error) {
167158
return reconcile.Result{}, wrapPhaseError(err, "failed to load the repository")
168159
}
169160

161+
version := spec.Version
162+
if version == "" {
163+
// User didn't set the version, so we need to find the latest one from the matching config maps.
164+
repoVersions, err := p.repo.GetVersions()
165+
if err != nil {
166+
return reconcile.Result{}, wrapPhaseError(err, fmt.Sprintf("failed to get a list of available versions for provider %q", p.provider.GetName()))
167+
}
168+
169+
version, err = getLatestVersion(repoVersions)
170+
if err != nil {
171+
return reconcile.Result{}, wrapPhaseError(err, fmt.Sprintf("failed to get the latest version for provider %q", p.provider.GetName()))
172+
}
173+
}
174+
170175
// Store some provider specific inputs for passing it to clusterctl library
171176
p.options = repository.ComponentsOptions{
172177
TargetNamespace: p.provider.GetNamespace(),
173178
SkipTemplateProcess: false,
174-
Version: spec.Version,
179+
Version: version,
175180
}
176181

177182
if err := p.validateRepoCAPIVersion(); err != nil {
@@ -476,3 +481,27 @@ func repositoryFactory(providerConfig configclient.Provider, configVariablesClie
476481

477482
return nil, fmt.Errorf("invalid provider url. Only GitHub and GitLab are supported for %q schema", rURL.Scheme)
478483
}
484+
485+
func getLatestVersion(repoVersions []string) (string, error) {
486+
if len(repoVersions) == 0 {
487+
err := fmt.Errorf("no versions available")
488+
489+
return "", wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason)
490+
}
491+
492+
// Initialize latest version with the first element value.
493+
latestVersion := versionutil.MustParseSemantic(repoVersions[0])
494+
495+
for _, versionString := range repoVersions {
496+
parsedVersion, err := versionutil.ParseSemantic(versionString)
497+
if err != nil {
498+
return "", wrapPhaseError(err, fmt.Sprintf("cannot parse version string: %s", versionString))
499+
}
500+
501+
if latestVersion.LessThan(parsedVersion) {
502+
latestVersion = parsedVersion
503+
}
504+
}
505+
506+
return "v" + latestVersion.String(), nil
507+
}

internal/controller/preflight_checks.go

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ var (
4646
capiVersionIncompatibilityMessage = "CAPI operator is only compatible with %s providers, detected %s for provider %s."
4747
invalidGithubTokenMessage = "Invalid github token, please check your github token value and its permissions" //nolint:gosec
4848
waitingForCoreProviderReadyMessage = "Waiting for the core provider to be installed."
49-
emptyVersionMessage = "Version cannot be empty"
5049
)
5150

5251
// preflightChecks performs preflight checks before installing provider.
@@ -57,30 +56,19 @@ func preflightChecks(ctx context.Context, c client.Client, provider genericprovi
5756

5857
spec := provider.GetSpec()
5958

60-
// Check that provider version is not empty.
61-
if spec.Version == "" {
62-
log.Info("Version can't be empty")
63-
conditions.Set(provider, conditions.FalseCondition(
64-
operatorv1.PreflightCheckCondition,
65-
operatorv1.EmptyVersionReason,
66-
clusterv1.ConditionSeverityError,
67-
emptyVersionMessage,
68-
))
69-
70-
return ctrl.Result{}, fmt.Errorf("version can't be empty for provider %s", provider.GetName())
71-
}
72-
73-
// Check that provider version contains a valid value.
74-
if _, err := version.ParseSemantic(spec.Version); err != nil {
75-
log.Info("Version contains invalid value")
76-
conditions.Set(provider, conditions.FalseCondition(
77-
operatorv1.PreflightCheckCondition,
78-
operatorv1.IncorrectVersionFormatReason,
79-
clusterv1.ConditionSeverityError,
80-
err.Error(),
81-
))
59+
// Check that provider version contains a valid value if it's not empty.
60+
if spec.Version != "" {
61+
if _, err := version.ParseSemantic(spec.Version); err != nil {
62+
log.Info("Version contains invalid value")
63+
conditions.Set(provider, conditions.FalseCondition(
64+
operatorv1.PreflightCheckCondition,
65+
operatorv1.IncorrectVersionFormatReason,
66+
clusterv1.ConditionSeverityError,
67+
err.Error(),
68+
))
8269

83-
return ctrl.Result{}, fmt.Errorf("version contains invalid value for provider %s", provider.GetName())
70+
return ctrl.Result{}, fmt.Errorf("version contains invalid value for provider %q", provider.GetName())
71+
}
8472
}
8573

8674
// Check that if a predefined provider is being installed, and if it's not - ensure that FetchConfig is specified.

internal/controller/preflight_checks_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -463,34 +463,34 @@ func TestPreflightChecks(t *testing.T) {
463463
},
464464
},
465465
{
466-
name: "missing version, preflight check failed",
467-
expectedError: true,
466+
name: "missing version, preflight check passed",
468467
providers: []genericprovider.GenericProvider{
469-
&genericprovider.InfrastructureProviderWrapper{
470-
InfrastructureProvider: &operatorv1.InfrastructureProvider{
468+
&genericprovider.CoreProviderWrapper{
469+
CoreProvider: &operatorv1.CoreProvider{
471470
ObjectMeta: metav1.ObjectMeta{
472-
Name: "aws",
471+
Name: "cluster-api",
473472
Namespace: namespaceName1,
474473
},
475474
TypeMeta: metav1.TypeMeta{
476-
Kind: "InfrastructureProvider",
475+
Kind: "CoreProvider",
477476
APIVersion: "operator.cluster.x-k8s.io/v1alpha1",
478477
},
479-
Spec: operatorv1.InfrastructureProviderSpec{
480-
ProviderSpec: operatorv1.ProviderSpec{},
478+
Spec: operatorv1.CoreProviderSpec{
479+
ProviderSpec: operatorv1.ProviderSpec{
480+
FetchConfig: &operatorv1.FetchConfiguration{
481+
URL: "https://example.com",
482+
},
483+
},
481484
},
482485
},
483486
},
484487
},
485488
expectedCondition: clusterv1.Condition{
486-
Type: operatorv1.PreflightCheckCondition,
487-
Reason: operatorv1.IncorrectVersionFormatReason,
488-
Severity: clusterv1.ConditionSeverityError,
489-
Message: "Version cannot be empty",
490-
Status: corev1.ConditionFalse,
489+
Type: operatorv1.PreflightCheckCondition,
490+
Status: corev1.ConditionTrue,
491491
},
492-
providerList: &genericprovider.InfrastructureProviderListWrapper{
493-
InfrastructureProviderList: &operatorv1.InfrastructureProviderList{},
492+
providerList: &genericprovider.CoreProviderListWrapper{
493+
CoreProviderList: &operatorv1.CoreProviderList{},
494494
},
495495
},
496496
{

0 commit comments

Comments
 (0)