<p>We have a AWS EKS running (setup using Pulumi), where we installed Tekton as described in the <a href="https://buildpacks.io/docs/tools/tekton/" rel="nofollow noreferrer">Cloud Native Buildpacks Tekton docs</a>. The <a href="https://github.com/jonashackt/tekton-argocd-eks" rel="nofollow noreferrer">example project is available</a>.</p>
<p><a href="https://github.com/jonashackt/tekton-argocd-eks/blob/main/pipelines/pipeline.yml" rel="nofollow noreferrer">Our Tekton pipeline</a> is configured like this (which is derived <a href="https://buildpacks.io/docs/tools/tekton/#43-pipeline" rel="nofollow noreferrer">from the Cloud Native Buildpacks Tekton docs</a> also):</p>
<pre><code>apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: buildpacks-test-pipeline
spec:
  params:
    - name: IMAGE
      type: string
      description: image URL to push
    - name: SOURCE_URL
      type: string
      description: A git repo url where the source code resides.
    - name: SOURCE_REVISION
      description: The branch, tag or SHA to checkout.
      default: &quot;&quot;
  workspaces:
    - name: source-workspace # Directory where application source is located. (REQUIRED)
    - name: cache-workspace # Directory where cache is stored (OPTIONAL)
  tasks:
    - name: fetch-repository # This task fetches a repository from github, using the `git-clone` task you installed
      taskRef:
        name: git-clone
      workspaces:
        - name: output
          workspace: source-workspace
      params:
        - name: url
          value: &quot;$(params.SOURCE_URL)&quot;
        - name: revision
          value: &quot;$(params.SOURCE_REVISION)&quot;
        - name: subdirectory
          value: &quot;&quot;
        - name: deleteExisting
          value: &quot;true&quot;
    - name: buildpacks # This task uses the `buildpacks` task to build the application
      taskRef:
        name: buildpacks
      runAfter:
        - fetch-repository
      workspaces:
        - name: source
          workspace: source-workspace
        - name: cache
          workspace: cache-workspace
      params:
        - name: APP_IMAGE
          value: &quot;$(params.IMAGE)&quot;
        - name: BUILDER_IMAGE
          value: paketobuildpacks/builder:base # This is the builder we want the task to use (REQUIRED)
</code></pre>
<p>We added <code>SOURCE_URL</code> and <code>SOURCE_REVISION</code> as parameters already.</p>
<p>The question is: <strong>How can we trigger a Tekton <code>PipelineRun</code> from GitLab CI</strong> (inside our <code>.gitlab-ci.yml</code>) adhering to the following requirements:</p>
<ul>
<li>simplest possible approach</li>
<li>Do not use the extra complexity introduced by <a href="https://github.com/tektoncd/triggers" rel="nofollow noreferrer">Tekton Triggers</a> (incl. <a href="https://github.com/tektoncd/experimental/tree/main/commit-status-tracker" rel="nofollow noreferrer">commit-status-tracker</a>) but still keep GitLab as the source of truth (e.g. see green/red pipeline runs on commits etc.)</li>
<li>report successfully run Tekton Pipelines as green GitLab CI Pipelines &amp; failed Tekton Pipelines as red GitLab CI Pipelines</li>
<li>preserve/stream the Tekton Pipeline logs into GitLab CI Pipeline logs - both in case of errors or success inside the Tekton Pipelines</li>
<li>use <a href="https://docs.gitlab.com/ee/ci/variables/predefined_variables.html" rel="nofollow noreferrer">GitLab CI Predefined Variables</a> for a generic approach</li>
</ul>

<p><strong>TLDR;</strong></p>
<p>I created a fully comprehensible example project showing all necessary steps and running pipelines here: <a href="https://gitlab.com/jonashackt/microservice-api-spring-boot/" rel="noreferrer">https://gitlab.com/jonashackt/microservice-api-spring-boot/</a> with the full <code>.gitlab-ci.yml</code> to directly trigger a Tekton Pipeline:</p>
<pre><code>image: registry.gitlab.com/jonashackt/aws-kubectl-tkn:0.21.0

