Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EventListener Selector For TriggerCRD #773

Merged
merged 1 commit into from
Nov 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cmd/eventlistenersink/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ func main() {

factory := externalversions.NewSharedInformerFactoryWithOptions(sinkClients.TriggersClient,
30*time.Second, externalversions.WithNamespace(sinkArgs.ElNamespace))
if sinkArgs.IsMultiNS {
factory = externalversions.NewSharedInformerFactory(sinkClients.TriggersClient,
30*time.Second)
}

go func(ctx context.Context) {
factory.Start(ctx.Done())
<-ctx.Done()
Expand Down
14 changes: 4 additions & 10 deletions cmd/triggerrun/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func trigger(triggerFile, httpPath, action, kubeconfig string, writer io.Writer)
}
case "create":
{
err := r.CreateResources("", resources, tri.Name, eventID, eventLog)
err := r.CreateResources(tri.Namespace, "", resources, tri.Name, eventID, eventLog)
if err != nil {
return fmt.Errorf("fail to create resources: %w", err)
}
Expand Down Expand Up @@ -193,15 +193,9 @@ func processTriggerSpec(kubeClient kubernetes.Interface, client triggersclientse
return nil, errors.New("trigger is not defined")
}

//convert trigger to eventListener
el, err := triggersv1.ToEventListenerTrigger(tri.Spec)
if err != nil {
return nil, fmt.Errorf("fail to convert Trigger to EvenetListener: %w", err)
}

log := eventLog.With(zap.String(triggersv1.TriggerLabelKey, el.Name))
log := eventLog.With(zap.String(triggersv1.TriggerLabelKey, r.EventListenerName))

finalPayload, header, iresp, err := r.ExecuteInterceptors(&el, request, body, log, eventID)
finalPayload, header, iresp, err := r.ExecuteInterceptors(*tri, request, body, log, eventID)
if err != nil {
log.Error(err)
return nil, err
Expand All @@ -216,7 +210,7 @@ func processTriggerSpec(kubeClient kubernetes.Interface, client triggersclientse
tri.Namespace = "default"
}

rt, err := template.ResolveTrigger(el,
rt, err := template.ResolveTrigger(*tri,
func(name string) (*triggersv1.TriggerBinding, error) {
return client.TriggersV1alpha1().TriggerBindings(tri.Namespace).Get(context.Background(), name, metav1.GetOptions{})
},
Expand Down
26 changes: 26 additions & 0 deletions docs/eventlisteners.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ using [Event Interceptors](#Interceptors).
- [PodTemplate](#podtemplate)
- [Resources](#resources)
- [Logging](#logging)
- [NamespaceSelector](#namespaceSelector)
- [Labels](#labels)
- [Annotations](#annotations)
- [Interceptors](#interceptors)
Expand Down Expand Up @@ -68,6 +69,8 @@ the following fields:
for your EventListener pod
- [`resources`](#resources) - Specifies the Kubernetes Resource information
for your EventListener pod
- [`namespaceSelector`](#namespaceSelector) - Specifies the namespaces where
EventListener can fetch triggers from and create Tekton resources.

[kubernetes-overview]:
https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields
Expand Down Expand Up @@ -294,6 +297,29 @@ To access logs for the EventListener sink, you can query for pods with the
kubectl get pods --selector eventlistener=my-eventlistener
```

### NamespaceSelector
The `namespaceSelector` field is optional.
This field determines the namespaces where EventListener can search for triggers and
create Tekton resources. If this field isn't provided, EventListener will only serve Triggers from its
own namespace.

Snippet below will function in foo and bar namespaces.
```yaml
namespaceSelector:
matchNames:
- foo
- bar
```

If EventListener is required to listen to serve the whole cluster, then below snippet
can be used where we only provide single argument for `matchNames` as `*`.
```yaml
namespaceSelector:
matchNames:
- *
```


## Labels

By default, EventListeners will attach the following labels automatically to all
Expand Down
10 changes: 10 additions & 0 deletions examples/selectors/00_ns.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: foo
---
apiVersion: v1
kind: Namespace
metadata:
name: bar
74 changes: 74 additions & 0 deletions examples/selectors/01_rbac.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
khrm marked this conversation as resolved.
Show resolved Hide resolved
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: el-sel-clusterrole
rules:
# Permissions for every EventListener deployment to function
- apiGroups: ["triggers.tekton.dev"]
resources: ["eventlisteners", "clustertriggerbindings", "triggerbindings", "triggertemplates", "triggers"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["impersonate"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: foo-el-sa
namespace: foo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: el-sel-clusterrolebinding
subjects:
- kind: ServiceAccount
name: foo-el-sa
namespace: foo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: el-sel-clusterrole
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: triggercr-role
namespace: bar
rules:
- apiGroups: ["tekton.dev"]
resources: ["pipelineruns", "pipelineresources", "taskruns"]
verbs: ["create"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: bar-trigger-sa
namespace: bar
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: triggercr-rolebinding
namespace: bar
subjects:
- kind: ServiceAccount
name: bar-trigger-sa
namespace: bar
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: triggercr-role
---
apiVersion: v1
kind: Secret
metadata:
name: github-secret
namespace: bar
type: Opaque
stringData:
secretToken: "1234567"
11 changes: 11 additions & 0 deletions examples/selectors/02_eventlistener-sel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
name: listener-sel
namespace: foo
spec:
serviceAccountName: foo-el-sa
namespaceSelector:
matchNames:
- foo
- bar
khrm marked this conversation as resolved.
Show resolved Hide resolved
77 changes: 77 additions & 0 deletions examples/selectors/03_trigger.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
apiVersion: triggers.tekton.dev/v1alpha1
kind: Trigger
metadata:
name: trigger
namespace: bar
spec:
serviceAccountName: bar-trigger-sa
interceptors:
- github:
secretRef:
secretName: github-secret
secretKey: secretToken
eventTypes:
- pull_request
bindings:
- ref: pipeline-binding
template:
ref: pipeline-template
---
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: pipeline-binding
namespace: bar
spec:
params:
- name: gitrevision
value: $(body.head_commit.id)
- name: gitrepositoryurl
value: $(body.repository.url)
- name: contenttype
value: $(header.Content-Type)
---
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
name: pipeline-template
namespace: bar
spec:
params:
- name: gitrevision
description: The git revision
default: master
- name: gitrepositoryurl
description: The git repository url
- name: message
description: The message to print
default: This is the default message
- name: contenttype
description: The Content-Type of the event
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: simple-pipeline-run-
spec:
pipelineRef:
name: simple-pipeline
podTemplate:
securityContext:
runAsNonRoot: true
runAsUser: 1001
params:
- name: message
value: $(tt.params.message)
- name: contenttype
value: $(tt.params.contenttype)
resources:
- name: git-source
resourceSpec:
type: git
params:
- name: revision
value: $(tt.params.gitrevision)
- name: url
value: $(tt.params.gitrepositoryurl)
khrm marked this conversation as resolved.
Show resolved Hide resolved
46 changes: 46 additions & 0 deletions examples/selectors/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## Namespace Selector EventListener

Creates an EventListener that serve triggers in multiple namespaces.

### Try it out locally:

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

```bash
kubectl apply -f examples/selectors/
```

2. Port forward:

```bash
kubectl config set-context --current --namespace=bar
kubectl apply -f examples/example-pipeline.yaml
```

**Note**: Instead of port forwarding, you can set the
[`serviceType`](https://github.com/tektoncd/triggers/blob/master/docs/eventlisteners.md#serviceType)
to `LoadBalancer` to expose the EventListener with a public IP.

3. Create sample pipelinerun in namespace bar:
```bash
kubectl port-forward \
-n foo $(kubectl get pod -n foo -o=name \
-l eventlistener=listener-sel) 8080
```

3. Test by sending the sample payload.

```bash
curl -k -v \
-H 'X-GitHub-Event: pull_request' \
-H 'X-Hub-Signature: sha1=8d7c4d33686fd908394208a07d997b8f5bd70aa6' \
-H 'Content-Type: application/json' \
-d '{"head_commit":{"id":"28911bbb5a3e2ea034daf1f6be0a822d50e31e73"},"action": "opened", "pull_request":{"head":{"sha": "28911bbb5a3e2ea034daf1f6be0a822d50e31e73"}},"repository":{"clone_url": "https://github.com/tektoncd/triggers.git", "url":"https://github.com/tektoncd/triggers.git"}}' http://localhost:8080 ```

The response status code should be `201 Created`

4. You should see a new Pipelinerun that got created:

```bash
tkn pr -n bar list
```
9 changes: 9 additions & 0 deletions pkg/apis/triggers/v1alpha1/event_listener_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type EventListenerSpec struct {
ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
Replicas *int32 `json:"replicas,omitempty"`
PodTemplate PodTemplate `json:"podTemplate,omitempty"`
NamespaceSelector NamespaceSelector `json:"namespaceSelector,omitempty"`
Resources Resources `json:"resources,omitempty"`
}

Expand Down Expand Up @@ -151,6 +152,14 @@ type EventListenerConfig struct {
GeneratedResourceName string `json:"generatedName"`
}

// NamespaceSelector is a selector for selecting either all namespaces or a
// list of namespaces.
// +k8s:openapi-gen=true
type NamespaceSelector struct {
// List of namespace names.
MatchNames []string `json:"matchNames,omitempty"`
}

// The conditions that are internally resolved by the EventListener reconciler
const (
// ServiceExists is the ConditionType set on the EventListener, which
Expand Down
3 changes: 0 additions & 3 deletions pkg/apis/triggers/v1alpha1/event_listener_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ func (s *EventListenerSpec) validate(ctx context.Context) (errs *apis.FieldError
errs = errs.Also(apis.ErrInvalidValue(*s.Replicas, "spec.replicas"))
}
}
if len(s.Triggers) == 0 {
errs = errs.Also(apis.ErrMissingField("spec.triggers"))
}
for i, trigger := range s.Triggers {
errs = errs.Also(trigger.validate(ctx).ViaField(fmt.Sprintf("spec.triggers[%d]", i)))
}
Expand Down
22 changes: 0 additions & 22 deletions pkg/apis/triggers/v1alpha1/event_listener_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,28 +215,6 @@ func TestEventListenerValidate_error(t *testing.T) {
name string
el *v1alpha1.EventListener
}{{
name: "no triggers",
el: &v1alpha1.EventListener{
ObjectMeta: metav1.ObjectMeta{
Name: "n",
Namespace: "namespace",
},
Spec: v1alpha1.EventListenerSpec{
Triggers: []v1alpha1.EventListenerTrigger{{}},
},
},
}, {
name: "EventListener with no Trigger ref or Template",
el: &v1alpha1.EventListener{
ObjectMeta: metav1.ObjectMeta{
Name: "n",
Namespace: "namespace",
},
Spec: v1alpha1.EventListenerSpec{
Triggers: nil,
},
},
}, {
name: "Valid EventListener with empty TriggerTemplate name",
el: bldr.EventListener("name", "namespace",
bldr.EventListenerSpec(
Expand Down
Loading