Repository: kurator-dev/kurator
Pull request #547 (2024-01-09 03:17:42)

pipeline: add render trigger func
**What type of PR is this?**

/kind feature

**What this PR does / why we need it**:

part of #493 

**Which issue(s) this PR fixes**:
Fixes #

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required.
-->
```release-note

```



netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *kurator-dev* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | e2238a46b64370ecbda74905cb59eac2c7649df3 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/kurator-dev/deploys/659cbad8f29c230008daf5c9 |
kurator-bot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please assign kevin-wangzefeng for approval. For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kurator-dev%2Fkurator).

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

- **[OWNERS](https://github.com/kurator-dev/kurator/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":["kevin-wangzefeng"]} -->

Patch:
diff --git a/pkg/fleet-manager/pipeline/render/trigger.go b/pkg/fleet-manager/pipeline/render/trigger.go
new file mode 100644
index 0000000..3aa0ccc
--- /dev/null
+++ b/pkg/fleet-manager/pipeline/render/trigger.go
@@ -0,0 +1,163 @@
+/*
+Copyright Kurator 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 render
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	pipelineapi "kurator.dev/kurator/pkg/apis/pipeline/v1alpha1"
+)
+
+const (
+	TriggerTemplateName = "pipeline trigger template"
+)
+
+type TriggerConfig struct {
+	PipelineName      string
+	PipelineNamespace string
+	OwnerReference    *metav1.OwnerReference
+	AccessMode        string
+	StorageRequest    string
+	StorageClassName  string
+	VolumeMode        string
+}
+
+// ServiceAccountName is the service account used by trigger
+func (cfg TriggerConfig) ServiceAccountName() string {
+	return cfg.PipelineName
+}
+
+// RenderTriggerWithPipeline takes a pipeline object and generates YAML byte array configuration representing the trigger configuration.
+func RenderTriggerWithPipeline(pipeline *pipelineapi.Pipeline) ([]byte, error) {
+	config := TriggerConfig{
+		PipelineName:      pipeline.Name,
+		PipelineNamespace: pipeline.Namespace,
+		OwnerReference:    GeneratePipelineOwnerRef(pipeline),
+	}
+	if pipeline.Spec.SharedWorkspace != nil {
+		config.AccessMode = string(pipeline.Spec.SharedWorkspace.AccessMode)
+		config.StorageRequest = pipeline.Spec.SharedWorkspace.StorageRequest
+		config.StorageClassName = pipeline.Spec.SharedWorkspace.StorageClassName
+		config.VolumeMode = string(pipeline.Spec.SharedWorkspace.VolumeMode)
+	}
+
+	return RenderTrigger(config)
+}
+
+// RenderTrigger takes a TriggerConfig object and generates YAML byte array configuration representing the trigger configuration.
+func RenderTrigger(cfg TriggerConfig) ([]byte, error) {
+	return renderTemplate(TriggerTemplateContent, TriggerTemplateName, cfg)
+}
+
+const TriggerTemplateContent = `apiVersion: triggers.tekton.dev/v1alpha1
+kind: TriggerTemplate
+metadata:
+  name: {{ .PipelineName }}-triggertemplate
+  namespace: {{ .PipelineNamespace }}
+{{- if .OwnerReference }}
+  ownerReferences:
+  - apiVersion: "{{ .OwnerReference.APIVersion }}"
+    kind: "{{ .OwnerReference.Kind }}"
+    name: "{{ .OwnerReference.Name }}"
+    uid: "{{ .OwnerReference.UID }}"
+{{- end }}
+spec:
+  params:
+  - name: gitrevision
+    description: The git revision
+  - name: gitrepositoryurl
+    description: The git repository url
+  - name: namespace
+    description: The namespace to create the resources
+  resourceTemplates:
+  - apiVersion: tekton.dev/v1beta1
+    kind: PipelineRun
+    metadata:
+      generateName: {{ .PipelineName }}-run-
+      namespace: $(tt.params.namespace)
+    spec:
+      serviceAccountName: {{ .ServiceAccountName }}
+      pipelineRef:
+        name: {{ .PipelineName }}
+      params:
+      - name: revision
+        value: $(tt.params.gitrevision)
+      - name: repo-url
+        value: $(tt.params.gitrepositoryurl)
+      workspaces:
+      - name: kurator-pipeline-shared-data # there only one pvc workspace in each pipeline, and the name is kurator-pipeline-shared-data
+        volumeClaimTemplate:
+          spec:
+            accessModes:
+              - {{ default "ReadWriteOnce" .AccessMode }}
+            resources:
+              requests:
+                storage: {{ default "1Gi" .StorageRequest }}
+{{- if .VolumeMode }}
+            volumeMode: {{ .VolumeMode }}
+{{- end }}
+{{- if .StorageClassName }}
+            storageClassName: {{ .StorageClassName }}
+{{- end }}
+      - name: git-credentials
+        secret:
+          secretName: git-credentials
+      - name: docker-credentials
+        secret:
+          secretName: docker-credentials  # auth for task
+---
+apiVersion: triggers.tekton.dev/v1alpha1
+kind: TriggerBinding
+metadata:
+  name: {{ .PipelineName }}-triggerbinding
+  namespace: {{ .PipelineNamespace}}
+{{- if .OwnerReference }}
+  ownerReferences:
+  - apiVersion: "{{ .OwnerReference.APIVersion }}"
+    kind: "{{ .OwnerReference.Kind }}"
+    name: "{{ .OwnerReference.Name }}"
+    uid: "{{ .OwnerReference.UID }}"
+{{- end }}
+spec:
+  params:
+  - name: gitrevision
+    value: $(body.head_commit.id)
+  - name: namespace
+    value: {{ .PipelineNamespace}}
+  - name: gitrepositoryurl
+    value: "https://github.com/$(body.repository.full_name)"
+---
+apiVersion: triggers.tekton.dev/v1alpha1
+kind: EventListener
+metadata:
+  name: {{ .PipelineName }}-listener
+  namespace: {{ .PipelineNamespace}}
+{{- if .OwnerReference }}
+  ownerReferences:
+  - apiVersion: "{{ .OwnerReference.APIVersion }}"
+    kind: "{{ .OwnerReference.Kind }}"
+    name: "{{ .OwnerReference.Name }}"
+    uid: "{{ .OwnerReference.UID }}"
+{{- end }}
+spec:
+  serviceAccountName: {{ .ServiceAccountName }}
+  triggers:
+  - bindings:
+    - ref: {{ .PipelineName }}-triggerbinding
+    template:
+      ref: {{ .PipelineName }}-triggertemplate
+`


Test patch:
diff --git a/pkg/fleet-manager/pipeline/render/testdata/trigger/default.yaml b/pkg/fleet-manager/pipeline/render/testdata/trigger/default.yaml
new file mode 100644
index 0000000..6fd299c
--- /dev/null
+++ b/pkg/fleet-manager/pipeline/render/testdata/trigger/default.yaml
@@ -0,0 +1,70 @@
+apiVersion: triggers.tekton.dev/v1alpha1
+kind: TriggerTemplate
+metadata:
+  name: test-pipeline-triggertemplate
+  namespace: kurator-pipeline
+spec:
+  params:
+  - name: gitrevision
+    description: The git revision
+  - name: gitrepositoryurl
+    description: The git repository url
+  - name: namespace
+    description: The namespace to create the resources
+  resourceTemplates:
+  - apiVersion: tekton.dev/v1beta1
+    kind: PipelineRun
+    metadata:
+      generateName: test-pipeline-run-
+      namespace: $(tt.params.namespace)
+    spec:
+      serviceAccountName: test-pipeline
+      pipelineRef:
+        name: test-pipeline
+      params:
+      - name: revision
+        value: $(tt.params.gitrevision)
+      - name: repo-url
+        value: $(tt.params.gitrepositoryurl)
+      workspaces:
+      - name: kurator-pipeline-shared-data # there only one pvc workspace in each pipeline, and the name is kurator-pipeline-shared-data
+        volumeClaimTemplate:
+          spec:
+            accessModes:
+              - ReadWriteOnce
+            resources:
+              requests:
+                storage: 1Gi
+      - name: git-credentials
+        secret:
+          secretName: git-credentials
+      - name: docker-credentials
+        secret:
+          secretName: docker-credentials  # auth for task
+---
+apiVersion: triggers.tekton.dev/v1alpha1
+kind: TriggerBinding
+metadata:
+  name: test-pipeline-triggerbinding
+  namespace: kurator-pipeline
+spec:
+  params:
+  - name: gitrevision
+    value: $(body.head_commit.id)
+  - name: namespace
+    value: kurator-pipeline
+  - name: gitrepositoryurl
+    value: "https://github.com/$(body.repository.full_name)"
+---
+apiVersion: triggers.tekton.dev/v1alpha1
+kind: EventListener
+metadata:
+  name: test-pipeline-listener
+  namespace: kurator-pipeline
+spec:
+  serviceAccountName: test-pipeline
+  triggers:
+  - bindings:
+    - ref: test-pipeline-triggerbinding
+    template:
+      ref: test-pipeline-triggertemplate
diff --git a/pkg/fleet-manager/pipeline/render/testdata/trigger/with-volume-claim.yaml b/pkg/fleet-manager/pipeline/render/testdata/trigger/with-volume-claim.yaml
new file mode 100644
index 0000000..e18ab7d
--- /dev/null
+++ b/pkg/fleet-manager/pipeline/render/testdata/trigger/with-volume-claim.yaml
@@ -0,0 +1,72 @@
+apiVersion: triggers.tekton.dev/v1alpha1
+kind: TriggerTemplate
+metadata:
+  name: test-pipeline-triggertemplate
+  namespace: kurator-pipeline
+spec:
+  params:
+  - name: gitrevision
+    description: The git revision
+  - name: gitrepositoryurl
+    description: The git repository url
+  - name: namespace
+    description: The namespace to create the resources
+  resourceTemplates:
+  - apiVersion: tekton.dev/v1beta1
+    kind: PipelineRun
+    metadata:
+      generateName: test-pipeline-run-
+      namespace: $(tt.params.namespace)
+    spec:
+      serviceAccountName: test-pipeline
+      pipelineRef:
+        name: test-pipeline
+      params:
+      - name: revision
+        value: $(tt.params.gitrevision)
+      - name: repo-url
+        value: $(tt.params.gitrepositoryurl)
+      workspaces:
+      - name: kurator-pipeline-shared-data # there only one pvc workspace in each pipeline, and the name is kurator-pipeline-shared-data
+        volumeClaimTemplate:
+          spec:
+            accessModes:
+              - ReadWriteOnce
+            resources:
+              requests:
+                storage: 500Mi
+            volumeMode: Filesystem
+            storageClassName: manual
+      - name: git-credentials
+        secret:
+          secretName: git-credentials
+      - name: docker-credentials
+        secret:
+          secretName: docker-credentials  # auth for task
+---
+apiVersion: triggers.tekton.dev/v1alpha1
+kind: TriggerBinding
+metadata:
+  name: test-pipeline-triggerbinding
+  namespace: kurator-pipeline
+spec:
+  params:
+  - name: gitrevision
+    value: $(body.head_commit.id)
+  - name: namespace
+    value: kurator-pipeline
+  - name: gitrepositoryurl
+    value: "https://github.com/$(body.repository.full_name)"
+---
+apiVersion: triggers.tekton.dev/v1alpha1
+kind: EventListener
+metadata:
+  name: test-pipeline-listener
+  namespace: kurator-pipeline
+spec:
+  serviceAccountName: test-pipeline
+  triggers:
+  - bindings:
+    - ref: test-pipeline-triggerbinding
+    template:
+      ref: test-pipeline-triggertemplate
diff --git a/pkg/fleet-manager/pipeline/render/trigger_test.go b/pkg/fleet-manager/pipeline/render/trigger_test.go
new file mode 100644
index 0000000..ac51844
--- /dev/null
+++ b/pkg/fleet-manager/pipeline/render/trigger_test.go
@@ -0,0 +1,73 @@
+/*
+Copyright Kurator 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 render
+
+import (
+	"os"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRenderTrigger(t *testing.T) {
+	expectedRBACFilePath := "testdata/trigger/"
+	cases := []struct {
+		name         string
+		cfg          TriggerConfig
+		expectError  bool
+		expectedFile string
+	}{
+		{
+			name: "default trigger configuration",
+			cfg: TriggerConfig{
+				PipelineName:      "test-pipeline",
+				PipelineNamespace: "kurator-pipeline",
+			},
+			expectError:  false,
+			expectedFile: "default.yaml",
+		},
+		{
+			name: "trigger configuration with custom volume claim",
+			cfg: TriggerConfig{
+				PipelineName:      "test-pipeline",
+				PipelineNamespace: "kurator-pipeline",
+				AccessMode:        "ReadWriteOnce",
+				StorageRequest:    "500Mi",
+				StorageClassName:  "manual",
+				VolumeMode:        "Filesystem",
+			},
+			expectError:  false,
+			expectedFile: "with-volume-claim.yaml",
+		},
+	}
+
+	for _, tc := range cases {
+		t.Run(tc.name, func(t *testing.T) {
+			result, err := RenderTrigger(tc.cfg)
+
+			if tc.expectError {
+				assert.Error(t, err)
+			} else {
+				assert.NoError(t, err)
+
+				expected, err := os.ReadFile(expectedRBACFilePath + tc.expectedFile)
+				assert.NoError(t, err)
+				assert.Equal(t, string(expected), string(result))
+			}
+		})
+	}
+}