variables:
  AWS_DEFAULT_REGION: 'eu-central-1'

before_script:
  - mkdir ~/.kube
  - echo &quot;$EKSKUBECONFIG&quot; &gt; ~/.kube/config
  - echo &quot;--- Testdrive connection to cluster&quot;
  - kubectl get nodes

stages:
  - build

build-image:
  stage: build
  script:
    - echo &quot;--- Create parameterized Tekton PipelineRun yaml&quot;
    - tkn pipeline start buildpacks-test-pipeline
      --serviceaccount buildpacks-service-account-gitlab
      --workspace name=source-workspace,subPath=source,claimName=buildpacks-source-pvc
      --workspace name=cache-workspace,subPath=cache,claimName=buildpacks-source-pvc
      --param IMAGE=$CI_REGISTRY_IMAGE
      --param SOURCE_URL=$CI_PROJECT_URL
      --param SOURCE_REVISION=$CI_COMMIT_REF_SLUG
      --dry-run
      --output yaml &gt; pipelinerun.yml

    - echo &quot;--- Trigger PipelineRun in Tekton / K8s&quot;
    - PIPELINE_RUN_NAME=$(kubectl create -f pipelinerun.yml --output=jsonpath='{.metadata.name}')

    - echo &quot;--- Show Tekton PipelineRun logs&quot;
    - tkn pipelinerun logs $PIPELINE_RUN_NAME --follow

    - echo &quot;--- Check if Tekton PipelineRun Failed &amp; exit GitLab Pipeline accordingly&quot;
    - kubectl get pipelineruns $PIPELINE_RUN_NAME --output=jsonpath='{.status.conditions[*].reason}' | grep Failed &amp;&amp; exit 1 || exit 0
</code></pre>
<p>Here are the brief steps you need to do:</p>
<p><strong>1. Choose a base image for your <code>.gitlab-ci.yml</code> providing <code>aws</code> CLI, <code>kubectl</code> and Tekton CLI (<code>tkn</code>)</strong></p>
<p>This is entirely up to you. I created an example project <a href="https://gitlab.com/jonashackt/aws-kubectl-tkn" rel="noreferrer">https://gitlab.com/jonashackt/aws-kubectl-tkn</a> which provides an image, which is based on the official <a href="https://hub.docker.com/r/amazon/aws-cli" rel="noreferrer">https://hub.docker.com/r/amazon/aws-cli</a> image and is accessible via <code>registry.gitlab.com/jonashackt/aws-kubectl-tkn:0.21.0</code>.</p>
<p><strong>2. CI/CD Variables for aws CLI &amp; Kubernetes cluster access</strong></p>
<p>Inside your GitLab CI project (or better: inside the group, where your GitLab CI project resides in) you need to create <code>AWS_ACCESS_KEY_ID</code>, <code>AWS_SECRET_ACCESS_KEY</code> as CI/CD Variables holding the the aws cli credentials (beware to <code>mask</code> them while creating them in order to prevent them beeing printed into the GitLab CI logs). Depending on your EKS clusters (or other K8s clusters) config, you need to provide a <code>kubeconfig</code> to access your cluster. One way is to create a GitLab CI/CD variable like <code>EKSKUBECONFIG</code> providing the necessary file (e.g. in the example project this is provided by Pulumi with <code>pulumi stack output kubeconfig &gt; kubeconfig</code>). In this setup using Pulumi there are no secret credentials inside the <code>kubeconfig</code> so the variable doesn't need to be masked. But be aware of possible credentials here and protect them accordingly if needed.</p>
<p><a href="https://i.stack.imgur.com/YF4CO.png" rel="noreferrer"><img src="https://i.stack.imgur.com/YF4CO.png" alt="enter image description here" /></a></p>
<p>Also define <code>AWS_DEFAULT_REGION</code> containing your EKS cluster's region:</p>
<pre><code># As we need kubectl, aws &amp; tkn CLI we use https://gitlab.com/jonashackt/aws-kubectl-tkn
image: registry.gitlab.com/jonashackt/aws-kubectl-tkn:0.21.0

