mirror of https://github.com/helm/helm
Merge pull request #1551 from technosophos/docs/yaml-tips
docs(yaml_techniques): add YAML techniques documentpull/1562/head
commit
57ccce10d3
@ -0,0 +1,349 @@
|
||||
# YAML Techniques
|
||||
|
||||
Most of this guide has been focused on writing the template language. Here,
|
||||
we'll look at the YAML format. YAML has some useful features that we, as
|
||||
template authors, can use to make our templates less error prone and easier
|
||||
to read.
|
||||
|
||||
## Scalars and Collections
|
||||
|
||||
According to the [YAML spec](http://yaml.org/spec/1.2/spec.html), there are two
|
||||
types of collections, and many scalar types.
|
||||
|
||||
The two types of collections are maps and sequences:
|
||||
|
||||
```yaml
|
||||
map:
|
||||
one: 1
|
||||
two: 2
|
||||
three: 3
|
||||
|
||||
sequence:
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
```
|
||||
|
||||
Scalar values are individual values (as opposed to collections)
|
||||
|
||||
### Scalar Types in YAML
|
||||
|
||||
In Helm's dialect of YAML, the scalar data type of a value is determined by a
|
||||
complex set of rules, including the Kubernetes schema for resource definitions.
|
||||
But when inferring types, the following rules tend to hold true.
|
||||
|
||||
If an integer or float is an unquoted bare word, it is typically treated as
|
||||
a numeric type:
|
||||
|
||||
```yaml
|
||||
count: 1
|
||||
size: 2.34
|
||||
```
|
||||
|
||||
But if they are quoted, they are treated as strings:
|
||||
|
||||
```yaml
|
||||
count: "1" # <-- string, not int
|
||||
size: '2.34' # <-- string, not float
|
||||
```
|
||||
|
||||
The same is true of booleans:
|
||||
|
||||
```yaml
|
||||
isGood: true # bool
|
||||
answer: "true" # string
|
||||
```
|
||||
|
||||
The word for an empty value is `null` (not `nil`).
|
||||
|
||||
Note that `port: "80"` is valid YAML, and will pass through both the
|
||||
template engine and the YAML parser, but will fail if Kubernetes expects
|
||||
`port` to be an integer.
|
||||
|
||||
In some cases, you can force a particular type inference using YAML node tags:
|
||||
|
||||
```yaml
|
||||
coffee: "yes, please"
|
||||
age: !!str 21
|
||||
port: !!int "80"
|
||||
```
|
||||
|
||||
In the above, `!!str` tells the parser that `age` is a string, even if it looks
|
||||
like an int. And `port` is treated as an int, even though it is quoted.
|
||||
|
||||
|
||||
## Strings in YAML
|
||||
|
||||
Much of the data that we place in YAML documents are strings. YAML has more than
|
||||
one way to represent a string. This section explains the ways and demonstrates
|
||||
how to use some of them.
|
||||
|
||||
There are three "inline" ways of declaring a string:
|
||||
|
||||
```yaml
|
||||
way1: bare words
|
||||
way2: "double-quoted strings"
|
||||
way3: 'single-quoted strings'
|
||||
```
|
||||
|
||||
All inline styles must be on one line.
|
||||
|
||||
- Bare words are unquoted, and are not escaped. For this reason, you have to
|
||||
be careful what characters you use.
|
||||
- Double-quoted strings can have specific characters escaped with `\`. For
|
||||
example `"\"Hello\", she said"`. You can escape line breaks with `\n`.
|
||||
- Single-quoted strings are "literal" strings, and do not use the `\` to
|
||||
escape characters. The only escape sequence is `''`, which is decoded as
|
||||
a single `'`.
|
||||
|
||||
In addition to the one-line strings, you can declare multi-line strings:
|
||||
|
||||
```yaml
|
||||
coffee: |
|
||||
Latte
|
||||
Cappuccino
|
||||
Espresso
|
||||
```
|
||||
|
||||
The above will treat the value of `coffee` as a single string equivalent to
|
||||
`Latte\nCappuccino\nEspresso\n`.
|
||||
|
||||
Note that the first line after the `|` must be correctly indented. So we could
|
||||
break the example above by doing this:
|
||||
|
||||
```yaml
|
||||
coffee: |
|
||||
Latte
|
||||
Cappuccino
|
||||
Espresso
|
||||
|
||||
```
|
||||
|
||||
Because `Latte` is incorrectly indented, we'd get an error like this:
|
||||
|
||||
```
|
||||
Error parsing file: error converting YAML to JSON: yaml: line 7: did not find expected key
|
||||
```
|
||||
|
||||
In templates, it is sometimes safer to put a fake "first line" of content in a
|
||||
multi-line document just for protection from the above error:
|
||||
|
||||
```yaml
|
||||
coffee: |
|
||||
# Commented first line
|
||||
Latte
|
||||
Cappuccino
|
||||
Espresso
|
||||
|
||||
```
|
||||
|
||||
Note that whatever that first line is, it will be preserved in the output of the
|
||||
string. So if you are, for example, using this technique to inject a file's contents
|
||||
into a ConfigMap, the comment should be of the type expected by whatever is
|
||||
reading that entry.
|
||||
|
||||
### Controlling Spaces in Multi-line Strings
|
||||
|
||||
In the example above, we used `|` to indicate a multi-line string. But notice
|
||||
that the content of our string was followed with a trailing `\n`. If we want
|
||||
the YAML processor to strip off the trailing newline, we can add a `-` after the
|
||||
`|`:
|
||||
|
||||
```yaml
|
||||
coffee: |-
|
||||
Latte
|
||||
Cappuccino
|
||||
Espresso
|
||||
```
|
||||
|
||||
Now the `coffee` value will be: `Latte\nCappuccino\nEspresso` (with no trailing
|
||||
`\n`).
|
||||
|
||||
Other times, we might want all trailing whitespace to be preserved. We can do
|
||||
this with the `|+` notation:
|
||||
|
||||
```yaml
|
||||
coffee: |+
|
||||
Latte
|
||||
Cappuccino
|
||||
Espresso
|
||||
|
||||
|
||||
another: value
|
||||
```
|
||||
|
||||
Now the value of `coffee` will be `Latte\nCappuccino\nEspresso\n\n\n`.
|
||||
|
||||
Indentation inside of a text block is preserved, and results in the preservation
|
||||
of line breaks, too:
|
||||
|
||||
```
|
||||
coffee: |-
|
||||
Latte
|
||||
12 oz
|
||||
16 oz
|
||||
Cappuccino
|
||||
Espresso
|
||||
```
|
||||
|
||||
In the above case, `coffee` will be `Latte\n 12 oz\n 16 oz\nCappuccino\nEspresso`.
|
||||
|
||||
### Indenting and Templates
|
||||
|
||||
When writing templates, you may find yourself wanting to inject the contents of
|
||||
a file into the template. As we saw in previous chapters, there are two ways
|
||||
of doing this:
|
||||
|
||||
- Use `{{ .Files.Get "FILENAME" }}` to get the contents of a file in the chart.
|
||||
- Use `{{ include "TEMPLATE" . }}` to render a template and then place its
|
||||
contents into the chart.
|
||||
|
||||
When inserting files into YAML, it's good to understand the multi-line rules above.
|
||||
Often times, the easiest way to insert a static file is to do something like
|
||||
this:
|
||||
|
||||
```yaml
|
||||
myfile: |
|
||||
{{ .Files.Get "myfile.txt" | indent 2 }}
|
||||
```
|
||||
|
||||
Note how we do the indentation above: `indent 2` tells the template engine to
|
||||
indent every line in "myfile.txt" with two spaces. Note that we do not indent
|
||||
that template line. That's because if we did, the file content of the first line
|
||||
would be indented twice.
|
||||
|
||||
### Folded Multi-line Strings
|
||||
|
||||
Sometimes you want to represent a string in your YAML with multiple lines, but
|
||||
want it to be treated as one long line when it is interpreted. This is called
|
||||
"folding". To declare a folded block, use `>` instead of `|`:
|
||||
|
||||
```yaml
|
||||
coffee: >
|
||||
Latte
|
||||
Cappuccino
|
||||
Espresso
|
||||
|
||||
|
||||
```
|
||||
|
||||
The value of `coffee` above will be `Latte Cappuccino Espresso\n`. Note that all
|
||||
but the last line feed will be converted to spaces. You can combine the whitespace
|
||||
controls with the folded text marker, so `>-` will replace or trim all newlines.
|
||||
|
||||
Note that in the folded syntax, indenting text will cause lines to be preserved.
|
||||
|
||||
```yaml
|
||||
coffee: >-
|
||||
Latte
|
||||
12 oz
|
||||
16 oz
|
||||
Cappuccino
|
||||
Espresso
|
||||
```
|
||||
|
||||
The above will produce `Latte\n 12 oz\n 16 oz\nCappuccino Espresso`. Note that
|
||||
both the spacing and the newlines are still there.
|
||||
|
||||
## Embedding Multiple Documents in One File
|
||||
|
||||
It is possible to place more than one YAML documents into a single file. This
|
||||
is done by prefixing a new document with `---` and ending the document with
|
||||
`...`
|
||||
|
||||
```yaml
|
||||
|
||||
---
|
||||
document:1
|
||||
...
|
||||
---
|
||||
document: 2
|
||||
...
|
||||
```
|
||||
|
||||
In many cases, either the `---` or the `...` may be omitted.
|
||||
|
||||
Some files in Helm cannot contain more than one doc. If, for example, more
|
||||
than one document is provided inside of a `values.yaml` file, only the first
|
||||
will be used.
|
||||
|
||||
Template files, however, may have more than one document. When this happends,
|
||||
the file (and all of its documents) is treated as one object during
|
||||
template rendering. But then the resulting YAML is split into multiple
|
||||
documents before it is fed to Kubernetes.
|
||||
|
||||
We recommend only using multiple documents per file when it is absolutely
|
||||
necessary. Having multiple documents in a file can be difficult to debug.
|
||||
|
||||
## YAML is a Superset of JSON
|
||||
|
||||
Because YAML is a superset of JSON, any valid JSON document _should_ be valid
|
||||
YAML.
|
||||
|
||||
```json
|
||||
{
|
||||
"coffee": "yes, please",
|
||||
"coffees": [
|
||||
"Latte", "Cappuccino", "Espresso"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The above is another way of representing this:
|
||||
|
||||
```yaml
|
||||
coffee: yes, please
|
||||
coffees:
|
||||
- Latte
|
||||
- Cappuccino
|
||||
- Espresso
|
||||
```
|
||||
|
||||
And the two can be mixed (with care):
|
||||
|
||||
```yaml
|
||||
coffee: "yes, please"
|
||||
coffees: [ "Latte", "Cappuccino", "Espresso"]
|
||||
```
|
||||
|
||||
All three of these should parse into the same internal representation.
|
||||
|
||||
While this means that files such as `values.yaml` may contain JSON data, Helm
|
||||
does not treat the file extension `.json` as a valid suffix.
|
||||
|
||||
## YAML Anchors
|
||||
|
||||
The YAML spec provides a way to store a reference to a value, and later
|
||||
refer to that value by reference. YAML refers to this as "anchoring":
|
||||
|
||||
```yaml
|
||||
coffee: "yes, please"
|
||||
favorite: &favoriteCoffee "Cappucino"
|
||||
coffess:
|
||||
- Latte
|
||||
- *favoriteCoffee
|
||||
- Espresso
|
||||
```
|
||||
|
||||
In the above, `&favoriteCoffee` sets a reference to `Cappuccino`. Later, that
|
||||
reference is used as `*favoriteCoffee`. So `coffees` becomes
|
||||
`Latte, Cappuccino, Espresso`.
|
||||
|
||||
While there are a few cases where anchors are useful, there is one aspect of
|
||||
them that can cause subtle bugs: The first time the YAML is consumed, the
|
||||
reference is expanded and then discarded.
|
||||
|
||||
So if we were to decode and then re-encode the example above, the resulting
|
||||
YAML would be:
|
||||
|
||||
```YAML
|
||||
coffee: yes, please
|
||||
favorite: Cappucino
|
||||
coffess:
|
||||
- Latte
|
||||
- Cappucino
|
||||
- Espresso
|
||||
```
|
||||
|
||||
Because Helm and Kubernetes often read, modify, and then rewrite YAML files,
|
||||
the anchors will be lost.
|
Loading…
Reference in new issue