It’s been a while since we last blogged about spruce, and since then, a lot of things have changed. Here’s a quick summary of the spruce
operators, and new features as of Spruce 1.4.2:
play.spruce.cf
Want a playground to easily experiment with spruce
operators, or debug/track down/report a bug? Check out http://play.spruce.cf.
Params
The (( param ))
operator can be used to require that an end-user override a particular value in a later manifest. For example, (( param "What should the admin user's password be set to?" ))
will produce the error: properties.users.admin.password: What should the admin user's password be set to?
Vault
(( vault "my/secret/path:key" ))
will contact the Vault server you’re currently targeted to via the VAULT_ADDR
environment variable, and attempt to retrieve the value stored in my/secret/path:key
. This allows you to store credentials in Vault, safely outside your manifest and templates. If you wish to commit a complete manifest without sensitive data to your repo, set the REDACT
environment variable to any value. spruce
will place REDACTED
as the value retrieved, rather than the actual value. Just make sure to deploy the non-redacted one to bosh, or your password might suddenly become "REDACTED" (security through confuscation?).
Environment Variables
spruce
can pull in the values of environment variables, and inject them into your manifest, alleviating the need to generate a new document by hand, with that variable, and merging it on top. For example (( grab $DIRECTOR_UUID ))
or (( concat "cf-" $ENVIRONMENT_NAME ))
JSON
spruce json <file>
or echo "my: yml" | spruce json
will take a YAML document, and convert it for JSON. This is super useful for piping BOSH manifests or templates through jq
for parsing.
Improved Error Display
Errors now utilize colors to improve readability where possible. Additionally whereas arrays would only show indexes for errors, spruce
will now try to resolve out array object names (e.g. jobs.0.networks: You need to specify a network.
will now show jobs.myjob_z1.networks: You need to specify a network.
). The final error display improvement is that any unsatisfied (( param ))
errors are shown in preference over other errors, to suppress issues caused by un-met parameters being grabbed, concatenated, injected, etc.
Logical-Or
The logical-Or operator can be used to allow failover from one reference to another, or to a default value, without causing an error. For example, your database client might want to grab the port of the database server, or if that was not set, provide a default value: (( grab properties.db.port || "5524" ))
Inject
The (( inject this.data ))
operator is used similarly to YAML’s anchoring/reference mechanism, except that it works across documents. It takes all of the data located under this.data
, and injects it at the parent of the key that invoked the operator. This is pretty handy in BOSH manifests for defining how a type of job/instance group should be at a global scale, and making sure each zoned instance of it gets the same data where necessary, overriding what makes sense.
Keys
(( keys my.map ))
is used to obtain an array listing all of the keys of a map, in case you need to do something with them.
Cartesian Products
Need to mash-up a list of ports with a list of server IPs? (( cartesian-product my.servers ":" my.ports ))
will do the trick.
Concat
(( concat thing1 thing2 ))
does just what it sounds like it would. It concatenates two values together. They can be references to scalars in the YAML document, environment variables, or quoted strings.
Grab
(( grab my.value ))
is a straightforward operator to take the value of another property, and stick it here. Need to set the port of a server in one part of the manifest, and send it to clients of that server in a different part of the manifest? This is your operator.
Static IPs
Auto-generating static IPs for non-cloud-config BOSH deployments is made a lot easier with this operator. It behaves similarly to spiff’s static_ips
operator: (( static_ips(0, 1, 2, ..., n) ))