diff --git a/extensions/pkg/util/secret/secret_test.go b/extensions/pkg/util/secret/secret_test.go index 3c3acaa3903..168d1852eb4 100644 --- a/extensions/pkg/util/secret/secret_test.go +++ b/extensions/pkg/util/secret/secret_test.go @@ -23,8 +23,10 @@ import ( 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/client/fake" + "github.com/gardener/gardener/extensions/pkg/util/index" secretutil "github.com/gardener/gardener/extensions/pkg/util/secret" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" ) @@ -41,7 +43,14 @@ var _ = Describe("Secret", func() { var ( scheme *runtime.Scheme + client client.Client + secret *corev1.Secret + secretBinding *gardencorev1beta1.SecretBinding + shoot *gardencorev1beta1.Shoot + ) + + JustBeforeEach(func() { secret = &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "secret", @@ -70,20 +79,33 @@ var _ = Describe("Secret", func() { SecretBindingName: secretBinding.Name, }, } - ) - BeforeEach(func() { scheme = runtime.NewScheme() Expect(corev1.AddToScheme(scheme)).NotTo(HaveOccurred()) Expect(gardencorev1beta1.AddToScheme(scheme)).NotTo(HaveOccurred()) + + client = fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects( + secret, + secretBinding, + shoot, + ). + WithIndex(&gardencorev1beta1.SecretBinding{}, index.SecretRefNamespaceField, index.SecretRefNamespaceIndexerFunc). + WithIndex(&gardencorev1beta1.Shoot{}, index.SecretBindingNameField, index.SecretBindingNameIndexerFunc). + Build() }) It("should return false when the Secret is not used", func() { - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects( - - secret, - secretBinding, - ).Build() + client := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects( + secret, + secretBinding, + ). + WithIndex(&gardencorev1beta1.SecretBinding{}, index.SecretRefNamespaceField, index.SecretRefNamespaceIndexerFunc). + WithIndex(&gardencorev1beta1.Shoot{}, index.SecretBindingNameField, index.SecretBindingNameIndexerFunc). + Build() isUsed, err := secretutil.IsSecretInUseByShoot(context.TODO(), client, secret, "gcp") Expect(isUsed).To(BeFalse()) @@ -91,56 +113,42 @@ var _ = Describe("Secret", func() { }) It("should return false when the Secret is in use but the provider does not match", func() { - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects( - secret, - secretBinding, - shoot, - ).Build() - isUsed, err := secretutil.IsSecretInUseByShoot(context.TODO(), client, secret, "other") Expect(isUsed).To(BeFalse()) Expect(err).NotTo(HaveOccurred()) }) It("should return true when the Secret is in use by Shoot with the given provider", func() { - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects( - secret, - secretBinding, - shoot, - ).Build() - isUsed, err := secretutil.IsSecretInUseByShoot(context.TODO(), client, secret, "gcp") Expect(isUsed).To(BeTrue()) Expect(err).NotTo(HaveOccurred()) }) - It("should return true when the Secret is in use by Shoot from another namespace", func() { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "secret", - Namespace: "another-namespace", - }, - } - secretBinding := &gardencorev1beta1.SecretBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: "secretbinding", - Namespace: namespace, - }, - SecretRef: corev1.SecretReference{ - Name: secret.Name, - Namespace: secret.Namespace, - }, - } - - client := fake.NewClientBuilder().WithScheme(scheme).WithObjects( - secret, - secretBinding, - shoot, - ).Build() - - isUsed, err := secretutil.IsSecretInUseByShoot(context.TODO(), client, secret, "gcp") - Expect(isUsed).To(BeTrue()) - Expect(err).NotTo(HaveOccurred()) + Context("when the Secret is in use by Shoot from another namespace", func() { + BeforeEach(func() { + secret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secret", + Namespace: "another-namespace", + }, + } + secretBinding = &gardencorev1beta1.SecretBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "secretbinding", + Namespace: namespace, + }, + SecretRef: corev1.SecretReference{ + Name: secret.Name, + Namespace: secret.Namespace, + }, + } + }) + + It("should return true", func() { + isUsed, err := secretutil.IsSecretInUseByShoot(context.TODO(), client, secret, "gcp") + Expect(isUsed).To(BeTrue()) + Expect(err).NotTo(HaveOccurred()) + }) }) }) }) diff --git a/pkg/api/indexer/core.go b/pkg/api/indexer/core.go index 877d41d0612..97feaf39f8d 100644 --- a/pkg/api/indexer/core.go +++ b/pkg/api/indexer/core.go @@ -25,15 +25,38 @@ import ( gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" ) -// AddProjectNamespace adds an index for core.ProjectNamespace to the given indexer. -func AddProjectNamespace(ctx context.Context, indexer client.FieldIndexer) error { - if err := indexer.IndexField(ctx, &gardencorev1beta1.Project{}, core.ProjectNamespace, func(obj client.Object) []string { +var ( + // ProjectNamespaceIndexerFunc extracts the .spec.namespace field of a Project. + ProjectNamespaceIndexerFunc = func(obj client.Object) []string { project, ok := obj.(*gardencorev1beta1.Project) if !ok { return []string{""} } return []string{pointer.StringDeref(project.Spec.Namespace, "")} - }); err != nil { + } + + // BackupBucketSeedNameIndexerFunc extracts the .spec.seedName field of a BackupBucket. + BackupBucketSeedNameIndexerFunc = func(obj client.Object) []string { + backupBucket, ok := obj.(*gardencorev1beta1.BackupBucket) + if !ok { + return []string{""} + } + return []string{pointer.StringDeref(backupBucket.Spec.SeedName, "")} + } + + // ControllerInstallationSeedRefNameIndexerFunc extracts the .spec.seedRef.name field of a ControllerInstallation. + ControllerInstallationSeedRefNameIndexerFunc = func(obj client.Object) []string { + controllerInstallation, ok := obj.(*gardencorev1beta1.ControllerInstallation) + if !ok { + return []string{""} + } + return []string{controllerInstallation.Spec.SeedRef.Name} + } +) + +// AddProjectNamespace adds an index for core.ProjectNamespace to the given indexer. +func AddProjectNamespace(ctx context.Context, indexer client.FieldIndexer) error { + if err := indexer.IndexField(ctx, &gardencorev1beta1.Project{}, core.ProjectNamespace, ProjectNamespaceIndexerFunc); err != nil { return fmt.Errorf("failed to add indexer for %s to Project Informer: %w", core.ProjectNamespace, err) } return nil @@ -69,13 +92,7 @@ func AddShootStatusSeedName(ctx context.Context, indexer client.FieldIndexer) er // AddBackupBucketSeedName adds an index for core.BackupBucketSeedName to the given indexer. func AddBackupBucketSeedName(ctx context.Context, indexer client.FieldIndexer) error { - if err := indexer.IndexField(ctx, &gardencorev1beta1.BackupBucket{}, core.BackupBucketSeedName, func(obj client.Object) []string { - backupBucket, ok := obj.(*gardencorev1beta1.BackupBucket) - if !ok { - return []string{""} - } - return []string{pointer.StringDeref(backupBucket.Spec.SeedName, "")} - }); err != nil { + if err := indexer.IndexField(ctx, &gardencorev1beta1.BackupBucket{}, core.BackupBucketSeedName, BackupBucketSeedNameIndexerFunc); err != nil { return fmt.Errorf("failed to add indexer for %s to BackupBucket Informer: %w", core.BackupBucketSeedName, err) } return nil @@ -111,13 +128,7 @@ func AddBackupEntryBucketName(ctx context.Context, indexer client.FieldIndexer) // AddControllerInstallationSeedRefName adds an index for core.ControllerInstallationSeedRefName to the given indexer. func AddControllerInstallationSeedRefName(ctx context.Context, indexer client.FieldIndexer) error { - if err := indexer.IndexField(ctx, &gardencorev1beta1.ControllerInstallation{}, core.SeedRefName, func(obj client.Object) []string { - controllerInstallation, ok := obj.(*gardencorev1beta1.ControllerInstallation) - if !ok { - return []string{""} - } - return []string{controllerInstallation.Spec.SeedRef.Name} - }); err != nil { + if err := indexer.IndexField(ctx, &gardencorev1beta1.ControllerInstallation{}, core.SeedRefName, ControllerInstallationSeedRefNameIndexerFunc); err != nil { return fmt.Errorf("failed to add indexer for %s to ControllerInstallation Informer: %w", core.SeedRefName, err) } return nil diff --git a/pkg/api/indexer/operations.go b/pkg/api/indexer/operations.go index 6ec940c209c..f869a5abbb8 100644 --- a/pkg/api/indexer/operations.go +++ b/pkg/api/indexer/operations.go @@ -24,15 +24,18 @@ import ( operationsv1alpha1 "github.com/gardener/gardener/pkg/apis/operations/v1alpha1" ) +// BastionShootNameIndexerFunc extracts the .spec.shootRef.name field of a Bastion. +var BastionShootNameIndexerFunc = func(obj client.Object) []string { + bastion, ok := obj.(*operationsv1alpha1.Bastion) + if !ok { + return []string{""} + } + return []string{bastion.Spec.ShootRef.Name} +} + // AddBastionShootName adds an index for operations.BastionShootName to the given indexer. func AddBastionShootName(ctx context.Context, indexer client.FieldIndexer) error { - if err := indexer.IndexField(ctx, &operationsv1alpha1.Bastion{}, operations.BastionShootName, func(obj client.Object) []string { - bastion, ok := obj.(*operationsv1alpha1.Bastion) - if !ok { - return []string{""} - } - return []string{bastion.Spec.ShootRef.Name} - }); err != nil { + if err := indexer.IndexField(ctx, &operationsv1alpha1.Bastion{}, operations.BastionShootName, BastionShootNameIndexerFunc); err != nil { return fmt.Errorf("failed to add indexer for %s to Bastion Informer: %w", operations.BastionShootName, err) } return nil diff --git a/pkg/api/indexer/seedmanagement.go b/pkg/api/indexer/seedmanagement.go index 666bf6ac807..0e2dabef3e0 100644 --- a/pkg/api/indexer/seedmanagement.go +++ b/pkg/api/indexer/seedmanagement.go @@ -24,18 +24,21 @@ import ( seedmanagementv1alpha1 "github.com/gardener/gardener/pkg/apis/seedmanagement/v1alpha1" ) +// ManagedSeedShootNameIndexerFunc extracts the .spec.shoot.name field of a ManagedSeed. +var ManagedSeedShootNameIndexerFunc = func(obj client.Object) []string { + managedSeed, ok := obj.(*seedmanagementv1alpha1.ManagedSeed) + if !ok { + return []string{""} + } + if managedSeed.Spec.Shoot == nil { + return []string{""} + } + return []string{managedSeed.Spec.Shoot.Name} +} + // AddManagedSeedShootName adds an index for seedmanagement.ManagedSeedShootName to the given indexer. func AddManagedSeedShootName(ctx context.Context, indexer client.FieldIndexer) error { - if err := indexer.IndexField(ctx, &seedmanagementv1alpha1.ManagedSeed{}, seedmanagement.ManagedSeedShootName, func(obj client.Object) []string { - managedSeed, ok := obj.(*seedmanagementv1alpha1.ManagedSeed) - if !ok { - return []string{""} - } - if managedSeed.Spec.Shoot == nil { - return []string{""} - } - return []string{managedSeed.Spec.Shoot.Name} - }); err != nil { + if err := indexer.IndexField(ctx, &seedmanagementv1alpha1.ManagedSeed{}, seedmanagement.ManagedSeedShootName, ManagedSeedShootNameIndexerFunc); err != nil { return fmt.Errorf("failed to add indexer for %s to ManagedSeed Informer: %w", seedmanagement.ManagedSeedShootName, err) } return nil diff --git a/pkg/controllermanager/controller/bastion/add_test.go b/pkg/controllermanager/controller/bastion/add_test.go index bd1c82ccf70..768bb296800 100644 --- a/pkg/controllermanager/controller/bastion/add_test.go +++ b/pkg/controllermanager/controller/bastion/add_test.go @@ -30,12 +30,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/gardener/gardener/pkg/api/indexer" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + "github.com/gardener/gardener/pkg/apis/operations" operationsv1alpha1 "github.com/gardener/gardener/pkg/apis/operations/v1alpha1" "github.com/gardener/gardener/pkg/client/kubernetes" . "github.com/gardener/gardener/pkg/controllermanager/controller/bastion" - bastionregistry "github.com/gardener/gardener/pkg/registry/operations/bastion" - "github.com/gardener/gardener/pkg/utils/test" ) var _ = Describe("Add", func() { @@ -152,10 +152,10 @@ var _ = Describe("Add", func() { BeforeEach(func() { log = logr.Discard() - fakeClient = test.NewClientWithFieldSelectorSupport( - fakeclient.NewClientBuilder().WithScheme(kubernetes.GardenScheme).Build(), - bastionregistry.ToSelectableFields, - ) + fakeClient = fakeclient.NewClientBuilder(). + WithScheme(kubernetes.GardenScheme). + WithIndex(&operationsv1alpha1.Bastion{}, operations.BastionShootName, indexer.BastionShootNameIndexerFunc). + Build() }) It("should do nothing if the object is no shoot", func() { diff --git a/pkg/controllermanager/controller/project/activity/add_test.go b/pkg/controllermanager/controller/project/activity/add_test.go index 12e3aaa8a4d..77c5fb4ef6e 100644 --- a/pkg/controllermanager/controller/project/activity/add_test.go +++ b/pkg/controllermanager/controller/project/activity/add_test.go @@ -32,11 +32,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/gardener/gardener/pkg/api/indexer" + "github.com/gardener/gardener/pkg/apis/core" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" "github.com/gardener/gardener/pkg/client/kubernetes" . "github.com/gardener/gardener/pkg/controllermanager/controller/project/activity" - projectregistry "github.com/gardener/gardener/pkg/registry/core/project" - "github.com/gardener/gardener/pkg/utils/test" ) var _ = Describe("Add", func() { @@ -172,10 +172,10 @@ var _ = Describe("Add", func() { BeforeEach(func() { log = logr.Discard() - fakeClient = test.NewClientWithFieldSelectorSupport( - fakeclient.NewClientBuilder().WithScheme(kubernetes.GardenScheme).Build(), - projectregistry.ToSelectableFields, - ) + fakeClient = fakeclient.NewClientBuilder(). + WithScheme(kubernetes.GardenScheme). + WithIndex(&gardencorev1beta1.Project{}, core.ProjectNamespace, indexer.ProjectNamespaceIndexerFunc). + Build() project = &gardencorev1beta1.Project{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/controllermanager/controller/seed/backupbucketscheck/reconciler_test.go b/pkg/controllermanager/controller/seed/backupbucketscheck/reconciler_test.go index e327e31f5cf..ad3eab9abb1 100644 --- a/pkg/controllermanager/controller/seed/backupbucketscheck/reconciler_test.go +++ b/pkg/controllermanager/controller/seed/backupbucketscheck/reconciler_test.go @@ -30,12 +30,12 @@ import ( fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/gardener/gardener/pkg/api/indexer" + "github.com/gardener/gardener/pkg/apis/core" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" "github.com/gardener/gardener/pkg/client/kubernetes" "github.com/gardener/gardener/pkg/controllermanager/apis/config" . "github.com/gardener/gardener/pkg/controllermanager/controller/seed/backupbucketscheck" - backupbucketregistry "github.com/gardener/gardener/pkg/registry/core/backupbucket" - "github.com/gardener/gardener/pkg/utils/test" . "github.com/gardener/gardener/pkg/utils/test/matchers" ) @@ -74,10 +74,11 @@ var _ = Describe("Reconciler", func() { fakeClock = testclock.NewFakeClock(time.Now().Round(time.Second)) - c = test.NewClientWithFieldSelectorSupport( - fakeclient.NewClientBuilder().WithScheme(kubernetes.GardenScheme).WithObjects(seed).Build(), - backupbucketregistry.ToSelectableFields, - ) + c = fakeclient.NewClientBuilder(). + WithScheme(kubernetes.GardenScheme). + WithObjects(seed). + WithIndex(&gardencorev1beta1.BackupBucket{}, core.BackupBucketSeedName, indexer.BackupBucketSeedNameIndexerFunc). + Build() conf = config.SeedBackupBucketsCheckControllerConfiguration{ SyncPeriod: &metav1.Duration{Duration: syncPeriod}, diff --git a/pkg/controllermanager/controller/seed/extensionscheck/reconciler_test.go b/pkg/controllermanager/controller/seed/extensionscheck/reconciler_test.go index be661327e33..d54a43a257b 100644 --- a/pkg/controllermanager/controller/seed/extensionscheck/reconciler_test.go +++ b/pkg/controllermanager/controller/seed/extensionscheck/reconciler_test.go @@ -28,6 +28,8 @@ import ( fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/gardener/gardener/pkg/api/indexer" + "github.com/gardener/gardener/pkg/apis/core" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" "github.com/gardener/gardener/pkg/client/kubernetes" "github.com/gardener/gardener/pkg/controllermanager/apis/config" @@ -64,7 +66,11 @@ var _ = Describe("Reconciler", func() { fakeClock = testclock.NewFakeClock(time.Now().Round(time.Second)) - c = fakeclient.NewClientBuilder().WithScheme(kubernetes.GardenScheme).WithObjects(seed).Build() + c = fakeclient.NewClientBuilder(). + WithScheme(kubernetes.GardenScheme). + WithObjects(seed). + WithIndex(&gardencorev1beta1.ControllerInstallation{}, core.SeedRefName, indexer.ControllerInstallationSeedRefNameIndexerFunc). + Build() conf := config.SeedExtensionsCheckControllerConfiguration{ SyncPeriod: &metav1.Duration{Duration: syncPeriodDuration}, } diff --git a/pkg/gardenlet/controller/controllerinstallation/required/add_test.go b/pkg/gardenlet/controller/controllerinstallation/required/add_test.go index cd699e4332f..8bb2fc532bf 100644 --- a/pkg/gardenlet/controller/controllerinstallation/required/add_test.go +++ b/pkg/gardenlet/controller/controllerinstallation/required/add_test.go @@ -31,6 +31,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/gardener/gardener/pkg/api/indexer" + "github.com/gardener/gardener/pkg/apis/core" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" "github.com/gardener/gardener/pkg/client/kubernetes" @@ -124,7 +126,10 @@ var _ = Describe("Add", func() { ) BeforeEach(func() { - fakeGardenClient = fakeclient.NewClientBuilder().WithScheme(kubernetes.GardenScheme).Build() + fakeGardenClient = fakeclient.NewClientBuilder(). + WithScheme(kubernetes.GardenScheme). + WithIndex(&gardencorev1beta1.ControllerInstallation{}, core.SeedRefName, indexer.ControllerInstallationSeedRefNameIndexerFunc). + Build() fakeSeedClient = fakeclient.NewClientBuilder().WithScheme(kubernetes.SeedScheme).Build() mapFn = reconciler.MapObjectKindToControllerInstallations(extensionsv1alpha1.InfrastructureResource, func() client.ObjectList { return &extensionsv1alpha1.InfrastructureList{} }) diff --git a/pkg/operation/botanist/component/kubeapiserverexposure/kube_apiserver_sni_test.go b/pkg/operation/botanist/component/kubeapiserverexposure/kube_apiserver_sni_test.go index ffa3eb5fa6a..53324b1b372 100644 --- a/pkg/operation/botanist/component/kubeapiserverexposure/kube_apiserver_sni_test.go +++ b/pkg/operation/botanist/component/kubeapiserverexposure/kube_apiserver_sni_test.go @@ -289,7 +289,17 @@ var _ = Describe("#SNI", func() { // TODO(mvladev): can't directly import the istio apis due to dependency issues. s.AddKnownTypeWithName(schema.FromAPIVersionAndKind("networking.istio.io/v1beta1", "VirtualServiceList"), &unstructured.UnstructuredList{}) s.AddKnownTypeWithName(schema.FromAPIVersionAndKind("networking.istio.io/v1beta1", "VirtualService"), &unstructured.Unstructured{}) - c = fake.NewClientBuilder().WithScheme(s).Build() + virtualServiceObj := &unstructured.Unstructured{} + virtualServiceObj.SetGroupVersionKind(schema.FromAPIVersionAndKind("networking.istio.io/v1beta1", "VirtualService")) + + c = fake.NewClientBuilder(). + WithScheme(s). + WithIndex( + virtualServiceObj, "metadata.name", func(o client.Object) []string { + return []string{o.GetName()} + }, + ). + Build() }) It("returns true when exists", func() { diff --git a/pkg/utils/kubernetes/managedseed_test.go b/pkg/utils/kubernetes/managedseed_test.go index 96d23a849e5..b36290eb7cb 100644 --- a/pkg/utils/kubernetes/managedseed_test.go +++ b/pkg/utils/kubernetes/managedseed_test.go @@ -27,6 +27,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "github.com/gardener/gardener/pkg/api/indexer" + "github.com/gardener/gardener/pkg/apis/seedmanagement" seedmanagementv1alpha1 "github.com/gardener/gardener/pkg/apis/seedmanagement/v1alpha1" "github.com/gardener/gardener/pkg/client/kubernetes" mockclient "github.com/gardener/gardener/pkg/mock/controller-runtime/client" @@ -66,7 +68,11 @@ var _ = Describe("managedseed", func() { ) BeforeEach(func() { - fakeClient = fakeclient.NewClientBuilder().WithScheme(kubernetes.GardenScheme).WithObjects(managedSeed).Build() + fakeClient = fakeclient.NewClientBuilder(). + WithScheme(kubernetes.GardenScheme). + WithObjects(managedSeed). + WithIndex(&seedmanagementv1alpha1.ManagedSeed{}, seedmanagement.ManagedSeedShootName, indexer.ManagedSeedShootNameIndexerFunc). + Build() }) It("should return the ManagedSeed for the given shoot namespace and name, if it exists", func() { diff --git a/pkg/utils/test/client.go b/pkg/utils/test/client.go deleted file mode 100644 index 19d92bbbdc4..00000000000 --- a/pkg/utils/test/client.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 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 test - -import ( - "context" - "fmt" - - "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/gardener/gardener/pkg/client/kubernetes" -) - -// NewClientWithFieldSelectorSupport takes a fake client and a function that returns selectable fields for the type T -// and adds support for field selectors to the client. -// TODO(plkokanov): remove this once the controller-runtime fake client supports field selectors. -func NewClientWithFieldSelectorSupport[T any](c client.Client, toSelectableFieldsFunc func(t *T) fields.Set) client.Client { - return &clientWithFieldSelectorSupport[T]{ - Client: c, - toSelectableFieldsFunc: toSelectableFieldsFunc, - } -} - -type clientWithFieldSelectorSupport[T any] struct { - client.Client - toSelectableFieldsFunc func(t *T) fields.Set -} - -func (c clientWithFieldSelectorSupport[T]) List(ctx context.Context, obj client.ObjectList, opts ...client.ListOption) error { - if err := c.Client.List(ctx, obj, opts...); err != nil { - return err - } - - listOpts := client.ListOptions{} - listOpts.ApplyOptions(opts) - - if listOpts.FieldSelector != nil { - objs, err := meta.ExtractList(obj) - if err != nil { - return err - } - filteredObjs, err := c.filterWithFieldSelector(objs, listOpts.FieldSelector) - if err != nil { - return err - } - err = meta.SetList(obj, filteredObjs) - if err != nil { - return err - } - } - - return nil -} - -func (c clientWithFieldSelectorSupport[T]) filterWithFieldSelector(objs []runtime.Object, sel fields.Selector) ([]runtime.Object, error) { - outItems := make([]runtime.Object, 0, len(objs)) - for _, obj := range objs { - // convert to internal - internalObj := new(T) - if err := kubernetes.GardenScheme.Convert(obj, internalObj, nil); err != nil { - return nil, err - } - - fieldSet := c.toSelectableFieldsFunc(internalObj) - - // complain about non-selectable fields if any - for _, req := range sel.Requirements() { - if !fieldSet.Has(req.Field) { - return nil, fmt.Errorf("field selector not supported for field %q", req.Field) - } - } - - if !sel.Matches(fieldSet) { - continue - } - outItems = append(outItems, obj.DeepCopyObject()) - } - return outItems, nil -}