// // Copyright (c) 2019-2021 Red Hat, Inc. // This program and the accompanying materials are made // available under the terms of the Eclipse Public License 2.0 // which is available at https://www.eclipse.org/legal/epl-2.0/ // // SPDX-License-Identifier: EPL-2.0 // // Contributors: // Red Hat, Inc. - initial API and implementation // package imagepuller import ( "context" "os" "sort" "strings" "unicode/utf8" "reflect" "time" chev1alpha1 "github.com/che-incubator/kubernetes-image-puller-operator/api/v1alpha1" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/eclipse-che/che-operator/pkg/common/constants" defaults "github.com/eclipse-che/che-operator/pkg/common/operator-defaults" "github.com/eclipse-che/che-operator/pkg/common/test" "github.com/eclipse-che/che-operator/pkg/common/utils" chev2 "github.com/eclipse-che/che-operator/api/v2" routev1 "github.com/openshift/api/route/v1" operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1" packagesv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" fakeDiscovery "k8s.io/client-go/discovery/fake" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" "testing" ) var ( namespace = "eclipse-che" csvName = "kubernetes-imagepuller-operator.v0.0.9" defaultImagePullerImages string valueTrue = true ) func TestImagePullerConfiguration(t *testing.T) { type testCase struct { name string initCR *chev2.CheCluster initObjects []runtime.Object expectedCR *chev2.CheCluster expectedOperatorGroup *operatorsv1.OperatorGroup expectedSubscription *operatorsv1alpha1.Subscription expectedImagePuller *chev1alpha1.KubernetesImagePuller shouldDelete bool } testCases := []testCase{ { name: "image puller enabled, no operatorgroup, should create an operatorgroup", initCR: InitCheCRWithImagePullerEnabled(), initObjects: []runtime.Object{ getPackageManifest(), }, expectedOperatorGroup: getOperatorGroup(), }, { name: "image puller enabled, operatorgroup exists, should create a subscription", initCR: InitCheCRWithImagePullerEnabled(), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), }, expectedSubscription: getSubscription(), }, { name: "image puller enabled, subscription created, should add finalizer", initCR: InitCheCRWithImagePullerEnabled(), expectedCR: ExpectedCheCRWithImagePullerFinalizer(), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), }, }, { name: "image puller enabled with finalizer but default values are empty, subscription exists, should update the CR", initCR: InitCheCRWithImagePullerFinalizer(), expectedCR: &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, ResourceVersion: "1", Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: true, Spec: chev1alpha1.KubernetesImagePullerSpec{ DeploymentName: "kubernetes-image-puller", ConfigMapName: "k8s-image-puller", }, }, }, }, }, initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), }, }, { name: "image puller enabled default values already set, subscription exists, should create a KubernetesImagePuller", initCR: InitCheCRWithImagePullerEnabledAndDefaultValuesSet(), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), }, expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}), }, { name: "image puller enabled, user images set, subscription exists, should create a KubernetesImagePuller with user images", initCR: InitCheCRWithImagePullerEnabledAndImagesSet("image=image_url"), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), }, expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url", ObjectMetaResourceVersion: "1"}), }, { name: "image puller enabled, latest default images set, subscription exists, should not update KubernetesImagePuller default images", initCR: InitCheCRWithImagePullerEnabledAndImagesSet(defaultImagePullerImages), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}), }, expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: defaultImagePullerImages, ObjectMetaResourceVersion: "1"}), }, { name: "image puller enabled, default images not set, subscription exists, should not set KubernetesImagePuller default images", initCR: InitCheCRWithImagePullerEnabledAndImagesSet("image=image_url;"), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;", ObjectMetaResourceVersion: "1"}), }, expectedImagePuller: InitImagePuller(ImagePullerOptions{SpecImages: "image=image_url;", ObjectMetaResourceVersion: "1"}), }, { name: "image puller enabled, KubernetesImagePuller created and spec in CheCluster is different, should update the KubernetesImagePuller", initCR: InitCheCRWithImagePullerEnabledAndNewValuesSet(), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), getDefaultImagePuller(), }, expectedImagePuller: &chev1alpha1.KubernetesImagePuller{ TypeMeta: metav1.TypeMeta{Kind: "KubernetesImagePuller", APIVersion: "che.eclipse.org/v1alpha1"}, ObjectMeta: metav1.ObjectMeta{ ResourceVersion: "2", Name: "eclipse-che-image-puller", Namespace: namespace, Labels: map[string]string{ "app": defaults.GetCheFlavor(), "component": "kubernetes-image-puller", "app.kubernetes.io/part-of": constants.CheEclipseOrg, }, OwnerReferences: []metav1.OwnerReference{ { APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", BlockOwnerDeletion: &valueTrue, Controller: &valueTrue, Name: "eclipse-che", }, }, }, Spec: chev1alpha1.KubernetesImagePullerSpec{ ConfigMapName: "k8s-image-puller-trigger-update", DeploymentName: "kubernetes-image-puller-trigger-update", }, }, }, { name: "image puller already created, imagePuller disabled, should delete everything", initCR: InitCheCRWithImagePullerDisabled(), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), getClusterServiceVersion(), getDefaultImagePuller(), }, expectedCR: InitCheCRWithImagePullerDisabled(), shouldDelete: true, }, { name: "image puller already created, finalizer deleted", initCR: InitCheCRWithImagePullerFinalizerAndDeletionTimestamp(), initObjects: []runtime.Object{ getPackageManifest(), getOperatorGroup(), getSubscription(), getClusterServiceVersion(), getDefaultImagePuller(), }, shouldDelete: true, expectedCR: nil, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { logf.SetLogger(zap.New(zap.WriteTo(os.Stdout), zap.UseDevMode(true))) deployContext := test.GetDeployContext(testCase.initCR, []runtime.Object{}) chev2.SchemeBuilder.AddToScheme(deployContext.ClusterAPI.Scheme) packagesv1.AddToScheme(deployContext.ClusterAPI.Scheme) operatorsv1alpha1.AddToScheme(deployContext.ClusterAPI.Scheme) operatorsv1.AddToScheme(deployContext.ClusterAPI.Scheme) chev1alpha1.AddToScheme(deployContext.ClusterAPI.Scheme) routev1.AddToScheme(deployContext.ClusterAPI.Scheme) for _, obj := range testCase.initObjects { obj.(metav1.Object).SetResourceVersion("") err := deployContext.ClusterAPI.NonCachingClient.Create(context.TODO(), obj.(client.Object)) if err != nil { t.Fatalf(err.Error()) } } deployContext.ClusterAPI.DiscoveryClient.(*fakeDiscovery.FakeDiscovery).Fake.Resources = []*metav1.APIResourceList{ { GroupVersion: "packages.operators.coreos.com/v1", APIResources: []metav1.APIResource{ { Kind: "PackageManifest", }, }, }, { GroupVersion: "operators.coreos.com/v1alpha1", APIResources: []metav1.APIResource{ {Kind: "OperatorGroup"}, {Kind: "Subscription"}, {Kind: "ClusterServiceVersion"}, }, }, { GroupVersion: "che.eclipse.org/v1alpha1", APIResources: []metav1.APIResource{ {Kind: "KubernetesImagePuller"}, }, }, } var err error if testCase.shouldDelete { if done := DeleteImagePullerOperatorAndFinalizer(deployContext); !done { t.Fatal("Error reconciling: failed to delete Image Puller") } } else { _, _, err = ReconcileImagePuller(deployContext) if err != nil { t.Fatalf("Error reconciling: %v", err) } } if testCase.expectedOperatorGroup != nil { gotOperatorGroup := &operatorsv1.OperatorGroup{} err := deployContext.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedOperatorGroup.Namespace, Name: testCase.expectedOperatorGroup.Name}, gotOperatorGroup) if err != nil { t.Errorf("Error getting OperatorGroup: %v", err) } if !reflect.DeepEqual(testCase.expectedOperatorGroup.Spec.TargetNamespaces, gotOperatorGroup.Spec.TargetNamespaces) { t.Errorf("Error expected target namespace %v but got %v", testCase.expectedOperatorGroup.Spec.TargetNamespaces, gotOperatorGroup.Spec.TargetNamespaces) } } if testCase.expectedSubscription != nil { gotSubscription := &operatorsv1alpha1.Subscription{} err := deployContext.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedSubscription.Namespace, Name: testCase.expectedSubscription.Name}, gotSubscription) if err != nil { t.Errorf("Error getting Subscription: %v", err) } if !reflect.DeepEqual(testCase.expectedSubscription.Spec, gotSubscription.Spec) { t.Errorf("Error, subscriptions differ (-want +got) %v", cmp.Diff(testCase.expectedSubscription.Spec, gotSubscription.Spec)) } } // if expectedCR is not set, don't check it if testCase.expectedCR != nil && !reflect.DeepEqual(testCase.initCR, testCase.expectedCR) { gotCR := &chev2.CheCluster{} err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "eclipse-che"}, gotCR) if err != nil { t.Errorf("Error getting CheCluster: %v", err) } if !reflect.DeepEqual(testCase.expectedCR, gotCR) { t.Errorf("Expected CR and CR returned from API server are different (-want +got): %v", cmp.Diff(testCase.expectedCR, gotCR)) } } if testCase.expectedImagePuller != nil { gotImagePuller := &chev1alpha1.KubernetesImagePuller{} err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: testCase.expectedImagePuller.Namespace, Name: testCase.expectedImagePuller.Name}, gotImagePuller) if err != nil { t.Errorf("Error getting KubernetesImagePuller: %v", err) } diff := cmp.Diff(testCase.expectedImagePuller, gotImagePuller, cmpopts.IgnoreFields(chev1alpha1.KubernetesImagePullerSpec{}, "Images")) if diff != "" { t.Errorf("Expected KubernetesImagePuller and KubernetesImagePuller returned from API server differ (-want, +got): %v", diff) } expectedImages := nonEmptySplit(testCase.expectedImagePuller.Spec.Images, ";") if len(nonEmptySplit(testCase.expectedImagePuller.Spec.Images, ";")) != len(expectedImages) { t.Errorf("Expected KubernetesImagePuller returns %d images", len(expectedImages)) } for _, expectedImage := range expectedImages { if !strings.Contains(gotImagePuller.Spec.Images, expectedImage) { t.Errorf("Expected KubernetesImagePuller returned image: %s, but it did not", expectedImage) } } } if testCase.shouldDelete { if testCase.expectedCR == nil { gotCR := &chev2.CheCluster{} err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "eclipse-che"}, gotCR) if !errors.IsNotFound(err) { t.Fatal("CR CheCluster should be removed") } } imagePuller := &chev1alpha1.KubernetesImagePuller{} err = deployContext.ClusterAPI.Client.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "eclipse-che-image-puller"}, imagePuller) if err == nil || !errors.IsNotFound(err) { t.Fatalf("Should not have found KubernetesImagePuller: %v", err) } clusterServiceVersion := &operatorsv1alpha1.ClusterServiceVersion{} err = deployContext.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: csvName}, clusterServiceVersion) if err == nil || !errors.IsNotFound(err) { t.Fatalf("Should not have found ClusterServiceVersion: %v", err) } subscription := &operatorsv1alpha1.Subscription{} err = deployContext.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "kubernetes-imagepuller-operator"}, subscription) if err == nil || !errors.IsNotFound(err) { t.Fatalf("Should not have found Subscription: %v", err) } operatorGroup := &operatorsv1.OperatorGroup{} err = deployContext.ClusterAPI.NonCachingClient.Get(context.TODO(), types.NamespacedName{Namespace: namespace, Name: "kubernetes-imagepuller-operator"}, operatorGroup) if err == nil || !errors.IsNotFound(err) { t.Fatalf("Should not have found OperatorGroup: %v", err) } } }) } } func TestEnvVars(t *testing.T) { type testcase struct { name string env map[string]string expected []ImageAndName } // unset RELATED_IMAGE environment variables, set them back // after tests complete matches := utils.GetEnvsByRegExp("^RELATED_IMAGE_.*") for _, match := range matches { if originalValue, exists := os.LookupEnv(match.Name); exists { os.Unsetenv(match.Name) defer os.Setenv(match.Name, originalValue) } } cases := []testcase{ { name: "detect theia images", env: map[string]string{ "RELATED_IMAGE_che_theia_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-theia", "RELATED_IMAGE_che_theia_endpoint_runtime_binary_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-theia-endpoint-runtime-binary", }, expected: []ImageAndName{ {Name: "che_theia_plugin_registry_image_IBZWQYJ", Image: "quay.io/eclipse/che-theia"}, {Name: "che_theia_endpoint_runtime_binary_plugin_registry_image_IBZWQYJ", Image: "quay.io/eclipse/che-theia-endpoint-runtime-binary"}, }, }, { name: "detect machine exec image", env: map[string]string{ "RELATED_IMAGE_che_machine_exec_plugin_registry_image_IBZWQYJ": "quay.io/eclipse/che-machine-exec", "RELATED_IMAGE_codeready_workspaces_machineexec_plugin_registry_image_GIXDCMQK": "registry.redhat.io/codeready-workspaces/machineexec-rhel8", }, expected: []ImageAndName{ {Name: "che_machine_exec_plugin_registry_image_IBZWQYJ", Image: "quay.io/eclipse/che-machine-exec"}, {Name: "codeready_workspaces_machineexec_plugin_registry_image_GIXDCMQK", Image: "registry.redhat.io/codeready-workspaces/machineexec-rhel8"}, }, }, { name: "detect plugin registry images", env: map[string]string{ "RELATED_IMAGE_che_openshift_plugin_registry_image_IBZWQYJ": "index.docker.io/dirigiblelabs/dirigible-openshift", "RELATED_IMAGE_codeready_workspaces_plugin_openshift_plugin_registry_image_GIXDCMQK": "registry.redhat.io/codeready-workspaces/plugin-openshift-rhel8", }, expected: []ImageAndName{ {Name: "che_openshift_plugin_registry_image_IBZWQYJ", Image: "index.docker.io/dirigiblelabs/dirigible-openshift"}, {Name: "codeready_workspaces_plugin_openshift_plugin_registry_image_GIXDCMQK", Image: "registry.redhat.io/codeready-workspaces/plugin-openshift-rhel8"}, }, }, { name: "detect devfile registry images", env: map[string]string{ "RELATED_IMAGE_universal_developer_image_devfile_registry_image_OVRGSOBNGBSTCOBZMQ4Q____": "quay.io/devfile/universal-developer-image:ubi8-38da5c2", }, expected: []ImageAndName{ {Name: "universal_developer_image_devfile_registry_image_OVRGSOBNGBSTCOBZMQ4Q____", Image: "quay.io/devfile/universal-developer-image:ubi8-38da5c2"}, }, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { for k, v := range c.env { os.Setenv(k, v) defer os.Unsetenv(k) } actual := GetDefaultImages() if d := cmp.Diff(sortImages(c.expected), sortImages(actual)); d != "" { t.Errorf("Error, collected images differ (-want, +got): %v", d) } }) } } func sortImages(images []ImageAndName) []ImageAndName { imagesCopy := make([]ImageAndName, len(images)) copy(imagesCopy, images) sort.Slice(imagesCopy, func(i, j int) bool { return imagesCopy[i].Name < imagesCopy[j].Name }) return imagesCopy } func InitCheCRWithImagePullerEnabled() *chev2.CheCluster { return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, ResourceVersion: "0", }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: true, }, }, }, } } func InitCheCRWithImagePullerFinalizer() *chev2.CheCluster { return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, ResourceVersion: "0", }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: true, }, }, }, } } func InitCheCRWithImagePullerFinalizerAndDeletionTimestamp() *chev2.CheCluster { return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, DeletionTimestamp: &metav1.Time{Time: time.Unix(1, 0)}, }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: true, }, }, }, } } func ExpectedCheCRWithImagePullerFinalizer() *chev2.CheCluster { return &chev2.CheCluster{ TypeMeta: metav1.TypeMeta{ APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", }, ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, ResourceVersion: "1", }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: true, }, }, }, } } func InitCheCRWithImagePullerDisabled() *chev2.CheCluster { return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, ResourceVersion: "0", }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: false, }, }, }, } } func InitCheCRWithImagePullerEnabledAndDefaultValuesSet() *chev2.CheCluster { return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, }, TypeMeta: metav1.TypeMeta{ APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: true, Spec: chev1alpha1.KubernetesImagePullerSpec{ DeploymentName: "kubernetes-image-puller", ConfigMapName: "k8s-image-puller", }, }, }, }, } } func InitCheCRWithImagePullerEnabledAndImagesSet(images string) *chev2.CheCluster { return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, }, TypeMeta: metav1.TypeMeta{ APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: true, Spec: chev1alpha1.KubernetesImagePullerSpec{ DeploymentName: "kubernetes-image-puller", ConfigMapName: "k8s-image-puller", Images: images, }, }, }, }, } } func InitCheCRWithImagePullerEnabledAndNewValuesSet() *chev2.CheCluster { return &chev2.CheCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che", Namespace: namespace, Finalizers: []string{ "kubernetesimagepullers.finalizers.che.eclipse.org", }, }, Spec: chev2.CheClusterSpec{ Components: chev2.CheClusterComponents{ ImagePuller: chev2.ImagePuller{ Enable: true, Spec: chev1alpha1.KubernetesImagePullerSpec{ DeploymentName: "kubernetes-image-puller-trigger-update", ConfigMapName: "k8s-image-puller-trigger-update", }, }, }, }, } } type ImagePullerOptions struct { SpecImages string ObjectMetaResourceVersion string } func InitImagePuller(options ImagePullerOptions) *chev1alpha1.KubernetesImagePuller { return &chev1alpha1.KubernetesImagePuller{ TypeMeta: metav1.TypeMeta{ APIVersion: "che.eclipse.org/v1alpha1", Kind: "KubernetesImagePuller", }, ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che-image-puller", Namespace: namespace, ResourceVersion: options.ObjectMetaResourceVersion, Labels: map[string]string{ "app": defaults.GetCheFlavor(), "app.kubernetes.io/part-of": constants.CheEclipseOrg, "component": "kubernetes-image-puller", }, OwnerReferences: []metav1.OwnerReference{ { APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", Controller: &valueTrue, BlockOwnerDeletion: &valueTrue, Name: "eclipse-che", }, }, }, Spec: chev1alpha1.KubernetesImagePullerSpec{ DeploymentName: "kubernetes-image-puller", ConfigMapName: "k8s-image-puller", Images: options.SpecImages, }, } } func getDefaultImagePuller() *chev1alpha1.KubernetesImagePuller { return &chev1alpha1.KubernetesImagePuller{ TypeMeta: metav1.TypeMeta{ APIVersion: "che.eclipse.org/v1alpha1", Kind: "KubernetesImagePuller", }, ObjectMeta: metav1.ObjectMeta{ Name: "eclipse-che-image-puller", Namespace: namespace, Labels: map[string]string{ "app": defaults.GetCheFlavor(), "app.kubernetes.io/part-of": constants.CheEclipseOrg, "component": "kubernetes-image-puller", }, OwnerReferences: []metav1.OwnerReference{ { APIVersion: "org.eclipse.che/v2", Kind: "CheCluster", Controller: &valueTrue, BlockOwnerDeletion: &valueTrue, Name: "eclipse-che", }, }, }, Spec: chev1alpha1.KubernetesImagePullerSpec{ DeploymentName: "kubernetes-image-puller", ConfigMapName: "k8s-image-puller", Images: defaultImagePullerImages, }, } } // Split string by separator without empty elems func nonEmptySplit(lineToSplit string, separator string) []string { splitFn := func(c rune) bool { runeChar, _ := utf8.DecodeRuneInString(separator) return c == runeChar } return strings.FieldsFunc(lineToSplit, splitFn) } func getPackageManifest() *packagesv1.PackageManifest { return &packagesv1.PackageManifest{ ObjectMeta: metav1.ObjectMeta{ Name: "kubernetes-imagepuller-operator", Namespace: namespace, }, Status: packagesv1.PackageManifestStatus{ CatalogSource: "community-operators", CatalogSourceNamespace: "olm", DefaultChannel: "stable", PackageName: "kubernetes-imagepuller-operator", }, } } func getOperatorGroup() *operatorsv1.OperatorGroup { return &operatorsv1.OperatorGroup{ ObjectMeta: metav1.ObjectMeta{ Name: "kubernetes-imagepuller-operator", Namespace: namespace, }, Spec: operatorsv1.OperatorGroupSpec{ TargetNamespaces: []string{ namespace, }, }, } } func getSubscription() *operatorsv1alpha1.Subscription { return &operatorsv1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ Name: "kubernetes-imagepuller-operator", Namespace: namespace, }, Spec: &operatorsv1alpha1.SubscriptionSpec{ CatalogSource: "community-operators", Channel: "stable", CatalogSourceNamespace: "olm", InstallPlanApproval: operatorsv1alpha1.ApprovalAutomatic, Package: "kubernetes-imagepuller-operator", }, } } func getClusterServiceVersion() *operatorsv1alpha1.ClusterServiceVersion { return &operatorsv1alpha1.ClusterServiceVersion{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: csvName, }, } }