Creating Ops Files
It's usually necessary to apply an opinionated set of structural changes to a YAML document (manifest, cloud config, etc.) before submitting it to the CLI commands (bosh create-env
, bosh deploy
, etc.) for processing. Such changes could be an addition or removal of certain job properties, instance groups, changes to property values.
Tip
Replacing values such as passwords and certificates is not considered a structural change. Variable Interpolation is a better way to handle values.
To get a final YAML document one can apply desired changes once and save the result; however, over time it may become harder or just tedious to reapply these changes if base document changes. Additionally if it's necessary to have multiple slightly different changes on top of the base document for different teams existing editing tools may not be enough. To make such workflows easier you can encode a set of changes into one or more operations file.
A single operation represents a single change. An operations file is a YAML document that contains multiple operations that are to be applied serially to a different YAML document. Instead of storing all operations in a single file, they can be grouped logically into many operations files.
Several CLI commands such as create-env
, deploy
and interpolate
allow to provide operations files via --ops-file
flag to be applied before processing the document.
Example¶
Following is an operations file (replace-name.yml
) with a single operation that replaces value of top level key name
with a string other-cf
:
- type: replace path: /name value: other-cf
Given base YAML document (base.yml
):
name: my-cf
Result of applying above operations file to the base YAML document would be:
name: other-cf
That could be demonstrated with the help of bosh interpolate
command whose purpose is to simply apply operations files to base document and print the result:
bosh interpolate base.yml --ops-file replace-name.yml
# name: other-cf
Path syntax¶
Each operation acts on a location within a YAML document. Path represents a location. It's important to note that path (location) does not represent what operation will be performed, just like lat & long do not represent what happens at a physical location.
Here are some path examples:
/
: matches document root/0
: matches 0th item in the array at the root/instance_groups/0
: matches 0th instance group withininstance_groups
array/instance_groups/name=zookeeper
: matches instance group (hash) with aname
key that has valuezookeeper
All paths follow these rules:
-
Paths can have multiple components separated by a
/
-
Paths always start at the root of the document with a
/
-
String components typically refer to hash keys (ex:
/key1
)- Strings ending with
?
refer to hash keys that may or may not exist- "optionality" carries over to the items to the right
- Strings ending with
-
Integer components refer to array indices (ex:
/0
,/-1
) -
Array index selection could be affected via
:prev
and:next
(as of CLI v2.0.40+) -
Array insertion could be affected via
:before
and:after
(as of CLI v2.0.40+) -
-
component refers to an imaginary index after last array index (ex:/-
)- If there is an array of length 3 (
[0,1,2]
), then-
would refer to 4th non-existent position
- If there is an array of length 3 (
-
key=val
component matches hashes within an array (ex:/key=val
)- Values ending with
?
refer to array items that may or may not exist
- Values ending with
Path components without "optional" (?
) annotation imply that referenced location must exist within a document. Operation will fail to be performed if that location is not found. "Optional" annotation can be used to signify indifference to the presense of referenced location, making it possible for operation either to ignore it (while removal) or create it lazily (while replacement). If a component in a path is annotated as optional, components following it will be considered optional implicitly.
Operations¶
There are currently two types of operations: replace and remove.
Replace operation can be used to append an item to an array of any length if last component of a path is a -
(see above for details).
Following base YAML document is used with operations below:
key: 1 key2: nested: super_nested: 2 other: 3 array: [4,5,6] items: - name: item7 - name: item8 - name: item8
Hash¶
Set key
to 10
...
- type: replace path: /key value: 10
Remove key
...
- type: remove path: /key
Errors because key_not_there
is expected (and does not have ?
)...
- type: replace path: /key_not_there value: 10
Errors because key_not_there
is expected (does not have ?
)...
- type: remove path: /key_not_there
Creates new_key
and sets it to 10
(note the ?
)...
- type: replace path: /new_key? value: 10
Requires that key2
and nested
hashes exist; and sets super_nested
to 10
...
- type: replace path: /key2/nested/super_nested value: 10
Requires that key2
and nested
hashes exist; and removes super_nested
...
- type: remove path: /key2/nested/super_nested
Requires that key2
hash exists; allows nested
, another_nested
and super_nested
not to exist because ?
carries over to nested keys; and creates another_nested
and super_nested
before setting super_nested
to 10
...
- type: replace path: /key2/nested?/another_nested/super_nested value: 10
Resulting in...
... key2: nested: another_nested: super_nested: 10 super_nested: 2 other: 3
Array¶
Requires array
to exist and be an array; and replaces 0th item in array
array with 10
...
- type: replace path: /array/0 value: 10
Requires array
to exist and be an array; and removes the 0th item in array
...
- type: remove path: /array/0
Requires array
to exist and be an array; and appends 10
to the end of array
...
- type: replace path: /array/- value: 10
Creates array2
array since it does not exist; and appends 10
to the end of array2
...
- type: replace path: /array2?/- value: 10
Requires array
to exist and be an array; and replaces 0th item in array
array with 10
...
- type: replace path: /array/1:prev value: 10
Requires array
to exist and be an array; and replaces 1st item (starting at 0) in array
array with 10
...
- type: replace path: /array/0:next value: 10
Requires array
to exist and be an array; and inserts 10
after 0th item in array
array...
- type: replace path: /array/0:after value: 10
Requires array
to exist and be an array; and inserts 10
before 0th item at the beginning of array
array...
- type: replace path: /array/0:before value: 10
Arrays of hashes¶
Finds and removes array item with matching key name
with value item7
...
- type: remove path: /items/name=item7
Errors because there are two values that have item8
as their name
...
- type: replace path: /items/name=item8/count value: 10
Appends array item with matching key name
with value item9
because values ends with ?
and item does not exist; creates count
and sets it to 10
within created array item...
- type: replace path: /items/name=item9?/count value: 10
Resulting in...
... items: - name: item7 - name: item8 - name: item8 - name: item9 count: 10
Finds array item with matching key name
with value item7
; and adds hash name: item6
before found array item...
- type: replace path: /items/name=item7:before value: name: item6
Resulting in...
... items: - name: item6 - name: item7 - name: item8 - name: item8
Escaping¶
The following characters can be escaped with special sequences...
Desired | Escaped |
---|---|
~ |
~0 |
/ |
~1 |
: |
~7 |
For example, to remove a variable with a name
of /root_certificate
, you might do...
- path: /variables/name=~1root_certificate type: remove