Automate CRD updates
In the previous post about Handle CRDs with GitOps I showed that CRDs for a helm chart can be generated using the helm cli and this enabled us to manage the full lifecycle of CRDs. In this post I will show automation around this, so that a helm chart version update triggers updates to the CRDs aswell.
Requirements
- helm
- yq
Creating recipies for CRD generation
Considering our previous example for a HelmRelease for cert-manager
# helm.yaml
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: jetstack
namespace: cert-manager
spec:
interval: 15m
url: https://charts.jetstack.io
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: cert-manager
namespace: cert-manager
spec:
interval: 5m
targetNamespace: cert-manager
chart:
spec:
chart: cert-manager
version: "v1.18.2"
sourceRef:
kind: HelmRepository
name: jetstack
interval: 15m
install:
crds: Skip
values:
installCRDs: false
...
we can create the following recipe for generating CRDs using Makefiles (other technologies can be used)
version := $(shell yq '. | select(.kind == "HelmRelease") | .spec.chart.spec.version' helm.yaml)
url := $(shell yq '. | select(.kind == "HelmRepository") | .spec.url' helm.yaml)
chart := $(shell yq '. | select(.kind == "HelmRelease") | .spec.chart.spec.chart' helm.yaml)
kube_version := v1.22.0 # required >= 1.22.0
release_name := $(shell yq '. | select(.kind == "HelmRelease") | .metadata.name' helm.yaml)
crds.yaml: helm.yaml
helm template $(release_name) $(chart) --repo $(url) --version $(version) --set installCRDs=true --kube-version $(kube_version) | yq '. | select(.kind == "CustomResourceDefinition")' > $@
now any updates made to helm.yaml will trigger a generation and overwrite of crds.yaml if make is run.
Creating a Github action
I have now showed that we can update CRDs manually if our helm chart version changes using make. Now the choice of choosing make makes our life a little bit difficult. In the root of our project I will create one Makefile to call make on all other Makefile's
base_dir := apps # path to our collection of HelmReleases
charts_with_crds := $(shell find $(base_dir) -name 'Makefile' -printf "%h\n")
all: $(charts_with_crds)
$(charts_with_crds):
@$(MAKE) -C $@
.PHONY: all $(charts_with_crds)
above Makefile is really complex and wasn't fun to write at all. Let's finish with the Github Action
# .github/workflows/update-crds.yaml
name: Update CRDs
on:
pull_request:
paths:
- "apps/*/**.yaml"
- "apps/*/**.yml"
jobs:
update-crds:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
- uses: alexellis/arkade-get@master
with:
helm: latest
yq: latest
- name: Get changed helm files
id: changed-files
uses: tj-actions/changed-files@v46
with:
files: |
apps/*/helm.yaml
- name: Touch all changed helm files
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
run: |
for file in ${ALL_CHANGED_FILES}; do
echo "touching file: ${file}"
touch ${file}
done
- name: Update CRDs
run: make
- uses: EndBug/add-and-commit@v9
with:
add: ./apps/*/crds.yaml
message: "Update CRDs"
So now when updates are made to our helm.yaml files updated crds.yaml are commited back by above workflow. The benefit is that changes to CRDs becomes really transparent.