kvert lets you generate yaml files in a declarative way. You can for example use it to manage your Kubernetes manifests.
It supports including parts of definitions into other ones, variables, generating files with a different shape based on a profile, reading values from environment variables... All of this allows you to manage your YAML files in an effective way.
The tool leverages the EDN format and the Aero library.
Why kvert ? It's simple, powerful and extensible.
I think neither templating or using YAML to generate more YAML are good solutions to manage Kubernetes resources and that's why I built this tool.
For Linux (x86-64), download the kvert binary from the release page and put it in your PATH. This binary is built using GraalVM, more targets may be added soon (help welcome).
You can alternatively download the jar file and then run it with java -jar kvert.jar (Java 17 needed).
A docker image is also provided. Note that this image uses java as well so executing it is a bit slower than the static binary built with GraalVM.
All example described below can be done using the Docker image by executing in the directory containing your templates docker run -v $(pwd):/data mcorbin/kvert:v0.2.0 <command>. The files will be availables in /data. Example: docker run -v $(pwd):/data mcorbin/kvert:v0.2.0 yaml -t /data/example.edn.
Once kvert installed, you are ready to use it. Let's for example generate a yaml file from a simple EDN definition. Put in pod.edn this content:
;; this is a comment
{:apiVersion "v1"
:kind "Pod"
:metadata {:name "dnsutils"
:namespace "default"}
:spec {:containers [{:name "dnsutils"
:image "k8s.gcr.io/e2e-test-images/jessie-dnsutils:1.3"
:command ["sleep" "3600"]
:imagePullPolicy "ifNotPresent"}]
:restartPolicy "Always"}}Now run kvert yaml --template pod.edn:
---
apiVersion: v1
kind: Pod
metadata:
name: dnsutils
namespace: default
spec:
containers:
- name: dnsutils
image: k8s.gcr.io/e2e-test-images/jessie-dnsutils:1.3
command:
- sleep
- '3600'
imagePullPolicy: ifNotPresent
restartPolicy: AlwaysYou can pass the -o (or --output) flag to save the output into a file.
As you can see, we can easily translate EDN to YAML. You can define multiple YAML resources into the same file as well:
[{:name "yaml-file-1"}
{:name "yaml-file-2"}]kvert will output:
---
name: yaml-file-1
---
name: yaml-file-2EDN supports readers, which can be used to extend it. Let's now put in pod.edn this content:
{:apiVersion "v1"
:kind "Pod"
:metadata {:name "dnsutils"
:namespace #kvert/var :namespace}
:spec {:containers [{:name "dnsutils"
:image #join ["k8s.gcr.io/e2e-test-images/jessie-dnsutils:" #kvert/var :container-version]
:command ["sleep" #or [#env SLEEP_DURATION 3600]]
:imagePullPolicy #profile {:production "ifNotPresent"
:default "Always"}}]
:restartPolicy "Always"}}As you an see, we use a few readers (which start with #) in this file:
#kvert/varwhich will replace the next keyword (:namespacefor example here) with a variable value#joinwhich will concatenate several values together#orwhich allows you to define default values#envto read values from environment variables#profilewhich create a switch based on the value on the profile you used to runkvert(more on that later). This reader also supports default values in:default
Readers can be combined together, like in ["sleep" #or [#env SLEEP_DURATION 3600]] in this example which will first read the SLEEP_DURATION environment variable and fallback to 3600 if it's not defined.
Let's create a new file named config.edn:
{:variables {:namespace "default"
:container-version "1.3"}
:profile :production}This file defines variables (referenced by #kvert/var in the pod.edn file) and the profile (:production).
Launch kvert yaml --template pod.edn -c config.edn, the output is:
---
apiVersion: v1
kind: Pod
metadata:
name: dnsutils
namespace: default
spec:
containers:
- name: dnsutils
image: k8s.gcr.io/e2e-test-images/jessie-dnsutils:1.3
command:
- sleep
- 3600
imagePullPolicy: ifNotPresent
restartPolicy: AlwaysThank to the readers we are able to customize our manifest. We could for example use variables and the profile to have variation between environment, kubernetes clusters...
We configured in this example the profile into the config.edn file. You can also set it by configuring the PROFILE environment variable when running kvert.
Aero, the library used by kvert to parse EDN files supports tons of readers out of the box. You can find them in the library documentation. Here are some interesting ones:
#include
For example, the #include reader allows you to include an EDN file into another one. Let's create a file named example.edn containing:
{:apiVersion "v1"
:kind "Pod"
:metadata {:labels #include "labels.edn"}}labels.edn (which can also contain readers if you need to) being:
{:foo "bar"
:environment "prod"}The output of kvert yaml --template example.edn will be:
---
apiVersion: v1
kind: Pod
metadata:
labels:
foo: bar
environment: prod#kvert/include
THe #kvert/include reader works exactly like #include but allows you to pass additional variables and configure another profile to the included file:
{:apiVersion "v1"
:kind "Pod"
:metadata {:labels #kvert/include {:path "labels.edn"
:variables {:foo "bar"}
:profile :prod}}}In this example, the content of the labels.edn file will be included. Additional variables (:foo in this example) are also provided and the profile is also overrided.
#ref
Another cool one is #ref, let's modify our example.edn with:
{:apiVersion "v1"
:kind "Pod"
:metadata {:name "foo"
:labels {:name #ref [:metadata :name]}}}This file will produce using kvert yaml --template example.edn:
---
apiVersion: v1
kind: Pod
metadata:
name: foo
labels:
name: fooAs you can see, ref allows you to reference another part of your edn file.
Don't hesitate to check the Aero documentation for more examples !
You can use kvert to convert an existing YAML file to EDN. This can help you to get started with the tool by using existing YAML files. An example with this file named pod.yml:
---
apiVersion: v1
kind: Pod
metadata:
name: dnsutils
namespace: default
spec:
containers:
- name: dnsutils
image: k8s.gcr.io/e2e-test-images/jessie-dnsutils:1.3
command:
- sleep
- '3600'
imagePullPolicy: ifNotPresent
restartPolicy: Alwayskvert edn --template pod.yaml will produce:
[{:apiVersion "v1",
:kind "Pod",
:metadata {:name "dnsutils", :namespace "default"},
:spec
{:containers
[{:name "dnsutils",
:image "k8s.gcr.io/e2e-test-images/jessie-dnsutils:1.3",
:command ["sleep" "3600"],
:imagePullPolicy "ifNotPresent"}],
:restartPolicy "Always"}}]