From 6ed66e5c15d82e4f234e7ff6c519cb806ea60ee3 Mon Sep 17 00:00:00 2001 From: Mikhail Fedosin Date: Sat, 3 Jun 2023 21:01:58 +0200 Subject: [PATCH 1/5] chore: small clean-up --- .../controller/genericprovider/bootstrapprovider_wrapper.go | 5 ----- .../genericprovider/controlplaneprovider_wrapper.go | 5 ----- internal/controller/genericprovider/coreprovider_wrapper.go | 5 ----- .../genericprovider/infrastructureprovider_wrapper.go | 5 ----- internal/envtest/environment.go | 1 - 5 files changed, 21 deletions(-) diff --git a/internal/controller/genericprovider/bootstrapprovider_wrapper.go b/internal/controller/genericprovider/bootstrapprovider_wrapper.go index 541e4b1cc..429cda133 100644 --- a/internal/controller/genericprovider/bootstrapprovider_wrapper.go +++ b/internal/controller/genericprovider/bootstrapprovider_wrapper.go @@ -22,11 +22,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - BootstrapProviderKind = "BootstrapProvider" - BootstrapProviderListKind = "BootstrapProviderList" -) - type BootstrapProviderWrapper struct { *operatorv1.BootstrapProvider } diff --git a/internal/controller/genericprovider/controlplaneprovider_wrapper.go b/internal/controller/genericprovider/controlplaneprovider_wrapper.go index a61f2fc99..c82b2869e 100644 --- a/internal/controller/genericprovider/controlplaneprovider_wrapper.go +++ b/internal/controller/genericprovider/controlplaneprovider_wrapper.go @@ -22,11 +22,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - ControlPlaneProviderKind = "ControlPlaneProvider" - ControlPlaneProviderListKind = "ControlPlaneProviderList" -) - type ControlPlaneProviderWrapper struct { *operatorv1.ControlPlaneProvider } diff --git a/internal/controller/genericprovider/coreprovider_wrapper.go b/internal/controller/genericprovider/coreprovider_wrapper.go index 67db45509..0ed07edc0 100644 --- a/internal/controller/genericprovider/coreprovider_wrapper.go +++ b/internal/controller/genericprovider/coreprovider_wrapper.go @@ -22,11 +22,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - CoreProviderKind = "CoreProvider" - CoreProviderListKind = "CoreProviderList" -) - type CoreProviderWrapper struct { *operatorv1.CoreProvider } diff --git a/internal/controller/genericprovider/infrastructureprovider_wrapper.go b/internal/controller/genericprovider/infrastructureprovider_wrapper.go index 225f86092..bae1a1179 100644 --- a/internal/controller/genericprovider/infrastructureprovider_wrapper.go +++ b/internal/controller/genericprovider/infrastructureprovider_wrapper.go @@ -22,11 +22,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -const ( - InfrastructureProviderKind = "InfrastructureProvider" - InfrastructureProviderListKind = "InfrastructureProviderList" -) - type InfrastructureProviderWrapper struct { *operatorv1.InfrastructureProvider } diff --git a/internal/envtest/environment.go b/internal/envtest/environment.go index 27ac5c94c..22aea0741 100644 --- a/internal/envtest/environment.go +++ b/internal/envtest/environment.go @@ -67,7 +67,6 @@ func init() { utilruntime.Must(admissionv1.AddToScheme(scheme.Scheme)) utilruntime.Must(operatorv1.AddToScheme(scheme.Scheme)) utilruntime.Must(clusterctlv1.AddToScheme(scheme.Scheme)) - utilruntime.Must(admissionv1.AddToScheme(scheme.Scheme)) } var ( From 58d8253c689676e10ba6f11f7ba0675f3e25b6e8 Mon Sep 17 00:00:00 2001 From: Mikhail Fedosin Date: Sat, 3 Jun 2023 21:05:23 +0200 Subject: [PATCH 2/5] feat: allow providers to return their type --- .../controller/genericprovider/bootstrapprovider_wrapper.go | 4 ++++ .../genericprovider/controlplaneprovider_wrapper.go | 4 ++++ internal/controller/genericprovider/coreprovider_wrapper.go | 4 ++++ .../controller/genericprovider/genericprovider_interfaces.go | 1 + .../genericprovider/infrastructureprovider_wrapper.go | 4 ++++ 5 files changed, 17 insertions(+) diff --git a/internal/controller/genericprovider/bootstrapprovider_wrapper.go b/internal/controller/genericprovider/bootstrapprovider_wrapper.go index 429cda133..4b55b9546 100644 --- a/internal/controller/genericprovider/bootstrapprovider_wrapper.go +++ b/internal/controller/genericprovider/bootstrapprovider_wrapper.go @@ -54,6 +54,10 @@ func (b *BootstrapProviderWrapper) GetObject() client.Object { return b.BootstrapProvider } +func (b *BootstrapProviderWrapper) GetType() string { + return "bootstrap" +} + type BootstrapProviderListWrapper struct { *operatorv1.BootstrapProviderList } diff --git a/internal/controller/genericprovider/controlplaneprovider_wrapper.go b/internal/controller/genericprovider/controlplaneprovider_wrapper.go index c82b2869e..cab6e392e 100644 --- a/internal/controller/genericprovider/controlplaneprovider_wrapper.go +++ b/internal/controller/genericprovider/controlplaneprovider_wrapper.go @@ -54,6 +54,10 @@ func (c *ControlPlaneProviderWrapper) GetObject() client.Object { return c.ControlPlaneProvider } +func (c *ControlPlaneProviderWrapper) GetType() string { + return "controlplane" +} + type ControlPlaneProviderListWrapper struct { *operatorv1.ControlPlaneProviderList } diff --git a/internal/controller/genericprovider/coreprovider_wrapper.go b/internal/controller/genericprovider/coreprovider_wrapper.go index 0ed07edc0..d7b51f948 100644 --- a/internal/controller/genericprovider/coreprovider_wrapper.go +++ b/internal/controller/genericprovider/coreprovider_wrapper.go @@ -54,6 +54,10 @@ func (c *CoreProviderWrapper) GetObject() client.Object { return c.CoreProvider } +func (c *CoreProviderWrapper) GetType() string { + return "core" +} + type CoreProviderListWrapper struct { *operatorv1.CoreProviderList } diff --git a/internal/controller/genericprovider/genericprovider_interfaces.go b/internal/controller/genericprovider/genericprovider_interfaces.go index 1dedf4d2e..89f88355e 100644 --- a/internal/controller/genericprovider/genericprovider_interfaces.go +++ b/internal/controller/genericprovider/genericprovider_interfaces.go @@ -30,6 +30,7 @@ type GenericProvider interface { GetStatus() operatorv1.ProviderStatus SetStatus(in operatorv1.ProviderStatus) GetObject() client.Object + GetType() string } type GenericProviderList interface { diff --git a/internal/controller/genericprovider/infrastructureprovider_wrapper.go b/internal/controller/genericprovider/infrastructureprovider_wrapper.go index bae1a1179..7f71c01e3 100644 --- a/internal/controller/genericprovider/infrastructureprovider_wrapper.go +++ b/internal/controller/genericprovider/infrastructureprovider_wrapper.go @@ -54,6 +54,10 @@ func (i *InfrastructureProviderWrapper) GetObject() client.Object { return i.InfrastructureProvider } +func (i *InfrastructureProviderWrapper) GetType() string { + return "infrastructure" +} + type InfrastructureProviderListWrapper struct { *operatorv1.InfrastructureProviderList } From 5862a81d7bfcb80fcc0c8ac4717cc007a3cd32c6 Mon Sep 17 00:00:00 2001 From: Mikhail Fedosin Date: Sat, 3 Jun 2023 22:29:50 +0200 Subject: [PATCH 3/5] feat: add manifests downloader --- internal/controller/manifests_downloader.go | 181 ++++++++++++++++++++ internal/controller/phases.go | 4 +- 2 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 internal/controller/manifests_downloader.go diff --git a/internal/controller/manifests_downloader.go b/internal/controller/manifests_downloader.go new file mode 100644 index 000000000..f9868b0c0 --- /dev/null +++ b/internal/controller/manifests_downloader.go @@ -0,0 +1,181 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha1" + "sigs.k8s.io/cluster-api-operator/util" + configclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +const ( + configMapVersionLabel = "provider.cluster.x-k8s.io/version" + configMapTypeLabel = "provider.cluster.x-k8s.io/type" + configMapNameLabel = "provider.cluster.x-k8s.io/name" + operatorManagedLabel = "managed-by.operator.cluster.x-k8s.io" + + metadataConfigMapKey = "metadata" + componentsConfigMapKey = "components" +) + +// downloadManifests downloads CAPI manifests from a url. +func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Result, error) { + log := ctrl.LoggerFrom(ctx) + + log.Info("Downloading provider manifests") + + // Return immediately if a custom config map is used instead of a url. + if p.provider.GetSpec().FetchConfig != nil && p.provider.GetSpec().FetchConfig.Selector != nil { + log.V(5).Info("Custom config map is used, skip downloading provider manifests") + + return reconcile.Result{}, nil + } + + // Check if manifests are already downloaded and stored in a configmap + labelSelector := metav1.LabelSelector{ + MatchLabels: p.prepareConfigMapLabels(), + } + + exists, err := p.checkConfigMapExists(ctx, labelSelector) + if err != nil { + return reconcile.Result{}, wrapPhaseError(err, "failed to check that config map with manifests exists", operatorv1.PreflightCheckCondition) + } + + if exists { + log.V(5).Info("Config map with downloaded manifests already exists, skip downloading provider manifests") + + return reconcile.Result{}, nil + } + + // Load provider's secret and config url. + reader, err := p.secretReader(ctx) + if err != nil { + return reconcile.Result{}, wrapPhaseError(err, "failed to load the secret reader", operatorv1.PreflightCheckCondition) + } + + // Initialize a client for interacting with the clusterctl configuration. + p.configClient, err = configclient.New("", configclient.InjectReader(reader)) + if err != nil { + return reconcile.Result{}, err + } + + // Get returns the configuration for the provider with a given name/type. + // This is done using clusterctl internal API types. + p.providerConfig, err = p.configClient.Providers().Get(p.provider.GetName(), util.ClusterctlProviderType(p.provider)) + if err != nil { + return reconcile.Result{}, wrapPhaseError(err, operatorv1.UnknownProviderReason, operatorv1.PreflightCheckCondition) + } + + repo, err := repositoryFactory(p.providerConfig, p.configClient.Variables()) + if err != nil { + err = fmt.Errorf("failed to create repo from provider url for provider %q: %w", p.provider.GetName(), err) + + return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.PreflightCheckCondition) + } + + // Fetch the provider metadata and components yaml files from the provided repository GitHub/GitLab. + metadataFile, err := repo.GetFile(p.options.Version, metadataFile) + if err != nil { + err = fmt.Errorf("failed to read %q from the repository for provider %q: %w", metadataFile, p.provider.GetName(), err) + + return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.PreflightCheckCondition) + } + + componentsFile, err := p.repo.GetFile(p.options.Version, p.repo.ComponentsPath()) + if err != nil { + err = fmt.Errorf("failed to read %q from provider's repository %q: %w", p.repo.ComponentsPath(), p.providerConfig.ManifestLabel(), err) + + return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.PreflightCheckCondition) + } + + if err := p.createManifestsConfigMap(ctx, string(metadataFile), string(componentsFile)); err != nil { + err = fmt.Errorf("failed to create config map for provider %q: %w", p.provider.GetName(), err) + + return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.PreflightCheckCondition) + } + + return reconcile.Result{}, nil +} + +// checkConfigMapExists checks if a config map exists in Kubernetes with the given LabelSelector. +func (p *phaseReconciler) checkConfigMapExists(ctx context.Context, labelSelector metav1.LabelSelector) (bool, error) { + labelSet := labels.Set(labelSelector.MatchLabels) + listOpts := []client.ListOption{ + client.MatchingLabelsSelector{Selector: labels.SelectorFromSet(labelSet)}, + } + + var configMapList corev1.ConfigMapList + + if err := p.ctrlClient.List(ctx, &configMapList, listOpts...); err != nil { + return false, fmt.Errorf("failed to list ConfigMaps: %w", err) + } + + if len(configMapList.Items) > 1 { + return false, fmt.Errorf("more than one config maps were found for given selector: %v", labelSelector.String()) + } + + return len(configMapList.Items) == 1, nil +} + +// prepareConfigMapLabels returns labels that identify a config map with downloaded manifests. +func (p *phaseReconciler) prepareConfigMapLabels() map[string]string { + return map[string]string{ + configMapVersionLabel: p.provider.GetSpec().Version, + configMapTypeLabel: p.provider.GetType(), + configMapNameLabel: p.provider.GetName(), + operatorManagedLabel: "true", + } +} + +// createManifestsConfigMap creates a config map with downloaded manifests. +func (p *phaseReconciler) createManifestsConfigMap(ctx context.Context, metadata, components string) error { + configMapName := fmt.Sprintf("%s-%s-%s", p.provider.GetType(), p.provider.GetName(), p.provider.GetSpec().Version) + + configMap := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: configMapName, + Namespace: p.provider.GetNamespace(), + Labels: p.prepareConfigMapLabels(), + }, + Data: map[string]string{ + metadataConfigMapKey: metadata, + componentsConfigMapKey: components, + }, + } + + gvk := p.provider.GetObjectKind().GroupVersionKind() + + configMap.SetOwnerReferences([]metav1.OwnerReference{ + { + APIVersion: gvk.GroupVersion().String(), + Kind: gvk.Kind, + Name: p.provider.GetName(), + UID: p.provider.GetUID(), + }, + }) + + return p.ctrlClient.Create(ctx, configMap) +} diff --git a/internal/controller/phases.go b/internal/controller/phases.go index 8a8db5017..c76ef5fa0 100644 --- a/internal/controller/phases.go +++ b/internal/controller/phases.go @@ -237,14 +237,14 @@ func (p *phaseReconciler) configmapRepository(ctx context.Context) (repository.R return nil, fmt.Errorf("ConfigMap %s/%s has invalid version:%s (%s)", cm.Namespace, cm.Name, version, errMsg) } - metadata, ok := cm.Data["metadata"] + metadata, ok := cm.Data[metadataConfigMapKey] if !ok { return nil, fmt.Errorf("ConfigMap %s/%s has no metadata", cm.Namespace, cm.Name) } mr.WithFile(version, metadataFile, []byte(metadata)) - components, ok := cm.Data["components"] + components, ok := cm.Data[componentsConfigMapKey] if !ok { return nil, fmt.Errorf("ConfigMap %s/%s has no components", cm.Namespace, cm.Name) } From ada3faa259afb23fadf315db30300de090566b7c Mon Sep 17 00:00:00 2001 From: Mikhail Fedosin Date: Sat, 3 Jun 2023 23:06:04 +0200 Subject: [PATCH 4/5] chore: add a phase to initialize phase reconciler --- .../controller/genericprovider_controller.go | 2 + internal/controller/manifests_downloader.go | 25 +--------- internal/controller/phases.go | 48 ++++++++++++------- internal/controller/phases_test.go | 2 +- 4 files changed, 37 insertions(+), 40 deletions(-) diff --git a/internal/controller/genericprovider_controller.go b/internal/controller/genericprovider_controller.go index f66237538..03a2d04d4 100644 --- a/internal/controller/genericprovider_controller.go +++ b/internal/controller/genericprovider_controller.go @@ -127,6 +127,8 @@ func (r *GenericProviderReconciler) reconcile(ctx context.Context, provider gene reconciler := newPhaseReconciler(*r, provider, genericProviderList) phases := []reconcilePhaseFn{ reconciler.preflightChecks, + reconciler.initializePhaseReconciler, + reconciler.downloadManifests, reconciler.load, reconciler.fetch, reconciler.preInstall, diff --git a/internal/controller/manifests_downloader.go b/internal/controller/manifests_downloader.go index f9868b0c0..373c328cb 100644 --- a/internal/controller/manifests_downloader.go +++ b/internal/controller/manifests_downloader.go @@ -24,8 +24,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha1" - "sigs.k8s.io/cluster-api-operator/util" - configclient "sigs.k8s.io/cluster-api/cmd/clusterctl/client/config" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -70,25 +68,6 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu return reconcile.Result{}, nil } - // Load provider's secret and config url. - reader, err := p.secretReader(ctx) - if err != nil { - return reconcile.Result{}, wrapPhaseError(err, "failed to load the secret reader", operatorv1.PreflightCheckCondition) - } - - // Initialize a client for interacting with the clusterctl configuration. - p.configClient, err = configclient.New("", configclient.InjectReader(reader)) - if err != nil { - return reconcile.Result{}, err - } - - // Get returns the configuration for the provider with a given name/type. - // This is done using clusterctl internal API types. - p.providerConfig, err = p.configClient.Providers().Get(p.provider.GetName(), util.ClusterctlProviderType(p.provider)) - if err != nil { - return reconcile.Result{}, wrapPhaseError(err, operatorv1.UnknownProviderReason, operatorv1.PreflightCheckCondition) - } - repo, err := repositoryFactory(p.providerConfig, p.configClient.Variables()) if err != nil { err = fmt.Errorf("failed to create repo from provider url for provider %q: %w", p.provider.GetName(), err) @@ -104,9 +83,9 @@ func (p *phaseReconciler) downloadManifests(ctx context.Context) (reconcile.Resu return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.PreflightCheckCondition) } - componentsFile, err := p.repo.GetFile(p.options.Version, p.repo.ComponentsPath()) + componentsFile, err := repo.GetFile(p.options.Version, repo.ComponentsPath()) if err != nil { - err = fmt.Errorf("failed to read %q from provider's repository %q: %w", p.repo.ComponentsPath(), p.providerConfig.ManifestLabel(), err) + err = fmt.Errorf("failed to read %q from the repository for provider %q: %w", componentsFile, p.provider.GetName(), err) return reconcile.Result{}, wrapPhaseError(err, operatorv1.ComponentsFetchErrorReason, operatorv1.PreflightCheckCondition) } diff --git a/internal/controller/phases.go b/internal/controller/phases.go index c76ef5fa0..88933b733 100644 --- a/internal/controller/phases.go +++ b/internal/controller/phases.go @@ -109,12 +109,8 @@ func (p *phaseReconciler) preflightChecks(ctx context.Context) (reconcile.Result return preflightChecks(ctx, p.ctrlClient, p.provider, p.providerList) } -// load provider specific configuration into phaseReconciler object. -func (p *phaseReconciler) load(ctx context.Context) (reconcile.Result, error) { - log := ctrl.LoggerFrom(ctx) - - log.Info("Loading provider") - +// initializePhaseReconciler initializes phase reconciler. +func (p *phaseReconciler) initializePhaseReconciler(ctx context.Context) (reconcile.Result, error) { // Load provider's secret and config url. reader, err := p.secretReader(ctx) if err != nil { @@ -136,16 +132,36 @@ func (p *phaseReconciler) load(ctx context.Context) (reconcile.Result, error) { spec := p.provider.GetSpec() - // If a configmap selector was specified, use it to find the configmap with provider configuration. This is - // a case for "air-gapped" environments. If no selector was specified, use GitHub/Gitlab repository. - if spec.FetchConfig != nil && spec.FetchConfig.Selector != nil { - log.V(5).Info("Custom ConfigMap was provided for fetching manifests") + // Store some provider specific inputs for passing it to clusterctl library + p.options = repository.ComponentsOptions{ + TargetNamespace: p.provider.GetNamespace(), + SkipTemplateProcess: false, + Version: spec.Version, + } - p.repo, err = p.configmapRepository(ctx) - } else { - p.repo, err = repositoryFactory(p.providerConfig, p.configClient.Variables()) + return reconcile.Result{}, nil +} + +// load provider specific configuration into phaseReconciler object. +func (p *phaseReconciler) load(ctx context.Context) (reconcile.Result, error) { + log := ctrl.LoggerFrom(ctx) + + log.Info("Loading provider") + + var err error + + spec := p.provider.GetSpec() + + labelSelector := &metav1.LabelSelector{ + MatchLabels: p.prepareConfigMapLabels(), + } + + // Replace label selector if user wants to use custom config map + if p.provider.GetSpec().FetchConfig != nil && p.provider.GetSpec().FetchConfig.Selector != nil { + labelSelector = p.provider.GetSpec().FetchConfig.Selector } + p.repo, err = p.configmapRepository(ctx, labelSelector) if err != nil { return reconcile.Result{}, wrapPhaseError(err, "failed to load the repository", operatorv1.PreflightCheckCondition) } @@ -202,13 +218,13 @@ func (p *phaseReconciler) secretReader(ctx context.Context) (configclient.Reader // configmapRepository use clusterctl NewMemoryRepository structure to store the manifests // and metadata from a given configmap. -func (p *phaseReconciler) configmapRepository(ctx context.Context) (repository.Repository, error) { +func (p *phaseReconciler) configmapRepository(ctx context.Context, labelSelector *metav1.LabelSelector) (repository.Repository, error) { mr := repository.NewMemoryRepository() mr.WithPaths("", "components.yaml") cml := &corev1.ConfigMapList{} - selector, err := metav1.LabelSelectorAsSelector(p.provider.GetSpec().FetchConfig.Selector) + selector, err := metav1.LabelSelectorAsSelector(labelSelector) if err != nil { return nil, err } @@ -218,7 +234,7 @@ func (p *phaseReconciler) configmapRepository(ctx context.Context) (repository.R } if len(cml.Items) == 0 { - return nil, fmt.Errorf("no ConfigMaps found with selector %s", p.provider.GetSpec().FetchConfig.Selector.String()) + return nil, fmt.Errorf("no ConfigMaps found with selector %s", labelSelector.String()) } for _, cm := range cml.Items { diff --git a/internal/controller/phases_test.go b/internal/controller/phases_test.go index 17bd86b4a..3e5c258b0 100644 --- a/internal/controller/phases_test.go +++ b/internal/controller/phases_test.go @@ -349,7 +349,7 @@ metadata: g.Expect(fakeclient.Create(ctx, &tt.configMaps[i])).To(Succeed()) } - got, err := p.configmapRepository(context.TODO()) + got, err := p.configmapRepository(context.TODO(), p.provider.GetSpec().FetchConfig.Selector) if len(tt.wantErr) > 0 { g.Expect(err).Should(MatchError(tt.wantErr)) return From 67bed926ebd86ee03c654cbe389949f2ca18c8da Mon Sep 17 00:00:00 2001 From: Mikhail Fedosin Date: Sun, 4 Jun 2023 22:35:51 +0200 Subject: [PATCH 5/5] test: add manifests downloader tests --- .../controller/manifests_downloader_test.go | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 internal/controller/manifests_downloader_test.go diff --git a/internal/controller/manifests_downloader_test.go b/internal/controller/manifests_downloader_test.go new file mode 100644 index 000000000..7fb49741d --- /dev/null +++ b/internal/controller/manifests_downloader_test.go @@ -0,0 +1,72 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "testing" + + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha1" + "sigs.k8s.io/cluster-api-operator/internal/controller/genericprovider" +) + +func TestManifestsDownloader(t *testing.T) { + g := NewWithT(t) + + ctx := context.Background() + + fakeclient := fake.NewClientBuilder().WithObjects().Build() + + namespace := "test-namespace" + + p := &phaseReconciler{ + ctrlClient: fakeclient, + provider: &genericprovider.CoreProviderWrapper{ + CoreProvider: &operatorv1.CoreProvider{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster-api", + Namespace: namespace, + }, + Spec: operatorv1.CoreProviderSpec{ + ProviderSpec: operatorv1.ProviderSpec{ + Version: "v1.4.2", + }, + }, + }, + }, + } + + _, err := p.initializePhaseReconciler(ctx) + g.Expect(err).ToNot(HaveOccurred()) + + _, err = p.downloadManifests(ctx) + g.Expect(err).ToNot(HaveOccurred()) + + // Ensure that config map was created + labelSelector := metav1.LabelSelector{ + MatchLabels: p.prepareConfigMapLabels(), + } + + exists, err := p.checkConfigMapExists(ctx, labelSelector) + g.Expect(err).ToNot(HaveOccurred()) + + g.Expect(exists).To(BeTrue()) +}