diff --git a/cmd/main.go b/cmd/main.go index 6bdb8eac6..1ef70f7de 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -40,8 +40,10 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/config" "sigs.k8s.io/controller-runtime/pkg/controller" "sigs.k8s.io/controller-runtime/pkg/healthz" + ctrlwebhook "sigs.k8s.io/controller-runtime/pkg/webhook" operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha2" @@ -63,6 +65,7 @@ var ( profilerAddress string enableContentionProfiling bool concurrencyNumber int + managerConcurrency int syncPeriod time.Duration webhookPort int webhookCertDir string @@ -113,6 +116,9 @@ func InitFlags(fs *pflag.FlagSet) { fs.IntVar(&concurrencyNumber, "concurrency", 1, "Number of core resources to process simultaneously") + fs.IntVar(&managerConcurrency, "manager-concurrency", 10, + "Number of concurrent reconciles to process simultaneously across all controllers") + fs.DurationVar(&syncPeriod, "sync-period", 10*time.Minute, "The minimum interval at which watched resources are reconciled (e.g. 15m)") @@ -174,6 +180,9 @@ func main() { }, }, }, + Controller: config.Controller{ + MaxConcurrentReconciles: managerConcurrency, + }, WebhookServer: ctrlwebhook.NewServer( ctrlwebhook.Options{ Port: webhookPort, diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 3cf3184ef..d661b1622 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -34,7 +34,7 @@ spec: resources: limits: cpu: 100m - memory: 150Mi + memory: 300Mi requests: cpu: 100m memory: 100Mi diff --git a/hack/charts/cluster-api-operator/values.yaml b/hack/charts/cluster-api-operator/values.yaml index 32f5f4390..123175ec7 100644 --- a/hack/charts/cluster-api-operator/values.yaml +++ b/hack/charts/cluster-api-operator/values.yaml @@ -54,7 +54,7 @@ resources: manager: limits: cpu: 100m - memory: 150Mi + memory: 300Mi requests: cpu: 100m memory: 100Mi diff --git a/internal/controller/phases.go b/internal/controller/phases.go index d7ab7d913..614a3b7c0 100644 --- a/internal/controller/phases.go +++ b/internal/controller/phases.go @@ -108,6 +108,35 @@ func newPhaseReconciler(r GenericProviderReconciler, provider genericprovider.Ge } } +type ConfigMapRepositorySettings struct { + repository.Repository + additionalManifests string + skipComponents bool + namespace string +} + +type ConfigMapRepositoryOption interface { + ApplyToConfigMapRepository(*ConfigMapRepositorySettings) +} + +type WithAdditionalManifests string + +func (w WithAdditionalManifests) ApplyToConfigMapRepository(settings *ConfigMapRepositorySettings) { + settings.additionalManifests = string(w) +} + +type SkipComponents struct{} + +func (s SkipComponents) ApplyToConfigMapRepository(settings *ConfigMapRepositorySettings) { + settings.skipComponents = true +} + +type InNamespace string + +func (i InNamespace) ApplyToConfigMapRepository(settings *ConfigMapRepositorySettings) { + settings.namespace = string(i) +} + // preflightChecks a wrapper around the preflight checks. func (p *phaseReconciler) preflightChecks(ctx context.Context) (reconcile.Result, error) { return reconcile.Result{}, preflightChecks(ctx, p.ctrlClient, p.provider, p.providerList) @@ -199,7 +228,7 @@ func (p *phaseReconciler) load(ctx context.Context) (reconcile.Result, error) { return reconcile.Result{}, wrapPhaseError(err, "failed to load additional manifests", operatorv1.ProviderInstalledCondition) } - p.repo, err = p.configmapRepository(ctx, labelSelector, p.provider.GetNamespace(), additionalManifests) + p.repo, err = p.configmapRepository(ctx, labelSelector, InNamespace(p.provider.GetNamespace()), WithAdditionalManifests(additionalManifests)) if err != nil { return reconcile.Result{}, wrapPhaseError(err, "failed to load the repository", operatorv1.ProviderInstalledCondition) } @@ -299,10 +328,18 @@ func (p *phaseReconciler) secretReader(ctx context.Context, providers ...configc // configmapRepository use clusterctl NewMemoryRepository structure to store the manifests // and metadata from a given configmap. -func (p *phaseReconciler) configmapRepository(ctx context.Context, labelSelector *metav1.LabelSelector, namespace, additionalManifests string) (repository.Repository, error) { +func (p *phaseReconciler) configmapRepository(ctx context.Context, labelSelector *metav1.LabelSelector, options ...ConfigMapRepositoryOption) (repository.Repository, error) { mr := repository.NewMemoryRepository() mr.WithPaths("", "components.yaml") + settings := &ConfigMapRepositorySettings{ + Repository: mr, + } + + for _, option := range options { + option.ApplyToConfigMapRepository(settings) + } + cml := &corev1.ConfigMapList{} selector, err := metav1.LabelSelectorAsSelector(labelSelector) @@ -310,7 +347,7 @@ func (p *phaseReconciler) configmapRepository(ctx context.Context, labelSelector return nil, err } - if err = p.ctrlClient.List(ctx, cml, &client.ListOptions{LabelSelector: selector, Namespace: namespace}); err != nil { + if err = p.ctrlClient.List(ctx, cml, &client.ListOptions{LabelSelector: selector, Namespace: settings.namespace}); err != nil { return nil, err } @@ -341,13 +378,22 @@ func (p *phaseReconciler) configmapRepository(ctx context.Context, labelSelector mr.WithFile(version, metadataFile, []byte(metadata)) + // Exclude components from the repository if only metadata is needed. + // Used for provider upgrades, when compatibility with other providers is + // established based on the metadata only. + if settings.skipComponents { + mr.WithFile(version, mr.ComponentsPath(), []byte{}) + + continue + } + components, err := getComponentsData(cm) if err != nil { return nil, err } - if additionalManifests != "" { - components = components + "\n---\n" + additionalManifests + if settings.additionalManifests != "" { + components = components + "\n---\n" + settings.additionalManifests } mr.WithFile(version, mr.ComponentsPath(), []byte(components)) @@ -649,7 +695,7 @@ func (p *phaseReconciler) repositoryProxy(ctx context.Context, provider configcl 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) } - repo, err := p.configmapRepository(ctx, providerLabelSelector(genericProvider), genericProvider.GetNamespace(), "") + repo, err := p.configmapRepository(ctx, providerLabelSelector(genericProvider), InNamespace(genericProvider.GetNamespace()), SkipComponents{}) if err != nil { provider := client.ObjectKeyFromObject(genericProvider) return nil, wrapPhaseError(err, "failed to load the repository for provider "+provider.String(), operatorv1.ProviderUpgradedCondition) diff --git a/internal/controller/phases_test.go b/internal/controller/phases_test.go index e06cfe9b2..ebd5462e3 100644 --- a/internal/controller/phases_test.go +++ b/internal/controller/phases_test.go @@ -389,7 +389,7 @@ metadata: g.Expect(fakeclient.Create(ctx, &tt.configMaps[i])).To(Succeed()) } - got, err := p.configmapRepository(context.TODO(), p.provider.GetSpec().FetchConfig.Selector, "ns1", tt.additionalManifests) + got, err := p.configmapRepository(context.TODO(), p.provider.GetSpec().FetchConfig.Selector, InNamespace("ns1"), WithAdditionalManifests(tt.additionalManifests)) if len(tt.wantErr) > 0 { g.Expect(err).Should(MatchError(tt.wantErr)) @@ -596,7 +596,7 @@ releaseSeries: MatchLabels: map[string]string{ operatorManagedLabel: "true", }, - }, "default", "") + }, InNamespace("default")) g.Expect(err).To(Succeed()) } diff --git a/test/e2e/resources/full-chart-install.yaml b/test/e2e/resources/full-chart-install.yaml index 0b789ded4..9e60afc64 100644 --- a/test/e2e/resources/full-chart-install.yaml +++ b/test/e2e/resources/full-chart-install.yaml @@ -22096,7 +22096,7 @@ spec: resources: limits: cpu: 100m - memory: 150Mi + memory: 300Mi requests: cpu: 100m memory: 100Mi