Functional YAML templating
Snipper is a project that started with the question “What if managing a bunch
of YAML for sophisticated Helm Charts didn’t have to involve Go text templating?”
Inspired by the Lift Framework snippets, Snipper aims to separate the
structure file from the transformations that need to be applied to it.
The most important thing: transformations are YAML too.
More formally, transformations define a function that takes in YAML and produces
different YAML on the other end. These functions are composable, so that you can
layer them on top of each other. Conditional statements are exchanged with
the decision to apply or not to apply a particular function to your templates.
The way that makes the most sense to apply those conditions will vary based on
the complexity of what you’re building, and Snipper remains unopinionated on
how you do that.
Snipper is still very new, and I expect rough edges and missing functionality.
Please let me know what you think by opening an issue. I welcome
bug reports, feature requests, and just general feedback on what you think.
Though Snipper itself is new, the ideas it’s built on are over a decade old,
and I believe are proven out with the experience of Lift Framework developers.
Please let me know what you think.
Snipper supports:
image=alpine
containers)Snipper does not support:
Snipper can be installed by downloading a stable binary for your system from the
releases page. Once installed somewhere useful on your PATH
I
recommend checking out the usage instructions first:
snipper - snippet style transformers for YAML
Usage: snipper template.yaml transformer.yaml [transformer.yaml [transformer.yaml ...]]
Alternately, if you want to work with the cutting edge code and you have a
working Go environment, you can do the following:
$ go get github.com/farmdawgnation/snipper
$ go install github.com/farmdawgnaton/snipper
Using Snipper from the command line is designed to be pretty straighforward.
Simply invoke the binary, provide a template YAML file, and then any number of
transformers that will be applied in that order.
Transformers are just YAML files, too, with a minor twist: the top level keys
of the YAML object are interpreted as instructions for Snipper. They are called
selectors. Conceptually, these are very similar to the CSS Selectors that
have been in use on the web for a long time. They merely have a tweaked syntax
that makes more sense in YAML-land. The selectors describe the “coordinates” of
where the value (the thing that ends up in the rendered YAML) should go.
Let’s take a concrete example. I’ve got some YAML describing a dog named Shadow.
# shadow-template.yaml
name: Shadow
type: Doggo
Shadow is a very good dog, and I want her YAML to indicate that. I could apply
the following selector to add a goodDog
key to Shadow’s YAML:
# good-dog.yaml
'goodDog': true
If I applied this to my YAML I would end up with:
goodDog: true
name: Shadow
type: Doggo
We could even get a bit more complex and replace her type
with an array using
this transform:
# collie-dogs.yaml
'type':
- Doggo
- Collie
And I could invoke snipper and ask it to apply both of these to my template:
$ snipper shadow-template.yaml good-dog.yaml collie-dogs.yaml
goodDog: true
name: Shadow
type:
- Doggo
- Collie
Snipper successfully separates the rules that need to be applied to a template
from the template itself.
Snipper’s selector syntax has the following rules:
'goodDog'
as the selector was good enough to get the:
). In a Kubernetes pod spec the containers are at 'spec:containers'
in.
) are exceptionally[]
) it does something with arrays.[]
just selects all arrays at a location. So, spec
[]
[0]
) selects a specific elementspec
[image=alpine]
only matches members of yourimage
set to alpine
.Selectors will, by default, replace the value at the coordinates they select
with the value that appears in the transform definition. You can alter this
behavior by adding an action character to the end of your selector:
+
which causes the'spec
[image=alpine]:image+': latest
.I encourage you to browse the examples folder and try some of them
out for yourself to see what they do.
To build this project you’ll need:
Once cloned run dep ensure
to get the dependencies. Then go build
to your
heart’s content. I’m not a fan of vendored repositories, so please restrain
yourself from opening a PR or issue along the lines of “you should have checked
in your vendor directory.”
Matt Farmer works at MailChimp and occasionally blogs at
farmdawgnation.com. He’s primarily a JVM
developer, is learning Go, and does agree that there are situations where that is
the more sensible choice.