Skip to main content

Kratix Ep 9: Promise CI/CD with GitHub Actions

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
kratix - This article is part of a series.
Part 9: This Article
If you treat your Kratix Promises as manual YAML scripts executed from a laptop, your IDP will eventually break. Promises are software products. They have versions (v1, v2). They have bugs. They require automated testing. Let’s build a CI/CD pipeline to automate the release of our Platform Engineering APIs.

1. The Promise Repository Structure
#

A standard Kratix Promise consists of two primary components:

  1. The Pipeline Source Code: The bash, Python, or Go code that actually processes the developer’s claim.
  2. The Promise YAML Definition: The Custom Resource Definition (CRD) and the configuration that references the Pipeline’s compiled Docker image.

To manage this properly, you should organize your Git repository (e.g., acmecorp-promises) like a standard software project:

kratix-promises/
└── redis/
    ├── pipeline/
    │   ├── Dockerfile
    │   └── execute.sh
    └── promise.yaml

2. The CI Phase: Building the Pipeline
#

When a Platform Engineer makes a change to execute.sh (for example, adding a new feature that automatically provisions a Grafana dashboard alongside the Redis database), they will open a Pull Request (PR) in GitHub.

We need GitHub Actions to automatically detect this PR, build the Docker image, and push it to our Docker registry, tagged dynamically with the Git commit hash.

Create a file at .github/workflows/ci.yml:

name: CI - Build Promise Pipelines

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build-redis-pipeline:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Login to DockerHub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and Push Pipeline Image
        uses: docker/build-push-action@v4
        with:
          context: ./redis/pipeline
          push: true
          # Crucial: Tag the image with the unique Git SHA!
          tags: acmecorp/redis-pipeline:${{ github.sha }}

3. The CD Phase: Releasing the Promise
#

Once the new Docker image is pushed to the registry, we have a problem. The promise.yaml file in our repository is still hardcoded to look for an old image (e.g., image: acmecorp/redis-pipeline:v1).

We need to dynamically update that YAML file to point to our newly built github.sha image, and then automatically deploy that updated Promise to the Kratix Platform Cluster.

We can use yq (a powerful command-line YAML processor) inside our GitHub Action to rewrite the image tag on the fly.

Add this deployment job to your workflow file:

  deploy-promise:
    needs: build-redis-pipeline
    runs-on: ubuntu-latest
    # Only deploy to the cluster if the code is merged to main!
    if: github.ref == 'refs/heads/main' 
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Install yq
        run: sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 && sudo chmod +x /usr/local/bin/yq

      - name: Inject new Image Tag into Promise YAML
        run: |
          # This command searches for the specific container in the Promise and replaces its image tag
          yq e -i '.spec.workflows.resource.configure[0].spec.containers[0].image = "acmecorp/redis-pipeline:${{ github.sha }}"' ./redis/promise.yaml

      - name: Setup Kubeconfig
        run: |
          mkdir -p ~/.kube
          echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config

      - name: Deploy Promise to Kratix Platform Cluster
        run: |
          kubectl apply -f ./redis/promise.yaml

4. The Platform Release Workflow
#

Let’s review the complete lifecycle of a Platform Engineering update:

  1. Feature Request: The E-Commerce Developers ask the Platform Team for Redis High-Availability (HA) support.
  2. Platform Engineering: A Platform Engineer edits the execute.sh bash script in the kratix-promises repository to output HA YAML if the developer selects size: large.
  3. Pull Request: The Engineer opens a PR.
  4. CI Build: GitHub Actions automatically builds the new Pipeline Docker image and tags it as redis-pipeline:a1b2c3d.
  5. Merge: The Platform Tech Lead reviews the bash script and merges the PR into main.
  6. CD Deploy: GitHub Actions updates the promise.yaml with the new a1b2c3d image tag and runs kubectl apply against the Platform Cluster.

Instantly, the API in the Platform Cluster is updated. The very next time an E-Commerce developer requests a Redis instance, Kratix will boot the brand new pipeline image, and the developer will receive an HA cluster.


Troubleshooting & Common Errors
#

  1. kubectl apply fails in GitHub Actions due to permissions

    • Root Cause: The KUBECONFIG secret you provided to GitHub Actions belongs to a Kubernetes Service Account that does not have ClusterAdmin permissions. Kratix Promises contain CRDs, which require cluster-wide RBAC privileges to install.
    • Solution: Ensure the CI/CD Service Account is bound to the cluster-admin ClusterRole inside the Platform Cluster.
  2. Existing Developer Claims are not updating to the new Pipeline

    • Root Cause: Updating a Promise does not automatically trigger a re-run of existing claims. If a developer provisioned Redis v1 yesterday, they will stay on v1 until they manually edit their YAML (triggering an update), or until a Platform Engineer forces a reconciliation.
    • Solution: This is intentional behavior designed to prevent breaking production environments. If you want to force all existing Redis instances to upgrade to v2, Kratix provides APIs to manually trigger pipeline re-runs across all claims.

Conclusion & Next Steps
#

By automating the build and deployment of Promises, your Platform Team can iterate incredibly fast. You can treat your Internal Developer Platform like a true SaaS product, releasing new features, fixing bugs, and deploying updates securely.

However, as you deploy hundreds of workloads across dozens of clusters, how do you monitor them? If a pipeline fails in the Platform cluster, or ArgoCD fails in a Worker cluster, how do you know?

In Episode 10: Observability and Operations, we will instrument the Kratix ecosystem with Prometheus and Grafana to build a centralized monitoring dashboard for your entire IDP fleet.

kratix - This article is part of a series.
Part 9: This Article