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"), - }, - } -}