From 876d2cef8a9ce1424c1b4d06e2f3323e8b7da061 Mon Sep 17 00:00:00 2001 From: dkarpele Date: Tue, 18 Aug 2026 21:15:29 +0200 Subject: [PATCH] feat: install one of two versions of the Console Plugin (PatternFly5/6) Co-authored-by: Claude Signed-off-by: dkarpele --- ...gitops-operator.clusterserviceversion.yaml | 2 +- common/common.go | 11 ++- controllers/consoleplugin.go | 38 ++++--- controllers/consoleplugin_test.go | 99 +++++++++++++++---- controllers/gitopsservice_controller.go | 28 +++--- controllers/gitopsservice_controller_test.go | 62 +++++++++++- hack/non-olm-install/README.md | 3 +- .../install-gitops-operator.sh | 5 + 8 files changed, 196 insertions(+), 52 deletions(-) diff --git a/bundle/manifests/gitops-operator.clusterserviceversion.yaml b/bundle/manifests/gitops-operator.clusterserviceversion.yaml index a9e1973a6c5..5f104e980cd 100644 --- a/bundle/manifests/gitops-operator.clusterserviceversion.yaml +++ b/bundle/manifests/gitops-operator.clusterserviceversion.yaml @@ -190,7 +190,7 @@ metadata: capabilities: Deep Insights console.openshift.io/plugins: '["gitops-plugin"]' containerImage: quay.io/redhat-developer/gitops-operator - createdAt: "2026-07-31T05:20:33Z" + createdAt: "2026-08-18T19:13:31Z" description: Enables teams to adopt GitOps principles for managing cluster configurations and application delivery across hybrid multi-cluster Kubernetes environments. features.operators.openshift.io/disconnected: "true" diff --git a/common/common.go b/common/common.go index 14421398885..d33dfd1af68 100644 --- a/common/common.go +++ b/common/common.go @@ -31,10 +31,17 @@ const ( InfraNodeLabelSelector = "node-role.kubernetes.io/infra" // Default console plugin image DefaultConsoleImage = "quay.io/redhat-user-workloads/rh-openshift-gitops-tenant/console-plugin-rhel9" + // DefaultConsoleImagePF5 is Default console plugin image for PatternFly 5 + DefaultConsoleImagePF5 = "quay.io/redhat-user-workloads/rh-openshift-gitops-tenant/console-plugin-4.18-rhel9" // Default console plugin version DefaultConsoleVersion = "main" - // Default console plugin installation OCP version - DefaultDynamicPluginStartOCPVersion = "4.15.0" + // DefaultConsoleVersionPF5 is a Default console plugin version for PatternFly 5 + DefaultConsoleVersionPF5 = "main" + // DefaultDynamicPluginStartOCPVersion is the minimum OCP version that supports the console plugin + DefaultDynamicPluginStartOCPVersion = "4.18.0" + // PluginPF6MinOCPVersion is the minimum OCP version that should use the PF6-based plugin; + // OCP versions >= 4.18 and < 4.19 use the PF5-based plugin instead. + PluginPF6MinOCPVersion = "4.19.0" // ImagePullPolicyEnvVar is the environment variable for configuring image pull policy ImagePullPolicy = "IMAGE_PULL_POLICY" // InfraNodeSelectorAnnotation is the OpenShift namespace annotation that applies a default node selector to all pods diff --git a/controllers/consoleplugin.go b/controllers/consoleplugin.go index fc61820e6b7..2db83f715da 100644 --- a/controllers/consoleplugin.go +++ b/controllers/consoleplugin.go @@ -38,6 +38,7 @@ const ( gitopsPluginSvcName = gitopsPluginName + "-service" proxyAlias = "gitops" pluginImageEnv = "GITOPS_CONSOLE_PLUGIN_IMAGE" + pluginImageEnvPF5 = "GITOPS_CONSOLE_PLUGIN_IMAGE_PF5" servicePort = 9001 pluginServingCertName = "console-serving-cert" kubeAppLabelApp = "app" @@ -49,12 +50,18 @@ const ( kubeAppLabelName = "app.kubernetes.io/name" ) -func getPluginPodSpec(crImagePullPolicy corev1.PullPolicy) corev1.PodSpec { - consolePluginImage := os.Getenv(pluginImageEnv) - if consolePluginImage == "" { - image := common.DefaultConsoleImage - version := common.DefaultConsoleVersion - consolePluginImage = image + ":" + version +func getPluginPodSpec(crImagePullPolicy corev1.PullPolicy, isPF5 bool) corev1.PodSpec { + var consolePluginImage string + if isPF5 { + consolePluginImage = os.Getenv(pluginImageEnvPF5) + if consolePluginImage == "" { + consolePluginImage = common.DefaultConsoleImagePF5 + ":" + common.DefaultConsoleVersionPF5 + } + } else { + consolePluginImage = os.Getenv(pluginImageEnv) + if consolePluginImage == "" { + consolePluginImage = common.DefaultConsoleImage + ":" + common.DefaultConsoleVersion + } } podSpec := corev1.PodSpec{ @@ -138,8 +145,8 @@ func getPluginPodSpec(crImagePullPolicy corev1.PullPolicy) corev1.PodSpec { return podSpec } -func pluginDeployment(crImagePullPolicy corev1.PullPolicy) *appsv1.Deployment { - podSpec := getPluginPodSpec(crImagePullPolicy) +func pluginDeployment(crImagePullPolicy corev1.PullPolicy, isPF5 bool) *appsv1.Deployment { + podSpec := getPluginPodSpec(crImagePullPolicy, isPF5) template := corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ @@ -362,9 +369,9 @@ func sortTolerations(tolerations []corev1.Toleration) []corev1.Toleration { return sorted } -func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.GitopsService, request reconcile.Request, newPluginConfigMap *corev1.ConfigMap) (reconcile.Result, error) { +func (r *ReconcileGitopsService) reconcileDeployment(cr *pipelinesv1alpha1.GitopsService, request reconcile.Request, newPluginConfigMap *corev1.ConfigMap, isPF5 bool) (reconcile.Result, error) { reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) - newPluginDeployment := pluginDeployment(cr.Spec.ImagePullPolicy) + newPluginDeployment := pluginDeployment(cr.Spec.ImagePullPolicy, isPF5) if err := controllerutil.SetControllerReference(cr, newPluginDeployment, r.Scheme); err != nil { return reconcile.Result{}, err @@ -481,7 +488,7 @@ func (r *ReconcileGitopsService) reconcileService(instance *pipelinesv1alpha1.Gi existingServiceRef.Labels = pluginServiceRef.Labels existingServiceRef.Spec.Selector = pluginServiceRef.Spec.Selector existingServiceRef.Spec.Ports = pluginServiceRef.Spec.Ports - return reconcile.Result{}, r.Client.Update(context.TODO(), pluginServiceRef) + return reconcile.Result{}, r.Client.Update(context.TODO(), existingServiceRef) } } return reconcile.Result{}, nil @@ -518,7 +525,7 @@ func (r *ReconcileGitopsService) reconcileConsolePlugin(instance *pipelinesv1alp reqLogger.Info("Reconciling Console Plugin", "Namespace", existingPlugin.Namespace, "Name", existingPlugin.Name) existingPlugin.Spec.DisplayName = newConsolePlugin.Spec.DisplayName existingPlugin.Spec.Backend.Service = newConsolePlugin.Spec.Backend.Service - return reconcile.Result{}, r.Client.Update(context.TODO(), newConsolePlugin) + return reconcile.Result{}, r.Client.Update(context.TODO(), existingPlugin) } } return reconcile.Result{}, nil @@ -576,8 +583,9 @@ func (r *ReconcileGitopsService) reconcileConfigMap(instance *pipelinesv1alpha1. return reconcile.Result{}, nil } -// is this func the reconciler enty point to reconcile the current plugin state? -func (r *ReconcileGitopsService) reconcilePlugin(instance *pipelinesv1alpha1.GitopsService, request reconcile.Request) (reconcile.Result, error) { +// reconcilePlugin is the entry point for reconciling all console plugin resources. +// isPF5 selects the PatternFly 5 image (OCP 4.18.x) vs the PatternFly 6 image (OCP >= 4.19). +func (r *ReconcileGitopsService) reconcilePlugin(instance *pipelinesv1alpha1.GitopsService, request reconcile.Request, isPF5 bool) (reconcile.Result, error) { reqLogger := logs.WithValues("Request.Namespace", request.Namespace, "Request.Name", request.Name) if !util.IsConsoleAPIFound() { reqLogger.Info("Skip console plugin reconcile: OpenShift Console API not found") @@ -595,7 +603,7 @@ func (r *ReconcileGitopsService) reconcilePlugin(instance *pipelinesv1alpha1.Git return result, err } - if result, err := r.reconcileDeployment(instance, request, newPluginConfigMap); err != nil { + if result, err := r.reconcileDeployment(instance, request, newPluginConfigMap, isPF5); err != nil { return result, err } diff --git a/controllers/consoleplugin_test.go b/controllers/consoleplugin_test.go index d3ae456f522..0799e080a6e 100644 --- a/controllers/consoleplugin_test.go +++ b/controllers/consoleplugin_test.go @@ -602,7 +602,7 @@ func TestPlugin_reconcileDeployment_changedTemplateLabels(t *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(newGitopsService(), d).Build() reconciler := newReconcileGitOpsService(fakeClient, s) - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) deployment := &appsv1.Deployment{} @@ -647,7 +647,7 @@ func TestPlugin_reconcileDeployment_changedContainers(t *testing.T) { assert.DeepEqual(t, deployment.Spec.Template.Spec.Containers[0].SecurityContext, securityContextForPlugin()) } - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // There should be a new console plugin deployment created @@ -672,7 +672,7 @@ func TestPlugin_reconcileDeployment_changedContainers(t *testing.T) { assertNoError(t, err) // Verify if the containers are reconciled back to the default values - _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) deployment = &appsv1.Deployment{} @@ -979,7 +979,7 @@ func TestPlugin_reconcileDeployment_infraNodeSelectorNotInPodSpec(t *testing.T) fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(gitopsService).Build() reconciler := newReconcileGitOpsService(fakeClient, s) - _, err := reconciler.reconcileDeployment(gitopsService, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(gitopsService, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) deployment := &appsv1.Deployment{} @@ -999,7 +999,7 @@ func TestPlugin_reconcileDeployment(t *testing.T) { reconciler := newReconcileGitOpsService(fakeClient, s) instance := &pipelinesv1alpha1.GitopsService{} - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) deployment := &appsv1.Deployment{} @@ -1036,7 +1036,7 @@ func TestPlugin_reconcileDeployment_ChangedResources(t *testing.T) { }, } - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) deployment := &appsv1.Deployment{} @@ -1056,7 +1056,7 @@ func TestPlugin_ReconcileDeployment_DefaultResourceValues(t *testing.T) { fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(newGitopsService()).Build() reconciler := newReconcileGitOpsService(fakeClient, s) instance := &pipelinesv1alpha1.GitopsService{} - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) deployment := &appsv1.Deployment{} @@ -1096,7 +1096,7 @@ func TestPlugin_ReconcileDeployment_ChangeExistingResourceValues(t *testing.T) { Resources: Resources, }, } - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) deployment := &appsv1.Deployment{} @@ -1120,7 +1120,7 @@ func TestPlugin_ReconcileDeployment_ChangeExistingResourceValues(t *testing.T) { } instance.Spec.ConsolePlugin.Backend.Resources, instance.Spec.ConsolePlugin.GitopsPlugin.Resources = updatedResources, updatedResources - _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) deployment = &appsv1.Deployment{} @@ -1669,7 +1669,7 @@ func TestReconcileDeployment_NoUpdateWhenContainersOrderDiffers(t *testing.T) { instance := &pipelinesv1alpha1.GitopsService{} // Create deployment - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // Get the deployment and capture initial ResourceVersion and Generation @@ -1692,7 +1692,7 @@ func TestReconcileDeployment_NoUpdateWhenContainersOrderDiffers(t *testing.T) { } // Reconcile again - should NOT trigger an update - _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // Verify no update was triggered @@ -1715,7 +1715,7 @@ func TestReconcileDeployment_NoUpdateWhenVolumesOrderDiffers(t *testing.T) { instance := &pipelinesv1alpha1.GitopsService{} // Create deployment - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // Get the deployment @@ -1740,7 +1740,7 @@ func TestReconcileDeployment_NoUpdateWhenVolumesOrderDiffers(t *testing.T) { genAfterManualUpdate := deployment.Generation // Reconcile again - should NOT trigger an update - _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // Verify no update was triggered @@ -1786,7 +1786,7 @@ func TestReconcileDeployment_NoUpdateWhenTolerationsOrderDiffers(t *testing.T) { reconciler := newReconcileGitOpsService(fakeClient, s) // Create deployment - _, err := reconciler.reconcileDeployment(gitopsService, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(gitopsService, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // Get the deployment @@ -1811,7 +1811,7 @@ func TestReconcileDeployment_NoUpdateWhenTolerationsOrderDiffers(t *testing.T) { genAfterManualUpdate := deployment.Generation // Reconcile again - should NOT trigger an update - _, err = reconciler.reconcileDeployment(gitopsService, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err = reconciler.reconcileDeployment(gitopsService, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // Verify no update was triggered @@ -1837,7 +1837,7 @@ func TestReconcileDeployment_UpdateWhenActualChange(t *testing.T) { instance := &pipelinesv1alpha1.GitopsService{} // Create deployment - _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // Get the deployment and capture initial ResourceVersion and Generation @@ -1852,7 +1852,7 @@ func TestReconcileDeployment_UpdateWhenActualChange(t *testing.T) { assertNoError(t, err) // Reconcile again - should trigger an update - _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap()) + _, err = reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), false) assertNoError(t, err) // Verify update was triggered @@ -2023,7 +2023,7 @@ func TestReconcileDeployment_AddsHashAnnotation(t *testing.T) { }, } r := &ReconcileGitopsService{Client: fake.NewClientBuilder().WithScheme(scheme).WithObjects(instance).Build(), Scheme: scheme} - _, err := r.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsService), cm) + _, err := r.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsService), cm, false) assert.NilError(t, err) deployment := &appsv1.Deployment{} err = r.Client.Get(context.TODO(), types.NamespacedName{Name: gitopsPluginName, Namespace: serviceNamespace}, deployment) @@ -2056,12 +2056,71 @@ func TestReconcileDeployment_UpdatesHashAnnotationWhenConfigChanges(t *testing.T }, } r := &ReconcileGitopsService{Client: fake.NewClientBuilder().WithScheme(scheme).WithObjects(instance).Build(), Scheme: scheme} - _, err := r.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsService), cm1) + _, err := r.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsService), cm1, false) assert.NilError(t, err) - _, err = r.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsService), cm2) + _, err = r.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsService), cm2, false) assert.NilError(t, err) deployment := &appsv1.Deployment{} err = r.Client.Get(context.TODO(), types.NamespacedName{Name: gitopsPluginName, Namespace: serviceNamespace}, deployment) assert.NilError(t, err) assert.Equal(t, getConfigMapHash(cm2), deployment.Spec.Template.Annotations["httpd-cfg-hash"]) } + +func TestPlugin_reconcileDeployment_PluginImage(t *testing.T) { + tests := []struct { + name string + isPF5 bool + envKey string + envValue string + expectedImage string + }{ + { + name: "PF6 default image when env var is not set", + isPF5: false, + expectedImage: common.DefaultConsoleImage + ":" + common.DefaultConsoleVersion, + }, + { + name: "PF6 image from env var when set", + isPF5: false, + envKey: pluginImageEnv, + envValue: "custom-pf6-image:v1.2.3", + expectedImage: "custom-pf6-image:v1.2.3", + }, + { + name: "PF5 default image when env var is not set", + isPF5: true, + expectedImage: common.DefaultConsoleImagePF5 + ":" + common.DefaultConsoleVersionPF5, + }, + { + name: "PF5 image from env var when set", + isPF5: true, + envKey: pluginImageEnvPF5, + envValue: "custom-pf5-image:v1.2.3", + expectedImage: "custom-pf5-image:v1.2.3", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if test.envKey != "" { + t.Setenv(test.envKey, test.envValue) + } + + s := scheme.Scheme + addKnownTypesToScheme(s) + + fakeClient := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(newGitopsService()).Build() + reconciler := newReconcileGitOpsService(fakeClient, s) + instance := &pipelinesv1alpha1.GitopsService{} + + _, err := reconciler.reconcileDeployment(instance, newRequest(serviceNamespace, gitopsPluginName), reconciler.pluginConfigMap(), test.isPF5) + assertNoError(t, err) + + deployment := &appsv1.Deployment{} + err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: gitopsPluginName, Namespace: serviceNamespace}, deployment) + assertNoError(t, err) + + assert.Equal(t, deployment.Spec.Template.Spec.Containers[0].Image, test.expectedImage) + }) + } +} diff --git a/controllers/gitopsservice_controller.go b/controllers/gitopsservice_controller.go index 6c434d93fb0..8e1f53fce6c 100644 --- a/controllers/gitopsservice_controller.go +++ b/controllers/gitopsservice_controller.go @@ -310,29 +310,33 @@ func (r *ReconcileGitopsService) Reconcile(ctx context.Context, request reconcil return reconcile.Result{}, nil } - v1, err := version.NewVersion(OCPVersion) + clusterVersion, err := version.NewVersion(OCPVersion) if err != nil { log.Printf("Unable to retrieve current OCP version: %v", err) return reconcile.Result{}, nil } - realVersion := v1.Segments() - realMajorVersion := realVersion[0] - realMinorVersion := realVersion[1] - v2, err := version.NewVersion(dynamicPluginStartOCPVersion) + minVersion, err := version.NewVersion(dynamicPluginStartOCPVersion) if err != nil { + log.Printf("Unable to parse minimum plugin OCP version: %v", err) return reconcile.Result{}, nil } - startVersion := v2.Segments() - startMajorVersion := startVersion[0] - startMinorVersion := startVersion[1] - if realMajorVersion < startMajorVersion || (realMajorVersion == startMajorVersion && realMinorVersion < startMinorVersion) { - // Skip plugin reconciliation if real OCP version is less than dynamic plugin start OCP version + if clusterVersion.LessThan(minVersion) { + // Skip plugin reconciliation if cluster version is below the minimum supported version return reconcile.Result{}, nil - } else { - return r.reconcilePlugin(instance, request) } + + pf6MinVersion, err := version.NewVersion(common.PluginPF6MinOCPVersion) + if err != nil { + log.Printf("Unable to parse PF6 minimum OCP version: %v", err) + return reconcile.Result{}, nil + } + + if clusterVersion.LessThan(pf6MinVersion) { + return r.reconcilePlugin(instance, request, true) // PF5: >= 4.18 && < 4.19 + } + return r.reconcilePlugin(instance, request, false) // PF6: >= 4.19 } // Detect the unsupported KAM components across Deployments , Routes , Services and deletes them to perform cleanup as KAM is no longer supported since 1.15 diff --git a/controllers/gitopsservice_controller_test.go b/controllers/gitopsservice_controller_test.go index 4655727d13a..f499d9a5c75 100644 --- a/controllers/gitopsservice_controller_test.go +++ b/controllers/gitopsservice_controller_test.go @@ -294,7 +294,7 @@ func TestReconcile(t *testing.T) { s := scheme.Scheme addKnownTypesToScheme(s) - fakeClient := fake.NewFakeClient(util.NewClusterVersion("4.15.1"), newGitopsService()) + fakeClient := fake.NewFakeClient(util.NewClusterVersion("4.19.0"), newGitopsService()) reconciler := newReconcileGitOpsService(fakeClient, s) _, err := reconciler.Reconcile(context.TODO(), newRequest("test", "test")) @@ -1154,6 +1154,66 @@ func assertNoError(t *testing.T, err error) { } } +func TestReconcile_PluginVersionSelection(t *testing.T) { + defer util.SetConsoleAPIFound(util.IsConsoleAPIFound()) + util.SetConsoleAPIFound(true) + + tests := []struct { + name string + clusterVersion string + expectPlugin bool + expectedImage string + }{ + { + name: "OCP below minimum (4.17) skips plugin entirely", + clusterVersion: "4.17.0", + expectPlugin: false, + }, + { + name: "OCP 4.18 installs PF5 plugin", + clusterVersion: "4.18.1", + expectPlugin: true, + expectedImage: common.DefaultConsoleImagePF5 + ":" + common.DefaultConsoleVersionPF5, + }, + { + name: "OCP 4.19 installs PF6 plugin", + clusterVersion: "4.19.0", + expectPlugin: true, + expectedImage: common.DefaultConsoleImage + ":" + common.DefaultConsoleVersion, + }, + { + name: "OCP 4.20 installs PF6 plugin", + clusterVersion: "4.20.0", + expectPlugin: true, + expectedImage: common.DefaultConsoleImage + ":" + common.DefaultConsoleVersion, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + s := scheme.Scheme + addKnownTypesToScheme(s) + + fakeClient := fake.NewFakeClient(util.NewClusterVersion(test.clusterVersion), newGitopsService()) + reconciler := newReconcileGitOpsService(fakeClient, s) + + _, err := reconciler.Reconcile(context.TODO(), newRequest("test", "test")) + assertNoError(t, err) + + pluginDeploy := &appsv1.Deployment{} + err = fakeClient.Get(context.TODO(), types.NamespacedName{Name: gitopsPluginName, Namespace: serviceNamespace}, pluginDeploy) + + if !test.expectPlugin { + assert.Assert(t, errors.IsNotFound(err), "expected plugin deployment to be absent for OCP %s, but it exists", test.clusterVersion) + return + } + + assertNoError(t, err) + assert.Equal(t, pluginDeploy.Spec.Template.Spec.Containers[0].Image, test.expectedImage) + }) + } +} + func deploymentDefaultTolerations() []corev1.Toleration { toleration := []corev1.Toleration{ { diff --git a/hack/non-olm-install/README.md b/hack/non-olm-install/README.md index dfd0e458543..9982aa632ba 100644 --- a/hack/non-olm-install/README.md +++ b/hack/non-olm-install/README.md @@ -73,7 +73,8 @@ The following environment variables can be set to configure various options for | **ARGOCD_REDIS_IMAGE** | Image override for Redis component | `registry.redhat.io/rhel8/redis-6:1-110` | | **ARGOCD_REDIS_HA_PROXY_IMAGE** | Image override for Redis HA proxy component | `registry.redhat.io/openshift4/ose-haproxy-router:v4.12.0-202302280915.p0.g3065f65.assembly.stream` | | **BACKEND_IMAGE** | Image override for Backend component |`${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_ORG}/${IMAGE_PREFIX}gitops-rhel8:${GITOPS_OPERATOR_VER}`| -| **GITOPS_CONSOLE_PLUGIN_IMAGE** | Image override for console plugin component | `${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_ORG}/${IMAGE_PREFIX}console-plugin-rhel8:${GITOPS_OPERATOR_VER}` | +| **GITOPS_CONSOLE_PLUGIN_IMAGE** | Image override for the PF6 console plugin (OCP >= 4.19) | `${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_ORG}/${IMAGE_PREFIX}console-plugin-rhel8:${GITOPS_OPERATOR_VER}` | +| **GITOPS_CONSOLE_PLUGIN_IMAGE_PF5** | Image override for the PF5 console plugin (OCP 4.18.x) | `${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_ORG}/${IMAGE_PREFIX}console-plugin-4.18-rhel9:${GITOPS_OPERATOR_VER}` | #### Variables for Operator parameters diff --git a/hack/non-olm-install/install-gitops-operator.sh b/hack/non-olm-install/install-gitops-operator.sh index e9370a8db4e..42d26312ac4 100755 --- a/hack/non-olm-install/install-gitops-operator.sh +++ b/hack/non-olm-install/install-gitops-operator.sh @@ -22,6 +22,7 @@ ARGOCD_DEX_IMAGE=${ARGOCD_DEX_IMAGE:-"${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_O ARGOCD_IMAGE=${ARGOCD_IMAGE:-"${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_ORG}/${IMAGE_PREFIX}argocd-rhel8:${GITOPS_OPERATOR_VER}"} BACKEND_IMAGE=${BACKEND_IMAGE:-"${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_ORG}/${IMAGE_PREFIX}gitops-rhel8:${GITOPS_OPERATOR_VER}"} GITOPS_CONSOLE_PLUGIN_IMAGE=${GITOPS_CONSOLE_PLUGIN_IMAGE:-"${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_ORG}/${IMAGE_PREFIX}console-plugin-rhel8:${GITOPS_OPERATOR_VER}"} +GITOPS_CONSOLE_PLUGIN_IMAGE_PF5=${GITOPS_CONSOLE_PLUGIN_IMAGE_PF5:-"${OPERATOR_REGISTRY}/${OPERATOR_REGISTRY_ORG}/${IMAGE_PREFIX}console-plugin-4.18-rhel9:${GITOPS_OPERATOR_VER}"} # other images ARGOCD_KEYCLOAK_IMAGE=${ARGOCD_KEYCLOAK_IMAGE:-"registry.redhat.io/rh-sso-7/sso76-openshift-rhel8:7.6-32"} @@ -221,6 +222,8 @@ spec: value: ${ARGOCD_REDIS_HA_PROXY_IMAGE} - name: GITOPS_CONSOLE_PLUGIN_IMAGE value: ${GITOPS_CONSOLE_PLUGIN_IMAGE} + - name: GITOPS_CONSOLE_PLUGIN_IMAGE_PF5 + value: ${GITOPS_CONSOLE_PLUGIN_IMAGE_PF5} - name: ARGOCD_CLUSTER_CONFIG_NAMESPACES value: \"${ARGOCD_CLUSTER_CONFIG_NAMESPACES}\" - name: CLUSTER_SCOPED_ARGO_ROLLOUTS_NAMESPACES @@ -281,6 +284,7 @@ function extract_component_images_from_bundle_image() { ARGOCD_REDIS_HA_PROXY_IMAGE=$(cat "${WORK_DIR}"/container.yaml | ${YQ} '.env[] | select(.name=="ARGOCD_REDIS_HA_PROXY_IMAGE").value') BACKEND_IMAGE=$(cat "${WORK_DIR}"/container.yaml | ${YQ} '.env[] | select(.name=="BACKEND_IMAGE").value') GITOPS_CONSOLE_PLUGIN_IMAGE=$(cat "${WORK_DIR}"/container.yaml | ${YQ} '.env[] | select(.name=="GITOPS_CONSOLE_PLUGIN_IMAGE").value') + GITOPS_CONSOLE_PLUGIN_IMAGE_PF5=$(cat "${WORK_DIR}"/container.yaml | ${YQ} '.env[] | select(.name=="GITOPS_CONSOLE_PLUGIN_IMAGE_PF5").value') } # Initialize a temporary work directory to store the artifacts and @@ -425,6 +429,7 @@ function print_info() { echo "ARGOCD_REDIS_HA_PROXY_IMAGE: ${ARGOCD_REDIS_HA_PROXY_IMAGE}" echo "BACKEND_IMAGE: ${BACKEND_IMAGE}" echo "GITOPS_CONSOLE_PLUGIN_IMAGE: ${GITOPS_CONSOLE_PLUGIN_IMAGE}" + echo "GITOPS_CONSOLE_PLUGIN_IMAGE_PF5: ${GITOPS_CONSOLE_PLUGIN_IMAGE_PF5}" echo "" echo "Operator configurations:"