Yong Wei Lun
Cloud Zone

Cloud Zone

Tekton CI/CD Part 3: Building custom Interceptor for Triggers

Photo by Fleur on Unsplash

Tekton CI/CD Part 3: Building custom Interceptor for Triggers

Trigger pipeline based on path changes from last commit

Yong Wei Lun's photo
Yong Wei Lun
·Mar 31, 2022·

4 min read

Table of contents

On previous post, we have built Triggers that will react to push events.

Problem

However, if we were using monorepo, we would like to trigger different Pipeline build based on the path change

For example the repository like this

|-- ui/
|-- backend/
|-- tools/

When changes on ui are detected, it will only trigger the Pipeline for ui

Solution 1: use another CI to trigger tekton

We could use another CI that supports this, for example

Write the path changes on the CI, and trigger Tekton

For example, on GitHub Actions

on:
  push:
    branches:
      - 'main'
    paths:
      - 'my-project/**'
jobs:
  trigger-project:
    runs-on: ubuntu-latest
    steps:
      - name: Trigger Tekton
        run: curl -X POST --data '{}' https://trigger-tekton.example.com

On Tekton, we need to add a listener

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
metadata:
  name: general-listener
spec:
  serviceAccountName: tekton-triggers-example-sa
  triggers:
    - name: push-trigger
      bindings:
      - ref: push-binding
      template:
        ref: simple-pipeline-template

Add TriggerBinding with hard-coded values

branch can be dynamically set, but that requires writing custom ClusterInterceptor, more on solution 2

apiVersion: triggers.tekton.dev/v1beta1
kind: TriggerBinding
metadata:
  name: push-binding
  namespace: tekton
spec:
  params:
    - name: git_branch
      value:  main
    - name: git_url
      value: https://github.com/tektoncd/triggers

this only handles one project inside monorepo, we need to repeat the process (some files can be reused, eg TriggerBinding) if we have more than one projects to be deployed on a monorepo

Solution 2: implement custom ClusterInterceptor

This solution will not depend on another CI service but will need to write ClusterInterceptor. A ClusterInterceptor must meet the following requirements

I have written a ClusterInterceptor, which will get the file changes based on the last commit, and match with the glob pattern on the config

Check out the examples directory for Tekton complete example

How it works

The ClusterInterceptor will do the following when it is called by Tekton Triggers

1. clone

It will clone the repository with --depth 2 to save time and network bandwidth

2. diff

It will run git diff, to get the file changes from the last commit

git --no-pager diff --name-only HEAD^

# example output
README.md
examples/01_deployment.yaml
examples/04_cluster-interceptor.yaml
examples/06_trigger.yaml
main.go

3. match

It will match the file changes based on glob pattern on config

example of config.yaml

repos:
  demo:
    # can be http format or ssh format
    url: https://github.com/WLun001/path-change 
    paths:
      # only match of if files inside examples directory
      - "examples/**"

response

if match: set the continue field in the response to true.

{
  "extensions": {
    "paths": "MATCH"
  },
  "continue": true,
  "status": {}
}

if not match: set the continue field to false

{
  "extensions": {
    "paths": "NOT_MATCH"
  },
  "continue": false,
  "status": {}
}

The response will be interpreted by Tekton

How to use

Check out the examples directory for Tekton complete example

Deploy path-change

Check out the example deployment on examples directory.

For git credentials, it works with any git credentials, just need to bind the volume to the deployment. The example is showing ssh credentials

After deploying path-change, we need to add ClusterInterceptor to reference it

apiVersion: triggers.tekton.dev/v1alpha1
kind: ClusterInterceptor
metadata:
  name: path-change-interceptor
spec:
  clientConfig:
    service:
      name: path-change
      namespace: tekton

And we can use it on EventListener, and set the repo that we are interested in at params field

For example, if we specify demo as our repo, based on the config on the previous example, it will check against https://github.com/WLun001/path-change and with the path that match "examples/**" pattern

apiVersion: triggers.tekton.dev/v1beta1
kind: EventListener
spec:
  triggers:
    - name: push-trigger
      interceptors:
         ...
        - ref:
            # here we can use our custom ClusterInterceptor
            name: path-change-interceptor
            kind: ClusterInterceptor
          params:
           # on params put the repo we would like to check with
            - name: repo
              value: demo # this will map the values in config.yaml
           ...

After deploying everything, we could do a test

Test it out

Do a port forward

kubectl port-forward -n tekton service/el-simple-listener 8080

Test it locally

Send example payload

The response status code should be 202 Accepted

HMAC tool used to create X-Hub-Signature.

curl -v \
-H 'X-GitHub-Event: push' \
-H 'X-Hub-Signature: sha1=87b1adbb9aca10522739f9f94d372afd1542e498' \
-H 'Content-Type: application/json' \
-d '{"ref": "refs/heads/main", "repository": {"git_url": "https://github.com/WLun001/cncf-demo.git"}}' \
http://localhost:8080

You should see a new PipelineRun is running

Test with GitHub

Create a tunnel using tools like ngrok

ngrok http 8080

Add the generated URL to Payload URL

Make a commit that matches the path pattern and push, you should see a new PipelineRun is running

图片.png

Dealing with monorepo

Back to our problem, to solve it, we can create a config.yaml that specify all the paths pattern and git URL, and create different EventListener and Webhook for each app

For example, we would create 3 EventListeners and Webhooks in order to trigger the correct Pipelines for ui, backend and tools.

|-- ui
|-- backend/
|-- tools/

Conclusion

Tekton is very powerful with its extensibility, it might feel complex at the first sight, but in a long run really worth investing time on it,

We have built a custom ClusterInterceptor that works well with Tekton Triggers, and solve our problems with monorepo.

Next, we will look at the advanced use case of Tekton Pipelines, running Docker-Compose on Pipelines

Did you find this article valuable?

Support Yong Wei Lun by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this