variables:
  AWS_DEFAULT_REGION: 'eu-central-1'
</code></pre>
<p><strong>3. Use <code>kubeconfig</code> and testdrive cluster connection in <code>before_script</code> section</strong></p>
<p>Preparing things we need later inside other steps could be done inside the <code>before_script</code> section. So let's create the directory <code>~/.kube</code> there and create the file <code>~/.kube/config</code> from the contents of the variable <code>EKSKUBECONFIG</code>. Finally fire a <code>kubectl get nodes</code> to check if the cluster connection is working. Our <code>before_script</code> section now looks like this:</p>
<pre><code>before_script:
  - mkdir ~/.kube
  - echo &quot;$EKSKUBECONFIG&quot; &gt; ~/.kube/config
  - echo &quot;--- Testdrive connection to cluster&quot;
  - kubectl get nodes
</code></pre>
<p><strong>4. Pass parameters to Tekton PipelineRun</strong></p>
<p>Passing parameters via <code>kubectl</code> isn't trivial - or even needs to be done using a templating engine like Helm. But luckily the Tekton CLI has something for us: <a href="https://stackoverflow.com/questions/67405826/tekton-running-pipeline-via-passing-parameter"><code>tkn pipeline start</code> accepts parameters</a>. So we can transform the <a href="https://buildpacks.io/docs/tools/tekton/#5-create--apply-pipelinerun" rel="noreferrer">Cloud Native Buildpacks Tekton PipelineRun Yaml file</a> into a <code>tkn</code> CLI command like this:</p>
<pre><code>tkn pipeline start buildpacks-test-pipeline \
    --serviceaccount buildpacks-service-account-gitlab \
    --workspace name=source-workspace,subPath=source,claimName=buildpacks-source-pvc \
    --workspace name=cache-workspace,subPath=cache,claimName=buildpacks-source-pvc \
    --param IMAGE=registry.gitlab.com/jonashackt/microservice-api-spring-boot \
    --param SOURCE_URL=https://gitlab.com/jonashackt/microservice-api-spring-boot \
    --param SOURCE_REVISION=main \
    --timeout 240s \
    --showlog
</code></pre>
<p>Now here are some points to consider. First the name <code>buildpacks-test-pipeline</code> right after the <code>tkn pipeline start</code> works as an equivalent to the yaml files <code>spec: pipelineRef: name: buildpacks-test-pipeline</code> definition.</p>
<p>It will also work as a reference to the <code>Pipeline</code> object defined inside the file <a href="https://github.com/jonashackt/pulumi-eks-tekton/blob/trigger-build-from-gitlab/tekton-ci-config/pipeline.yml" rel="noreferrer">pipeline.yml</a> which starts with <code>metadata: name: buildpacks-test-pipeline</code> like:</p>
<p>apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: buildpacks-test-pipeline
...</p>
<p>Second to define workspaces isn't trivial. Luckily <a href="https://issueexplorer.com/issue/tektoncd/cli/1368" rel="noreferrer">there's help</a>. We can define a workspace in <code>tkn</code> CLI like this: <code>--workspace name=source-workspace,subPath=source,claimName=buildpacks-source-pvc</code>.</p>
<p>Third using the parameters as intended now becomes easy. Simply use <code>--param</code> accordingly. We also use <code>--showlog</code> to directly stream the Tekton logs into the commandline (or GitLab CI!) together with <code>--timeout</code>.</p>
<p>Finally using <a href="https://docs.gitlab.com/ee/ci/variables/predefined_variables.html" rel="noreferrer">GitLab CI Predefined variables</a> our <code>.gitlab-ci.yml</code>'s build stage looks like this:</p>
<pre><code>build-image:
  stage: build
  script:
    - echo &quot;--- Run Tekton Pipeline&quot;
    - tkn pipeline start buildpacks-test-pipeline
      --serviceaccount buildpacks-service-account-gitlab
      --workspace name=source-workspace,subPath=source,claimName=buildpacks-source-pvc
      --workspace name=cache-workspace,subPath=cache,claimName=buildpacks-source-pvc
      --param IMAGE=$CI_REGISTRY_IMAGE
      --param SOURCE_URL=$CI_PROJECT_URL
      --param SOURCE_REVISION=$CI_COMMIT_REF_SLUG
      --timeout 240s
      --showlog
