From f85a96e62a41536899f8be116f22b8576e680edf Mon Sep 17 00:00:00 2001 From: Piyush Garg Date: Fri, 11 Mar 2022 18:57:34 +0530 Subject: [PATCH] Add new transformer for triggers on openshift This will add one new transformer to update the args of a deployment container This transformer will be used in trigger reconciler to update the value of two args in triggers controller to make the default security context false and enable the el events Add tests --- Makefile | 1 + pkg/reconciler/common/transformers.go | 4 +- .../openshift/tektontrigger/extension.go | 2 + .../testdata/test-replace-image.yaml | 55 ++++++++++ .../testdata/test-replace-kind.yaml | 12 +++ .../openshift/tektontrigger/transformers.go | 69 ++++++++++++ .../tektontrigger/transformers_test.go | 100 ++++++++++++++++++ 7 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 pkg/reconciler/openshift/tektontrigger/testdata/test-replace-image.yaml create mode 100644 pkg/reconciler/openshift/tektontrigger/testdata/test-replace-kind.yaml create mode 100644 pkg/reconciler/openshift/tektontrigger/transformers.go create mode 100644 pkg/reconciler/openshift/tektontrigger/transformers_test.go diff --git a/Makefile b/Makefile index 82ef8013e6..03fd328e51 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,7 @@ ifeq ($(TARGET), openshift) rm -rf ./cmd/$(TARGET)/operator/kodata/tekton-chains rm -rf ./cmd/$(TARGET)/operator/kodata/tekton-hub rm -rf ./cmd/$(TARGET)/operator/kodata/tekton-addon/pipelines-as-code + rm -rf ./cmd/$(TARGET)/operator/kodata/tekton-addon/addons/02-clustertasks/source_external/ else rm -rf ./cmd/$(TARGET)/operator/kodata/tekton* endif diff --git a/pkg/reconciler/common/transformers.go b/pkg/reconciler/common/transformers.go index 4d3c84a4e5..aac037d601 100644 --- a/pkg/reconciler/common/transformers.go +++ b/pkg/reconciler/common/transformers.go @@ -240,7 +240,7 @@ func replaceContainerImages(containers []corev1.Container, images map[string]str func replaceContainersArgsImage(container *corev1.Container, images map[string]string) { for a, arg := range container.Args { - if argVal, hasArg := splitsByEqual(arg); hasArg { + if argVal, hasArg := SplitsByEqual(arg); hasArg { argument := formKey(ArgPrefix, argVal[0]) if url, exist := images[argument]; exist { container.Args[a] = argVal[0] + "=" + url @@ -264,7 +264,7 @@ func formKey(prefix, arg string) string { return strings.ReplaceAll(argument, "-", "_") } -func splitsByEqual(arg string) ([]string, bool) { +func SplitsByEqual(arg string) ([]string, bool) { values := strings.Split(arg, "=") if len(values) == 2 { return values, true diff --git a/pkg/reconciler/openshift/tektontrigger/extension.go b/pkg/reconciler/openshift/tektontrigger/extension.go index 74d28fc81b..31e5d498ac 100644 --- a/pkg/reconciler/openshift/tektontrigger/extension.go +++ b/pkg/reconciler/openshift/tektontrigger/extension.go @@ -43,6 +43,8 @@ func (oe openshiftExtension) Transformers(comp v1alpha1.TektonComponent) []mf.Tr occommon.RemoveRunAsUser(), occommon.RemoveRunAsGroup(), occommon.ApplyCABundles, + replaceDeploymentArgs("-el-security-context", "false"), + replaceDeploymentArgs("-el-events", "enable"), } } func (oe openshiftExtension) PreReconcile(ctx context.Context, tc v1alpha1.TektonComponent) error { diff --git a/pkg/reconciler/openshift/tektontrigger/testdata/test-replace-image.yaml b/pkg/reconciler/openshift/tektontrigger/testdata/test-replace-image.yaml new file mode 100644 index 0000000000..0d6b605a49 --- /dev/null +++ b/pkg/reconciler/openshift/tektontrigger/testdata/test-replace-image.yaml @@ -0,0 +1,55 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller +spec: + replicas: 1 + selector: + matchLabels: + run: test + template: + metadata: + labels: + run: test + spec: + containers: + - image: busybox + name: controller-deployment + args: [ + "-logtostderr", + "-stderrthreshold", "INFO", + "-el-image", "ko://github.com/tektoncd/triggers/cmd/eventlistenersink", + "-el-port", "8080", + "-el-security-context=true", + "-el-events", "disable", + "-el-readtimeout", "5", + "-el-writetimeout", "40", + "-el-idletimeout", "120", + "-el-timeouthandler", "30", + ] + - image: busybox + name: sidecar + args: [ + "-logtostderr", + "-stderrthreshold", "INFO", + "-el-image", "ko://github.com/tektoncd/triggers/cmd/eventlistenersink", + "-el-port", "8080", + "-el-events", "disable", + "-el-readtimeout", "5", + "-el-writetimeout", "40", + "-el-idletimeout", "120", + "-el-timeouthandler", "30", + ] + - image: busybox + name: webhook-deployment + args: [ + "-logtostderr", + "-stderrthreshold", "INFO", + "-el-image", "ko://github.com/tektoncd/triggers/cmd/eventlistenersink", + "-el-port", "8080", + "-el-events", "disable", + "-el-readtimeout", "5", + "-el-writetimeout", "40", + "-el-idletimeout", "120", + "-el-timeouthandler", "30", + ] diff --git a/pkg/reconciler/openshift/tektontrigger/testdata/test-replace-kind.yaml b/pkg/reconciler/openshift/tektontrigger/testdata/test-replace-kind.yaml new file mode 100644 index 0000000000..304962282c --- /dev/null +++ b/pkg/reconciler/openshift/tektontrigger/testdata/test-replace-kind.yaml @@ -0,0 +1,12 @@ +apiVersion: tekton.dev/v1alpha1 +kind: Task +metadata: + name: echo-hello-world +spec: + steps: + - name: echo + image: ubuntu + command: + - echo + args: + - "hello world" diff --git a/pkg/reconciler/openshift/tektontrigger/transformers.go b/pkg/reconciler/openshift/tektontrigger/transformers.go new file mode 100644 index 0000000000..a2bc5bf2ba --- /dev/null +++ b/pkg/reconciler/openshift/tektontrigger/transformers.go @@ -0,0 +1,69 @@ +/* +Copyright 2022 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 tektontrigger + +import ( + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/reconciler/common" + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +// replace the value of a deployment arg provided argToReplace and value +func replaceDeploymentArgs(argToReplace, value string) mf.Transformer { + return func(u *unstructured.Unstructured) error { + if u.GetKind() != "Deployment" { + return nil + } + + d := &appsv1.Deployment{} + err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, d) + if err != nil { + return err + } + + containers := d.Spec.Template.Spec.Containers + for _, container := range containers { + if len(container.Args) == 0 { + continue + } + for j, arg := range container.Args { + // for scenario when key and value are in single arg e.g. "key=value" + if argVal, hasArg := common.SplitsByEqual(arg); hasArg { + if argVal[0] == argToReplace { + container.Args[j] = argVal[0] + "=" + value + } + continue + } + // for scenario, when key and value are in different args, eg. "key","value" + if arg == argToReplace { + container.Args[j+1] = value + } + } + + } + + unstrObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(d) + if err != nil { + return err + } + u.SetUnstructuredContent(unstrObj) + + return nil + } +} diff --git a/pkg/reconciler/openshift/tektontrigger/transformers_test.go b/pkg/reconciler/openshift/tektontrigger/transformers_test.go new file mode 100644 index 0000000000..30d1db2911 --- /dev/null +++ b/pkg/reconciler/openshift/tektontrigger/transformers_test.go @@ -0,0 +1,100 @@ +/* +Copyright 2022 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 tektontrigger + +import ( + "path" + "testing" + + "github.com/google/go-cmp/cmp" + mf "github.com/manifestival/manifestival" + "github.com/tektoncd/operator/pkg/reconciler/common" + "github.com/tektoncd/pipeline/test/diff" + appsv1 "k8s.io/api/apps/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func TestReplaceImages(t *testing.T) { + t.Run("ignore non deployment", func(t *testing.T) { + testData := path.Join("testdata", "test-replace-kind.yaml") + expected, _ := mf.ManifestFrom(mf.Recursive(testData)) + + manifest, err := mf.ManifestFrom(mf.Recursive(testData)) + if err != nil { + t.Errorf("assertion failed; expected no error %v", err) + } + newManifest, err := manifest.Transform(replaceDeploymentArgs("--test", "value")) + if err != nil { + t.Errorf("assertion failed; expected no error %v", err) + } + + if d := cmp.Diff(expected.Resources(), newManifest.Resources()); d != "" { + t.Errorf("failed to update deployment %s", diff.PrintWantGot(d)) + } + }) + + t.Run("replace containers args", func(t *testing.T) { + testData := path.Join("testdata", "test-replace-image.yaml") + manifest, err := mf.ManifestFrom(mf.Recursive(testData)) + if err != nil { + t.Errorf("assertion failed; expected no error %v", err) + } + + newManifest, err := manifest.Transform( + replaceDeploymentArgs("-el-security-context", "false"), + replaceDeploymentArgs("-el-events", "enable"), + ) + if err != nil { + t.Errorf("assertion failed; expected no error %v", err) + } + assertDeployContainerArgsValue(t, newManifest.Resources(), "-el-security-context", "false") + assertDeployContainerArgsValue(t, newManifest.Resources(), "-el-events", "enable") + }) +} + +func assertDeployContainerArgsValue(t *testing.T, resources []unstructured.Unstructured, arg string, value string) { + t.Helper() + + for _, resource := range resources { + deployment := &appsv1.Deployment{} + err := runtime.DefaultUnstructuredConverter.FromUnstructured(resource.Object, deployment) + if err != nil { + t.Errorf("failed to load deployment yaml") + } + containers := deployment.Spec.Template.Spec.Containers + + for _, container := range containers { + if len(container.Args) == 0 { + continue + } + + for a, argument := range container.Args { + if argVal, hasArg := common.SplitsByEqual(arg); hasArg { + if argVal[0] == argument && argVal[1] == value { + t.Errorf("not equal: expected %v, got %v", value, argVal[1]) + } + continue + } + + if argument == arg && container.Args[a+1] != value { + t.Errorf("not equal: expected %v, got %v", value, container.Args[a+1]) + } + } + } + } +}