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?

Example

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?).

Example

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 ))

Example

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" ))

Example

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.

Example

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.

Example

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.

Example

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.

Example

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.

Example

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) ))

Example