From a41e2b77fbeb51e3ca8ab60ff638bd79ec16b551 Mon Sep 17 00:00:00 2001 From: Ismail Alidzhikov <9372594+ialidzhikov@users.noreply.github.com> Date: Wed, 11 Dec 2024 14:21:14 +0200 Subject: [PATCH] Address PR review feedback (3) --- .../registrycaches/registry_caches.go | 10 +- .../registrycaches/registry_caches_test.go | 2 +- .../registry_cache_services_test.go | 92 +++---- pkg/controller/cache/actuator.go | 16 +- pkg/webhook/cache/ensurer_test.go | 246 +++++++++++++++++- 5 files changed, 295 insertions(+), 71 deletions(-) diff --git a/pkg/component/registrycaches/registry_caches.go b/pkg/component/registrycaches/registry_caches.go index 3abcb1a6..7188c8ce 100644 --- a/pkg/component/registrycaches/registry_caches.go +++ b/pkg/component/registrycaches/registry_caches.go @@ -85,25 +85,25 @@ type Values struct { KeepObjectsOnDestroy bool } -// NewComponent creates a new instance of Interface for registry caches. -func NewComponent( +// New creates a new instance of Interface for registry caches. +func New( client client.Client, - secretManager secretsmanager.Interface, namespace string, + secretManager secretsmanager.Interface, values Values, ) Interface { return ®istryCaches{ client: client, - secretManager: secretManager, namespace: namespace, + secretManager: secretManager, values: values, } } type registryCaches struct { client client.Client - secretManager secretsmanager.Interface namespace string + secretManager secretsmanager.Interface values Values caSecretName string diff --git a/pkg/component/registrycaches/registry_caches_test.go b/pkg/component/registrycaches/registry_caches_test.go index 18158d99..5a35513c 100644 --- a/pkg/component/registrycaches/registry_caches_test.go +++ b/pkg/component/registrycaches/registry_caches_test.go @@ -133,7 +133,7 @@ var _ = Describe("RegistryCaches", func() { }) JustBeforeEach(func() { - registryCaches = NewComponent(c, secretsManager, namespace, values) + registryCaches = New(c, namespace, secretsManager, values) }) Describe("#Deploy", func() { diff --git a/pkg/component/registrycacheservices/registry_cache_services_test.go b/pkg/component/registrycacheservices/registry_cache_services_test.go index 956034d5..03b5c71e 100644 --- a/pkg/component/registrycacheservices/registry_cache_services_test.go +++ b/pkg/component/registrycacheservices/registry_cache_services_test.go @@ -19,10 +19,12 @@ import ( . "github.com/gardener/gardener/pkg/utils/test/matchers" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/onsi/gomega/types" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/intstr" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" @@ -37,7 +39,7 @@ func TestRegistryCacheServices(t *testing.T) { RunSpecs(t, "Component RegistryCacheServices Suite") } -var _ = Describe("RegistryCaches", func() { +var _ = Describe("RegistryCacheServices", func() { const ( managedResourceName = "extension-registry-cache-services" @@ -51,8 +53,39 @@ var _ = Describe("RegistryCaches", func() { values Values managedResource *resourcesv1alpha1.ManagedResource managedResourceSecret *corev1.Secret + consistOf func(...client.Object) types.GomegaMatcher registryCacheServices component.DeployWaiter + + serviceFor = func(name, upstream, remoteURL string) *corev1.Service { + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "kube-system", + Labels: map[string]string{ + "app": name, + "upstream-host": upstream, + }, + Annotations: map[string]string{ + "upstream": upstream, + "remote-url": remoteURL, + }, + }, + Spec: corev1.ServiceSpec{ + Selector: map[string]string{ + "app": name, + "upstream-host": upstream, + }, + Ports: []corev1.ServicePort{{ + Name: "registry-cache", + Port: 5000, + Protocol: corev1.ProtocolTCP, + TargetPort: intstr.FromString("registry-cache"), + }}, + Type: corev1.ServiceTypeClusterIP, + }, + } + } ) BeforeEach(func() { @@ -80,6 +113,7 @@ var _ = Describe("RegistryCaches", func() { Namespace: namespace, }, } + consistOf = NewManagedResourceConsistOfObjectsMatcher(c) }) JustBeforeEach(func() { @@ -87,36 +121,7 @@ var _ = Describe("RegistryCaches", func() { }) Describe("#Deploy", func() { - var serviceYAMLFor = func(name, upstream, remoteURL string) string { - return `apiVersion: v1 -kind: Service -metadata: - annotations: - remote-url: ` + remoteURL + ` - upstream: ` + upstream + ` - creationTimestamp: null - labels: - app: ` + name + ` - upstream-host: ` + upstream + ` - name: ` + name + ` - namespace: kube-system -spec: - ports: - - name: registry-cache - port: 5000 - protocol: TCP - targetPort: registry-cache - selector: - app: ` + name + ` - upstream-host: ` + upstream + ` - type: ClusterIP -status: - loadBalancer: {} -` - } - It("should successfully deploy the resources", func() { - Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(MatchError(apierrors.NewNotFound(schema.GroupResource{Group: resourcesv1alpha1.SchemeGroupVersion.Group, Resource: "managedresources"}, managedResource.Name))) Expect(registryCacheServices.Deploy(ctx)).To(Succeed()) Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(Succeed()) @@ -138,33 +143,18 @@ status: utilruntime.Must(references.InjectAnnotations(expectedMr)) Expect(managedResource).To(DeepEqual(expectedMr)) - managedResourceSecret.Name = managedResource.Spec.SecretRefs[0].Name - Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResourceSecret), managedResourceSecret)).To(Succeed()) - Expect(managedResourceSecret.Type).To(Equal(corev1.SecretTypeOpaque)) - Expect(managedResourceSecret.Immutable).To(Equal(ptr.To(true))) - Expect(managedResourceSecret.Labels["resources.gardener.cloud/garbage-collectable-reference"]).To(Equal("true")) - - manifests, err := test.ExtractManifestsFromManagedResourceData(managedResourceSecret.Data) - Expect(err).NotTo(HaveOccurred()) - Expect(manifests).To(HaveLen(2)) - - expectedManifests := []string{ - serviceYAMLFor("registry-docker-io", "docker.io", "https://registry-1.docker.io"), - serviceYAMLFor("registry-europe-docker-pkg-dev", "europe-docker.pkg.dev", "https://europe-docker.pkg.dev"), - } - Expect(manifests).To(ConsistOf(expectedManifests)) - Expect(manifests).To(ConsistOf(expectedManifests)) + Expect(managedResource).To(consistOf( + serviceFor("registry-docker-io", "docker.io", "https://registry-1.docker.io"), + serviceFor("registry-europe-docker-pkg-dev", "europe-docker.pkg.dev", "https://europe-docker.pkg.dev"), + )) }) }) - Describe("#Delete", func() { + Describe("#Destroy", func() { It("should successfully destroy all resources", func() { Expect(c.Create(ctx, managedResource)).To(Succeed()) Expect(c.Create(ctx, managedResourceSecret)).To(Succeed()) - Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(Succeed()) - Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResourceSecret), managedResourceSecret)).To(Succeed()) - Expect(registryCacheServices.Destroy(ctx)).To(Succeed()) Expect(c.Get(ctx, client.ObjectKeyFromObject(managedResource), managedResource)).To(MatchError(apierrors.NewNotFound(schema.GroupResource{Group: resourcesv1alpha1.SchemeGroupVersion.Group, Resource: "managedresources"}, managedResource.Name))) @@ -237,6 +227,8 @@ status: Describe("#WaitCleanup", func() { It("should fail when the wait for the managed resource deletion times out", func() { + fakeOps.MaxAttempts = 2 + Expect(c.Create(ctx, managedResource)).To(Succeed()) Expect(registryCacheServices.WaitCleanup(ctx)).To(MatchError(ContainSubstring("still exists"))) diff --git a/pkg/controller/cache/actuator.go b/pkg/controller/cache/actuator.go index 3a25c7f1..46d8f98a 100644 --- a/pkg/controller/cache/actuator.go +++ b/pkg/controller/cache/actuator.go @@ -107,7 +107,7 @@ func (a *actuator) Reconcile(ctx context.Context, logger logr.Logger, ex *extens return fmt.Errorf("failed to find the registry image: %w", err) } - registryCaches := registrycaches.NewComponent(a.client, secretsManager, namespace, registrycaches.Values{ + registryCaches := registrycaches.New(a.client, namespace, secretsManager, registrycaches.Values{ Image: image.String(), VPAEnabled: v1beta1helper.ShootWantsVerticalPodAutoscaler(cluster.Shoot), Services: services, @@ -147,10 +147,10 @@ func (a *actuator) Delete(ctx context.Context, logger logr.Logger, ex *extension registryCacheServices := registrycacheservices.New(a.client, namespace, registrycacheservices.Values{}) if err := component.OpDestroyAndWait(registryCacheServices).Destroy(ctx); err != nil { - return fmt.Errorf("failed to destroy the registry caches component: %w", err) + return fmt.Errorf("failed to destroy the registry cache services component: %w", err) } - registryCaches := registrycaches.NewComponent(a.client, secretsManager, namespace, registrycaches.Values{}) + registryCaches := registrycaches.New(a.client, namespace, secretsManager, registrycaches.Values{}) if err := component.OpDestroyAndWait(registryCaches).Destroy(ctx); err != nil { return fmt.Errorf("failed to destroy the registry caches component: %w", err) } @@ -174,7 +174,7 @@ func (a *actuator) Migrate(ctx context.Context, _ logr.Logger, ex *extensionsv1a return fmt.Errorf("failed to destroy the registry cache services component: %w", err) } - registryCaches := registrycaches.NewComponent(a.client, nil, namespace, registrycaches.Values{ + registryCaches := registrycaches.New(a.client, namespace, nil, registrycaches.Values{ KeepObjectsOnDestroy: true, }) if err := component.OpDestroyAndWait(registryCaches).Destroy(ctx); err != nil { @@ -201,12 +201,12 @@ func (a *actuator) ForceDelete(ctx context.Context, logger logr.Logger, ex *exte } registryCacheServices := registrycacheservices.New(a.client, namespace, registrycacheservices.Values{}) - if err := component.OpDestroyAndWait(registryCacheServices).Destroy(ctx); err != nil { - return fmt.Errorf("failed to destroy the registry caches component: %w", err) + if err := registryCacheServices.Destroy(ctx); err != nil { + return fmt.Errorf("failed to destroy the registry cache services component: %w", err) } - registryCaches := registrycaches.NewComponent(a.client, secretsManager, namespace, registrycaches.Values{}) - if err := component.OpDestroy(registryCaches).Destroy(ctx); err != nil { + registryCaches := registrycaches.New(a.client, namespace, secretsManager, registrycaches.Values{}) + if err := registryCaches.Destroy(ctx); err != nil { return fmt.Errorf("failed to destroy the registry caches component: %w", err) } diff --git a/pkg/webhook/cache/ensurer_test.go b/pkg/webhook/cache/ensurer_test.go index b8abffdc..b1153309 100644 --- a/pkg/webhook/cache/ensurer_test.go +++ b/pkg/webhook/cache/ensurer_test.go @@ -6,6 +6,7 @@ package cache_test import ( "context" + "encoding/base64" "testing" extensionscontextwebhook "github.com/gardener/gardener/extensions/pkg/webhook/context" @@ -34,6 +35,8 @@ func TestRegistryCacheWebhook(t *testing.T) { } var _ = Describe("Ensurer", func() { + const namespace = "shoot--foo--bar" + var ( logger = logr.Discard() ctx = context.Background() @@ -45,6 +48,7 @@ var _ = Describe("Ensurer", func() { BeforeEach(func() { scheme := runtime.NewScheme() Expect(extensionsv1alpha1.AddToScheme(scheme)).To(Succeed()) + Expect(corev1.AddToScheme(scheme)).To(Succeed()) registryinstall.Install(scheme) decoder = serializer.NewCodecFactory(scheme, serializer.EnableStrict).UniversalDecoder() @@ -60,14 +64,14 @@ var _ = Describe("Ensurer", func() { BeforeEach(func() { cluster = &extensions.Cluster{ - ObjectMeta: metav1.ObjectMeta{Name: "shoot--foo--bar"}, + ObjectMeta: metav1.ObjectMeta{Name: namespace}, Shoot: &gardencorev1beta1.Shoot{}, } extension = &extensionsv1alpha1.Extension{ ObjectMeta: metav1.ObjectMeta{ Name: "registry-cache", - Namespace: cluster.ObjectMeta.Name, + Namespace: namespace, }, Status: extensionsv1alpha1.ExtensionStatus{ DefaultStatus: extensionsv1alpha1.DefaultStatus{ @@ -121,7 +125,7 @@ var _ = Describe("Ensurer", func() { It("should return err when it fails to get the cluster", func() { osc := &extensionsv1alpha1.OperatingSystemConfig{ ObjectMeta: metav1.ObjectMeta{ - Namespace: "shoot--foo--bar", + Namespace: namespace, }, } gctx := extensionscontextwebhook.NewGardenContext(fakeClient, osc) @@ -130,7 +134,7 @@ var _ = Describe("Ensurer", func() { err := ensurer.EnsureCRIConfig(ctx, gctx, &criConfig, nil) Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("failed to get the cluster resource: could not get cluster for namespace 'shoot--foo--bar'"))) + Expect(err).To(MatchError(ContainSubstring("failed to get the cluster resource: could not get cluster for namespace '%s'", namespace))) }) It("should do nothing if the shoot has a deletion timestamp set", func() { @@ -165,7 +169,7 @@ var _ = Describe("Ensurer", func() { err := ensurer.EnsureCRIConfig(ctx, gctx, &criConfig, nil) Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("failed to get extension 'shoot--foo--bar/registry-cache'"))) + Expect(err).To(MatchError(ContainSubstring("failed to get extension '%s/registry-cache'", namespace))) }) It("should return err when extension .status.providerStatus is nil", func() { @@ -178,7 +182,7 @@ var _ = Describe("Ensurer", func() { err := ensurer.EnsureCRIConfig(ctx, gctx, &criConfig, nil) Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("extension 'shoot--foo--bar/registry-cache' does not have a .status.providerStatus specified"))) + Expect(err).To(MatchError(ContainSubstring("extension '%s/registry-cache' does not have a .status.providerStatus specified", namespace))) }) It("should return err when extension .status.providerStatus cannot be decoded", func() { @@ -191,7 +195,7 @@ var _ = Describe("Ensurer", func() { err := ensurer.EnsureCRIConfig(ctx, gctx, &criConfig, nil) Expect(err).To(HaveOccurred()) - Expect(err).To(MatchError(ContainSubstring("failed to decode providerStatus of extension 'shoot--foo--bar/registry-cache'"))) + Expect(err).To(MatchError(ContainSubstring("failed to decode providerStatus of extension '%s/registry-cache'", namespace))) }) It("should add additional registry config to a nil containerd registry configs", func() { @@ -254,6 +258,234 @@ var _ = Describe("Ensurer", func() { Expect(criConfig.Containerd.Registries).To(ConsistOf(expectedRegistries)) }) }) + + Describe("#EnsureAdditionalFiles", func() { + const caSecretName = "ca-extension-registry-cache-bundle-1a2b3c4d" + + var ( + cluster *extensions.Cluster + extension *extensionsv1alpha1.Extension + caSecret *corev1.Secret + + newFiles []extensionsv1alpha1.File + ) + + BeforeEach(func() { + cluster = &extensions.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: namespace}, + Shoot: &gardencorev1beta1.Shoot{}, + } + + extension = &extensionsv1alpha1.Extension{ + ObjectMeta: metav1.ObjectMeta{ + Name: "registry-cache", + Namespace: cluster.ObjectMeta.Name, + }, + Status: extensionsv1alpha1.ExtensionStatus{ + DefaultStatus: extensionsv1alpha1.DefaultStatus{ + ProviderStatus: &runtime.RawExtension{ + Object: &v1alpha3.RegistryStatus{ + TypeMeta: metav1.TypeMeta{ + APIVersion: v1alpha3.SchemeGroupVersion.String(), + Kind: "RegistryStatus", + }, + CASecretName: caSecretName, + }, + }, + }, + }, + } + caSecret = &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: caSecretName, + Namespace: namespace, + }, + Data: map[string][]byte{ + "bundle.crt": []byte("bar"), + }, + } + + newFiles = []extensionsv1alpha1.File{ + { + Path: "/var/lib/foo/bar.txt", + Content: extensionsv1alpha1.FileContent{ + Inline: &extensionsv1alpha1.FileContentInline{ + Data: "plain-text", + }, + }, + }, + } + }) + + It("should return err when it fails to get the cluster", func() { + osc := &extensionsv1alpha1.OperatingSystemConfig{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + } + gctx := extensionscontextwebhook.NewGardenContext(fakeClient, osc) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + + err := ensurer.EnsureAdditionalFiles(ctx, gctx, nil, nil) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(ContainSubstring("failed to get the cluster resource: could not get cluster for namespace '%s'", namespace))) + }) + + It("should do nothing if the shoot has a deletion timestamp set", func() { + deletionTimestamp := metav1.Now() + cluster.Shoot.ObjectMeta.DeletionTimestamp = &deletionTimestamp + + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + expectedNewFiles := make([]extensionsv1alpha1.File, len(newFiles)) + copy(expectedNewFiles, newFiles) + + Expect(ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil)).To(Succeed()) + Expect(newFiles).To(Equal(expectedNewFiles)) + }) + + It("should do nothing if hibernation is enabled for Shoot", func() { + cluster.Shoot.Spec.Hibernation = &gardencorev1beta1.Hibernation{Enabled: ptr.To(true)} + + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + expectedNewFiles := make([]extensionsv1alpha1.File, len(newFiles)) + copy(expectedNewFiles, newFiles) + + Expect(ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil)).To(Succeed()) + Expect(newFiles).To(Equal(expectedNewFiles)) + }) + + It("return err when it fails to get the extension", func() { + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + + err := ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(ContainSubstring("failed to get extension '%s/registry-cache'", namespace))) + }) + + It("should return err when extension .status.providerStatus is nil", func() { + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + extension.Status.DefaultStatus.ProviderStatus = nil + + Expect(fakeClient.Create(ctx, extension)).To(Succeed()) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + + err := ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(ContainSubstring("extension '%s/registry-cache' does not have a .status.providerStatus specified", namespace))) + }) + + It("should return err when extension .status.providerStatus cannot be decoded", func() { + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + extension.Status.DefaultStatus.ProviderStatus = &runtime.RawExtension{Object: &corev1.Pod{}} + + Expect(fakeClient.Create(ctx, extension)).To(Succeed()) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + + err := ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(ContainSubstring("failed to decode providerStatus of extension '%s/registry-cache'", namespace))) + }) + + It("should return err when the CA bundle secret does not exist", func() { + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + + Expect(fakeClient.Create(ctx, extension)).To(Succeed()) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + + err := ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(ContainSubstring("failed to get CA bundle secret '%s/%s'", namespace, caSecretName))) + }) + + It("should return err when the CA bundle secret does not contain the required field", func() { + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + + caSecret.Data = map[string][]byte{ + "foo": []byte("bar"), + } + Expect(fakeClient.Create(ctx, caSecret)).To(Succeed()) + Expect(fakeClient.Create(ctx, extension)).To(Succeed()) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + + err := ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil) + Expect(err).To(HaveOccurred()) + Expect(err).To(MatchError(ContainSubstring("failed to find 'bundle.crt' key in the CA bundle secret '%s/%s'", namespace, caSecretName))) + }) + + It("should add additional file to the current files", func() { + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + + Expect(fakeClient.Create(ctx, caSecret)).To(Succeed()) + Expect(fakeClient.Create(ctx, extension)).To(Succeed()) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + expectedNewFiles := make([]extensionsv1alpha1.File, len(newFiles)) + copy(expectedNewFiles, newFiles) + expectedNewFiles = append(expectedNewFiles, + extensionsv1alpha1.File{ + Path: "/etc/containerd/certs.d/ca-bundle.pem", + Permissions: ptr.To[uint32](0644), + Content: extensionsv1alpha1.FileContent{ + Inline: &extensionsv1alpha1.FileContentInline{ + Encoding: "b64", + Data: base64.StdEncoding.EncodeToString([]byte("bar")), + }, + }, + }, + ) + + Expect(ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil)).To(Succeed()) + Expect(newFiles).To(ConsistOf(expectedNewFiles)) + }) + + It("should update file with the expected content if it already exists", func() { + gctx := extensionscontextwebhook.NewInternalGardenContext(cluster) + + Expect(fakeClient.Create(ctx, caSecret)).To(Succeed()) + Expect(fakeClient.Create(ctx, extension)).To(Succeed()) + + ensurer := cache.NewEnsurer(fakeClient, decoder, logger) + + newFiles = append(newFiles, + extensionsv1alpha1.File{ + Path: "/etc/containerd/certs.d/ca-bundle.pem", + Permissions: ptr.To[uint32](0642), + Content: extensionsv1alpha1.FileContent{ + Inline: &extensionsv1alpha1.FileContentInline{ + Encoding: "b64", + Data: base64.StdEncoding.EncodeToString([]byte("old-content")), + }, + }, + }, + ) + expectedNewFiles := make([]extensionsv1alpha1.File, len(newFiles)) + copy(expectedNewFiles, newFiles) + expectedNewFiles[1] = extensionsv1alpha1.File{ + Path: "/etc/containerd/certs.d/ca-bundle.pem", + Permissions: ptr.To[uint32](0644), + Content: extensionsv1alpha1.FileContent{ + Inline: &extensionsv1alpha1.FileContentInline{ + Encoding: "b64", + Data: base64.StdEncoding.EncodeToString([]byte("bar")), + }, + }, + } + + Expect(ensurer.EnsureAdditionalFiles(ctx, gctx, &newFiles, nil)).To(Succeed()) + Expect(newFiles).To(ConsistOf(expectedNewFiles)) + }) + }) }) func createRegistryConfig(upstream, server, host string) extensionsv1alpha1.RegistryConfig {