Skip to content

Commit

Permalink
[cel] CEL Custom Tasks CRD
Browse files Browse the repository at this point in the history
We introduced CEL Custom Tasks to experiment with using an expression
language with Tekton Pipelines.

Given feedback from the past several months of usage, we have
identified three main current challenges:
- CEL custom tasks do not take variables for the CEL environment.
As such, users cannot evaluate CEL expressions given specific variables
or in specific context. For example, as described in tektoncd#716
and tektoncd/community#403, a user needed to
declate runtime storage variables in the CEL environment.
- CEL custom tasks are not a CRD thus making them unreusable across
different Runs and Pipelines. Read more in tektoncd/community#314 (review).
- CEL custom tasks take the CEL expressions through Parameters which
is misleading to some users.

To address the above challenges, this change introduces a CRD for CEL
Custom Tasks, which takes CEL expressions and CEL environment variables.
  • Loading branch information
jerop committed Aug 26, 2021
1 parent eddc3ed commit 957c3c5
Show file tree
Hide file tree
Showing 3,010 changed files with 965,227 additions and 323 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
354 changes: 244 additions & 110 deletions cel/README.md

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion cel/cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,26 @@ limitations under the License.
package main

import (
"flag"
"github.com/tektoncd/experimental/cel/pkg/reconciler/cel"
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 = "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,
cel.NewController(*namespace),
)
}
129 changes: 129 additions & 0 deletions cel/cmd/webhook/main.go
Original file line number Diff line number Diff line change
@@ -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"
celv1alpha1 "github.com/tektoncd/experimental/cel/pkg/apis/cel/v1alpha1"
"os"

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 = "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 = "cel-webhook"
}

secretName := os.Getenv("WEBHOOK_SECRET_NAME")
if secretName == "" {
secretName = "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,
)
}
37 changes: 34 additions & 3 deletions cel/config/201-clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ rules:
resources: ["runs"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
- apiGroups: ["tekton.dev"]
resources: ["runs/finalizers"]
resources: ["runs/status", "runs/finalizers"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
- apiGroups: ["tekton.dev"]
resources: ["runs/status"]
- apiGroups: ["custom.tekton.dev"]
resources: ["cels"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]

# Controller needs cluster access to leases for leader election.
Expand All @@ -41,3 +41,34 @@ rules:
- apiGroups: [""]
resources: ["events"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]

# 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"]
51 changes: 51 additions & 0 deletions cel/config/300-cel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 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-run
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
- tekton-cel-run
scope: Namespaced
# Opt into the status subresource so metadata.generation
# starts to increment
subresources:
status: {}
6 changes: 1 addition & 5 deletions cel/config/400-controller-service.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,9 @@ metadata:
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"
version: devel
name: cel-controller
namespace: tekton-cel-run
spec:
Expand Down
6 changes: 2 additions & 4 deletions cel/config/500-controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ metadata:
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
spec:
replicas: 1
Expand All @@ -30,7 +29,7 @@ spec:
app.kubernetes.io/name: cel-controller
app.kubernetes.io/component: cel-controller
app.kubernetes.io/instance: default
app.kubernetes.io/part-of: cel
app.kubernetes.io/part-of: tekton-cel-run
template:
metadata:
annotations:
Expand All @@ -39,8 +38,7 @@ spec:
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: cel
app.kubernetes.io/part-of: tekton-cel-run
app: cel-controller
spec:
serviceAccountName: cel-controller
Expand Down
63 changes: 63 additions & 0 deletions cel/config/500-webhook-configuration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# 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: cel-webhook-certs
namespace: tekton-cel-run
labels:
app.kubernetes.io/component: webhook
app.kubernetes.io/instance: default
app.kubernetes.io/part-of: tekton-cel-run
---
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-run
webhooks:
- admissionReviewVersions:
- v1beta1
clientConfig:
service:
name: cel-webhook
namespace: tekton-cel-run
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-run
webhooks:
- admissionReviewVersions:
- v1beta1
clientConfig:
service:
name: cel-webhook
namespace: tekton-cel-run
failurePolicy: Fail
sideEffects: None
name: webhook.cel.custom.tekton.dev

Loading

0 comments on commit 957c3c5

Please sign in to comment.