/kyaml-a2c

Primary LanguageGoApache License 2.0Apache-2.0

Convert Kubernetes Annotations to Field Comments

kyaml-a2c is a command line utility and a go library which converts Kubernetes manifest annotations to field comments.

Why?

Flux's Image Automation Controller uses comments in Kubernetes manifests as a way to configure the fields that will be patched as new images are released as part of a GitOps workflow.

Here's an example of a HelmRelease resource where spec.values.image.repository and spec.values.image.tag will be updated by the image automation controller when a new image that matches the podinfo ImagePolicy is pushed to GitHub Container Registry.

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: podinfo
  namespace: default
spec:
  values:
    image:
      repository: ghcr.io/stefanprodan/podinfo # {"$imagepolicy": "flux-system:podinfo:name"}
      tag: 5.0.0  # {"$imagepolicy": "flux-system:podinfo:tag"}

The $imagepolicy markers in the field comments are what determine which fields will be patched by Flux when a new image is released.

This works well if the manifests are written manually, but it doesn't work with most manifest generation tools because they can't generate comments. It's common nowadays manifests to be generated from jsonnet templates, but it's impossible to produce comments in yaml files from those templates.

kyaml-a2c can be used as part of the manifest generation pipeline to convert standard Kubernetes annotations to field comments.

Basic Example

The manifest above can be generated by running kyaml-a2c with the following input:

apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  annotations:
    comment.kyaml.io/set.spec.values.image.repository: '{"$imagepolicy": "flux-system:podinfo:name"}'
    comment.kyaml.io/set.spec.values.image.tag: '{"$imagepolicy": "flux-system:podinfo:tag"}'
  name: podinfo
  namespace: default
spec:
  values:
    image:
      repository: ghcr.io/stefanprodan/podinfo 
      tag: 5.0.0

The file will be patched in-place and the value of the annotations will be set as field comments of spec.values.image.repository and spec.values.image.tag.

Installation

The binary can be downloaded from the releases or installed with go

go install github.com/toddkazakov/kyaml-a2c@latest

Running

After you install the tool, you can run it by providing the path to the manifests

$GOPATH/bin/kyaml-a2c --path=./examples
cat ./examples/manifests.yaml

Configuration Options:

  • --path REQUIRED - takes a path to a file or a directory containing the kubernetes manifests
  • --prefix - the annotation key prefix. Defaults to comment.kyaml.io/set.

Annotation Syntax

Kubernetes resource annotations are key/value pairs. kyaml-a2c looks for annotations keys which start with the configured prefix (defaults to comment.kyaml.io/set.) followed by the field path in the yaml file. The path segement separator is .. A comment with the annotation value will be added to the referenced field.

Examples

kyaml-a2c will only set comments to scalar fields and will ignore field that don't exist.

Updating Map Fields:

Input:

annotations:
  comment.kyaml.io/set.spec.values.image.tag: '{"$imagepolicy": "flux-system:podinfo:tag"}'

Output:

...
spec:
  values:
    image:
      repository: ghcr.io/stefanprodan/podinfo # '{"$imagepolicy": "flux-system:podinfo:tag"}
...

Updating List Elements:

Input:

annotations:
  comment.kyaml.io/set.spec.containers.[name=nginx].image: '{"$imagepolicy": "flux-system:nginx:image"}'
  comment.kyaml.io/set.spec.containers.[1].image: '{"$imagepolicy": "flux-system:debian:image"}'

Output:

...
spec:
  volumes:
  - name: html
    emptyDir: {}
  containers:
  - name: 1st
    image: nginx # {"$imagepolicy": "flux-system:nginx:image"}
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx/html
  - name: 2nd
    image: debian # {"$imagepolicy": "flux-system:debian:image"}
    volumeMounts:
    - name: html
      mountPath: /html
    command: ["/bin/sh", "-c"]
    args:
      - while true; do
          date >> /html/index.html;
          sleep 1;
        done
...