# Automated deployment with Tekton

Take a real example Podinfo (opens new window) application and create full pipeline for it...

# Fork podinfo application

Fork the Podinfo (opens new window) repository podinfo:

cd tmp
hub clone "stefanprodan/podinfo"
hub -C "podinfo" fork

Output:

Cloning into 'podinfo'...
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 5266 (delta 0), reused 3 (delta 0), pack-reused 5256
Receiving objects: 100% (5266/5266), 9.52 MiB | 1.93 MiB/s, done.
Resolving deltas: 100% (2342/2342), done.
Updating ruzickap
From https://github.com/stefanprodan/podinfo
 * [new branch]      gh-pages   -> ruzickap/gh-pages
 * [new branch]      master     -> ruzickap/master
 * [new branch]      v0.x       -> ruzickap/v0.x
 * [new branch]      v1.x       -> ruzickap/v1.x
 * [new branch]      v3.x       -> ruzickap/v3.x
new remote: ruzickap

# Create Tekton Triggers configuration

Create new namespace:

kubectl create namespace getting-started

Create the admin user, role and rolebinding:

cat << EOF | kubectl apply -f -
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tekton-triggers-admin
  namespace: getting-started
rules:
- apiGroups:
  - tekton.dev
  resources:
  - eventlisteners
  - triggerbindings
  - triggertemplates
  - pipelineresources
  verbs:
  - get
- apiGroups:
  - tekton.dev
  resources:
  - pipelineruns
  - pipelineresources
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - apps
  resources:
  - deployments
  verbs:
  - get
  - list
  - watch
  - create
  - patch
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tekton-triggers-admin
  namespace: getting-started
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: tekton-triggers-admin-binding
  namespace: getting-started
subjects:
  - kind: ServiceAccount
    name: tekton-triggers-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tekton-triggers-admin
EOF

Create the create-webhook user, role and rolebinding:

cat << EOF | kubectl apply -f -
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tekton-triggers-createwebhook
  namespace: getting-started
rules:
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - get
  - list
  - create
  - update
  - delete
- apiGroups:
  - tekton.dev
  resources:
  - eventlisteners
  verbs:
  - get
  - list
  - create
  - update
  - delete
- apiGroups:
  - networking.istio.io
  resources:
  - virtualservices
  - gateways
  verbs:
  - create
  - get
  - list
  - delete
  - update
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tekton-triggers-createwebhook
  namespace: getting-started
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: tekton-triggers-createwebhook
  namespace: getting-started
subjects:
  - kind: ServiceAccount
    name: tekton-triggers-createwebhook
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tekton-triggers-createwebhook
EOF

Create secret for Harbor registry to let Tekton pipeline to upload the container image:

kubectl create secret docker-registry -n getting-started harbor-docker-config \
  --docker-server="${CONTAINER_REGISTRY_SERVER}" \
  --docker-username="${CONTAINER_REGISTRY_USERNAME}" \
  --docker-password="${CONTAINER_REGISTRY_PASSWORD}"

Install the Pipeline:

cat << \EOF | kubectl apply -f -
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: build-docker-image-from-git-task
  namespace: getting-started
