From 4b70c989af1d5aeccc13af6f5bb48e45cfa69b8a Mon Sep 17 00:00:00 2001 From: Jawed khelil Date: Thu, 30 Apr 2026 12:34:19 +0200 Subject: [PATCH] feat(tls): inject centrally managed TLS config into pipelines-as-code deployment and webhook Extend the OpenShift TLS centralization pattern (introduced for Tekton Pipelines and Triggers webhooks) to the Pipelines-as-Code webhook. The openshiftpipelinesascode extension now resolves the cluster-wide APIServer TLS security profile in PreReconcile and injects the resulting TLS_MIN_VERSION and TLS_CIPHER_SUITES environment variables into the pipelines-as-code-webhook deployment (pac-webhook container) via the Transformers step, identical to the approach used for tekton-triggers-webhook. PlatformDataHashKey propagation is wired through EnsureOpenShiftPipelinesAsCodeExists / createOPAC / updateOPAC so that any change to the cluster APIServer TLS profile automatically re-reconciles the OpenShiftPipelinesAsCode CR and redeploys the webhook with the updated settings. Resolves: SRVKP-9616 Made-with: Cursor --- .../kubernetes/tektonconfig/extension.go | 2 +- .../tektoninstallerset/client/check.go | 12 +- .../tektoninstallerset/client/check_test.go | 8 +- .../tektoninstallerset/client/update.go | 8 +- .../tektoninstallerset/client/update_test.go | 8 +- .../openshiftpipelinesascode/extension.go | 53 ++++- .../extension_test.go | 193 ++++++++++++++++++ .../openshift/tektonconfig/extension.go | 2 +- .../pipelinesascode/pipelinesascode.go | 25 ++- .../pipelinesascode/pipelinesascode_test.go | 12 +- 10 files changed, 298 insertions(+), 25 deletions(-) create mode 100644 pkg/reconciler/openshift/openshiftpipelinesascode/extension_test.go diff --git a/pkg/reconciler/kubernetes/tektonconfig/extension.go b/pkg/reconciler/kubernetes/tektonconfig/extension.go index 1d26194efa..45fe981230 100644 --- a/pkg/reconciler/kubernetes/tektonconfig/extension.go +++ b/pkg/reconciler/kubernetes/tektonconfig/extension.go @@ -61,7 +61,7 @@ func (oe kubernetesExtension) PostReconcile(ctx context.Context, comp v1alpha1.T pacSpec := configInstance.Spec.PipelinesAsCodeForCurrentPlatform() if pacSpec != nil && pacSpec.Enable != nil && *pacSpec.Enable { - if _, err := pac.EnsureOpenShiftPipelinesAsCodeExists(ctx, oe.operatorClientSet.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), configInstance, configInstance.Status.Version); err != nil { + if _, err := pac.EnsureOpenShiftPipelinesAsCodeExists(ctx, oe.operatorClientSet.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), configInstance, configInstance.Status.Version, ""); err != nil { configInstance.Status.MarkComponentNotReady(fmt.Sprintf("OpenShiftPipelinesAsCode: %s", err.Error())) return v1alpha1.REQUEUE_EVENT_AFTER } diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/check.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/check.go index 39e7ed4616..518e3862aa 100644 --- a/pkg/reconciler/kubernetes/tektoninstallerset/client/check.go +++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/check.go @@ -133,9 +133,19 @@ func verifyMeta(resourceKind, isType string, logger *zap.SugaredLogger, set v1al } // Spec Hash Check + // The hash intentionally includes the platform-data-hash annotation so that + // a change to the cluster-wide TLS security profile (which is propagated via + // that annotation) triggers an InstallerSet update even when the CR spec is + // otherwise unchanged. logger.Debugf("%v/%v: spec hash check", resourceKind, isType) - expectedHash, err := hash.Compute(comp.GetSpec()) + expectedHash, err := hash.Compute(struct { + Spec interface{} + PlatformDataHash string + }{ + Spec: comp.GetSpec(), + PlatformDataHash: comp.GetAnnotations()[v1alpha1.PlatformDataHashKey], + }) if err != nil { return err } diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/check_test.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/check_test.go index 322245e50c..d4330f7a37 100644 --- a/pkg/reconciler/kubernetes/tektoninstallerset/client/check_test.go +++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/check_test.go @@ -49,7 +49,13 @@ func buildTriggerComponent(disabled bool) *v1alpha1.TektonTrigger { } func computeHash(comp *v1alpha1.TektonTrigger) string { - h, err := hash.Compute(comp.GetSpec()) + h, err := hash.Compute(struct { + Spec interface{} + PlatformDataHash string + }{ + Spec: comp.GetSpec(), + PlatformDataHash: comp.GetAnnotations()[v1alpha1.PlatformDataHashKey], + }) if err != nil { panic("failed to compute hash: " + err.Error()) } diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/update.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/update.go index f3a7dae5fd..61d8ec6740 100644 --- a/pkg/reconciler/kubernetes/tektoninstallerset/client/update.go +++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/update.go @@ -88,7 +88,13 @@ func (i *InstallerSetClient) updateSet(ctx context.Context, comp v1alpha1.Tekton return err } - specHash, err := hash.Compute(comp.GetSpec()) + specHash, err := hash.Compute(struct { + Spec interface{} + PlatformDataHash string + }{ + Spec: comp.GetSpec(), + PlatformDataHash: comp.GetAnnotations()[v1alpha1.PlatformDataHashKey], + }) if err != nil { return err } diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/update_test.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/update_test.go index 3626229438..3189999898 100644 --- a/pkg/reconciler/kubernetes/tektoninstallerset/client/update_test.go +++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/update_test.go @@ -41,7 +41,13 @@ func TestInstallerSetClient_Update(t *testing.T) { }, } - expectedHash, err := hash.Compute(comp.GetSpec()) + expectedHash, err := hash.Compute(struct { + Spec interface{} + PlatformDataHash string + }{ + Spec: comp.GetSpec(), + PlatformDataHash: comp.GetAnnotations()[v1alpha1.PlatformDataHashKey], + }) assert.NilError(t, err) tests := []struct { diff --git a/pkg/reconciler/openshift/openshiftpipelinesascode/extension.go b/pkg/reconciler/openshift/openshiftpipelinesascode/extension.go index 8875e06466..382c673947 100644 --- a/pkg/reconciler/openshift/openshiftpipelinesascode/extension.go +++ b/pkg/reconciler/openshift/openshiftpipelinesascode/extension.go @@ -25,8 +25,10 @@ import ( mf "github.com/manifestival/manifestival" "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" operatorclient "github.com/tektoncd/operator/pkg/client/injection/client" + tektonConfiginformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektonconfig" "github.com/tektoncd/operator/pkg/reconciler/common" "github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client" + occommon "github.com/tektoncd/operator/pkg/reconciler/openshift/common" "go.uber.org/zap" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -37,7 +39,13 @@ import ( ) const ( - openshiftNS = "openshift" + openshiftNS = "openshift" + pacControllerDeployment = "pipelines-as-code-controller" + pacControllerContainerName = "pac-controller" + pacWatcherDeployment = "pipelines-as-code-watcher" + pacWatcherContainerName = "pac-watcher" + pacWebhookDeployment = "pipelines-as-code-webhook" + pacWebhookContainerName = "pac-webhook" ) func OpenShiftExtension(ctx context.Context) common.Extension { @@ -68,13 +76,14 @@ func OpenShiftExtension(ctx context.Context) common.Extension { } tisClient := operatorclient.Get(ctx).OperatorV1alpha1().TektonInstallerSets() - return openshiftExtension{ + return &openshiftExtension{ // component version is used for metrics, passing a dummy // value through extension not going to affect execution installerSetClient: client.NewInstallerSetClient(tisClient, operatorVer, "pipelines-as-code-ext", v1alpha1.KindOpenShiftPipelinesAsCode, nil), pacManifest: &pacManifest, pipelineRunTemplates: prTemplates, kubeClientSet: kubeclient.Get(ctx), + tektonConfigLister: tektonConfiginformer.Get(ctx).Lister(), } } @@ -83,17 +92,44 @@ type openshiftExtension struct { pacManifest *mf.Manifest pipelineRunTemplates *mf.Manifest kubeClientSet kubernetes.Interface + tektonConfigLister occommon.TektonConfigLister + resolvedTLSConfig *occommon.TLSEnvVars } -func (oe openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Transformer { - return []mf.Transformer{ +func (oe *openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Transformer { + trns := []mf.Transformer{ InjectNamespaceOwnerForPACWebhook(oe.kubeClientSet, comp.GetSpec().GetTargetNamespace()), } + + // Inject APIServer TLS profile env vars into all three PAC deployments so that + // they apply the cluster-wide TLS version and cipher suite policy (PQC readiness). + if oe.resolvedTLSConfig != nil { + trns = append(trns, + occommon.InjectTLSEnvVars(oe.resolvedTLSConfig, "Deployment", pacControllerDeployment, []string{pacControllerContainerName}), + occommon.InjectTLSEnvVars(oe.resolvedTLSConfig, "Deployment", pacWatcherDeployment, []string{pacWatcherContainerName}), + occommon.InjectTLSEnvVars(oe.resolvedTLSConfig, "Deployment", pacWebhookDeployment, []string{pacWebhookContainerName}), + ) + } + + return trns } -func (oe openshiftExtension) PreReconcile(context.Context, v1alpha1.TektonComponent) error { + +func (oe *openshiftExtension) PreReconcile(ctx context.Context, _ v1alpha1.TektonComponent) error { + logger := logging.FromContext(ctx) + + resolvedTLS, err := occommon.ResolveCentralTLSToEnvVars(ctx, oe.tektonConfigLister) + if err != nil { + return err + } + oe.resolvedTLSConfig = resolvedTLS + if oe.resolvedTLSConfig != nil { + logger.Infof("Injecting central TLS config into PAC deployments: MinVersion=%s", oe.resolvedTLSConfig.MinVersion) + } + return nil } -func (oe openshiftExtension) PostReconcile(ctx context.Context, comp v1alpha1.TektonComponent) error { + +func (oe *openshiftExtension) PostReconcile(ctx context.Context, comp v1alpha1.TektonComponent) error { logger := logging.FromContext(ctx) if err := oe.installerSetClient.PostSet(ctx, comp, oe.pipelineRunTemplates, extFilterAndTransform()); err != nil { @@ -107,11 +143,12 @@ func (oe openshiftExtension) PostReconcile(ctx context.Context, comp v1alpha1.Te } return nil } -func (oe openshiftExtension) Finalize(context.Context, v1alpha1.TektonComponent) error { + +func (oe *openshiftExtension) Finalize(context.Context, v1alpha1.TektonComponent) error { return nil } -func (oe openshiftExtension) GetPlatformData() string { +func (oe *openshiftExtension) GetPlatformData() string { return "" } diff --git a/pkg/reconciler/openshift/openshiftpipelinesascode/extension_test.go b/pkg/reconciler/openshift/openshiftpipelinesascode/extension_test.go new file mode 100644 index 0000000000..11109eaae0 --- /dev/null +++ b/pkg/reconciler/openshift/openshiftpipelinesascode/extension_test.go @@ -0,0 +1,193 @@ +/* +Copyright 2026 The Tekton 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 openshiftpipelinesascode + +import ( + "testing" + + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/apis/operator/v1alpha1" + occommon "github.com/tektoncd/operator/pkg/reconciler/openshift/common" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +// makePACDeployment returns an unstructured PAC Deployment for transformer tests. +func makePACDeployment(t *testing.T, deploymentName, containerName string) unstructured.Unstructured { + t.Helper() + + d := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: deploymentName, + Namespace: "openshift-pipelines", + }, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: containerName}, + }, + }, + }, + }, + } + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(d) + if err != nil { + t.Fatalf("failed to convert deployment to unstructured: %v", err) + } + u := unstructured.Unstructured{Object: obj} + u.SetKind("Deployment") + u.SetAPIVersion("apps/v1") + return u +} + +func makePACWebhookDeployment(t *testing.T) unstructured.Unstructured { + t.Helper() + return makePACDeployment(t, pacWebhookDeployment, pacWebhookContainerName) +} + +func TestPACTransformers_NoTLSConfig(t *testing.T) { + ext := &openshiftExtension{ + resolvedTLSConfig: nil, + } + + transformers := ext.Transformers(&v1alpha1.OpenShiftPipelinesAsCode{}) + + u := makePACWebhookDeployment(t) + manifest, err := mf.ManifestFrom(mf.Slice([]unstructured.Unstructured{u})) + if err != nil { + t.Fatalf("failed to build manifest: %v", err) + } + + transformed, err := manifest.Transform(transformers...) + if err != nil { + t.Fatalf("transform failed: %v", err) + } + + d := &appsv1.Deployment{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(transformed.Resources()[0].Object, d); err != nil { + t.Fatalf("failed to convert back: %v", err) + } + for _, c := range d.Spec.Template.Spec.Containers { + if c.Name != pacWebhookContainerName { + continue + } + for _, e := range c.Env { + if e.Name == occommon.TLSMinVersionEnvVar || e.Name == occommon.TLSCipherSuitesEnvVar { + t.Errorf("unexpected TLS env var %s set when resolvedTLSConfig is nil", e.Name) + } + } + } +} + +func TestPACTransformers_WithTLSConfig_InjectsEnvVarsIntoWebhook(t *testing.T) { + tlsConfig := &occommon.TLSEnvVars{ + MinVersion: "1.2", + CipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_AES_128_GCM_SHA256", + } + assertTLSInjected(t, &openshiftExtension{resolvedTLSConfig: tlsConfig}, pacWebhookDeployment, pacWebhookContainerName, tlsConfig) +} + +// assertTLSInjected runs the Transformers against a single-resource manifest and +// checks that TLS env vars are present in the named container. +func assertTLSInjected(t *testing.T, ext *openshiftExtension, deploymentName, containerName string, tlsConfig *occommon.TLSEnvVars) { + t.Helper() + + u := makePACDeployment(t, deploymentName, containerName) + manifest, err := mf.ManifestFrom(mf.Slice([]unstructured.Unstructured{u})) + if err != nil { + t.Fatalf("failed to build manifest: %v", err) + } + + transformed, err := manifest.Transform(ext.Transformers(&v1alpha1.OpenShiftPipelinesAsCode{})...) + if err != nil { + t.Fatalf("transform failed: %v", err) + } + + d := &appsv1.Deployment{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(transformed.Resources()[0].Object, d); err != nil { + t.Fatalf("failed to convert back: %v", err) + } + + envMap := map[string]string{} + for _, c := range d.Spec.Template.Spec.Containers { + if c.Name != containerName { + continue + } + for _, e := range c.Env { + envMap[e.Name] = e.Value + } + } + + if got := envMap[occommon.TLSMinVersionEnvVar]; got != tlsConfig.MinVersion { + t.Errorf("[%s/%s] %s = %q, want %q", deploymentName, containerName, occommon.TLSMinVersionEnvVar, got, tlsConfig.MinVersion) + } + if got := envMap[occommon.TLSCipherSuitesEnvVar]; got != tlsConfig.CipherSuites { + t.Errorf("[%s/%s] %s = %q, want %q", deploymentName, containerName, occommon.TLSCipherSuitesEnvVar, got, tlsConfig.CipherSuites) + } +} + +func TestPACTransformers_WithTLSConfig_InjectsEnvVarsIntoController(t *testing.T) { + tlsConfig := &occommon.TLSEnvVars{ + MinVersion: "1.2", + CipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_AES_128_GCM_SHA256", + } + assertTLSInjected(t, &openshiftExtension{resolvedTLSConfig: tlsConfig}, pacControllerDeployment, pacControllerContainerName, tlsConfig) +} + +func TestPACTransformers_WithTLSConfig_InjectsEnvVarsIntoWatcher(t *testing.T) { + tlsConfig := &occommon.TLSEnvVars{ + MinVersion: "1.2", + CipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_AES_128_GCM_SHA256", + } + assertTLSInjected(t, &openshiftExtension{resolvedTLSConfig: tlsConfig}, pacWatcherDeployment, pacWatcherContainerName, tlsConfig) +} + +func TestPACTransformers_WithTLSConfig_DoesNotInjectIntoUnknownDeployment(t *testing.T) { + tlsConfig := &occommon.TLSEnvVars{ + MinVersion: "1.3", + CipherSuites: "TLS_AES_128_GCM_SHA256", + } + ext := &openshiftExtension{resolvedTLSConfig: tlsConfig} + transformers := ext.Transformers(&v1alpha1.OpenShiftPipelinesAsCode{}) + + // An unrelated deployment must not receive TLS env vars. + u := makePACDeployment(t, "some-other-deployment", "some-container") + manifest, err := mf.ManifestFrom(mf.Slice([]unstructured.Unstructured{u})) + if err != nil { + t.Fatalf("failed to build manifest: %v", err) + } + transformed, err := manifest.Transform(transformers...) + if err != nil { + t.Fatalf("transform failed: %v", err) + } + + result := &appsv1.Deployment{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(transformed.Resources()[0].Object, result); err != nil { + t.Fatalf("failed to convert back: %v", err) + } + for _, c := range result.Spec.Template.Spec.Containers { + for _, e := range c.Env { + if e.Name == occommon.TLSMinVersionEnvVar || e.Name == occommon.TLSCipherSuitesEnvVar { + t.Errorf("unexpected TLS env var %s injected into unrelated deployment", e.Name) + } + } + } +} diff --git a/pkg/reconciler/openshift/tektonconfig/extension.go b/pkg/reconciler/openshift/tektonconfig/extension.go index 8d848f9fc7..43f2face98 100644 --- a/pkg/reconciler/openshift/tektonconfig/extension.go +++ b/pkg/reconciler/openshift/tektonconfig/extension.go @@ -191,7 +191,7 @@ func (oe openshiftExtension) PostReconcile(ctx context.Context, comp v1alpha1.Te pacSpec := configInstance.Spec.PipelinesAsCodeForCurrentPlatform() if pacSpec != nil && pacSpec.Enable != nil && *pacSpec.Enable { - if _, err := pac.EnsureOpenShiftPipelinesAsCodeExists(ctx, oe.operatorClientSet.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), configInstance, oe.operatorVersion); err != nil { + if _, err := pac.EnsureOpenShiftPipelinesAsCodeExists(ctx, oe.operatorClientSet.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), configInstance, oe.operatorVersion, oe.GetPlatformData()); err != nil { configInstance.Status.MarkComponentNotReady(fmt.Sprintf("OpenShiftPipelinesAsCode: %s", err.Error())) return v1alpha1.REQUEUE_EVENT_AFTER } diff --git a/pkg/reconciler/shared/tektonconfig/pipelinesascode/pipelinesascode.go b/pkg/reconciler/shared/tektonconfig/pipelinesascode/pipelinesascode.go index e088b0ff74..4400ef017b 100644 --- a/pkg/reconciler/shared/tektonconfig/pipelinesascode/pipelinesascode.go +++ b/pkg/reconciler/shared/tektonconfig/pipelinesascode/pipelinesascode.go @@ -40,19 +40,19 @@ func pacSettingsFromTektonPAC(p *v1alpha1.PipelinesAsCode) v1alpha1.PACSettings } } -func EnsureOpenShiftPipelinesAsCodeExists(ctx context.Context, clients op.OpenShiftPipelinesAsCodeInterface, config *v1alpha1.TektonConfig, operatorVersion string) (*v1alpha1.OpenShiftPipelinesAsCode, error) { +func EnsureOpenShiftPipelinesAsCodeExists(ctx context.Context, clients op.OpenShiftPipelinesAsCodeInterface, config *v1alpha1.TektonConfig, operatorVersion string, platformData string) (*v1alpha1.OpenShiftPipelinesAsCode, error) { opacCR, err := GetPAC(ctx, clients, v1alpha1.OpenShiftPipelinesAsCodeName) if err != nil { if !apierrs.IsNotFound(err) { return nil, err } - if _, err = createOPAC(ctx, clients, config, operatorVersion); err != nil { + if _, err = createOPAC(ctx, clients, config, operatorVersion, platformData); err != nil { return nil, err } return nil, v1alpha1.RECONCILE_AGAIN_ERR } - opacCR, err = updateOPAC(ctx, opacCR, config, clients, operatorVersion) + opacCR, err = updateOPAC(ctx, opacCR, config, clients, operatorVersion, platformData) if err != nil { return nil, err } @@ -68,11 +68,16 @@ func EnsureOpenShiftPipelinesAsCodeExists(ctx context.Context, clients op.OpenSh return opacCR, err } -func createOPAC(ctx context.Context, clients op.OpenShiftPipelinesAsCodeInterface, config *v1alpha1.TektonConfig, operatorVersion string) (*v1alpha1.OpenShiftPipelinesAsCode, error) { +func createOPAC(ctx context.Context, clients op.OpenShiftPipelinesAsCodeInterface, config *v1alpha1.TektonConfig, operatorVersion string, platformData string) (*v1alpha1.OpenShiftPipelinesAsCode, error) { ownerRef := *metav1.NewControllerRef(config, config.GroupVersionKind()) pacSettings := pacSettingsFromTektonPAC(config.Spec.PipelinesAsCodeForCurrentPlatform()) + annotations := map[string]string{} + if platformData != "" { + annotations[v1alpha1.PlatformDataHashKey] = platformData + } + opacCR := &v1alpha1.OpenShiftPipelinesAsCode{ ObjectMeta: metav1.ObjectMeta{ Name: v1alpha1.OpenShiftPipelinesAsCodeName, @@ -80,6 +85,7 @@ func createOPAC(ctx context.Context, clients op.OpenShiftPipelinesAsCodeInterfac Labels: map[string]string{ v1alpha1.ReleaseVersionKey: operatorVersion, }, + Annotations: annotations, }, Spec: v1alpha1.OpenShiftPipelinesAsCodeSpec{ CommonSpec: v1alpha1.CommonSpec{ @@ -101,7 +107,7 @@ func GetPAC(ctx context.Context, clients op.OpenShiftPipelinesAsCodeInterface, n } func updateOPAC(ctx context.Context, opacCR *v1alpha1.OpenShiftPipelinesAsCode, config *v1alpha1.TektonConfig, - clients op.OpenShiftPipelinesAsCodeInterface, operatorVersion string, + clients op.OpenShiftPipelinesAsCodeInterface, operatorVersion string, platformData string, ) (*v1alpha1.OpenShiftPipelinesAsCode, error) { updated := false @@ -148,6 +154,15 @@ func updateOPAC(ctx context.Context, opacCR *v1alpha1.OpenShiftPipelinesAsCode, updated = true } + oldPlatformData := opacCR.ObjectMeta.Annotations[v1alpha1.PlatformDataHashKey] + if oldPlatformData != platformData { + if opacCR.ObjectMeta.Annotations == nil { + opacCR.ObjectMeta.Annotations = map[string]string{} + } + opacCR.ObjectMeta.Annotations[v1alpha1.PlatformDataHashKey] = platformData + updated = true + } + if updated { _, err := clients.Update(ctx, opacCR, metav1.UpdateOptions{}) if err != nil { diff --git a/pkg/reconciler/shared/tektonconfig/pipelinesascode/pipelinesascode_test.go b/pkg/reconciler/shared/tektonconfig/pipelinesascode/pipelinesascode_test.go index 5343056a4e..edd569eea9 100644 --- a/pkg/reconciler/shared/tektonconfig/pipelinesascode/pipelinesascode_test.go +++ b/pkg/reconciler/shared/tektonconfig/pipelinesascode/pipelinesascode_test.go @@ -38,22 +38,22 @@ func TestEnsureOpenShiftPipelinesAsCodeExists(t *testing.T) { t.Setenv("PLATFORM", "openshift") tConfig.SetDefaults(ctx) - _, err := EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0") + _, err := EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0", "") util.AssertEqual(t, err, v1alpha1.RECONCILE_AGAIN_ERR) - _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0") + _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0", "") util.AssertEqual(t, err, v1alpha1.RECONCILE_AGAIN_ERR) markOPACReady(t, ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes()) - _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0") + _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0", "") util.AssertEqual(t, err, nil) tConfig.Spec.TargetNamespace = "foobar" - _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0") + _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0", "") util.AssertEqual(t, err, v1alpha1.RECONCILE_AGAIN_ERR) - _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0") + _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0", "") util.AssertEqual(t, err, nil) } @@ -68,7 +68,7 @@ func TestEnsureOpenShiftPipelinesAsCodeCRNotExists(t *testing.T) { tConfig := pipeline.GetTektonConfig() tConfig.SetDefaults(ctx) - _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0") + _, err = EnsureOpenShiftPipelinesAsCodeExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes(), tConfig, "v0.70.0", "") util.AssertEqual(t, err, v1alpha1.RECONCILE_AGAIN_ERR) err = EnsureOpenShiftPipelinesAsCodeCRNotExists(ctx, c.OperatorV1alpha1().OpenShiftPipelinesAsCodes())