Skip to content

Commit

Permalink
Remove limitation of only creating Tekton resources with TriggerTempl…
Browse files Browse the repository at this point in the history
…ates

Fix name of listener

And another which should becom dynamic instead of set

Remove empty line

Add e2e for custom resources

Go imports

Fix a few references

Use dynamic client instead of set to make it possible to create non tekton resources

Remove dead code after removing type validation

Removal of tekton type resource validation on v1beta1 as well

Remove note about tekton resources but leave the validation

First removal of the allowed type validation

Remove dynamic client set because its not used anymore

Better docs

Change name in test examples

Rename to create-configmap
  • Loading branch information
GijsvanDulmen committed Feb 23, 2024
1 parent f6f77f4 commit 1d3e7b0
Show file tree
Hide file tree
Showing 18 changed files with 192 additions and 191 deletions.
5 changes: 1 addition & 4 deletions cmd/eventlistenersink/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ import (
"knative.dev/pkg/signals"

"github.com/tektoncd/triggers/pkg/adapter"
dynamicClientset "github.com/tektoncd/triggers/pkg/client/dynamic/clientset"
"github.com/tektoncd/triggers/pkg/client/dynamic/clientset/tekton"
triggersclient "github.com/tektoncd/triggers/pkg/client/injection/client"
"github.com/tektoncd/triggers/pkg/sink"
"github.com/tektoncd/triggers/pkg/sink/cloudevent"
Expand All @@ -50,8 +48,7 @@ func main() {
ctx = injection.WithConfig(ctx, cfg)

dc := dynamic.NewForConfigOrDie(cfg)
dClientSet := dynamicClientset.New(tekton.WithClient(dc))
ctx = context.WithValue(ctx, dynamicclient.Key{}, dClientSet)
ctx = context.WithValue(ctx, dynamicclient.Key{}, dc)

sinkArgs, err := sink.GetArgs()
if err != nil {
Expand Down
5 changes: 1 addition & 4 deletions cmd/triggerrun/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ import (
"github.com/tektoncd/triggers/pkg/apis/triggers"
triggersv1 "github.com/tektoncd/triggers/pkg/apis/triggers/v1beta1"
triggersclientset "github.com/tektoncd/triggers/pkg/client/clientset/versioned"
dynamicClientset "github.com/tektoncd/triggers/pkg/client/dynamic/clientset"
"github.com/tektoncd/triggers/pkg/client/dynamic/clientset/tekton"
"github.com/tektoncd/triggers/pkg/sink"
"github.com/tektoncd/triggers/pkg/template"
"go.uber.org/zap"
Expand Down Expand Up @@ -254,7 +252,6 @@ func newSink(ctx context.Context, config *rest.Config) sink.Sink {
log.Fatalf("Failed to get the dynamic client: %v", err)
}

dynamicCS := dynamicClientset.New(tekton.WithClient(dynamicClient))
kubeClient, _, err := getKubeClient(kubeconfig)
if err != nil {
log.Fatalf("fail to get clients: %v", err)
Expand All @@ -266,7 +263,7 @@ func newSink(ctx context.Context, config *rest.Config) sink.Sink {
Auth: sink.DefaultAuthOverride{},
WGProcessTriggers: &sync.WaitGroup{},
DiscoveryClient: sinkClients.DiscoveryClient,
DynamicClient: dynamicCS,
DynamicClient: dynamicClient,
Logger: logging.FromContext(ctx),
EventListenerNamespace: "default",
}
Expand Down
5 changes: 3 additions & 2 deletions docs/triggertemplates.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ Keep the following in mind:
* `tekton.dev/triggers-eventid`:`<EventID>` to track resources created by a specific event.

* To support arbitrary resource types, Tekton resolves resource templates internally as byte blobs. Because of this, Tekton only validates these
resources when processing an event rather than at the creation of the `TriggerTemplate`. Thus, you can **only** specify Tekton resources in a
`TriggerTemplate`.
resources when processing an event rather than at the creation of the `TriggerTemplate`. The default assigned cluster role to the `EventListener`
service account only allows creating Tekton resources. You will need to add the appropriate roles to the `EventListener` to be able to create other
non Tekton resources.

* As of Tekton Pipelines [0.8.0](https://github.com/tektoncd/pipeline/releases/tag/v0.8.0), you can embed resource definitions directly in
your `TriggerTemplate` definition. To prevent a race condition between creating and using resources, you **must** embed each resource definition
Expand Down
34 changes: 34 additions & 0 deletions examples/v1beta1/create-configmap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Create Configmap with EventListener

Creates an EventListener that will create a configmap as an example how to create non tekton resources with Triggers.

### Try it out locally:

1. To create the Custom trigger and all related resources, run:

```bash
kubectl apply -f .
```

1. Port forward:

```bash
kubectl port-forward service/el-create-configmap-listener 8080
```

1. Test by sending the sample payload.

```bash
curl -v \
-H 'Content-Type: application/json' \
-d '{"action": "opened"}' \
http://localhost:8080
```

The response status code should be `202 Accepted`

1. You should see a new ConfigMap that got created:

```bash
kubectl get configmaps | grep sample-
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: create-configmap-listener
spec:
triggers:
- name: create-configmap-listener
interceptors:
- name: "only when field is something"
ref:
name: "cel"
params:
- name: "filter"
value: "body.action in ['opened']"
bindings:
- ref: create-configmap-binding
template:
ref: create-configmap-template
resources:
kubernetesResource:
spec:
template:
spec:
serviceAccountName: tekton-triggers-example-sa
containers:
- resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
---
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
name: create-configmap-binding
spec:
params:
- name: action
value: $(body.action)

---
apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerTemplate
metadata:
name: create-configmap-template
spec:
params:
- name: action
resourcetemplates:
- apiVersion: v1
kind: ConfigMap
metadata:
generateName: sample-
data:
field: "Action is : $(tt.params.action)"
4 changes: 4 additions & 0 deletions examples/v1beta1/create-configmap/curl.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
curl -v \
-H 'Content-Type: application/json' \
-d '{"action": "opened"}' \
http://localhost:8080
50 changes: 50 additions & 0 deletions examples/v1beta1/create-configmap/rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: tekton-triggers-example-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: triggers-example-eventlistener-binding
subjects:
- kind: ServiceAccount
name: tekton-triggers-example-sa
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-eventlistener-roles
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: triggers-example-eventlistener-clusterbinding
subjects:
- kind: ServiceAccount
name: tekton-triggers-example-sa
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-eventlistener-clusterroles
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: tekton-triggers-configmap-roles
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: triggers-example-configmap-eventlistener-binding
subjects:
- kind: ServiceAccount
name: tekton-triggers-example-sa
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: tekton-triggers-configmap-roles
5 changes: 4 additions & 1 deletion pkg/adapter/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,13 @@ func (s *sinker) Start(ctx context.Context) error {
return err
}
// Create EventListener Sink

dynamicClient := dynamicclient.Get(ctx)

r := sink.Sink{
KubeClientSet: kubeclient.Get(ctx),
DiscoveryClient: s.Clients.DiscoveryClient,
DynamicClient: dynamicclient.Get(ctx),
DynamicClient: dynamicClient,
TriggersClient: s.Clients.TriggersClient,
HTTPClient: clientObj,
CEClient: s.Clients.CEClient,
Expand Down
37 changes: 0 additions & 37 deletions pkg/apis/config/allowed_types.go
Original file line number Diff line number Diff line change
@@ -1,43 +1,6 @@
package config

import (
pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
customrunsv1alpha1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
pipelinev1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
)

var Decoder runtime.Decoder

// TODO(dibyom): We should have a way of configuring this instead of an init function?
func init() {
scheme := runtime.NewScheme()
utilruntime.Must(customrunsv1alpha1.AddToScheme(scheme))
utilruntime.Must(pipelinev1beta1.AddToScheme(scheme))
utilruntime.Must(pipelinev1.AddToScheme(scheme))
codec := serializer.NewCodecFactory(scheme)
Decoder = codec.UniversalDecoder(
customrunsv1alpha1.SchemeGroupVersion, // customrunsv1alpha1 share the same SchemeGroupVersion
pipelinev1beta1.SchemeGroupVersion,
pipelinev1.SchemeGroupVersion,
)
}

// EnsureAllowedType returns nil if the resourceTemplate has an apiVersion
// and kind field set to one of the allowed ones.
func EnsureAllowedType(rt runtime.RawExtension) error {
_, err := runtime.Decode(Decoder, rt.Raw)
return err
}

var (
AllowedPipelineTypes = map[string][]string{
"v1alpha1": {"pipelineruns", "taskruns", "pipelines", "clustertasks", "tasks", "conditions", "runs"},
"v1beta1": {"pipelineruns", "taskruns", "pipelines", "clustertasks", "tasks", "customruns"},
"v1": {"pipelineruns", "taskruns", "pipelines", "tasks"},
}
AllowedTriggersTypes = map[string][]string{
"v1alpha1": {"clusterinterceptors", "interceptors"},
"v1beta1": {"clustertriggerbindings", "eventlisteners", "triggerbindings", "triggers", "triggertemplates"},
Expand Down
33 changes: 9 additions & 24 deletions pkg/apis/triggers/v1alpha1/trigger_template_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,10 @@ import (
"context"
"fmt"
"regexp"
"strings"

"github.com/tektoncd/pipeline/pkg/apis/validate"
"github.com/tektoncd/triggers/pkg/apis/config"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/sets"
"knative.dev/pkg/apis"
)
Expand Down Expand Up @@ -60,27 +58,14 @@ func (s *TriggerTemplateSpec) validate(ctx context.Context) (errs *apis.FieldErr

func validateResourceTemplates(templates []TriggerResourceTemplate) (errs *apis.FieldError) {
for i, trt := range templates {
if err := config.EnsureAllowedType(trt.RawExtension); err != nil {
if runtime.IsMissingVersion(err) {
errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("[%d].apiVersion", i)))
}
if runtime.IsMissingKind(err) {
errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("[%d].kind", i)))
}
if runtime.IsNotRegisteredError(err) {
errStr := err.Error()
if inSchemeIdx := strings.Index(errStr, " in scheme"); inSchemeIdx > -1 {
// not registered error messages currently include the scheme variable location in your file,
// which can of course change if you move the location of the variable in your file.
// So will filter it out here to facilitate our unit testing, as the scheme location is not
// useful for our purposes.
errStr = errStr[:inSchemeIdx]
}
errs = errs.Also(apis.ErrInvalidValue(
errStr,
fmt.Sprintf("[%d]", i)))
}
// we allow structural errors because of param substitution
data := new(unstructured.Unstructured)
if err := data.UnmarshalJSON(trt.Raw); err != nil {
// a missing kind makes the unmarshalling throw an error
errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("[%d].kind", i)))
}

if data.GetAPIVersion() == "" {
errs = errs.Also(apis.ErrMissingField(fmt.Sprintf("[%d].apiVersion", i)))
}
}
return errs
Expand Down
38 changes: 4 additions & 34 deletions pkg/apis/triggers/v1alpha1/trigger_template_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func TestTriggerTemplate_Validate(t *testing.T) {
Paths: []string{"spec.resourcetemplates[0].apiVersion"},
},
}, {
name: "resource template invalid apiVersion",
name: "resource template other kind then tekton resource",
template: &v1alpha1.TriggerTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "tt",
Expand All @@ -262,44 +262,14 @@ func TestTriggerTemplate_Validate(t *testing.T) {
ResourceTemplates: []v1alpha1.TriggerResourceTemplate{{
RawExtension: test.RawExtension(t, pipelinev1.PipelineRun{
TypeMeta: metav1.TypeMeta{
APIVersion: "foobar",
Kind: "pipelinerun",
APIVersion: "v1",
Kind: "batch/Job",
},
}),
}},
},
},
want: &apis.FieldError{
Message: `invalid value: no kind "pipelinerun" is registered for version "foobar"`,
Paths: []string{"spec.resourcetemplates[0]"},
},
}, {
name: "resource template invalid kind",
template: &v1alpha1.TriggerTemplate{
ObjectMeta: metav1.ObjectMeta{
Name: "tt",
Namespace: "foo",
},
Spec: v1alpha1.TriggerTemplateSpec{
Params: []v1alpha1.ParamSpec{{
Name: "foo",
Description: "desc",
Default: ptr.String("val"),
}},
ResourceTemplates: []v1alpha1.TriggerResourceTemplate{{
RawExtension: test.RawExtension(t, pipelinev1.PipelineRun{
TypeMeta: metav1.TypeMeta{
APIVersion: "foo",
Kind: "tekton.dev/v1alpha1",
},
}),
}},
},
},
want: &apis.FieldError{
Message: `invalid value: no kind "tekton.dev/v1alpha1" is registered for version "foo"`,
Paths: []string{"spec.resourcetemplates[0]"},
},
want: nil,
}, {
name: "tt.params used in resource template are declared",
template: &v1alpha1.TriggerTemplate{
Expand Down
Loading

0 comments on commit 1d3e7b0

Please sign in to comment.