Skip to content

Commit eb267f4

Browse files
Collect correct manifest config map per provider while performing upgrade
Clusterctl upgrade logic validates other installed providers, while perfoming upgrade for version compatibility. Operator stores this data in a ConfigMap to allow air-gapped support. This ensures we fetch correct configMap first, to validate version against correct metadata.yaml. Signed-off-by: Danil Grigorev <[email protected]>
1 parent 19c0388 commit eb267f4

File tree

3 files changed

+94
-11
lines changed

3 files changed

+94
-11
lines changed

internal/controller/manifests_downloader.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
6666
MatchLabels: p.prepareConfigMapLabels(),
6767
}
6868

69-
exists, err := p.checkConfigMapExists(ctx, labelSelector)
69+
exists, err := p.checkConfigMapExists(ctx, labelSelector, p.provider.GetNamespace())
7070
if err != nil {
7171
return reconcile.Result{}, wrapPhaseError(err, "failed to check that config map with manifests exists", operatorv1.ProviderInstalledCondition)
7272
}
@@ -123,11 +123,11 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu
123123
}
124124

125125
// checkConfigMapExists checks if a config map exists in Kubernetes with the given LabelSelector.
126-
func (p *phaseReconciler) checkConfigMapExists(ctx context.Context, labelSelector metav1.LabelSelector) (bool, error) {
126+
func (p *phaseReconciler) checkConfigMapExists(ctx context.Context, labelSelector metav1.LabelSelector, namespace string) (bool, error) {
127127
labelSet := labels.Set(labelSelector.MatchLabels)
128128
listOpts := []client.ListOption{
129129
client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(labelSet)},
130-
client.InNamespace(p.provider.GetNamespace()),
130+
client.InNamespace(namespace),
131131
}
132132

133133
var configMapList corev1.ConfigMapList
@@ -145,12 +145,7 @@ func (p *phaseReconciler) checkConfigMapExists(ctx context.Context, labelSelecto
145145

146146
// prepareConfigMapLabels returns labels that identify a config map with downloaded manifests.
147147
func (p *phaseReconciler) prepareConfigMapLabels() map[string]string {
148-
return map[string]string{
149-
configMapVersionLabel: p.provider.GetSpec().Version,
150-
configMapTypeLabel: p.provider.GetType(),
151-
configMapNameLabel: p.provider.GetName(),
152-
operatorManagedLabel: "true",
153-
}
148+
return providerLabels(p.provider)
154149
}
155150

156151
// createManifestsConfigMap creates a config map with downloaded manifests.
@@ -210,6 +205,27 @@ func (p *phaseReconciler) createManifestsConfigMap(ctx context.Context, metadata
210205
return nil
211206
}
212207

208+
func providerLabelSelector(provider operatorv1.GenericProvider) *metav1.LabelSelector {
209+
// Replace label selector if user wants to use custom config map
210+
if provider.GetSpec().FetchConfig != nil && provider.GetSpec().FetchConfig.Selector != nil {
211+
return provider.GetSpec().FetchConfig.Selector
212+
}
213+
214+
return &metav1.LabelSelector{
215+
MatchLabels: providerLabels(provider),
216+
}
217+
}
218+
219+
// prepareConfigMapLabels returns default set of labels that identify a config map with downloaded manifests.
220+
func providerLabels(provider operatorv1.GenericProvider) map[string]string {
221+
return map[string]string{
222+
configMapVersionLabel: provider.GetSpec().Version,
223+
configMapTypeLabel: provider.GetType(),
224+
configMapNameLabel: provider.GetName(),
225+
operatorManagedLabel: "true",
226+
}
227+
}
228+
213229
// needToCompress checks whether the input data exceeds the maximum configmap
214230
// size limit and returns whether it should be compressed.
215231
func needToCompress(bs ...[]byte) bool {

internal/controller/phases.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ import (
4848
"sigs.k8s.io/controller-runtime/pkg/reconcile"
4949
)
5050

51-
const metadataFile = "metadata.yaml"
51+
const (
52+
metadataFile = "metadata.yaml"
53+
)
5254

5355
// phaseReconciler holds all required information for interacting with clusterctl code and
5456
// helps to iterate through provider reconciliation phases.
@@ -574,7 +576,32 @@ func clusterctlProviderName(provider operatorv1.GenericProvider) client.ObjectKe
574576
}
575577

576578
func (p *phaseReconciler) repositoryProxy(ctx context.Context, provider configclient.Provider, configClient configclient.Client, options ...repository.Option) (repository.Client, error) {
577-
cl, err := repository.New(ctx, provider, configClient, append([]repository.Option{repository.InjectRepository(p.repo)}, options...)...)
579+
injectRepo := p.repo
580+
581+
if !provider.SameAs(p.providerConfig) {
582+
genericProvider, err := util.GetGenericProvider(ctx, p.ctrlClient, provider)
583+
if err != nil {
584+
return nil, wrapPhaseError(err, "unable to find generic provider for configclient "+string(provider.Type())+": "+provider.Name(), operatorv1.ProviderUpgradedCondition)
585+
}
586+
587+
if exists, err := p.checkConfigMapExists(ctx, *providerLabelSelector(genericProvider), genericProvider.GetNamespace()); err != nil {
588+
provider := client.ObjectKeyFromObject(genericProvider)
589+
return nil, wrapPhaseError(err, "failed to check the config map repository existence for provider "+provider.String(), operatorv1.ProviderUpgradedCondition)
590+
} else if !exists {
591+
provider := client.ObjectKeyFromObject(genericProvider)
592+
return nil, wrapPhaseError(fmt.Errorf("config map not found"), "config map repository required for validation does not exist yet for provider "+provider.String(), operatorv1.ProviderUpgradedCondition)
593+
}
594+
595+
repo, err := p.configmapRepository(ctx, providerLabelSelector(genericProvider), genericProvider.GetNamespace(), "")
596+
if err != nil {
597+
provider := client.ObjectKeyFromObject(genericProvider)
598+
return nil, wrapPhaseError(err, "failed to load the repository for provider "+provider.String(), operatorv1.ProviderUpgradedCondition)
599+
}
600+
601+
injectRepo = repo
602+
}
603+
604+
cl, err := repository.New(ctx, provider, configClient, append([]repository.Option{repository.InjectRepository(injectRepo)}, options...)...)
578605
if err != nil {
579606
return nil, err
580607
}

util/util.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
2828
configclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config"
2929
"sigs.k8s.io/cluster-api/cmd/clusterctl/client/repository"
30+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
3031
)
3132

3233
const (
@@ -36,6 +37,11 @@ const (
3637
gitlabPackagesAPIPrefix = "/api/v4/projects/"
3738
)
3839

40+
type genericProviderList interface {
41+
ctrlclient.ObjectList
42+
operatorv1.GenericProviderList
43+
}
44+
3945
func IsCoreProvider(p genericprovider.GenericProvider) bool {
4046
_, ok := p.(*operatorv1.CoreProvider)
4147
return ok
@@ -61,6 +67,40 @@ func ClusterctlProviderType(genericProvider operatorv1.GenericProvider) clusterc
6167
return clusterctlv1.ProviderTypeUnknown
6268
}
6369

70+
// GetGenericProvider returns the first of generic providers matching the type and the name from the configclient.Provider.
71+
func GetGenericProvider(ctx context.Context, cl ctrlclient.Client, provider configclient.Provider) (operatorv1.GenericProvider, error) {
72+
var list genericProviderList
73+
74+
switch provider.Type() {
75+
case clusterctlv1.CoreProviderType:
76+
list = &operatorv1.CoreProviderList{}
77+
case clusterctlv1.ControlPlaneProviderType:
78+
list = &operatorv1.ControlPlaneProviderList{}
79+
case clusterctlv1.InfrastructureProviderType:
80+
list = &operatorv1.InfrastructureProviderList{}
81+
case clusterctlv1.BootstrapProviderType:
82+
list = &operatorv1.BootstrapProviderList{}
83+
case clusterctlv1.AddonProviderType:
84+
list = &operatorv1.AddonProviderList{}
85+
case clusterctlv1.IPAMProviderType:
86+
list = &operatorv1.IPAMProviderList{}
87+
case clusterctlv1.RuntimeExtensionProviderType, clusterctlv1.ProviderTypeUnknown:
88+
return nil, fmt.Errorf("provider %s type is not supported %s", provider.Name(), provider.Type())
89+
}
90+
91+
if err := cl.List(ctx, list); err != nil {
92+
return nil, err
93+
}
94+
95+
for _, p := range list.GetItems() {
96+
if p.GetName() == provider.Name() {
97+
return p, nil
98+
}
99+
}
100+
101+
return nil, fmt.Errorf("unable to find provider manifest with name %s", provider.Name())
102+
}
103+
64104
// RepositoryFactory returns the repository implementation corresponding to the provider URL.
65105
// inspired by https://github.com/kubernetes-sigs/cluster-api/blob/124d9be7035e492f027cdc7a701b6b179451190a/cmd/clusterctl/client/repository/client.go#L170
66106
func RepositoryFactory(ctx context.Context, providerConfig configclient.Provider, configVariablesClient configclient.VariablesClient) (repository.Repository, error) {

0 commit comments

Comments
 (0)