spec:
  inputs:
    resources:
      - name: docker-source
        type: git
    params:
      - name: pathToDockerFile
        description: The path to the dockerfile to build
        default: /workspace/docker-source/Dockerfile
      - name: pathToContext
        description:
          The build context used by Kaniko
          (https://github.com/GoogleContainerTools/kaniko#kaniko-build-contexts)
        default: /workspace/docker-source
  outputs:
    resources:
      - name: image-source
        type: image
  volumes:
    - name: docker-config
      secret:
        secretName: harbor-docker-config
        items:
          - key: .dockerconfigjson
            path: config.json
    - name: shared-storage
      emptyDir: {}
  steps:
    - name: build
      image: gcr.io/kaniko-project/executor
      env:
        - name: "DOCKER_CONFIG"
          value: "/builder/home/.docker/"
      command:
        - /kaniko/executor
      args:
        - --dockerfile=$(inputs.params.pathToDockerFile)
        - --destination=$(outputs.resources.image-source.url)
        - --context=$(inputs.params.pathToContext)
        - --skip-tls-verify
      volumeMounts:
        - name: docker-config
          mountPath: /builder/home/.docker/
---
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: deploy-locally
  namespace: getting-started
spec:
  inputs:
    resources:
      - name: image-source
        type: image
  outputs:
    resources:
    - name: event-to-sink
      type: cloudEvent
  steps:
    - name: run-kubectl
      image: lachlanevenson/k8s-kubectl:latest
      command:
      - sh
      args:
      - -ce
      - |
        set -ex
        kubectl run podinfo --image $(inputs.resources.image-source.url) -o yaml --dry-run | kubectl apply -f -
---
apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
  name: getting-started-pipeline
  namespace: getting-started
spec:
  resources:
  - name: docker-source
    type: git
  - name: image-source
    type: image
  - name: event-to-sink
    type: cloudEvent
  tasks:
    - name: build-docker-image-from-git-task-run
      taskRef:
        name: build-docker-image-from-git-task
      params:
      - name: pathToDockerFile
        value: Dockerfile
      - name: pathToContext
        value: /workspace/docker-source/
      resources:
        inputs:
        - name: docker-source
          resource: docker-source
        outputs:
        - name: image-source
          resource: image-source
    - name: deploy-locally
      taskRef:
        name: deploy-locally
      resources:
        inputs:
          - name: image-source
            resource: image-source
            from:
              - build-docker-image-from-git-task-run
        outputs:
          - name: event-to-sink
            resource: event-to-sink
---
apiVersion: v1
kind: Service
metadata:
  name: event-display
  namespace: getting-started
  labels:
    app: event-display
spec:
  type: ClusterIP
  ports:
    - name: listener
      port: 8080
      protocol: TCP
  selector:
    app: event-display
---
  apiVersion: v1
  kind: Pod
  metadata:
    name: event-display
    namespace: getting-started
    labels:
      name: event-display
  spec:
    hostname: event-display
    containers:
    - image: gcr.io/knative-releases/github.com/knative/eventing-sources/cmd/event_display
      name: web
EOF

Install the TriggerTemplate, TriggerBinding and EventListener:

cat << EOF | kubectl apply -f -
apiVersion: tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
  name: getting-started-triggertemplate
  namespace: getting-started
spec:
  params:
    - name: gitrevision
      description: The git revision
      default: master
    - name: gitrepositoryurl
      description: The git repository url
    - name: image_registry_url
      description: The container registry url
    - name: namespace
      description: The namespace to create the resources
  resourcetemplates:
    - apiVersion: tekton.dev/v1alpha1
      kind: PipelineResource
      metadata:
        name: source-repo-\$(uid)
        namespace: \$(params.namespace)
      spec:
        type: git
        params:
        - name: revision
          value: \$(params.gitrevision)
        - name: url
          value: \$(params.gitrepositoryurl)
    - apiVersion: tekton.dev/v1alpha1
      kind: PipelineResource
      metadata:
        name: image-source-\$(uid)
        namespace: \$(params.namespace)
      spec:
        type: image
        params:
          - name: url
            value: \$(params.image_registry_url)
    - apiVersion: tekton.dev/v1alpha1
      kind: PipelineResource
      metadata:
        name: event-to-sink-\$(uid)
        namespace: \$(params.namespace)
      spec:
        type: cloudEvent
        params:
          - name: targetURI
            value: http://event-display.getting-started.svc.cluster.local
    - apiVersion: tekton.dev/v1alpha1
      kind: PipelineRun
      metadata:
        name: getting-started-pipeline-run-\$(uid)
        namespace: \$(params.namespace)
      spec:
        serviceAccount: tekton-triggers-admin
        pipelineRef:
          name: getting-started-pipeline
        resources:
          - name: docker-source
            resourceRef:
              name: source-repo-\$(uid)
          - name: image-source
            resourceRef:
              name: image-source-\$(uid)
          - name: event-to-sink
            resourceRef:
              name: event-to-sink-\$(uid)
---
apiVersion: tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
  name: getting-started-pipelinebinding
  namespace: getting-started
spec:
  params:
    - name: gitrevision
      value: \$(body.head_commit.id)
    - name: namespace
      value: getting-started
    - name: gitrepositoryurl
      value: "https://github.com/\$(body.repository.full_name)"
    - name: image_registry_url
      value: "harbor.${MY_DOMAIN}/library/\$(body.repository.name)"
---
apiVersion: tekton.dev/v1alpha1
kind: EventListener
metadata:
  name: getting-started-listener
  namespace: getting-started
spec:
  serviceAccountName: tekton-triggers-admin
  triggers:
    - binding:
        name: getting-started-pipelinebinding
      template:
        name: getting-started-triggertemplate
EOF

# Configure webhook

Create Task which will create Istio Gateway and VirtualService to handle incoming webhook form GitHub:

cat << \EOF2 | kubectl apply -f -
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: create-istio-gateway-virtualservice
  namespace: getting-started
spec:
  volumes:
  - name: work
    emptyDir: {}

  inputs:
    params:
    - name: TLScredentialName
      description: "Specify the secret with wildcard certificate"
    - name: ExternalDomain
      description: "The external domain for the EventListener"
    - name: Service
      description: "The name of the Service used in the VirtualService"
    - name: ServicePort
      description: "The service port that the VirtualService is being created on"

  steps:
  - name: create-istio-gateway-virtualservice
    image: lachlanevenson/k8s-kubectl:latest
    command:
    - sh
    args:
    - -ce
    - |
      set -ex
      cat << EOF | kubectl create -f -
      apiVersion: networking.istio.io/v1alpha3
      kind: Gateway
      metadata:
        name: $(inputs.params.Service)-gateway
      spec:
        selector:
          istio: ingressgateway
        servers:
        - port:
            number: 443
            name: https-$(inputs.params.Service)
            protocol: HTTPS
          hosts:
          - $(inputs.params.ExternalDomain)
          tls:
            credentialName: $(inputs.params.TLScredentialName)
            mode: SIMPLE
            privateKey: sds
            serverCertificate: sds
      ---
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: $(inputs.params.Service)-virtual-service
      spec:
        hosts:
        - $(inputs.params.ExternalDomain)
        gateways:
        - $(inputs.params.Service)-gateway
        http:
        - route:
          - destination:
              host: $(inputs.params.Service)
              port:
                number: $(inputs.params.ServicePort)
      EOF
EOF2

Create TaskRun to start create-istio-gateway-virtualservice Task:

cat << EOF | kubectl apply -f -
apiVersion: tekton.dev/v1alpha1
kind: TaskRun
metadata:
  name: create-istio-gateway-virtualservice-run
  namespace: getting-started
spec:
  taskRef:
    name: create-istio-gateway-virtualservice
  inputs:
    params:
    - name: TLScredentialName
      value: ingress-cert-${LETSENCRYPT_ENVIRONMENT}
    - name: ExternalDomain
      value: getting-started.${MY_DOMAIN}
    - name: Service
      value: el-getting-started-listener
    - name: ServicePort
      value: "8080"
  timeout: 1000s
  serviceAccount: tekton-triggers-createwebhook
EOF

Create webhook Task:

cat << \EOF | kubectl apply -f -
apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: create-webhook
  namespace: getting-started
spec:
  volumes:
  - name: github-secret
    secret:
      secretName: $(inputs.params.GitHubSecretName)

  inputs:
    params:
    - name: ExternalDomain
      description: "The external domain for the EventListener e.g. `$(inputs.params.EventListenerName).<PROXYIP>.nip.io`"
    - name: GitHubUser
      description: "The GitHub user"
    - name: GitHubRepo
      description: "The GitHub repo where the webhook will be created"
    - name: GitHubOrg
      description: "The GitHub organization where the webhook will be created"
    - name: GitHubSecretName
      description: "The Secret name for GitHub access token. This is always mounted and must exist"
    - name: GitHubAccessTokenKey
      description: "The GitHub access token key name"
    - name: GitHubSecretStringKey
      description: "The GitHub secret string key name"
    - name: GitHubDomain
      description: "The GitHub domain. Override for GitHub Enterprise"
      default: "github.com"
    - name: WebhookEvents
      description: "List of events the webhook will send notifications for"
      default: "[\\\"push\\\",\\\"pull_request\\\"]"

  steps:
  - name: create-webhook
    image: pstauffer/curl:latest
    volumeMounts:
    - name: github-secret
      mountPath: /var/secret
    command:
    - sh
    args:
    - -ce
    - |
      set -e
      echo "Create Webhook"
      if [ $(inputs.params.GitHubDomain) = "github.com" ];then
        curl -v -d "{\"name\": \"web\",\"active\": true,\"events\": $(inputs.params.WebhookEvents),\"config\": {\"url\": \"https://$(inputs.params.ExternalDomain)\",\"content_type\": \"json\",\"insecure_ssl\": \"1\" ,\"secret\": \"$(cat /var/secret/$(inputs.params.GitHubSecretStringKey))\"}}" -X POST -u $(inputs.params.GitHubUser):$(cat /var/secret/$(inputs.params.GitHubAccessTokenKey)) -L https://api.github.com/repos/$(inputs.params.GitHubOrg)/$(inputs.params.GitHubRepo)/hooks
      else
        curl -d "{\"name\": \"web\",\"active\": true,\"events\": $(inputs.params.WebhookEvents),\"config\": {\"url\": \"https://$(inputs.params.ExternalDomain)/\",\"content_type\": \"json\",\"insecure_ssl\": \"1\" ,\"secret\": \"$(cat /var/secret/$(inputs.params.GitHubSecretStringKey))\"}}" -X POST -u $(inputs.params.GitHubUser):$(cat /var/secret/$(inputs.params.GitHubAccessTokenKey)) -L https://$(inputs.params.GitHubDomain)/api/v3/repos/$(inputs.params.GitHubOrg)/$(inputs.params.GitHubRepo)/hooks
      fi
EOF

Create secret with GitHub Personal Access Token (opens new window):

cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: webhook-secret
  namespace: getting-started
stringData:
  token: ${GITHUB_TOKEN}
  secret: random-string-data
EOF

Create TaskRun to initiate Webhook:

sleep 20 # Wait for DNS created by previous TaskRun
cat << EOF | kubectl apply -f -
apiVersion: tekton.dev/v1alpha1
kind: TaskRun
metadata:
  name: create-webhook-run
  namespace: getting-started
spec:
  taskRef:
    name: create-webhook
  inputs:
    params:
    - name: GitHubOrg
      value: "ruzickap"
    - name: GitHubUser
      value: "ruzickap"
    - name: GitHubRepo
      value: "podinfo"
    - name: GitHubSecretName
      value: webhook-secret
    - name: GitHubAccessTokenKey
      value: token
    - name: GitHubSecretStringKey
      value: secret
    - name: ExternalDomain
      value: getting-started.${MY_DOMAIN}
  timeout: 1000s
  serviceAccount: tekton-triggers-createwebhook
EOF
sleep 5

Verify if the TaskRuns were successfully executed:

kubectl get taskruns.tekton.dev -n getting-started

Output:

NAME                                      SUCCEEDED   REASON      STARTTIME   COMPLETIONTIME
create-istio-gateway-virtualservice-run   True        Succeeded   80s         71s
create-webhook-run                        Unknown     Pending     2s

Look at the logs from create-istio-gateway-virtualservice TaskRun:

tkn taskrun logs -n getting-started create-istio-gateway-virtualservice-run

Output:

[create-istio-gateway-virtualservice] + + kubectl create -f -
[create-istio-gateway-virtualservice] cat
[create-istio-gateway-virtualservice] gateway.networking.istio.io/el-getting-started-listener-gateway created
[create-istio-gateway-virtualservice] virtualservice.networking.istio.io/el-getting-started-listener-virtual-service created

Look at the logs from create-webhook-run TaskRun:

tkn taskrun logs -n getting-started create-webhook-run

Output:

[create-webhook] Create Webhook
[create-webhook] Note: Unnecessary use of -X or --request, POST is already inferred.
[create-webhook]   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
[create-webhook]                                  Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 140.82.118.6...
[create-webhook] * TCP_NODELAY set
[create-webhook] * Connected to api.github.com (140.82.118.6) port 443 (#0)
[create-webhook] * ALPN, offering h2
[create-webhook] * ALPN, offering http/1.1
[create-webhook] * successfully set certificate verify locations:
[create-webhook] *   CAfile: /etc/ssl/certs/ca-certificates.crt
[create-webhook]   CApath: none
[create-webhook] * TLSv1.2 (OUT), TLS handshake, Client hello (1):
[create-webhook] } [232 bytes data]
[create-webhook] * TLSv1.2 (IN), TLS handshake, Server hello (2):
[create-webhook] { [108 bytes data]
[create-webhook] * TLSv1.2 (IN), TLS handshake, Certificate (11):
[create-webhook] { [2851 bytes data]
[create-webhook] * TLSv1.2 (IN), TLS handshake, Server key exchange (12):
[create-webhook] { [300 bytes data]
[create-webhook] * TLSv1.2 (IN), TLS handshake, Server finished (14):
[create-webhook] { [4 bytes data]
[create-webhook] * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
[create-webhook] } [37 bytes data]
[create-webhook] * TLSv1.2 (OUT), TLS change cipher, Client hello (1):
[create-webhook] } [1 bytes data]
[create-webhook] * TLSv1.2 (OUT), TLS handshake, Finished (20):
[create-webhook] } [16 bytes data]
[create-webhook] * TLSv1.2 (IN), TLS change cipher, Client hello (1):
[create-webhook] { [1 bytes data]
[create-webhook] * TLSv1.2 (IN), TLS handshake, Finished (20):
[create-webhook] { [16 bytes data]
[create-webhook] * SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
[create-webhook] * ALPN, server accepted to use http/1.1
[create-webhook] * Server certificate:
[create-webhook] *  subject: C=US; ST=California; L=San Francisco; O=GitHub, Inc.; CN=*.github.com
[create-webhook] *  start date: Jul  8 00:00:00 2019 GMT
[create-webhook] *  expire date: Jul 16 12:00:00 2020 GMT
[create-webhook] *  subjectAltName: host "api.github.com" matched cert's "*.github.com"
[create-webhook] *  issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=DigiCert SHA2 High Assurance Server CA
[create-webhook] *  SSL certificate verify ok.
[create-webhook] * Server auth using Basic with user 'ruzickap'
[create-webhook] > POST /repos/ruzickap/podinfo/hooks HTTP/1.1
[create-webhook] > Host: api.github.com
[create-webhook] > Authorization: Basic cnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxU3MA==
[create-webhook] > User-Agent: curl/7.60.0
[create-webhook] > Accept: */*
[create-webhook] > Content-Length: 195
[create-webhook] > Content-Type: application/x-www-form-urlencoded
[create-webhook] >
[create-webhook] } [195 bytes data]
[create-webhook] * upload completely sent off: 195 out of 195 bytes
[create-webhook] &lt; HTTP/1.1 201 Created
[create-webhook] &lt; Date: Fri, 27 Dec 2019 10:19:37 GMT
[create-webhook] &lt; Content-Type: application/json; charset=utf-8
[create-webhook] &lt; Content-Length: 688
[create-webhook] &lt; Server: GitHub.com
[create-webhook] &lt; Status: 201 Created
[create-webhook] &lt; X-RateLimit-Limit: 5000
[create-webhook] &lt; X-RateLimit-Remaining: 4992
[create-webhook] &lt; X-RateLimit-Reset: 1577445296
[create-webhook] &lt; Cache-Control: private, max-age=60, s-maxage=60
[create-webhook] &lt; Vary: Accept, Authorization, Cookie, X-GitHub-OTP
[create-webhook] &lt; ETag: "a9xxxxxxxxxxxxxxxxxxxxxxxxxxxxe3"
[create-webhook] &lt; X-OAuth-Scopes: admin:org_hook, admin:repo_hook, delete_repo, read:org, repo, user:email
[create-webhook] &lt; X-Accepted-OAuth-Scopes: admin:repo_hook, public_repo, repo, write:repo_hook
[create-webhook] &lt; Location: https://api.github.com/repos/ruzickap/podinfo/hooks/170061618
[create-webhook] &lt; X-GitHub-Media-Type: github.v3; format=json
[create-webhook] &lt; Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type
[create-webhook] &lt; Access-Control-Allow-Origin: *
[create-webhook] &lt; Strict-Transport-Security: max-age=31536000; includeSubdomains; preload
[create-webhook] &lt; X-Frame-Options: deny
[create-webhook] &lt; X-Content-Type-Options: nosniff
[create-webhook] &lt; X-XSS-Protection: 1; mode=block
[create-webhook] &lt; Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin
[create-webhook] &lt; Content-Security-Policy: default-src 'none'
[create-webhook] &lt; Vary: Accept-Encoding
[create-webhook] &lt; X-GitHub-Request-Id: CEXX:XXXXX:XXXXXXX:XXXXXXX:XXXXXXB9
[create-webhook] &lt;
[create-webhook] { [688 bytes data]
100   883  100   688  100   195   1746    494 --:--:-- --:--:-- --:--:--  2241
[create-webhook] * Connection #0 to host api.github.com left intact
[create-webhook] {
[create-webhook]   "type": "Repository",
[create-webhook]   "id": 170061618,
[create-webhook]   "name": "web",
[create-webhook]   "active": true,
[create-webhook]   "events": [
[create-webhook]     "pull_request",
[create-webhook]     "push"
[create-webhook]   ],
[create-webhook]   "config": {
[create-webhook]     "content_type": "json",
[create-webhook]     "insecure_ssl": "1",
[create-webhook]     "secret": "********",
[create-webhook]     "url": "https://getting-started.mylabs.dev"
[create-webhook]   },
[create-webhook]   "updated_at": "2019-12-27T10:19:37Z",
[create-webhook]   "created_at": "2019-12-27T10:19:37Z",
[create-webhook]   "url": "https://api.github.com/repos/ruzickap/podinfo/hooks/170061618",
[create-webhook]   "test_url": "https://api.github.com/repos/ruzickap/podinfo/hooks/170061618/test",
[create-webhook]   "ping_url": "https://api.github.com/repos/ruzickap/podinfo/hooks/170061618/pings",
[create-webhook]   "last_response": {
[create-webhook]     "code": null,
[create-webhook]     "status": "unused",
[create-webhook]     "message": null
[create-webhook]   }
[create-webhook] }

You should also see in the GitHub / Settings / Webhook the registration https://github.com/ruzickap/podinfo/settings/hooks/ (opens new window):

GitHub Webhooks

After clicking on the Webhook you can see the details:

GitHub Webhooks - Details

In case you are troubleshooting the incoming traffic from GitHub the look at the logs of the pod. You should be able to see the details in the kubectl logs.

kubectl get pods -n getting-started -l eventlistener=getting-started-listener
sleep 5

Output:

NAME                                          READY   STATUS    RESTARTS   AGE
el-getting-started-listener-fcffc467d-xwsfp   1/1     Running   0          2m6s

# Change the source code of the app

Trigger the pipeline by calling the:

echo "Trigger build" >> podinfo/README.md
git -C podinfo commit -s -a -m "Standard version"
git -C podinfo push ruzickap
sleep 30
kubectl --timeout=10m -n getting-started wait --for=condition=Succeeded pipelineruns --all
sleep 5

Output:

[master d4120e6] Standard version
 1 file changed, 1 insertion(+), 1 deletion(-)
Warning: Permanently added '[ssh.github.com]:443,[192.30.253.122]:443' (RSA) to the list of known hosts.
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 325 bytes | 162.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:ruzickap/podinfo.git
   948de81..d4120e6  master -> master
pipelinerun.tekton.dev/getting-started-pipeline-run-fzrtg condition met

Look at the logs of the newly deployed pod (look at "Starting podinfo"):

PODINFO_POD=$(kubectl get pods -n getting-started -l=run=podinfo -o jsonpath="{.items[0].metadata.name}")
kubectl -n getting-started logs -n getting-started ${PODINFO_POD}

Output:

{"level":"info","ts":"2019-11-27T15:00:58.907Z","caller":"podinfo/main.go:120","msg":"Starting podinfo","version":"3.1.5","revision":"164a27b33b09d1b50fad277a60a6c19d353cb9d8","port":"9898"}

Let's try to change the code of the application:

sed -i "s/Starting podinfo/Starting podinfo - new Tekton build version/" podinfo/cmd/podinfo/main.go
git -C podinfo diff
git -C podinfo commit -s -a -m "String changed"
git -C podinfo push ruzickap
sleep 20

Output:

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
modified: cmd/podinfo/main.go
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
@ cmd/podinfo/main.go:120 @ func main() {
    }

    // log version and port
    logger.Info("Starting podinfo",
    logger.Info("Starting podinfo - new Tekton build version",
        zap.String("version", viper.GetString("version")),
        zap.String("revision", viper.GetString("revision")),
        zap.String("port", srvCfg.Port),
[master 4d9f5bc] String changed
 1 file changed, 1 insertion(+), 1 deletion(-)
Warning: Permanently added '[ssh.github.com]:443,[192.30.253.122]:443' (RSA) to the list of known hosts.
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 459 bytes | 459.00 KiB/s, done.
Total 5 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:ruzickap/podinfo.git
   d4120e6..4d9f5bc  master -> master

Wait for the pipelines to complete:

kubectl --timeout=10m -n getting-started wait --for=condition=Succeeded pipelineruns --all
sleep 5

Output:

pipelinerun.tekton.dev/getting-started-pipeline-run-dzqdl condition met
pipelinerun.tekton.dev/getting-started-pipeline-run-fzrtg condition met

Check how the logs of the newly deployed podinfo logs should contain "Starting podinfo - new Tekton build version":

PODINFO_POD=$(kubectl get pods -n getting-started -l=run=podinfo -o jsonpath="{.items[0].metadata.name}")
kubectl -n getting-started delete pod -n getting-started ${PODINFO_POD}
sleep 5
PODINFO_POD=$(kubectl get pods -n getting-started -l=run=podinfo -o jsonpath="{.items[0].metadata.name}")
kubectl -n getting-started logs -n getting-started ${PODINFO_POD}
cd ..

Output:

pod "podinfo-6ccfb5f9b6-bwrbn" deleted
{"level":"info","ts":"2019-12-27T10:28:22.304Z","caller":"podinfo/main.go:120","msg":"Starting podinfo - new Tekton build version","version":"3.1.5","revision":"4d9f5bc71bd9a8c7ae44a9cd9631e45067727a2e","port":"9898"}