</code></pre>
<p><strong>5. Solve the every GitLab CI Pipeline is green problem</strong></p>
<p>This could have been everything we need to do. But: right now every GitLab CI Pipeline is green, regardless of the Tekton Pipeline's status.</p>
<p>Therefore we remove <code>--showlog</code> and <code>--timeout</code> again, but add a <code>--dry-run</code> together with the <code>--output yaml</code> flags. Without the <code>--dry-run</code> the <code>tkn pipeline start</code> command would create a <code>PipelineRun</code> object definition already, which we can't create then using <code>kubectl</code> anymore:</p>
<pre><code>build-image:
  stage: build
  script:
    - echo &quot;--- Create parameterized Tekton PipelineRun yaml&quot;
    - tkn pipeline start buildpacks-test-pipeline
      --serviceaccount buildpacks-service-account-gitlab
      --workspace name=source-workspace,subPath=source,claimName=buildpacks-source-pvc
      --workspace name=cache-workspace,subPath=cache,claimName=buildpacks-source-pvc
      --param IMAGE=$CI_REGISTRY_IMAGE
      --param SOURCE_URL=$CI_PROJECT_URL
      --param SOURCE_REVISION=$CI_COMMIT_REF_SLUG
      --dry-run
      --output yaml &gt; pipelinerun.yml
</code></pre>
<p>Now that we removed <code>--showlog</code> and don't start an actual Tekton pipeline using <code>tkn</code> CLI, we need to create the pipeline run using:</p>
<pre><code>- PIPELINE_RUN_NAME=$(kubectl create -f pipelinerun.yml --output=jsonpath='{.metadata.name}')
</code></pre>
<p>Having the temporary variable <code>PIPELINE_RUN_NAME</code> available containing the exact pipeline run id, we can stream the Tekton pipeline logs into our GitLab CI log again:</p>
<pre><code>- tkn pipelinerun logs $PIPELINE_RUN_NAME --follow
</code></pre>
<p>Finally we need to check for Tekton pipeline run's status and exit our GitLab CI pipeline accordingly in order to prevent red Tekton pipelines resulting in green GitLab CI pipelines. Therefore let's check the status of the Tekton pipeline run first. This can be achieved <a href="https://stackoverflow.com/a/55077916/4964553">using <code>--output=jsonpath='{.status.conditions[*].reason}'</code></a> together with a <code>kubectl get pipelineruns</code>:</p>
<pre><code>kubectl get pipelineruns $PIPELINE_RUN_NAME --output=jsonpath='{.status.conditions[*].reason}'
</code></pre>
<p>Then we pipe the result into <a href="https://stackoverflow.com/a/11612379/4964553">a <code>grep</code> which checks, if <code>Failed</code> is inside</a> the <code>status.condiditons.reason</code> field.</p>
<p>Finally we use a bash onliner (which is <code>&lt;expression to check true or false&gt; &amp;&amp; command when true || command when false</code>) to issue the suitable <code>exit</code> command (see <a href="https://askubuntu.com/a/892605">https://askubuntu.com/a/892605</a>):</p>
<pre><code>- kubectl get pipelineruns $PIPELINE_RUN_NAME --output=jsonpath='{.status.conditions[*].reason}' | grep Failed &amp;&amp; exit 1 || exit 0
</code></pre>
<p>Now every GitLab CI Pipeline becomes green, when the Tekton Pipeline succeeded - and gets red when the Tekton Pipeline failed. <a href="https://gitlab.com/jonashackt/microservice-api-spring-boot/-/pipelines" rel="noreferrer">The example project has some logs</a> if you're interested. It's pretty cool to see the Tekton logs inside the GitLab CI logs:</p>
<p><a href="https://i.stack.imgur.com/XJnP7.png" rel="noreferrer"><img src="https://i.stack.imgur.com/XJnP7.png" alt="enter image description here" /></a></p>
