An EventListener
is a Kubernetes object that listens for events at a specified port on your Kubernetes cluster.
It exposes an addressable sink that receives incoming event and specifies one or more Triggers
.
The sink is a Kubernetes service running the sink logic inside a dedicated Pod.
Each Trigger
, in turn, allows you to specify one or more TriggerBindings
that allow
you to extract fields and their values from event payloads, and one or more TriggerTemplates
that receive field values from the corresponding TriggerBindings
and allow Tekton Triggers to instantiate resources
such as TaskRuns
and PipelineRuns
with that data.
If you need to modify, filter, or validate the event payload data before passing it to a TriggerBinding
, you can optionally specify one
or more Interceptors
.
- Structure of an
EventListener
- Specifying the Kubernetes service account
- Specifying
Triggers
- Specifying
TriggerGroups
- Specifying
Resources
- Specifying
Interceptors
- Specifying
cloudEventURI
- Constraining
EventListeners
to specific namespaces - Constraining
EventListeners
to specific labels - Disabling Payload Validation
- Labels in
EventListeners
- Specifying
EventListener
timeouts - Annotations in
EventListeners
- Understanding
EventListener
response - TLS HTTPS support in
EventListeners
- Obtaining the status of deployed
EventListeners
- Configuring logging for
EventListeners
- Exposing an
EventListener
outside of the cluster - Understanding the deployment of an
EventListener
- Deploying
EventListeners
in multi-tenant scenarios - CloudEvents during Trigger Processing
An EventListener
definition consists of the following fields:
- Required:
apiVersion
- specifies the target API version, for exampletriggers.tekton.dev/v1alpha1
kind
- specifies that this Kubernetes resource is anEventListener
objectmetadata
- specifies data that uniquely identifies thisEventListener
object, for example aname
spec
- specifies the configuration of yourEventListener
:serviceAccountName
- Specifies theServiceAccount
theEventListener
will use to instantiate Tekton resources
- Optional:
triggers
- specifies a list ofTriggers
to execute upon event detectioncloudEventURI
- specifies the URI for cloudevent sinkresources
- specifies the resources that will be available to the event listening servicenamespaceSelector
- specifies the namespace for theEventListener
; this is where theEventListener
looks for the specifiedTriggers
and stores the Tekton objects it instantiates upon event detectionlabelSelector
- specifies the labels for which yourEventListener
recognizesTriggers
and instantiates the specified Tekton objects
See our Tekton Triggers examples for ready-to-use example EventListener
definitions.
You must specify a Kubernetes service account in the serviceAccountName
field that the EventListener
will use to instantiate Tekton objects.
Tekton Trigger creates 2 clusterroles while installing with necessary permissions required for an eventlistener. You can directly create bindings for your serviceaccount with the clusterroles.
-
A Kubernetes RoleBinding with
tekton-triggers-eventlistener-roles
clusterrole. -
A Kubernetes ClusterRoleBinding with
tekton-triggers-eventlistener-clusterroles
clusterrole.You can checkout an example here.
-
If you're using
namespaceSelectors
in yourEventListener
, you will have to create an additionalClusterRoleBinding
withtekton-triggers-eventlistener-roles
clusterrole.
You can optionally specify one or more Triggers
that define the actions to take when the EventListener
detects a qualifying event. You can specify either a reference to an
external Trigger
object or reference/define the TriggerBindings
, TriggerTemplates
, and Interceptors
in the Trigger
definition. A Trigger
definition
specifies the following fields:
name
- (optional) a valid Kubernetes name that uniquely identifies theTrigger
interceptors
- (optional) a list ofInterceptors
that will process event payload data before passing it to the associatedTriggerBinding
bindings
- (optional) a list ofTriggerBindings
for thisTrigger
; you can either reference existingTriggerBindings
or embed their definitions directlytemplate
- (optional) aTriggerTemplate
for thisTrigger
; you can either reference an existingTriggerTemplate
or embed its definition directlytriggerRef
- (optional) a reference to an externalTrigger
Below is an example Trigger
definition that references the desired TriggerBindings
, TriggerTemplates
, and Interceptors
:
triggers:
- name: trigger-1
interceptors:
- github:
eventTypes: ["pull_request"]
bindings:
- ref: pipeline-binding # Reference to a TriggerBinding object
- name: message # Embedded Binding
value: Hello from the Triggers EventListener!
template:
ref: pipeline-template
Below is an example Trigger
definition that specifies a reference to an external Trigger
object:
triggers:
- triggerRef: trigger
Below is an example Trigger
definition that embeds a triggerTemplate
definition directly:
triggers:
- name: "my-trigger"
template:
spec:
params:
- name: "my-param-name"
resourceTemplates:
- apiVersion: "tekton.dev/v1beta1"
kind: TaskRun
metadata:
generateName: "pr-run-"
spec:
taskSpec:
steps:
- image: ubuntu
script: echo "hello there"
Below is an example Trigger
definition tailored to a multi-tenant scenario in which you may not
want all of your Trigger
objects to have the same permissions as the EventListener
. In such case,
you can specify a different service account at the Trigger
level. This service account overrides
the service account specified in the EventListener
.
triggers:
- name: trigger-1
serviceAccountName: trigger-1-sa
interceptors:
- github:
eventTypes: ["pull_request"]
bindings:
- ref: pipeline-binding
- ref: message-binding
template:
ref: pipeline-template
You must update the Role
assigned to the service account specified in the EventListener
as shown below
to allow it to impersonate the service account specified in the Trigger
:
rules:
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["impersonate"]
Specifying the URI for cloud event sink which receives cloud events during Trigger Processing.
spec:
cloudEventURI: http://eventlistener.free.beeceptor.com
TriggerGroups
is a feature that allows you to specify a set of interceptors that will process before a set of
Trigger
resources are processed by the eventlistener. The goal of this feature is described in
TEP-0053. TriggerGroups
allow for
a common set of interceptors to be defined inline in the EventListenerSpec
before Triggers
are invoked.
A TriggerGroup
definition specifies the following fields:
name
- (optional) a valid Kubernetes name that uniquely identifies theTriggerGroup
interceptors
- a list ofInterceptors
that will process event payload data before passing it to the downstreamTriggers
triggerSelector
- a combination of a KuberneteslabelSelector
and anamespaceSelector
as defined later in this document. These two fields work together to define theTriggers
that will be processed onceInterceptors
processing completes.
Below is an example EventListener that defines an inline triggerGroup
:
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: eventlistener
spec:
triggerGroups:
- name: github-pr-group
interceptors:
- name: "validate GitHub payload and filter on eventType"
ref:
name: "github"
params:
- name: "secretRef"
value:
secretName: github-secret
secretKey: secretToken
- name: "eventTypes"
value: ["pull_request"]
triggerSelector:
labelSelector:
matchLabels:
type: github-pr
This configuration would first process any event that is sent to the EventListener
and determine if it matches
the outlined conditions. If it passes these conditions, it will use the triggerSelector
matching criteria to determine
the target Trigger
resources to continue processing.
Any extensions
fields added during triggerGroup
processing are passed to the downstream Trigger
execution. This allows
for shared data across all Triggers that are processed after group execution completes. As an example, extensions.myfield
would
be available to all Trigger
resources matched by this group:
apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
name: eventlistener
spec:
triggerGroups:
- name: cel-filter-group
interceptors:
- name: "validate body and add field"
ref:
name: "cel"
params:
- name: "filter"
value: "body.action in ['opened', 'reopened']"
- name: "overlays"
value:
- key: myfield
expression: "body.pull_request.head.sha.truncate(7)"
triggerSelector:
namespaceSelector:
matchNames:
- foo
labelSelector:
matchLabels:
type: cel-preprocessed
At this time, each TriggerGroup
determines its own downstream Triggers, so if two separate groups select the same
downstream Trigger
resources, it may be executed multiple times. If you use this feature, ensure that Trigger
resources
are labeled to be queried by the appropriate set of TriggerGroups
.
You can optionally customize the sink deployment for your EventListener
using the resources
field. It accepts the following types of objects:
- Kubernetes Resource using the
kubernetesResource
field - Custom Resource objects via the
CustomResource
field
Legal values for the PodSpec
sub-fields for both kubernetesResource
and CustomResource
are:
ServiceAccountName
NodeSelector
Tolerations
Containers
Affinity
TopologySpreadConstraints
Legal values for the Containers
sub-field for kubernetesResource
and CustomResource
are:
kubernetesResource:
Resources
Env
LivenessProbe
ReadinessProbe
StartupProbe
CustomResource:
Resources
Env
Below is an example resources:
field definition specifying a kubernetesResource
object:
spec:
resources:
kubernetesResource:
serviceType: NodePort
servicePort: 80
spec:
template:
metadata:
labels:
key: "value"
annotations:
key: "value"
spec:
serviceAccountName: tekton-triggers-github-sa
nodeSelector:
app: test
tolerations:
- key: key
value: value
operator: Equal
effect: NoSchedule
The type and port for the Service
created for the EventListener
can be configured via the ServiceType
and ServicePort
specifications respectively. By default, the Service
type is set to ClusterIP
and port is set to 8080
.
spec:
resources:
kubernetesResource:
serviceType: LoadBalancer
servicePort: 8128
If you use a loadbalancer service, you can optionally define a LoadBalancerClass with the ServiceLoadBalancerClass
attribute.
spec:
resources:
kubernetesResource:
serviceType: LoadBalancer
serviceLoadBalancerClass: internal
You can optionally use the replicas
field to instruct Tekton Triggers to deploy more than one instance of your EventListener
in individual Kubernetes Pods.
If you do not specify this value, the default number of instances (and thus, the number of respective Pods) per EventListener
is 1. If you set a value for the replicas
field
while creating or upgrading the EventListener's
YAML file, that value overrides any value you set manually later as well as a value set by any other deployment
mechanism, such as HPA.
You can specify a Kubernetes Custom Resource object using the CustomResource
field. This field has one sub-field, runtime.RawExtension
that allows you to specify dynamic objects.
The CustomResource
object must abide by the contract shown below.
Contract-mandated CRD structure for the spec
field:
spec:
template:
metadata:
spec:
Contract-mandated CRD structure for the status
field:
type EventListenerStatus struct {
duckv1beta1.Status `json:",inline"`
// EventListener is addressable via the DNS address of the sink.
duckv1alpha1.AddressStatus `json:",inline"`
}
Note: The CRD must follow the WithPod{} spec.
Below is an example resources:
field definition specifying a CustomResource
object using a Knative Service:
Note: This example assumes that Knative is installed on your cluster.
spec:
resources:
customResource:
apiVersion: serving.knative.dev/v1
kind: Service
# metadata:
# name: knativeservice # name is optional; if not specified, Triggers substitutes the EventListener's name with an "el-" prefix, for example: el-github-knative-listener
spec:
template:
spec:
serviceAccountName: tekton-triggers-example-sa
containers:
- resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
An Interceptor
is a "catch-all" event processor for a specific platform that runs before the TriggerBinding
. It allows you to perform payload filtering,
verification (using a secret), transformation, define and test trigger conditions, and implement other useful processing. Once the event data passes through
an Interceptor
, it then goes to the Trigger
before you pass the payload data to the TriggerBinding
. You can also use an Interceptor
to modify the
behavior of the associated Trigger
.
For more information, see Interceptors
.
You can optionally specify a list of namespaces in which your EventListener
will search for Triggers
and instantiate the specified Tekton objects using the namespaceSelector
field.
If you omit this field, your EventListener
will only recognize Triggers
specified in its definition or found under one or more specified target labels.
Below is an example namespaceSelector
field that configures the EventListener
to use the foo
and bar
namespaces:
namespaceSelector:
matchNames:
- foo
- bar
If you want your EventListener
to recognize Triggers
across your entire cluster, use a wildcard between quote as the only namespace:
namespaceSelector:
matchNames:
- "*"
At present, if an EventListeners has Triggers
inside its own spec as well as namespace-selector
, Triggers
in spec as well as in selected namespaces will be processed for a request. Triggers
inside EventListener spec when using namespace-selector
mode is deprecated and ability to specify both will be removed.
You can optionally specify the labels for which your EventListener
recognizes Triggers
and instantiates the specified Tekton objects using the labelSelector
field.
This field uses the standard Kubernetes labelSelector
mechanism and supports the matchExpressions
sub-field. If you omit the labelSelector
field, the EventListener
accepts all resource labels.
Below is an example labelSelector
field definition that constrains your EventListener
to only recognize Triggers
within its own namespace that are labeled foo=bar
:
labelSelector:
matchLabels:
foo: bar
Below is an example labelSelector
field definition that uses the matchExpression
sub-field to specify expressions that allow the EventListener
to recognize Triggers
across all namespaces in the cluster:
namespaceSelector:
matchNames:
- *
labelSelector:
matchExpressions:
- {key: environment, operator: In, values: [dev,stage]}
- {key: trigger-phase, operator: NotIn, values: [testing]}
An EventListener
times out if it cannot process an event request within a timeout specified in controller.yaml. The timeouts are as follows:
-el-readtimeout
: Read timeout; default is 5 seconds.-el-writetimeout
: Write timeout; default is 40 seconds.-el-idletimeout
: Idle timeout; default is 120 seconds.-el-timeouthandler
: Server route handler timeout; default is 30 seconds.
To disable incoming payload validation for an EventListener, you can define an annotation tekton.dev/payload-validation: false
on EventListener.
apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
name: eventlistener
annotations:
tekton.dev/payload-validation: "false"
By default, payload validation is enabled and will be disabled only if the annotation is defined. Removing the annotation will enable the payload validation.
By default, each EventListener
automatically attaches the following labels to all resources it instantiates:
Name | Description |
---|---|
triggers.tekton.dev/eventlistener | Name of the EventListener that instantiated the resource. |
triggers.tekton.dev/trigger | Name of the Trigger that instantiated the resource. |
triggers.tekton.dev/eventid | UID of the incoming event. |
Note: Because they're used as labels, EventListener
and Trigger
names must conform to the Kubernetes syntax and character set requirements.
Tekton Triggers propagates all annotations that you include in your EventListener
to the Kubernetes service and deployment created by that EventListener
.
Keep in mind that annotations propagated from the EventListener
override annotations already present in its Kubernetes service and deployment.
Below is an example load balancer protocol annotation in an EventListener
definition that automatically propagates to the EventListener's
service:
apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
name: eventlistener
annotations:
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: https
An EventListener
responds with a 202 ACCEPTED
HTTP response when the EventListener
has been able to process the request and selected the appropriate triggers to process
based off the EventListener
configuration.
After detecting an event, the EventListener
responds with the following message:
{
"eventListener": "listener",
"namespace": "default",
"eventListenerUID": "ea71a6e4-9531-43a1-94fe-6136515d938c",
"eventID": "14a657c3-6816-45bf-b214-4afdaefc4ebd"
}
eventListenerUID
- UID of the target EventListener.eventID
- UID assigned to this event request
EventListener can acts as sink for CloudEvents. When it acts as such, then its response is different from above.
EventListener sends out this cloudevent as response assuming an EventListener with name listener
in the default
namespace with uuid ea71a6e4-9531-43a1-94fe-6136515d938c
:
type: dev.tekton.event.triggers.accepted.v1
source: listener
subject: default.listener accepted eventID
id: 14a657c3-6816-45bf-b214-4afdaefc4ebd
id
is assigned eventID
.
It also has following data:
{
"eventListener": "listener",
"namespace": "default",
"eventListenerUID": "ea71a6e4-9531-43a1-94fe-6136515d938c",
"eventID": "14a657c3-6816-45bf-b214-4afdaefc4ebd"
}
eventID
- UID assigned to this event request
These fields are included in EventListener
responses, but will be removed in a future release.
eventListener
- name of the target EventListener. UseeventListenerUID
instead.namespace
- namespace of the target EventListener. UseeventListenerUID
instead.
Tekton Triggers supports both HTTP and TLS-based HTTPS connections. To configure your EventListener
for TLS,
add the TLS_CERT
and TLS_KEY
reserved environment variables using the secretKeyRef
variable type, then
specify a secret
containing the cert
and key
files. See TEP-0027
and our TLS configuration example for more information.
Use the following command to get a list of EventListeners
deployed on your cluster along with their statuses:
kubectl get el
You will get a response similar to the following:
NAME ADDRESS AVAILABLE REASON READY REASON
tls-listener-interceptor http://el-tls-listener-interceptor.default.svc.cluster.local True MinimumReplicasAvailable
Where for each returned line, the column values are, from left to right:
NAME
- name of theEventListener
ADDRESS
- IP address or URL of theEventListener
AVAILABLE
- readiness state of the associatedDeployment
andService
REASON
- reason for the value displayed in theAVAILABLE
columnREADY
- readiness state of the Kubernetes Custom Resource object specified in theEventListener
REASON
- reason for the value displayed in theREADY
column
Note: The status messaging described above is being refactored. For more information, see Issue 932.
You can configure logging for your EventListener
s using the config-logging-triggers
ConfigMap
located in the tekton-pipelines
namespace (config-logging.yaml).
Tekton Triggers automatically reconciles this configmap into environment variables on your
event listener deployment.
To access your EventListener
logs, query your cluster for Pods whose eventlistener
label matches the name of your EventListener
object. For example:
kubectl get pods --selector eventlistener=my-eventlistener
The following pipeline metrics are available on the eventlistener
Service on port 9000
.
Name | Type | Labels/Tags | Status |
---|---|---|---|
eventlistener_triggered_resources |
Counter | kind =<kind> |
experimental |
eventlistener_event_received_count |
Counter | status =<status> |
experimental |
eventlistener_http_duration_seconds_[bucket, sum, count] |
Histogram | - | experimental |
Several kinds of exporters can be configured for an EventListener
, including Prometheus, Google Stackdriver, and many others.
You can configure metrics using the config-observability-triggers
config map in the EventListener
namespaces.
There is a config-observability-triggers
configmap in the tekton-pipelines
namespace that can be configured for the operation of the Triggers
webhook and controller components.
See the Knative documentation for more information about available exporters and configuration values.
EventListeners
create an underlying Kubernetes service (unless a user specifies a customResource
EventListener deployment).
By convention, this service is the same name as the EventListener prefixed with el
. So, an EventListener named foo
will create a service called el-foo
.
This service, by default is of type ClusterIP
which means it is only accessible within the cluster on which it is running.
You can expose this service as you would with any regular Kubernetes service. A few ways are highlighted below:
- Using a
LoadBalancer
Service type - Using a Kubernetes
Ingress
object - Using the NGINX Ingress Controller
- Using OpenShift Route
If your Kubernetes cluster supports external load balancers,
you can set the serviceType
field to LoadBalancer
to switch the Kubernetes service type:
spec:
resources:
kubernetesResource:
serviceType: LoadBalancer
Note: You can find the external IP of this service by running kubectl get svc/el-${EVENTLISTENER-NAME} -o=jsonpath='{.status.loadBalancer.ingress[0].ip}'
You can expose the service created by the EventListener using a regular Kubernetes Ingress.
To do this, you may first have to change the serviceType
to NodePort
:
spec:
resources:
kubernetesResource:
serviceType: NodePort
You can also use the Tekton create-ingress
task to configure an Ingress
object using self-signed certificates.
Below are instructions for configuring an OpenShift 4.2 cluster running API version v1.14.6+32dc4a0
. For more information,
see Route Configuration.
-
Obtain the name of your
EventListener
service:oc get el <EVENTLISTENR_NAME> -o=jsonpath='{.status.configuration.generatedName}'
-
Expose the
EventListener
service:oc expose svc/[el-listener] # REPLACE el-listener WITH YOUR SERVICE NAME FROM STEP 1
-
Obtain the IP address of the exposed route:
oc get route el-listener -o=jsonpath='{.spec.host}' # REPLACE el-listener WITH YOUR SERVICE NAME FROM STEP 1
-
Test the configuration with
curl
or set up a GitHub Webhook that sends events to it.
Below is a high-level walkthrough through the deployment of an EventListener
using a GitHub example provided by Tekton Triggers.
-
Instantiate the example
EventListener
on your cluster:kubectl create -f https://github.com/tektoncd/triggers/tree/main/examples/v1beta1/github
Tekton Triggers creates a new
Deployment
andService
for theEventListener
. using theEventListener
definition,metadata.labels
, and pre-existing values such as containerImage
,Name
, andPort
. Tekton Triggers uses theEventListener
name prefixed withel-
to name theDeployment
andService
when instantiating them. For example, if theEventListener
name isfoo
, theDeployment
andService
names are namedel-foo
. -
Use
kubectl
to verify theDeployment
is running on your cluster:kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE el-github-listener-interceptor 1/1 1 1 11s
-
Use
kubectl
to verify theService
is running on your cluster:kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE el-github-listener-interceptor ClusterIP 10.99.188.140 <none> 8080/TCP 52s
-
Obtain the URL on which the
EventListener
is listening for events:kubectl get eventlistener NAME ADDRESS AVAILABLE REASON github-listener-interceptor http://el-github-listener-interceptor.ptest.svc.cluster.local:8080 True MinimumReplicasAvailable
See our GitHub
EventListener
example to try instantiating anEventListener
locally.
EventListeners
are effectively Tekton clients that use HTTP to bypass the normal Kubernetes authentication
mechanism established through kubeconfig
files and the kubectl config
command tree. Because of this,
you must be conscious of your configuration decisions, such as:
- How to securely expose each
EventListener
to the outside of your cluster, - How to securely control how each
EventListener
and its associated objects, such asInterceptors
, interact with data on your cluster.
At the minimum, each EventListener
specifies its own Kubernetes Service account as explained earlier, and
it acts on all events it receives with the permissions of that service account. If your business needs mandate
more granular permission control across the Triggers
and Interceptors
specified in your EventListeners
,
you have the following options:
- Deploy each
EventListener
in its own namespace - Deploy multiple
EventListeners
in the same namespace - Specify a separate service account for each
Trigger
In this scenario, you create multiple EventListeners
that in turn use a variety of Triggers
and Interceptors
,
each EventListener
gets its own namespace. This way, you can use a different service account for each namespace
and tailor the permissions of those accounts to the functionality of their corresponding EventListeners
. Because
creating a namespace often instantiates the necessary service accounts based on pre-configured permissions, this
also simplifies the deployment process as you simply need to update the permissions associated with those accounts.
However, this approach has the following drawbacks:
- Namespaces with separately associated
Secrets
andServiceAccounts
can be the most expensive items in the Kubernetesetcd
store; on large clusters, the capacity of theetcd
store can become a concern. - Since each
EventListener
requires its own HTTP port to listen for events, you must configure your network to allow access to each corresponding IP address and port combination unless you configure an ingress abstraction layer, such as the KubernetesIngress
object,or OpenShift Route.
In this scenario, you create multiple EventListeners
in the same namespace. This will require customization of
the associated service account(s), secret(s), and RBAC, since the automatically generated defaults are not always
ideal, but you will not incur a significant etcd
store cost as in the multiple namespace scenario. Network security
and configuration overhead concerns, however, still apply as described earlier. You can also achieve a similar result
by specifying a separate service account for each Trigger
used across your EventListener
pool at the cost of
increased administration overhead.
The cloud event that is sent to a target URI
during Trigger processing. The types of events send for now are:
Type | Description |
---|---|
dev.tekton.event.triggers.started.v1 | triggers processing started in eventlistener |
dev.tekton.event.triggers.successful.v1 | triggers processing successful and a resource created |
dev.tekton.event.triggers.failed.v1 | triggers failed in eventlistener |
dev.tekton.event.triggers.done.v1 | triggers processing done in eventlistener handle |