diff --git a/cel/.gitignore b/cel/.gitignore index 202521131..e99de55b7 100644 --- a/cel/.gitignore +++ b/cel/.gitignore @@ -53,6 +53,3 @@ cmd/*/kodata/source.tar.gz test/pullrequest/pullrequest-init /.bin/ /bin/ - -# allow vendor -!vendor/ diff --git a/cel/.ko.yaml b/cel/.ko.yaml new file mode 100644 index 000000000..6ccd2d5eb --- /dev/null +++ b/cel/.ko.yaml @@ -0,0 +1 @@ +defaultBaseImage: gcr.io/distroless/static:nonroot diff --git a/cel/README.md b/cel/README.md index 590b9cce7..d045f7d18 100644 --- a/cel/README.md +++ b/cel/README.md @@ -5,25 +5,33 @@ This is an experimental project that provides support for Common Expression Language (CEL) in Tekton Pipelines. The functionality is provided by a controller that implements the Custom Task interface. Its use cases include -evaluating complex expressions to be used in [`WhenExpressions`](https://github.com/tektoncd/pipeline/blob/master/docs/pipelines.md#guard-task-execution-using-whenexpressions) -in subsequent `Tasks` to guard their execution. +evaluating complex expressions to be used in [`when` expressions][when-expressions] in subsequent `Tasks` to guard +their execution. - [Install](#install) - [Usage](#usage) + - [Configuring a `Cel`](#configuring-a-cel) + - [Configuring a `Cel` in a `Pipeline`](#configuring-a-cel-in-a-pipeline) + - [Configuring a `Cel` in a `PipelineRun`](#configuring-a-cel-in-a-pipelinerun) + - [Specifying CEL expressions](#specifying-cel-expressions) + - [Specifying CEL environment variables](#specifying-cel-environment-variables) + - [Monitoring execution status](#monitoring-execution-status) + - [Using the evaluation results](#using-the-evaluation-results) - [Uninstall](#uninstall) +- [Contributions](#contributions) ## Install -Install and configure [`ko`](https://github.com/google/ko). +Install and configure [`ko`][ko]. ``` ko apply -f config/ ``` -This will build and install the `CEL Controller` on your cluster, in the namespace `tekton-cel-run`. +This will build and install the `CEL Controller` on your cluster, in the namespace `tekton-pipelines`. ```commandline -$ k get pods -n tekton-cel-run +$ k get pods -n tekton-pipelines NAME READY STATUS RESTARTS AGE cel-controller-654bdc4cc8-7bvvn 1/1 Running 0 3m4s @@ -37,23 +45,40 @@ kubectl apply --filename https://storage.cloud.google.com/tekton-releases-nightl ## Usage -To evaluate a CEL expressions using `Custom Tasks`, we need to define a [`Run`](https://github.com/tektoncd/pipeline/blob/master/docs/runs.md) -type with `apiVersion: cel.tekton.dev/v1alpha1` and `kind: CEL`. The `Run` takes the CEL expressions to be evaluated -as `Parameters`. If executed successfully, the `Run` will produce the evaluation results as `Results` with names corresponding -with the `Parameters` names. See the [examples](examples) folder for `CEL` `Custom Tasks` to run or use as samples. +To evaluate a CEL expressions using `Custom Tasks`, we need to define a [`Run`][run] type with +`apiVersion: custom.tekton.dev/v1alpha1` and `kind: Cel`. -### Configuring a `CEL` `Custom Task` +The `Run` takes the CEL expressions to be evaluated through `expressions` field. The `Run` optionally takes CEL +environment variables through the `variables` field. -The `CEL` `Custom Task` is defined in a `Run`, which supports the following fields: +If executed successfully, the `Run` will produce the evaluation results as `Results` with names corresponding +with the `Expressions`'s names. See the [examples](examples) folder for `Cels` to run or use as samples. -- [`apiVersion`][kubernetes-overview] - Specifies the API version, `tekton.dev/v1alpha1` -- [`kind`][kubernetes-overview] - Identifies this resource object as a `Run` object -- [`metadata`][kubernetes-overview] - Specifies the metadata that uniquely identifies the `Run`, such as a `name` -- [`spec`][kubernetes-overview] - Specifies the configuration for the `Run` -- [`ref`][kubernetes-overview] - Specifies the `CEL` `Custom Task` - - [`apiVersion`][kubernetes-overview] - Specifies the API version, `cel.tekton.dev/v1alpha1` - - [`kind`][kubernetes-overview] - Identifies this resource object as a `CEL` object -- [`params`](#specifying-cel-expressions) - Specifies the CEL expressions to be evaluated as parameters +### Configuring a `Cel` + +A `Cel` definition supports the following fields: + +- Required: + - [`apiVersion`][kubernetes-overview] - Specifies the API version, `custom.tekton.dev/v1alpha1`. + - [`kind`][kubernetes-overview] - Identifies this resource object as a `Cel` object. + - [`metadata`][kubernetes-overview] - Specifies the metadata that uniquely identifies the `Cel`, such as a `name`. + - [`spec`][kubernetes-overview] - Specifies the configuration for the `Cel`. + - [`expressions`](#specifying-cel-expressions) - Specifies the CEL expressions to be evaluated +- Optional: + - [`variables`](#specifying-cel-environment-variables) - Specifies the CEL environment variables + +The example below shows a basic `Cel`: + +```yaml +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: get-type +spec: + expressions: + - name: expression + value: "type(1)" +``` The example below shows a basic `Run`: @@ -61,207 +86,310 @@ The example below shows a basic `Run`: apiVersion: tekton.dev/v1alpha1 kind: Run metadata: - generateName: celrun- + generateName: get-type- spec: ref: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: - - name: expression - value: "type(1)" + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: get-type ``` -### Configuring a `CEL` `Custom Task` in a `Pipeline` +### Configuring a `Cel` in a `Pipeline` -The `CEL` `Custom Task` can be specified within a `Pipeline`, as such: +A `Cel` can be specified within a `Pipeline`, as such: ```yaml +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: get-red +spec: + expressions: + - name: expression + value: "{'blue': '0x000080', 'red': '0xFF0000'}['red']" +--- apiVersion: tekton.dev/v1beta1 kind: Pipeline metadata: - generateName: pipeline- + generateName: pipeline-get-red- spec: tasks: - name: get-red taskRef: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: - - name: red - value: "{'blue': '0x000080', 'red': '0xFF0000'}['red']" + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: get-red ``` -### Configuring a `CEL` `Custom Task` in a `PipelineRun` +### Configuring a `Cel` in a `PipelineRun` -The `CEL` `Custom Task` can be specified within a `PipelineRun`, as such: +A `Cel` can be specified within a `PipelineRun`, as such: ```yaml +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: get-blue +spec: + expressions: + - name: expression + value: "{'blue': '0x000080', 'red': '0xFF0000'}['blue']" +--- apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: - generateName: pipelinerun- + generateName: pipelinerun-get-blue- spec: pipelineSpec: tasks: - name: get-blue taskRef: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: - - name: blue - value: "{'blue': '0x000080', 'red': '0xFF0000'}['blue']" + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: get-blue ``` ### Specifying CEL expressions -The CEL expressions to be evaluated by the `Run` are specified using parameters. The parameters can be specified -in the `Run` directly or be passed through from a `Pipeline` or `PipelineRun`, as such: +The CEL expressions to be evaluated by the `Cel` are specified in `expressions` field. + +They are specified as `Parameters`, that is, they are made up of `name` and `value` pairs which are Strings. ```yaml +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: get-type +spec: + expressions: + - name: type + value: "type(1)" +--- apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: - generateName: pipelinerun- + generateName: pipeline-run-get-type- spec: + serviceAccountName: 'default' pipelineSpec: - params: - - name: is-red-expr - type: string tasks: - - name: is-red + - name: get-type taskRef: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: - - name: is-red-expr - value: "$(params.is-red-expr)" - params: - - name: is-red-expr - value: "{'blue': '0x000080', 'red': '0xFF0000'}['red'] == '0xFF0000'" + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: get-type + - name: echo-get-type + when: + - input: "$(tasks.get-type.results.type)" + operator: in + values: ["int"] + taskSpec: + steps: + - name: echo + image: ubuntu + script: echo ISINT! ``` -For more information about specifying `Parameters`, read [specifying parameters](https://github.com/tektoncd/pipeline/blob/master/docs/pipelines.md#specifying-parameters). +For more information about `Parameters`, read [specifying `Parameters`][specifying-parameters]. + +### Specifying CEL environment variables + +The CEL variables to be declared in the environment before evaluation by `Cel` are specified in `variables` field. + +They are specified as `Parameters`, that is, they are made up of `name` and `value` pairs which are Strings. + +```yaml +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: get-sev +spec: + expressions: + - name: severe + value: "severity in ['sev1', 'sev2']" +--- +apiVersion: tekton.dev/v1beta1 +kind: PipelineRun +metadata: + generateName: pipeline-run-get-sev- +spec: + serviceAccountName: 'default' + pipelineSpec: + tasks: + - name: get-sev + taskRef: + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: get-sev + - name: is-severe + when: + - input: "$(tasks.get-sev.results.severe)" + operator: in + values: ["true"] + taskSpec: + steps: + - name: echo + image: ubuntu + script: echo SEVERE! +``` + +For more information about `Parameters`, read [specifying `Parameters`][specifying-parameters]. For more information +about CEL environment, read [environment setup][cel-docs]. ### Monitoring execution status As the `Run` executes, its `status` field accumulates information about the execution status of the `Run` in general. If the evaluation is successful, it will also contain the `Results` of the evaluation under `status.results` with the -corresponding names of the CEL expressions as provided in the `Parameters`. +corresponding names of the CEL expressions as provided in the `expressions`. ```yaml -Name: celrun-is-red-8lbwv +Name: colors-xgtb9 Namespace: default +Labels: custom.tekton.dev/cel=colors +Annotations: API Version: tekton.dev/v1alpha1 Kind: Run Metadata: - Creation Timestamp: 2021-01-20T17:51:52Z - Generate Name: celrun-is-red- + Creation Timestamp: 2021-08-26T05:03:24Z + Generate Name: colors- # […] Spec: - Params: - Name: red - Value: {'blue': '0x000080', 'red': '0xFF0000'}['red'] - Name: is-red - Value: {'blue': '0x000080', 'red': '0xFF0000'}['red'] == '0xFF0000' Ref: - API Version: cel.tekton.dev/v1alpha1 - Kind: CEL + API Version: custom.tekton.dev/v1alpha1 + Kind: Cel + Name: colors Service Account Name: default Status: - Completion Time: 2021-01-20T17:51:52Z + Completion Time: 2021-08-26T05:03:24Z Conditions: - Last Transition Time: 2021-01-20T17:51:52Z + Last Transition Time: 2021-08-26T05:03:24Z Message: CEL expressions were evaluated successfully Reason: EvaluationSuccess Status: True Type: Succeeded - Extra Fields: - Observed Generation: 1 + Extra Fields: + Cel Results: + Name: red + Value: 0xFF0000 + Name: blue + Value: 0x000080 + Name: is-red + Value: true + Name: is-blue + Value: false + Cel Spec: + Expressions: + Name: red + Value: {'blue': '0x000080', 'red': '0xFF0000'}['red'] + Name: blue + Value: {'blue': '0x000080', 'red': '0xFF0000'}['blue'] + Name: is-red + Value: {'blue': '0x000080', 'red': '0xFF0000'}['red'] == '0xFF0000' + Name: is-blue + Value: {'blue': '0x000080', 'red': '0xFF0000'}['blue'] == '0xFF0000' + Observed Generation: 1 Results: Name: red Value: 0xFF0000 + Name: blue + Value: 0x000080 Name: is-red Value: true - Start Time: 2021-01-20T17:51:52Z + Name: is-blue + Value: false + Start Time: 2021-08-26T05:03:24Z Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal RunReconciled 13s cel-controller Run reconciled: "default/celrun-is-red-8lbwv" + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Started 18s (x2 over 18s) Cel + Normal Succeeded 18s (x2 over 18s) Cel CEL expressions were evaluated successfully ``` -If no CEL expressions are provided, any CEL expression is invalid or there's any other error, the `CEL` `Custom Task` -will fail and the details will be included in `status.conditions` as such: +If no CEL expressions are provided, any CEL expression is invalid or there's any other error, the `Run` will fail and +the details will be included in `status.conditions` as such: ```yaml -Name: celrun-is-red-4ttr8 +Name: colors-f8n9t Namespace: default +Labels: custom.tekton.dev/cel=colors +Annotations: API Version: tekton.dev/v1alpha1 Kind: Run Metadata: - Creation Timestamp: 2021-01-20T17:58:53Z - Generate Name: celrun-is-red- + Creation Timestamp: 2021-08-26T05:06:34Z + Generate Name: colors- # […] Spec: Ref: - API Version: cel.tekton.dev/v1alpha1 - Kind: CEL + API Version: custom.tekton.dev/v1alpha1 + Kind: Cel + Name: colors Service Account Name: default Status: - Completion Time: 2021-01-20T17:58:53Z + Completion Time: 2021-08-26T05:06:34Z Conditions: - Last Transition Time: 2021-01-20T17:58:53Z - Message: Run can't be run because it has an invalid spec - missing field(s) params + Last Transition Time: 2021-08-26T05:06:34Z + Message: Run can't be run because it has an invalid spec - missing field(s): expressions Reason: RunValidationFailed Status: False Type: Succeeded - Extra Fields: - Observed Generation: 1 - Start Time: 2021-01-20T17:58:53Z -Events: + Extra Fields: + Cel Spec: + Expressions: + Observed Generation: 1 + Start Time: 2021-08-26T05:06:34Z +Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Started 14s Cel + Warning Failed 14s Cel Run can't be run because it has an invalid spec - missing field(s): expressions ``` -For more information about monitoring `Run` in general, read [monitoring execution status](https://github.com/tektoncd/pipeline/blob/master/docs/runs.md#monitoring-execution-status). +For general information about monitoring a `Run`, read [monitoring execution status][monitoring-run-execution-status]. ### Using the evaluation results A successful `Run` contains the `Results` of evaluating the CEL expressions under `status.results`, with the name of -each evaluation `Result` matching the name of the corresponding CEL expression as provided in the `Parameters`. +each evaluation `Result` matching the name of the corresponding CEL expression as provided in `expressions`. + Users can reference the `Results` in subsequent `Tasks` using variable substitution, as such: ```yaml +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: get-type +spec: + expressions: + - name: type + value: "type(1)" +--- apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: - generateName: pipelinerun- + generateName: pipeline-run-get-type- spec: + serviceAccountName: 'default' pipelineSpec: - params: - - name: is-red-expr - type: string tasks: - - name: is-red + - name: get-type taskRef: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: - - name: is-red-expr - value: "$(params.is-red-expr)" - - name: echo-is-red + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: get-type + - name: echo-get-type when: - - input: "$(tasks.is-red.results.is-red-expr)" + - input: "$(tasks.get-type.results.type)" operator: in - values: ["true"] + values: ["int"] taskSpec: steps: - name: echo image: ubuntu - script: echo RED! - params: - - name: is-red-expr - value: "{'blue': '0x000080', 'red': '0xFF0000'}['red'] == '0xFF0000'" + script: echo ISINT! ``` -For more information about using `Results`, read [using results](https://github.com/tektoncd/pipeline/blob/master/docs/pipelines.md#using-results). +For more information about using `Results`, read [using results][using-results]. ## Uninstall @@ -276,5 +404,11 @@ This will delete the `CEL Controller` and related resources on your cluster. Read an overview of our processes in [Tekton Community](https://github.com/tektoncd/community). -[kubernetes-overview]: -https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields +[ko]: https://github.com/google/ko +[run]: https://github.com/tektoncd/pipeline/blob/main/docs/runs.md +[kubernetes-overview]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields +[when-expressions]: https://github.com/tektoncd/pipeline/blob/main/docs/pipelines.md#guard-task-execution-using-when-expressions +[specifying-parameters]: https://github.com/tektoncd/pipeline/blob/master/docs/pipelines.md#specifying-parameters +[monitoring-run-execution-status]: https://github.com/tektoncd/pipeline/blob/main/docs/runs.md#monitoring-execution-status +[using-results]: https://github.com/tektoncd/pipeline/blob/main/docs/pipelines.md#using-results +[cel-docs]: https://github.com/google/cel-go#environment-setup \ No newline at end of file diff --git a/cel/cmd/controller/main.go b/cel/cmd/controller/main.go index 55389b588..e99809cac 100644 --- a/cel/cmd/controller/main.go +++ b/cel/cmd/controller/main.go @@ -17,10 +17,26 @@ limitations under the License. package main import ( - "github.com/tektoncd/experimental/cel/pkg/reconciler/cel" + "flag" + "github.com/tektoncd/experimental/cel/pkg/reconciler/celrun" + corev1 "k8s.io/api/core/v1" + "knative.dev/pkg/injection" "knative.dev/pkg/injection/sharedmain" + "knative.dev/pkg/signals" +) + +const ( + // ControllerLogKey is the name of the logger for the controller cmd + ControllerLogKey = "tekton-cel-controller" +) + +var ( + namespace = flag.String("namespace", corev1.NamespaceAll, "Namespace to restrict informer to. Optional, defaults to all namespaces.") ) func main() { - sharedmain.Main(cel.ControllerName, cel.NewController) + flag.Parse() + sharedmain.MainWithContext(injection.WithNamespaceScope(signals.NewContext(), *namespace), ControllerLogKey, + celrun.NewController(*namespace), + ) } diff --git a/cel/cmd/webhook/main.go b/cel/cmd/webhook/main.go new file mode 100644 index 000000000..fdd317da1 --- /dev/null +++ b/cel/cmd/webhook/main.go @@ -0,0 +1,129 @@ +/* +Copyright 2021 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 main + +import ( + "context" + "os" + + celv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + defaultconfig "github.com/tektoncd/pipeline/pkg/apis/config" + "github.com/tektoncd/pipeline/pkg/contexts" + "github.com/tektoncd/pipeline/pkg/system" + "k8s.io/apimachinery/pkg/runtime/schema" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" + "knative.dev/pkg/injection" + "knative.dev/pkg/injection/sharedmain" + "knative.dev/pkg/logging" + "knative.dev/pkg/signals" + "knative.dev/pkg/webhook" + "knative.dev/pkg/webhook/certificates" + "knative.dev/pkg/webhook/resourcesemantics" + "knative.dev/pkg/webhook/resourcesemantics/defaulting" + "knative.dev/pkg/webhook/resourcesemantics/validation" +) + +const ( + // WebhookLogKey is the name of the logger for the webhook cmd. + // This name is also used to form lease names for the leader election of the webhook's controllers. + WebhookLogKey = "tekton-cel-webhook" +) + +var types = map[schema.GroupVersionKind]resourcesemantics.GenericCRD{ + celv1alpha1.SchemeGroupVersion.WithKind("Cel"): &celv1alpha1.Cel{}, +} + +func newDefaultingAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl { + // Decorate contexts with the current state of the config. + store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store")) + store.WatchConfigs(cmw) + + return defaulting.NewAdmissionController(ctx, + + // Name of the resource webhook. + "webhook.cel.custom.tekton.dev", + + // The path on which to serve the webhook. + "/defaulting", + + // The resources to validate and default. + types, + + // A function that infuses the context passed to Validate/SetDefaults with custom metadata. + func(ctx context.Context) context.Context { + return contexts.WithUpgradeViaDefaulting(store.ToContext(ctx)) + }, + + // Whether to disallow unknown fields. + true, + ) +} + +func newValidationAdmissionController(ctx context.Context, cmw configmap.Watcher) *controller.Impl { + // Decorate contexts with the current state of the config. + store := defaultconfig.NewStore(logging.FromContext(ctx).Named("config-store")) + store.WatchConfigs(cmw) + return validation.NewAdmissionController(ctx, + + // Name of the resource webhook. + "validation.webhook.cel.custom.tekton.dev", + + // The path on which to serve the webhook. + "/resource-validation", + + // The resources to validate and default. + types, + + // A function that infuses the context passed to Validate/SetDefaults with custom metadata. + func(ctx context.Context) context.Context { + return contexts.WithUpgradeViaDefaulting(store.ToContext(ctx)) + }, + + // Whether to disallow unknown fields. + true, + ) +} + +func main() { + serviceName := os.Getenv("WEBHOOK_SERVICE_NAME") + if serviceName == "" { + serviceName = "tekton-cel-webhook" + } + + secretName := os.Getenv("WEBHOOK_SECRET_NAME") + if secretName == "" { + secretName = "tekton-cel-webhook-certs" // #nosec + } + + // Scope informers to the webhook's namespace instead of cluster-wide + ctx := injection.WithNamespaceScope(signals.NewContext(), system.GetNamespace()) + + // Set up a signal context with our webhook options + ctx = webhook.WithOptions(ctx, webhook.Options{ + ServiceName: serviceName, + Port: 8443, + SecretName: secretName, + }) + + sharedmain.WebhookMainWithConfig(ctx, WebhookLogKey, + sharedmain.ParseAndGetConfigOrDie(), + certificates.NewController, + newDefaultingAdmissionController, + newValidationAdmissionController, + ) +} diff --git a/cel/config/100-namespace.yaml b/cel/config/100-namespace.yaml deleted file mode 100644 index b6fd2b8d0..000000000 --- a/cel/config/100-namespace.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2021 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 -# -# https://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. - -apiVersion: v1 -kind: Namespace -metadata: - name: tekton-cel-run - labels: - app.kubernetes.io/instance: default - app.kubernetes.io/part-of: tekton-cel-run diff --git a/cel/config/200-serviceaccount.yaml b/cel/config/200-serviceaccount.yaml index dad3c3f61..28b75ed2f 100644 --- a/cel/config/200-serviceaccount.yaml +++ b/cel/config/200-serviceaccount.yaml @@ -15,9 +15,19 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: cel-controller - namespace: tekton-cel-run + name: tekton-cel-controller + namespace: tekton-pipelines labels: - app.kubernetes.io/component: cel-controller + app.kubernetes.io/component: controller app.kubernetes.io/instance: default - app.kubernetes.io/part-of: tekton-cel-run + app.kubernetes.io/part-of: tekton-cel +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: tekton-cel-webhook + namespace: tekton-pipelines + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel \ No newline at end of file diff --git a/cel/config/201-clusterrole.yaml b/cel/config/201-clusterrole.yaml index 407e7f7da..cd3b4275c 100644 --- a/cel/config/201-clusterrole.yaml +++ b/cel/config/201-clusterrole.yaml @@ -15,29 +15,86 @@ kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: - name: cel-controller-cluster-access + name: tekton-cel-controller-cluster-access labels: - app.kubernetes.io/component: cel-controller + app.kubernetes.io/component: controller app.kubernetes.io/instance: default - app.kubernetes.io/part-of: tekton-cel-run + app.kubernetes.io/part-of: tekton-cel rules: - # Controller needs cluster access to all Run CRs. + # Controller needs cluster access to all of the CRDs that it is responsible for managing. - apiGroups: ["tekton.dev"] resources: ["runs"] verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] - - apiGroups: ["tekton.dev"] - resources: ["runs/finalizers"] - verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] - apiGroups: ["tekton.dev"] resources: ["runs/status"] verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] - - # Controller needs cluster access to leases for leader election. - - apiGroups: ["coordination.k8s.io"] - resources: ["leases"] + - apiGroups: ["custom.tekton.dev"] + resources: ["cels"] verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] - - # Controller needs permission to emit events associated with Run CRs. +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + # This is the access that the controller needs on a per-namespace basis. + name: tekton-cel-controller-tenant-access + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +rules: - apiGroups: [""] resources: ["events"] verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: tekton-cel-webhook-cluster-access + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +rules: + # The webhook needs to be able to list and update customresourcedefinitions, + # mainly to update the webhook certificates. + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions", "customresourcedefinitions/status"] + verbs: ["get", "list", "update", "patch", "watch"] + - apiGroups: ["admissionregistration.k8s.io"] + # The webhook performs a reconciliation on these two resources and continuously + # updates configuration. + resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"] + # knative starts informers on these things, which is why we need get, list and watch. + verbs: ["list", "watch"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["mutatingwebhookconfigurations"] + # This mutating webhook is responsible for applying defaults to tekton objects + # as they are received. + resourceNames: ["webhook.cel.custom.tekton.dev"] + # When there are changes to the configs or secrets, knative updates the mutatingwebhook config + # with the updated certificates or the refreshed set of rules. + verbs: ["get", "update"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["validatingwebhookconfigurations"] + # validation.webhook.cel.custom.tekton.dev performs schema validation when you, for example, create cels. + resourceNames: ["validation.webhook.cel.custom.tekton.dev"] + # When there are changes to the configs or secrets, knative updates the validatingwebhook config + # with the updated certificates or the refreshed set of rules. + verbs: ["get", "update"] + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: ["tekton-pipelines"] + verbs: ["use"] +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: tekton-cel-leader-election + labels: + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +rules: + # We uses leases for leaderelection + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] diff --git a/cel/config/201-role.yaml b/cel/config/201-role.yaml index 4f6ee32ee..076ff473b 100644 --- a/cel/config/201-role.yaml +++ b/cel/config/201-role.yaml @@ -15,18 +15,54 @@ kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: - name: cel-controller - namespace: tekton-cel-run + name: tekton-cel-controller + namespace: tekton-pipelines labels: - app.kubernetes.io/component: cel-controller + app.kubernetes.io/component: controller app.kubernetes.io/instance: default - app.kubernetes.io/part-of: tekton-cel-run + app.kubernetes.io/part-of: tekton-cel rules: - apiGroups: [""] resources: ["configmaps"] verbs: ["list", "watch"] - # The controller needs access to these configmaps for logging information and runtime configuration. - apiGroups: [""] resources: ["configmaps"] verbs: ["get"] resourceNames: ["config-logging", "config-observability", "config-leader-election"] + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: ["tekton-pipelines"] + verbs: ["use"] +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: tekton-cel-webhook + namespace: tekton-pipelines + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +rules: + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["list", "watch"] + # The webhook needs access to these configmaps for logging information. + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["get"] + resourceNames: ["config-logging", "config-observability", "config-leader-election"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["list", "watch"] + # The webhook daemon makes a reconciliation loop on tekton-cel-webhook-certs. Whenever + # the secret changes it updates the webhook configurations with the certificates + # stored in the secret. + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "update"] + resourceNames: ["tekton-cel-webhook-certs"] + - apiGroups: ["policy"] + resources: ["podsecuritypolicies"] + resourceNames: ["tekton-pipelines"] + verbs: ["use"] diff --git a/cel/config/201-rolebinding.yaml b/cel/config/201-rolebinding.yaml index 25c37d9d3..4b10cf5fc 100644 --- a/cel/config/201-rolebinding.yaml +++ b/cel/config/201-rolebinding.yaml @@ -15,17 +15,35 @@ apiVersion: rbac.authorization.k8s.io/v1beta1 kind: RoleBinding metadata: - name: cel-controller - namespace: tekton-cel-run + name: tekton-cel-controller + namespace: tekton-pipelines labels: - app.kubernetes.io/component: cel-controller + app.kubernetes.io/component: controller app.kubernetes.io/instance: default - app.kubernetes.io/part-of: tekton-cel-run + app.kubernetes.io/part-of: tekton-cel subjects: - kind: ServiceAccount - name: cel-controller - namespace: tekton-cel-run + name: tekton-cel-controller + namespace: tekton-pipelines roleRef: kind: Role - name: cel-controller + name: tekton-cel-controller + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: RoleBinding +metadata: + name: tekton-cel-webhook + namespace: tekton-pipelines + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +subjects: + - kind: ServiceAccount + name: tekton-cel-webhook + namespace: tekton-pipelines +roleRef: + kind: Role + name: tekton-cel-webhook apiGroup: rbac.authorization.k8s.io diff --git a/cel/config/202-clusterrolebinding.yaml b/cel/config/202-clusterrolebinding.yaml index 9b27c976a..ccc18d0ed 100644 --- a/cel/config/202-clusterrolebinding.yaml +++ b/cel/config/202-clusterrolebinding.yaml @@ -15,16 +15,88 @@ apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: - name: cel-controller-cluster-access + name: tekton-cel-controller-cluster-access labels: - app.kubernetes.io/component: cel-controller + app.kubernetes.io/component: controller app.kubernetes.io/instance: default - app.kubernetes.io/part-of: tekton-cel-run + app.kubernetes.io/part-of: tekton-cel subjects: - kind: ServiceAccount - name: cel-controller - namespace: tekton-cel-run + name: tekton-cel-controller + namespace: tekton-pipelines roleRef: kind: ClusterRole - name: cel-controller-cluster-access + name: tekton-cel-controller-cluster-access apiGroup: rbac.authorization.k8s.io +--- +# If this ClusterRoleBinding is replaced with a RoleBinding +# then the ClusterRole would be namespaced. The access described by +# the tekton-cel-controller-tenant-access ClusterRole would +# be scoped to individual tenant namespaces. +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: tekton-cel-controller-tenant-access + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +subjects: + - kind: ServiceAccount + name: tekton-cel-controller + namespace: tekton-pipelines +roleRef: + kind: ClusterRole + name: tekton-cel-controller-tenant-access + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: tekton-cel-controller-leaderelection + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +subjects: + - kind: ServiceAccount + name: tekton-cel-controller + namespace: tekton-pipelines +roleRef: + kind: ClusterRole + name: tekton-cel-leader-election + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: tekton-cel-webhook-cluster-access + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +subjects: + - kind: ServiceAccount + name: tekton-cel-webhook + namespace: tekton-pipelines +roleRef: + kind: ClusterRole + name: tekton-cel-webhook-cluster-access + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: tekton-cel-webhook-leaderelection + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel +subjects: + - kind: ServiceAccount + name: tekton-cel-webhook + namespace: tekton-pipelines +roleRef: + kind: ClusterRole + name: tekton-cel-leader-election + apiGroup: rbac.authorization.k8s.io \ No newline at end of file diff --git a/cel/config/300-cel.yaml b/cel/config/300-cel.yaml new file mode 100644 index 000000000..f12e18225 --- /dev/null +++ b/cel/config/300-cel.yaml @@ -0,0 +1,52 @@ +# Copyright 2021 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: cels.custom.tekton.dev + labels: + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel + pipeline.tekton.dev/release: "devel" + version: "devel" +spec: + group: custom.tekton.dev + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + # One can use x-kubernetes-preserve-unknown-fields: true + # at the root of the schema (and inside any properties, additionalProperties) + # to get the traditional CRD behaviour that nothing is pruned, despite + # setting spec.preserveUnknownProperties: false. + # + # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ + # See issue: https://github.com/knative/serving/issues/912 + x-kubernetes-preserve-unknown-fields: true + versions: + - name: v1alpha1 + served: true + storage: true + names: + kind: Cel + plural: cels + categories: + - tekton + - tekton-pipelines + scope: Namespaced + # Opt into the status subresource so metadata.generation + # starts to increment + subresources: + status: {} diff --git a/cel/config/400-controller-service.yaml b/cel/config/400-controller-service.yaml deleted file mode 100644 index bd3c9429b..000000000 --- a/cel/config/400-controller-service.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2021 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 -# -# https://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. - -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/name: cel-controller - app.kubernetes.io/component: cel-controller - app.kubernetes.io/instance: default - app.kubernetes.io/version: devel - app.kubernetes.io/part-of: tekton-cel-run - # tekton.dev/release value replaced with inputs.params.versionTag in pipeline/tekton/publish.yaml - pipeline.tekton.dev/release: "devel" - # labels below are related to istio and should not be used for resource lookup - app: cel-controller - version: "devel" - name: cel-controller - namespace: tekton-cel-run -spec: - ports: - - name: http-metrics - port: 9090 - protocol: TCP - targetPort: 9090 - selector: - app.kubernetes.io/name: cel-controller - app.kubernetes.io/component: cel-controller - app.kubernetes.io/instance: default - app.kubernetes.io/part-of: tekton-cel-run diff --git a/cel/config/500-controller.yaml b/cel/config/500-controller.yaml index fed9c1927..7236c48c4 100644 --- a/cel/config/500-controller.yaml +++ b/cel/config/500-controller.yaml @@ -15,52 +15,55 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: cel-controller - namespace: tekton-cel-run + name: tekton-cel-controller + namespace: tekton-pipelines labels: - app.kubernetes.io/name: cel-controller - app.kubernetes.io/component: cel-controller + app.kubernetes.io/name: controller + app.kubernetes.io/component: controller app.kubernetes.io/instance: default - app.kubernetes.io/version: devel - app.kubernetes.io/part-of: tekton-cel-run + app.kubernetes.io/part-of: tekton-cel + app.kubernetes.io/version: "devel" + pipeline.tekton.dev/release: "devel" + version: "devel" spec: replicas: 1 selector: matchLabels: - app.kubernetes.io/name: cel-controller - app.kubernetes.io/component: cel-controller + app.kubernetes.io/name: controller + app.kubernetes.io/component: controller app.kubernetes.io/instance: default - app.kubernetes.io/part-of: cel + app.kubernetes.io/part-of: tekton-cel template: metadata: annotations: cluster-autoscaler.kubernetes.io/safe-to-evict: "false" labels: - app.kubernetes.io/name: cel-controller - app.kubernetes.io/component: cel-controller + app.kubernetes.io/name: controller + app.kubernetes.io/component: controller app.kubernetes.io/instance: default - app.kubernetes.io/version: devel - app.kubernetes.io/part-of: cel - app: cel-controller + app.kubernetes.io/part-of: tekton-cel + app: tekton-cel-controller + app.kubernetes.io/version: "devel" + pipeline.tekton.dev/release: "devel" + version: "devel" spec: - serviceAccountName: cel-controller + serviceAccountName: tekton-cel-controller containers: - - name: cel-controller - image: ko://github.com/tektoncd/experimental/cel/cmd/controller - volumeMounts: - - name: config-logging - mountPath: /etc/config-logging - env: - - name: SYSTEM_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - # If you are changing these names, you will also need to update - # the controller's Role in 200-role.yaml to include the new - # values in the "configmaps" "get" rule. - - name: CONFIG_LOGGING_NAME - value: config-logging - volumes: - - name: config-logging - configMap: - name: config-logging + - name: tekton-cel-controller + image: ko://github.com/tektoncd/experimental/cel/cmd/controller + env: + - name: SYSTEM_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONFIG_LEADERELECTION_NAME + value: config-leader-election + - name: CONFIG_LOGGING_NAME + value: config-logging + - name: CONFIG_OBSERVABILITY_NAME + value: config-observability + - name: METRICS_DOMAIN + value: tekton.dev/pipeline + securityContext: + allowPrivilegeEscalation: false + runAsUser: 1001 diff --git a/cel/config/500-webhook-configuration.yaml b/cel/config/500-webhook-configuration.yaml new file mode 100644 index 000000000..11207a777 --- /dev/null +++ b/cel/config/500-webhook-configuration.yaml @@ -0,0 +1,66 @@ +# Copyright 2021 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 +# +# https://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. + +apiVersion: v1 +kind: Secret +metadata: + name: tekton-cel-webhook-certs + namespace: tekton-pipelines + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel + pipeline.tekton.dev/release: "devel" +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: ValidatingWebhookConfiguration +metadata: + name: validation.webhook.cel.custom.tekton.dev + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel + pipeline.tekton.dev/release: "devel" +webhooks: +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: tekton-cel-webhook + namespace: tekton-pipelines + failurePolicy: Fail + sideEffects: None + name: validation.webhook.cel.custom.tekton.dev + +--- +apiVersion: admissionregistration.k8s.io/v1beta1 +kind: MutatingWebhookConfiguration +metadata: + name: webhook.cel.custom.tekton.dev + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel + pipeline.tekton.dev/release: "devel" +webhooks: +- admissionReviewVersions: + - v1beta1 + clientConfig: + service: + name: tekton-cel-webhook + namespace: tekton-pipelines + failurePolicy: Fail + sideEffects: None + name: webhook.cel.custom.tekton.dev + diff --git a/cel/config/500-webhook.yaml b/cel/config/500-webhook.yaml new file mode 100644 index 000000000..ad05e1736 --- /dev/null +++ b/cel/config/500-webhook.yaml @@ -0,0 +1,115 @@ +# Copyright 2021 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 +# +# https://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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: tekton-cel-webhook + namespace: tekton-pipelines + labels: + app.kubernetes.io/name: webhook + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel + app.kubernetes.io/version: "devel" + pipeline.tekton.dev/release: "devel" + version: "devel" +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: webhook + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel + template: + metadata: + annotations: + cluster-autoscaler.kubernetes.io/safe-to-evict: "false" + labels: + app.kubernetes.io/name: webhook + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel + app: tekton-cel-webhook + app.kubernetes.io/version: "devel" + pipeline.tekton.dev/release: "devel" + version: "devel" + spec: + serviceAccountName: tekton-cel-webhook + containers: + - name: webhook + image: ko://github.com/tektoncd/experimental/cel/cmd/webhook + env: + - name: SYSTEM_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + # If you are changing these names, you will also need to update + # the webhook's Role in 200-role.yaml to include the new + # values in the "configmaps" "get" rule. + - name: CONFIG_LOGGING_NAME + value: config-logging + - name: CONFIG_OBSERVABILITY_NAME + value: config-observability + - name: CONFIG_LEADERELECTION_NAME + value: config-leader-election + - name: WEBHOOK_SERVICE_NAME + value: tekton-cel-webhook + - name: WEBHOOK_SECRET_NAME + value: tekton-cel-webhook-certs + - name: METRICS_DOMAIN + value: tekton.dev/pipeline + securityContext: + allowPrivilegeEscalation: false + runAsUser: 1001 + ports: + - name: metrics + containerPort: 9090 + - name: profiling + containerPort: 8008 + - name: https-webhook + containerPort: 8443 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: webhook + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel + app: tekton-cel-webhook + app.kubernetes.io/version: "devel" + pipeline.tekton.dev/release: "devel" + version: "devel" + name: tekton-cel-webhook + namespace: tekton-pipelines +spec: + ports: + # Define metrics and profiling for them to be accessible within service meshes. + - name: http-metrics + port: 9090 + targetPort: 9090 + - name: http-profiling + port: 8008 + targetPort: 8008 + - name: https-webhook + port: 443 + targetPort: 8443 + selector: + app.kubernetes.io/name: webhook + app.kubernetes.io/component: webhook + app.kubernetes.io/instance: default + app.kubernetes.io/part-of: tekton-cel diff --git a/cel/config/config-logging.yaml b/cel/config/config-logging.yaml deleted file mode 100644 index eee518b8a..000000000 --- a/cel/config/config-logging.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2021 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 -# -# https://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 - -apiVersion: v1 -kind: ConfigMap -metadata: - name: config-logging - namespace: tekton-cel-run - labels: - app.kubernetes.io/instance: default - app.kubernetes.io/part-of: tekton-cel-run -data: - # Common configuration for all knative codebase - zap-logger-config: | - { - "level": "info", - "development": false, - "sampling": { - "initial": 100, - "thereafter": 100 - }, - "outputPaths": ["stdout"], - "errorOutputPaths": ["stderr"], - "encoding": "json", - "encoderConfig": { - "timeKey": "", - "levelKey": "level", - "nameKey": "logger", - "callerKey": "caller", - "messageKey": "msg", - "stacktraceKey": "stacktrace", - "lineEnding": "", - "levelEncoder": "", - "timeEncoder": "", - "durationEncoder": "", - "callerEncoder": "" - } - } - - # Log level overrides - loglevel.controller: "info" - loglevel.webhook: "info" diff --git a/cel/examples/celrun-is-red.yaml b/cel/examples/celrun-colors.yaml similarity index 67% rename from cel/examples/celrun-is-red.yaml rename to cel/examples/celrun-colors.yaml index eef4c346f..d93085c25 100644 --- a/cel/examples/celrun-is-red.yaml +++ b/cel/examples/celrun-colors.yaml @@ -1,12 +1,9 @@ -apiVersion: tekton.dev/v1alpha1 -kind: Run +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel metadata: - generateName: celrun-is-red- + name: colors spec: - ref: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: + expressions: - name: red value: "{'blue': '0x000080', 'red': '0xFF0000'}['red']" - name: blue @@ -15,3 +12,13 @@ spec: value: "{'blue': '0x000080', 'red': '0xFF0000'}['red'] == '0xFF0000'" - name: is-blue value: "{'blue': '0x000080', 'red': '0xFF0000'}['blue'] == '0xFF0000'" +--- +apiVersion: tekton.dev/v1alpha1 +kind: Run +metadata: + generateName: colors- +spec: + ref: + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: colors diff --git a/cel/examples/celrun-get-type.yaml b/cel/examples/celrun-get-type.yaml index a49efabf3..2ee838be1 100644 --- a/cel/examples/celrun-get-type.yaml +++ b/cel/examples/celrun-get-type.yaml @@ -1,11 +1,18 @@ +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: get-type +spec: + expressions: + - name: expression + value: "type(1)" +--- apiVersion: tekton.dev/v1alpha1 kind: Run metadata: - generateName: celrun-get-type- + generateName: get-type- spec: ref: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: - - name: expression - value: "type(1)" + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: get-type diff --git a/cel/examples/celrun-sevs.yaml b/cel/examples/celrun-sevs.yaml new file mode 100644 index 000000000..410d54fb0 --- /dev/null +++ b/cel/examples/celrun-sevs.yaml @@ -0,0 +1,23 @@ +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: sevs +spec: + variables: + - name: job_priority + value: "high" + - name: alert_enable + value: "true" + expressions: + - name: job_severity + value: "job_priority in ['high', 'normal'] ? 'sev-1' : 'sev-2'" +--- +apiVersion: tekton.dev/v1alpha1 +kind: Run +metadata: + generateName: sevs- +spec: + ref: + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: sevs diff --git a/cel/examples/pipelinerun-celruns.yaml b/cel/examples/pipelinerun-celruns.yaml index 48d22e6a8..bd013812a 100644 --- a/cel/examples/pipelinerun-celruns.yaml +++ b/cel/examples/pipelinerun-celruns.yaml @@ -1,3 +1,21 @@ +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: is-red +spec: + expressions: + - name: red + value: "{'blue': '0x000080', 'red': '0xFF0000'}['red'] == '0xFF0000'" +--- +apiVersion: custom.tekton.dev/v1alpha1 +kind: Cel +metadata: + name: get-type +spec: + expressions: + - name: type + value: "type(1)" +--- apiVersion: tekton.dev/v1beta1 kind: PipelineRun metadata: @@ -5,26 +23,12 @@ metadata: spec: serviceAccountName: 'default' pipelineSpec: - params: - - name: red - type: string - - name: type - type: string tasks: - name: is-red taskRef: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: - - name: red - value: "$(params.red)" - - name: get-type - taskRef: - apiVersion: cel.tekton.dev/v1alpha1 - kind: CEL - params: - - name: type - value: "$(params.type)" + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: is-red - name: echo-is-red when: - input: "$(tasks.is-red.results.red)" @@ -35,6 +39,11 @@ spec: - name: echo image: ubuntu script: echo ISRED! + - name: get-type + taskRef: + apiVersion: custom.tekton.dev/v1alpha1 + kind: Cel + name: get-type - name: echo-get-type when: - input: "$(tasks.get-type.results.type)" @@ -45,8 +54,3 @@ spec: - name: echo image: ubuntu script: echo ISINT! - params: - - name: red - value: "{'blue': '0x000080', 'red': '0xFF0000'}['red'] == '0xFF0000'" - - name: type - value: "type(1)" diff --git a/cel/go.mod b/cel/go.mod index 2ced0b5d0..7b3e666d2 100644 --- a/cel/go.mod +++ b/cel/go.mod @@ -3,21 +3,24 @@ module github.com/tektoncd/experimental/cel go 1.15 require ( - github.com/google/cel-go v0.7.0 + github.com/ghodss/yaml v1.0.0 + github.com/google/cel-go v0.7.3 github.com/google/go-cmp v0.5.4 - github.com/tektoncd/pipeline v0.20.0 + github.com/hashicorp/go-multierror v1.1.0 + github.com/tektoncd/pipeline v0.20.1 + go.opencensus.io v0.22.5 go.uber.org/zap v1.16.0 - k8s.io/api v0.20.2 - k8s.io/apimachinery v0.18.12 + google.golang.org/genproto v0.0.0-20201211151036-40ec1c210f7a + k8s.io/api v0.18.12 + k8s.io/apimachinery v0.19.0 k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible - knative.dev/pkg v0.0.0-20210114223020-f0ea5e6b9c4e + knative.dev/pkg v0.0.0-20210107022335-51c72e24c179 ) -// Knative deps +// Knative deps (release-0.20) replace ( - contrib.go.opencensus.io/exporter/stackdriver => contrib.go.opencensus.io/exporter/stackdriver v0.12.9-0.20191108183826-59d068f8d8ff + contrib.go.opencensus.io/exporter/stackdriver => contrib.go.opencensus.io/exporter/stackdriver v0.13.4 github.com/Azure/azure-sdk-for-go => github.com/Azure/azure-sdk-for-go v38.2.0+incompatible - github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.4.0+incompatible ) // Pin k8s deps to v0.18.8 @@ -25,6 +28,7 @@ replace ( k8s.io/api => k8s.io/api v0.18.8 k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.18.8 k8s.io/apimachinery => k8s.io/apimachinery v0.18.8 + k8s.io/apiserver => k8s.io/apiserver v0.18.8 k8s.io/client-go => k8s.io/client-go v0.18.8 k8s.io/code-generator => k8s.io/code-generator v0.18.8 k8s.io/kube-openapi => k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 diff --git a/cel/go.sum b/cel/go.sum index c1e96cd3f..048d5c0d5 100644 --- a/cel/go.sum +++ b/cel/go.sum @@ -39,44 +39,34 @@ contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d h contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY= contrib.go.opencensus.io/exporter/prometheus v0.2.1-0.20200609204449-6bcf6f8577f0 h1:2O3c1g5CzMc1+Uah4Waot9Obm0yw70VXJzWaP6Fz3nw= contrib.go.opencensus.io/exporter/prometheus v0.2.1-0.20200609204449-6bcf6f8577f0/go.mod h1:MjHoxkI7Ny27toPeFkRbXbzVjzIGkwOAptrAy8Mxtm8= -contrib.go.opencensus.io/exporter/stackdriver v0.12.9-0.20191108183826-59d068f8d8ff h1:g4QkFNN0ak+sCs/jqbhYLNkQaF1NVaKVoQ4Xm1RV3wM= -contrib.go.opencensus.io/exporter/stackdriver v0.12.9-0.20191108183826-59d068f8d8ff/go.mod h1:XyyafDnFOsqoxHJgTFycKZMrRUrPThLh2iYTJF6uoO0= +contrib.go.opencensus.io/exporter/stackdriver v0.13.4 h1:ksUxwH3OD5sxkjzEqGxNTl+Xjsmu3BnC/300MhSVTSc= +contrib.go.opencensus.io/exporter/stackdriver v0.13.4/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc= contrib.go.opencensus.io/exporter/zipkin v0.1.2/go.mod h1:mP5xM3rrgOjpn79MM8fZbj3gsxcuytSqtH0dxSWW1RE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/azure-sdk-for-go v38.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v13.4.0+incompatible h1:YI49AKujDVk2KcLPKYGfkP7DlwU1xlRdI8GA0JWVYvE= -github.com/Azure/go-autorest v13.4.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= -github.com/Azure/go-autorest/autorest v0.10.2 h1:NuSF3gXetiHyUbVdneJMEVyPUYAe5wh+aN08JYAf1tI= github.com/Azure/go-autorest/autorest v0.10.2/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= -github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= -github.com/Azure/go-autorest/autorest/adal v0.9.5 h1:Y3bBUV4rTuxenJJs41HU3qmqsb+auo+a3Lz+PlJPpL0= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0 h1:yW+Zlqf26583pE43KhfnhFcdmSWlm5Ew6bxipnr/tbM= github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= -github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= -github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -188,7 +178,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-gk v0.0.0-20140819190930-201884a44051/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= @@ -228,7 +217,6 @@ github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5I github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= @@ -305,12 +293,12 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ= +github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48 h1:X+zN6RZXsvnrSJaAIQhZezPfAfvsqihKKR8oiLHid34= github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -358,8 +346,8 @@ github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/cel-go v0.7.0 h1:J0J8RSCJW+SdB53YPwPSm2m1Kfz1tqGXdwMuIAVRU9o= -github.com/google/cel-go v0.7.0/go.mod h1:4EtyFAHT5xNr0Msu0MJjyGxPUgdr9DlcaPyzLt/kkt8= +github.com/google/cel-go v0.7.3 h1:8v9BSN0avuGwrHFKNCjfiQ/CE6+D6sW+BDyOVoEeP6o= +github.com/google/cel-go v0.7.3/go.mod h1:4EtyFAHT5xNr0Msu0MJjyGxPUgdr9DlcaPyzLt/kkt8= github.com/google/cel-spec v0.5.0/go.mod h1:Nwjgxy5CbjlPrtCWjeDjUyKMl8w41YBYGjsyDdqk0xA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -408,7 +396,6 @@ github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTV github.com/googleapis/gnostic v0.2.2/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.4.0 h1:BXDUo8p/DaxC+4FJY/SSx3gvnx9C1VdHNgaUkiEL5mk= github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU= -github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -475,6 +462,7 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -521,6 +509,7 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.1-0.20191009090205-6c0755d89d1e h1:jcoUdG1TzY/M/eM5BLFLP8DJeMximx5NQYSlLL9YeWc= github.com/mailru/easyjson v0.7.1-0.20191009090205-6c0755d89d1e/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/markbates/inflect v1.0.4 h1:5fh1gzTFhfae06u3hzHYO9xe3l3v3nW5Pwt3naLTP5g= github.com/markbates/inflect v1.0.4/go.mod h1:1fR9+pO2KHEO9ZRtto13gDwwZaAKstQzferVeWqbgNs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -596,6 +585,7 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/openzipkin/zipkin-go v0.2.0/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.5 h1:UwtQQx2pyPIgWYHRg+epgdx1/HnBQTgN3/oIYEJTQzU= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= @@ -669,6 +659,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rubiojr/go-vhd v0.0.0-20160810183302-0bfd3b39853c/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -721,8 +712,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/tektoncd/pipeline v0.20.0 h1:JW9YgkzQAvW2vzU1zpwyz3IALqzyMNBnfLOzz88mZ0A= -github.com/tektoncd/pipeline v0.20.0/go.mod h1:xF5WxMLvp/05oGZ+Fvqcbglmf4HVU9u1keXHaM+rR14= +github.com/tektoncd/pipeline v0.20.1 h1:hNJa7B2xOPsg4S1a961kyDk6hnDaEAHUll9GKHCMdMg= +github.com/tektoncd/pipeline v0.20.1/go.mod h1:xF5WxMLvp/05oGZ+Fvqcbglmf4HVU9u1keXHaM+rR14= github.com/tektoncd/plumbing v0.0.0-20201021153918-6b7e894737b5/go.mod h1:WTWwsg91xgm+jPOKoyKVK/yRYxnVDlUYeDlypB1lDdQ= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -757,7 +748,6 @@ go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4-0.20200608061201-1901b56b9515/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -863,7 +853,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -936,7 +926,6 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1013,6 +1002,7 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1229,11 +1219,13 @@ k8s.io/apiserver v0.18.8/go.mod h1:12u5FuGql8Cc497ORNj79rhPdiXQC4bf53X/skR/1YM= k8s.io/client-go v0.18.8 h1:SdbLpIxk5j5YbFr1b7fq8S7mDgDjYmUxSbszyoesoDM= k8s.io/client-go v0.18.8/go.mod h1:HqFqMllQ5NnQJNwjro9k5zMyfhZlOwpuTLVrxjkYSxU= k8s.io/cloud-provider v0.18.8/go.mod h1:cn9AlzMPVIXA4HHLVbgGUigaQlZyHSZ7WAwDEFNrQSs= +k8s.io/code-generator v0.18.8 h1:lgO1P1wjikEtzNvj7ia+x1VC4svJ28a/r0wnOLhhOTU= k8s.io/code-generator v0.18.8/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/component-base v0.18.8/go.mod h1:00frPRDas29rx58pPCxNkhUfPbwajlyyvu8ruNgSErU= k8s.io/csi-translation-lib v0.18.8/go.mod h1:6cA6Btlzxy9s3QrS4BCZzQqclIWnTLr6Jx3H2ctAzY4= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20200205140755-e0e292d8aa12 h1:pZzawYyz6VRNPVYpqGv61LWCimQv1BihyeqFrp50/G4= k8s.io/gengo v0.0.0-20200205140755-e0e292d8aa12/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= @@ -1247,10 +1239,10 @@ k8s.io/legacy-cloud-providers v0.18.8/go.mod h1:tgp4xYf6lvjrWnjQwTOPvWQE9IVqSBGP k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7MpmSnvtrOieolJKoE= k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +knative.dev/hack v0.0.0-20201214230143-4ed1ecb8db24 h1:kIztWfvnIFV8Lhlea02K3YO2mIzcDyQNzrBLn0Oq9sA= knative.dev/hack v0.0.0-20201214230143-4ed1ecb8db24/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI= +knative.dev/pkg v0.0.0-20210107022335-51c72e24c179 h1:lkrgrv69iUk2qhOG9symy15kJUaJZmMybSloi7C3gIw= knative.dev/pkg v0.0.0-20210107022335-51c72e24c179/go.mod h1:hckgW978SdzPA2H5EDvRPY8xsnPuDZLJLbPf8Jte7Q0= -knative.dev/pkg v0.0.0-20210114223020-f0ea5e6b9c4e h1:3k5tzvlM9VGZFiXRj8UKc3CUpMGpqBlEbIY0Dp3F3NU= -knative.dev/pkg v0.0.0-20210114223020-f0ea5e6b9c4e/go.mod h1:hckgW978SdzPA2H5EDvRPY8xsnPuDZLJLbPf8Jte7Q0= pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/cel/hack/boilerplate/boilerplate.go.txt b/cel/hack/boilerplate/boilerplate.go.txt new file mode 100644 index 000000000..a07160895 --- /dev/null +++ b/cel/hack/boilerplate/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* +Copyright 2021 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. +*/ diff --git a/cel/hack/tools.go b/cel/hack/tools.go new file mode 100644 index 000000000..eae239b18 --- /dev/null +++ b/cel/hack/tools.go @@ -0,0 +1,7 @@ +// +build tools + +package tools + +import ( + _ "knative.dev/pkg/hack" +) diff --git a/cel/hack/update-codegen.sh b/cel/hack/update-codegen.sh new file mode 100755 index 000000000..249057dab --- /dev/null +++ b/cel/hack/update-codegen.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# Copyright 2021 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. + +set -o errexit +set -o nounset +set -o pipefail + +export GO111MODULE=on + +REPO_ROOT=$(dirname ${BASH_SOURCE})/.. +CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${REPO_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)} + +KNATIVE_CODEGEN_PKG=${KNATIVE_CODEGEN_PKG:-$(cd ${REPO_ROOT}; ls -d -1 ./vendor/knative.dev/pkg 2>/dev/null || echo ../pkg)} + +# generate the code with: +# --output-base because this script should also be able to run inside the vendor dir of +# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir +# instead of the $GOPATH directly. For normal projects this can be dropped. +chmod +x ${CODEGEN_PKG}/generate-groups.sh +${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \ + github.com/tektoncd/experimental/cel/pkg/client github.com/tektoncd/experimental/cel/pkg/apis \ + "cel:v1alpha1" \ + --go-header-file ${REPO_ROOT}/hack/boilerplate/boilerplate.go.txt + +# Knative Injection + +chmod +x ${KNATIVE_CODEGEN_PKG}/hack/generate-knative.sh +${KNATIVE_CODEGEN_PKG}/hack/generate-knative.sh "injection" \ + github.com/tektoncd/experimental/cel/pkg/client github.com/tektoncd/experimental/cel/pkg/apis \ + "cel:v1alpha1" \ + --go-header-file ${REPO_ROOT}/hack/boilerplate/boilerplate.go.txt + +# Make sure our dependencies are up-to-date +${REPO_ROOT}/hack/update-deps.sh diff --git a/cel/hack/update-deps.sh b/cel/hack/update-deps.sh new file mode 100755 index 000000000..84e99510d --- /dev/null +++ b/cel/hack/update-deps.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +# Copyright 2021 The Knative 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. + +readonly ROOT_DIR=$(dirname $0)/.. +source $(git rev-parse --show-toplevel)/vendor/github.com/tektoncd/plumbing/scripts/library.sh + +set -o errexit +set -o nounset +set -o pipefail + +export GO111MODULE=on +export GOFLAGS=-mod=vendor + +cd ${ROOT_DIR} + +VERSION="release-0.18" + +# The list of dependencies that we track at HEAD and periodically +# float forward in this repository. +FLOATING_DEPS=( + "knative.dev/pkg@${VERSION}" + "knative.dev/eventing@${VERSION}" + "knative.dev/test-infra@${VERSION}" +) + +# Parse flags to determine any we should pass to dep. +GO_GET=0 +while [[ $# -ne 0 ]]; do + parameter=$1 + case ${parameter} in + --upgrade) GO_GET=1 ;; + *) abort "unknown option ${parameter}" ;; + esac + shift +done +readonly GO_GET + +if (( GO_GET )); then + go get -d ${FLOATING_DEPS[@]} +fi + +# Prune modules. +go mod tidy +go mod vendor + +rm -rf $(find vendor/ -name 'OWNERS') +rm -rf $(find vendor/ -name '*_test.go') diff --git a/cel/pkg/apis/cel/controller.go b/cel/pkg/apis/cel/controller.go new file mode 100644 index 000000000..a40f87894 --- /dev/null +++ b/cel/pkg/apis/cel/controller.go @@ -0,0 +1,22 @@ +/* +Copyright 2021 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 cel + +const ( + // ControllerName holds the name of the Cel controller + ControllerName = "Cel" +) diff --git a/cel/pkg/apis/cel/register.go b/cel/pkg/apis/cel/register.go new file mode 100644 index 000000000..977814d22 --- /dev/null +++ b/cel/pkg/apis/cel/register.go @@ -0,0 +1,22 @@ +/* +Copyright 2021 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 cel + +const ( + // GroupName is the Kubernetes resource group name for Cel types + GroupName = "custom.tekton.dev" +) diff --git a/cel/pkg/apis/cel/v1alpha1/cel_defaults.go b/cel/pkg/apis/cel/v1alpha1/cel_defaults.go new file mode 100644 index 000000000..94c087c8b --- /dev/null +++ b/cel/pkg/apis/cel/v1alpha1/cel_defaults.go @@ -0,0 +1,34 @@ +/* +Copyright 2021 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 v1alpha1 + +import ( + "context" + + "knative.dev/pkg/apis" +) + +var _ apis.Defaultable = (*Cel)(nil) + +// SetDefaults set any defaults for the Cel +func (c *Cel) SetDefaults(ctx context.Context) { + c.Spec.SetDefaults(ctx) +} + +// SetDefaults set any defaults for the Cel spec +func (cs *CelSpec) SetDefaults(ctx context.Context) { +} diff --git a/cel/pkg/apis/cel/v1alpha1/cel_types.go b/cel/pkg/apis/cel/v1alpha1/cel_types.go new file mode 100644 index 000000000..b05dc2292 --- /dev/null +++ b/cel/pkg/apis/cel/v1alpha1/cel_types.go @@ -0,0 +1,96 @@ +/* +Copyright 2021 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 v1alpha1 + +import ( + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +genclient:noStatus +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Cel evaluates a CEL expression with given environment variables +// +k8s:openapi-gen=true +type Cel struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata"` + + // CelSpec holds the desired state of the Cel from the client + // +optional + Spec CelSpec `json:"spec"` +} + +// CelSpec defines the desired state of the Cel +type CelSpec struct { + // Variables to be configured in the CEL environment before evaluation + // +optional + Variables []*v1beta1.Param `json:"variables,omitempty"` + + // Expressions are a list of CEL expressions to be evaluated given the environment Variables + Expressions []*v1beta1.Param `json:"expressions"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// CelList contains a list of Cels +type CelList struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + Items []Cel `json:"items"` +} + +// CelRunReason represents a reason for the Run "Succeeded" condition +type CelRunReason string + +const ( + // CelRunReasonFailedValidation indicates that the reason for failure status is that Cel failed validation + CelRunReasonFailedValidation CelRunReason = "RunValidationFailed" + + // CelRunReasonSyntaxError indicates that the reason for failure status is that a Cel expression couldn't be parsed + CelRunReasonSyntaxError CelRunReason = "SyntaxError" + + // CelRunReasonEvaluationError indicates that the reason for failure status is that a Cel expression couldn't be + // evaluated typically due to evaluation environment or executable program + CelRunReasonEvaluationError CelRunReason = "EvaluationError" + + // CelRunReasonEvaluationSuccess indicates that the reason for the success status is that all Cel expressions were + // evaluated successfully and the evaluation results were produced + CelRunReasonEvaluationSuccess CelRunReason = "EvaluationSuccess" + + // CelRunReasonCouldntGetCel indicates that the reason for the failure status is that the Run could not find the Cel + CelRunReasonCouldntGetCel CelRunReason = "CouldntGetCel" + + // CelRunReasonInternalError indicates that the Cel failed due to an internal error in the reconciler + CelRunReasonInternalError CelRunReason = "InternalError" +) + +func (t CelRunReason) String() string { + return string(t) +} + +type CelStatus struct { + // CelSpec contains the exact spec used to instantiate the Run + CelSpec *CelSpec `json:"celSpec,omitempty"` + + // CelResults contains the results from evaluating the Cel expressions given the environment Variables + CelResults []v1alpha1.RunResult `json:"celResults,omitempty"` +} diff --git a/cel/pkg/apis/cel/v1alpha1/cel_validation.go b/cel/pkg/apis/cel/v1alpha1/cel_validation.go new file mode 100644 index 000000000..51cd07c2a --- /dev/null +++ b/cel/pkg/apis/cel/v1alpha1/cel_validation.go @@ -0,0 +1,74 @@ +/* +Copyright 2021 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 v1alpha1 + +import ( + "context" + "fmt" + "github.com/tektoncd/pipeline/pkg/apis/validate" + "knative.dev/pkg/apis" +) + +var _ apis.Validatable = (*Cel)(nil) + +func (c *Cel) Validate(ctx context.Context) *apis.FieldError { + if err := validate.ObjectMetadata(c.GetObjectMeta()); err != nil { + return err.ViaField("metadata") + } + return c.Spec.Validate() +} + +func (cs *CelSpec) Validate() *apis.FieldError { + if err := validateCel(cs); err != nil { + return err + } + return nil +} + +func validateCel(cs *CelSpec) (errs *apis.FieldError) { + errs = errs.Also(validateExpressionsProvided(cs)) + errs = errs.Also(validateExpressionsType(cs)) + errs = errs.Also(validateVariablesType(cs)) + return errs +} + +func validateExpressionsProvided(cs *CelSpec) (errs *apis.FieldError) { + if len(cs.Expressions) == 0 { + errs = errs.Also(apis.ErrMissingField("expressions")) + } + return errs +} + +func validateExpressionsType(cs *CelSpec) (errs *apis.FieldError) { + for _, expression := range cs.Expressions { + if expression.Value.StringVal == "" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("CEL expression %s must be a string", expression.Name), + "value").ViaFieldKey("expressions", expression.Name)) + } + } + return errs +} + +func validateVariablesType(cs *CelSpec) (errs *apis.FieldError) { + for _, variable := range cs.Variables { + if variable.Value.StringVal == "" { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("CEL environment variable %s must be a string", variable.Name), + "value").ViaFieldKey("variables", variable.Name)) + } + } + return errs +} diff --git a/cel/pkg/apis/cel/v1alpha1/cel_validation_test.go b/cel/pkg/apis/cel/v1alpha1/cel_validation_test.go new file mode 100644 index 000000000..39bed6acf --- /dev/null +++ b/cel/pkg/apis/cel/v1alpha1/cel_validation_test.go @@ -0,0 +1,147 @@ +/* +Copyright 2021 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 v1alpha1_test + +import ( + "context" + celv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/tektoncd/pipeline/test/diff" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" +) + +func Test_Cel_Validate_Valid(t *testing.T) { + tests := []struct { + name string + cs *celv1alpha1.Cel + }{{ + name: "expressions only", + cs: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{Name: "cel"}, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{ + StringVal: "foo", + }, + }}, + }, + }, + }, { + name: "expressions and variables", + cs: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{Name: "cel"}, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{ + StringVal: "foo", + }, + }}, + Variables: []*v1beta1.Param{{ + Name: "var1", + Value: v1beta1.ArrayOrString{ + StringVal: "bar", + }, + }}, + }, + }, + }} + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.cs.Validate(context.Background()) + if err != nil { + t.Errorf("Unexpected error for %s: %s", tc.name, err) + } + }) + } +} + +func Test_Cel_Validate_Invalid(t *testing.T) { + tests := []struct { + name string + cs *celv1alpha1.Cel + expectedError apis.FieldError + }{{ + name: "no expressions", + cs: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{Name: "cel"}, + Spec: celv1alpha1.CelSpec{}, + }, + expectedError: apis.FieldError{ + Message: "missing field(s)", + Paths: []string{"expressions"}, + }, + }, { + name: "array expressions", + cs: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{Name: "cel"}, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{ + ArrayVal: []string{"foo", "bar"}, + }, + }}, + }, + }, + expectedError: apis.FieldError{ + Message: "invalid value: CEL expression expr1 must be a string", + Paths: []string{"expressions[expr1].value"}, + }, + }, { + name: "array variables", + cs: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{Name: "cel"}, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{ + StringVal: "foo", + }, + }}, + Variables: []*v1beta1.Param{{ + Name: "var1", + Value: v1beta1.ArrayOrString{ + ArrayVal: []string{"foo", "bar"}, + }, + }}, + }, + }, + expectedError: apis.FieldError{ + Message: "invalid value: CEL environment variable var1 must be a string", + Paths: []string{"variables[var1].value"}, + }, + }} + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + err := tc.cs.Validate(context.Background()) + if err == nil { + t.Errorf("Expected an Error but did not get one for %s", tc.name) + } else { + if d := cmp.Diff(tc.expectedError.Error(), err.Error(), cmpopts.IgnoreUnexported(apis.FieldError{})); d != "" { + t.Errorf("Error is different from expected for %s. diff %s", tc.name, diff.PrintWantGot(d)) + } + } + }) + } +} diff --git a/cel/pkg/apis/cel/v1alpha1/doc.go b/cel/pkg/apis/cel/v1alpha1/doc.go new file mode 100644 index 000000000..e5ee8a003 --- /dev/null +++ b/cel/pkg/apis/cel/v1alpha1/doc.go @@ -0,0 +1,22 @@ +/* +Copyright 2021 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 v1alpha1 contains API Schema definitions for the cel v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:defaulter-gen=TypeMeta +// +groupName=custom.tekton.dev +package v1alpha1 diff --git a/cel/pkg/apis/cel/v1alpha1/register.go b/cel/pkg/apis/cel/v1alpha1/register.go new file mode 100644 index 000000000..96ef6382d --- /dev/null +++ b/cel/pkg/apis/cel/v1alpha1/register.go @@ -0,0 +1,54 @@ +/* +Copyright 2021 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 v1alpha1 + +import ( + "github.com/tektoncd/experimental/cel/pkg/apis/cel" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: cel.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds Build types to the scheme + AddToScheme = schemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Cel{}, + &CelList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/cel/pkg/apis/cel/v1alpha1/zz_generated.deepcopy.go b/cel/pkg/apis/cel/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..6c09f045f --- /dev/null +++ b/cel/pkg/apis/cel/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,152 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2021 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + runv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/run/v1alpha1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Cel) DeepCopyInto(out *Cel) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Cel. +func (in *Cel) DeepCopy() *Cel { + if in == nil { + return nil + } + out := new(Cel) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Cel) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CelList) DeepCopyInto(out *CelList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Cel, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CelList. +func (in *CelList) DeepCopy() *CelList { + if in == nil { + return nil + } + out := new(CelList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CelList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CelSpec) DeepCopyInto(out *CelSpec) { + *out = *in + if in.Variables != nil { + in, out := &in.Variables, &out.Variables + *out = make([]*v1beta1.Param, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1beta1.Param) + (*in).DeepCopyInto(*out) + } + } + } + if in.Expressions != nil { + in, out := &in.Expressions, &out.Expressions + *out = make([]*v1beta1.Param, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(v1beta1.Param) + (*in).DeepCopyInto(*out) + } + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CelSpec. +func (in *CelSpec) DeepCopy() *CelSpec { + if in == nil { + return nil + } + out := new(CelSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CelStatus) DeepCopyInto(out *CelStatus) { + *out = *in + if in.CelSpec != nil { + in, out := &in.CelSpec, &out.CelSpec + *out = new(CelSpec) + (*in).DeepCopyInto(*out) + } + if in.CelResults != nil { + in, out := &in.CelResults, &out.CelResults + *out = make([]runv1alpha1.RunResult, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CelStatus. +func (in *CelStatus) DeepCopy() *CelStatus { + if in == nil { + return nil + } + out := new(CelStatus) + in.DeepCopyInto(out) + return out +} diff --git a/cel/pkg/client/clientset/versioned/clientset.go b/cel/pkg/client/clientset/versioned/clientset.go new file mode 100644 index 000000000..8cd905734 --- /dev/null +++ b/cel/pkg/client/clientset/versioned/clientset.go @@ -0,0 +1,97 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + customv1alpha1 "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + CustomV1alpha1() customv1alpha1.CustomV1alpha1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + customV1alpha1 *customv1alpha1.CustomV1alpha1Client +} + +// CustomV1alpha1 retrieves the CustomV1alpha1Client +func (c *Clientset) CustomV1alpha1() customv1alpha1.CustomV1alpha1Interface { + return c.customV1alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.customV1alpha1, err = customv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.customV1alpha1 = customv1alpha1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.customV1alpha1 = customv1alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/cel/pkg/client/clientset/versioned/doc.go b/cel/pkg/client/clientset/versioned/doc.go new file mode 100644 index 000000000..115793bae --- /dev/null +++ b/cel/pkg/client/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/cel/pkg/client/clientset/versioned/fake/clientset_generated.go b/cel/pkg/client/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 000000000..213ddd963 --- /dev/null +++ b/cel/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,82 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned" + customv1alpha1 "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1" + fakecustomv1alpha1 "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +var _ clientset.Interface = &Clientset{} + +// CustomV1alpha1 retrieves the CustomV1alpha1Client +func (c *Clientset) CustomV1alpha1() customv1alpha1.CustomV1alpha1Interface { + return &fakecustomv1alpha1.FakeCustomV1alpha1{Fake: &c.Fake} +} diff --git a/cel/pkg/client/clientset/versioned/fake/doc.go b/cel/pkg/client/clientset/versioned/fake/doc.go new file mode 100644 index 000000000..7f73ec73d --- /dev/null +++ b/cel/pkg/client/clientset/versioned/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/cel/pkg/client/clientset/versioned/fake/register.go b/cel/pkg/client/clientset/versioned/fake/register.go new file mode 100644 index 000000000..72a3669ae --- /dev/null +++ b/cel/pkg/client/clientset/versioned/fake/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + customv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) +var parameterCodec = runtime.NewParameterCodec(scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + customv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/cel/pkg/client/clientset/versioned/scheme/doc.go b/cel/pkg/client/clientset/versioned/scheme/doc.go new file mode 100644 index 000000000..ec818b613 --- /dev/null +++ b/cel/pkg/client/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/cel/pkg/client/clientset/versioned/scheme/register.go b/cel/pkg/client/clientset/versioned/scheme/register.go new file mode 100644 index 000000000..f48ad29dd --- /dev/null +++ b/cel/pkg/client/clientset/versioned/scheme/register.go @@ -0,0 +1,56 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + customv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + customv1alpha1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/cel.go b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/cel.go new file mode 100644 index 000000000..18394cf3d --- /dev/null +++ b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/cel.go @@ -0,0 +1,178 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + scheme "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// CelsGetter has a method to return a CelInterface. +// A group's client should implement this interface. +type CelsGetter interface { + Cels(namespace string) CelInterface +} + +// CelInterface has methods to work with Cel resources. +type CelInterface interface { + Create(ctx context.Context, cel *v1alpha1.Cel, opts v1.CreateOptions) (*v1alpha1.Cel, error) + Update(ctx context.Context, cel *v1alpha1.Cel, opts v1.UpdateOptions) (*v1alpha1.Cel, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Cel, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.CelList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Cel, err error) + CelExpansion +} + +// cels implements CelInterface +type cels struct { + client rest.Interface + ns string +} + +// newCels returns a Cels +func newCels(c *CustomV1alpha1Client, namespace string) *cels { + return &cels{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the cel, and returns the corresponding cel object, and an error if there is any. +func (c *cels) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Cel, err error) { + result = &v1alpha1.Cel{} + err = c.client.Get(). + Namespace(c.ns). + Resource("cels"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of Cels that match those selectors. +func (c *cels) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.CelList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.CelList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("cels"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested cels. +func (c *cels) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("cels"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a cel and creates it. Returns the server's representation of the cel, and an error, if there is any. +func (c *cels) Create(ctx context.Context, cel *v1alpha1.Cel, opts v1.CreateOptions) (result *v1alpha1.Cel, err error) { + result = &v1alpha1.Cel{} + err = c.client.Post(). + Namespace(c.ns). + Resource("cels"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(cel). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a cel and updates it. Returns the server's representation of the cel, and an error, if there is any. +func (c *cels) Update(ctx context.Context, cel *v1alpha1.Cel, opts v1.UpdateOptions) (result *v1alpha1.Cel, err error) { + result = &v1alpha1.Cel{} + err = c.client.Put(). + Namespace(c.ns). + Resource("cels"). + Name(cel.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(cel). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the cel and deletes it. Returns an error if one occurs. +func (c *cels) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("cels"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *cels) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("cels"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched cel. +func (c *cels) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Cel, err error) { + result = &v1alpha1.Cel{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("cels"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/cel_client.go b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/cel_client.go new file mode 100644 index 000000000..fec356734 --- /dev/null +++ b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/cel_client.go @@ -0,0 +1,89 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type CustomV1alpha1Interface interface { + RESTClient() rest.Interface + CelsGetter +} + +// CustomV1alpha1Client is used to interact with features provided by the custom.tekton.dev group. +type CustomV1alpha1Client struct { + restClient rest.Interface +} + +func (c *CustomV1alpha1Client) Cels(namespace string) CelInterface { + return newCels(c, namespace) +} + +// NewForConfig creates a new CustomV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*CustomV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &CustomV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new CustomV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *CustomV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new CustomV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *CustomV1alpha1Client { + return &CustomV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *CustomV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/doc.go b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/doc.go new file mode 100644 index 000000000..530b4b4c9 --- /dev/null +++ b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/doc.go b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/doc.go new file mode 100644 index 000000000..f363a1ab8 --- /dev/null +++ b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/fake_cel.go b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/fake_cel.go new file mode 100644 index 000000000..a6c6cf05e --- /dev/null +++ b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/fake_cel.go @@ -0,0 +1,130 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeCels implements CelInterface +type FakeCels struct { + Fake *FakeCustomV1alpha1 + ns string +} + +var celsResource = schema.GroupVersionResource{Group: "custom.tekton.dev", Version: "v1alpha1", Resource: "cels"} + +var celsKind = schema.GroupVersionKind{Group: "custom.tekton.dev", Version: "v1alpha1", Kind: "Cel"} + +// Get takes name of the cel, and returns the corresponding cel object, and an error if there is any. +func (c *FakeCels) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Cel, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(celsResource, c.ns, name), &v1alpha1.Cel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cel), err +} + +// List takes label and field selectors, and returns the list of Cels that match those selectors. +func (c *FakeCels) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.CelList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(celsResource, celsKind, c.ns, opts), &v1alpha1.CelList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.CelList{ListMeta: obj.(*v1alpha1.CelList).ListMeta} + for _, item := range obj.(*v1alpha1.CelList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested cels. +func (c *FakeCels) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(celsResource, c.ns, opts)) + +} + +// Create takes the representation of a cel and creates it. Returns the server's representation of the cel, and an error, if there is any. +func (c *FakeCels) Create(ctx context.Context, cel *v1alpha1.Cel, opts v1.CreateOptions) (result *v1alpha1.Cel, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(celsResource, c.ns, cel), &v1alpha1.Cel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cel), err +} + +// Update takes the representation of a cel and updates it. Returns the server's representation of the cel, and an error, if there is any. +func (c *FakeCels) Update(ctx context.Context, cel *v1alpha1.Cel, opts v1.UpdateOptions) (result *v1alpha1.Cel, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(celsResource, c.ns, cel), &v1alpha1.Cel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cel), err +} + +// Delete takes name of the cel and deletes it. Returns an error if one occurs. +func (c *FakeCels) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(celsResource, c.ns, name), &v1alpha1.Cel{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeCels) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(celsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.CelList{}) + return err +} + +// Patch applies the patch and returns the patched cel. +func (c *FakeCels) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Cel, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(celsResource, c.ns, name, pt, data, subresources...), &v1alpha1.Cel{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.Cel), err +} diff --git a/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/fake_cel_client.go b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/fake_cel_client.go new file mode 100644 index 000000000..0eb07f206 --- /dev/null +++ b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/fake/fake_cel_client.go @@ -0,0 +1,40 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeCustomV1alpha1 struct { + *testing.Fake +} + +func (c *FakeCustomV1alpha1) Cels(namespace string) v1alpha1.CelInterface { + return &FakeCels{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeCustomV1alpha1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/generated_expansion.go b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/generated_expansion.go new file mode 100644 index 000000000..26b0c621a --- /dev/null +++ b/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1/generated_expansion.go @@ -0,0 +1,21 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type CelExpansion interface{} diff --git a/cel/pkg/client/informers/externalversions/cel/interface.go b/cel/pkg/client/informers/externalversions/cel/interface.go new file mode 100644 index 000000000..ca3192102 --- /dev/null +++ b/cel/pkg/client/informers/externalversions/cel/interface.go @@ -0,0 +1,46 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package cel + +import ( + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions/cel/v1alpha1" + internalinterfaces "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1alpha1 provides access to shared informers for resources in V1alpha1. + V1alpha1() v1alpha1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1alpha1 returns a new v1alpha1.Interface. +func (g *group) V1alpha1() v1alpha1.Interface { + return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/cel/pkg/client/informers/externalversions/cel/v1alpha1/cel.go b/cel/pkg/client/informers/externalversions/cel/v1alpha1/cel.go new file mode 100644 index 000000000..287fb9e02 --- /dev/null +++ b/cel/pkg/client/informers/externalversions/cel/v1alpha1/cel.go @@ -0,0 +1,90 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + celv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + versioned "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned" + internalinterfaces "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/client/listers/cel/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// CelInformer provides access to a shared informer and lister for +// Cels. +type CelInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.CelLister +} + +type celInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewCelInformer constructs a new informer for Cel type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewCelInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredCelInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredCelInformer constructs a new informer for Cel type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredCelInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.CustomV1alpha1().Cels(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.CustomV1alpha1().Cels(namespace).Watch(context.TODO(), options) + }, + }, + &celv1alpha1.Cel{}, + resyncPeriod, + indexers, + ) +} + +func (f *celInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredCelInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *celInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&celv1alpha1.Cel{}, f.defaultInformer) +} + +func (f *celInformer) Lister() v1alpha1.CelLister { + return v1alpha1.NewCelLister(f.Informer().GetIndexer()) +} diff --git a/cel/pkg/client/informers/externalversions/cel/v1alpha1/interface.go b/cel/pkg/client/informers/externalversions/cel/v1alpha1/interface.go new file mode 100644 index 000000000..c6987b17d --- /dev/null +++ b/cel/pkg/client/informers/externalversions/cel/v1alpha1/interface.go @@ -0,0 +1,45 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + internalinterfaces "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // Cels returns a CelInformer. + Cels() CelInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// Cels returns a CelInformer. +func (v *version) Cels() CelInformer { + return &celInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/cel/pkg/client/informers/externalversions/factory.go b/cel/pkg/client/informers/externalversions/factory.go new file mode 100644 index 000000000..71d7c45cb --- /dev/null +++ b/cel/pkg/client/informers/externalversions/factory.go @@ -0,0 +1,180 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned" + cel "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions/cel" + internalinterfaces "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions/internalinterfaces" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Custom() cel.Interface +} + +func (f *sharedInformerFactory) Custom() cel.Interface { + return cel.New(f, f.namespace, f.tweakListOptions) +} diff --git a/cel/pkg/client/informers/externalversions/generic.go b/cel/pkg/client/informers/externalversions/generic.go new file mode 100644 index 000000000..c009e27e0 --- /dev/null +++ b/cel/pkg/client/informers/externalversions/generic.go @@ -0,0 +1,62 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=custom.tekton.dev, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithResource("cels"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Custom().V1alpha1().Cels().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/cel/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/cel/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 000000000..d67a471cb --- /dev/null +++ b/cel/pkg/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,40 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/cel/pkg/client/injection/client/client.go b/cel/pkg/client/injection/client/client.go new file mode 100644 index 000000000..89a782dcd --- /dev/null +++ b/cel/pkg/client/injection/client/client.go @@ -0,0 +1,54 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package client + +import ( + context "context" + + versioned "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned" + rest "k8s.io/client-go/rest" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterClient(withClient) +} + +// Key is used as the key for associating information with a context.Context. +type Key struct{} + +func withClient(ctx context.Context, cfg *rest.Config) context.Context { + return context.WithValue(ctx, Key{}, versioned.NewForConfigOrDie(cfg)) +} + +// Get extracts the versioned.Interface client from the context. +func Get(ctx context.Context) versioned.Interface { + untyped := ctx.Value(Key{}) + if untyped == nil { + if injection.GetConfig(ctx) == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned.Interface from context. This context is not the application context (which is typically given to constructors via sharedmain).") + } else { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned.Interface from context.") + } + } + return untyped.(versioned.Interface) +} diff --git a/cel/pkg/client/injection/client/fake/fake.go b/cel/pkg/client/injection/client/fake/fake.go new file mode 100644 index 000000000..e0a154177 --- /dev/null +++ b/cel/pkg/client/injection/client/fake/fake.go @@ -0,0 +1,54 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + fake "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/fake" + client "github.com/tektoncd/experimental/cel/pkg/client/injection/client" + runtime "k8s.io/apimachinery/pkg/runtime" + rest "k8s.io/client-go/rest" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Fake.RegisterClient(withClient) +} + +func withClient(ctx context.Context, cfg *rest.Config) context.Context { + ctx, _ = With(ctx) + return ctx +} + +func With(ctx context.Context, objects ...runtime.Object) (context.Context, *fake.Clientset) { + cs := fake.NewSimpleClientset(objects...) + return context.WithValue(ctx, client.Key{}, cs), cs +} + +// Get extracts the Kubernetes client from the context. +func Get(ctx context.Context) *fake.Clientset { + untyped := ctx.Value(client.Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/fake.Clientset from context.") + } + return untyped.(*fake.Clientset) +} diff --git a/cel/pkg/client/injection/informers/cel/v1alpha1/cel/cel.go b/cel/pkg/client/injection/informers/cel/v1alpha1/cel/cel.go new file mode 100644 index 000000000..7fbbf8284 --- /dev/null +++ b/cel/pkg/client/injection/informers/cel/v1alpha1/cel/cel.go @@ -0,0 +1,52 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package cel + +import ( + context "context" + + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions/cel/v1alpha1" + factory "github.com/tektoncd/experimental/cel/pkg/client/injection/informers/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformer(withInformer) +} + +// Key is used for associating the Informer inside the context.Context. +type Key struct{} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := factory.Get(ctx) + inf := f.Custom().V1alpha1().Cels() + return context.WithValue(ctx, Key{}, inf), inf.Informer() +} + +// Get extracts the typed informer from the context. +func Get(ctx context.Context) v1alpha1.CelInformer { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions/cel/v1alpha1.CelInformer from context.") + } + return untyped.(v1alpha1.CelInformer) +} diff --git a/cel/pkg/client/injection/informers/cel/v1alpha1/cel/fake/fake.go b/cel/pkg/client/injection/informers/cel/v1alpha1/cel/fake/fake.go new file mode 100644 index 000000000..a7c289851 --- /dev/null +++ b/cel/pkg/client/injection/informers/cel/v1alpha1/cel/fake/fake.go @@ -0,0 +1,40 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + cel "github.com/tektoncd/experimental/cel/pkg/client/injection/informers/cel/v1alpha1/cel" + fake "github.com/tektoncd/experimental/cel/pkg/client/injection/informers/factory/fake" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = cel.Get + +func init() { + injection.Fake.RegisterInformer(withInformer) +} + +func withInformer(ctx context.Context) (context.Context, controller.Informer) { + f := fake.Get(ctx) + inf := f.Custom().V1alpha1().Cels() + return context.WithValue(ctx, cel.Key{}, inf), inf.Informer() +} diff --git a/cel/pkg/client/injection/informers/factory/factory.go b/cel/pkg/client/injection/informers/factory/factory.go new file mode 100644 index 000000000..c4c64bcc9 --- /dev/null +++ b/cel/pkg/client/injection/informers/factory/factory.go @@ -0,0 +1,56 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package factory + +import ( + context "context" + + externalversions "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions" + client "github.com/tektoncd/experimental/cel/pkg/client/injection/client" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformerFactory(withInformerFactory) +} + +// Key is used as the key for associating information with a context.Context. +type Key struct{} + +func withInformerFactory(ctx context.Context) context.Context { + c := client.Get(ctx) + opts := make([]externalversions.SharedInformerOption, 0, 1) + if injection.HasNamespaceScope(ctx) { + opts = append(opts, externalversions.WithNamespace(injection.GetNamespaceScope(ctx))) + } + return context.WithValue(ctx, Key{}, + externalversions.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), opts...)) +} + +// Get extracts the InformerFactory from the context. +func Get(ctx context.Context) externalversions.SharedInformerFactory { + untyped := ctx.Value(Key{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions.SharedInformerFactory from context.") + } + return untyped.(externalversions.SharedInformerFactory) +} diff --git a/cel/pkg/client/injection/informers/factory/fake/fake.go b/cel/pkg/client/injection/informers/factory/fake/fake.go new file mode 100644 index 000000000..f499cc8ff --- /dev/null +++ b/cel/pkg/client/injection/informers/factory/fake/fake.go @@ -0,0 +1,45 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package fake + +import ( + context "context" + + externalversions "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions" + fake "github.com/tektoncd/experimental/cel/pkg/client/injection/client/fake" + factory "github.com/tektoncd/experimental/cel/pkg/client/injection/informers/factory" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" +) + +var Get = factory.Get + +func init() { + injection.Fake.RegisterInformerFactory(withInformerFactory) +} + +func withInformerFactory(ctx context.Context) context.Context { + c := fake.Get(ctx) + opts := make([]externalversions.SharedInformerOption, 0, 1) + if injection.HasNamespaceScope(ctx) { + opts = append(opts, externalversions.WithNamespace(injection.GetNamespaceScope(ctx))) + } + return context.WithValue(ctx, factory.Key{}, + externalversions.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), opts...)) +} diff --git a/cel/pkg/client/injection/informers/factory/filtered/fake/fake_filtered_factory.go b/cel/pkg/client/injection/informers/factory/filtered/fake/fake_filtered_factory.go new file mode 100644 index 000000000..954f74bcc --- /dev/null +++ b/cel/pkg/client/injection/informers/factory/filtered/fake/fake_filtered_factory.go @@ -0,0 +1,59 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package fakeFilteredFactory + +import ( + context "context" + + externalversions "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions" + fake "github.com/tektoncd/experimental/cel/pkg/client/injection/client/fake" + filtered "github.com/tektoncd/experimental/cel/pkg/client/injection/informers/factory/filtered" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +var Get = filtered.Get + +func init() { + injection.Fake.RegisterInformerFactory(withInformerFactory) +} + +func withInformerFactory(ctx context.Context) context.Context { + c := fake.Get(ctx) + opts := []externalversions.SharedInformerOption{} + if injection.HasNamespaceScope(ctx) { + opts = append(opts, externalversions.WithNamespace(injection.GetNamespaceScope(ctx))) + } + untyped := ctx.Value(filtered.LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + for _, selector := range labelSelectors { + thisOpts := append(opts, externalversions.WithTweakListOptions(func(l *v1.ListOptions) { + l.LabelSelector = selector + })) + ctx = context.WithValue(ctx, filtered.Key{Selector: selector}, + externalversions.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), thisOpts...)) + } + return ctx +} diff --git a/cel/pkg/client/injection/informers/factory/filtered/filtered_factory.go b/cel/pkg/client/injection/informers/factory/filtered/filtered_factory.go new file mode 100644 index 000000000..54c543257 --- /dev/null +++ b/cel/pkg/client/injection/informers/factory/filtered/filtered_factory.go @@ -0,0 +1,77 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package filteredFactory + +import ( + context "context" + + externalversions "github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions" + client "github.com/tektoncd/experimental/cel/pkg/client/injection/client" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + controller "knative.dev/pkg/controller" + injection "knative.dev/pkg/injection" + logging "knative.dev/pkg/logging" +) + +func init() { + injection.Default.RegisterInformerFactory(withInformerFactory) +} + +// Key is used as the key for associating information with a context.Context. +type Key struct { + Selector string +} + +type LabelKey struct{} + +func WithSelectors(ctx context.Context, selector ...string) context.Context { + return context.WithValue(ctx, LabelKey{}, selector) +} + +func withInformerFactory(ctx context.Context) context.Context { + c := client.Get(ctx) + opts := []externalversions.SharedInformerOption{} + if injection.HasNamespaceScope(ctx) { + opts = append(opts, externalversions.WithNamespace(injection.GetNamespaceScope(ctx))) + } + untyped := ctx.Value(LabelKey{}) + if untyped == nil { + logging.FromContext(ctx).Panic( + "Unable to fetch labelkey from context.") + } + labelSelectors := untyped.([]string) + for _, selector := range labelSelectors { + thisOpts := append(opts, externalversions.WithTweakListOptions(func(l *v1.ListOptions) { + l.LabelSelector = selector + })) + ctx = context.WithValue(ctx, Key{Selector: selector}, + externalversions.NewSharedInformerFactoryWithOptions(c, controller.GetResyncPeriod(ctx), thisOpts...)) + } + return ctx +} + +// Get extracts the InformerFactory from the context. +func Get(ctx context.Context, selector string) externalversions.SharedInformerFactory { + untyped := ctx.Value(Key{Selector: selector}) + if untyped == nil { + logging.FromContext(ctx).Panicf( + "Unable to fetch github.com/tektoncd/experimental/cel/pkg/client/informers/externalversions.SharedInformerFactory with selector %s from context.", selector) + } + return untyped.(externalversions.SharedInformerFactory) +} diff --git a/cel/pkg/client/listers/cel/v1alpha1/cel.go b/cel/pkg/client/listers/cel/v1alpha1/cel.go new file mode 100644 index 000000000..6efc16907 --- /dev/null +++ b/cel/pkg/client/listers/cel/v1alpha1/cel.go @@ -0,0 +1,94 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// CelLister helps list Cels. +type CelLister interface { + // List lists all Cels in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.Cel, err error) + // Cels returns an object that can list and get Cels. + Cels(namespace string) CelNamespaceLister + CelListerExpansion +} + +// celLister implements the CelLister interface. +type celLister struct { + indexer cache.Indexer +} + +// NewCelLister returns a new CelLister. +func NewCelLister(indexer cache.Indexer) CelLister { + return &celLister{indexer: indexer} +} + +// List lists all Cels in the indexer. +func (s *celLister) List(selector labels.Selector) (ret []*v1alpha1.Cel, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Cel)) + }) + return ret, err +} + +// Cels returns an object that can list and get Cels. +func (s *celLister) Cels(namespace string) CelNamespaceLister { + return celNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// CelNamespaceLister helps list and get Cels. +type CelNamespaceLister interface { + // List lists all Cels in the indexer for a given namespace. + List(selector labels.Selector) (ret []*v1alpha1.Cel, err error) + // Get retrieves the Cel from the indexer for a given namespace and name. + Get(name string) (*v1alpha1.Cel, error) + CelNamespaceListerExpansion +} + +// celNamespaceLister implements the CelNamespaceLister +// interface. +type celNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all Cels in the indexer for a given namespace. +func (s celNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Cel, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.Cel)) + }) + return ret, err +} + +// Get retrieves the Cel from the indexer for a given namespace and name. +func (s celNamespaceLister) Get(name string) (*v1alpha1.Cel, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("cel"), name) + } + return obj.(*v1alpha1.Cel), nil +} diff --git a/cel/pkg/client/listers/cel/v1alpha1/expansion_generated.go b/cel/pkg/client/listers/cel/v1alpha1/expansion_generated.go new file mode 100644 index 000000000..015ecfd21 --- /dev/null +++ b/cel/pkg/client/listers/cel/v1alpha1/expansion_generated.go @@ -0,0 +1,27 @@ +/* +Copyright 2021 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. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +// CelListerExpansion allows custom methods to be added to +// CelLister. +type CelListerExpansion interface{} + +// CelNamespaceListerExpansion allows custom methods to be added to +// CelNamespaceLister. +type CelNamespaceListerExpansion interface{} diff --git a/cel/pkg/reconciler/cel/celrun.go b/cel/pkg/reconciler/cel/celrun.go deleted file mode 100644 index 2d06db79b..000000000 --- a/cel/pkg/reconciler/cel/celrun.go +++ /dev/null @@ -1,162 +0,0 @@ -/* -Copyright 2021 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 cel - -import ( - "context" - "fmt" - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/tektoncd/pipeline/pkg/client/injection/reconciler/pipeline/v1alpha1/run" - "knative.dev/pkg/apis" - "knative.dev/pkg/logging" - - v1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" - v1 "k8s.io/api/core/v1" - reconciler "knative.dev/pkg/reconciler" -) - -const ( - // ReasonFailedValidation indicates that the reason for failure status is that Run failed runtime validation - ReasonFailedValidation = "RunValidationFailed" - - // ReasonSyntaxError indicates that the reason for failure status is that a CEL expression couldn't be parsed - ReasonSyntaxError = "SyntaxError" - - // ReasonEvaluationError indicates that the reason for failure status is that a CEL expression couldn't be evaluated - // typically due to evaluation environment or executable program - ReasonEvaluationError = "EvaluationError" - - // ReasonEvaluationSuccess indicates that the reason for the success status is that all CEL expressions were - // evaluated successfully and the results were produced - ReasonEvaluationSuccess = "EvaluationSuccess" -) - -// newReconciledNormal makes a new reconciler event with event type Normal, and reason RunReconciled. -func newReconciledNormal(namespace, name string) reconciler.Event { - return reconciler.NewEvent(v1.EventTypeNormal, "RunReconciled", "Run reconciled: \"%s/%s\"", namespace, name) -} - -// Reconciler implements controller.Reconciler for Run resources. -type Reconciler struct { -} - -// Check that our Reconciler implements Interface -var _ run.Interface = (*Reconciler)(nil) - -// ReconcileKind implements Interface.ReconcileKind. -func (r *Reconciler) ReconcileKind(ctx context.Context, run *v1alpha1.Run) reconciler.Event { - logger := logging.FromContext(ctx) - logger.Infof("Reconciling Run %s/%s", run.Namespace, run.Name) - - // If the Run has not started, initialize the Condition and set the start time. - if !run.HasStarted() { - logger.Infof("Starting new Run %s/%s", run.Namespace, run.Name) - run.Status.InitializeConditions() - // In case node time was not synchronized, when controller has been scheduled to other nodes. - if run.Status.StartTime.Sub(run.CreationTimestamp.Time) < 0 { - logger.Warnf("Run %s/%s createTimestamp %s is after the Run started %s", run.Namespace, run.Name, run.CreationTimestamp, run.Status.StartTime) - run.Status.StartTime = &run.CreationTimestamp - } - } - - if run.IsDone() { - logger.Infof("Run %s/%s is done", run.Namespace, run.Name) - return nil - } - - if err := validate(run); err != nil { - logger.Errorf("Run %s/%s is invalid because of %s", run.Namespace, run.Name, err) - run.Status.MarkRunFailed(ReasonFailedValidation, - "Run can't be run because it has an invalid spec - %v", err) - return nil - } - - // Create a program environment configured with the standard library of CEL functions and macros - env, err := cel.NewEnv(cel.Declarations()) - if err != nil { - logger.Errorf("Couldn't create a program env with standard library of CEL functions & macros when reconciling Run %s/%s: %v", run.Namespace, run.Name, err) - return err - } - - var runResults []v1alpha1.RunResult - for _, param := range run.Spec.Params { - // Combine the Parse and Check phases CEL program compilation to produce an Ast and associated issues - ast, iss := env.Compile(param.Value.StringVal) - if iss.Err() != nil { - logger.Errorf("CEL expression %s could not be parsed when reconciling Run %s/%s: %v", param.Name, run.Namespace, run.Name, iss.Err()) - run.Status.MarkRunFailed(ReasonSyntaxError, - "CEL expression %s could not be parsed", param.Name, iss.Err()) - return nil - } - - // Generate an evaluable instance of the Ast within the environment - prg, err := env.Program(ast) - if err != nil { - logger.Errorf("CEL expression %s could not be evaluated when reconciling Run %s/%s: %v", param.Name, run.Namespace, run.Name, err) - run.Status.MarkRunFailed(ReasonEvaluationError, - "CEL expression %s could not be evaluated", param.Name, err) - return nil - } - - // Evaluate the CEL expression (Ast) - out, _, err := prg.Eval(map[string]interface{}{}) - if err != nil { - logger.Errorf("CEL expression %s could not be evaluated when reconciling Run %s/%s: %v", param.Name, run.Namespace, run.Name, err) - run.Status.MarkRunFailed(ReasonEvaluationError, - "CEL expression %s could not be evaluated", param.Name, err) - return nil - } - - // Evaluation of CEL expression was successful - logger.Infof("CEL expression %s evaluated successfully when reconciling Run %s/%s", param.Name, run.Namespace, run.Name) - runResults = append(runResults, v1alpha1.RunResult{ - Name: param.Name, - Value: fmt.Sprintf("%s", out.ConvertToType(types.StringType).Value()), - }) - } - - // All CEL expressions were evaluated successfully - run.Status.Results = append(run.Status.Results, runResults...) - run.Status.MarkRunSucceeded(ReasonEvaluationSuccess, - "CEL expressions were evaluated successfully") - - return newReconciledNormal(run.Namespace, run.Name) -} - -func validate(run *v1alpha1.Run) (errs *apis.FieldError) { - errs = errs.Also(validateExpressionsProvided(run)) - errs = errs.Also(validateExpressionsType(run)) - return errs -} - -func validateExpressionsProvided(run *v1alpha1.Run) (errs *apis.FieldError) { - if len(run.Spec.Params) == 0 { - errs = errs.Also(apis.ErrMissingField("params")) - } - return errs -} - -func validateExpressionsType(run *v1alpha1.Run) (errs *apis.FieldError) { - for _, param := range run.Spec.Params { - if param.Value.StringVal == "" { - errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("CEL expression parameter %s must be a string", param.Name), - "value").ViaFieldKey("params", param.Name)) - } - } - return errs -} diff --git a/cel/pkg/reconciler/cel/celrun_test.go b/cel/pkg/reconciler/cel/celrun_test.go deleted file mode 100644 index 6ffa075ea..000000000 --- a/cel/pkg/reconciler/cel/celrun_test.go +++ /dev/null @@ -1,302 +0,0 @@ -/* -Copyright 2021 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 cel - -import ( - "context" - "fmt" - "github.com/google/go-cmp/cmp" - "github.com/tektoncd/experimental/cel/test" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" - ttesting "github.com/tektoncd/pipeline/pkg/reconciler/testing" - "github.com/tektoncd/pipeline/test/diff" - - "github.com/tektoncd/pipeline/test/names" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" - "knative.dev/pkg/apis" - "knative.dev/pkg/configmap" - "knative.dev/pkg/controller" - "knative.dev/pkg/logging" - "knative.dev/pkg/reconciler" - "strings" - "testing" - "time" -) - -func TestReconcileCelRun(t *testing.T) { - testcases := []struct { - name string - run *v1alpha1.Run - expectedStatus corev1.ConditionStatus - expectedReason string - expectedResults []v1alpha1.RunResult - expectedMessage string - expectedEvents []string - }{{ - name: "one expression successful", - run: &v1alpha1.Run{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cel-run", - Namespace: "foo", - Labels: map[string]string{ - "myTestLabel": "myTestLabelValue", - }, - Annotations: map[string]string{ - "myTestAnnotation": "myTestAnnotationValue", - }, - }, - Spec: v1alpha1.RunSpec{ - Params: []v1beta1.Param{{ - Name: "expr1", - Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "type(100)"}, - }}, - Ref: &v1alpha1.TaskRef{ - APIVersion: apiVersion, - Kind: kind, - Name: "a-celrun", - }, - }, - }, - expectedStatus: corev1.ConditionTrue, - expectedReason: ReasonEvaluationSuccess, - expectedMessage: "CEL expressions were evaluated successfully", - expectedResults: []v1alpha1.RunResult{{ - Name: "expr1", - Value: "int", - }}, - expectedEvents: []string{"Normal RunReconciled Run reconciled: \"foo/cel-run\""}, - }, { - name: "multiple expressions successful", - run: &v1alpha1.Run{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cel-run", - Namespace: "foo", - Labels: map[string]string{ - "myTestLabel": "myTestLabelValue", - }, - Annotations: map[string]string{ - "myTestAnnotation": "myTestAnnotationValue", - }, - }, - Spec: v1alpha1.RunSpec{ - Params: []v1beta1.Param{{ - Name: "expr1", - Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "type(100)"}, - }, { - Name: "expr2", - Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "3 == 3"}, - }}, - Ref: &v1alpha1.TaskRef{ - APIVersion: apiVersion, - Kind: kind, - Name: "a-celrun", - }, - }, - }, - expectedStatus: corev1.ConditionTrue, - expectedReason: ReasonEvaluationSuccess, - expectedMessage: "CEL expressions were evaluated successfully", - expectedResults: []v1alpha1.RunResult{{ - Name: "expr1", - Value: "int", - }, { - Name: "expr2", - Value: "true", - }}, - expectedEvents: []string{"Normal RunReconciled Run reconciled: \"foo/cel-run\""}, - }, { - name: "CEL expressions with invalid type", - run: &v1alpha1.Run{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cel-run", - Namespace: "foo", - Labels: map[string]string{ - "myTestLabel": "myTestLabelValue", - }, - Annotations: map[string]string{ - "myTestAnnotation": "myTestAnnotationValue", - }, - }, - Spec: v1alpha1.RunSpec{ - Params: []v1beta1.Param{{ - Name: "expr1", - Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"type(100)", "3 == 3"}}, - }}, - Ref: &v1alpha1.TaskRef{ - APIVersion: apiVersion, - Kind: kind, - Name: "a-celrun", - }, - }, - }, - expectedStatus: corev1.ConditionFalse, - expectedReason: ReasonFailedValidation, - expectedMessage: "Run can't be run because it has an invalid spec - invalid value: CEL expression parameter expr1 must be a string: params[expr1].value", - }, { - name: "missing CEL expressions", - run: &v1alpha1.Run{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cel-run", - Namespace: "foo", - Labels: map[string]string{ - "myTestLabel": "myTestLabelValue", - }, - Annotations: map[string]string{ - "myTestAnnotation": "myTestAnnotationValue", - }, - }, - Spec: v1alpha1.RunSpec{ - Ref: &v1alpha1.TaskRef{ - APIVersion: apiVersion, - Kind: kind, - Name: "a-celrun", - }, - }, - }, - expectedStatus: corev1.ConditionFalse, - expectedReason: ReasonFailedValidation, - expectedMessage: "Run can't be run because it has an invalid spec - missing field(s): params", - }} - for _, tc := range testcases { - t.Run(tc.name, func(t *testing.T) { - ctx := context.Background() - names.TestingSeed() - - d := test.Data{ - Runs: []*v1alpha1.Run{tc.run}, - } - - testAssets, _ := getCelController(t, d) - c := testAssets.Controller - clients := testAssets.Clients - - if err := c.Reconciler.Reconcile(ctx, getRunName(tc.run)); err != nil { - t.Fatalf("Error reconciling: %s", err) - } - - // Fetch the updated Run - reconciledRun, err := clients.Pipeline.TektonV1alpha1().Runs(tc.run.Namespace).Get(ctx, tc.run.Name, metav1.GetOptions{}) - if err != nil { - t.Fatalf("Error getting reconciled run from fake client: %s", err) - } - - // Verify that the Run has the expected status and reason - checkRunCondition(t, reconciledRun, tc.expectedStatus, tc.expectedReason, tc.expectedMessage) - - // Verify expected events were created - if err := checkEvents(testAssets.Recorder, tc.name, tc.expectedEvents); err != nil { - t.Errorf(err.Error()) - } - - // Verify the expected Results were produced - if d := cmp.Diff(tc.expectedResults, reconciledRun.Status.Results); d != "" { - t.Errorf("Status Results: %s", diff.PrintWantGot(d)) - } - - }) - } -} - -func getCelController(t *testing.T, d test.Data) (test.Assets, func()) { - ctx, _ := ttesting.SetupFakeContext(t) - ctx, cancel := context.WithCancel(ctx) - c, informers := test.SeedTestData(t, ctx, d) - - configMapWatcher := configmap.NewStaticWatcher() - //configMapWatcher := configmap.NewInformedWatcher(c.Kube, system.GetNamespace()) - ctl := NewController(ctx, configMapWatcher) - - if la, ok := ctl.Reconciler.(reconciler.LeaderAware); ok { - la.Promote(reconciler.UniversalBucket(), func(reconciler.Bucket, types.NamespacedName) {}) - } - if err := configMapWatcher.Start(ctx.Done()); err != nil { - t.Fatalf("error starting configmap watcher: %v", err) - } - - return test.Assets{ - Logger: logging.FromContext(ctx), - Controller: ctl, - Clients: c, - Informers: informers, - Recorder: controller.GetEventRecorder(ctx).(*record.FakeRecorder), - }, cancel -} - -func getRunName(run *v1alpha1.Run) string { - return strings.Join([]string{run.Namespace, run.Name}, "/") -} - -func checkRunCondition(t *testing.T, run *v1alpha1.Run, expectedStatus corev1.ConditionStatus, expectedReason string, expectedMessage string) { - condition := run.Status.GetCondition(apis.ConditionSucceeded) - if condition == nil { - t.Error("Condition missing in Run") - } else { - if condition.Status != expectedStatus { - t.Errorf("Expected Run status to be %v but was %v", expectedStatus, condition) - } - if condition.Reason != expectedReason { - t.Errorf("Expected reason to be %q but was %q", expectedReason, condition.Reason) - } - if condition.Message != expectedMessage { - t.Errorf("Expected message to be %q but was %q", expectedMessage, condition.Message) - } - } - if run.Status.StartTime == nil { - t.Errorf("Expected Run start time to be set but it wasn't") - } - if expectedStatus == corev1.ConditionUnknown { - if run.Status.CompletionTime != nil { - t.Errorf("Expected Run completion time to not be set but it was") - } - } else if run.Status.CompletionTime == nil { - t.Errorf("Expected Run completion time to be set but it wasn't") - } -} - -func checkEvents(fr *record.FakeRecorder, testName string, wantEvents []string) error { - // The fake recorder runs in a go routine, so the timeout is here to avoid waiting - // on the channel forever if fewer than expected events are received. - // We only hit the timeout in case of failure of the test, so the actual value - // of the timeout is not so relevant. It's only used when tests are going to fail. - timer := time.NewTimer(1 * time.Second) - foundEvents := []string{} - for ii := 0; ii < len(wantEvents)+1; ii++ { - // We loop over all the events that we expect. Once they are all received - // we exit the loop. If we never receive enough events, the timeout takes us - // out of the loop. - select { - case event := <-fr.Events: - foundEvents = append(foundEvents, event) - if ii > len(wantEvents)-1 { - return fmt.Errorf(`Received extra event "%s" for test "%s"`, event, testName) - } - wantEvent := wantEvents[ii] - if !(strings.HasPrefix(event, wantEvent)) { - return fmt.Errorf(`Expected event "%s" but got "%s" instead for test "%s"`, wantEvent, event, testName) - } - case <-timer.C: - if len(foundEvents) > len(wantEvents) { - return fmt.Errorf(`Received %d events but %d expected for test "%s". Found events: %#v`, len(foundEvents), len(wantEvents), testName, foundEvents) - } - } - } - return nil -} diff --git a/cel/pkg/reconciler/cel/controller.go b/cel/pkg/reconciler/cel/controller.go deleted file mode 100644 index 02b35f8ce..000000000 --- a/cel/pkg/reconciler/cel/controller.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright 2021 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 cel - -import ( - context "context" - run "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1alpha1/run" - v1alpha1run "github.com/tektoncd/pipeline/pkg/client/injection/reconciler/pipeline/v1alpha1/run" - tkncontroller "github.com/tektoncd/pipeline/pkg/controller" - "k8s.io/client-go/tools/cache" - configmap "knative.dev/pkg/configmap" - controller "knative.dev/pkg/controller" - logging "knative.dev/pkg/logging" -) - -const ( - ControllerName = "cel-controller" - apiVersion = "cel.tekton.dev/v1alpha1" - kind = "CEL" -) - -// NewController creates a Reconciler for Run and returns the result of NewImpl. -func NewController(ctx context.Context, cmw configmap.Watcher) *controller.Impl { - logger := logging.FromContext(ctx) - - runInformer := run.Get(ctx) - - r := &Reconciler{} - - impl := v1alpha1run.NewImpl(ctx, r, func(impl *controller.Impl) controller.Options { - return controller.Options{ - AgentName: ControllerName, - } - }) - - logger.Info("Setting up event handlers") - - runInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ - FilterFunc: tkncontroller.FilterRunRef(apiVersion, kind), - Handler: controller.HandleAll(impl.Enqueue), - }) - - return impl -} diff --git a/cel/pkg/reconciler/celrun/celrun.go b/cel/pkg/reconciler/celrun/celrun.go new file mode 100644 index 000000000..15909c2ac --- /dev/null +++ b/cel/pkg/reconciler/celrun/celrun.go @@ -0,0 +1,278 @@ +/* +Copyright 2021 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 celrun + +import ( + "context" + "encoding/json" + "fmt" + gocel "github.com/google/cel-go/cel" + "github.com/google/cel-go/checker/decls" + celtypes "github.com/google/cel-go/common/types" + "github.com/hashicorp/go-multierror" + "github.com/tektoncd/experimental/cel/pkg/apis/cel" + celv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + celclientset "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned" + listerscel "github.com/tektoncd/experimental/cel/pkg/client/listers/cel/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + clientset "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + "github.com/tektoncd/pipeline/pkg/client/injection/reconciler/pipeline/v1alpha1/run" + listersalpha "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/reconciler/events" + "go.uber.org/zap" + "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "knative.dev/pkg/apis" + "knative.dev/pkg/logging" + "knative.dev/pkg/reconciler" + "reflect" +) + +const ( + // celLabelKey is the label identifier for a Cel, which is added to the Run + celLabelKey = "/cel" +) + +// Reconciler implements controller.Reconciler for Run resources. +type Reconciler struct { + pipelineClientSet clientset.Interface + celClientSet celclientset.Interface + runLister listersalpha.RunLister + celLister listerscel.CelLister +} + +// Check that our Reconciler implements Interface +var _ run.Interface = (*Reconciler)(nil) + +// ReconcileKind implements Interface.ReconcileKind. +func (r *Reconciler) ReconcileKind(ctx context.Context, run *v1alpha1.Run) reconciler.Event { + var merr error + logger := logging.FromContext(ctx) + logger.Infof("Reconciling Run %s/%s", run.Namespace, run.Name) + + if !run.HasStarted() { + logger.Infof("Starting new Run %s/%s", run.Namespace, run.Name) + run.Status.InitializeConditions() + // In case node time was not synchronized, when controller has been scheduled to other nodes. + if run.Status.StartTime.Sub(run.CreationTimestamp.Time) < 0 { + logger.Warnf("Run %s/%s createTimestamp %s is after the Run started %s", run.Namespace, run.Name, run.CreationTimestamp, run.Status.StartTime) + run.Status.StartTime = &run.CreationTimestamp + } + afterCondition := run.Status.GetCondition(apis.ConditionSucceeded) + events.Emit(ctx, nil, afterCondition, run) + } + + if run.IsDone() { + logger.Infof("Run %s/%s is done", run.Namespace, run.Name) + return nil + } + + beforeCondition := run.Status.GetCondition(apis.ConditionSucceeded) + + status := &celv1alpha1.CelStatus{} + if err := run.Status.DecodeExtraFields(status); err != nil { + run.Status.MarkRunFailed(celv1alpha1.CelRunReasonInternalError.String(), + "Internal error calling DecodeExtraFields: %v", err) + logger.Errorf("DecodeExtraFields error: %v", err.Error()) + } + + if err := r.reconcile(ctx, run, status); err != nil { + logger.Errorf("Reconcile error: %v", err.Error()) + merr = multierror.Append(merr, err) + } + + if err := r.updateLabelsAndAnnotations(ctx, run); err != nil { + logger.Warn("Failed to update Run labels/annotations", zap.Error(err)) + merr = multierror.Append(merr, err) + } + + if err := run.Status.EncodeExtraFields(status); err != nil { + run.Status.MarkRunFailed(celv1alpha1.CelRunReasonCouldntGetCel.String(), + "Internal error calling EncodeExtraFields: %v", err) + logger.Errorf("EncodeExtraFields error: %v", err.Error()) + } + + afterCondition := run.Status.GetCondition(apis.ConditionSucceeded) + events.Emit(ctx, beforeCondition, afterCondition, run) + + // Only transient errors that should retry the reconcile are returned. + return merr +} + +func (c *Reconciler) reconcile(ctx context.Context, run *v1alpha1.Run, status *celv1alpha1.CelStatus) error { + logger := logging.FromContext(ctx) + + celMeta, celSpec, err := c.getCel(ctx, run) + if err != nil { + return nil + } + + storeCelSpec(status, celSpec) + + propagateCelLabelsAndAnnotations(run, celMeta) + + if err := celSpec.Validate(); err != nil { + logger.Errorf("Run %s/%s is invalid because of %s", run.Namespace, run.Name, err) + run.Status.MarkRunFailed(celv1alpha1.CelRunReasonFailedValidation.String(), + "Run can't be run because it has an invalid spec - %v", err) + return nil + } + + variablesMap := getVariablesMap(celSpec.Variables) + // Create a program environment configured with the standard library of CEL functions and macros + env, err := gocel.NewEnv(gocel.Declarations(getCelEnvironmentDeclarations(variablesMap)...)) + if err != nil { + logger.Errorf("Couldn't create a program env with standard library of CEL functions & macros when reconciling Run %s/%s: %v", run.Namespace, run.Name, err) + return err + } + + for _, expression := range celSpec.Expressions { + // Combine the Parse and Check phases CEL program compilation to produce an Ast and associated issues + ast, iss := env.Compile(expression.Value.StringVal) + if iss.Err() != nil { + logger.Errorf("CEL expression %s could not be parsed when reconciling Run %s/%s: %v", expression.Name, run.Namespace, run.Name, iss.Err()) + run.Status.MarkRunFailed(celv1alpha1.CelRunReasonSyntaxError.String(), + "CEL expression %s could not be parsed", expression.Name, iss.Err()) + return nil + } + + // Generate an evaluable instance of the Ast within the environment + prg, err := env.Program(ast) + if err != nil { + logger.Errorf("CEL expression %s could not be evaluated when reconciling Run %s/%s: %v", expression.Name, run.Namespace, run.Name, err) + run.Status.MarkRunFailed(celv1alpha1.CelRunReasonEvaluationError.String(), + "CEL expression %s could not be evaluated", expression.Name, err) + return nil + } + + // Evaluate the CEL expression (Ast) + variablesMap := getVariablesMap(celSpec.Variables) + out, _, err := prg.Eval(variablesMap) + if err != nil { + logger.Errorf("CEL expression %s could not be evaluated when reconciling Run %s/%s: %v", expression.Name, run.Namespace, run.Name, err) + run.Status.MarkRunFailed(celv1alpha1.CelRunReasonEvaluationError.String(), + "CEL expression %s could not be evaluated", expression.Name, err) + return nil + } + + // Evaluation of CEL expression was successful + logger.Infof("CEL expression %s evaluated successfully when reconciling Run %s/%s", expression.Name, run.Namespace, run.Name) + status.CelResults = append(status.CelResults, v1alpha1.RunResult{ + Name: expression.Name, + Value: fmt.Sprintf("%s", out.ConvertToType(celtypes.StringType).Value()), + }) + } + + // All CEL expressions were evaluated successfully + run.Status.Results = append(run.Status.Results, status.CelResults...) + run.Status.MarkRunSucceeded(celv1alpha1.CelRunReasonEvaluationSuccess.String(), + "CEL expressions were evaluated successfully") + + return nil +} + +func (c *Reconciler) getCel(ctx context.Context, run *v1alpha1.Run) (*metav1.ObjectMeta, *celv1alpha1.CelSpec, error) { + celMeta := metav1.ObjectMeta{} + celSpec := celv1alpha1.CelSpec{} + if run.Spec.Ref != nil && run.Spec.Ref.Name != "" { + // Use the k8 client to get the Cel rather than the lister. This avoids a timing issue where + // the Cel is not yet in the lister cache if it is created at nearly the same time as the Run. + // See https://github.com/tektoncd/pipeline/issues/2740 for discussion on this issue. + cs, err := c.celClientSet.CustomV1alpha1().Cels(run.Namespace).Get(ctx, run.Spec.Ref.Name, metav1.GetOptions{}) + if err != nil { + run.Status.MarkRunFailed(celv1alpha1.CelRunReasonCouldntGetCel.String(), + "Error retrieving Cel for Run %s/%s: %s", + run.Namespace, run.Name, err) + return nil, nil, fmt.Errorf("error retrieving Cel for Run %s: %w", fmt.Sprintf("%s/%s", run.Namespace, run.Name), err) + } + celMeta = cs.ObjectMeta + celSpec = cs.Spec + } else { + // Run does not require name but for Cel it does + run.Status.MarkRunFailed(celv1alpha1.CelRunReasonCouldntGetCel.String(), + "Missing spec.ref.name for Run %s/%s", + run.Namespace, run.Name) + return nil, nil, fmt.Errorf("missing spec.ref.name for Run %s", fmt.Sprintf("%s/%s", run.Namespace, run.Name)) + } + return &celMeta, &celSpec, nil +} + +func storeCelSpec(status *celv1alpha1.CelStatus, cs *celv1alpha1.CelSpec) { + // Only store the CelSpec once, if it has never been set before + if status.CelSpec == nil { + status.CelSpec = cs + } +} + +func propagateCelLabelsAndAnnotations(run *v1alpha1.Run, celMeta *metav1.ObjectMeta) { + // Propagate labels from Cel to Run + if run.ObjectMeta.Labels == nil { + run.ObjectMeta.Labels = make(map[string]string, len(celMeta.Labels)+1) + } + for key, value := range celMeta.Labels { + run.ObjectMeta.Labels[key] = value + } + run.ObjectMeta.Labels[cel.GroupName+celLabelKey] = celMeta.Name + + // Propagate annotations from Cel to Run + if run.ObjectMeta.Annotations == nil { + run.ObjectMeta.Annotations = make(map[string]string, len(celMeta.Annotations)) + } + for key, value := range celMeta.Annotations { + run.ObjectMeta.Annotations[key] = value + } +} + +func (r *Reconciler) updateLabelsAndAnnotations(ctx context.Context, run *v1alpha1.Run) error { + newRun, err := r.runLister.Runs(run.Namespace).Get(run.Name) + if err != nil { + return fmt.Errorf("error getting Run %s when updating labels/annotations: %w", run.Name, err) + } + if !reflect.DeepEqual(run.ObjectMeta.Labels, newRun.ObjectMeta.Labels) || !reflect.DeepEqual(run.ObjectMeta.Annotations, newRun.ObjectMeta.Annotations) { + mergePatch := map[string]interface{}{ + "metadata": map[string]interface{}{ + "labels": run.ObjectMeta.Labels, + "annotations": run.ObjectMeta.Annotations, + }, + } + patch, err := json.Marshal(mergePatch) + if err != nil { + return err + } + _, err = r.pipelineClientSet.TektonV1alpha1().Runs(run.Namespace).Patch(ctx, run.Name, types.MergePatchType, patch, metav1.PatchOptions{}) + return err + } + return nil +} + +func getVariablesMap(variables []*v1beta1.Param) map[string]interface{} { + variablesMap := make(map[string]interface{}) + for _, variable := range variables { + variablesMap[variable.Name] = variable.Value.StringVal + } + return variablesMap +} + +func getCelEnvironmentDeclarations(variablesMap map[string]interface{}) []*expr.Decl { + var celEnvironmentDeclarations []*expr.Decl + for variableName := range variablesMap { + celEnvironmentDeclarations = append(celEnvironmentDeclarations, decls.NewVar(variableName, decls.String)) + } + return celEnvironmentDeclarations +} diff --git a/cel/pkg/reconciler/celrun/celrun_test.go b/cel/pkg/reconciler/celrun/celrun_test.go new file mode 100644 index 000000000..b98fe6f46 --- /dev/null +++ b/cel/pkg/reconciler/celrun/celrun_test.go @@ -0,0 +1,452 @@ +/* +Copyright 2021 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 celrun + +import ( + "context" + "fmt" + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/experimental/cel/pkg/apis/cel" + celv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + fakeclient "github.com/tektoncd/experimental/cel/pkg/client/injection/client/fake" + fakecelinformer "github.com/tektoncd/experimental/cel/pkg/client/injection/informers/cel/v1alpha1/cel/fake" + "github.com/tektoncd/experimental/cel/test" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + ttesting "github.com/tektoncd/pipeline/pkg/reconciler/testing" + "github.com/tektoncd/pipeline/test/diff" + cminformer "knative.dev/pkg/configmap/informer" + + "github.com/tektoncd/pipeline/test/names" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + "knative.dev/pkg/apis" + "knative.dev/pkg/controller" + "knative.dev/pkg/logging" + "knative.dev/pkg/reconciler" + "knative.dev/pkg/system" + _ "knative.dev/pkg/system/testing" + "strings" + "testing" + "time" +) + +var ( + namespace = "" +) + +func TestReconcileCelRun(t *testing.T) { + testcases := []struct { + name string + cel *celv1alpha1.Cel + run *v1alpha1.Run + expectedStatus corev1.ConditionStatus + expectedReason string + expectedResults []v1alpha1.RunResult + expectedMessage string + expectedEvents []string + }{{ + name: "one expression successful", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "type(100)"}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionTrue, + expectedReason: celv1alpha1.CelRunReasonEvaluationSuccess.String(), + expectedMessage: "CEL expressions were evaluated successfully", + expectedResults: []v1alpha1.RunResult{{ + Name: "expr1", + Value: "int", + }}, + expectedEvents: []string{ + "Normal Started", + "Normal Succeeded CEL expressions were evaluated successfully", + }, + }, { + name: "multiple expressions successful", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "type(100)"}, + }, { + Name: "expr2", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "3 == 3"}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionTrue, + expectedReason: celv1alpha1.CelRunReasonEvaluationSuccess.String(), + expectedMessage: "CEL expressions were evaluated successfully", + expectedResults: []v1alpha1.RunResult{{ + Name: "expr1", + Value: "int", + }, { + Name: "expr2", + Value: "true", + }}, + expectedEvents: []string{ + "Normal Started", + "Normal Succeeded CEL expressions were evaluated successfully", + }, + }, { + name: "one expression and one variable successful", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Variables: []*v1beta1.Param{{ + Name: "var1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "foo"}, + }}, + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "var1 in ['foo', 'bar']"}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionTrue, + expectedReason: celv1alpha1.CelRunReasonEvaluationSuccess.String(), + expectedMessage: "CEL expressions were evaluated successfully", + expectedResults: []v1alpha1.RunResult{{ + Name: "expr1", + Value: "true", + }}, + expectedEvents: []string{ + "Normal Started", + "Normal Succeeded CEL expressions were evaluated successfully", + }, + }, { + name: "multiple expressions and multiple variable successful", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Variables: []*v1beta1.Param{{ + Name: "var1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "foo"}, + }, { + Name: "var2", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "bar"}, + }}, + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "'foo' in [var1, var2]"}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionTrue, + expectedReason: celv1alpha1.CelRunReasonEvaluationSuccess.String(), + expectedMessage: "CEL expressions were evaluated successfully", + expectedResults: []v1alpha1.RunResult{{ + Name: "expr1", + Value: "true", + }}, + expectedEvents: []string{ + "Normal Started", + "Normal Succeeded CEL expressions were evaluated successfully", + }, + }, { + name: "CEL expressions with invalid type", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeArray, ArrayVal: []string{"type(100)", "3 == 3"}}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionFalse, + expectedReason: celv1alpha1.CelRunReasonFailedValidation.String(), + expectedMessage: "Run can't be run because it has an invalid spec - invalid value: CEL expression expr1 must be a string: expressions[expr1].value", + expectedEvents: []string{ + "Normal Started", + "Warning Failed Run can't be run because it has an invalid spec - invalid value: CEL expression expr1 must be a string: expressions[expr1].value", + }, + }, { + name: "missing CEL expressions", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionFalse, + expectedReason: celv1alpha1.CelRunReasonFailedValidation.String(), + expectedMessage: "Run can't be run because it has an invalid spec - missing field(s): expressions", + expectedEvents: []string{ + "Normal Started", + "Warning Failed Run can't be run because it has an invalid spec - missing field(s): expressions", + }, + }} + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + names.TestingSeed() + + d := test.Data{ + Runs: []*v1alpha1.Run{tc.run}, + } + + testAssets, _ := getCelController(t, d, []*celv1alpha1.Cel{tc.cel}) + c := testAssets.Controller + clients := testAssets.Clients + + if err := c.Reconciler.Reconcile(ctx, getRunName(tc.run)); err != nil { + t.Fatalf("Error reconciling: %s", err) + } + + // Fetch the updated Run + reconciledRun, err := clients.Pipeline.TektonV1alpha1().Runs(tc.run.Namespace).Get(ctx, tc.run.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Error getting reconciled run from fake client: %s", err) + } + + // Verify that the Run has the expected status and reason + checkRunCondition(t, reconciledRun, tc.expectedStatus, tc.expectedReason, tc.expectedMessage) + + // Verify expected events were created + if err := checkEvents(testAssets.Recorder, tc.name, tc.expectedEvents); err != nil { + t.Errorf(err.Error()) + } + + // Verify the expected Results were produced + if d := cmp.Diff(tc.expectedResults, reconciledRun.Status.Results); d != "" { + t.Errorf("Status Results: %s", diff.PrintWantGot(d)) + } + + }) + } +} + +func getCelController(t *testing.T, d test.Data, cels []*celv1alpha1.Cel) (test.Assets, func()) { + ctx, _ := ttesting.SetupFakeContext(t) + ctx, cancel := context.WithCancel(ctx) + c, informers := test.SeedTestData(t, ctx, d) + + client := fakeclient.Get(ctx) + client.PrependReactor("*", "cels", test.AddToInformer(t, fakecelinformer.Get(ctx).Informer().GetIndexer())) + for _, cel := range cels { + cel := cel.DeepCopy() // Avoid assumptions that the informer's copy is modified. + if _, err := client.CustomV1alpha1().Cels(cel.Namespace).Create(ctx, cel, metav1.CreateOptions{}); err != nil { + t.Fatal(err) + } + } + + configMapWatcher := cminformer.NewInformedWatcher(c.Kube, system.Namespace()) + ctl := NewController(namespace)(ctx, configMapWatcher) + + if la, ok := ctl.Reconciler.(reconciler.LeaderAware); ok { + la.Promote(reconciler.UniversalBucket(), func(reconciler.Bucket, types.NamespacedName) {}) + } + if err := configMapWatcher.Start(ctx.Done()); err != nil { + t.Fatalf("error starting configmap watcher: %v", err) + } + + return test.Assets{ + Logger: logging.FromContext(ctx), + Controller: ctl, + Clients: c, + Informers: informers, + Recorder: controller.GetEventRecorder(ctx).(*record.FakeRecorder), + }, cancel +} + +func getRunName(run *v1alpha1.Run) string { + return strings.Join([]string{run.Namespace, run.Name}, "/") +} + +func checkRunCondition(t *testing.T, run *v1alpha1.Run, expectedStatus corev1.ConditionStatus, expectedReason string, expectedMessage string) { + condition := run.Status.GetCondition(apis.ConditionSucceeded) + if condition == nil { + t.Error("Condition missing in Run") + } else { + if condition.Status != expectedStatus { + t.Errorf("Expected Run status to be %v but was %v", expectedStatus, condition) + } + if condition.Reason != expectedReason { + t.Errorf("Expected reason to be %q but was %q", expectedReason, condition.Reason) + } + if condition.Message != expectedMessage { + t.Errorf("Expected message to be %q but was %q", expectedMessage, condition.Message) + } + } + if run.Status.StartTime == nil { + t.Errorf("Expected Run start time to be set but it wasn't") + } + if expectedStatus == corev1.ConditionUnknown { + if run.Status.CompletionTime != nil { + t.Errorf("Expected Run completion time to not be set but it was") + } + } else if run.Status.CompletionTime == nil { + t.Errorf("Expected Run completion time to be set but it wasn't") + } +} + +func checkEvents(fr *record.FakeRecorder, testName string, wantEvents []string) error { + // The fake recorder runs in a go routine, so the timeout is here to avoid waiting + // on the channel forever if fewer than expected events are received. + // We only hit the timeout in case of failure of the test, so the actual value + // of the timeout is not so relevant. It's only used when tests are going to fail. + timer := time.NewTimer(1 * time.Second) + foundEvents := []string{} + for ii := 0; ii < len(wantEvents)+1; ii++ { + // We loop over all the events that we expect. Once they are all received + // we exit the loop. If we never receive enough events, the timeout takes us + // out of the loop. + select { + case event := <-fr.Events: + foundEvents = append(foundEvents, event) + if ii > len(wantEvents)-1 { + return fmt.Errorf(`Received extra event "%s" for test "%s"`, event, testName) + } + wantEvent := wantEvents[ii] + if !(strings.HasPrefix(event, wantEvent)) { + return fmt.Errorf(`Expected event "%s" but got "%s" instead for test "%s"`, wantEvent, event, testName) + } + case <-timer.C: + if len(foundEvents) > len(wantEvents) { + return fmt.Errorf(`Received %d events but %d expected for test "%s". Found events: %#v`, len(foundEvents), len(wantEvents), testName, foundEvents) + } + } + } + return nil +} diff --git a/cel/pkg/reconciler/celrun/controller.go b/cel/pkg/reconciler/celrun/controller.go new file mode 100644 index 000000000..4a3defcc9 --- /dev/null +++ b/cel/pkg/reconciler/celrun/controller.go @@ -0,0 +1,64 @@ +/* +Copyright 2021 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 celrun + +import ( + "context" + "github.com/tektoncd/experimental/cel/pkg/apis/cel" + celv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + celclient "github.com/tektoncd/experimental/cel/pkg/client/injection/client" + celinformer "github.com/tektoncd/experimental/cel/pkg/client/injection/informers/cel/v1alpha1/cel" + pipelineclient "github.com/tektoncd/pipeline/pkg/client/injection/client" + runinformer "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1alpha1/run" + runreconciler "github.com/tektoncd/pipeline/pkg/client/injection/reconciler/pipeline/v1alpha1/run" + pipelinecontroller "github.com/tektoncd/pipeline/pkg/controller" + "k8s.io/client-go/tools/cache" + "knative.dev/pkg/configmap" + "knative.dev/pkg/controller" +) + +// NewController instantiates a new controller.Impl from knative.dev/pkg/controller +func NewController(namespace string) func(context.Context, configmap.Watcher) *controller.Impl { + return func(ctx context.Context, cmw configmap.Watcher) *controller.Impl { + + pipelineclientset := pipelineclient.Get(ctx) + celclientset := celclient.Get(ctx) + runInformer := runinformer.Get(ctx) + celInformer := celinformer.Get(ctx) + + r := &Reconciler{ + pipelineClientSet: pipelineclientset, + celClientSet: celclientset, + runLister: runInformer.Lister(), + celLister: celInformer.Lister(), + } + + impl := runreconciler.NewImpl(ctx, r, func(impl *controller.Impl) controller.Options { + return controller.Options{ + AgentName: cel.ControllerName, + } + }) + + // Add event handler for Runs + runInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{ + FilterFunc: pipelinecontroller.FilterRunRef(celv1alpha1.SchemeGroupVersion.String(), cel.ControllerName), + Handler: controller.HandleAll(impl.Enqueue), + }) + + return impl + } +} diff --git a/cel/test/build_logs.go b/cel/test/build_logs.go new file mode 100644 index 000000000..74b190540 --- /dev/null +++ b/cel/test/build_logs.go @@ -0,0 +1,74 @@ +/* +Copyright 2019 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 test + +import ( + "context" + "fmt" + "io/ioutil" + "strings" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "knative.dev/pkg/test/logging" +) + +// CollectPodLogs will get the logs for all containers in a Pod +func CollectPodLogs(ctx context.Context, c *clients, podName, namespace string, logf logging.FormatLogger) { + logs, err := getContainersLogsFromPod(ctx, c.KubeClient, podName, namespace) + if err != nil { + logf("Could not get logs for pod %s: %s", podName, err) + } + logf("build logs %s", logs) +} + +func getContainersLogsFromPod(ctx context.Context, c kubernetes.Interface, pod, namespace string) (string, error) { + p, err := c.CoreV1().Pods(namespace).Get(ctx, pod, metav1.GetOptions{}) + if err != nil { + return "", err + } + + sb := strings.Builder{} + for _, container := range p.Spec.Containers { + sb.WriteString(fmt.Sprintf("\n>>> Container %s:\n", container.Name)) + logs, err := getContainerLogsFromPod(ctx, c, pod, container.Name, namespace) + if err != nil { + return "", err + } + sb.WriteString(logs) + } + return sb.String(), nil +} + +func getContainerLogsFromPod(ctx context.Context, c kubernetes.Interface, pod, container, namespace string) (string, error) { + sb := strings.Builder{} + // Do not follow, which will block until the Pod terminates, and potentially deadlock the test. + // If done in the wrong order, this could actually block things and prevent the Pod from being + // deleted at all. + req := c.CoreV1().Pods(namespace).GetLogs(pod, &corev1.PodLogOptions{Follow: false, Container: container}) + rc, err := req.Stream(ctx) + if err != nil { + return "", err + } + bs, err := ioutil.ReadAll(rc) + if err != nil { + return "", err + } + sb.Write(bs) + return sb.String(), nil +} diff --git a/cel/test/celrun_test.go b/cel/test/celrun_test.go new file mode 100644 index 000000000..fe055c220 --- /dev/null +++ b/cel/test/celrun_test.go @@ -0,0 +1,420 @@ +// +build e2e + +/* +Copyright 2021 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 test + +import ( + "context" + "github.com/google/go-cmp/cmp" + "github.com/tektoncd/experimental/cel/pkg/apis/cel" + celv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1" + "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned" + resourceversioned "github.com/tektoncd/experimental/cel/pkg/client/clientset/versioned/typed/cel/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/tektoncd/pipeline/test/diff" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/apis" + knativetest "knative.dev/pkg/test" + "regexp" + "testing" + "time" +) + +var ( + runTimeout = 10 * time.Minute +) + +// Expected events can be required or optional +type ev struct { + message string + required bool +} + +func TestCelRun(t *testing.T) { + t.Parallel() + + testcases := []struct { + name string + cel *celv1alpha1.Cel + run *v1alpha1.Run + expectedStatus corev1.ConditionStatus + expectedReason string + expectedResults []v1alpha1.RunResult + expectedMessage string + expectedEvents []ev + }{{ + name: "one expression successful", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "type(100)"}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionTrue, + expectedReason: celv1alpha1.CelRunReasonEvaluationSuccess.String(), + expectedMessage: "CEL expressions were evaluated successfully", + expectedResults: []v1alpha1.RunResult{{ + Name: "expr1", + Value: "int", + }}, + expectedEvents: []ev{ + {"", true}, + {"CEL expressions were evaluated successfully", true}, + }, + }, { + name: "multiple expressions successful", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "type(100)"}, + }, { + Name: "expr2", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "3 == 3"}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionTrue, + expectedReason: celv1alpha1.CelRunReasonEvaluationSuccess.String(), + expectedMessage: "CEL expressions were evaluated successfully", + expectedResults: []v1alpha1.RunResult{{ + Name: "expr1", + Value: "int", + }, { + Name: "expr2", + Value: "true", + }}, + expectedEvents: []ev{ + {"", true}, + {"CEL expressions were evaluated successfully", true}, + }, + }, { + name: "one expression and one variable successful", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Variables: []*v1beta1.Param{{ + Name: "var1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "foo"}, + }}, + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "var1 in ['foo', 'bar']"}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionTrue, + expectedReason: celv1alpha1.CelRunReasonEvaluationSuccess.String(), + expectedMessage: "CEL expressions were evaluated successfully", + expectedResults: []v1alpha1.RunResult{{ + Name: "expr1", + Value: "true", + }}, + expectedEvents: []ev{ + {"", true}, + {"CEL expressions were evaluated successfully", true}, + }, + }, { + name: "multiple expressions and multiple variable successful", + cel: &celv1alpha1.Cel{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel", + Namespace: "foo", + Labels: map[string]string{"myCelLabel": "myCelLabelValue"}, + Annotations: map[string]string{"myCelAnnotation": "myCelAnnotationValue"}, + }, + Spec: celv1alpha1.CelSpec{ + Variables: []*v1beta1.Param{{ + Name: "var1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "foo"}, + }, { + Name: "var2", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "bar"}, + }}, + Expressions: []*v1beta1.Param{{ + Name: "expr1", + Value: v1beta1.ArrayOrString{Type: v1beta1.ParamTypeString, StringVal: "'foo' in [var1, var2]"}, + }}, + }, + }, + run: &v1alpha1.Run{ + ObjectMeta: metav1.ObjectMeta{ + Name: "a-cel-run", + Namespace: "foo", + Labels: map[string]string{"myTestLabel": "myTestLabelValue"}, + Annotations: map[string]string{"myTestAnnotation": "myTestAnnotationValue"}, + }, + Spec: v1alpha1.RunSpec{ + Ref: &v1alpha1.TaskRef{ + APIVersion: celv1alpha1.SchemeGroupVersion.String(), + Kind: cel.ControllerName, + Name: "a-cel", + }, + }, + }, + expectedStatus: corev1.ConditionTrue, + expectedReason: celv1alpha1.CelRunReasonEvaluationSuccess.String(), + expectedMessage: "CEL expressions were evaluated successfully", + expectedResults: []v1alpha1.RunResult{{ + Name: "expr1", + Value: "true", + }}, + expectedEvents: []ev{ + {"", true}, + {"CEL expressions were evaluated successfully", true}, + }, + }} + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + tc := tc // Copy current tc to local variable due to test parallelization + t.Parallel() + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + c, namespace := setup(ctx, t) + celClient := getCelClient(t, namespace) + + knativetest.CleanupOnInterrupt(func() { tearDown(ctx, t, c, namespace) }, t.Logf) + defer tearDown(ctx, t, c, namespace) + + if tc.cel != nil { + cel := tc.cel.DeepCopy() + cel.Namespace = namespace + if _, err := celClient.Create(ctx, cel, metav1.CreateOptions{}); err != nil { + t.Fatalf("Failed to create Cel `%s`: %s", tc.cel.Name, err) + } + } + + run := tc.run.DeepCopy() + run.Namespace = namespace + run, err := c.RunClient.Create(ctx, run, metav1.CreateOptions{}) + if err != nil { + t.Fatalf("Failed to create Run `%s`: %s", run.Name, err) + } + + t.Logf("Waiting for Run %s in namespace %s to complete", run.Name, run.Namespace) + var inState ConditionAccessorFn + var desc string + if tc.expectedStatus == corev1.ConditionTrue { + inState = Succeed(run.Name) + desc = "RunSuccess" + } else { + inState = FailedWithReason(tc.expectedReason, run.Name) + desc = "RunFailed" + } + if err := WaitForRunState(ctx, c, run.Name, runTimeout, inState, desc); err != nil { + t.Fatalf("Error waiting for Run %s/%s to finish: %s", run.Namespace, run.Name, err) + } + + t.Logf("Run %s in namespace %s completed - fetching it", run.Name, run.Namespace) + run, err = c.RunClient.Get(ctx, run.Name, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Couldn't get expected Run %s/%s: %s", run.Namespace, run.Name, err) + } + + t.Logf("Checking Cel status in the Run status") + status := &celv1alpha1.CelStatus{} + if err := run.Status.DecodeExtraFields(status); err != nil { + t.Errorf("DecodeExtraFields error: %v", err.Error()) + } + + t.Logf("Checking the status, reason and message in the Run's ConditionSucceeded") + checkRunCondition(t, run, tc.expectedStatus, tc.expectedReason, tc.expectedMessage) + + t.Logf("Verifying the expected results are produced") + if d := cmp.Diff(tc.expectedResults, run.Status.Results); d != "" { + t.Errorf("Status Results: %s", diff.PrintWantGot(d)) + } + + t.Logf("Checking events that were created from Run") + checkEvents(ctx, t, c, run, namespace, tc.expectedEvents) + }) + } +} + +func getCelClient(t *testing.T, namespace string) resourceversioned.CelInterface { + configPath := knativetest.Flags.Kubeconfig + clusterName := knativetest.Flags.Cluster + cfg, err := knativetest.BuildClientConfig(configPath, clusterName) + if err != nil { + t.Fatalf("failed to create configuration obj from %s for cluster %s: %s", configPath, clusterName, err) + } + cs, err := versioned.NewForConfig(cfg) + if err != nil { + t.Fatalf("failed to create cel clientset from config file at %s: %s", configPath, err) + } + return cs.CustomV1alpha1().Cels(namespace) +} + +func checkRunCondition(t *testing.T, run *v1alpha1.Run, expectedStatus corev1.ConditionStatus, expectedReason string, expectedMessage string) { + condition := run.Status.GetCondition(apis.ConditionSucceeded) + if condition == nil { + t.Error("Condition missing in Run") + } else { + if condition.Status != expectedStatus { + t.Errorf("Expected Run status to be %v but was %v", expectedStatus, condition) + } + if condition.Reason != expectedReason { + t.Errorf("Expected reason to be %q but was %q", expectedReason, condition.Reason) + } + if condition.Message != expectedMessage { + t.Errorf("Expected message to be %q but was %q", expectedMessage, condition.Message) + } + } + if run.Status.StartTime == nil { + t.Errorf("Expected Run start time to be set but it wasn't") + } + if expectedStatus == corev1.ConditionUnknown { + if run.Status.CompletionTime != nil { + t.Errorf("Expected Run completion time to not be set but it was") + } + } else if run.Status.CompletionTime == nil { + t.Errorf("Expected Run completion time to be set but it wasn't") + } +} + +func checkEvents(ctx context.Context, t *testing.T, c *clients, run *v1alpha1.Run, namespace string, expectedEvents []ev) { + matchKinds := map[string][]string{"Run": {run.Name}} + events, err := collectMatchingEvents(ctx, c.KubeClient, namespace, matchKinds) + if err != nil { + t.Fatalf("Failed to collect matching events: %q", err) + } + // Log the received events. + receivedEvents := make([]string, 0, len(events)) + for _, receivedEvent := range events { + receivedEvents = append(receivedEvents, receivedEvent.Message) + } + t.Logf("Received events: %q", receivedEvents) + // In the concurrency scenarios some events may or may not happen based on timing. + e := 0 + for _, expectedEvent := range expectedEvents { + if e >= len(events) { + if !expectedEvent.required { + continue + } + t.Errorf("Did not get expected event %q", expectedEvent.message) + continue + } + if matched, _ := regexp.MatchString(expectedEvent.message, events[e].Message); !matched { + if !expectedEvent.required { + continue + } + t.Errorf("Expected event %q but got %q", expectedEvent.message, events[e].Message) + } + e++ + } +} + +// collectMatchingEvents collects a list of events under 5 seconds that match certain objects by kind and name. +// This is copied from pipelinerun_test and modified to drop the reason parameter. +func collectMatchingEvents(ctx context.Context, kubeClient *knativetest.KubeClient, namespace string, kinds map[string][]string) ([]*corev1.Event, error) { + var events []*corev1.Event + + watchEvents, err := kubeClient.CoreV1().Events(namespace).Watch(ctx, metav1.ListOptions{}) + // close watchEvents channel + defer watchEvents.Stop() + if err != nil { + return events, err + } + + // create timer to not wait for events longer than 5 seconds + timer := time.NewTimer(5 * time.Second) + + for { + select { + case wevent := <-watchEvents.ResultChan(): + event := wevent.Object.(*corev1.Event) + if val, ok := kinds[event.InvolvedObject.Kind]; ok { + for _, expectedName := range val { + if event.InvolvedObject.Name == expectedName { + events = append(events, event) + } + } + } + case <-timer.C: + return events, nil + } + } +} diff --git a/cel/test/clients.go b/cel/test/clients.go new file mode 100644 index 000000000..a08a66879 --- /dev/null +++ b/cel/test/clients.go @@ -0,0 +1,79 @@ +/* +Copyright 2019 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 test + +import ( + "testing" + + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/typed/pipeline/v1beta1" + resourceversioned "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned" + resourcev1alpha1 "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/typed/resource/v1alpha1" + knativetest "knative.dev/pkg/test" +) + +// clients holds instances of interfaces for making requests to the Pipeline controllers. +type clients struct { + KubeClient *knativetest.KubeClient + + PipelineClient v1beta1.PipelineInterface + ClusterTaskClient v1beta1.ClusterTaskInterface + TaskClient v1beta1.TaskInterface + TaskRunClient v1beta1.TaskRunInterface + PipelineRunClient v1beta1.PipelineRunInterface + PipelineResourceClient resourcev1alpha1.PipelineResourceInterface + ConditionClient v1alpha1.ConditionInterface + RunClient v1alpha1.RunInterface +} + +// newClients instantiates and returns several clientsets required for making requests to the +// Pipeline cluster specified by the combination of clusterName and configPath. Clients can +// make requests within namespace. +func newClients(t *testing.T, configPath, clusterName, namespace string) *clients { + t.Helper() + var err error + c := &clients{} + + c.KubeClient, err = knativetest.NewKubeClient(configPath, clusterName) + if err != nil { + t.Fatalf("failed to create kubeclient from config file at %s: %s", configPath, err) + } + + cfg, err := knativetest.BuildClientConfig(configPath, clusterName) + if err != nil { + t.Fatalf("failed to create configuration obj from %s for cluster %s: %s", configPath, clusterName, err) + } + + cs, err := versioned.NewForConfig(cfg) + if err != nil { + t.Fatalf("failed to create pipeline clientset from config file at %s: %s", configPath, err) + } + rcs, err := resourceversioned.NewForConfig(cfg) + if err != nil { + t.Fatalf("failed to create pipeline clientset from config file at %s: %s", configPath, err) + } + c.PipelineClient = cs.TektonV1beta1().Pipelines(namespace) + c.ClusterTaskClient = cs.TektonV1beta1().ClusterTasks() + c.TaskClient = cs.TektonV1beta1().Tasks(namespace) + c.TaskRunClient = cs.TektonV1beta1().TaskRuns(namespace) + c.PipelineRunClient = cs.TektonV1beta1().PipelineRuns(namespace) + c.PipelineResourceClient = rcs.TektonV1alpha1().PipelineResources(namespace) + c.ConditionClient = cs.TektonV1alpha1().Conditions(namespace) + c.RunClient = cs.TektonV1alpha1().Runs(namespace) + return c +} diff --git a/cel/test/controller.go b/cel/test/controller.go index 77c6cefe0..7d9fd8734 100644 --- a/cel/test/controller.go +++ b/cel/test/controller.go @@ -19,6 +19,15 @@ package test import ( "context" "fmt" + "sync/atomic" + "testing" + + // Link in the fakes so they get injected into injection.Fake + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + fakepipelineclientset "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/fake" + informersv1alpha1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1" + informersv1beta1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1" fakepipelineclient "github.com/tektoncd/pipeline/pkg/client/injection/client/fake" fakeconditioninformer "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1alpha1/condition/fake" fakeruninformer "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1alpha1/run/fake" @@ -27,35 +36,26 @@ import ( fakepipelineruninformer "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1beta1/pipelinerun/fake" faketaskinformer "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1beta1/task/fake" faketaskruninformer "github.com/tektoncd/pipeline/pkg/client/injection/informers/pipeline/v1beta1/taskrun/fake" + fakeresourceclientset "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/fake" + resourceinformersv1alpha1 "github.com/tektoncd/pipeline/pkg/client/resource/informers/externalversions/resource/v1alpha1" fakeresourceclient "github.com/tektoncd/pipeline/pkg/client/resource/injection/client/fake" fakeresourceinformer "github.com/tektoncd/pipeline/pkg/client/resource/injection/informers/resource/v1alpha1/pipelineresource/fake" + cloudeventclient "github.com/tektoncd/pipeline/pkg/reconciler/events/cloudevent" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" apierrs "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + coreinformers "k8s.io/client-go/informers/core/v1" + fakekubeclientset "k8s.io/client-go/kubernetes/fake" ktesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" + "k8s.io/client-go/tools/record" fakekubeclient "knative.dev/pkg/client/injection/kube/client/fake" fakeconfigmapinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/configmap/fake" fakepodinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/pod/fake" fakeserviceaccountinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/serviceaccount/fake" - "sync/atomic" - "testing" - - // Link in the fakes so they get injected into injection.Fake - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1" - "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" - fakepipelineclientset "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/fake" - informersv1alpha1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1alpha1" - informersv1beta1 "github.com/tektoncd/pipeline/pkg/client/informers/externalversions/pipeline/v1beta1" - fakeresourceclientset "github.com/tektoncd/pipeline/pkg/client/resource/clientset/versioned/fake" - resourceinformersv1alpha1 "github.com/tektoncd/pipeline/pkg/client/resource/informers/externalversions/resource/v1alpha1" - cloudeventclient "github.com/tektoncd/pipeline/pkg/reconciler/events/cloudevent" - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - coreinformers "k8s.io/client-go/informers/core/v1" - fakekubeclientset "k8s.io/client-go/kubernetes/fake" - "k8s.io/client-go/tools/record" "knative.dev/pkg/controller" ) diff --git a/cel/test/e2e-cels.sh b/cel/test/e2e-cels.sh new file mode 100755 index 000000000..ccae901ab --- /dev/null +++ b/cel/test/e2e-cels.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +# Copyright 2021 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. + +# Helper functions for E2E tests. + +source $(git rev-parse --show-toplevel)/vendor/github.com/tektoncd/plumbing/scripts/e2e-tests.sh + +function install_cel_crd() { + echo ">> Deploying Cel custom task" + ko resolve -f config/ \ + | sed -e 's%"level": "info"%"level": "debug"%' \ + | sed -e 's%loglevel.controller: "info"%loglevel.controller: "debug"%' \ + | sed -e 's%loglevel.webhook: "info"%loglevel.webhook: "debug"%' \ + | kubectl apply -f - || fail_test "Build pipeline installation failed" + verify_cel_installation +} + +function verify_cel_installation() { + # Make sure that everything is cleaned up in the current namespace + delete_cel_resources + + # Wait for pods to be running in the namespaces we are deploying to + wait_until_pods_running tekton-pipelines || fail_test "Cel controller or webhook did not come up" +} + +function uninstall_cel_crd() { + echo ">> Uninstalling Cel custom task" + ko delete --ignore-not-found=true -f ${REPO_ROOT_DIR}/cel/config/ + + # Make sure that everything is cleaned up in the current namespace. + delete_cel_resources +} + +function delete_cel_resources() { + for res in run; do + kubectl delete --ignore-not-found=true ${res}.tekton.dev --all + done + for res in cel; do + kubectl delete --ignore-not-found=true ${res}.custom.tekton.dev --all + done +} diff --git a/cel/test/e2e-common.sh b/cel/test/e2e-common.sh new file mode 100755 index 000000000..1fba02495 --- /dev/null +++ b/cel/test/e2e-common.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +# Copyright 2019 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. + +# Helper functions for E2E tests. + +source $(git rev-parse --show-toplevel)/vendor/github.com/tektoncd/plumbing/scripts/e2e-tests.sh + +function install_pipeline_crd() { + echo ">> Deploying Tekton Pipelines" + ko resolve -f config/ \ + | sed -e 's%"level": "info"%"level": "debug"%' \ + | sed -e 's%loglevel.controller: "info"%loglevel.controller: "debug"%' \ + | sed -e 's%loglevel.webhook: "info"%loglevel.webhook: "debug"%' \ + | kubectl apply -f - || fail_test "Build pipeline installation failed" + verify_pipeline_installation +} + +# Install the Tekton pipeline crd based on the release number +function install_pipeline_crd_version() { + echo ">> Deploying Tekton Pipelines of Version $1" + kubectl apply -f "https://github.com/tektoncd/pipeline/releases/download/$1/release.yaml" || fail_test "Build pipeline installation failed of Version $1" + verify_pipeline_installation +} + +function verify_pipeline_installation() { + # Make sure that everything is cleaned up in the current namespace. + delete_pipeline_resources + + # Wait for pods to be running in the namespaces we are deploying to + wait_until_pods_running tekton-pipelines || fail_test "Tekton Pipeline did not come up" +} + +function uninstall_pipeline_crd() { + echo ">> Uninstalling Tekton Pipelines" + ko delete --ignore-not-found=true -f config/ + + # Make sure that everything is cleaned up in the current namespace. + delete_pipeline_resources +} + +function uninstall_pipeline_crd_version() { + echo ">> Uninstalling Tekton Pipelines of version $1" + kubectl delete --ignore-not-found=true -f "https://github.com/tektoncd/pipeline/releases/download/$1/release.yaml" + + # Make sure that everything is cleaned up in the current namespace. + delete_pipeline_resources +} + +function delete_pipeline_resources() { + for res in conditions pipelineresources tasks clustertasks pipelines taskruns pipelineruns; do + kubectl delete --ignore-not-found=true ${res}.tekton.dev --all + done +} diff --git a/cel/test/e2e-tests.sh b/cel/test/e2e-tests.sh new file mode 100755 index 000000000..f2e180186 --- /dev/null +++ b/cel/test/e2e-tests.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Copyright 2021 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. + +# This script calls out to scripts in tektoncd/plumbing to setup a cluster +# and deploy Tekton Pipelines to it for running integration tests. + +source $(git rev-parse --show-toplevel)/cel/test/e2e-common.sh +source $(git rev-parse --show-toplevel)/cel/test/e2e-cels.sh + +# Script entry point. + +initialize $@ + +# initialize function does a CD to REPO_ROOT_DIR so we have to CD back here. +cd ${REPO_ROOT_DIR}/cel + +header "Setting up environment" + +install_pipeline_crd_version v0.20.1 + +install_cel_crd + +failed=0 + +# Run the integration tests +header "Running Go e2e tests" +go_test_e2e -timeout=20m ./test/... || failed=1 + +(( failed )) && fail_test +success diff --git a/cel/test/init_test.go b/cel/test/init_test.go new file mode 100644 index 000000000..e4ffe42d0 --- /dev/null +++ b/cel/test/init_test.go @@ -0,0 +1,240 @@ +// +build e2e examples + +/* +Copyright 2019 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. +*/ + +// This file contains initialization logic for the tests, such as special magical global state that needs to be initialized. + +package test + +import ( + "context" + "flag" + "fmt" + "github.com/ghodss/yaml" + "github.com/tektoncd/pipeline/pkg/names" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // Mysteriously by k8s libs, or they fail to create `KubeClient`s when using oidc authentication. Apparently just importing it is enough. @_@ side effects @_@. https://github.com/kubernetes/client-go/issues/345 + _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" + knativetest "knative.dev/pkg/test" + "knative.dev/pkg/test/logging" // Mysteriously by k8s libs, or they fail to create `KubeClient`s from config. Apparently just importing it is enough. @_@ side effects @_@. https://github.com/kubernetes/client-go/issues/242 + "os" + "strings" + "sync" + "testing" +) + +var initMetrics sync.Once +var skipRootUserTests = false + +func init() { + flag.BoolVar(&skipRootUserTests, "skipRootUserTests", false, "Skip tests that require root user") +} + +func setup(ctx context.Context, t *testing.T, fn ...func(context.Context, *testing.T, *clients, string)) (*clients, string) { + // skipIfExcluded(t) + + t.Helper() + namespace := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("arendelle") + + initializeLogsAndMetrics(t) + + c := newClients(t, knativetest.Flags.Kubeconfig, knativetest.Flags.Cluster, namespace) + createNamespace(ctx, t, namespace, c.KubeClient) + verifyServiceAccountExistence(ctx, t, namespace, c.KubeClient) + + for _, f := range fn { + f(ctx, t, c, namespace) + } + + return c, namespace +} + +func header(t *testing.T, text string) { + t.Helper() + left := "### " + right := " ###" + txt := left + text + right + bar := strings.Repeat("#", len(txt)) + t.Logf(bar) + t.Logf(txt) + t.Logf(bar) +} + +func tearDown(ctx context.Context, t *testing.T, cs *clients, namespace string) { + t.Helper() + if cs.KubeClient == nil { + return + } + if t.Failed() { + header(t, fmt.Sprintf("Dumping objects from %s", namespace)) + bs, err := getCRDYaml(ctx, cs, namespace) + if err != nil { + t.Error(err) + } else { + t.Log(string(bs)) + } + header(t, fmt.Sprintf("Dumping logs from Pods in the %s", namespace)) + taskruns, err := cs.TaskRunClient.List(ctx, metav1.ListOptions{}) + if err != nil { + t.Errorf("Error getting Runs list %s", err) + } + for _, tr := range taskruns.Items { + if tr.Status.PodName != "" { + CollectPodLogs(ctx, cs, tr.Status.PodName, namespace, t.Logf) + } + } + } + + if os.Getenv("TEST_KEEP_NAMESPACES") == "" && !t.Failed() { + t.Logf("Deleting namespace %s", namespace) + if err := cs.KubeClient.CoreV1().Namespaces().Delete(ctx, namespace, metav1.DeleteOptions{}); err != nil { + t.Errorf("Failed to delete namespace %s: %s", namespace, err) + } + } +} + +func initializeLogsAndMetrics(t *testing.T) { + initMetrics.Do(func() { + flag.Parse() + flag.Set("alsologtostderr", "true") + logging.InitializeLogger() + + //if knativetest.Flags.EmitMetrics { + logging.InitializeMetricExporter(t.Name()) + //} + }) +} + +func createNamespace(ctx context.Context, t *testing.T, namespace string, kubeClient *knativetest.KubeClient) { + t.Logf("Create namespace %s to deploy to", namespace) + labels := map[string]string{ + "tekton.dev/test-e2e": "true", + } + if _, err := kubeClient.CoreV1().Namespaces().Create(ctx, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: namespace, + Labels: labels, + }, + }, metav1.CreateOptions{}); err != nil { + t.Fatalf("Failed to create namespace %s for tests: %s", namespace, err) + } +} + +func verifyServiceAccountExistence(ctx context.Context, t *testing.T, namespace string, kubeClient *knativetest.KubeClient) { + defaultSA := "default" + t.Logf("Verify SA %q is created in namespace %q", defaultSA, namespace) + + if err := wait.PollImmediate(interval, timeout, func() (bool, error) { + _, err := kubeClient.CoreV1().ServiceAccounts(namespace).Get(ctx, defaultSA, metav1.GetOptions{}) + if err != nil && errors.IsNotFound(err) { + return false, nil + } + return true, err + }); err != nil { + t.Fatalf("Failed to get SA %q in namespace %q for tests: %s", defaultSA, namespace, err) + } +} + +// TestMain initializes anything global needed by the tests. Right now this is just log and metric +// setup since the log and metric libs we're using use global state :( +func TestMain(m *testing.M) { + flag.Parse() + c := m.Run() + fmt.Fprintf(os.Stderr, "Using kubeconfig at `%s` with cluster `%s`\n", knativetest.Flags.Kubeconfig, knativetest.Flags.Cluster) + os.Exit(c) +} + +func getCRDYaml(ctx context.Context, cs *clients, ns string) ([]byte, error) { + var output []byte + printOrAdd := func(i interface{}) { + bs, err := yaml.Marshal(i) + if err != nil { + return + } + output = append(output, []byte("\n---\n")...) + output = append(output, bs...) + } + + ps, err := cs.PipelineClient.List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("could not get pipeline: %w", err) + } + for _, i := range ps.Items { + printOrAdd(i) + } + + prs, err := cs.PipelineResourceClient.List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("could not get pipelinerun resource: %w", err) + } + for _, i := range prs.Items { + printOrAdd(i) + } + + prrs, err := cs.PipelineRunClient.List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("could not get pipelinerun: %w", err) + } + for _, i := range prrs.Items { + printOrAdd(i) + } + + ts, err := cs.TaskClient.List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("could not get tasks: %w", err) + } + for _, i := range ts.Items { + printOrAdd(i) + } + + cts, err := cs.ClusterTaskClient.List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("could not get clustertasks: %w", err) + } + for _, i := range cts.Items { + printOrAdd(i) + } + + trs, err := cs.TaskRunClient.List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("could not get taskruns: %w", err) + } + for _, i := range trs.Items { + printOrAdd(i) + } + + rs, err := cs.RunClient.List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("could not get runs: %v", err) + } + for _, i := range rs.Items { + printOrAdd(i) + } + + pods, err := cs.KubeClient.CoreV1().Pods(ns).List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, fmt.Errorf("could not get pods: %w", err) + } + for _, i := range pods.Items { + printOrAdd(i) + } + + return output, nil +} diff --git a/cel/test/wait.go b/cel/test/wait.go new file mode 100644 index 000000000..d7148dae5 --- /dev/null +++ b/cel/test/wait.go @@ -0,0 +1,103 @@ +/* +Copyright 2019 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 test + +import ( + "context" + "fmt" + "time" + + "go.opencensus.io/trace" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "knative.dev/pkg/apis" +) + +const ( + interval = 1 * time.Second + timeout = 10 * time.Minute +) + +// ConditionAccessorFn is a condition function used polling functions +type ConditionAccessorFn func(ca apis.ConditionAccessor) (bool, error) + +func pollImmediateWithContext(ctx context.Context, fn func() (bool, error)) error { + return wait.PollImmediate(interval, timeout, func() (bool, error) { + select { + case <-ctx.Done(): + return true, ctx.Err() + default: + } + return fn() + }) +} + +// WaitForRunState polls the status of the Run called name from client every +// interval until inState returns `true` indicating it is done, returns an +// error or timeout. desc will be used to name the metric that is emitted to +// track how long it took for name to get into the state checked by inState. +func WaitForRunState(ctx context.Context, c *clients, name string, polltimeout time.Duration, inState ConditionAccessorFn, desc string) error { + metricName := fmt.Sprintf("WaitForRunState/%s/%s", name, desc) + _, span := trace.StartSpan(context.Background(), metricName) + defer span.End() + + ctx, cancel := context.WithTimeout(ctx, polltimeout) + defer cancel() + return pollImmediateWithContext(ctx, func() (bool, error) { + r, err := c.RunClient.Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return true, err + } + return inState(&r.Status) + }) +} + +// Succeed provides a poll condition function that checks if the ConditionAccessor +// resource has successfully completed or not. +func Succeed(name string) ConditionAccessorFn { + return func(ca apis.ConditionAccessor) (bool, error) { + c := ca.GetCondition(apis.ConditionSucceeded) + if c != nil { + if c.Status == corev1.ConditionTrue { + return true, nil + } else if c.Status == corev1.ConditionFalse { + return true, fmt.Errorf("%q failed", name) + } + } + return false, nil + } +} + +// FailedWithReason provides a poll function that checks if the ConditionAccessor +// resource has failed with the TimeoudOut reason +func FailedWithReason(reason, name string) ConditionAccessorFn { + return func(ca apis.ConditionAccessor) (bool, error) { + c := ca.GetCondition(apis.ConditionSucceeded) + if c != nil { + if c.Status == corev1.ConditionFalse { + if c.Reason == reason { + return true, nil + } + return true, fmt.Errorf("%q completed with the wrong reason: %s (message: %s)", name, c.Reason, c.Message) + } else if c.Status == corev1.ConditionTrue { + return true, fmt.Errorf("%q completed successfully, should have been failed with reason %q", name, reason) + } + } + return false, nil + } +}