From dc96bcbad855bedf66cf38837c42ca9e7dc47d9f Mon Sep 17 00:00:00 2001 From: Rafael Franzke Date: Thu, 22 Jun 2023 09:15:22 +0200 Subject: [PATCH] [GEP-22] Drop `shootstate-extensions` and `shootstate-secret` controllers (#8136) * Drop legacy `ShootState` controllers * Remove legacy ShootState controller finalizer from secrets * Drop {Get,Set}ShootState methods on `Botanist` * Drop `EnsureShootStateExists` function Instead, we now fetch the `ShootState` object in the restore phase only (this is the only phase when we need it) and store it directly on the internal `Shoot` object. * Delete `ShootState` after restoration Only if the gardenlet is responsible for an unmanaged `Seed` - otherwise, its `shoot-state` reconciler wants to perform regular backups of the `ShootState`, so we shouldn't delete it even after successful restoration. * Disable `shoot-state` reconciler locally From https://github.com/gardener/gardener/pull/8112#discussion_r1236417469: > In our e2e tests the cpm test shoots are always scheduled on a non-managed seed. With this, the controller would always be active and we wouldn't verify that migration also works without periodic backups. Since we don't need this periodic backup functionality in e2e tests, we simply disable the controller. --- .../gardener/gardenlet/templates/_helpers.tpl | 6 - charts/gardener/gardenlet/values.yaml | 4 - cmd/gardenlet/app/app.go | 33 ++ docs/concepts/gardenlet.md | 19 +- example/20-componentconfig-gardenlet.yaml | 4 - example/gardener-local/gardenlet/values.yaml | 2 + pkg/apis/core/v1beta1/helper/helper.go | 5 + pkg/apis/core/v1beta1/helper/helper_test.go | 9 + pkg/gardenlet/apis/config/types.go | 17 - .../apis/config/v1alpha1/defaults.go | 23 - .../apis/config/v1alpha1/defaults_test.go | 15 - pkg/gardenlet/apis/config/v1alpha1/types.go | 21 - .../v1alpha1/zz_generated.conversion.go | 64 --- .../config/v1alpha1/zz_generated.deepcopy.go | 52 --- .../config/v1alpha1/zz_generated.defaults.go | 6 - .../apis/config/zz_generated.deepcopy.go | 52 --- pkg/gardenlet/controller/add.go | 5 - .../managedseed/charttest/charttest.go | 6 - .../charttest/gardenlet_chart_test.go | 38 +- pkg/gardenlet/controller/shoot/add.go | 34 +- .../controller/shoot/shoot/reconciler.go | 19 +- .../shoot/shoot/reconciler_delete.go | 8 +- .../shoot/shoot/reconciler_migrate.go | 9 +- .../shoot/shoot/reconciler_reconcile.go | 19 +- pkg/gardenlet/controller/shootstate/add.go | 64 --- .../controller/shootstate/extensions/add.go | 110 ----- .../shootstate/extensions/add_test.go | 172 ------- .../extensions/extensions_suite_test.go | 27 -- .../shootstate/extensions/reconciler.go | 215 --------- .../shootstate/extensions/reconciler_test.go | 306 ------------- .../controller/shootstate/secret/add.go | 60 --- .../controller/shootstate/secret/add_test.go | 66 --- .../shootstate/secret/reconciler.go | 175 ------- .../shootstate/secret/secret_suite_test.go | 27 -- pkg/operation/botanist/backupentry.go | 6 +- pkg/operation/botanist/containerruntime.go | 4 +- .../botanist/containerruntime_test.go | 2 +- pkg/operation/botanist/controlplane.go | 6 +- pkg/operation/botanist/controlplane_test.go | 4 +- pkg/operation/botanist/dnsrecord.go | 8 +- pkg/operation/botanist/dnsrecord_test.go | 4 +- pkg/operation/botanist/etcd.go | 2 +- pkg/operation/botanist/extension.go | 8 +- pkg/operation/botanist/extension_test.go | 2 +- pkg/operation/botanist/infrastructure.go | 6 +- pkg/operation/botanist/infrastructure_test.go | 2 +- pkg/operation/botanist/kubeapiserver_test.go | 2 +- pkg/operation/botanist/migration.go | 11 +- pkg/operation/botanist/migration_test.go | 46 +- pkg/operation/botanist/network.go | 4 +- pkg/operation/botanist/network_test.go | 2 +- pkg/operation/botanist/nginxingress.go | 2 +- pkg/operation/botanist/nginxingress_test.go | 2 +- .../botanist/operatingsystemconfig.go | 4 +- .../botanist/operatingsystemconfig_test.go | 2 +- pkg/operation/botanist/secrets.go | 4 +- pkg/operation/botanist/secrets_test.go | 4 +- pkg/operation/botanist/shootstate.go | 65 --- pkg/operation/botanist/shootstate_test.go | 99 ---- pkg/operation/botanist/worker.go | 4 +- pkg/operation/botanist/worker_test.go | 2 +- pkg/operation/shoot/shoot.go | 11 + skaffold.yaml | 3 - .../extensions/extensions_suite_test.go | 143 ------ .../shootstate/extensions/extensions_test.go | 370 --------------- .../shootstate/secret/secret_suite_test.go | 162 ------- .../shootstate/secret/secret_test.go | 428 ------------------ 67 files changed, 205 insertions(+), 2911 deletions(-) delete mode 100644 pkg/gardenlet/controller/shootstate/add.go delete mode 100644 pkg/gardenlet/controller/shootstate/extensions/add.go delete mode 100644 pkg/gardenlet/controller/shootstate/extensions/add_test.go delete mode 100644 pkg/gardenlet/controller/shootstate/extensions/extensions_suite_test.go delete mode 100644 pkg/gardenlet/controller/shootstate/extensions/reconciler.go delete mode 100644 pkg/gardenlet/controller/shootstate/extensions/reconciler_test.go delete mode 100644 pkg/gardenlet/controller/shootstate/secret/add.go delete mode 100644 pkg/gardenlet/controller/shootstate/secret/add_test.go delete mode 100644 pkg/gardenlet/controller/shootstate/secret/reconciler.go delete mode 100644 pkg/gardenlet/controller/shootstate/secret/secret_suite_test.go delete mode 100644 pkg/operation/botanist/shootstate.go delete mode 100644 pkg/operation/botanist/shootstate_test.go delete mode 100644 test/integration/gardenlet/shootstate/extensions/extensions_suite_test.go delete mode 100644 test/integration/gardenlet/shootstate/extensions/extensions_test.go delete mode 100644 test/integration/gardenlet/shootstate/secret/secret_suite_test.go delete mode 100644 test/integration/gardenlet/shootstate/secret/secret_test.go diff --git a/charts/gardener/gardenlet/templates/_helpers.tpl b/charts/gardener/gardenlet/templates/_helpers.tpl index 7bec93c5c63..1aeca9d978d 100644 --- a/charts/gardener/gardenlet/templates/_helpers.tpl +++ b/charts/gardener/gardenlet/templates/_helpers.tpl @@ -226,12 +226,6 @@ config.yaml: | concurrentSyncs: {{ required ".Values.config.controllers.shootState.concurrentSyncs is required" .Values.config.controllers.shootState.concurrentSyncs }} syncPeriod: {{ required ".Values.config.controllers.shootState.syncPeriod is required" .Values.config.controllers.shootState.syncPeriod }} {{- end }} - {{- if .Values.config.controllers.shootSecret }} - shootSecret: - concurrentSyncs: {{ required ".Values.config.controllers.shootSecret.concurrentSyncs is required" .Values.config.controllers.shootSecret.concurrentSyncs }} - {{- end }} - shootStateSync: - concurrentSyncs: {{ required ".Values.config.controllers.shootStateSync.concurrentSyncs is required" .Values.config.controllers.shootStateSync.concurrentSyncs }} {{- if .Values.config.controllers.managedSeed }} managedSeed: concurrentSyncs: {{ required ".Values.config.controllers.managedSeed.concurrentSyncs is required" .Values.config.controllers.managedSeed.concurrentSyncs }} diff --git a/charts/gardener/gardenlet/values.yaml b/charts/gardener/gardenlet/values.yaml index da8f7f312a8..0e0c91dd589 100644 --- a/charts/gardener/gardenlet/values.yaml +++ b/charts/gardener/gardenlet/values.yaml @@ -116,10 +116,6 @@ config: shootState: concurrentSyncs: 5 syncPeriod: 6h - shootSecret: - concurrentSyncs: 5 - shootStateSync: - concurrentSyncs: 5 managedSeed: concurrentSyncs: 5 syncPeriod: 1h diff --git a/cmd/gardenlet/app/app.go b/cmd/gardenlet/app/app.go index c5f6565f9f3..06ffefd43fa 100644 --- a/cmd/gardenlet/app/app.go +++ b/cmd/gardenlet/app/app.go @@ -71,6 +71,7 @@ import ( "github.com/gardener/gardener/pkg/utils/flow" gardenerutils "github.com/gardener/gardener/pkg/utils/gardener" kubernetesutils "github.com/gardener/gardener/pkg/utils/kubernetes" + secretsmanager "github.com/gardener/gardener/pkg/utils/secrets/manager" ) // Name is a const for the name of this component. @@ -350,6 +351,12 @@ func (g *garden) Start(ctx context.Context) error { return err } + // TODO(rfranzke): Remove this code after v1.74 has been released. + log.Info("Removing legacy ShootState controller finalizer from persistable secrets in seed cluster") + if err := removeLegacyShootStateControllerFinalizerFromSecrets(ctx, g.mgr.GetClient()); err != nil { + return err + } + log.Info("Setting up shoot client map") shootClientMap, err := clientmapbuilder. NewShootClientMapBuilder(). @@ -475,6 +482,32 @@ func (g *garden) updateProcessingShootStatusToAborted(ctx context.Context, garde return flow.Parallel(taskFns...)(ctx) } +func removeLegacyShootStateControllerFinalizerFromSecrets(ctx context.Context, seedClient client.Client) error { + secretList := &metav1.PartialObjectMetadataList{} + secretList.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("SecretList")) + if err := seedClient.List(ctx, secretList, client.MatchingLabels{ + secretsmanager.LabelKeyManagedBy: secretsmanager.LabelValueSecretsManager, + secretsmanager.LabelKeyPersist: secretsmanager.LabelValueTrue, + }); err != nil { + return fmt.Errorf("failed listing all secrets that must be persisted: %w", err) + } + + var taskFns []flow.TaskFn + + for _, s := range secretList.Items { + secret := s + + taskFns = append(taskFns, func(ctx context.Context) error { + if err := controllerutils.RemoveFinalizers(ctx, seedClient, &secret, "gardenlet.gardener.cloud/secret-controller"); err != nil { + return fmt.Errorf("failed to remove legacy ShootState controller finalizer from secret %q: %w", client.ObjectKeyFromObject(&secret), err) + } + return nil + }) + } + + return flow.Parallel(taskFns...)(ctx) +} + func addAllFieldIndexes(ctx context.Context, i client.FieldIndexer) error { for _, fn := range []func(context.Context, client.FieldIndexer) error{ // core API group diff --git a/docs/concepts/gardenlet.md b/docs/concepts/gardenlet.md index eacaf13b0ce..52570ff406b 100644 --- a/docs/concepts/gardenlet.md +++ b/docs/concepts/gardenlet.md @@ -444,24 +444,9 @@ A pod is considered stale when: This reconciler periodically (default: every `6h`) performs backups of the state of `Shoot` clusters and persists them into `ShootState` resources into the same namespace as the `Shoot`s in the garden cluster. It is only started in case the `gardenlet` is responsible for an unmanaged `Seed`, i.e. a `Seed` which is not backed by a `seedmanagement.gardener.cloud/v1alpha1.ManagedSeed` object. -Please refer to [GEP-22: Improved Usage of the `ShootState` API](../proposals/22-improved-usage-of-shootstate-api.md) for all information. - -### [`ShootState` Controller](../../pkg/gardenlet/controller/shootstate) - -The `ShootState` controller in the `gardenlet` reconciles resources containing information that has to be synced to the `ShootState`. -This information is used when a [control plane migration](../usage/control_plane_migration.md) is performed. - -#### "Extensions" Reconciler +Alternatively, it can be disabled by setting the `concurrentSyncs=0` for the controller in the `gardenlet`'s component configuration. -This reconciler watches resources in the `extensions.gardener.cloud` API group in the seed cluster which contain a `Shoot`-specific state or data. -Those are `BackupEntry`s, `ContainerRuntime`s, `ControlPlane`s, `DNSRecord`s, `Extension`s, `Infrastructure`s, `Network`s, `OperatingSystemConfig`s, and `Worker`s. - -When there is a change in the `.status.state` or `.status.resources[]` fields of these resources, then this information is synced into the `ShootState` resource in the garden cluster. - -#### "Secret" Reconciler - -This reconciler reconciles `Secret`s having labels `managed-by=secrets-manager` and `persist=true` in the shoot namespaces in the seed cluster. -It syncs them to the `ShootState` so that the secrets can be restored from there in case a shoot control plane has to be restored to another seed cluster (in case of migration). +Please refer to [GEP-22: Improved Usage of the `ShootState` API](../proposals/22-improved-usage-of-shootstate-api.md) for all information. ## Managed Seeds diff --git a/example/20-componentconfig-gardenlet.yaml b/example/20-componentconfig-gardenlet.yaml index 10a22c9e64a..53bd3dbbc6c 100644 --- a/example/20-componentconfig-gardenlet.yaml +++ b/example/20-componentconfig-gardenlet.yaml @@ -71,10 +71,6 @@ controllers: shootState: concurrentSyncs: 5 syncPeriod: 6h - shootSecret: - concurrentSyncs: 5 - shootStateSync: - concurrentSyncs: 1 seed: syncPeriod: 1h # leaseResyncSeconds: 2 diff --git a/example/gardener-local/gardenlet/values.yaml b/example/gardener-local/gardenlet/values.yaml index b5ba0b55a39..5a5de213be7 100644 --- a/example/gardener-local/gardenlet/values.yaml +++ b/example/gardener-local/gardenlet/values.yaml @@ -32,6 +32,8 @@ config: controllers: shoot: reconcileInMaintenanceOnly: true + shootState: + concurrentSyncs: 0 # we don't need the shootstate controller locally, and enabling it would even distort the results of CPM e2e tests featureGates: HVPA: true HVPAForShootedSeed: true diff --git a/pkg/apis/core/v1beta1/helper/helper.go b/pkg/apis/core/v1beta1/helper/helper.go index eea05051883..69bc3033490 100644 --- a/pkg/apis/core/v1beta1/helper/helper.go +++ b/pkg/apis/core/v1beta1/helper/helper.go @@ -1403,3 +1403,8 @@ func GetFailureToleranceType(shoot *gardencorev1beta1.Shoot) *gardencorev1beta1. func IsTopologyAwareRoutingForShootControlPlaneEnabled(seed *gardencorev1beta1.Seed, shoot *gardencorev1beta1.Shoot) bool { return SeedSettingTopologyAwareRoutingEnabled(seed.Spec.Settings) && IsMultiZonalShootControlPlane(shoot) } + +// ShootHasOperationType returns true when the 'type' in the last operation matches the provided type. +func ShootHasOperationType(lastOperation *gardencorev1beta1.LastOperation, lastOperationType gardencorev1beta1.LastOperationType) bool { + return lastOperation != nil && lastOperation.Type == lastOperationType +} diff --git a/pkg/apis/core/v1beta1/helper/helper_test.go b/pkg/apis/core/v1beta1/helper/helper_test.go index ffd2097c1fa..25259efb6a7 100644 --- a/pkg/apis/core/v1beta1/helper/helper_test.go +++ b/pkg/apis/core/v1beta1/helper/helper_test.go @@ -2678,6 +2678,15 @@ var _ = Describe("helper", func() { BeTrue(), ), ) + + DescribeTable("#ShootHasOperationType", + func(lastOperation *gardencorev1beta1.LastOperation, lastOperationType gardencorev1beta1.LastOperationType, matcher gomegatypes.GomegaMatcher) { + Expect(ShootHasOperationType(lastOperation, lastOperationType)).To(matcher) + }, + Entry("last operation nil", nil, gardencorev1beta1.LastOperationTypeCreate, BeFalse()), + Entry("last operation type does not match", &gardencorev1beta1.LastOperation{}, gardencorev1beta1.LastOperationTypeCreate, BeFalse()), + Entry("last operation type matches", &gardencorev1beta1.LastOperation{Type: gardencorev1beta1.LastOperationTypeCreate}, gardencorev1beta1.LastOperationTypeCreate, BeTrue()), + ) }) func timePointer(t time.Time) *metav1.Time { diff --git a/pkg/gardenlet/apis/config/types.go b/pkg/gardenlet/apis/config/types.go index 5001c80ebf2..683dbccdabd 100644 --- a/pkg/gardenlet/apis/config/types.go +++ b/pkg/gardenlet/apis/config/types.go @@ -153,14 +153,10 @@ type GardenletControllerConfiguration struct { ShootCare *ShootCareControllerConfiguration // ShootState defines the configuration of the ShootState controller. ShootState *ShootStateControllerConfiguration - // ShootStateSync defines the configuration of the ShootState controller. - ShootStateSync *ShootStateSyncControllerConfiguration // NetworkPolicy defines the configuration of the NetworkPolicy controller. NetworkPolicy *NetworkPolicyControllerConfiguration // ManagedSeed defines the configuration of the ManagedSeed controller. ManagedSeed *ManagedSeedControllerConfiguration - // ShootSecretControllerConfiguration defines the configuration of the ShootSecret controller. - ShootSecret *ShootSecretControllerConfiguration // TokenRequestorControllerConfiguration defines the configuration of the TokenRequestor controller. TokenRequestor *TokenRequestorControllerConfiguration } @@ -302,12 +298,6 @@ type ShootStateControllerConfiguration struct { SyncPeriod *metav1.Duration } -// ShootSecretControllerConfiguration defines the configuration of the ShootSecret controller. -type ShootSecretControllerConfiguration struct { - // ConcurrentSyncs is the number of workers used for the controller to work on events. - ConcurrentSyncs *int -} - // StaleExtensionHealthChecks defines the configuration of the check for stale extension health checks. type StaleExtensionHealthChecks struct { // Enabled specifies whether the check for stale extensions health checks is enabled. @@ -327,13 +317,6 @@ type ConditionThreshold struct { Duration metav1.Duration } -// ShootStateSyncControllerConfiguration defines the configuration of the ShootState Sync controller. -type ShootStateSyncControllerConfiguration struct { - // ConcurrentSyncs is the number of workers used for the controller to work on - // events. - ConcurrentSyncs *int -} - // NetworkPolicyControllerConfiguration defines the configuration of the NetworkPolicy // controller. type NetworkPolicyControllerConfiguration struct { diff --git a/pkg/gardenlet/apis/config/v1alpha1/defaults.go b/pkg/gardenlet/apis/config/v1alpha1/defaults.go index 9d328c79465..d4ba1538822 100644 --- a/pkg/gardenlet/apis/config/v1alpha1/defaults.go +++ b/pkg/gardenlet/apis/config/v1alpha1/defaults.go @@ -168,12 +168,6 @@ func SetDefaults_GardenletControllerConfiguration(obj *GardenletControllerConfig if obj.ShootState == nil { obj.ShootState = &ShootStateControllerConfiguration{} } - if obj.ShootSecret == nil { - obj.ShootSecret = &ShootSecretControllerConfiguration{} - } - if obj.ShootStateSync == nil { - obj.ShootStateSync = &ShootStateSyncControllerConfiguration{} - } if obj.NetworkPolicy == nil { obj.NetworkPolicy = &NetworkPolicyControllerConfiguration{} } @@ -380,23 +374,6 @@ func SetDefaults_ShootStateControllerConfiguration(obj *ShootStateControllerConf } } -// SetDefaults_ShootSecretControllerConfiguration sets defaults for the shoot secret controller. -func SetDefaults_ShootSecretControllerConfiguration(obj *ShootSecretControllerConfiguration) { - if obj.ConcurrentSyncs == nil { - obj.ConcurrentSyncs = pointer.Int(5) - } -} - -// SetDefaults_ShootStateSyncControllerConfiguration sets defaults for the shoot state controller. -func SetDefaults_ShootStateSyncControllerConfiguration(obj *ShootStateSyncControllerConfiguration) { - if obj.ConcurrentSyncs == nil { - // The controller actually starts one controller per extension resource per Seed. - // For one seed that is already 1 * 10 extension resources = 10 workers. - v := 1 - obj.ConcurrentSyncs = &v - } -} - // SetDefaults_NetworkPolicyControllerConfiguration sets defaults for the network policy controller. func SetDefaults_NetworkPolicyControllerConfiguration(obj *NetworkPolicyControllerConfiguration) { if obj.ConcurrentSyncs == nil { diff --git a/pkg/gardenlet/apis/config/v1alpha1/defaults_test.go b/pkg/gardenlet/apis/config/v1alpha1/defaults_test.go index e148dfc87fd..ddb02a9992e 100644 --- a/pkg/gardenlet/apis/config/v1alpha1/defaults_test.go +++ b/pkg/gardenlet/apis/config/v1alpha1/defaults_test.go @@ -56,7 +56,6 @@ var _ = Describe("Defaults", func() { Expect(obj.Controllers.ShootCare).NotTo(BeNil()) Expect(obj.Controllers.SeedCare).NotTo(BeNil()) Expect(obj.Controllers.ShootState).NotTo(BeNil()) - Expect(obj.Controllers.ShootStateSync).NotTo(BeNil()) Expect(obj.Controllers.ManagedSeed).NotTo(BeNil()) Expect(obj.LeaderElection).NotTo(BeNil()) Expect(obj.LogLevel).To(Equal(logger.InfoLevel)) @@ -361,20 +360,6 @@ var _ = Describe("Defaults", func() { }) }) - Describe("#SetDefaults_ShootSecretControllerConfiguration", func() { - var obj *ShootSecretControllerConfiguration - - BeforeEach(func() { - obj = &ShootSecretControllerConfiguration{} - }) - - It("should default the configuration", func() { - SetDefaults_ShootSecretControllerConfiguration(obj) - - Expect(obj.ConcurrentSyncs).To(PointTo(Equal(5))) - }) - }) - Describe("#SetDefaults_BackupEntryControllerConfiguration", func() { var obj *BackupEntryControllerConfiguration diff --git a/pkg/gardenlet/apis/config/v1alpha1/types.go b/pkg/gardenlet/apis/config/v1alpha1/types.go index 32ad2991028..6d102ee1133 100644 --- a/pkg/gardenlet/apis/config/v1alpha1/types.go +++ b/pkg/gardenlet/apis/config/v1alpha1/types.go @@ -189,18 +189,12 @@ type GardenletControllerConfiguration struct { // ShootState defines the configuration of the ShootState controller. // +optional ShootState *ShootStateControllerConfiguration `json:"shootState,omitempty"` - // ShootStateSync defines the configuration of the ShootState controller - // +optional - ShootStateSync *ShootStateSyncControllerConfiguration `json:"shootStateSync,omitempty"` // NetworkPolicy defines the configuration of the NetworkPolicy controller // +optional NetworkPolicy *NetworkPolicyControllerConfiguration `json:"networkPolicy,omitempty"` // ManagedSeed defines the configuration of the ManagedSeed controller. // +optional ManagedSeed *ManagedSeedControllerConfiguration `json:"managedSeed,omitempty"` - // ShootSecretControllerConfiguration defines the configuration of the ShootSecret controller. - // +optional - ShootSecret *ShootSecretControllerConfiguration `json:"shootSecret,omitempty"` // TokenRequestorControllerConfiguration defines the configuration of the TokenRequestor controller. // +optional TokenRequestor *TokenRequestorControllerConfiguration `json:"tokenRequestor,omitempty"` @@ -372,13 +366,6 @@ type ShootStateControllerConfiguration struct { SyncPeriod *metav1.Duration `json:"syncPeriod,omitempty"` } -// ShootSecretControllerConfiguration defines the configuration of the ShootSecret controller. -type ShootSecretControllerConfiguration struct { - // ConcurrentSyncs is the number of workers used for the controller to work on events. - // +optional - ConcurrentSyncs *int `json:"concurrentSyncs,omitempty"` -} - // StaleExtensionHealthChecks defines the configuration of the check for stale extension health checks. type StaleExtensionHealthChecks struct { // Enabled specifies whether the check for stale extensions health checks is enabled. @@ -399,14 +386,6 @@ type ConditionThreshold struct { Duration metav1.Duration `json:"duration"` } -// ShootStateSyncControllerConfiguration defines the configuration of the ShootState Sync controller. -type ShootStateSyncControllerConfiguration struct { - // ConcurrentSyncs is the number of workers used for the controller to work on - // events. - // +optional - ConcurrentSyncs *int `json:"concurrentSyncs,omitempty"` -} - // NetworkPolicyControllerConfiguration defines the configuration of the NetworkPolicy // controller. type NetworkPolicyControllerConfiguration struct { diff --git a/pkg/gardenlet/apis/config/v1alpha1/zz_generated.conversion.go b/pkg/gardenlet/apis/config/v1alpha1/zz_generated.conversion.go index 1f29dbf1942..2927e0ca90c 100644 --- a/pkg/gardenlet/apis/config/v1alpha1/zz_generated.conversion.go +++ b/pkg/gardenlet/apis/config/v1alpha1/zz_generated.conversion.go @@ -443,16 +443,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ShootSecretControllerConfiguration)(nil), (*config.ShootSecretControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_ShootSecretControllerConfiguration_To_config_ShootSecretControllerConfiguration(a.(*ShootSecretControllerConfiguration), b.(*config.ShootSecretControllerConfiguration), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*config.ShootSecretControllerConfiguration)(nil), (*ShootSecretControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_config_ShootSecretControllerConfiguration_To_v1alpha1_ShootSecretControllerConfiguration(a.(*config.ShootSecretControllerConfiguration), b.(*ShootSecretControllerConfiguration), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*ShootStateControllerConfiguration)(nil), (*config.ShootStateControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_ShootStateControllerConfiguration_To_config_ShootStateControllerConfiguration(a.(*ShootStateControllerConfiguration), b.(*config.ShootStateControllerConfiguration), scope) }); err != nil { @@ -463,16 +453,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ShootStateSyncControllerConfiguration)(nil), (*config.ShootStateSyncControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1alpha1_ShootStateSyncControllerConfiguration_To_config_ShootStateSyncControllerConfiguration(a.(*ShootStateSyncControllerConfiguration), b.(*config.ShootStateSyncControllerConfiguration), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*config.ShootStateSyncControllerConfiguration)(nil), (*ShootStateSyncControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_config_ShootStateSyncControllerConfiguration_To_v1alpha1_ShootStateSyncControllerConfiguration(a.(*config.ShootStateSyncControllerConfiguration), b.(*ShootStateSyncControllerConfiguration), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*StaleExtensionHealthChecks)(nil), (*config.StaleExtensionHealthChecks)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha1_StaleExtensionHealthChecks_To_config_StaleExtensionHealthChecks(a.(*StaleExtensionHealthChecks), b.(*config.StaleExtensionHealthChecks), scope) }); err != nil { @@ -1026,10 +1006,8 @@ func autoConvert_v1alpha1_GardenletControllerConfiguration_To_config_GardenletCo out.Shoot = (*config.ShootControllerConfiguration)(unsafe.Pointer(in.Shoot)) out.ShootCare = (*config.ShootCareControllerConfiguration)(unsafe.Pointer(in.ShootCare)) out.ShootState = (*config.ShootStateControllerConfiguration)(unsafe.Pointer(in.ShootState)) - out.ShootStateSync = (*config.ShootStateSyncControllerConfiguration)(unsafe.Pointer(in.ShootStateSync)) out.NetworkPolicy = (*config.NetworkPolicyControllerConfiguration)(unsafe.Pointer(in.NetworkPolicy)) out.ManagedSeed = (*config.ManagedSeedControllerConfiguration)(unsafe.Pointer(in.ManagedSeed)) - out.ShootSecret = (*config.ShootSecretControllerConfiguration)(unsafe.Pointer(in.ShootSecret)) out.TokenRequestor = (*config.TokenRequestorControllerConfiguration)(unsafe.Pointer(in.TokenRequestor)) return nil } @@ -1051,10 +1029,8 @@ func autoConvert_config_GardenletControllerConfiguration_To_v1alpha1_GardenletCo out.Shoot = (*ShootControllerConfiguration)(unsafe.Pointer(in.Shoot)) out.ShootCare = (*ShootCareControllerConfiguration)(unsafe.Pointer(in.ShootCare)) out.ShootState = (*ShootStateControllerConfiguration)(unsafe.Pointer(in.ShootState)) - out.ShootStateSync = (*ShootStateSyncControllerConfiguration)(unsafe.Pointer(in.ShootStateSync)) out.NetworkPolicy = (*NetworkPolicyControllerConfiguration)(unsafe.Pointer(in.NetworkPolicy)) out.ManagedSeed = (*ManagedSeedControllerConfiguration)(unsafe.Pointer(in.ManagedSeed)) - out.ShootSecret = (*ShootSecretControllerConfiguration)(unsafe.Pointer(in.ShootSecret)) out.TokenRequestor = (*TokenRequestorControllerConfiguration)(unsafe.Pointer(in.TokenRequestor)) return nil } @@ -1606,26 +1582,6 @@ func Convert_config_ShootNodeLogging_To_v1alpha1_ShootNodeLogging(in *config.Sho return autoConvert_config_ShootNodeLogging_To_v1alpha1_ShootNodeLogging(in, out, s) } -func autoConvert_v1alpha1_ShootSecretControllerConfiguration_To_config_ShootSecretControllerConfiguration(in *ShootSecretControllerConfiguration, out *config.ShootSecretControllerConfiguration, s conversion.Scope) error { - out.ConcurrentSyncs = (*int)(unsafe.Pointer(in.ConcurrentSyncs)) - return nil -} - -// Convert_v1alpha1_ShootSecretControllerConfiguration_To_config_ShootSecretControllerConfiguration is an autogenerated conversion function. -func Convert_v1alpha1_ShootSecretControllerConfiguration_To_config_ShootSecretControllerConfiguration(in *ShootSecretControllerConfiguration, out *config.ShootSecretControllerConfiguration, s conversion.Scope) error { - return autoConvert_v1alpha1_ShootSecretControllerConfiguration_To_config_ShootSecretControllerConfiguration(in, out, s) -} - -func autoConvert_config_ShootSecretControllerConfiguration_To_v1alpha1_ShootSecretControllerConfiguration(in *config.ShootSecretControllerConfiguration, out *ShootSecretControllerConfiguration, s conversion.Scope) error { - out.ConcurrentSyncs = (*int)(unsafe.Pointer(in.ConcurrentSyncs)) - return nil -} - -// Convert_config_ShootSecretControllerConfiguration_To_v1alpha1_ShootSecretControllerConfiguration is an autogenerated conversion function. -func Convert_config_ShootSecretControllerConfiguration_To_v1alpha1_ShootSecretControllerConfiguration(in *config.ShootSecretControllerConfiguration, out *ShootSecretControllerConfiguration, s conversion.Scope) error { - return autoConvert_config_ShootSecretControllerConfiguration_To_v1alpha1_ShootSecretControllerConfiguration(in, out, s) -} - func autoConvert_v1alpha1_ShootStateControllerConfiguration_To_config_ShootStateControllerConfiguration(in *ShootStateControllerConfiguration, out *config.ShootStateControllerConfiguration, s conversion.Scope) error { out.ConcurrentSyncs = (*int)(unsafe.Pointer(in.ConcurrentSyncs)) out.SyncPeriod = (*v1.Duration)(unsafe.Pointer(in.SyncPeriod)) @@ -1648,26 +1604,6 @@ func Convert_config_ShootStateControllerConfiguration_To_v1alpha1_ShootStateCont return autoConvert_config_ShootStateControllerConfiguration_To_v1alpha1_ShootStateControllerConfiguration(in, out, s) } -func autoConvert_v1alpha1_ShootStateSyncControllerConfiguration_To_config_ShootStateSyncControllerConfiguration(in *ShootStateSyncControllerConfiguration, out *config.ShootStateSyncControllerConfiguration, s conversion.Scope) error { - out.ConcurrentSyncs = (*int)(unsafe.Pointer(in.ConcurrentSyncs)) - return nil -} - -// Convert_v1alpha1_ShootStateSyncControllerConfiguration_To_config_ShootStateSyncControllerConfiguration is an autogenerated conversion function. -func Convert_v1alpha1_ShootStateSyncControllerConfiguration_To_config_ShootStateSyncControllerConfiguration(in *ShootStateSyncControllerConfiguration, out *config.ShootStateSyncControllerConfiguration, s conversion.Scope) error { - return autoConvert_v1alpha1_ShootStateSyncControllerConfiguration_To_config_ShootStateSyncControllerConfiguration(in, out, s) -} - -func autoConvert_config_ShootStateSyncControllerConfiguration_To_v1alpha1_ShootStateSyncControllerConfiguration(in *config.ShootStateSyncControllerConfiguration, out *ShootStateSyncControllerConfiguration, s conversion.Scope) error { - out.ConcurrentSyncs = (*int)(unsafe.Pointer(in.ConcurrentSyncs)) - return nil -} - -// Convert_config_ShootStateSyncControllerConfiguration_To_v1alpha1_ShootStateSyncControllerConfiguration is an autogenerated conversion function. -func Convert_config_ShootStateSyncControllerConfiguration_To_v1alpha1_ShootStateSyncControllerConfiguration(in *config.ShootStateSyncControllerConfiguration, out *ShootStateSyncControllerConfiguration, s conversion.Scope) error { - return autoConvert_config_ShootStateSyncControllerConfiguration_To_v1alpha1_ShootStateSyncControllerConfiguration(in, out, s) -} - func autoConvert_v1alpha1_StaleExtensionHealthChecks_To_config_StaleExtensionHealthChecks(in *StaleExtensionHealthChecks, out *config.StaleExtensionHealthChecks, s conversion.Scope) error { out.Enabled = in.Enabled out.Threshold = (*v1.Duration)(unsafe.Pointer(in.Threshold)) diff --git a/pkg/gardenlet/apis/config/v1alpha1/zz_generated.deepcopy.go b/pkg/gardenlet/apis/config/v1alpha1/zz_generated.deepcopy.go index be34ebfb79b..4bf8ce203a9 100644 --- a/pkg/gardenlet/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/gardenlet/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -575,11 +575,6 @@ func (in *GardenletControllerConfiguration) DeepCopyInto(out *GardenletControlle *out = new(ShootStateControllerConfiguration) (*in).DeepCopyInto(*out) } - if in.ShootStateSync != nil { - in, out := &in.ShootStateSync, &out.ShootStateSync - *out = new(ShootStateSyncControllerConfiguration) - (*in).DeepCopyInto(*out) - } if in.NetworkPolicy != nil { in, out := &in.NetworkPolicy, &out.NetworkPolicy *out = new(NetworkPolicyControllerConfiguration) @@ -590,11 +585,6 @@ func (in *GardenletControllerConfiguration) DeepCopyInto(out *GardenletControlle *out = new(ManagedSeedControllerConfiguration) (*in).DeepCopyInto(*out) } - if in.ShootSecret != nil { - in, out := &in.ShootSecret, &out.ShootSecret - *out = new(ShootSecretControllerConfiguration) - (*in).DeepCopyInto(*out) - } if in.TokenRequestor != nil { in, out := &in.TokenRequestor, &out.TokenRequestor *out = new(TokenRequestorControllerConfiguration) @@ -1256,27 +1246,6 @@ func (in *ShootNodeLogging) DeepCopy() *ShootNodeLogging { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ShootSecretControllerConfiguration) DeepCopyInto(out *ShootSecretControllerConfiguration) { - *out = *in - if in.ConcurrentSyncs != nil { - in, out := &in.ConcurrentSyncs, &out.ConcurrentSyncs - *out = new(int) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShootSecretControllerConfiguration. -func (in *ShootSecretControllerConfiguration) DeepCopy() *ShootSecretControllerConfiguration { - if in == nil { - return nil - } - out := new(ShootSecretControllerConfiguration) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ShootStateControllerConfiguration) DeepCopyInto(out *ShootStateControllerConfiguration) { *out = *in @@ -1303,27 +1272,6 @@ func (in *ShootStateControllerConfiguration) DeepCopy() *ShootStateControllerCon return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ShootStateSyncControllerConfiguration) DeepCopyInto(out *ShootStateSyncControllerConfiguration) { - *out = *in - if in.ConcurrentSyncs != nil { - in, out := &in.ConcurrentSyncs, &out.ConcurrentSyncs - *out = new(int) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShootStateSyncControllerConfiguration. -func (in *ShootStateSyncControllerConfiguration) DeepCopy() *ShootStateSyncControllerConfiguration { - if in == nil { - return nil - } - out := new(ShootStateSyncControllerConfiguration) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StaleExtensionHealthChecks) DeepCopyInto(out *StaleExtensionHealthChecks) { *out = *in diff --git a/pkg/gardenlet/apis/config/v1alpha1/zz_generated.defaults.go b/pkg/gardenlet/apis/config/v1alpha1/zz_generated.defaults.go index 0786eaf9e82..33e04a18037 100644 --- a/pkg/gardenlet/apis/config/v1alpha1/zz_generated.defaults.go +++ b/pkg/gardenlet/apis/config/v1alpha1/zz_generated.defaults.go @@ -86,18 +86,12 @@ func SetObjectDefaults_GardenletConfiguration(in *GardenletConfiguration) { if in.Controllers.ShootState != nil { SetDefaults_ShootStateControllerConfiguration(in.Controllers.ShootState) } - if in.Controllers.ShootStateSync != nil { - SetDefaults_ShootStateSyncControllerConfiguration(in.Controllers.ShootStateSync) - } if in.Controllers.NetworkPolicy != nil { SetDefaults_NetworkPolicyControllerConfiguration(in.Controllers.NetworkPolicy) } if in.Controllers.ManagedSeed != nil { SetDefaults_ManagedSeedControllerConfiguration(in.Controllers.ManagedSeed) } - if in.Controllers.ShootSecret != nil { - SetDefaults_ShootSecretControllerConfiguration(in.Controllers.ShootSecret) - } if in.Controllers.TokenRequestor != nil { SetDefaults_TokenRequestorControllerConfiguration(in.Controllers.TokenRequestor) } diff --git a/pkg/gardenlet/apis/config/zz_generated.deepcopy.go b/pkg/gardenlet/apis/config/zz_generated.deepcopy.go index 003ea5d24ba..ce247d6fe0a 100644 --- a/pkg/gardenlet/apis/config/zz_generated.deepcopy.go +++ b/pkg/gardenlet/apis/config/zz_generated.deepcopy.go @@ -575,11 +575,6 @@ func (in *GardenletControllerConfiguration) DeepCopyInto(out *GardenletControlle *out = new(ShootStateControllerConfiguration) (*in).DeepCopyInto(*out) } - if in.ShootStateSync != nil { - in, out := &in.ShootStateSync, &out.ShootStateSync - *out = new(ShootStateSyncControllerConfiguration) - (*in).DeepCopyInto(*out) - } if in.NetworkPolicy != nil { in, out := &in.NetworkPolicy, &out.NetworkPolicy *out = new(NetworkPolicyControllerConfiguration) @@ -590,11 +585,6 @@ func (in *GardenletControllerConfiguration) DeepCopyInto(out *GardenletControlle *out = new(ManagedSeedControllerConfiguration) (*in).DeepCopyInto(*out) } - if in.ShootSecret != nil { - in, out := &in.ShootSecret, &out.ShootSecret - *out = new(ShootSecretControllerConfiguration) - (*in).DeepCopyInto(*out) - } if in.TokenRequestor != nil { in, out := &in.TokenRequestor, &out.TokenRequestor *out = new(TokenRequestorControllerConfiguration) @@ -1256,27 +1246,6 @@ func (in *ShootNodeLogging) DeepCopy() *ShootNodeLogging { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ShootSecretControllerConfiguration) DeepCopyInto(out *ShootSecretControllerConfiguration) { - *out = *in - if in.ConcurrentSyncs != nil { - in, out := &in.ConcurrentSyncs, &out.ConcurrentSyncs - *out = new(int) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShootSecretControllerConfiguration. -func (in *ShootSecretControllerConfiguration) DeepCopy() *ShootSecretControllerConfiguration { - if in == nil { - return nil - } - out := new(ShootSecretControllerConfiguration) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ShootStateControllerConfiguration) DeepCopyInto(out *ShootStateControllerConfiguration) { *out = *in @@ -1303,27 +1272,6 @@ func (in *ShootStateControllerConfiguration) DeepCopy() *ShootStateControllerCon return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ShootStateSyncControllerConfiguration) DeepCopyInto(out *ShootStateSyncControllerConfiguration) { - *out = *in - if in.ConcurrentSyncs != nil { - in, out := &in.ConcurrentSyncs, &out.ConcurrentSyncs - *out = new(int) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ShootStateSyncControllerConfiguration. -func (in *ShootStateSyncControllerConfiguration) DeepCopy() *ShootStateSyncControllerConfiguration { - if in == nil { - return nil - } - out := new(ShootStateSyncControllerConfiguration) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StaleExtensionHealthChecks) DeepCopyInto(out *StaleExtensionHealthChecks) { *out = *in diff --git a/pkg/gardenlet/controller/add.go b/pkg/gardenlet/controller/add.go index 928637c952f..0948fb5e213 100644 --- a/pkg/gardenlet/controller/add.go +++ b/pkg/gardenlet/controller/add.go @@ -43,7 +43,6 @@ import ( "github.com/gardener/gardener/pkg/gardenlet/controller/networkpolicy" "github.com/gardener/gardener/pkg/gardenlet/controller/seed" "github.com/gardener/gardener/pkg/gardenlet/controller/shoot" - "github.com/gardener/gardener/pkg/gardenlet/controller/shootstate" "github.com/gardener/gardener/pkg/healthz" gardenerutils "github.com/gardener/gardener/pkg/utils/gardener" "github.com/gardener/gardener/pkg/utils/imagevector" @@ -142,10 +141,6 @@ func AddToManager( return fmt.Errorf("failed adding Shoot controller: %w", err) } - if err := shootstate.AddToManager(mgr, gardenCluster, seedCluster, *cfg); err != nil { - return fmt.Errorf("failed adding ShootState controller: %w", err) - } - if err := (&tokenrequestor.Reconciler{ ConcurrentSyncs: pointer.IntDeref(cfg.Controllers.TokenRequestor.ConcurrentSyncs, 0), Clock: clock.RealClock{}, diff --git a/pkg/gardenlet/controller/managedseed/charttest/charttest.go b/pkg/gardenlet/controller/managedseed/charttest/charttest.go index 2373d8fa32d..bb1a6ea7b34 100644 --- a/pkg/gardenlet/controller/managedseed/charttest/charttest.go +++ b/pkg/gardenlet/controller/managedseed/charttest/charttest.go @@ -794,12 +794,6 @@ func ComputeExpectedGardenletConfiguration( ConcurrentSyncs: &five, SyncPeriod: &metav1.Duration{Duration: 6 * time.Hour}, }, - ShootSecret: &gardenletv1alpha1.ShootSecretControllerConfiguration{ - ConcurrentSyncs: &five, - }, - ShootStateSync: &gardenletv1alpha1.ShootStateSyncControllerConfiguration{ - ConcurrentSyncs: &five, - }, TokenRequestor: &gardenletv1alpha1.TokenRequestorControllerConfiguration{ ConcurrentSyncs: &five, }, diff --git a/pkg/gardenlet/controller/managedseed/charttest/gardenlet_chart_test.go b/pkg/gardenlet/controller/managedseed/charttest/gardenlet_chart_test.go index da83d4dbb2b..ebf957bf0ac 100644 --- a/pkg/gardenlet/controller/managedseed/charttest/gardenlet_chart_test.go +++ b/pkg/gardenlet/controller/managedseed/charttest/gardenlet_chart_test.go @@ -349,13 +349,13 @@ var _ = Describe("#Gardenlet Chart Test", func() { ValidateGardenletChartVPA(ctx, c) } }, - Entry("verify the default values for the Gardenlet chart & the Gardenlet component config", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + Entry("verify the default values for the Gardenlet chart & the Gardenlet component config", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify Gardenlet with component config having the Garden client connection kubeconfig set", pointer.String("dummy garden kubeconfig"), nil, nil, nil, nil, nil, nil, nil, nil, nil, map[string]string{ - "gardenlet-configmap": "gardenlet-configmap-39ba728a", + "gardenlet-configmap": "gardenlet-configmap-a33669a5", "gardenlet-kubeconfig-garden": "gardenlet-kubeconfig-garden-8c9ae097", }), Entry("verify Gardenlet with component config having the Seed client connection kubeconfig set", nil, pointer.String("dummy seed kubeconfig"), nil, nil, nil, nil, nil, nil, nil, nil, map[string]string{ - "gardenlet-configmap": "gardenlet-configmap-82c4fd64", + "gardenlet-configmap": "gardenlet-configmap-3f1c11bd", "gardenlet-kubeconfig-seed": "gardenlet-kubeconfig-seed-662d92ae", }), Entry("verify Gardenlet with component config having a Bootstrap kubeconfig set", nil, nil, &corev1.SecretReference{ @@ -365,7 +365,7 @@ var _ = Describe("#Gardenlet Chart Test", func() { Name: "gardenlet-kubeconfig", Namespace: v1beta1constants.GardenNamespace, }, pointer.String("dummy bootstrap kubeconfig"), nil, nil, nil, nil, nil, map[string]string{ - "gardenlet-configmap": "gardenlet-configmap-b0ecc895", + "gardenlet-configmap": "gardenlet-configmap-84fd9f4e", }), Entry("verify that the SeedConfig is set in the component config Config Map", nil, nil, nil, nil, nil, &gardenletv1alpha1.SeedConfig{ @@ -377,7 +377,7 @@ var _ = Describe("#Gardenlet Chart Test", func() { Provider: gardencorev1beta1.SeedProvider{}, }, }, - }, nil, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-d4aa96c4"}), + }, nil, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-4b5dc832"}), Entry("verify deployment with two replica and three zones", nil, nil, nil, nil, nil, &gardenletv1alpha1.SeedConfig{ SeedTemplate: gardencorev1beta1.SeedTemplate{ @@ -392,7 +392,7 @@ var _ = Describe("#Gardenlet Chart Test", func() { }, }, &seedmanagement.GardenletDeployment{ ReplicaCount: pointer.Int32(2), - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-8dd838f7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-c61b5286"}), Entry("verify deployment with only one replica", nil, nil, nil, nil, nil, &gardenletv1alpha1.SeedConfig{ SeedTemplate: gardencorev1beta1.SeedTemplate{ @@ -407,7 +407,7 @@ var _ = Describe("#Gardenlet Chart Test", func() { }, }, &seedmanagement.GardenletDeployment{ ReplicaCount: pointer.Int32(1), - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-8dd838f7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-c61b5286"}), Entry("verify deployment with only one zone", nil, nil, nil, nil, nil, &gardenletv1alpha1.SeedConfig{ SeedTemplate: gardencorev1beta1.SeedTemplate{ @@ -420,23 +420,23 @@ var _ = Describe("#Gardenlet Chart Test", func() { }, }, }, - }, nil, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-b99a9dde"}), + }, nil, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-f209639a"}), Entry("verify deployment with image vector override", nil, nil, nil, nil, nil, nil, nil, pointer.String("dummy-override-content"), nil, nil, map[string]string{ - "gardenlet-configmap": "gardenlet-configmap-3a6db5a7", + "gardenlet-configmap": "gardenlet-configmap-64fb6ca6", "gardenlet-imagevector-overwrite": "gardenlet-imagevector-overwrite-32ecb769", }), Entry("verify deployment with component image vector override", nil, nil, nil, nil, nil, nil, nil, nil, pointer.String("dummy-override-content"), nil, map[string]string{ - "gardenlet-configmap": "gardenlet-configmap-3a6db5a7", + "gardenlet-configmap": "gardenlet-configmap-64fb6ca6", "gardenlet-imagevector-overwrite-components": "gardenlet-imagevector-overwrite-components-53f94952", }), Entry("verify deployment with custom replica count", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ ReplicaCount: pointer.Int32(3), - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify deployment with service account", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ ServiceAccountName: pointer.String("ax"), - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify deployment with resources", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ Resources: &corev1.ResourceRequirements{ @@ -448,19 +448,19 @@ var _ = Describe("#Gardenlet Chart Test", func() { corev1.ResourceMemory: resource.MustParse("25Mi"), }, }, - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify deployment with pod labels", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ PodLabels: map[string]string{ "x": "y", }, - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify deployment with pod annotations", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ PodAnnotations: map[string]string{ "x": "y", }, - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify deployment with additional volumes", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ AdditionalVolumes: []corev1.Volume{ @@ -469,7 +469,7 @@ var _ = Describe("#Gardenlet Chart Test", func() { VolumeSource: corev1.VolumeSource{}, }, }, - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify deployment with additional volume mounts", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ AdditionalVolumeMounts: []corev1.VolumeMount{ @@ -477,7 +477,7 @@ var _ = Describe("#Gardenlet Chart Test", func() { Name: "a", }, }, - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify deployment with env variables", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ Env: []corev1.EnvVar{ @@ -486,11 +486,11 @@ var _ = Describe("#Gardenlet Chart Test", func() { Value: "XY", }, }, - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), Entry("verify deployment with VPA enabled", nil, nil, nil, nil, nil, nil, &seedmanagement.GardenletDeployment{ VPA: pointer.Bool(true), - }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-3a6db5a7"}), + }, nil, nil, nil, map[string]string{"gardenlet-configmap": "gardenlet-configmap-64fb6ca6"}), ) }) diff --git a/pkg/gardenlet/controller/shoot/add.go b/pkg/gardenlet/controller/shoot/add.go index 0220e77d5c6..5b545628ba8 100644 --- a/pkg/gardenlet/controller/shoot/add.go +++ b/pkg/gardenlet/controller/shoot/add.go @@ -19,6 +19,7 @@ import ( "fmt" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/cluster" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -48,13 +49,24 @@ func AddToManager( gardenClusterIdentity string, imageVector imagevector.ImageVector, ) error { + var responsibleForUnmanagedSeed bool + if err := gardenCluster.GetAPIReader().Get(ctx, client.ObjectKey{Name: cfg.SeedConfig.Name, Namespace: v1beta1constants.GardenNamespace}, &seedmanagementv1alpha1.ManagedSeed{}); err != nil { + if !apierrors.IsNotFound(err) { + return fmt.Errorf("failed checking whether gardenlet is responsible for a managed seed: %w", err) + } + // ManagedSeed was not found, hence gardenlet is responsible for an unmanaged seed. + responsibleForUnmanagedSeed = true + } + shootStateControllerEnabled := responsibleForUnmanagedSeed && pointer.IntDeref(cfg.Controllers.ShootState.ConcurrentSyncs, 0) > 0 + if err := (&shoot.Reconciler{ - SeedClientSet: seedClientSet, - ShootClientMap: shootClientMap, - Config: cfg, - ImageVector: imageVector, - Identity: identity, - GardenClusterIdentity: gardenClusterIdentity, + SeedClientSet: seedClientSet, + ShootClientMap: shootClientMap, + Config: cfg, + ImageVector: imageVector, + Identity: identity, + GardenClusterIdentity: gardenClusterIdentity, + ShootStateControllerEnabled: shootStateControllerEnabled, }).AddToManager(mgr, gardenCluster); err != nil { return fmt.Errorf("failed adding main reconciler: %w", err) } @@ -71,13 +83,9 @@ func AddToManager( return fmt.Errorf("failed adding care reconciler: %w", err) } - if err := gardenCluster.GetAPIReader().Get(ctx, client.ObjectKey{Name: cfg.SeedConfig.Name, Namespace: v1beta1constants.GardenNamespace}, &seedmanagementv1alpha1.ManagedSeed{}); err != nil { - if !apierrors.IsNotFound(err) { - return fmt.Errorf("failed checking whether gardenlet is responsible for a managed seed: %w", err) - } - - // ManagedSeed was not found, hence gardenlet is responsible for an unmanaged seed. In this case, we want to add - // the state reconciler which performs periodic backups of shoot states (see GEP-22). + // If gardenlet is responsible for an unmanaged seed we want to add the state reconciler which performs periodic + // backups of shoot states (see GEP-22). + if shootStateControllerEnabled { mgr.GetLogger().Info("Adding shoot state reconciler since gardenlet is responsible for an unmanaged seed") if err := (&state.Reconciler{ diff --git a/pkg/gardenlet/controller/shoot/shoot/reconciler.go b/pkg/gardenlet/controller/shoot/shoot/reconciler.go index 95a50ca96d4..383628e37cb 100644 --- a/pkg/gardenlet/controller/shoot/shoot/reconciler.go +++ b/pkg/gardenlet/controller/shoot/shoot/reconciler.go @@ -67,15 +67,16 @@ const taskID = "initializeOperation" // Reconciler implements the main shoot reconciliation logic, i.e., creation, hibernation, migration and deletion. type Reconciler struct { - GardenClient client.Client - SeedClientSet kubernetes.Interface - ShootClientMap clientmap.ClientMap - Config config.GardenletConfiguration - Recorder record.EventRecorder - ImageVector imagevector.ImageVector - Identity *gardencorev1beta1.Gardener - GardenClusterIdentity string - Clock clock.Clock + GardenClient client.Client + SeedClientSet kubernetes.Interface + ShootClientMap clientmap.ClientMap + Config config.GardenletConfiguration + Recorder record.EventRecorder + ImageVector imagevector.ImageVector + Identity *gardencorev1beta1.Gardener + GardenClusterIdentity string + Clock clock.Clock + ShootStateControllerEnabled bool } // Reconcile implements the main shoot reconciliation logic, i.e., creation, hibernation, migration and deletion. diff --git a/pkg/gardenlet/controller/shoot/shoot/reconciler_delete.go b/pkg/gardenlet/controller/shoot/shoot/reconciler_delete.go index 92e8a27e2ef..6cf96fe2b6b 100644 --- a/pkg/gardenlet/controller/shoot/shoot/reconciler_delete.go +++ b/pkg/gardenlet/controller/shoot/shoot/reconciler_delete.go @@ -154,10 +154,6 @@ func (r *Reconciler) runDeleteShootFlow(ctx context.Context, o *operation.Operat g = flow.NewGraph("Shoot cluster deletion") - ensureShootStateExists = g.Add(flow.Task{ - Name: "Ensuring that ShootState exists", - Fn: flow.TaskFn(botanist.EnsureShootStateExists).RetryUntilTimeout(defaultInterval, defaultTimeout), - }) deployNamespace = g.Add(flow.Task{ Name: "Deploying Shoot namespace in Seed", Fn: flow.TaskFn(botanist.DeploySeedNamespace).RetryUntilTimeout(defaultInterval, defaultTimeout).DoIf(nonTerminatingNamespace), @@ -200,12 +196,12 @@ func (r *Reconciler) runDeleteShootFlow(ctx context.Context, o *operation.Operat initializeSecretsManagement = g.Add(flow.Task{ Name: "Initializing secrets management", Fn: flow.TaskFn(botanist.InitializeSecretsManagement).DoIf(nonTerminatingNamespace).RetryUntilTimeout(defaultInterval, defaultTimeout), - Dependencies: flow.NewTaskIDs(deployNamespace, ensureShootStateExists), + Dependencies: flow.NewTaskIDs(deployNamespace), }) deployReferencedResources = g.Add(flow.Task{ Name: "Deploying referenced resources", Fn: flow.TaskFn(botanist.DeployReferencedResources).RetryUntilTimeout(defaultInterval, defaultTimeout).DoIf(nonTerminatingNamespace), - Dependencies: flow.NewTaskIDs(deployNamespace, ensureShootStateExists), + Dependencies: flow.NewTaskIDs(deployNamespace), }) deployInternalDomainDNSRecord = g.Add(flow.Task{ Name: "Deploying internal domain DNS record", diff --git a/pkg/gardenlet/controller/shoot/shoot/reconciler_migrate.go b/pkg/gardenlet/controller/shoot/shoot/reconciler_migrate.go index 0d3306cc515..b73b8ed3ec1 100644 --- a/pkg/gardenlet/controller/shoot/shoot/reconciler_migrate.go +++ b/pkg/gardenlet/controller/shoot/shoot/reconciler_migrate.go @@ -132,14 +132,9 @@ func (r *Reconciler) runMigrateShootFlow(ctx context.Context, o *operation.Opera g = flow.NewGraph("Shoot cluster preparation for migration") - ensureShootStateExists = g.Add(flow.Task{ - Name: "Ensuring that ShootState exists", - Fn: flow.TaskFn(botanist.EnsureShootStateExists).RetryUntilTimeout(defaultInterval, defaultTimeout), - }) initializeSecretsManagement = g.Add(flow.Task{ - Name: "Initializing secrets management", - Fn: flow.TaskFn(botanist.InitializeSecretsManagement).DoIf(nonTerminatingNamespace).RetryUntilTimeout(defaultInterval, defaultTimeout), - Dependencies: flow.NewTaskIDs(ensureShootStateExists), + Name: "Initializing secrets management", + Fn: flow.TaskFn(botanist.InitializeSecretsManagement).DoIf(nonTerminatingNamespace).RetryUntilTimeout(defaultInterval, defaultTimeout), }) deployETCD = g.Add(flow.Task{ Name: "Deploying main and events etcd", diff --git a/pkg/gardenlet/controller/shoot/shoot/reconciler_reconcile.go b/pkg/gardenlet/controller/shoot/shoot/reconciler_reconcile.go index 9df0ae8eb47..f87174b7dd8 100644 --- a/pkg/gardenlet/controller/shoot/shoot/reconciler_reconcile.go +++ b/pkg/gardenlet/controller/shoot/shoot/reconciler_reconcile.go @@ -36,6 +36,7 @@ import ( "github.com/gardener/gardener/pkg/utils/errors" "github.com/gardener/gardener/pkg/utils/flow" "github.com/gardener/gardener/pkg/utils/gardener/secretsrotation" + "github.com/gardener/gardener/pkg/utils/gardener/shootstate" "github.com/gardener/gardener/pkg/utils/gardener/tokenrequest" kubernetesutils "github.com/gardener/gardener/pkg/utils/kubernetes" retryutils "github.com/gardener/gardener/pkg/utils/retry" @@ -125,11 +126,7 @@ func (r *Reconciler) runReconcileShootFlow(ctx context.Context, o *operation.Ope } var ( - g = flow.NewGraph(fmt.Sprintf("Shoot cluster %s", utils.IifString(isRestoring, "restoration", "reconciliation"))) - ensureShootStateExists = g.Add(flow.Task{ - Name: "Ensuring that ShootState exists", - Fn: flow.TaskFn(botanist.EnsureShootStateExists).RetryUntilTimeout(defaultInterval, defaultTimeout), - }) + g = flow.NewGraph(fmt.Sprintf("Shoot cluster %s", utils.IifString(isRestoring, "restoration", "reconciliation"))) deployNamespace = g.Add(flow.Task{ Name: "Deploying Shoot namespace in Seed", Fn: flow.TaskFn(botanist.DeploySeedNamespace).RetryUntilTimeout(defaultInterval, defaultTimeout), @@ -167,7 +164,7 @@ func (r *Reconciler) runReconcileShootFlow(ctx context.Context, o *operation.Ope initializeSecretsManagement = g.Add(flow.Task{ Name: "Initializing secrets management", Fn: flow.TaskFn(botanist.InitializeSecretsManagement).RetryUntilTimeout(defaultInterval, defaultTimeout), - Dependencies: flow.NewTaskIDs(deployNamespace, ensureShootStateExists), + Dependencies: flow.NewTaskIDs(deployNamespace), }) _ = g.Add(flow.Task{ Name: "Deploying Kubernetes API server ingress with trusted certificate in the Seed cluster", @@ -228,7 +225,7 @@ func (r *Reconciler) runReconcileShootFlow(ctx context.Context, o *operation.Ope deployBackupEntryInGarden = g.Add(flow.Task{ Name: "Deploying backup entry", Fn: flow.TaskFn(botanist.DeployBackupEntry).DoIf(allowBackup), - Dependencies: flow.NewTaskIDs(ensureShootStateExists, waitUntilSourceBackupEntryInGardenReconciled), + Dependencies: flow.NewTaskIDs(waitUntilSourceBackupEntryInGardenReconciled), }) waitUntilBackupEntryInGardenReconciled = g.Add(flow.Task{ Name: "Waiting until the backup entry has been reconciled", @@ -816,6 +813,14 @@ func (r *Reconciler) runReconcileShootFlow(ctx context.Context, o *operation.Ope return v1beta1helper.NewWrappedLastErrors(v1beta1helper.FormatLastErrDescription(err), err) } + if !r.ShootStateControllerEnabled && botanist.IsRestorePhase() { + o.Logger.Info("Deleting Shoot State after successful restoration") + if err := shootstate.Delete(ctx, botanist.GardenClient, botanist.Shoot.GetInfo()); err != nil { + err = fmt.Errorf("failed to delete shoot state: %w", err) + return v1beta1helper.NewWrappedLastErrors(v1beta1helper.FormatLastErrDescription(err), err) + } + } + // ensure that shoot client is invalidated after it has been hibernated if o.Shoot.HibernationEnabled { if err := o.ShootClientMap.InvalidateClient(keys.ForShoot(o.Shoot.GetInfo())); err != nil { diff --git a/pkg/gardenlet/controller/shootstate/add.go b/pkg/gardenlet/controller/shootstate/add.go deleted file mode 100644 index 1ad14a683e3..00000000000 --- a/pkg/gardenlet/controller/shootstate/add.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 shootstate - -import ( - "fmt" - - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/cluster" - "sigs.k8s.io/controller-runtime/pkg/manager" - - extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" - "github.com/gardener/gardener/pkg/gardenlet/apis/config" - "github.com/gardener/gardener/pkg/gardenlet/controller/shootstate/extensions" - "github.com/gardener/gardener/pkg/gardenlet/controller/shootstate/secret" -) - -// AddToManager adds all controllers to the given manager. -func AddToManager( - mgr manager.Manager, - gardenCluster cluster.Cluster, - seedCluster cluster.Cluster, - cfg config.GardenletConfiguration, -) error { - for objectKind, newObjectFunc := range map[string]func() client.Object{ - extensionsv1alpha1.BackupEntryResource: func() client.Object { return &extensionsv1alpha1.BackupEntry{} }, - extensionsv1alpha1.ContainerRuntimeResource: func() client.Object { return &extensionsv1alpha1.ContainerRuntime{} }, - extensionsv1alpha1.ControlPlaneResource: func() client.Object { return &extensionsv1alpha1.ControlPlane{} }, - extensionsv1alpha1.DNSRecordResource: func() client.Object { return &extensionsv1alpha1.DNSRecord{} }, - extensionsv1alpha1.ExtensionResource: func() client.Object { return &extensionsv1alpha1.Extension{} }, - extensionsv1alpha1.InfrastructureResource: func() client.Object { return &extensionsv1alpha1.Infrastructure{} }, - extensionsv1alpha1.NetworkResource: func() client.Object { return &extensionsv1alpha1.Network{} }, - extensionsv1alpha1.OperatingSystemConfigResource: func() client.Object { return &extensionsv1alpha1.OperatingSystemConfig{} }, - extensionsv1alpha1.WorkerResource: func() client.Object { return &extensionsv1alpha1.Worker{} }, - } { - if err := (&extensions.Reconciler{ - Config: *cfg.Controllers.ShootStateSync, - ObjectKind: objectKind, - NewObjectFunc: newObjectFunc, - }).AddToManager(mgr, gardenCluster, seedCluster); err != nil { - return fmt.Errorf("failed adding extensions reconciler for %s: %w", objectKind, err) - } - } - - if err := (&secret.Reconciler{ - Config: *cfg.Controllers.ShootSecret, - }).AddToManager(mgr, gardenCluster, seedCluster); err != nil { - return fmt.Errorf("failed adding secret reconciler: %w", err) - } - - return nil -} diff --git a/pkg/gardenlet/controller/shootstate/extensions/add.go b/pkg/gardenlet/controller/shootstate/extensions/add.go deleted file mode 100644 index a2cc0ce9d71..00000000000 --- a/pkg/gardenlet/controller/shootstate/extensions/add.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 extensions - -import ( - "fmt" - - apiequality "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/cluster" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" - "sigs.k8s.io/controller-runtime/pkg/source" - - v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants" - extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" -) - -// ControllerName is the name of this controller. -const ControllerName = "shootstate-extensions" - -// AddToManager adds Reconciler to the given manager. -func (r *Reconciler) AddToManager(mgr manager.Manager, gardenCluster, seedCluster cluster.Cluster) error { - if r.GardenClient == nil { - r.GardenClient = gardenCluster.GetClient() - } - if r.SeedClient == nil { - r.SeedClient = seedCluster.GetClient() - } - - return builder. - ControllerManagedBy(mgr). - Named(fmt.Sprintf("%s-%s", ControllerName, r.ObjectKind)). - WithOptions(controller.Options{ - MaxConcurrentReconciles: pointer.IntDeref(r.Config.ConcurrentSyncs, 0), - }). - Watches( - source.NewKindWithCache(r.NewObjectFunc(), seedCluster.GetCache()), - &handler.EnqueueRequestForObject{}, - builder.WithPredicates( - r.ObjectPredicate(), - r.InvalidOperationAnnotationPredicate(), - ), - ). - Complete(r) -} - -// ObjectPredicate returns true for 'create' and 'update' events. For updates, it only returns true when the extension -// state or the extension resources in the status have changed, or when the operation annotation has changed. -func (r *Reconciler) ObjectPredicate() predicate.Predicate { - return predicate.Funcs{ - CreateFunc: func(_ event.CreateEvent) bool { - return true - }, - UpdateFunc: func(e event.UpdateEvent) bool { - // enqueue on periodic cache resyncs - if e.ObjectOld.GetResourceVersion() == e.ObjectNew.GetResourceVersion() { - return true - } - - extensionObj, ok := e.ObjectNew.(extensionsv1alpha1.Object) - if !ok { - return false - } - - oldExtensionObj, ok := e.ObjectOld.(extensionsv1alpha1.Object) - if !ok { - return false - } - - return !apiequality.Semantic.DeepEqual(extensionObj.GetExtensionStatus().GetState(), oldExtensionObj.GetExtensionStatus().GetState()) || - !apiequality.Semantic.DeepEqual(extensionObj.GetExtensionStatus().GetResources(), oldExtensionObj.GetExtensionStatus().GetResources()) || - (invalidOperationAnnotations.Has(oldExtensionObj.GetAnnotations()[v1beta1constants.GardenerOperation]) && !invalidOperationAnnotations.Has(extensionObj.GetAnnotations()[v1beta1constants.GardenerOperation])) - }, - DeleteFunc: func(_ event.DeleteEvent) bool { return true }, - GenericFunc: func(_ event.GenericEvent) bool { return false }, - } -} - -var invalidOperationAnnotations = sets.New( - v1beta1constants.GardenerOperationWaitForState, - v1beta1constants.GardenerOperationRestore, - v1beta1constants.GardenerOperationMigrate, -) - -// InvalidOperationAnnotationPredicate returns a predicate which evaluates to false if the object has one of the -// following operation annotations: 'wait-for-state', 'restore', 'migrate'. -func (r *Reconciler) InvalidOperationAnnotationPredicate() predicate.Predicate { - return predicate.NewPredicateFuncs(func(obj client.Object) bool { - return !invalidOperationAnnotations.Has(obj.GetAnnotations()[v1beta1constants.GardenerOperation]) - }) -} diff --git a/pkg/gardenlet/controller/shootstate/extensions/add_test.go b/pkg/gardenlet/controller/shootstate/extensions/add_test.go deleted file mode 100644 index 4d65f448f35..00000000000 --- a/pkg/gardenlet/controller/shootstate/extensions/add_test.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 extensions_test - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" - - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" - . "github.com/gardener/gardener/pkg/gardenlet/controller/shootstate/extensions" -) - -var _ = Describe("Add", func() { - var ( - reconciler *Reconciler - infrastructure *extensionsv1alpha1.Infrastructure - ) - - BeforeEach(func() { - reconciler = &Reconciler{} - infrastructure = &extensionsv1alpha1.Infrastructure{ - Spec: extensionsv1alpha1.InfrastructureSpec{ - DefaultSpec: extensionsv1alpha1.DefaultSpec{ - Type: "type", - }, - }, - } - }) - - Describe("#ObjectPredicate", func() { - var p predicate.Predicate - - BeforeEach(func() { - p = reconciler.ObjectPredicate() - }) - - Describe("#Create", func() { - It("should return true", func() { - Expect(p.Create(event.CreateEvent{})).To(BeTrue()) - }) - }) - - Describe("#Update", func() { - It("should return true for periodic cache resyncs", func() { - Expect(p.Update(event.UpdateEvent{ObjectNew: infrastructure, ObjectOld: infrastructure.DeepCopy()})).To(BeTrue()) - }) - - It("should return false because object is no extensions object", func() { - Expect(p.Update(event.UpdateEvent{ObjectOld: &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{ResourceVersion: "123"}}, ObjectNew: infrastructure})).To(BeFalse()) - }) - - It("should return false because old object is no extensions object", func() { - Expect(p.Update(event.UpdateEvent{ObjectOld: infrastructure, ObjectNew: &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{ResourceVersion: "123"}}})).To(BeFalse()) - }) - - It("should return false because neither state nor resources nor operation annotation changed", func() { - oldInfrastructure := infrastructure.DeepCopy() - infrastructure.ResourceVersion = "1" - Expect(p.Update(event.UpdateEvent{ObjectNew: infrastructure, ObjectOld: oldInfrastructure})).To(BeFalse()) - }) - - It("should return true because state was changed", func() { - oldInfrastructure := infrastructure.DeepCopy() - infrastructure.ResourceVersion = "1" - infrastructure.Status.State = &runtime.RawExtension{} - Expect(p.Update(event.UpdateEvent{ObjectNew: infrastructure, ObjectOld: oldInfrastructure})).To(BeTrue()) - }) - - It("should return true because resources were changed", func() { - oldInfrastructure := infrastructure.DeepCopy() - infrastructure.ResourceVersion = "1" - infrastructure.Status.Resources = []gardencorev1beta1.NamedResourceReference{{}} - Expect(p.Update(event.UpdateEvent{ObjectNew: infrastructure, ObjectOld: oldInfrastructure})).To(BeTrue()) - }) - - It("should return true because operation annotation was changed to valid value", func() { - oldInfrastructure := infrastructure.DeepCopy() - infrastructure.ResourceVersion = "1" - metav1.SetMetaDataAnnotation(&oldInfrastructure.ObjectMeta, "gardener.cloud/operation", "wait-for-state") - metav1.SetMetaDataAnnotation(&infrastructure.ObjectMeta, "gardener.cloud/operation", "foo") - Expect(p.Update(event.UpdateEvent{ObjectNew: infrastructure, ObjectOld: oldInfrastructure})).To(BeTrue()) - }) - - It("should return false because operation annotation was changed to invalid value", func() { - oldInfrastructure := infrastructure.DeepCopy() - infrastructure.ResourceVersion = "1" - metav1.SetMetaDataAnnotation(&infrastructure.ObjectMeta, "gardener.cloud/operation", "restore") - Expect(p.Update(event.UpdateEvent{ObjectNew: infrastructure, ObjectOld: oldInfrastructure})).To(BeFalse()) - }) - }) - - Describe("#Delete", func() { - It("should return true", func() { - Expect(p.Delete(event.DeleteEvent{})).To(BeTrue()) - }) - }) - - Describe("#Generic", func() { - It("should return false", func() { - Expect(p.Generic(event.GenericEvent{})).To(BeFalse()) - }) - }) - }) - - Describe("#InvalidOperationAnnotationPredicate", func() { - var p predicate.Predicate - - BeforeEach(func() { - p = reconciler.InvalidOperationAnnotationPredicate() - }) - - tests := func(f func(*extensionsv1alpha1.Infrastructure) bool) { - It("should return true when no operation annotation present", func() { - Expect(f(infrastructure)).To(BeTrue()) - }) - - It("should return false when operation annotation is 'wait-for-state'", func() { - metav1.SetMetaDataAnnotation(&infrastructure.ObjectMeta, "gardener.cloud/operation", "wait-for-state") - Expect(f(infrastructure)).To(BeFalse()) - }) - - It("should return false when operation annotation is 'migrate'", func() { - metav1.SetMetaDataAnnotation(&infrastructure.ObjectMeta, "gardener.cloud/operation", "migrate") - Expect(f(infrastructure)).To(BeFalse()) - }) - - It("should return false when operation annotation is 'restore'", func() { - metav1.SetMetaDataAnnotation(&infrastructure.ObjectMeta, "gardener.cloud/operation", "restore") - Expect(f(infrastructure)).To(BeFalse()) - }) - - It("should return true when operation annotation has different value", func() { - metav1.SetMetaDataAnnotation(&infrastructure.ObjectMeta, "gardener.cloud/operation", "foo") - Expect(f(infrastructure)).To(BeTrue()) - }) - } - - Describe("#Create", func() { - tests(func(obj *extensionsv1alpha1.Infrastructure) bool { return p.Create(event.CreateEvent{Object: obj}) }) - }) - - Describe("#Update", func() { - tests(func(obj *extensionsv1alpha1.Infrastructure) bool { return p.Update(event.UpdateEvent{ObjectNew: obj}) }) - }) - - Describe("#Delete", func() { - tests(func(obj *extensionsv1alpha1.Infrastructure) bool { return p.Delete(event.DeleteEvent{Object: obj}) }) - }) - - Describe("#Generic", func() { - tests(func(obj *extensionsv1alpha1.Infrastructure) bool { return p.Generic(event.GenericEvent{Object: obj}) }) - }) - }) -}) diff --git a/pkg/gardenlet/controller/shootstate/extensions/extensions_suite_test.go b/pkg/gardenlet/controller/shootstate/extensions/extensions_suite_test.go deleted file mode 100644 index 46739c7d72c..00000000000 --- a/pkg/gardenlet/controller/shootstate/extensions/extensions_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 extensions_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestExtensions(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Gardenlet Controller ShootState Extensions Suite") -} diff --git a/pkg/gardenlet/controller/shootstate/extensions/reconciler.go b/pkg/gardenlet/controller/shootstate/extensions/reconciler.go deleted file mode 100644 index bc7002e9efe..00000000000 --- a/pkg/gardenlet/controller/shootstate/extensions/reconciler.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 extensions - -import ( - "context" - "fmt" - - autoscalingv1 "k8s.io/api/autoscaling/v1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - apiextensions "github.com/gardener/gardener/pkg/api/extensions" - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - v1beta1helper "github.com/gardener/gardener/pkg/apis/core/v1beta1/helper" - "github.com/gardener/gardener/pkg/controllerutils" - "github.com/gardener/gardener/pkg/extensions" - "github.com/gardener/gardener/pkg/gardenlet/apis/config" - gardenerutils "github.com/gardener/gardener/pkg/utils/gardener" - unstructuredutils "github.com/gardener/gardener/pkg/utils/kubernetes/unstructured" -) - -// Reconciler is used to update data about extensions and any resources required by them in the ShootState. -type Reconciler struct { - GardenClient client.Client - SeedClient client.Client - Config config.ShootStateSyncControllerConfiguration - - ObjectKind string - NewObjectFunc func() client.Object -} - -// Reconcile performs the main reconciliation logic. -func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { - log := logf.FromContext(ctx) - - ctx, cancel := controllerutils.GetMainReconciliationContext(ctx, controllerutils.DefaultReconciliationTimeout) - defer cancel() - - obj := r.NewObjectFunc() - if err := r.SeedClient.Get(ctx, request.NamespacedName, obj); err != nil { - if apierrors.IsNotFound(err) { - log.V(1).Info("Object is gone, stop reconciling") - return reconcile.Result{}, nil - } - return reconcile.Result{}, fmt.Errorf("error retrieving object from store: %w", err) - } - - extensionObj, err := apiextensions.Accessor(obj) - if err != nil { - return reconcile.Result{}, err - } - - var ( - name = extensionObj.GetName() - purpose = extensionObj.GetExtensionSpec().GetExtensionPurpose() - newState = extensionObj.GetExtensionStatus().GetState() - newResources = extensionObj.GetExtensionStatus().GetResources() - ) - - if obj.GetDeletionTimestamp() != nil { - newState, newResources = nil, nil - } - - log = log.WithValues("extensionName", name, "extensionPurpose", purposeToString(purpose)) - - shootState, _, err := extensions.GetShootStateForCluster(ctx, r.GardenClient, r.SeedClient, r.getClusterNameFromRequest(request)) - if err != nil { - if apierrors.IsNotFound(err) { - return reconcile.Result{}, nil - } - return reconcile.Result{}, err - } - - log = log.WithValues("shootState", client.ObjectKeyFromObject(shootState)) - - patch := client.MergeFromWithOptions(shootState.DeepCopy(), client.MergeFromWithOptimisticLock{}) - currentState, currentResources := getShootStateExtensionStateAndResources(shootState, r.ObjectKind, &name, purpose) - - // Delete resources which are no longer present in the extension state from the ShootState.Spec.Resources list - for _, resource := range currentResources { - if v1beta1helper.GetResourceByName(newResources, resource.Name) == nil { - updateShootStateResourceData(shootState, &resource.ResourceRef, nil) - } - } - - resourcesToUpdate, err := r.getResourcesToUpdate(ctx, shootState, extensionObj.GetNamespace(), newResources) - if err != nil { - return reconcile.Result{}, err - } - for _, resourceToUpdate := range resourcesToUpdate { - updateShootStateResourceData(shootState, &resourceToUpdate.CrossVersionObjectReference, &resourceToUpdate.Data) - } - - shouldUpdateExtensionData := !apiequality.Semantic.DeepEqual(newState, currentState) || !apiequality.Semantic.DeepEqual(newResources, currentResources) - if shouldUpdateExtensionData { - updateShootStateExtensionStateAndResources(shootState, r.ObjectKind, &name, purpose, newState, newResources) - } - - if !shouldUpdateExtensionData && len(resourcesToUpdate) == 0 { - log.Info("Skipping sync because state is already up-to-date") - return reconcile.Result{}, nil - } - - if err := r.GardenClient.Patch(ctx, shootState, patch); err != nil { - return reconcile.Result{}, fmt.Errorf("could not sync extension state for shoot %q for extension %s/%s/%s: %w", client.ObjectKeyFromObject(shootState).String(), r.ObjectKind, name, purposeToString(purpose), err) - } - - log.Info("Syncing state was successful") - return reconcile.Result{}, nil -} - -func (r *Reconciler) getResourcesToUpdate(ctx context.Context, shootState *gardencorev1beta1.ShootState, namespace string, newResources []gardencorev1beta1.NamedResourceReference) (v1beta1helper.ResourceDataList, error) { - var resourcesToAddUpdate v1beta1helper.ResourceDataList - - for _, newResource := range newResources { - obj, err := unstructuredutils.GetObjectByRef(ctx, r.SeedClient, &newResource.ResourceRef, namespace) - if err != nil { - return nil, err - } - if obj == nil { - return nil, fmt.Errorf("object not found %v", newResource.ResourceRef) - } - - raw := &runtime.RawExtension{} - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj, raw); err != nil { - return nil, err - } - - resourceList := v1beta1helper.ResourceDataList(shootState.Spec.Resources) - currentResource := resourceList.Get(&newResource.ResourceRef) - - if currentResource == nil || !apiequality.Semantic.DeepEqual(currentResource.Data, newResource) { - resourcesToAddUpdate = append(resourcesToAddUpdate, gardencorev1beta1.ResourceData{ - CrossVersionObjectReference: newResource.ResourceRef, - Data: *raw, - }) - } - } - - return resourcesToAddUpdate, nil -} - -func getShootStateExtensionStateAndResources(shootState *gardencorev1beta1.ShootState, kind string, name, purpose *string) (*runtime.RawExtension, []gardencorev1beta1.NamedResourceReference) { - list := v1beta1helper.ExtensionResourceStateList(shootState.Spec.Extensions) - s := list.Get(kind, name, purpose) - if s != nil { - return s.State, s.Resources - } - return nil, nil -} - -func updateShootStateExtensionStateAndResources(shootState *gardencorev1beta1.ShootState, kind string, name, purpose *string, state *runtime.RawExtension, resources []gardencorev1beta1.NamedResourceReference) { - list := v1beta1helper.ExtensionResourceStateList(shootState.Spec.Extensions) - if state != nil || len(resources) > 0 { - list.Upsert(&gardencorev1beta1.ExtensionResourceState{ - Kind: kind, - Name: name, - Purpose: purpose, - State: state, - Resources: resources, - }) - } else { - list.Delete(kind, name, purpose) - } - shootState.Spec.Extensions = list -} - -func updateShootStateResourceData(shootState *gardencorev1beta1.ShootState, ref *autoscalingv1.CrossVersionObjectReference, data *runtime.RawExtension) { - list := v1beta1helper.ResourceDataList(shootState.Spec.Resources) - if data != nil { - list.Upsert(&gardencorev1beta1.ResourceData{ - CrossVersionObjectReference: *ref, - Data: *data, - }) - } else { - list.Delete(ref) - } - shootState.Spec.Resources = list -} - -func purposeToString(purpose *string) string { - if purpose == nil { - return "" - } - return *purpose -} - -func (r *Reconciler) getClusterNameFromRequest(req reconcile.Request) string { - var clusterName string - if req.Namespace == "" { - // Handling for cluster-scoped backupentry extension resources. - clusterName, _ = gardenerutils.ExtractShootDetailsFromBackupEntryName(req.Name) - } else { - clusterName = req.Namespace - } - - return clusterName -} diff --git a/pkg/gardenlet/controller/shootstate/extensions/reconciler_test.go b/pkg/gardenlet/controller/shootstate/extensions/reconciler_test.go deleted file mode 100644 index 50e81c3ac97..00000000000 --- a/pkg/gardenlet/controller/shootstate/extensions/reconciler_test.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 extensions_test - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/golang/mock/gomock" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - autoscalingv1 "k8s.io/api/autoscaling/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/fake" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" - . "github.com/gardener/gardener/pkg/gardenlet/controller/shootstate/extensions" - mockclient "github.com/gardener/gardener/pkg/mock/controller-runtime/client" -) - -var _ = Describe("Reconciler", func() { - var ( - objectKind = extensionsv1alpha1.ExtensionResource - newObjectFunc = func() client.Object { return &extensionsv1alpha1.Extension{} } - - fakeGardenClient client.Client - fakeSeedClient client.Client - reconciler *Reconciler - - ctx = context.TODO() - - shootName = "my-shoot" - projectName = "my-project" - secretName = "my-secret" - clusterName = fmt.Sprintf("shoot--%s--%s", projectName, shootName) - controlPlaneNamespace = clusterName - projectNamespace = fmt.Sprintf("garden-%s", projectName) - secretDataJSON = []byte(fmt.Sprintf(`{"apiVersion":"v1","data":{"data":"c29tZSBzZWNyZXQgZGF0YQ=="},"kind":"Secret","metadata":{"name":"%s","namespace":"%s"}}`, secretName, controlPlaneNamespace)) - - reconcileRequest = reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: controlPlaneNamespace, - Name: shootName, - }, - } - - shootState *gardencorev1beta1.ShootState - shootStateWithExtensionData *gardencorev1beta1.ShootState - cluster *extensionsv1alpha1.Cluster - extension *extensionsv1alpha1.Extension - extensionState *runtime.RawExtension - extensionResources []gardencorev1beta1.NamedResourceReference - stateResources []gardencorev1beta1.ResourceData - secretObj *corev1.Secret - ) - - BeforeEach(func() { - gardenScheme := runtime.NewScheme() - Expect(gardencorev1beta1.AddToScheme(gardenScheme)).NotTo(HaveOccurred()) - fakeGardenClient = fake.NewClientBuilder().WithScheme(gardenScheme).Build() - - seedScheme := runtime.NewScheme() - Expect(corev1.AddToScheme(seedScheme)).NotTo(HaveOccurred()) - Expect(extensionsv1alpha1.AddToScheme(seedScheme)).NotTo(HaveOccurred()) - fakeSeedClient = fake.NewClientBuilder().WithScheme(seedScheme).Build() - - reconciler = &Reconciler{ - GardenClient: fakeGardenClient, - SeedClient: fakeSeedClient, - ObjectKind: objectKind, - NewObjectFunc: newObjectFunc, - } - - cluster = &extensionsv1alpha1.Cluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: clusterName, - }, - Spec: extensionsv1alpha1.ClusterSpec{ - Shoot: runtime.RawExtension{ - Raw: encode(&gardencorev1beta1.Shoot{ - TypeMeta: metav1.TypeMeta{ - APIVersion: gardencorev1beta1.SchemeGroupVersion.String(), - Kind: "Shoot", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: projectNamespace, - Name: shootName, - }, - }), - }, - }, - } - - shootState = &gardencorev1beta1.ShootState{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: projectNamespace, - Name: shootName, - }, - } - - secretObj = &corev1.Secret{} - Expect(json.Unmarshal(secretDataJSON, secretObj)).To(Succeed()) - - stateResources = []gardencorev1beta1.ResourceData{ - { - CrossVersionObjectReference: autoscalingv1.CrossVersionObjectReference{ - Name: secretName, - Kind: "Secret", - APIVersion: "v1", - }, - Data: runtime.RawExtension{ - Raw: secretDataJSON, - }, - }, - } - extensionState = &runtime.RawExtension{ - Raw: []byte(`{"data":"data1"}`), - } - - extensionResources = make([]gardencorev1beta1.NamedResourceReference, 0, len(stateResources)) - for _, resource := range stateResources { - extensionResources = append(extensionResources, gardencorev1beta1.NamedResourceReference{ - Name: resource.Name, - ResourceRef: resource.CrossVersionObjectReference, - }) - } - - extension = &extensionsv1alpha1.Extension{ - ObjectMeta: metav1.ObjectMeta{ - Name: shootName, - Namespace: controlPlaneNamespace, - }, - Status: extensionsv1alpha1.ExtensionStatus{ - DefaultStatus: extensionsv1alpha1.DefaultStatus{ - State: extensionState.DeepCopy(), - }, - }, - } - - shootStateWithExtensionData = &gardencorev1beta1.ShootState{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: projectNamespace, - Name: shootName, - }, - Spec: gardencorev1beta1.ShootStateSpec{ - Extensions: []gardencorev1beta1.ExtensionResourceState{ - { - Kind: extensionsv1alpha1.ExtensionResource, - Name: &shootName, - State: extensionState.DeepCopy(), - }, - }, - }, - } - }) - - Describe("#CreateShootStateSyncReconcileFunc", func() { - It("should properly update ShootState with extension state", func() { - Expect(fakeGardenClient.Create(ctx, shootState)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, extension)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, cluster)).To(Succeed()) - - _, err := reconciler.Reconcile(ctx, reconcileRequest) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeGardenClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - Expect(shootState.Spec).To(Equal(shootStateWithExtensionData.Spec)) - }) - - It("should properly update ShootState with extension state and resources", func() { - extension.Status.Resources = extensionResources - shootStateWithExtensionData.Spec.Resources = stateResources - shootStateWithExtensionData.Spec.Extensions[0].Resources = extensionResources - - Expect(fakeGardenClient.Create(ctx, shootState)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, extension)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, secretObj)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, cluster)).To(Succeed()) - - _, err := reconciler.Reconcile(ctx, reconcileRequest) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeGardenClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - Expect(shootState.Spec).To(Equal(shootStateWithExtensionData.Spec)) - }) - - It("should properly update ShootState with extension state if it was changed", func() { - extension.Status.State = &runtime.RawExtension{Raw: []byte(`{"data":"newdata"}`)} - expectedShootState := shootStateWithExtensionData.DeepCopy() - expectedShootState.Spec.Extensions[0].State = extension.Status.State - - Expect(fakeGardenClient.Create(ctx, shootStateWithExtensionData)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, extension)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, cluster)).To(Succeed()) - - _, err := reconciler.Reconcile(ctx, reconcileRequest) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeGardenClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - Expect(shootState.Spec).To(Equal(expectedShootState.Spec)) - }) - - It("should update ShootState with extension resources if they changed", func() { - extension.Status.Resources = extensionResources - shootStateWithExtensionData.Spec.Resources = stateResources - shootStateWithExtensionData.Spec.Extensions[0].Resources = extensionResources - - newSecretJSON := []byte(fmt.Sprintf(`{"apiVersion":"v1","data":{"data":"bmV3IHNlY3JldCBkYXRh"},"kind":"Secret","metadata":{"name":"%s","namespace":"%s"}}`, secretName, controlPlaneNamespace)) - newSecretObj := &corev1.Secret{} - Expect(json.Unmarshal(newSecretJSON, newSecretObj)).To(Succeed()) - expectedShootState := shootStateWithExtensionData.DeepCopy() - expectedShootState.Spec.Resources[0].Data.Raw = newSecretJSON - - Expect(fakeGardenClient.Create(ctx, shootStateWithExtensionData)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, extension)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, newSecretObj)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, cluster)).To(Succeed()) - - _, err := reconciler.Reconcile(ctx, reconcileRequest) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeGardenClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - Expect(shootState.Spec).To(Equal(expectedShootState.Spec)) - }) - - It("should remove the extension state from the ShootState if its new value is null", func() { - expectedShootState := shootState.DeepCopy() - extension.Status.State = nil - Expect(fakeGardenClient.Create(ctx, shootStateWithExtensionData)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, extension)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, cluster)).To(Succeed()) - - _, err := reconciler.Reconcile(ctx, reconcileRequest) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeGardenClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - Expect(shootState.Spec).To(Equal(expectedShootState.Spec)) - }) - - It("should delete resources which do not exist in the new extension state", func() { - expectedShootState := shootStateWithExtensionData.DeepCopy() - - shootStateWithExtensionData.Spec.Resources = stateResources - shootStateWithExtensionData.Spec.Extensions[0].Resources = extensionResources - - Expect(fakeGardenClient.Create(ctx, shootStateWithExtensionData)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, extension)).To(Succeed()) - Expect(fakeSeedClient.Create(ctx, cluster)).To(Succeed()) - - _, err := reconciler.Reconcile(ctx, reconcileRequest) - Expect(err).NotTo(HaveOccurred()) - - Expect(fakeGardenClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - Expect(shootState.Spec).To(Equal(expectedShootState.Spec)) - }) - - It("should not try to patch the ShootState if there are no changes to the extension state", func() { - ctrl := gomock.NewController(GinkgoT()) - mockClient := mockclient.NewMockClient(ctrl) - reconciler.GardenClient = mockClient - reconciler.SeedClient = mockClient - - gomock.InOrder( - mockClient.EXPECT().Get(gomock.Any(), client.ObjectKeyFromObject(extension), gomock.AssignableToTypeOf(&extensionsv1alpha1.Extension{})), - mockClient.EXPECT().Get(gomock.Any(), client.ObjectKeyFromObject(cluster), gomock.AssignableToTypeOf(&extensionsv1alpha1.Cluster{})).SetArg(2, *cluster), - mockClient.EXPECT().Get(gomock.Any(), client.ObjectKeyFromObject(shootState), gomock.AssignableToTypeOf(&gardencorev1beta1.ShootState{})), - ) - - _, err := reconciler.Reconcile(ctx, reconcileRequest) - Expect(err).NotTo(HaveOccurred()) - - ctrl.Finish() - }) - - It("should not throw any errors if Cluster resource does not exists", func() { - Expect(fakeSeedClient.Create(ctx, extension)).To(Succeed()) - - _, err := reconciler.Reconcile(ctx, reconcileRequest) - Expect(err).NotTo(HaveOccurred()) - }) - }) -}) - -func encode(obj runtime.Object) []byte { - out, _ := json.Marshal(obj) - return out -} diff --git a/pkg/gardenlet/controller/shootstate/secret/add.go b/pkg/gardenlet/controller/shootstate/secret/add.go deleted file mode 100644 index ca4faac5777..00000000000 --- a/pkg/gardenlet/controller/shootstate/secret/add.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 secret - -import ( - corev1 "k8s.io/api/core/v1" - "k8s.io/client-go/util/workqueue" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/cluster" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/predicate" - - secretsmanager "github.com/gardener/gardener/pkg/utils/secrets/manager" -) - -// ControllerName is the name of this controller. -const ControllerName = "shootstate-secret" - -// AddToManager adds Reconciler to the given manager. -func (r *Reconciler) AddToManager(mgr manager.Manager, gardenCluster, seedCluster cluster.Cluster) error { - if r.GardenClient == nil { - r.GardenClient = gardenCluster.GetClient() - } - if r.SeedClient == nil { - r.SeedClient = seedCluster.GetClient() - } - - return builder. - ControllerManagedBy(mgr). - Named(ControllerName). - For(&corev1.Secret{}, builder.WithPredicates(r.SecretPredicate())). - WithOptions(controller.Options{ - MaxConcurrentReconciles: *r.Config.ConcurrentSyncs, - RateLimiter: workqueue.DefaultControllerRateLimiter(), - }). - Complete(r) -} - -// SecretPredicate returns the predicates for the secret watch. -func (r *Reconciler) SecretPredicate() predicate.Predicate { - return predicate.NewPredicateFuncs(func(object client.Object) bool { - labels := object.GetLabels() - return labels[secretsmanager.LabelKeyManagedBy] == secretsmanager.LabelValueSecretsManager && - labels[secretsmanager.LabelKeyPersist] == secretsmanager.LabelValueTrue - }) -} diff --git a/pkg/gardenlet/controller/shootstate/secret/add_test.go b/pkg/gardenlet/controller/shootstate/secret/add_test.go deleted file mode 100644 index bd6933b7828..00000000000 --- a/pkg/gardenlet/controller/shootstate/secret/add_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 secret_test - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" - - . "github.com/gardener/gardener/pkg/gardenlet/controller/shootstate/secret" - secretsmanager "github.com/gardener/gardener/pkg/utils/secrets/manager" -) - -var _ = Describe("Add", func() { - var reconciler *Reconciler - - BeforeEach(func() { - reconciler = &Reconciler{} - }) - - Describe("SecretPredicate", func() { - var ( - p predicate.Predicate - secret *corev1.Secret - ) - - BeforeEach(func() { - p = reconciler.SecretPredicate() - secret = &corev1.Secret{} - }) - - It("should return false if required label is not present on secret", func() { - Expect(p.Create(event.CreateEvent{Object: secret})).To(BeFalse()) - Expect(p.Update(event.UpdateEvent{ObjectNew: secret})).To(BeFalse()) - Expect(p.Delete(event.DeleteEvent{Object: secret})).To(BeFalse()) - }) - - It("should return true if required label is present on secret", func() { - secret.ObjectMeta = metav1.ObjectMeta{ - Labels: map[string]string{ - secretsmanager.LabelKeyManagedBy: secretsmanager.LabelValueSecretsManager, - secretsmanager.LabelKeyPersist: secretsmanager.LabelValueTrue, - }, - } - - Expect(p.Create(event.CreateEvent{Object: secret})).To(BeTrue()) - Expect(p.Update(event.UpdateEvent{ObjectNew: secret})).To(BeTrue()) - Expect(p.Delete(event.DeleteEvent{Object: secret})).To(BeTrue()) - }) - }) -}) diff --git a/pkg/gardenlet/controller/shootstate/secret/reconciler.go b/pkg/gardenlet/controller/shootstate/secret/reconciler.go deleted file mode 100644 index 14126c815f7..00000000000 --- a/pkg/gardenlet/controller/shootstate/secret/reconciler.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 secret - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" - apiequality "k8s.io/apimachinery/pkg/api/equality" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants" - v1beta1helper "github.com/gardener/gardener/pkg/apis/core/v1beta1/helper" - "github.com/gardener/gardener/pkg/controllerutils" - "github.com/gardener/gardener/pkg/extensions" - "github.com/gardener/gardener/pkg/gardenlet/apis/config" - kubernetesutils "github.com/gardener/gardener/pkg/utils/kubernetes" -) - -const finalizerName = "gardenlet.gardener.cloud/secret-controller" - -// Reconciler reconciles secrets in seed. -type Reconciler struct { - GardenClient client.Client - SeedClient client.Client - Config config.ShootSecretControllerConfiguration -} - -// Reconcile reconciles `Secret`s having labels `managed-by=secrets-manager` and `persist=true` in -// the shoot namespaces in the seed cluster. -// It syncs them to the `ShootState` so that the secrets can be restored from there in case a shoot -// control plane has to be restored to another seed cluster (in case of migration). -func (r *Reconciler) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) { - log := logf.FromContext(ctx) - - ctx, cancel := controllerutils.GetMainReconciliationContext(ctx, controllerutils.DefaultReconciliationTimeout) - defer cancel() - - secret := &corev1.Secret{} - if err := r.SeedClient.Get(ctx, request.NamespacedName, secret); err != nil { - if apierrors.IsNotFound(err) { - log.V(1).Info("Object is gone, stop reconciling") - return reconcile.Result{}, nil - } - return reconcile.Result{}, fmt.Errorf("error retrieving object from store: %w", err) - } - - namespace := &corev1.Namespace{} - if err := r.SeedClient.Get(ctx, kubernetesutils.Key(secret.Namespace), namespace); err != nil { - return reconcile.Result{}, err - } - if namespace.Labels[v1beta1constants.GardenRole] != v1beta1constants.GardenRoleShoot { - return reconcile.Result{}, nil - } - - shootState, shoot, err := extensions.GetShootStateForCluster(ctx, r.GardenClient, r.SeedClient, secret.Namespace) - if err != nil { - if apierrors.IsNotFound(err) { - if controllerutil.ContainsFinalizer(secret, finalizerName) { - log.Info("Removing finalizer") - if err := controllerutils.RemoveFinalizers(ctx, r.SeedClient, secret, finalizerName); err != nil { - return reconcile.Result{}, fmt.Errorf("failed to remove finalizer: %w", err) - } - } - return reconcile.Result{}, nil - } - return reconcile.Result{}, err - } - - if secret.DeletionTimestamp != nil { - return r.delete(ctx, log, secret, shootState, shoot) - } - return r.reconcile(ctx, log, secret, shootState) -} - -func (r *Reconciler) reconcile( - ctx context.Context, - log logr.Logger, - secret *corev1.Secret, - shootState *gardencorev1beta1.ShootState, -) ( - reconcile.Result, - error, -) { - log.Info("Reconciling secret information in ShootState and ensuring its finalizer") - - if !controllerutil.ContainsFinalizer(secret, finalizerName) { - log.Info("Adding finalizer") - if err := controllerutils.AddFinalizers(ctx, r.SeedClient, secret, finalizerName); err != nil { - return reconcile.Result{}, fmt.Errorf("failed to add finalizer: %w", err) - } - } - - dataJSON, err := json.Marshal(secret.Data) - if err != nil { - return reconcile.Result{}, err - } - - patch := client.StrategicMergeFrom(shootState.DeepCopy()) - - dataList := v1beta1helper.GardenerResourceDataList(shootState.Spec.Gardener) - oldSecretData := dataList.Get(secret.Name).DeepCopy() - dataList.Upsert(&gardencorev1beta1.GardenerResourceData{ - Name: secret.Name, - Labels: secret.Labels, - Type: "secret", - Data: runtime.RawExtension{Raw: dataJSON}, - }) - newSecretData := dataList.Get(secret.Name) - - // If the secret data did not change, do not even try to send an empty PATCH request. - if apiequality.Semantic.DeepEqual(oldSecretData, newSecretData) { - return reconcile.Result{}, nil - } - - shootState.Spec.Gardener = dataList - return reconcile.Result{}, r.GardenClient.Patch(ctx, shootState, patch) -} - -func (r *Reconciler) delete( - ctx context.Context, - log logr.Logger, - secret *corev1.Secret, - shootState *gardencorev1beta1.ShootState, - shoot *gardencorev1beta1.Shoot, -) ( - reconcile.Result, - error, -) { - if lastOp := shoot.Status.LastOperation; lastOp != nil && lastOp.Type == gardencorev1beta1.LastOperationTypeMigrate { - log.Info("Keeping Secret in ShootState since Shoot is in migration but releasing the finalizer") - } else { - log.Info("Removing Secret from ShootState and releasing its finalizer") - - patch := client.StrategicMergeFrom(shootState.DeepCopy()) - - dataList := v1beta1helper.GardenerResourceDataList(shootState.Spec.Gardener) - dataList.Delete(secret.Name) - shootState.Spec.Gardener = dataList - - if err := r.GardenClient.Patch(ctx, shootState, patch); err != nil { - return reconcile.Result{}, err - } - } - - if controllerutil.ContainsFinalizer(secret, finalizerName) { - log.Info("Removing finalizer") - if err := controllerutils.RemoveFinalizers(ctx, r.SeedClient, secret, finalizerName); err != nil { - return reconcile.Result{}, fmt.Errorf("failed to remove finalizer: %w", err) - } - } - - return reconcile.Result{}, nil -} diff --git a/pkg/gardenlet/controller/shootstate/secret/secret_suite_test.go b/pkg/gardenlet/controller/shootstate/secret/secret_suite_test.go deleted file mode 100644 index 3a7c30a7ce2..00000000000 --- a/pkg/gardenlet/controller/shootstate/secret/secret_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 secret_test - -import ( - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" -) - -func TestSecret(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Gardenlet Controller ShootState Secret Suite") -} diff --git a/pkg/operation/botanist/backupentry.go b/pkg/operation/botanist/backupentry.go index b2a0a8a8e51..4cf64cd49f5 100644 --- a/pkg/operation/botanist/backupentry.go +++ b/pkg/operation/botanist/backupentry.go @@ -51,8 +51,8 @@ func (b *Botanist) DefaultCoreBackupEntry() corebackupentry.Interface { // DeployBackupEntry deploys the BackupEntry resource in the Garden cluster and triggers the restore operation in case // the Shoot is in the restore phase of the control plane migration. func (b *Botanist) DeployBackupEntry(ctx context.Context) error { - if b.isRestorePhase() { - return b.Shoot.Components.BackupEntry.Restore(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return b.Shoot.Components.BackupEntry.Restore(ctx, b.Shoot.GetShootState()) } return b.Shoot.Components.BackupEntry.Deploy(ctx) } @@ -95,7 +95,7 @@ func (b *Botanist) DeploySourceBackupEntry(ctx context.Context) error { // DestroySourceBackupEntry destroys the source BackupEntry. It returns nil if the // Seed backup is not enabled or the Shoot is not in restore phase. func (b *Botanist) DestroySourceBackupEntry(ctx context.Context) error { - if b.Seed.GetInfo().Spec.Backup == nil || !b.isRestorePhase() { + if b.Seed.GetInfo().Spec.Backup == nil || !b.IsRestorePhase() { return nil } diff --git a/pkg/operation/botanist/containerruntime.go b/pkg/operation/botanist/containerruntime.go index 33c1956e820..e7938f35619 100644 --- a/pkg/operation/botanist/containerruntime.go +++ b/pkg/operation/botanist/containerruntime.go @@ -38,8 +38,8 @@ func (b *Botanist) DefaultContainerRuntime() containerruntime.Interface { // DeployContainerRuntime deploys the ContainerRuntime custom resources and triggers the restore operation in case // the Shoot is in the restore phase of the control plane migration func (b *Botanist) DeployContainerRuntime(ctx context.Context) error { - if b.isRestorePhase() { - return b.Shoot.Components.Extensions.ContainerRuntime.Restore(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return b.Shoot.Components.Extensions.ContainerRuntime.Restore(ctx, b.Shoot.GetShootState()) } return b.Shoot.Components.Extensions.ContainerRuntime.Deploy(ctx) } diff --git a/pkg/operation/botanist/containerruntime_test.go b/pkg/operation/botanist/containerruntime_test.go index 0528f194b6d..60ae15f32c3 100644 --- a/pkg/operation/botanist/containerruntime_test.go +++ b/pkg/operation/botanist/containerruntime_test.go @@ -52,7 +52,7 @@ var _ = Describe("ContainerRuntime", func() { }, }, }} - botanist.SetShootState(shootState) + botanist.Shoot.SetShootState(shootState) botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{}) }) diff --git a/pkg/operation/botanist/controlplane.go b/pkg/operation/botanist/controlplane.go index f335a60fe6d..5c45202725c 100644 --- a/pkg/operation/botanist/controlplane.go +++ b/pkg/operation/botanist/controlplane.go @@ -192,8 +192,8 @@ func (b *Botanist) DeployControlPlaneExposure(ctx context.Context) error { } func (b *Botanist) deployOrRestoreControlPlane(ctx context.Context, controlPlane extensionscontrolplane.Interface) error { - if b.isRestorePhase() { - return controlPlane.Restore(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return controlPlane.Restore(ctx, b.Shoot.GetShootState()) } return controlPlane.Deploy(ctx) } @@ -201,7 +201,7 @@ func (b *Botanist) deployOrRestoreControlPlane(ctx context.Context, controlPlane // RestoreControlPlane restores the ControlPlane custom resource (purpose normal) func (b *Botanist) RestoreControlPlane(ctx context.Context) error { b.Shoot.Components.Extensions.ControlPlane.SetInfrastructureProviderStatus(b.Shoot.Components.Extensions.Infrastructure.ProviderStatus()) - return b.Shoot.Components.Extensions.ControlPlane.Restore(ctx, b.GetShootState()) + return b.Shoot.Components.Extensions.ControlPlane.Restore(ctx, b.Shoot.GetShootState()) } // RestartControlPlanePods restarts (deletes) pods of the shoot control plane. diff --git a/pkg/operation/botanist/controlplane_test.go b/pkg/operation/botanist/controlplane_test.go index f69916a67f3..6a948930135 100644 --- a/pkg/operation/botanist/controlplane_test.go +++ b/pkg/operation/botanist/controlplane_test.go @@ -101,7 +101,7 @@ var _ = Describe("controlplane", func() { var shootState = &gardencorev1beta1.ShootState{} BeforeEach(func() { - botanist.SetShootState(shootState) + botanist.Shoot.SetShootState(shootState) botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{ Status: gardencorev1beta1.ShootStatus{ LastOperation: &gardencorev1beta1.LastOperation{ @@ -140,7 +140,7 @@ var _ = Describe("controlplane", func() { var shootState = &gardencorev1beta1.ShootState{} BeforeEach(func() { - botanist.SetShootState(shootState) + botanist.Shoot.SetShootState(shootState) botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{ Status: gardencorev1beta1.ShootStatus{ LastOperation: &gardencorev1beta1.LastOperation{ diff --git a/pkg/operation/botanist/dnsrecord.go b/pkg/operation/botanist/dnsrecord.go index 1261c7547e4..627d74a73b8 100644 --- a/pkg/operation/botanist/dnsrecord.go +++ b/pkg/operation/botanist/dnsrecord.go @@ -31,7 +31,7 @@ func (b *Botanist) DefaultExternalDNSRecord() extensionsdnsrecord.Interface { SecretName: DNSRecordSecretPrefix + "-" + b.Shoot.GetInfo().Name + "-" + v1beta1constants.DNSRecordExternalName, Namespace: b.Shoot.SeedNamespace, TTL: b.Config.Controllers.Shoot.DNSEntryTTLSeconds, - AnnotateOperation: controllerutils.HasTask(b.Shoot.GetInfo().Annotations, v1beta1constants.ShootTaskDeployDNSRecordExternal) || b.isRestorePhase(), + AnnotateOperation: controllerutils.HasTask(b.Shoot.GetInfo().Annotations, v1beta1constants.ShootTaskDeployDNSRecordExternal) || b.IsRestorePhase(), } if b.NeedsExternalDNS() { @@ -63,7 +63,7 @@ func (b *Botanist) DefaultInternalDNSRecord() extensionsdnsrecord.Interface { ReconcileOnlyOnChangeOrError: b.Shoot.GetInfo().DeletionTimestamp != nil, AnnotateOperation: b.Shoot.GetInfo().DeletionTimestamp != nil || controllerutils.HasTask(b.Shoot.GetInfo().Annotations, v1beta1constants.ShootTaskDeployDNSRecordInternal) || - b.isRestorePhase(), + b.IsRestorePhase(), } if b.NeedsInternalDNS() { @@ -150,8 +150,8 @@ func (b *Botanist) MigrateInternalDNSRecord(ctx context.Context) error { } func (b *Botanist) deployOrRestoreDNSRecord(ctx context.Context, dnsRecord component.DeployMigrateWaiter) error { - if b.isRestorePhase() { - return dnsRecord.Restore(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return dnsRecord.Restore(ctx, b.Shoot.GetShootState()) } return dnsRecord.Deploy(ctx) } diff --git a/pkg/operation/botanist/dnsrecord_test.go b/pkg/operation/botanist/dnsrecord_test.go index 09ffa532236..bc61bbde82c 100644 --- a/pkg/operation/botanist/dnsrecord_test.go +++ b/pkg/operation/botanist/dnsrecord_test.go @@ -424,7 +424,7 @@ var _ = Describe("dnsrecord", func() { var shootState = &gardencorev1beta1.ShootState{} JustBeforeEach(func() { - b.SetShootState(shootState) + b.Shoot.SetShootState(shootState) b.Shoot.GetInfo().Status = gardencorev1beta1.ShootStatus{ LastOperation: &gardencorev1beta1.LastOperation{ Type: gardencorev1beta1.LastOperationTypeRestore, @@ -484,7 +484,7 @@ var _ = Describe("dnsrecord", func() { var shootState = &gardencorev1beta1.ShootState{} JustBeforeEach(func() { - b.SetShootState(shootState) + b.Shoot.SetShootState(shootState) b.Shoot.GetInfo().Status = gardencorev1beta1.ShootStatus{ LastOperation: &gardencorev1beta1.LastOperation{ Type: gardencorev1beta1.LastOperationTypeRestore, diff --git a/pkg/operation/botanist/etcd.go b/pkg/operation/botanist/etcd.go index 33857e97afe..3c2381d64cc 100644 --- a/pkg/operation/botanist/etcd.go +++ b/pkg/operation/botanist/etcd.go @@ -205,7 +205,7 @@ func (b *Botanist) deployOrRestoreMainEtcd(ctx context.Context) error { } func (b *Botanist) isRestorationOfMultiNodeMainEtcdRequired(ctx context.Context) (bool, error) { - if !b.isRestorePhase() || !v1beta1helper.IsHAControlPlaneConfigured(b.Shoot.GetInfo()) { + if !b.IsRestorePhase() || !v1beta1helper.IsHAControlPlaneConfigured(b.Shoot.GetInfo()) { return false, nil } diff --git a/pkg/operation/botanist/extension.go b/pkg/operation/botanist/extension.go index ebe9da35d44..6ce064e24f0 100644 --- a/pkg/operation/botanist/extension.go +++ b/pkg/operation/botanist/extension.go @@ -54,8 +54,8 @@ func (b *Botanist) DefaultExtension(ctx context.Context) (extension.Interface, e // DeployExtensionsAfterKubeAPIServer deploys the Extension custom resources and triggers the restore operation in case // the Shoot is in the restore phase of the control plane migration. func (b *Botanist) DeployExtensionsAfterKubeAPIServer(ctx context.Context) error { - if b.isRestorePhase() { - return b.Shoot.Components.Extensions.Extension.RestoreAfterKubeAPIServer(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return b.Shoot.Components.Extensions.Extension.RestoreAfterKubeAPIServer(ctx, b.Shoot.GetShootState()) } return b.Shoot.Components.Extensions.Extension.DeployAfterKubeAPIServer(ctx) } @@ -63,8 +63,8 @@ func (b *Botanist) DeployExtensionsAfterKubeAPIServer(ctx context.Context) error // DeployExtensionsBeforeKubeAPIServer deploys the Extension custom resources and triggers the restore operation in case // the Shoot is in the restore phase of the control plane migration. func (b *Botanist) DeployExtensionsBeforeKubeAPIServer(ctx context.Context) error { - if b.isRestorePhase() { - return b.Shoot.Components.Extensions.Extension.RestoreBeforeKubeAPIServer(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return b.Shoot.Components.Extensions.Extension.RestoreBeforeKubeAPIServer(ctx, b.Shoot.GetShootState()) } return b.Shoot.Components.Extensions.Extension.DeployBeforeKubeAPIServer(ctx) } diff --git a/pkg/operation/botanist/extension_test.go b/pkg/operation/botanist/extension_test.go index 946fcd4aec6..fcf25170f67 100644 --- a/pkg/operation/botanist/extension_test.go +++ b/pkg/operation/botanist/extension_test.go @@ -69,7 +69,7 @@ var _ = Describe("Extensions", func() { SeedNamespace: namespace, }, }} - botanist.SetShootState(shootState) + botanist.Shoot.SetShootState(shootState) botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{}) }) diff --git a/pkg/operation/botanist/infrastructure.go b/pkg/operation/botanist/infrastructure.go index f0a3306e796..99486ccee5a 100644 --- a/pkg/operation/botanist/infrastructure.go +++ b/pkg/operation/botanist/infrastructure.go @@ -38,7 +38,7 @@ func (b *Botanist) DefaultInfrastructure() infrastructure.Interface { Type: b.Shoot.GetInfo().Spec.Provider.Type, ProviderConfig: b.Shoot.GetInfo().Spec.Provider.InfrastructureConfig, Region: b.Shoot.GetInfo().Spec.Region, - AnnotateOperation: controllerutils.HasTask(b.Shoot.GetInfo().Annotations, v1beta1constants.ShootTaskDeployInfrastructure) || b.isRestorePhase(), + AnnotateOperation: controllerutils.HasTask(b.Shoot.GetInfo().Annotations, v1beta1constants.ShootTaskDeployInfrastructure) || b.IsRestorePhase(), }, infrastructure.DefaultInterval, infrastructure.DefaultSevereThreshold, @@ -57,8 +57,8 @@ func (b *Botanist) DeployInfrastructure(ctx context.Context) error { b.Shoot.Components.Extensions.Infrastructure.SetSSHPublicKey(sshKeypairSecret.Data[secrets.DataKeySSHAuthorizedKeys]) } - if b.isRestorePhase() { - return b.Shoot.Components.Extensions.Infrastructure.Restore(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return b.Shoot.Components.Extensions.Infrastructure.Restore(ctx, b.Shoot.GetShootState()) } return b.Shoot.Components.Extensions.Infrastructure.Deploy(ctx) diff --git a/pkg/operation/botanist/infrastructure_test.go b/pkg/operation/botanist/infrastructure_test.go index 393214b2660..091a80dd63e 100644 --- a/pkg/operation/botanist/infrastructure_test.go +++ b/pkg/operation/botanist/infrastructure_test.go @@ -78,7 +78,7 @@ var _ = Describe("Infrastructure", func() { }, }, } - botanist.SetShootState(shootState) + botanist.Shoot.SetShootState(shootState) botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{ Spec: gardencorev1beta1.ShootSpec{ Provider: gardencorev1beta1.Provider{ diff --git a/pkg/operation/botanist/kubeapiserver_test.go b/pkg/operation/botanist/kubeapiserver_test.go index 46596a0d894..d2984a83a68 100644 --- a/pkg/operation/botanist/kubeapiserver_test.go +++ b/pkg/operation/botanist/kubeapiserver_test.go @@ -164,7 +164,7 @@ var _ = Describe("KubeAPIServer", func() { TechnicalID: seedNamespace, }, }) - botanist.SetShootState(&gardencorev1beta1.ShootState{}) + botanist.Shoot.SetShootState(&gardencorev1beta1.ShootState{}) botanist.Seed.SetInfo(&gardencorev1beta1.Seed{ Spec: gardencorev1beta1.SeedSpec{ diff --git a/pkg/operation/botanist/migration.go b/pkg/operation/botanist/migration.go index 8efadd96b36..9dc0aa781f8 100644 --- a/pkg/operation/botanist/migration.go +++ b/pkg/operation/botanist/migration.go @@ -21,6 +21,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + v1beta1helper "github.com/gardener/gardener/pkg/apis/core/v1beta1/helper" "github.com/gardener/gardener/pkg/component" "github.com/gardener/gardener/pkg/utils/flow" ) @@ -74,7 +75,7 @@ func (b *Botanist) runParallelTaskForEachComponent(ctx context.Context, componen // IsCopyOfBackupsRequired check if etcd backups need to be copied between seeds. func (b *Botanist) IsCopyOfBackupsRequired(ctx context.Context) (bool, error) { - if b.Seed.GetInfo().Spec.Backup == nil || !b.isRestorePhase() { + if b.Seed.GetInfo().Spec.Backup == nil || !b.IsRestorePhase() { return false, nil } @@ -111,9 +112,7 @@ func (b *Botanist) IsCopyOfBackupsRequired(ctx context.Context) (bool, error) { return true, nil } -func (b *Botanist) isRestorePhase() bool { - return b.Shoot != nil && - b.Shoot.GetInfo() != nil && - b.Shoot.GetInfo().Status.LastOperation != nil && - b.Shoot.GetInfo().Status.LastOperation.Type == gardencorev1beta1.LastOperationTypeRestore +// IsRestorePhase returns true when the shoot is in phase 'restore'. +func (b *Botanist) IsRestorePhase() bool { + return v1beta1helper.ShootHasOperationType(b.Shoot.GetInfo().Status.LastOperation, gardencorev1beta1.LastOperationTypeRestore) } diff --git a/pkg/operation/botanist/migration_test.go b/pkg/operation/botanist/migration_test.go index a5a511e022b..f14866c06ff 100644 --- a/pkg/operation/botanist/migration_test.go +++ b/pkg/operation/botanist/migration_test.go @@ -27,7 +27,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "github.com/gardener/gardener/pkg/apis/core/v1beta1" + gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" mockbackupentry "github.com/gardener/gardener/pkg/component/backupentry/mock" mocketcd "github.com/gardener/gardener/pkg/component/etcd/mock" mockcontainerruntime "github.com/gardener/gardener/pkg/component/extensions/containerruntime/mock" @@ -185,26 +185,26 @@ var _ = Describe("migration", func() { ) BeforeEach(func() { - botanist.Shoot.SetInfo(&v1beta1.Shoot{ + botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{ ObjectMeta: metav1.ObjectMeta{ Name: "bar", Namespace: "foo", }, - Status: v1beta1.ShootStatus{ - LastOperation: &v1beta1.LastOperation{ - Type: v1beta1.LastOperationTypeRestore, + Status: gardencorev1beta1.ShootStatus{ + LastOperation: &gardencorev1beta1.LastOperation{ + Type: gardencorev1beta1.LastOperationTypeRestore, }, }, }) botanist.Seed = &seed.Seed{} - botanist.Seed.SetInfo(&v1beta1.Seed{ + botanist.Seed.SetInfo(&gardencorev1beta1.Seed{ ObjectMeta: metav1.ObjectMeta{ Name: "seed", Namespace: "garden", UID: "new-seed", }, - Spec: v1beta1.SeedSpec{ - Backup: &v1beta1.SeedBackup{ + Spec: gardencorev1beta1.SeedSpec{ + Backup: &gardencorev1beta1.SeedBackup{ Provider: "gcp", }, }, @@ -221,7 +221,7 @@ var _ = Describe("migration", func() { }) It("should return false if lastOperation is not restore", func() { - botanist.Shoot.GetInfo().Status.LastOperation.Type = v1beta1.LastOperationTypeReconcile + botanist.Shoot.GetInfo().Status.LastOperation.Type = gardencorev1beta1.LastOperationTypeReconcile copyRequired, err := botanist.IsCopyOfBackupsRequired(ctx) Expect(err).NotTo(HaveOccurred()) Expect(copyRequired).To(BeFalse()) @@ -279,8 +279,8 @@ var _ = Describe("migration", func() { }) It("should return true if backupentry.Spec.BucketName has not been switched to the new seed", func() { - backupEntry.EXPECT().Get(ctx).Return(&v1beta1.BackupEntry{ - Spec: v1beta1.BackupEntrySpec{ + backupEntry.EXPECT().Get(ctx).Return(&gardencorev1beta1.BackupEntry{ + Spec: gardencorev1beta1.BackupEntrySpec{ BucketName: "old-seed", }, }, nil) @@ -293,8 +293,8 @@ var _ = Describe("migration", func() { Context("Last operation is restore, etcd-main resource exists and backupentry.Spec.BucketName is switched to the new seed", func() { BeforeEach(func() { etcdMain.EXPECT().Get(ctx).Return(nil, apierrors.NewNotFound(schema.GroupResource{}, "etcd-main")) - backupEntry.EXPECT().Get(ctx).Return(&v1beta1.BackupEntry{ - Spec: v1beta1.BackupEntrySpec{ + backupEntry.EXPECT().Get(ctx).Return(&gardencorev1beta1.BackupEntry{ + Spec: gardencorev1beta1.BackupEntrySpec{ BucketName: string(botanist.Seed.GetInfo().UID), }, }, nil) @@ -316,8 +316,8 @@ var _ = Describe("migration", func() { }) It("should return an error if source backupentry and destination backupentry point to the same bucket", func() { - sourceBackupEntry.EXPECT().Get(ctx).Return(&v1beta1.BackupEntry{ - Spec: v1beta1.BackupEntrySpec{ + sourceBackupEntry.EXPECT().Get(ctx).Return(&gardencorev1beta1.BackupEntry{ + Spec: gardencorev1beta1.BackupEntrySpec{ BucketName: string(botanist.Seed.GetInfo().UID), }, }, nil) @@ -327,8 +327,8 @@ var _ = Describe("migration", func() { }) It("should return true if source backupentry and destination backupentry point to different buckets", func() { - sourceBackupEntry.EXPECT().Get(ctx).Return(&v1beta1.BackupEntry{ - Spec: v1beta1.BackupEntrySpec{ + sourceBackupEntry.EXPECT().Get(ctx).Return(&gardencorev1beta1.BackupEntry{ + Spec: gardencorev1beta1.BackupEntrySpec{ BucketName: "old-seed", }, }, nil) @@ -338,4 +338,16 @@ var _ = Describe("migration", func() { }) }) }) + + Describe("#IsRestorePhase", func() { + It("should return true", func() { + botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{Status: gardencorev1beta1.ShootStatus{LastOperation: &gardencorev1beta1.LastOperation{Type: gardencorev1beta1.LastOperationTypeRestore}}}) + Expect(botanist.IsRestorePhase()).To(BeTrue()) + }) + + It("should return false", func() { + botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{Status: gardencorev1beta1.ShootStatus{LastOperation: &gardencorev1beta1.LastOperation{Type: gardencorev1beta1.LastOperationTypeCreate}}}) + Expect(botanist.IsRestorePhase()).To(BeFalse()) + }) + }) }) diff --git a/pkg/operation/botanist/network.go b/pkg/operation/botanist/network.go index f86e4fd4c74..d2e5ba17491 100644 --- a/pkg/operation/botanist/network.go +++ b/pkg/operation/botanist/network.go @@ -43,8 +43,8 @@ func (b *Botanist) DefaultNetwork() component.DeployMigrateWaiter { // DeployNetwork deploys the Network custom resource and triggers the restore operation in case // the Shoot is in the restore phase of the control plane migration func (b *Botanist) DeployNetwork(ctx context.Context) error { - if b.isRestorePhase() { - return b.Shoot.Components.Extensions.Network.Restore(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return b.Shoot.Components.Extensions.Network.Restore(ctx, b.Shoot.GetShootState()) } return b.Shoot.Components.Extensions.Network.Deploy(ctx) diff --git a/pkg/operation/botanist/network_test.go b/pkg/operation/botanist/network_test.go index 4cec8c2cab2..2ae8867b3ec 100644 --- a/pkg/operation/botanist/network_test.go +++ b/pkg/operation/botanist/network_test.go @@ -52,7 +52,7 @@ var _ = Describe("Network", func() { }, }, }} - botanist.SetShootState(shootState) + botanist.Shoot.SetShootState(shootState) botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{}) }) diff --git a/pkg/operation/botanist/nginxingress.go b/pkg/operation/botanist/nginxingress.go index a088d818f92..758836053cc 100644 --- a/pkg/operation/botanist/nginxingress.go +++ b/pkg/operation/botanist/nginxingress.go @@ -103,7 +103,7 @@ func (b *Botanist) DefaultIngressDNSRecord() extensionsdnsrecord.Interface { SecretName: DNSRecordSecretPrefix + "-" + b.Shoot.GetInfo().Name + "-" + v1beta1constants.DNSRecordExternalName, Namespace: b.Shoot.SeedNamespace, TTL: b.Config.Controllers.Shoot.DNSEntryTTLSeconds, - AnnotateOperation: controllerutils.HasTask(b.Shoot.GetInfo().Annotations, v1beta1constants.ShootTaskDeployDNSRecordIngress) || b.isRestorePhase(), + AnnotateOperation: controllerutils.HasTask(b.Shoot.GetInfo().Annotations, v1beta1constants.ShootTaskDeployDNSRecordIngress) || b.IsRestorePhase(), } // Set component values even if the nginx-ingress addons is not enabled. diff --git a/pkg/operation/botanist/nginxingress_test.go b/pkg/operation/botanist/nginxingress_test.go index fbcefb0075a..38a4c8acd0b 100644 --- a/pkg/operation/botanist/nginxingress_test.go +++ b/pkg/operation/botanist/nginxingress_test.go @@ -382,7 +382,7 @@ var _ = Describe("NginxIngress", func() { var shootState = &gardencorev1beta1.ShootState{} BeforeEach(func() { - b.SetShootState(shootState) + b.Shoot.SetShootState(shootState) b.Shoot.GetInfo().Status = gardencorev1beta1.ShootStatus{ LastOperation: &gardencorev1beta1.LastOperation{ Type: gardencorev1beta1.LastOperationTypeRestore, diff --git a/pkg/operation/botanist/operatingsystemconfig.go b/pkg/operation/botanist/operatingsystemconfig.go index 7b1adf48802..f572e29ae68 100644 --- a/pkg/operation/botanist/operatingsystemconfig.go +++ b/pkg/operation/botanist/operatingsystemconfig.go @@ -116,8 +116,8 @@ func (b *Botanist) DeployOperatingSystemConfig(ctx context.Context) error { b.Shoot.Components.Extensions.OperatingSystemConfig.SetSSHPublicKeys(publicKeys) } - if b.isRestorePhase() { - return b.Shoot.Components.Extensions.OperatingSystemConfig.Restore(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return b.Shoot.Components.Extensions.OperatingSystemConfig.Restore(ctx, b.Shoot.GetShootState()) } return b.Shoot.Components.Extensions.OperatingSystemConfig.Deploy(ctx) diff --git a/pkg/operation/botanist/operatingsystemconfig_test.go b/pkg/operation/botanist/operatingsystemconfig_test.go index c4ae5ad8dc8..eefe0f42f12 100644 --- a/pkg/operation/botanist/operatingsystemconfig_test.go +++ b/pkg/operation/botanist/operatingsystemconfig_test.go @@ -101,7 +101,7 @@ var _ = Describe("operatingsystemconfig", func() { Seed: &seedpkg.Seed{}, }, } - botanist.SetShootState(shootState) + botanist.Shoot.SetShootState(shootState) botanist.Seed.SetInfo(&gardencorev1beta1.Seed{ Spec: gardencorev1beta1.SeedSpec{ Ingress: &gardencorev1beta1.Ingress{ diff --git a/pkg/operation/botanist/secrets.go b/pkg/operation/botanist/secrets.go index 76d366ad673..ff2ef909de6 100644 --- a/pkg/operation/botanist/secrets.go +++ b/pkg/operation/botanist/secrets.go @@ -49,7 +49,7 @@ func (b *Botanist) InitializeSecretsManagement(ctx context.Context) error { // create corresponding secrets in the shoot namespace in the seed before initializing it. Note that this is // explicitly only done in case of restoration to prevent split-brain situations as described in // https://github.com/gardener/gardener/issues/5377. - if b.isRestorePhase() { + if b.IsRestorePhase() { if err := b.restoreSecretsFromShootStateForSecretsManagerAdoption(ctx); err != nil { return err } @@ -111,7 +111,7 @@ func (b *Botanist) lastSecretRotationStartTimes() map[string]time.Time { func (b *Botanist) restoreSecretsFromShootStateForSecretsManagerAdoption(ctx context.Context) error { var fns []flow.TaskFn - for _, v := range b.GetShootState().Spec.Gardener { + for _, v := range b.Shoot.GetShootState().Spec.Gardener { entry := v if entry.Labels[secretsmanager.LabelKeyManagedBy] != secretsmanager.LabelValueSecretsManager || diff --git a/pkg/operation/botanist/secrets_test.go b/pkg/operation/botanist/secrets_test.go index afaef44c425..1c17aa67ae4 100644 --- a/pkg/operation/botanist/secrets_test.go +++ b/pkg/operation/botanist/secrets_test.go @@ -105,7 +105,7 @@ var _ = Describe("Secrets", func() { }, }, }) - botanist.SetShootState(&gardencorev1beta1.ShootState{}) + botanist.Shoot.SetShootState(&gardencorev1beta1.ShootState{}) }) Describe("#InitializeSecretsManagement", func() { @@ -225,7 +225,7 @@ var _ = Describe("Secrets", func() { }) It("should restore all secrets from the shootstate", func() { - botanist.SetShootState(&gardencorev1beta1.ShootState{ + botanist.Shoot.SetShootState(&gardencorev1beta1.ShootState{ Spec: gardencorev1beta1.ShootStateSpec{ Gardener: []gardencorev1beta1.GardenerResourceData{ { diff --git a/pkg/operation/botanist/shootstate.go b/pkg/operation/botanist/shootstate.go deleted file mode 100644 index 547953c1f3a..00000000000 --- a/pkg/operation/botanist/shootstate.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2023 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 botanist - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" -) - -// EnsureShootStateExists creates the ShootState resource for the corresponding shoot and updates the operations object -func (b *Botanist) EnsureShootStateExists(ctx context.Context) error { - var ( - err error - shootState = &gardencorev1beta1.ShootState{ - ObjectMeta: metav1.ObjectMeta{ - Name: b.Shoot.GetInfo().Name, - Namespace: b.Shoot.GetInfo().Namespace, - }, - } - ) - - if err = b.GardenClient.Create(ctx, shootState); client.IgnoreAlreadyExists(err) != nil { - return err - } - - if err = b.GardenClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState); err != nil { - return err - } - - b.SetShootState(shootState) - return nil -} - -// GetShootState returns the shootstate resource of this Shoot in a concurrency safe way. -// This method should be used only for reading the data of the returned shootstate resource. The returned shootstate -// resource MUST NOT BE MODIFIED (except in test code) since this might interfere with other concurrent reads and writes. -// To properly update the shootstate resource of this Shoot use SaveGardenerResourceDataInShootState. -func (b *Botanist) GetShootState() *gardencorev1beta1.ShootState { - return b.Shoot.GetShootState() -} - -// SetShootState sets the shootstate resource of this Shoot in a concurrency safe way. -// This method is not protected by a mutex and does not update the shootstate resource in the cluster and so -// should be used only in exceptional situations, or as a convenience in test code. The shootstate passed as a parameter -// MUST NOT BE MODIFIED after the call to SetShootState (except in test code) since this might interfere with other concurrent reads and writes. -// To properly update the shootstate resource of this Shoot use SaveGardenerResourceDataInShootState. -func (b *Botanist) SetShootState(shootState *gardencorev1beta1.ShootState) { - b.Shoot.SetShootState(shootState) -} diff --git a/pkg/operation/botanist/shootstate_test.go b/pkg/operation/botanist/shootstate_test.go deleted file mode 100644 index 9f66866d0ef..00000000000 --- a/pkg/operation/botanist/shootstate_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2023 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 botanist_test - -import ( - "context" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" - - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - "github.com/gardener/gardener/pkg/client/kubernetes" - "github.com/gardener/gardener/pkg/operation" - . "github.com/gardener/gardener/pkg/operation/botanist" - shootpkg "github.com/gardener/gardener/pkg/operation/shoot" -) - -var _ = Describe("botanist", func() { - var ( - ctx = context.TODO() - shootState *gardencorev1beta1.ShootState - shoot *gardencorev1beta1.Shoot - gardenClient client.Client - botanist *Botanist - ) - - BeforeEach(func() { - shoot = &gardencorev1beta1.Shoot{ - ObjectMeta: metav1.ObjectMeta{ - Name: "fakeShootName", - Namespace: "fakeShootNS", - }, - } - shootState = &gardencorev1beta1.ShootState{ - TypeMeta: metav1.TypeMeta{ - APIVersion: gardencorev1beta1.SchemeGroupVersion.String(), - Kind: "ShootState", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: shoot.Name, - Namespace: shoot.Namespace, - ResourceVersion: "1", - }, - } - - gardenClient = fakeclient.NewClientBuilder().WithScheme(kubernetes.GardenScheme).Build() - botanist = &Botanist{ - Operation: &operation.Operation{ - GardenClient: gardenClient, - Shoot: &shootpkg.Shoot{}, - }, - } - botanist.Shoot.SetInfo(shoot) - }) - - Describe("#EnsureShootStateExists", func() { - It("should create ShootState and add it to the Botanist object", func() { - Expect(botanist.EnsureShootStateExists(ctx)).To(Succeed()) - - Expect(botanist.GetShootState()).To(Equal(shootState)) - }) - - It("should succeed and update Botanist object if ShootState already exists", func() { - shootState.SetAnnotations(map[string]string{"foo": "bar"}) - shootState.ResourceVersion = "" - Expect(gardenClient.Create(ctx, shootState)).To(Succeed()) - - Expect(botanist.EnsureShootStateExists(ctx)).To(Succeed()) - - Expect(botanist.GetShootState()).To(Equal(shootState)) - }) - }) - - Describe("#{Get,Set}ShootState", func() { - It("should not panic if ShootState was not stored", func() { - Expect(botanist.GetShootState()).To(BeNil()) - }) - - It("should return the correct ShootState", func() { - botanist.SetShootState(shootState) - Expect(botanist.GetShootState()).To(Equal(shootState)) - }) - }) -}) diff --git a/pkg/operation/botanist/worker.go b/pkg/operation/botanist/worker.go index 125883eaef4..d802ba1b50a 100644 --- a/pkg/operation/botanist/worker.go +++ b/pkg/operation/botanist/worker.go @@ -74,8 +74,8 @@ func (b *Botanist) DeployWorker(ctx context.Context) error { b.Shoot.Components.Extensions.Worker.SetInfrastructureProviderStatus(b.Shoot.Components.Extensions.Infrastructure.ProviderStatus()) b.Shoot.Components.Extensions.Worker.SetWorkerNameToOperatingSystemConfigsMap(b.Shoot.Components.Extensions.OperatingSystemConfig.WorkerNameToOperatingSystemConfigsMap()) - if b.isRestorePhase() { - return b.Shoot.Components.Extensions.Worker.Restore(ctx, b.GetShootState()) + if b.IsRestorePhase() { + return b.Shoot.Components.Extensions.Worker.Restore(ctx, b.Shoot.GetShootState()) } return b.Shoot.Components.Extensions.Worker.Deploy(ctx) diff --git a/pkg/operation/botanist/worker_test.go b/pkg/operation/botanist/worker_test.go index 4961afcfc6f..9e76bedea3d 100644 --- a/pkg/operation/botanist/worker_test.go +++ b/pkg/operation/botanist/worker_test.go @@ -95,7 +95,7 @@ var _ = Describe("Worker", func() { }, }, } - botanist.SetShootState(shootState) + botanist.Shoot.SetShootState(shootState) botanist.Shoot.SetInfo(&gardencorev1beta1.Shoot{ Spec: gardencorev1beta1.ShootSpec{ Provider: gardencorev1beta1.Provider{ diff --git a/pkg/operation/shoot/shoot.go b/pkg/operation/shoot/shoot.go index c8611882b20..191413703b2 100644 --- a/pkg/operation/shoot/shoot.go +++ b/pkg/operation/shoot/shoot.go @@ -22,6 +22,7 @@ import ( "github.com/Masterminds/semver" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" @@ -257,6 +258,16 @@ func (b *Builder) Build(ctx context.Context, c client.Reader) (*Shoot, error) { } } + if lastOperation := shootObject.Status.LastOperation; lastOperation != nil && + lastOperation.Type == gardencorev1beta1.LastOperationTypeRestore && + lastOperation.State != gardencorev1beta1.LastOperationStateSucceeded { + shootState := &gardencorev1beta1.ShootState{ObjectMeta: metav1.ObjectMeta{Name: shootObject.Name, Namespace: shootObject.Namespace}} + if err := c.Get(ctx, client.ObjectKeyFromObject(shootState), shootState); err != nil { + return nil, err + } + shoot.SetShootState(shootState) + } + return shoot, nil } diff --git a/skaffold.yaml b/skaffold.yaml index b0a639597a5..bb226a93173 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -828,9 +828,6 @@ build: - pkg/gardenlet/controller/shoot/shoot - pkg/gardenlet/controller/shoot/shoot/helper - pkg/gardenlet/controller/shoot/state - - pkg/gardenlet/controller/shootstate - - pkg/gardenlet/controller/shootstate/secret - - pkg/gardenlet/controller/shootstate/extensions - pkg/gardenlet/features - pkg/healthz - pkg/logger diff --git a/test/integration/gardenlet/shootstate/extensions/extensions_suite_test.go b/test/integration/gardenlet/shootstate/extensions/extensions_suite_test.go deleted file mode 100644 index 26e74650551..00000000000 --- a/test/integration/gardenlet/shootstate/extensions/extensions_suite_test.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 extensions_test - -import ( - "context" - "path/filepath" - "testing" - - "github.com/go-logr/logr" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/uuid" - "k8s.io/client-go/rest" - "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/manager" - - extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" - "github.com/gardener/gardener/pkg/client/kubernetes" - gardenerenvtest "github.com/gardener/gardener/pkg/envtest" - "github.com/gardener/gardener/pkg/gardenlet/apis/config" - "github.com/gardener/gardener/pkg/gardenlet/controller/shootstate/extensions" - "github.com/gardener/gardener/pkg/logger" - "github.com/gardener/gardener/pkg/utils" -) - -func TestExtensions(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Test Integration Gardenlet ShootState Extensions Suite") -} - -const testID = "shootstate-extensions-controller-test" - -var ( - ctx = context.Background() - log logr.Logger - - restConfig *rest.Config - testEnv *gardenerenvtest.GardenerTestEnvironment - testClient client.Client - mgrClient client.Client - - testRunID string -) - -var _ = BeforeSuite(func() { - logf.SetLogger(logger.MustNewZapLogger(logger.DebugLevel, logger.FormatJSON, zap.WriteTo(GinkgoWriter))) - log = logf.Log.WithName(testID) - - By("Start test environment") - testEnv = &gardenerenvtest.GardenerTestEnvironment{ - Environment: &envtest.Environment{ - CRDInstallOptions: envtest.CRDInstallOptions{ - Paths: []string{ - filepath.Join("..", "..", "..", "..", "..", "example", "seed-crds", "10-crd-extensions.gardener.cloud_clusters.yaml"), - filepath.Join("..", "..", "..", "..", "..", "example", "seed-crds", "10-crd-extensions.gardener.cloud_infrastructures.yaml"), - }, - }, - ErrorIfCRDPathMissing: true, - }, - GardenerAPIServer: &gardenerenvtest.GardenerAPIServer{ - Args: []string{"--disable-admission-plugins=DeletionConfirmation,ResourceReferenceManager,ExtensionValidator,ShootQuotaValidator,ShootValidator,ShootTolerationRestriction,ShootDNS"}, - }, - } - - var err error - restConfig, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(restConfig).NotTo(BeNil()) - - DeferCleanup(func() { - By("Stop test environment") - Expect(testEnv.Stop()).To(Succeed()) - }) - - testSchemeBuilder := runtime.NewSchemeBuilder( - kubernetes.AddGardenSchemeToScheme, - extensionsv1alpha1.AddToScheme, - ) - testScheme := runtime.NewScheme() - Expect(testSchemeBuilder.AddToScheme(testScheme)).To(Succeed()) - - By("Create test client") - testClient, err = client.New(restConfig, client.Options{Scheme: testScheme}) - Expect(err).NotTo(HaveOccurred()) - - testRunID = utils.ComputeSHA256Hex([]byte(uuid.NewUUID()))[:8] - log.Info("Using test run ID for test", "testRunID", testRunID) - - By("Setup manager") - mgr, err := manager.New(restConfig, manager.Options{ - Scheme: testScheme, - MetricsBindAddress: "0", - NewCache: cache.BuilderWithOptions(cache.Options{ - DefaultSelector: cache.ObjectSelector{ - Label: labels.SelectorFromSet(labels.Set{testID: testRunID}), - }, - }), - }) - Expect(err).NotTo(HaveOccurred()) - mgrClient = mgr.GetClient() - - By("Register controller") - Expect((&extensions.Reconciler{ - Config: config.ShootStateSyncControllerConfiguration{ - ConcurrentSyncs: pointer.Int(5), - }, - ObjectKind: extensionsv1alpha1.InfrastructureResource, - NewObjectFunc: func() client.Object { return &extensionsv1alpha1.Infrastructure{} }, - }).AddToManager(mgr, mgr, mgr)).To(Succeed()) - - By("Start manager") - mgrContext, mgrCancel := context.WithCancel(ctx) - - go func() { - defer GinkgoRecover() - Expect(mgr.Start(mgrContext)).To(Succeed()) - }() - - DeferCleanup(func() { - By("Stop manager") - mgrCancel() - }) -}) diff --git a/test/integration/gardenlet/shootstate/extensions/extensions_test.go b/test/integration/gardenlet/shootstate/extensions/extensions_test.go deleted file mode 100644 index 203120f02c6..00000000000 --- a/test/integration/gardenlet/shootstate/extensions/extensions_test.go +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 extensions_test - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - autoscalingv1 "k8s.io/api/autoscaling/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" - . "github.com/gardener/gardener/pkg/utils/test/matchers" -) - -var _ = Describe("ShootState Extensions controller tests", func() { - var ( - testNamespace *corev1.Namespace - shootState *gardencorev1beta1.ShootState - cluster *extensionsv1alpha1.Cluster - infrastructure *extensionsv1alpha1.Infrastructure - ) - - BeforeEach(func() { - By("Create test Namespace") - testNamespace = &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - // create dedicated namespace for each test run, so that we can run multiple tests concurrently for stress tests - GenerateName: "garden-", - }, - } - Expect(testClient.Create(ctx, testNamespace)).To(Succeed()) - log.Info("Created Namespace for test", "namespaceName", testNamespace.Name) - - DeferCleanup(func() { - By("Delete test Namespace") - Expect(testClient.Delete(ctx, testNamespace)).To(Or(Succeed(), BeNotFoundError())) - }) - - shootState = &gardencorev1beta1.ShootState{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "shoot-", - Namespace: testNamespace.Name, - Labels: map[string]string{testID: testRunID}, - }, - } - cluster = &extensionsv1alpha1.Cluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: testNamespace.Name, - Labels: map[string]string{testID: testRunID}, - }, - Spec: extensionsv1alpha1.ClusterSpec{ - CloudProfile: runtime.RawExtension{Raw: []byte(`{}`)}, - Seed: runtime.RawExtension{Raw: []byte(`{}`)}, - }, - } - infrastructure = &extensionsv1alpha1.Infrastructure{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "infra-", - Namespace: testNamespace.Name, - Labels: map[string]string{testID: testRunID}, - }, - } - }) - - JustBeforeEach(func() { - By("Create ShootState") - Expect(testClient.Create(ctx, shootState)).To(Succeed()) - log.Info("Created ShootState", "shootState", client.ObjectKeyFromObject(shootState)) - - By("Wait until manager has observed ShootState") - Eventually(func() error { - return mgrClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState) - }).Should(Succeed()) - - By("Create Cluster") - cluster.Spec.Shoot.Object = &gardencorev1beta1.Shoot{ - ObjectMeta: metav1.ObjectMeta{ - Name: shootState.Name, - Namespace: shootState.Namespace, - }, - } - Expect(testClient.Create(ctx, cluster)).To(Succeed()) - log.Info("Created Cluster", "cluster", client.ObjectKeyFromObject(cluster)) - - By("Wait until manager has observed Cluster") - // Use the manager's cache to ensure it has observed the Cluster. Otherwise, the controller might simply not - // sync the state of the extension resource. This should not happen in reality, so make sure to stabilize the - // test and keep the controller simple. See https://github.com/gardener/gardener/issues/6923. - Eventually(func() error { - return mgrClient.Get(ctx, client.ObjectKeyFromObject(cluster), cluster) - }).Should(Succeed()) - - By("Create Infrastructure") - Expect(testClient.Create(ctx, infrastructure)).To(Succeed()) - log.Info("Created Infrastructure", "infrastructure", client.ObjectKeyFromObject(infrastructure)) - - DeferCleanup(func() { - By("Delete Infrastructure") - Expect(client.IgnoreNotFound(testClient.Delete(ctx, infrastructure))).To(Succeed()) - - By("Delete Cluster") - Expect(client.IgnoreNotFound(testClient.Delete(ctx, cluster))).To(Succeed()) - - By("Delete ShootState") - Expect(client.IgnoreNotFound(testClient.Delete(ctx, shootState))).To(Succeed()) - - By("Wait for Infrastructure to be gone") - Eventually(func() error { - return testClient.Get(ctx, client.ObjectKeyFromObject(infrastructure), infrastructure) - }).Should(BeNotFoundError()) - - By("Wait for Cluster to be gone") - Eventually(func() error { - return testClient.Get(ctx, client.ObjectKeyFromObject(cluster), cluster) - }).Should(BeNotFoundError()) - - By("Wait for ShootState to be gone") - Eventually(func() error { - return testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState) - }).Should(BeNotFoundError()) - }) - }) - - Context("when reconciliation should be performed", func() { - It("should update the state", func() { - By("Patch status.state in Infrastructure") - patch := client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Status.State = &runtime.RawExtension{Raw: []byte(`{"some":"state"}`)} - Expect(testClient.Status().Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Wait for ShootState to reflect new status") - Eventually(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(ConsistOf(gardencorev1beta1.ExtensionResourceState{ - Kind: "Infrastructure", - Name: &infrastructure.Name, - State: infrastructure.Status.State, - })) - }).Should(Succeed()) - }) - - It("should update the resources", func() { - By("Create secrets to be referenced") - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "some-secret", - Namespace: infrastructure.Namespace, - Labels: map[string]string{testID: testRunID}, - }, - Data: map[string][]byte{"foo": []byte("bar")}, - } - Expect(testClient.Create(ctx, secret)).To(Succeed()) - - configMap := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "some-configmap", - Namespace: infrastructure.Namespace, - Labels: map[string]string{testID: testRunID}, - }, - Data: map[string]string{"foo": "bar"}, - } - Expect(testClient.Create(ctx, configMap)).To(Succeed()) - - DeferCleanup(func() { - Expect(testClient.Delete(ctx, configMap)).To(Succeed()) - Expect(testClient.Delete(ctx, secret)).To(Succeed()) - }) - - By("Patch status.resources in Infrastructure to reference new secret") - patch := client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Status.Resources = append(infrastructure.Status.Resources, gardencorev1beta1.NamedResourceReference{ - Name: "foo", - ResourceRef: autoscalingv1.CrossVersionObjectReference{ - APIVersion: "v1", - Kind: "Secret", - Name: secret.Name, - }, - }) - Expect(testClient.Status().Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Wait for ShootState to reflect new status") - Eventually(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(ConsistOf(gardencorev1beta1.ExtensionResourceState{ - Kind: "Infrastructure", - Name: &infrastructure.Name, - Resources: infrastructure.Status.Resources, - })) - g.Expect(shootState.Spec.Resources).To(ConsistOf(gardencorev1beta1.ResourceData{ - CrossVersionObjectReference: infrastructure.Status.Resources[0].ResourceRef, - Data: runtime.RawExtension{Raw: []byte(`{"apiVersion":"v1","data":{"foo":"YmFy"},"kind":"Secret","metadata":{"labels":{"` + testID + `":"` + testRunID + `"},"name":"` + secret.Name + `","namespace":"` + secret.Namespace + `"},"type":"Opaque"}`)}, - })) - }).Should(Succeed()) - - By("Update secret data") - patch = client.MergeFrom(secret.DeepCopy()) - secret.Data["foo"] = []byte("baz") - Expect(testClient.Patch(ctx, secret, patch)).To(Succeed()) - - By("Patch status.resources in Infrastructure to reference new ConfigMap") - patch = client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Status.Resources = append(infrastructure.Status.Resources, gardencorev1beta1.NamedResourceReference{ - Name: "bar", - ResourceRef: autoscalingv1.CrossVersionObjectReference{ - APIVersion: "v1", - Kind: "ConfigMap", - Name: configMap.Name, - }, - }) - Expect(testClient.Status().Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Wait for ShootState to reflect new status") - Eventually(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(ConsistOf(gardencorev1beta1.ExtensionResourceState{ - Kind: "Infrastructure", - Name: &infrastructure.Name, - Resources: infrastructure.Status.Resources, - })) - g.Expect(shootState.Spec.Resources).To(ConsistOf( - gardencorev1beta1.ResourceData{ - CrossVersionObjectReference: infrastructure.Status.Resources[0].ResourceRef, - Data: runtime.RawExtension{Raw: []byte(`{"apiVersion":"v1","data":{"foo":"YmF6"},"kind":"Secret","metadata":{"labels":{"` + testID + `":"` + testRunID + `"},"name":"` + secret.Name + `","namespace":"` + secret.Namespace + `"},"type":"Opaque"}`)}, - }, - gardencorev1beta1.ResourceData{ - CrossVersionObjectReference: infrastructure.Status.Resources[1].ResourceRef, - Data: runtime.RawExtension{Raw: []byte(`{"apiVersion":"v1","data":{"foo":"bar"},"kind":"ConfigMap","metadata":{"labels":{"` + testID + `":"` + testRunID + `"},"name":"` + configMap.Name + `","namespace":"` + configMap.Namespace + `"}}`)}, - }, - )) - }).Should(Succeed()) - - By("Patch status.resources in Infrastructure to nil") - patch = client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Status.Resources = nil - Expect(testClient.Status().Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Wait for ShootState to reflect new status") - Eventually(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(BeEmpty()) - g.Expect(shootState.Spec.Resources).To(BeEmpty()) - }).Should(Succeed()) - }) - - It("should remove the state when deletion timestamp is set", func() { - By("Patch status.state in Infrastructure") - patch := client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Status.State = &runtime.RawExtension{Raw: []byte(`{"some":"state"}`)} - Expect(testClient.Status().Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Wait for ShootState to reflect new status") - Eventually(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(ConsistOf(gardencorev1beta1.ExtensionResourceState{ - Kind: "Infrastructure", - Name: &infrastructure.Name, - State: infrastructure.Status.State, - })) - }).Should(Succeed()) - - By("Add fake finalizer to Infrastructure to prolong deletion") - patch = client.MergeFrom(infrastructure.DeepCopy()) - Expect(controllerutil.AddFinalizer(infrastructure, "foo.com/bar")).To(BeTrue()) - Expect(testClient.Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Delete Infrastructure") - Expect(testClient.Delete(ctx, infrastructure)).To(Succeed()) - - By("Patch status.state in Infrastructure to some new information") - patch = client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Status.State = &runtime.RawExtension{Raw: []byte(`{"some":"new-state"}`)} - Expect(testClient.Status().Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Wait for ShootState to be updated (state completely removed)") - Eventually(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(BeEmpty()) - }).Should(Succeed()) - - By("Remove fake finalizer from Infrastructure") - patch = client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Finalizers = nil - Expect(testClient.Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Ensure ShootState was not updated anymore") - Consistently(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(BeEmpty()) - }).Should(Succeed()) - }) - }) - - Context("when reconciliation should be skipped", func() { - testForOperationAnnotation := func(operationAnnotation string) { - It("should do nothing because of operation annotation "+operationAnnotation, func() { - By("Patch status.state in Infrastructure") - patch := client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Status.State = &runtime.RawExtension{Raw: []byte(`{"some":"state"}`)} - Expect(testClient.Status().Patch(ctx, infrastructure, patch)).To(Succeed()) - state := *infrastructure.Status.State - - By("Wait for ShootState to reflect new status") - Eventually(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(ConsistOf(gardencorev1beta1.ExtensionResourceState{ - Kind: "Infrastructure", - Name: &infrastructure.Name, - State: &state, - })) - }).Should(Succeed()) - - By("Add operation annotation") - patch = client.MergeFrom(infrastructure.DeepCopy()) - metav1.SetMetaDataAnnotation(&infrastructure.ObjectMeta, "gardener.cloud/operation", operationAnnotation) - Expect(testClient.Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Patch status.state in Infrastructure to some new information") - patch = client.MergeFrom(infrastructure.DeepCopy()) - infrastructure.Status.State = &runtime.RawExtension{Raw: []byte(`{"some":"new-state"}`)} - Expect(testClient.Status().Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Ensure that ShootState was not updated") - Consistently(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(ConsistOf(gardencorev1beta1.ExtensionResourceState{ - Kind: "Infrastructure", - Name: &infrastructure.Name, - State: &state, - })) - }).Should(Succeed()) - - By("Remove operation annotation") - patch = client.MergeFrom(infrastructure.DeepCopy()) - delete(infrastructure.Annotations, "gardener.cloud/operation") - Expect(testClient.Patch(ctx, infrastructure, patch)).To(Succeed()) - - By("Wait for ShootState to reflect new status") - Eventually(func(g Gomega) { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - g.Expect(shootState.Spec.Extensions).To(ConsistOf(gardencorev1beta1.ExtensionResourceState{ - Kind: "Infrastructure", - Name: &infrastructure.Name, - State: infrastructure.Status.State, - })) - }).Should(Succeed()) - }) - } - - Context("wait-for-state", func() { testForOperationAnnotation("wait-for-state") }) - Context("restore", func() { testForOperationAnnotation("restore") }) - Context("migrate", func() { testForOperationAnnotation("migrate") }) - }) -}) diff --git a/test/integration/gardenlet/shootstate/secret/secret_suite_test.go b/test/integration/gardenlet/shootstate/secret/secret_suite_test.go deleted file mode 100644 index 2d4982c1d88..00000000000 --- a/test/integration/gardenlet/shootstate/secret/secret_suite_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 secret_test - -import ( - "context" - "path/filepath" - "testing" - - "github.com/go-logr/logr" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/uuid" - "k8s.io/client-go/rest" - "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/cache" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/manager" - - v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants" - extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" - "github.com/gardener/gardener/pkg/client/kubernetes" - gardenerenvtest "github.com/gardener/gardener/pkg/envtest" - "github.com/gardener/gardener/pkg/gardenlet/apis/config" - shootstatesecretcontroller "github.com/gardener/gardener/pkg/gardenlet/controller/shootstate/secret" - "github.com/gardener/gardener/pkg/logger" - "github.com/gardener/gardener/pkg/utils" - . "github.com/gardener/gardener/pkg/utils/test/matchers" -) - -func TestSecret(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Test Integration Gardenlet ShootState Secret Suite") -} - -const testID = "shoot-secret-controller-test" - -var ( - ctx = context.Background() - log logr.Logger - - restConfig *rest.Config - testEnv *gardenerenvtest.GardenerTestEnvironment - testClient client.Client - mgrClient client.Client - - testNamespace *corev1.Namespace - testRunID string -) - -var _ = BeforeSuite(func() { - logf.SetLogger(logger.MustNewZapLogger(logger.DebugLevel, logger.FormatJSON, zap.WriteTo(GinkgoWriter))) - log = logf.Log.WithName(testID) - - By("Start test environment") - testEnv = &gardenerenvtest.GardenerTestEnvironment{ - Environment: &envtest.Environment{ - CRDInstallOptions: envtest.CRDInstallOptions{ - Paths: []string{filepath.Join("..", "..", "..", "..", "..", "example", "seed-crds", "10-crd-extensions.gardener.cloud_clusters.yaml")}, - }, - ErrorIfCRDPathMissing: true, - }, - GardenerAPIServer: &gardenerenvtest.GardenerAPIServer{ - Args: []string{"--disable-admission-plugins=DeletionConfirmation,ResourceReferenceManager,ExtensionValidator,ShootQuotaValidator,ShootValidator,ShootTolerationRestriction"}, - }, - } - - var err error - restConfig, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(restConfig).NotTo(BeNil()) - - DeferCleanup(func() { - By("Stop test environment") - Expect(testEnv.Stop()).To(Succeed()) - }) - - testSchemeBuilder := runtime.NewSchemeBuilder( - kubernetes.AddGardenSchemeToScheme, - extensionsv1alpha1.AddToScheme, - ) - testScheme := runtime.NewScheme() - Expect(testSchemeBuilder.AddToScheme(testScheme)).To(Succeed()) - - By("Create test client") - testClient, err = client.New(restConfig, client.Options{Scheme: testScheme}) - Expect(err).NotTo(HaveOccurred()) - - testRunID = testID + "-" + utils.ComputeSHA256Hex([]byte(uuid.NewUUID()))[:8] - log.Info("Using test run ID for test", "testRunID", testRunID) - - By("Create test Namespace") - testNamespace = &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - // create dedicated namespace for each test run, so that we can run multiple tests concurrently for stress tests - GenerateName: "garden-", - Labels: map[string]string{ - v1beta1constants.GardenRole: v1beta1constants.GardenRoleShoot, - testID: testRunID, - }, - }, - } - Expect(testClient.Create(ctx, testNamespace)).To(Succeed()) - log.Info("Created Namespace for test", "namespaceName", testNamespace.Name) - - DeferCleanup(func() { - By("Delete project namespace") - Expect(testClient.Delete(ctx, testNamespace)).To(Or(Succeed(), BeNotFoundError())) - }) - - By("Setup manager") - mgr, err := manager.New(restConfig, manager.Options{ - Scheme: testScheme, - MetricsBindAddress: "0", - NewCache: cache.BuilderWithOptions(cache.Options{ - DefaultSelector: cache.ObjectSelector{ - Label: labels.SelectorFromSet(labels.Set{testID: testRunID}), - }, - }), - }) - Expect(err).NotTo(HaveOccurred()) - mgrClient = mgr.GetClient() - - By("Register controller") - Expect((&shootstatesecretcontroller.Reconciler{ - Config: config.ShootSecretControllerConfiguration{ - ConcurrentSyncs: pointer.Int(5), - }, - }).AddToManager(mgr, mgr, mgr)).To(Succeed()) - - By("Start manager") - mgrContext, mgrCancel := context.WithCancel(ctx) - - go func() { - defer GinkgoRecover() - Expect(mgr.Start(mgrContext)).To(Succeed()) - }() - - DeferCleanup(func() { - By("Stop manager") - mgrCancel() - }) -}) diff --git a/test/integration/gardenlet/shootstate/secret/secret_test.go b/test/integration/gardenlet/shootstate/secret/secret_test.go deleted file mode 100644 index 0189a27bb2d..00000000000 --- a/test/integration/gardenlet/shootstate/secret/secret_test.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file -// -// 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 secret_test - -import ( - "encoding/json" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/onsi/gomega/gstruct" - gomegatypes "github.com/onsi/gomega/types" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/uuid" - "k8s.io/utils/pointer" - "sigs.k8s.io/controller-runtime/pkg/client" - - gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" - v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants" - extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" - "github.com/gardener/gardener/pkg/utils" - secretsmanager "github.com/gardener/gardener/pkg/utils/secrets/manager" - . "github.com/gardener/gardener/pkg/utils/test/matchers" -) - -var _ = Describe("ShootSecret controller tests", func() { - var ( - resourceName string - - shoot *gardencorev1beta1.Shoot - shootState *gardencorev1beta1.ShootState - - cluster *extensionsv1alpha1.Cluster - - seedNamespace *corev1.Namespace - ) - - BeforeEach(func() { - resourceName = "test-" + utils.ComputeSHA256Hex([]byte(uuid.NewUUID()))[:8] - - By("Create seed namespace") - // name doesn't follow the usual naming scheme (technical ID), but this doesn't matter for this test, as - // long as the cluster has the same name - seedNamespace = &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "shoot--" + resourceName, - Labels: map[string]string{ - v1beta1constants.GardenRole: v1beta1constants.GardenRoleShoot, - testID: testRunID, - }, - }, - } - Expect(testClient.Create(ctx, seedNamespace)).To(Succeed()) - log.Info("Created seed Namespace for test", "namespaceName", seedNamespace.Name) - - DeferCleanup(func() { - By("Delete seed namespace") - Expect(testClient.Delete(ctx, seedNamespace)).To(Or(Succeed(), BeNotFoundError())) - }) - - By("Wait until manager has observed seed namespace") - Eventually(func() error { - return mgrClient.Get(ctx, client.ObjectKeyFromObject(seedNamespace), &corev1.Namespace{}) - }).Should(Succeed()) - - By("Build shoot object") - shoot = &gardencorev1beta1.Shoot{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourceName, - Namespace: testNamespace.Name, - }, - Spec: gardencorev1beta1.ShootSpec{ - SecretBindingName: pointer.String("my-provider-account"), - CloudProfileName: "cloudprofile1", - Region: "europe-central-1", - Provider: gardencorev1beta1.Provider{ - Type: "foo-provider", - Workers: []gardencorev1beta1.Worker{ - { - Name: "cpu-worker", - Minimum: 3, - Maximum: 3, - Machine: gardencorev1beta1.Machine{ - Type: "large", - }, - }, - }, - }, - Kubernetes: gardencorev1beta1.Kubernetes{ - Version: "1.25.1", - }, - Networking: &gardencorev1beta1.Networking{ - Type: pointer.String("foo-networking"), - }, - }, - } - - By("Create shootstate") - shootState = &gardencorev1beta1.ShootState{ - ObjectMeta: metav1.ObjectMeta{ - Name: shoot.Name, - Namespace: shoot.Namespace, - Labels: map[string]string{testID: testRunID}, - }, - } - Expect(testClient.Create(ctx, shootState)).To(Succeed()) - log.Info("Created shootstate for test", "shootState", client.ObjectKeyFromObject(shootState)) - - DeferCleanup(func() { - By("Delete shootstate") - Expect(client.IgnoreNotFound(testClient.Delete(ctx, shootState))).To(Succeed()) - }) - - By("Create cluster") - cluster = &extensionsv1alpha1.Cluster{ - ObjectMeta: metav1.ObjectMeta{ - Name: seedNamespace.Name, - Labels: map[string]string{testID: testRunID}, - }, - Spec: extensionsv1alpha1.ClusterSpec{ - Shoot: runtime.RawExtension{ - Object: shoot, - }, - CloudProfile: runtime.RawExtension{ - Object: &gardencorev1beta1.CloudProfile{}, - }, - Seed: runtime.RawExtension{ - Object: &gardencorev1beta1.Seed{}, - }, - }, - } - Expect(testClient.Create(ctx, cluster)).To(Succeed()) - log.Info("Created cluster for test", "cluster", client.ObjectKeyFromObject(cluster)) - - By("Wait until manager has observed cluster creation") - Eventually(func(g Gomega) error { - return mgrClient.Get(ctx, client.ObjectKeyFromObject(cluster), &extensionsv1alpha1.Cluster{}) - }).Should(Succeed()) - - DeferCleanup(func() { - By("Delete cluster") - Expect(client.IgnoreNotFound(testClient.Delete(ctx, cluster))).To(Succeed()) - }) - }) - - It("should sync relevant secrets to the shootstate", func() { - By("Create irrelevant secret") - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: resourceName, - Namespace: seedNamespace.Name, - Labels: map[string]string{testID: testRunID}, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "some": []byte("data"), - }, - } - Expect(testClient.Create(ctx, secret)).To(Succeed()) - - By("Wait until manager has observed secret") - Eventually(func(g Gomega) error { - return mgrClient.Get(ctx, client.ObjectKeyFromObject(secret), &corev1.Secret{}) - }).Should(Succeed()) - - By("Verify secret did not get synced to shootstate") - Consistently(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).ShouldNot(containData(withName(secret.Name))) - - By("Verify secret has no finalizers") - Consistently(func(g Gomega) []string { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(secret), secret)).To(Succeed()) - return secret.Finalizers - }).Should(BeEmpty()) - - By("Update irrelevant secret to stay irrelevant") - patch := client.MergeFrom(secret.DeepCopy()) - metav1.SetMetaDataLabel(&secret.ObjectMeta, secretsmanager.LabelKeyManagedBy, secretsmanager.LabelValueSecretsManager) - Expect(testClient.Patch(ctx, secret, patch)).To(Succeed()) - - By("Verify secret did still not get synced to shootstate") - Consistently(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).ShouldNot(containData(withName(secret.Name))) - - By("Verify secret has no finalizers") - Consistently(func(g Gomega) []string { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(secret), secret)).To(Succeed()) - return secret.Finalizers - }).Should(BeEmpty()) - - By("Update irrelevant secret to become relevant") - patch = client.MergeFrom(secret.DeepCopy()) - metav1.SetMetaDataLabel(&secret.ObjectMeta, secretsmanager.LabelKeyPersist, secretsmanager.LabelValueTrue) - Expect(testClient.Patch(ctx, secret, patch)).To(Succeed()) - - By("Verify secret did now get synced to shootstate") - Eventually(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).Should(containData( - withName(secret.Name), - withType("secret"), - withLabels(secret.Labels), - withData(secret.Data), - )) - - By("Verify secret has finalizers") - Eventually(func(g Gomega) []string { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(secret), secret)).To(Succeed()) - return secret.Finalizers - }).Should(ConsistOf("gardenlet.gardener.cloud/secret-controller")) - - By("Update data of relevant secret") - patch = client.MergeFrom(secret.DeepCopy()) - secret.Data["more"] = []byte("data") - Expect(testClient.Patch(ctx, secret, patch)).To(Succeed()) - - By("Verify secret did now get synced") - Eventually(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).Should(containData( - withName(secret.Name), - withType("secret"), - withLabels(secret.Labels), - withData(secret.Data), - )) - - By("Delete relevant secret") - Expect(testClient.Delete(ctx, secret)).To(Succeed()) - - By("Verify secret got removed") - Eventually(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).ShouldNot(containData(withName(secret.Name))) - - By("Verify secret has been removed from the system") - Eventually(func() error { - return testClient.Get(ctx, client.ObjectKeyFromObject(secret), secret) - }).Should(BeNotFoundError()) - }) - - It("should sync external secrets to the shootstate", func() { - By("Create external secret") - secret := newRelevantSecret(resourceName, seedNamespace.Name) - metav1.SetMetaDataLabel(&secret.ObjectMeta, secretsmanager.LabelKeyManagerIdentity, "extension") - Expect(testClient.Create(ctx, secret)).To(Succeed()) - - By("Wait until manager has observed external secret") - Eventually(func(g Gomega) error { - return mgrClient.Get(ctx, client.ObjectKeyFromObject(secret), &corev1.Secret{}) - }).Should(Succeed()) - - By("Verify secret did get synced to shootstate") - Eventually(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).Should(containData( - withName(secret.Name), - withType("secret"), - withLabels(secret.Labels), - withData(secret.Data), - )) - - By("Verify secret has finalizers") - Eventually(func(g Gomega) []string { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(secret), secret)).To(Succeed()) - return secret.Finalizers - }).Should(ConsistOf("gardenlet.gardener.cloud/secret-controller")) - }) - - It("should do nothing if the secret does not belong to a shoot namespace", func() { - By("Create other namespace") - nonShootNamespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Labels: map[string]string{testID: testRunID}, - }, - } - Expect(testClient.Create(ctx, nonShootNamespace)).To(Succeed()) - log.Info("Created other Namespace for test", "namespaceName", nonShootNamespace.Name) - - DeferCleanup(func() { - By("Delete other namespace") - Expect(testClient.Delete(ctx, nonShootNamespace)).To(Or(Succeed(), BeNotFoundError())) - }) - - By("Create relevant secret") - secret := newRelevantSecret(resourceName, nonShootNamespace.Name) - Expect(testClient.Create(ctx, secret)).To(Succeed()) - - By("Verify secret does not get added to shootstate") - Consistently(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).ShouldNot(containData(withName(secret.Name))) - - By("Verify secret has no finalizers") - Consistently(func(g Gomega) []string { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(secret), secret)).To(Succeed()) - return secret.Finalizers - }).Should(BeEmpty()) - }) - - It("should not remove secrets from shootstate when shoot is in migration", func() { - By("Create secret") - secret := newRelevantSecret(resourceName, seedNamespace.Name) - Expect(testClient.Create(ctx, secret)).To(Succeed()) - - By("Wait until manager has observed secret creation") - Eventually(func() error { - return mgrClient.Get(ctx, client.ObjectKeyFromObject(secret), &corev1.Secret{}) - }).Should(Succeed()) - - By("Verify secret gets synced to shootstate") - Eventually(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).Should(containData(withName(secret.Name))) - - By("Verify secret has finalizer") - Eventually(func(g Gomega) []string { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(secret), secret)).To(Succeed()) - return secret.Finalizers - }).Should(ConsistOf("gardenlet.gardener.cloud/secret-controller")) - - By("Mark shoot for migration") - shoot.Status.LastOperation = &gardencorev1beta1.LastOperation{Type: gardencorev1beta1.LastOperationTypeMigrate} - shootRaw, err := json.Marshal(shoot) - Expect(err).NotTo(HaveOccurred()) - - By("Update cluster") - patch := client.MergeFromWithOptions(cluster.DeepCopy(), client.MergeFromWithOptimisticLock{}) - cluster.Spec.Shoot.Raw = shootRaw - Expect(testClient.Patch(ctx, cluster, patch)).To(Succeed()) - - resourceVersion := cluster.GetResourceVersion() - By("Wait until manager has observed updated cluster") - Eventually(func(g Gomega) string { - g.Expect(mgrClient.Get(ctx, client.ObjectKeyFromObject(cluster), cluster)).To(Succeed()) - return (cluster.ResourceVersion) - }).Should(Equal(resourceVersion)) - - By("Delete secret") - Expect(testClient.Delete(ctx, secret)).To(Succeed()) - - By("Verify secret has been removed from the system") - Eventually(func() error { - return testClient.Get(ctx, client.ObjectKeyFromObject(secret), secret) - }).Should(BeNotFoundError()) - - By("Verify secret info did not get removed in shootstate") - Consistently(func(g Gomega) []gardencorev1beta1.GardenerResourceData { - g.Expect(testClient.Get(ctx, client.ObjectKeyFromObject(shootState), shootState)).To(Succeed()) - return shootState.Spec.Gardener - }).Should(containData(withName(secret.Name))) - }) -}) - -func containData(matchers ...gomegatypes.GomegaMatcher) gomegatypes.GomegaMatcher { - return ContainElement(And(matchers...)) -} - -func withName(name string) gomegatypes.GomegaMatcher { - return gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Name": Equal(name), - }) -} - -func withType(t string) gomegatypes.GomegaMatcher { - return gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Type": Equal(t), - }) -} - -func withLabels(labels map[string]string) gomegatypes.GomegaMatcher { - return gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Labels": Equal(labels), - }) -} - -func withData(data map[string][]byte) gomegatypes.GomegaMatcher { - rawData, err := json.Marshal(data) - Expect(err).NotTo(HaveOccurred()) - - return gstruct.MatchFields(gstruct.IgnoreExtras, gstruct.Fields{ - "Data": Equal(runtime.RawExtension{ - Raw: rawData, - }), - }) -} - -func newRelevantSecret(name, namespace string) *corev1.Secret { - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: map[string]string{ - secretsmanager.LabelKeyManagedBy: secretsmanager.LabelValueSecretsManager, - secretsmanager.LabelKeyManagerIdentity: "test", - secretsmanager.LabelKeyPersist: secretsmanager.LabelValueTrue, - testID: testRunID, - }, - }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{ - "some": []byte("data"), - }, - } -}