From 77de86dc333661b2b3da12e44f4f0eb7f0f4b000 Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Wed, 23 Nov 2022 00:50:02 -0500 Subject: [PATCH] (bugfix): update run bundle(-upgrade) logic (#6182) * update run bundle(-upgrade) logic to be able to handle large FBCs by partitioning them into multiple ConfigMaps mounted as volumes * add changelog * fix lint issues * add fbc registry pod unit tests * add unit tests Signed-off-by: rashmigottipati --- changelog/fragments/04-rbu-large-fbc.yaml | 19 + .../registry/fbcindex/fbc_registry_pod.go | 193 +++++++--- .../fbcindex/fbc_registry_pod_test.go | 334 ++++++++++++++++++ testdata/go/v3/memcached-operator/README.md | 2 +- .../api/v1alpha1/groupversion_info.go | 2 +- .../api/v1alpha1/memcached_types.go | 2 +- .../api/v1alpha1/memcached_webhook.go | 2 +- .../api/v1alpha1/webhook_suite_test.go | 2 +- .../api/v1alpha1/zz_generated.deepcopy.go | 2 +- .../controllers/memcached_controller.go | 2 +- .../controllers/memcached_controller_test.go | 2 +- .../controllers/suite_test.go | 2 +- .../hack/boilerplate.go.txt | 2 +- testdata/go/v3/memcached-operator/main.go | 2 +- .../go/v4-alpha/memcached-operator/README.md | 2 +- .../api/v1alpha1/groupversion_info.go | 2 +- .../api/v1alpha1/memcached_types.go | 2 +- .../api/v1alpha1/memcached_webhook.go | 2 +- .../api/v1alpha1/webhook_suite_test.go | 2 +- .../api/v1alpha1/zz_generated.deepcopy.go | 2 +- .../controllers/memcached_controller.go | 2 +- .../controllers/memcached_controller_test.go | 2 +- .../controllers/suite_test.go | 2 +- .../hack/boilerplate.go.txt | 2 +- .../go/v4-alpha/memcached-operator/main.go | 2 +- 25 files changed, 520 insertions(+), 70 deletions(-) create mode 100644 changelog/fragments/04-rbu-large-fbc.yaml create mode 100644 internal/olm/operator/registry/fbcindex/fbc_registry_pod_test.go diff --git a/changelog/fragments/04-rbu-large-fbc.yaml b/changelog/fragments/04-rbu-large-fbc.yaml new file mode 100644 index 0000000000..cee3b6ee49 --- /dev/null +++ b/changelog/fragments/04-rbu-large-fbc.yaml @@ -0,0 +1,19 @@ +# entries is a list of entries to include in +# release notes and/or the migration guide +entries: + - description: > + For `operator-sdk run bundle(-upgrade)`: fix a bug in the logic that would attempt to + create a `ConfigMap` that contained the entire contents of an FBC. Now if the FBC contents + are to large to fit into a single `ConfigMap`, the FBC contents will be partitioned and split + amongst multiple `ConfigMap` resources. + + # kind is one of: + # - addition + # - change + # - deprecation + # - removal + # - bugfix + kind: "bugfix" + + # Is this a breaking change? + breaking: false diff --git a/internal/olm/operator/registry/fbcindex/fbc_registry_pod.go b/internal/olm/operator/registry/fbcindex/fbc_registry_pod.go index b56f86eb1d..9099060ebe 100644 --- a/internal/olm/operator/registry/fbcindex/fbc_registry_pod.go +++ b/internal/olm/operator/registry/fbcindex/fbc_registry_pod.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "path" + "strings" "text/template" "time" @@ -46,8 +47,9 @@ const ( defaultContainerName = "registry-grpc" defaultContainerPortName = "grpc" - defaultConfigMapName = "operator-sdk-run-bundle-config" - defaultConfigMapKey = "extraFBC" + defaultConfigMapKey = "extraFBC" + + maxConfigMapSize = 1 * 1024 * 1024 ) // FBCRegistryPod holds resources necessary for creation of a registry pod in FBC scenarios. @@ -76,6 +78,8 @@ type FBCRegistryPod struct { //nolint:maligned // SecurityContext on the Pod SecurityContext string + configMapName string + cfg *operator.Configuration } @@ -89,6 +93,10 @@ func (f *FBCRegistryPod) init(cfg *operator.Configuration, cs *v1alpha1.CatalogS f.FBCIndexRootDir = fmt.Sprintf("/%s-configs", cs.Name) } + if f.configMapName == "" { + f.configMapName = fmt.Sprintf("%s-configmap", cs.Name) + } + f.cfg = cfg // validate the FBCRegistryPod struct and ensure required fields are set @@ -210,11 +218,39 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core // create ConfigMap if it does not exist, // if it exists, then update it with new content. - cm, err := f.createConfigMap(cs) + cms, err := f.createConfigMaps(cs) if err != nil { return nil, fmt.Errorf("configMap error: %w", err) } + volumes := []corev1.Volume{} + volumeMounts := []corev1.VolumeMount{} + + for _, cm := range cms { + volumes = append(volumes, corev1.Volume{ + Name: k8sutil.TrimDNS1123Label(cm.Name + "-volume"), + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + Items: []corev1.KeyToPath{ + { + Key: defaultConfigMapKey, + Path: path.Join(cm.Name, fmt.Sprintf("%s.yaml", defaultConfigMapKey)), + }, + }, + LocalObjectReference: corev1.LocalObjectReference{ + Name: cm.Name, + }, + }, + }, + }) + + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: k8sutil.TrimDNS1123Label(cm.Name + "-volume"), + MountPath: path.Join(f.FBCIndexRootDir, cm.Name), + SubPath: cm.Name, + }) + } + // make the pod definition f.pod = &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -256,24 +292,7 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core // Type: corev1.SeccompProfileTypeRuntimeDefault, // }, // }, - Volumes: []corev1.Volume{ - { - Name: k8sutil.TrimDNS1123Label(cm.Name + "-volume"), - VolumeSource: corev1.VolumeSource{ - ConfigMap: &corev1.ConfigMapVolumeSource{ - Items: []corev1.KeyToPath{ - { - Key: defaultConfigMapKey, - Path: path.Join(defaultConfigMapName, fmt.Sprintf("%s.yaml", defaultConfigMapKey)), - }, - }, - LocalObjectReference: corev1.LocalObjectReference{ - Name: cm.Name, - }, - }, - }, - }, - }, + Volumes: volumes, Containers: []corev1.Container{ { Name: defaultContainerName, @@ -286,13 +305,7 @@ func (f *FBCRegistryPod) podForBundleRegistry(cs *v1alpha1.CatalogSource) (*core Ports: []corev1.ContainerPort{ {Name: defaultContainerPortName, ContainerPort: f.GRPCPort}, }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: k8sutil.TrimDNS1123Label(cm.Name + "-volume"), - MountPath: path.Join(f.FBCIndexRootDir, cm.Name), - SubPath: cm.Name, - }, - }, + VolumeMounts: volumeMounts, SecurityContext: &corev1.SecurityContext{ Privileged: pointer.Bool(false), ReadOnlyRootFilesystem: pointer.Bool(false), @@ -315,50 +328,134 @@ const fbcCmdTemplate = `opm serve {{ .FBCIndexRootDir}} -p {{ .GRPCPort }}` // createConfigMap creates a ConfigMap if it does not exist and if it does, then update it with new content. // Also, sets the owner reference by making CatalogSource the owner of ConfigMap object for cleanup purposes. -func (f *FBCRegistryPod) createConfigMap(cs *v1alpha1.CatalogSource) (*corev1.ConfigMap, error) { - // new ConfigMap - cm := &corev1.ConfigMap{ +func (f *FBCRegistryPod) createConfigMaps(cs *v1alpha1.CatalogSource) ([]*corev1.ConfigMap, error) { + // By default just use the partitioning logic. + // If the entire FBC contents can fit in one ConfigMap it will. + cms := f.partitionedConfigMaps() + + // Loop through all the ConfigMaps and set the OwnerReference and try to create them + for _, cm := range cms { + // set owner reference by making catalog source the owner of ConfigMap object + if err := controllerutil.SetOwnerReference(cs, cm, f.cfg.Scheme); err != nil { + return nil, fmt.Errorf("set configmap %q owner reference: %v", cm.GetName(), err) + } + + err := f.createOrUpdateConfigMap(cm) + if err != nil { + return nil, err + } + } + + return cms, nil +} + +// partitionedConfigMaps will create and return a list of *corev1.ConfigMap +// that represents all the ConfigMaps that will need to be created to +// properly have all the FBC contents rendered in the registry pod. +func (f *FBCRegistryPod) partitionedConfigMaps() []*corev1.ConfigMap { + // Split on the YAML separator `---` + yamlDefs := strings.Split(f.FBCContent, "---")[1:] + configMaps := []*corev1.ConfigMap{} + + // Keep the number of ConfigMaps that are created to a minimum by + // stuffing them as full as possible. + partitionCount := 1 + cm := f.makeBaseConfigMap() + // for each chunk of yaml see if it can be added to the ConfigMap partition + for _, yamlDef := range yamlDefs { + // If the ConfigMap has data then lets attempt to add to it + if len(cm.Data) != 0 { + // Create a copy to use to verify that adding the data doesn't + // exceed the max ConfigMap size of 1 MiB. + tempCm := cm.DeepCopy() + tempCm.Data[defaultConfigMapKey] = tempCm.Data[defaultConfigMapKey] + "\n---\n" + yamlDef + + // if it would be too large adding the data then partition it. + if tempCm.Size() >= maxConfigMapSize { + // Set the ConfigMap name based on the partition it is + cm.SetName(fmt.Sprintf("%s-partition-%d", f.configMapName, partitionCount)) + // Increase the partition count + partitionCount++ + // Add the ConfigMap to the list of ConfigMaps + configMaps = append(configMaps, cm.DeepCopy()) + + // Create a new ConfigMap + cm = f.makeBaseConfigMap() + // Since adding this data would have made the previous + // ConfigMap too large, add it to this new one. + // No chunk of YAML from the bundle should cause + // the ConfigMap size to exceed 1 MiB and if + // somehow it does then there is a problem with the + // YAML itself. We can't reasonably break it up smaller + // since it is a single object. + cm.Data[defaultConfigMapKey] = yamlDef + } else { + // if adding the data to the ConfigMap + // doesn't make the ConfigMap exceed the + // size limit then actually add it. + cm.Data = tempCm.Data + } + } else { + // If there is no data in the ConfigMap + // then this is the first pass. Since it is + // the first pass go ahead and add the data. + cm.Data[defaultConfigMapKey] = yamlDef + } + } + + // if there aren't as many ConfigMaps as partitions AND the unadded ConfigMap has data + // then add it to the list of ConfigMaps. This is so we don't miss adding a ConfigMap + // after the above loop completes. + if len(configMaps) != partitionCount && len(cm.Data) != 0 { + cm.SetName(fmt.Sprintf("%s-partition-%d", f.configMapName, partitionCount)) + configMaps = append(configMaps, cm.DeepCopy()) + } + + return configMaps +} + +// makeBaseConfigMap will return the base *corev1.ConfigMap +// definition that is used by various functions when creating a ConfigMap. +func (f *FBCRegistryPod) makeBaseConfigMap() *corev1.ConfigMap { + return &corev1.ConfigMap{ TypeMeta: metav1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), Kind: "ConfigMap", }, ObjectMeta: metav1.ObjectMeta{ - Name: defaultConfigMapName, Namespace: f.cfg.Namespace, }, - Data: map[string]string{ - defaultConfigMapKey: f.FBCContent, - }, - } - - // set owner reference by making catalog source the owner of ConfigMap object - if err := controllerutil.SetOwnerReference(cs, cm, f.cfg.Scheme); err != nil { - return nil, fmt.Errorf("set configmap %q owner reference: %v", cm.GetName(), err) + Data: map[string]string{}, } +} +// createOrUpdateConfigMap will create a ConfigMap if it doesn't exist or +// update it if it already exists. +func (f *FBCRegistryPod) createOrUpdateConfigMap(cm *corev1.ConfigMap) error { cmKey := types.NamespacedName{ - Namespace: f.cfg.Namespace, - Name: defaultConfigMapName, + Namespace: cm.GetNamespace(), + Name: cm.GetName(), } // create a ConfigMap if it does not exist; // update it with new data if it already exists. if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { - err := f.cfg.Client.Get(context.TODO(), cmKey, cm) + tempCm := &corev1.ConfigMap{} + err := f.cfg.Client.Get(context.TODO(), cmKey, tempCm) if apierrors.IsNotFound(err) { if err := f.cfg.Client.Create(context.TODO(), cm); err != nil { return fmt.Errorf("error creating ConfigMap: %w", err) } + return nil } // update ConfigMap with new FBCContent - cm.Data = map[string]string{defaultConfigMapKey: f.FBCContent} - return f.cfg.Client.Update(context.TODO(), cm) + tempCm.Data = cm.Data + return f.cfg.Client.Update(context.TODO(), tempCm) }); err != nil { - return nil, fmt.Errorf("error updating ConfigMap: %w", err) + return fmt.Errorf("error updating ConfigMap: %w", err) } - return cm, nil - + return nil } // getContainerCmd uses templating to construct the container command diff --git a/internal/olm/operator/registry/fbcindex/fbc_registry_pod_test.go b/internal/olm/operator/registry/fbcindex/fbc_registry_pod_test.go new file mode 100644 index 0000000000..94d11de023 --- /dev/null +++ b/internal/olm/operator/registry/fbcindex/fbc_registry_pod_test.go @@ -0,0 +1,334 @@ +// Copyright 2020 The Operator-SDK Authors +// +// 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 fbcindex + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-sdk/internal/olm/operator" + "github.com/operator-framework/operator-sdk/internal/olm/operator/registry/index" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" +) + +const testIndexImageTag = "some-image:v1.2.3" + +func TestCreateRegistryPod(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Test Registry Pod Suite") +} + +// newFakeClient() returns a fake controller runtime client +func newFakeClient() client.Client { + return fakeclient.NewClientBuilder().Build() +} + +var _ = Describe("FBCRegistryPod", func() { + + var defaultBundleItems = []index.BundleItem{{ + ImageTag: "quay.io/example/example-operator-bundle:0.2.0", + AddMode: index.SemverBundleAddMode, + }} + + Describe("creating registry pod", func() { + var ( + rp *FBCRegistryPod + cfg *operator.Configuration + cs *v1alpha1.CatalogSource + ) + + BeforeEach(func() { + cs = &v1alpha1.CatalogSource{ + ObjectMeta: v1.ObjectMeta{ + Name: "test-catalogsource", + }, + } + + schm := runtime.NewScheme() + Expect(v1alpha1.AddToScheme(schm)).ShouldNot(HaveOccurred()) + + cfg = &operator.Configuration{ + Client: newFakeClient(), + Namespace: "test-default", + Scheme: schm, + } + rp = &FBCRegistryPod{ + BundleItems: defaultBundleItems, + IndexImage: testIndexImageTag, + } + By("initializing the FBCRegistryPod") + Expect(rp.init(cfg, cs)).To(Succeed()) + }) + + Context("with valid registry pod values", func() { + It("should create the FBCRegistryPod successfully", func() { + expectedPodName := "quay-io-example-example-operator-bundle-0-2-0" + Expect(rp).NotTo(BeNil()) + Expect(rp.pod.Name).To(Equal(expectedPodName)) + Expect(rp.pod.Namespace).To(Equal(rp.cfg.Namespace)) + Expect(rp.pod.Spec.Containers[0].Name).To(Equal(defaultContainerName)) + if len(rp.pod.Spec.Containers) > 0 { + if len(rp.pod.Spec.Containers[0].Ports) > 0 { + Expect(rp.pod.Spec.Containers[0].Ports[0].ContainerPort).To(Equal(rp.GRPCPort)) + } + } + }) + + It("should create a registry pod when database path is not provided", func() { + Expect(rp.FBCIndexRootDir).To(Equal(fmt.Sprintf("/%s-configs", cs.Name))) + }) + + It("should return a valid container command for one image", func() { + output, err := rp.getContainerCmd() + Expect(err).To(BeNil()) + Expect(output).Should(Equal(containerCommandFor(rp.FBCIndexRootDir, rp.GRPCPort))) + }) + + It("should return a valid container command for three images", func() { + bundleItems := append(defaultBundleItems, + index.BundleItem{ + ImageTag: "quay.io/example/example-operator-bundle:0.3.0", + AddMode: index.ReplacesBundleAddMode, + }, + index.BundleItem{ + ImageTag: "quay.io/example/example-operator-bundle:1.0.1", + AddMode: index.SemverBundleAddMode, + }, + index.BundleItem{ + ImageTag: "localhost/example-operator-bundle:1.0.1", + AddMode: index.SemverBundleAddMode, + }, + ) + rp2 := FBCRegistryPod{ + GRPCPort: defaultGRPCPort, + BundleItems: bundleItems, + } + output, err := rp2.getContainerCmd() + Expect(err).To(BeNil()) + Expect(output).Should(Equal(containerCommandFor(rp2.FBCIndexRootDir, rp2.GRPCPort))) + }) + }) + + Context("with invalid registry pod values", func() { + It("should error when bundle image is not provided", func() { + expectedErr := "bundle image set cannot be empty" + rp := &FBCRegistryPod{} + err := rp.init(cfg, cs) + Expect(err).NotTo(BeNil()) + Expect(err.Error()).Should(ContainSubstring(expectedErr)) + }) + + It("checkPodStatus should return error when pod check is false and context is done", func() { + rp := &FBCRegistryPod{ + BundleItems: defaultBundleItems, + IndexImage: testIndexImageTag, + } + Expect(rp.init(cfg, cs)).To(Succeed()) + + mockBadPodCheck := wait.ConditionFunc(func() (done bool, err error) { + return false, fmt.Errorf("error waiting for registry pod") + }) + + expectedErr := "error waiting for registry pod" + // create a new context with a deadline of 1 millisecond + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond) + cancel() + + err := rp.checkPodStatus(ctx, mockBadPodCheck) + Expect(err).NotTo(BeNil()) + Expect(err.Error()).Should(ContainSubstring(expectedErr)) + }) + }) + + Context("creating a ConfigMap", func() { + It("makeBaseConfigMap() should return a basic ConfigMap manifest", func() { + cm := rp.makeBaseConfigMap() + Expect(cm.GetObjectKind().GroupVersionKind()).Should(Equal(corev1.SchemeGroupVersion.WithKind("ConfigMap"))) + Expect(cm.GetNamespace()).Should(Equal(cfg.Namespace)) + Expect(cm.Data).ShouldNot(BeNil()) + Expect(len(cm.Data)).Should(Equal(0)) + }) + + It("partitionedConfigMaps() should return a single ConfigMap", func() { + rp.FBCContent = testYaml + expectedYaml := "" + for i, yaml := range strings.Split(testYaml, "---")[1:] { + if i != 0 { + expectedYaml += "\n---\n" + } + + expectedYaml += yaml + } + cms := rp.partitionedConfigMaps() + Expect(len(cms)).Should(Equal(1)) + Expect(cms[0].Data).Should(HaveKey("extraFBC")) + Expect(cms[0].Data["extraFBC"]).Should(Equal(expectedYaml)) + }) + + It("partitionedConfigMaps() should return multiple ConfigMaps", func() { + // Create a large yaml manifest + largeYaml := "" + for i := len([]byte(largeYaml)); i < maxConfigMapSize; { + largeYaml += testYaml + i = len([]byte(largeYaml)) + } + + rp.FBCContent = largeYaml + + cms := rp.partitionedConfigMaps() + Expect(len(cms)).Should(Equal(2)) + Expect(cms[0].Data).Should(HaveKey("extraFBC")) + Expect(cms[0].Data["extraFBC"]).ShouldNot(BeEmpty()) + Expect(cms[1].Data).Should(HaveKey("extraFBC")) + Expect(cms[1].Data["extraFBC"]).ShouldNot(BeEmpty()) + }) + + It("createOrUpdateConfigMap() should create the ConfigMap if it does not exist", func() { + cm := rp.makeBaseConfigMap() + cm.SetName("test-cm") + cm.Data["test"] = "hello test world!" + + Expect(rp.createOrUpdateConfigMap(cm)).Should(Succeed()) + + testCm := &corev1.ConfigMap{} + Expect(rp.cfg.Client.Get(context.TODO(), types.NamespacedName{Namespace: rp.cfg.Namespace, Name: cm.GetName()}, testCm)).Should(Succeed()) + Expect(testCm).Should(BeEquivalentTo(cm)) + }) + + It("createOrUpdateConfigMap() should update the ConfigMap if it already exists", func() { + cm := rp.makeBaseConfigMap() + cm.SetName("test-cm") + cm.Data["test"] = "hello test world!" + Expect(rp.cfg.Client.Create(context.TODO(), cm)).Should(Succeed()) + cm.Data["test"] = "hello changed world!" + cm.SetResourceVersion("2") + + Expect(rp.createOrUpdateConfigMap(cm)).Should(Succeed()) + + testCm := &corev1.ConfigMap{} + Expect(rp.cfg.Client.Get(context.TODO(), types.NamespacedName{Namespace: rp.cfg.Namespace, Name: cm.GetName()}, testCm)).Should(Succeed()) + Expect(testCm).Should(BeEquivalentTo(cm)) + }) + + It("createConfigMaps() should create a single ConfigMap", func() { + rp.FBCContent = testYaml + expectedYaml := "" + for i, yaml := range strings.Split(testYaml, "---")[1:] { + if i != 0 { + expectedYaml += "\n---\n" + } + + expectedYaml += yaml + } + + expectedName := fmt.Sprintf("%s-configmap-partition-1", cs.GetName()) + + cms, err := rp.createConfigMaps(cs) + Expect(err).ShouldNot(HaveOccurred()) + Expect(len(cms)).Should(Equal(1)) + Expect(cms[0].GetNamespace()).Should(Equal(rp.cfg.Namespace)) + Expect(cms[0].GetName()).Should(Equal(expectedName)) + Expect(cms[0].Data).Should(HaveKey("extraFBC")) + Expect(cms[0].Data["extraFBC"]).Should(Equal(expectedYaml)) + + testCm := &corev1.ConfigMap{} + Expect(rp.cfg.Client.Get(context.TODO(), types.NamespacedName{Namespace: rp.cfg.Namespace, Name: expectedName}, testCm)).Should(Succeed()) + Expect(testCm.Data).Should(HaveKey("extraFBC")) + Expect(testCm.Data["extraFBC"]).Should(Equal(expectedYaml)) + Expect(len(testCm.OwnerReferences)).Should(Equal(1)) + }) + + It("createConfigMaps() should create multiple ConfigMaps", func() { + largeYaml := "" + for i := len([]byte(largeYaml)); i < maxConfigMapSize; { + largeYaml += testYaml + i = len([]byte(largeYaml)) + } + rp.FBCContent = largeYaml + + cms, err := rp.createConfigMaps(cs) + Expect(err).ShouldNot(HaveOccurred()) + Expect(len(cms)).Should(Equal(2)) + + for i, cm := range cms { + expectedName := fmt.Sprintf("%s-configmap-partition-%d", cs.GetName(), i+1) + Expect(cm.Data).Should(HaveKey("extraFBC")) + Expect(cm.Data["extraFBC"]).ShouldNot(BeEmpty()) + Expect(cm.GetNamespace()).Should(Equal(rp.cfg.Namespace)) + Expect(cm.GetName()).Should(Equal(expectedName)) + + testCm := &corev1.ConfigMap{} + Expect(rp.cfg.Client.Get(context.TODO(), types.NamespacedName{Namespace: rp.cfg.Namespace, Name: expectedName}, testCm)).Should(Succeed()) + Expect(testCm.Data).Should(HaveKey("extraFBC")) + Expect(testCm.Data["extraFBC"]).Should(Equal(cm.Data["extraFBC"])) + Expect(len(testCm.OwnerReferences)).Should(Equal(1)) + } + }) + }) + }) +}) + +// containerCommandFor returns the expected container command for a db path and set of bundle items. +func containerCommandFor(indexRootDir string, grpcPort int32) string { //nolint:unparam + return fmt.Sprintf("opm serve %s -p %d", indexRootDir, grpcPort) +} + +const testYaml = ` +--- +name: 'Vada O''Connell' +email: braun.leta@hirthe.biz +phone: 815.290.6848 +description: 'Sit velit accusantium repellat itaque quisquam dolorem. Necessitatibus et provident explicabo. Animi officia enim omnis unde odio odio. Inventore autem repellendus ducimus et et et iure.' +address: + streetName: 'Terence Garden' + streetAddress: '644 Ward Ranch' + city: 'North Newtonhaven' + postcode: 37068-6948 + country: 'Saudi Arabia' +--- +name: 'Miss Rita Gulgowski' +email: oran02@gmail.com +phone: '+13346133601' +description: 'Inventore recusandae ducimus nemo consequatur. Dolorum vel voluptas sint tempore iste maiores. Voluptatem nisi incidunt sit. Vel et officiis eum enim dolores dolor.' +address: + streetName: 'Skylar Gateway' + streetAddress: '8717 Karley Creek Suite 375' + city: Kuhlmanshire + postcode: '59539' + country: Bolivia +--- +name: 'Prof. Laverna Stanton' +email: nicklaus.turner@gmail.com +phone: 928.205.3796 +description: 'Fugiat quos aspernatur iste fugit provident fugit aut. Optio rem exercitationem quas esse et nesciunt velit excepturi. Doloremque aliquid iure aut quaerat id repellat.' +address: + streetName: 'Kamron Roads' + streetAddress: '956 Lemke Camp' + city: Malikatown + postcode: '89393' + country: 'French Southern Territories' +` diff --git a/testdata/go/v3/memcached-operator/README.md b/testdata/go/v3/memcached-operator/README.md index cb5b4efc00..e4dd2e876a 100644 --- a/testdata/go/v3/memcached-operator/README.md +++ b/testdata/go/v3/memcached-operator/README.md @@ -78,7 +78,7 @@ More information can be found via the [Kubebuilder Documentation](https://book.k ## License -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/groupversion_info.go b/testdata/go/v3/memcached-operator/api/v1alpha1/groupversion_info.go index 593f0d4431..b5a1e8f2d3 100644 --- a/testdata/go/v3/memcached-operator/api/v1alpha1/groupversion_info.go +++ b/testdata/go/v3/memcached-operator/api/v1alpha1/groupversion_info.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_types.go b/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_types.go index eb79e261ba..822d0a4084 100644 --- a/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_types.go +++ b/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_types.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_webhook.go b/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_webhook.go index c6997a42de..ce5e119e46 100644 --- a/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_webhook.go +++ b/testdata/go/v3/memcached-operator/api/v1alpha1/memcached_webhook.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/webhook_suite_test.go b/testdata/go/v3/memcached-operator/api/v1alpha1/webhook_suite_test.go index 75bba1ebad..7fa23aac47 100644 --- a/testdata/go/v3/memcached-operator/api/v1alpha1/webhook_suite_test.go +++ b/testdata/go/v3/memcached-operator/api/v1alpha1/webhook_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go b/testdata/go/v3/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go index ad3b8725d8..26ea5c32e4 100644 --- a/testdata/go/v3/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/testdata/go/v3/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -2,7 +2,7 @@ // +build !ignore_autogenerated /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/controllers/memcached_controller.go b/testdata/go/v3/memcached-operator/controllers/memcached_controller.go index ffd7a4c786..26b9b3ecd0 100644 --- a/testdata/go/v3/memcached-operator/controllers/memcached_controller.go +++ b/testdata/go/v3/memcached-operator/controllers/memcached_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/controllers/memcached_controller_test.go b/testdata/go/v3/memcached-operator/controllers/memcached_controller_test.go index 3598c5629d..b392106785 100644 --- a/testdata/go/v3/memcached-operator/controllers/memcached_controller_test.go +++ b/testdata/go/v3/memcached-operator/controllers/memcached_controller_test.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/controllers/suite_test.go b/testdata/go/v3/memcached-operator/controllers/suite_test.go index 0e0366714e..6b6c12f463 100644 --- a/testdata/go/v3/memcached-operator/controllers/suite_test.go +++ b/testdata/go/v3/memcached-operator/controllers/suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/hack/boilerplate.go.txt b/testdata/go/v3/memcached-operator/hack/boilerplate.go.txt index 29c55ecda3..65b8622718 100644 --- a/testdata/go/v3/memcached-operator/hack/boilerplate.go.txt +++ b/testdata/go/v3/memcached-operator/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v3/memcached-operator/main.go b/testdata/go/v3/memcached-operator/main.go index 738188527e..5a43d31319 100644 --- a/testdata/go/v3/memcached-operator/main.go +++ b/testdata/go/v3/memcached-operator/main.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/README.md b/testdata/go/v4-alpha/memcached-operator/README.md index cb5b4efc00..e4dd2e876a 100644 --- a/testdata/go/v4-alpha/memcached-operator/README.md +++ b/testdata/go/v4-alpha/memcached-operator/README.md @@ -78,7 +78,7 @@ More information can be found via the [Kubebuilder Documentation](https://book.k ## License -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/groupversion_info.go b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/groupversion_info.go index 593f0d4431..b5a1e8f2d3 100644 --- a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/groupversion_info.go +++ b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/groupversion_info.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/memcached_types.go b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/memcached_types.go index eb79e261ba..822d0a4084 100644 --- a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/memcached_types.go +++ b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/memcached_types.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/memcached_webhook.go b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/memcached_webhook.go index c6997a42de..ce5e119e46 100644 --- a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/memcached_webhook.go +++ b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/memcached_webhook.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/webhook_suite_test.go b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/webhook_suite_test.go index 75bba1ebad..7fa23aac47 100644 --- a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/webhook_suite_test.go +++ b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/webhook_suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go index ad3b8725d8..26ea5c32e4 100644 --- a/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/testdata/go/v4-alpha/memcached-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -2,7 +2,7 @@ // +build !ignore_autogenerated /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/controllers/memcached_controller.go b/testdata/go/v4-alpha/memcached-operator/controllers/memcached_controller.go index ffd7a4c786..26b9b3ecd0 100644 --- a/testdata/go/v4-alpha/memcached-operator/controllers/memcached_controller.go +++ b/testdata/go/v4-alpha/memcached-operator/controllers/memcached_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/controllers/memcached_controller_test.go b/testdata/go/v4-alpha/memcached-operator/controllers/memcached_controller_test.go index 3598c5629d..b392106785 100644 --- a/testdata/go/v4-alpha/memcached-operator/controllers/memcached_controller_test.go +++ b/testdata/go/v4-alpha/memcached-operator/controllers/memcached_controller_test.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/controllers/suite_test.go b/testdata/go/v4-alpha/memcached-operator/controllers/suite_test.go index 0e0366714e..6b6c12f463 100644 --- a/testdata/go/v4-alpha/memcached-operator/controllers/suite_test.go +++ b/testdata/go/v4-alpha/memcached-operator/controllers/suite_test.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/hack/boilerplate.go.txt b/testdata/go/v4-alpha/memcached-operator/hack/boilerplate.go.txt index 29c55ecda3..65b8622718 100644 --- a/testdata/go/v4-alpha/memcached-operator/hack/boilerplate.go.txt +++ b/testdata/go/v4-alpha/memcached-operator/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/testdata/go/v4-alpha/memcached-operator/main.go b/testdata/go/v4-alpha/memcached-operator/main.go index 738188527e..5a43d31319 100644 --- a/testdata/go/v4-alpha/memcached-operator/main.go +++ b/testdata/go/v4-alpha/memcached-operator/main.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2023. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.