Repository: tektoncd/operator
Pull request #1172 (2022-11-21 06:27:34)

Remove Tekton Dashboard obselete installerset on upgrade
Signed-off-by: Puneet Punamiya ppunamiy@redhat.com

# Changes

<!-- 🎉🎉🎉 Thank you for the PR!!! 🎉🎉🎉 -->

<!-- Describe your changes here- ideally you can get that description straight from
your descriptive commit message(s)! -->

# Submitter Checklist

These are the criteria that every PR should meet, please check them off as you
review them:

- [ ] Run `make test lint` before submitting a PR
- [ ] Includes [tests](https://github.com/tektoncd/community/blob/master/standards.md#principles) (if functionality changed/added)
- [ ] Includes [docs](https://github.com/tektoncd/community/blob/master/standards.md#principles) (if user facing)
- [ ] Commit messages follow [commit message best practices](https://github.com/tektoncd/community/blob/master/standards.md#commit-messages)

_See [the contribution guide](https://github.com/tektoncd/operator/blob/master/CONTRIBUTING.md) for more details._

# Release Notes

<!--
Describe any user facing changes here, or delete this block.

Examples of user facing changes:
- API changes
- Bug fixes
- Any changes in behavior
- Changes requiring upgrade notices or deprecation warnings

For pull requests with a release note:

```release-note
Your release note here
```

For pull requests that require additional action from users switching to the new release, include the string "action required" (case insensitive) in the release note:

```release-note
action required: your release note here
```

For pull requests that don't need to be mentioned at release time, use the `/release-note-none` Prow command to add the `release-note-none` label to the PR. You can also write the string "NONE" as a release note in your PR description:

```release-note
NONE
```
-->


tekton-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **vincent-pli** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @vincent-pli` in a comment when ready.

The full list of commands accepted by this bot can be found [here](https://prow.tekton.dev/command-help?repo=tektoncd%2Foperator).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/tektoncd/operator/blob/main/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["vincent-pli"]} -->

Patch:
diff --git a/README.md b/README.md
index 55b311d8a..16894d387 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,10 @@
 [![Go Report Card](https://goreportcard.com/badge/tektoncd/operator)](https://goreportcard.com/report/tektoncd/operator)
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6548/badge)](https://bestpractices.coreinfrastructure.org/projects/6548)
 
+<p align="center">
+<img width="250" height="325"  src="tekton-operator.png" alt="Operator Icon" title="Operator Icon"></img>
+</p>
+
 The quickest and easiest way to install, upgrade and manage TektonCD [Pipelines](https://github.com/tektoncd/pipeline),
 [Dashboard](https://github.com/tektoncd/dashboard), [Triggers](https://github.com/tektoncd/triggers)
 on any Kubernetes Cluster.
diff --git a/components.yaml b/components.yaml
index ee4653961..16fd0e6de 100644
--- a/components.yaml
+++ b/components.yaml
@@ -6,13 +6,13 @@ dashboard:
   version: v0.30.0
 hub:
   github: tektoncd/hub
-  version: v1.11.0
+  version: v1.11.1
 pipeline:
   github: tektoncd/pipeline
   version: v0.41.0
 pipelines-as-code:
   github: openshift-pipelines/pipelines-as-code
-  version: v0.13.1
+  version: v0.14.1
 results:
   github: tektoncd/results
   version: v0.4.0
diff --git a/docs/TektonDashboard.md b/docs/TektonDashboard.md
index 4582c3bf3..be562f9b1 100644
--- a/docs/TektonDashboard.md
+++ b/docs/TektonDashboard.md
@@ -27,6 +27,10 @@ You can install this component using [TektonConfig](./TektonConfig.md) by choosi
 
 - `readonly` (Default: `false`)
 
-    If set to true, installs the Dashboard in read-only mode.
+  If set to true, installs the Dashboard in read-only mode.
+
+- `external-logs`
+
+  External URL from which to fetch logs when logs are not available in the cluster  
 
 [dashboard]:https://github.com/tektoncd/dashboard
diff --git a/pkg/apis/operator/v1alpha1/tektondashboard_types.go b/pkg/apis/operator/v1alpha1/tektondashboard_types.go
index 96f21028b..b06e00f93 100644
--- a/pkg/apis/operator/v1alpha1/tektondashboard_types.go
+++ b/pkg/apis/operator/v1alpha1/tektondashboard_types.go
@@ -88,4 +88,6 @@ type Dashboard struct {
 type DashboardProperties struct {
 	// Readonly when set to true configures the Tekton dashboard in read-only mode
 	Readonly bool `json:"readonly"`
+	// +optional
+	ExternalLogs string `json:"external-logs,omitempty"`
 }
diff --git a/pkg/reconciler/common/transformers.go b/pkg/reconciler/common/transformers.go
index ea580601d..86f42ee42 100644
--- a/pkg/reconciler/common/transformers.go
+++ b/pkg/reconciler/common/transformers.go
@@ -777,3 +777,33 @@ func CopyConfigMap(configMapName string, expectedValues map[string]string) mf.Tr
 		return nil
 	}
 }
+
+func ReplaceDeploymentArg(deploymentName, existingArg, newArg string) mf.Transformer {
+	return func(u *unstructured.Unstructured) error {
+		if u.GetKind() != "Deployment" {
+			return nil
+		}
+		if u.GetName() != deploymentName {
+			return nil
+		}
+
+		d := &appsv1.Deployment{}
+		err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, d)
+		if err != nil {
+			return err
+		}
+
+		for i, arg := range d.Spec.Template.Spec.Containers[0].Args {
+			if arg == existingArg {
+				d.Spec.Template.Spec.Containers[0].Args[i] = newArg
+			}
+		}
+
+		unstrObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(d)
+		if err != nil {
+			return err
+		}
+		u.SetUnstructuredContent(unstrObj)
+		return nil
+	}
+}
diff --git a/pkg/reconciler/kubernetes/tektonconfig/extension/dashboard.go b/pkg/reconciler/kubernetes/tektonconfig/extension/dashboard.go
index be4c9d1ed..90163f05f 100644
--- a/pkg/reconciler/kubernetes/tektonconfig/extension/dashboard.go
+++ b/pkg/reconciler/kubernetes/tektonconfig/extension/dashboard.go
@@ -19,7 +19,6 @@ package extension
 import (
 	"context"
 	"fmt"
-	"knative.dev/pkg/apis"
 	"reflect"
 	"strings"
 
@@ -27,6 +26,7 @@ import (
 	op "github.com/tektoncd/operator/pkg/client/clientset/versioned/typed/operator/v1alpha1"
 	apierrs "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"knative.dev/pkg/apis"
 )
 
 func EnsureTektonDashboardExists(ctx context.Context, clients op.TektonDashboardInterface, config *v1alpha1.TektonConfig) (*v1alpha1.TektonDashboard, error) {
diff --git a/pkg/reconciler/kubernetes/tektondashboard/finalize.go b/pkg/reconciler/kubernetes/tektondashboard/finalize.go
index f385a7610..78d7d13f8 100644
--- a/pkg/reconciler/kubernetes/tektondashboard/finalize.go
+++ b/pkg/reconciler/kubernetes/tektondashboard/finalize.go
@@ -18,6 +18,7 @@ package tektondashboard
 
 import (
 	"context"
+
 	mf "github.com/manifestival/manifestival"
 	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
 	"knative.dev/pkg/logging"
diff --git a/pkg/reconciler/kubernetes/tektondashboard/reconcile.go b/pkg/reconciler/kubernetes/tektondashboard/reconcile.go
index aed013c06..74795cabb 100644
--- a/pkg/reconciler/kubernetes/tektondashboard/reconcile.go
+++ b/pkg/reconciler/kubernetes/tektondashboard/reconcile.go
@@ -89,6 +89,11 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, td *v1alpha1.TektonDashb
 	}
 	td.Status.MarkDependenciesInstalled()
 
+	if err := r.installerSetClient.RemoveObsoleteSets(ctx); err != nil {
+		logger.Error("failed to remove obsolete installer sets: %v", err)
+		return err
+	}
+
 	if err := r.extension.PreReconcile(ctx, td); err != nil {
 		td.Status.MarkPreReconcilerFailed(fmt.Sprintf("PreReconciliation failed: %s", err.Error()))
 		return err
diff --git a/pkg/reconciler/kubernetes/tektondashboard/transform.go b/pkg/reconciler/kubernetes/tektondashboard/transform.go
index 34e3fcb97..98bab42e9 100644
--- a/pkg/reconciler/kubernetes/tektondashboard/transform.go
+++ b/pkg/reconciler/kubernetes/tektondashboard/transform.go
@@ -18,15 +18,19 @@ package tektondashboard
 
 import (
 	"context"
+
 	mf "github.com/manifestival/manifestival"
 	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
 	"github.com/tektoncd/operator/pkg/reconciler/common"
 	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client"
 )
 
+const (
+	externalLogsArg = "--external-logs="
+)
+
 func filterAndTransform(extension common.Extension) client.FilterAndTransform {
 	return func(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) (*mf.Manifest, error) {
-
 		dashboard := comp.(*v1alpha1.TektonDashboard)
 		trns := extension.Transformers(dashboard)
 		extra := []mf.Transformer{
@@ -36,10 +40,13 @@ func filterAndTransform(extension common.Extension) client.FilterAndTransform {
 			common.AddDeploymentRestrictedPSA(),
 		}
 		trns = append(trns, extra...)
+		if dashboard.Spec.ExternalLogs != "" {
+			updatedExternalLogsArg := externalLogsArg + dashboard.Spec.ExternalLogs
+			trns = append(trns, common.ReplaceDeploymentArg("tekton-dashboard", externalLogsArg, updatedExternalLogsArg))
+		}
 		if err := common.Transform(ctx, manifest, dashboard, trns...); err != nil {
 			return nil, err
 		}
-
 		return manifest, nil
 	}
 }
diff --git a/pkg/reconciler/kubernetes/tektoninstallerset/client/obsolete_sets.go b/pkg/reconciler/kubernetes/tektoninstallerset/client/obsolete_sets.go
index 70c505bc2..d0d731872 100644
--- a/pkg/reconciler/kubernetes/tektoninstallerset/client/obsolete_sets.go
+++ b/pkg/reconciler/kubernetes/tektoninstallerset/client/obsolete_sets.go
@@ -27,14 +27,19 @@ import (
 
 func (i *InstallerSetClient) RemoveObsoleteSets(ctx context.Context) error {
 	var sets []string
-	if i.resourceKind == v1alpha1.KindTektonPipeline {
+
+	switch i.resourceKind {
+	case v1alpha1.KindTektonPipeline:
 		sets = []string{"pipeline", "PrePipeline", "PostPipeline"}
-	} else if i.resourceKind == v1alpha1.KindTektonTrigger {
+	case v1alpha1.KindTektonTrigger:
 		sets = []string{"trigger"}
-	} else if i.resourceKind == v1alpha1.KindTektonAddon {
+	case v1alpha1.KindTektonAddon:
 		// not adding VersionedClusterTask here, as we keep versioned clustertasks on upgrade
 		sets = []string{"ClusterTask", "CommunityClusterTask", "PipelinesTemplate", "TriggersResources", "ConsoleCLI", "MiscellaneousResources", "PipelinesAsCode"}
+	case v1alpha1.KindTektonDashboard:
+		sets = []string{"dashboard"}
 	}
+
 	labelSelector := labels.NewSelector()
 	createdReq, _ := labels.NewRequirement(v1alpha1.CreatedByKey, selection.Equals, []string{i.resourceKind})
 	if createdReq != nil {


Test patch:
diff --git a/pkg/reconciler/common/testdata/test-dashboard-deployment.yaml b/pkg/reconciler/common/testdata/test-dashboard-deployment.yaml
new file mode 100644
index 000000000..1349487c1
--- /dev/null
+++ b/pkg/reconciler/common/testdata/test-dashboard-deployment.yaml
@@ -0,0 +1,76 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  labels:
+    app: tekton-dashboard
+    app.kubernetes.io/component: dashboard
+    app.kubernetes.io/instance: default
+    app.kubernetes.io/name: dashboard
+    app.kubernetes.io/part-of: tekton-dashboard
+    app.kubernetes.io/version: v0.30.0
+    dashboard.tekton.dev/release: v0.30.0
+    version: v0.30.0
+  name: tekton-dashboard
+  namespace: tekton-pipelines
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app.kubernetes.io/component: dashboard
+      app.kubernetes.io/instance: default
+      app.kubernetes.io/name: dashboard
+      app.kubernetes.io/part-of: tekton-dashboard
+  template:
+    metadata:
+      labels:
+        app: tekton-dashboard
+        app.kubernetes.io/component: dashboard
+        app.kubernetes.io/instance: default
+        app.kubernetes.io/name: dashboard
+        app.kubernetes.io/part-of: tekton-dashboard
+        app.kubernetes.io/version: v0.30.0
+      name: tekton-dashboard
+    spec:
+      containers:
+        - args:
+            - --port=9097
+            - --logout-url=
+            - --pipelines-namespace=tekton-pipelines
+            - --triggers-namespace=tekton-pipelines
+            - --read-only=true
+            - --log-level=info
+            - --log-format=json
+            - --namespace=
+            - --stream-logs=true
+            - --external-logs=
+          env:
+            - name: INSTALLED_NAMESPACE
+              valueFrom:
+                fieldRef:
+                  fieldPath: metadata.namespace
+          image: gcr.io/tekton-releases/github.com/tektoncd/dashboard/cmd/dashboard:v0.30.0@sha256:85f7d38086fadb07556052ce873d44861c29ef690f47735f32d7e6a153ca8a92
+          livenessProbe:
+            httpGet:
+              path: /health
+              port: 9097
+          name: tekton-dashboard
+          ports:
+            - containerPort: 9097
+          readinessProbe:
+            httpGet:
+              path: /readiness
+              port: 9097
+          securityContext:
+            allowPrivilegeEscalation: false
+            capabilities:
+              drop:
+                - ALL
+            runAsGroup: 65532
+            runAsNonRoot: true
+            runAsUser: 65532
+            seccompProfile:
+              type: RuntimeDefault
+      nodeSelector:
+        kubernetes.io/os: linux
+      serviceAccountName: tekton-dashboard
+      volumes: []
diff --git a/pkg/reconciler/common/transformers_test.go b/pkg/reconciler/common/transformers_test.go
index 30be23ee5..6c5835fa9 100644
--- a/pkg/reconciler/common/transformers_test.go
+++ b/pkg/reconciler/common/transformers_test.go
@@ -602,3 +602,32 @@ func TestCopyConfigMapValues(t *testing.T) {
 	assert.Equal(t, cm.Data["ignore-me-field"], "")
 
 }
+
+func TestReplaceDeploymentArg(t *testing.T) {
+	testData := path.Join("testdata", "test-dashboard-deployment.yaml")
+	manifest, err := mf.ManifestFrom(mf.Recursive(testData))
+	assert.NilError(t, err)
+
+	existingArg := "--external-logs="
+	newArg := "--external-logs=abc"
+
+	newManifest, err := manifest.Transform(ReplaceDeploymentArg("tekton-dashboard", existingArg, newArg))
+	assert.NilError(t, err)
+
+	got := &appsv1.Deployment{}
+	err = runtime.DefaultUnstructuredConverter.FromUnstructured(newManifest.Resources()[0].Object, got)
+	if err != nil {
+		t.Errorf("failed to load deployment yaml")
+	}
+
+	found := false
+	for _, a := range got.Spec.Template.Spec.Containers[0].Args {
+		if a == newArg {
+			found = true
+		}
+	}
+
+	if !found {
+		t.Fatalf("failed to find new arg in deployment")
+	}
+}