Introduction

YAMLFu is an YAML templating language that allows to embed dynamic elements, using regular Pyhon expressions and YAMLFu specific functions.

The yamlfu command line utility processes YAMLFu documents and generates regular YAML.

Caution
yamlfu document rendering is equivalent to a python script execution. You should not render documents obtained from untrusted sources.

Dynamic Elements Using Python

Strings contained within {} «curly brackets» will be processed as dynamic elements, their content is evaluated in a python runtime, the result of that evalution replaces the dynamic string.

Constant Expressions

Constant expressions can be used to preserve calculation formulas in the source and only the result in the output.

Table 1. Constant expression examples
YAMLFu YAML
input.yaml
Arithmetic: '{2 + 1}'
Strings: '{"abc".upper()}'
List: '{[x.upper() for x in ["a", "b"]]}'
yamlfu input.yaml
Arithmetic: 3
Strings: ABC
List: [ "A", "B"]

Sibling References

Sibling references can be used to facilitate string composition and transformation, all sibling keys in a map are available to the python execution as variables matching their key names.

Table 2. Sibling reference examples
YAMLFu YAML
input.yaml
release: "18.04"
update: ".4"
url: 'http://releases.ubuntu.com/{release}/ubuntu-{release}{update}-desktop-amd64.iso'
yamlfu input.yaml
  release: "18.04"
  update: ".4"
  url: http://releases.ubuntu.com/18.04/ubuntu-18.04.4-desktop-amd64.iso

Internal Nodes

Internal nodes can be referenced from other nodes but they are not included in the output. They can be useful for storing data that needs to be referenced multiple times or for intermediate transformations.

A node is treated as "silent" when its key name starts wih an (_) underscore symbol.

Table 3. Template node example
YAMLFu YAML
_config:
  platform: Linux
  arch: amd64
System: '{_config.platform} {_config.arch}'
System: Linux amd64

Template Nodes

Templated nodes can be used to render nodes that need to be reproduced multiple with custom variables. Template nodes must be dictionaries, and they must have an _arguments field with a (csv) string representing the fields required to render a template instance.

In order to render a template, the special render() function is available within the python runtime. It must be invoked with the template name as the first argument, followed by the template arguments.

The next example illustrates the generation of multiple documents using a list for the kind argument.

Table 4. Template node example
YAMLFu YAML
# Leading double underscore to mark it as template node
__traefik_crd:
  _arguments: kind   # Arguments required for rendering
  apiVersion: apiextensions.k8s.io/v1beta1
  kind: CustomResourceDefinition
  metadata:
    name: '{kind.lower()}s.traefik.containo.us'

  spec:
    group: traefik.containo.us
    version: v1alpha1
    names:
      kind: '{kind}'
      plural: '{kind.lower()}s'
      singular: '{kind.lower()}'
    scope: Namespaced

_kinds: [ IngressRoute, IngressRouteTCP, Middleware, TLSOption ]
_render_all_crds: '{[render(__traefik_crd, k) for k in _kinds]}'
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
  scope: Namespaced
  version: v1alpha1

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
  scope: Namespaced
  version: v1alpha1

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
  scope: Namespaced
  version: v1alpha1

---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us
spec:
  group: traefik.containo.us
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption
  scope: Namespaced
  version: v1alpha1

Include YAML files

The ability to include additional YAML files to compose a single document is useful for managing large projects, it can also be used to store configuration data that needs to be shared accross several YAML documents.

Table 5. Include other files example
YAMLFu YAML
_config.yaml
base: http://localhost:9080
urls:
  a: insert
  b: update
_ok: This is a plain value
render_file.yaml
_config: '{render("_config.yaml")}'

test_1:
  method: GET
  url: '{_._config.base}/{_._config.urls.a}'

test_2:
  method: GET
  url: '{_._config.base}/{_._config.urls.b}'
Result
test_1:
  method: GET
  url: http://localhost:9080/insert
test_2:
  method: GET
  url: http://localhost:9080/update

Use External YAML for values

It is a common pattern to use a main template file that depends on additional values provided in an external file, yamlfu provides this capability with the -x command line option.

e.g

yamlfu main.yaml -x services/nginx.yaml
Table 6. Extra YAML example
YAMLFu YAML
main.yaml
apiVersion: v1
kind: Service
metadata:
  name: "{SERVICE}"
  labels:
    run: "{SERVICE}"
spec:
  ports:
  - port: "{PORT}"
    protocol: TCP
  selector:
    run: "{SERVICE}"
services/nginx.yaml
SERVICE: nginx
PORT: 80
yamlfu main.yaml -x ervices/nginx.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    run: nginx
  name: nginx
spec:
  ports:
    - port: 80
      protocol: TCP
  selector:
    run: nginx

Use values from environment vriables

The command line option -i can be used to inject environment variables.

e.g

yamlfu test.yaml -e NAME
Table 7. Extra YAML example
YAMLFu YAML
test.yaml
JOB NAME: '{NAME}'
export NAME="Boat" && yamlfu test.yaml -e NAME
JOB NAME: Boat