I got an itch a few days ago to try out an idea. This was, in part, motivated by a few conversations about Helm templates for Kubernetes. Helm Charts rely on weaving templating into your Kubernetes object definitions. The templating that Helm uses hails from the same tradition as ERB templates, mixed PHP/HTML pages, etc.
I started thinking on what it would be like if I could template Kubernetes definitions using something resembling Lift snippets - where the template and the instructions that lead to the final result are totally separated. And, since I’m working on getting a bit more familiar with Go - I decided to build it using Go. What I ended up with is Snipper.
I’ll start by saying that Snipper is very much a proof of concept.
Snipper is available on GitHub. Pre-built binaries are available on the releases page, or you can get it using
go install if you have a Go development environment set up.
Once it’s installed you can use it by providing a template YAML file - which is just a regular old YAML file. You can then invoke snipper with the template and any number of transformers like so:
$ snipper template.yaml transformer1.yaml transformer2.yaml...
The resulting YAML will get sent to standard out.
Let’s say that I’ve got this Pod definition pulled mercilessly from the Kubernetes documentation.
apiVersion: v1 kind: Pod metadata: name: myapp-pod labels: app: myapp spec: containers: - name: myapp-container image: busybox command: - 'sh' - '-c' - 'echo The app is running! && sleep 3600'
I can use snipper to modify this pod to my heart’s content. Let’s say that I would like to add a
team label to the container. I might define this in the transformer YAML file like so:
'metadata:labels:team': Farmdawg Nation
Transformer YAML files follow some special conventions. Specifically:
metadataselects the metadata node at the root.
metadata:labelsselects the labels under
metadata, but would not select the labels under
specif there were one.
+causes snipper to append a value to a string or a list instead of replacing the value
as a selector selects all array members at the level you’re selecting at.
spec:containers::command:would select all components of the command array for all of the containers above.
These rules are still heavily in flux. They will likely change, but let’s take a few more examples of things we can do.
We could suffix the pod name with the number 2:
We could change the image to
We could add an additional container:
'spec:containers+': - name: myapp-hello image: hello-world
And of course, a single transformer file can have multiple rules:
'metadata:labels:team': Farmdawg Nation 'metadata:labels:alerting': enabled 'spec:containers+': - name: myapp-hello image: hello-world
All of these instructions remain separate from the actual template itself. You no longer have to litter a single file with the template and what needs to happen to it. With Snipper, these concepts are separate.
You’ll notice that there aren’t any logic constructs. That’s actually an intentional decision. Snipper by itself isn’t a total replacement for a templating engine. However, when combined with an orchestration layer that conditionally applies transforms, you wouldn’t be far off. I have not (yet) written such an orchestration layer, but I could imagine a tool that defines, say, a:
I’m not sure. Part of this was scratching an itch. Part of it was wanting to develop some better informed opinions about Go. What I do next with this project will depend heavily on whether or not folks find it useful / interesting. So, if you find it useful / interesting please star it on GitHub, file bugs and feature requests, or share with your friends.