Skip to content

A tool for managing kubernetes manifests with KCL

License

Notifications You must be signed in to change notification settings

tvandinther/knit

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

knit

knit is a tool containing the missing pieces for using KCL to define kubernetes manifests when using the rendered manifests pattern.

It knits together Helm and Kustomize, with KCL to help keep code manageable and most imortantly, type-safe.

KCL

KCL is a configuration language and a CNCF sandbox project. It is used as the way to define your data structures empowered with modern language features to enable you to write DRY code.

Helm

knit incorporates Helm so that you can leverage existing charts in KCL without having to run a pipeline such as helm template | kcl import. Instead, you can add helm charts using knit add helm which will create a vendored KCL module for the chart which you can import. Then you can render out the chart using the helm.template() function within your KCL when knit render is run.

Kustomize

knit also includes functions to interact with Kustomize to enable you to use existing transformations and overlays without requiring a full migration to KCL templating or to empower your KCL code with Kustomize features. Use the kustomize.build() function to run a kustomization.


Quick Start

Start by initialising a project in a directory. This command creates KCL module files just like kcl mod init does.

knit init

Add a helm chart to the project.

knit add helm https://stefanprodan.github.io/podinfo podinfo --version 6.7.1

In main.k, try rendering the default chart.

# main.k
import vendored.helm.podinfo
import knit.helm

[manifest for manifest in helm.template(podinfo.Chart{})]

knit render

You can also set values for the Helm chart, for example:

# main.k
import vendored.helm.podinfo
import knit.helm
import knit.kustomize
import manifests

_chart = podinfo.Chart {
    releaseName = "my-app"
    values = podinfo.Values {
        ingress = podinfo.ValuesIngress {
            enabled = True
        }
    }
}

_manifests = helm.template(_chart)

You can post-process the helm chart with Kustomize. The kustomization provided does not include resource relative path names. Instead, the resources are given as a list of KCL maps in an additional argument. For example, to set the namespace of all resources and to change the tag of the image you can do the following:

# main.k continued...
_kustomization = kustomize.Kustomization {
    namespace = "team-a"
    images = [{
        name = "ghcr.io/stefanprodan/podinfo"
        digest = "sha256:862ca45e61b32392f7941a1bdfdbe5ff8b6899070135f1bdca1c287d0057fc94"
    }]
}

_kustomized = kustomize.build(_kustomization, _manifests)

Then collect the list of manifests into a stream of YAML documents.

# main.k continued...
# We use the builtin yaml_stream function here because we cannot put a list and a field at the top-level in the same file
manifests.yaml_stream(_kustomized)

Finally, run render again and inspect the changes Kustomize has made:

knit render | grep -e "kind:" -e "image:" -e "namespace:"

The examples above are just minimal examples. Break things up into modules as required and utilise KCL's language features to make things work for you.

You can view this example here.

GitHub Action

A GitHub Action for the knit render command is also published to make it easy to generate your manifests in a GitHub Workflow. Below is an example snippet for how you can use it:

name: Render

on:
  push:
    branches:
      - main

jobs:
  test-action:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      # runs `knit render clusters/prod.k`
      - name: knit render
        uses: tvandinther/[email protected]
        with:
          filepath: clusters/prod.k

Tip: You can utilise KCL's file and yaml packages to create organised directories and files for your data. These directories and files will then be available to subsequent steps in your job where you may commit and push them to another repo/branch to help build your rendered manifest workflow.

Reference

Functions

The table below shows the KCL functions available to use from the knit module.

Import Function Description
import knit.helm helm.template(chart: helm.Chart) -> [kubernetes.Manifest] Runs an equivalent of helm template on the given chart and returns a list of kubernetes manifests.
import knit.kustomize kustomize.build(kustomization: kustomize.Kustomization, resources: [{str: any}]) -> [kubernetes.Manifest] Runs an equivalent of kustomize build with the given base kustomization and resources. Any resources given in the 2nd argument will be written to YAML and appended to the resources of the given kustomization. Keep in mind that the resources defined under resources should only be remote resources. Local resources should be first converted to KCL (you can use kcl import for this) and provided in the 2nd argument.

Schemas

The table below shows the KCL schemas available to use from the knit module.

Import Schema Description
import knit.helm helm.Chart Represents a helm chart to be parsed by the helm.template function.
import knit.kustomize kustomize.Kustomization The schema for a valid kustomization.yaml.
import knit.kubernetes kubernetes.Manifest A minimal schema for a Kubernetes resource manifest.

Notes

Kustomize

Mutations offered by Kustomize can often be performed using simple KCL. For example, using the namespace transformer can be done simply using KCL dict unions or schema instances. The following shows setting the namespace on a list of Manifests using both approaches.

# Using Kustomize
_kustomized = kustomize.build({namespace: "team-a"}, _manifests)

# Using a KCL schema instance
_kustomized = [m {metadata.namespace = "team-a"} for m in _manifests]

More complex transformation may be better off left to Kustomize to perform. You have the freedom to combine the power of both approaches.

KCL Plugins

knit uses KCL plugins. These plugins only work within KCL VMs where they are explicitly imported. This is done within the knit render command which uses the KCL sdk to instantiate a KCL interpreter with the custom plugins. This means that you will encounter errors if using kcl run on files which use the custom plugin functions.