DEV Community

Tim Downey
Tim Downey

Posted on

How to default null YAML values to empty strings when using ytt

An example of converting null YAML values to empty strings when using ytt for Kubernetes config templating. This approach uses Python's boolean short-circuiting behavior to concisely substitute empty strings for None values. It was inspired by this Stack Overflow post.

If you stumbled upon this page and have no clue what ytt is, it's basically yet another way to template out YAML for Kubernetes. Think helm template but where you get to use a Python-like programming language and manipulate the YAML structures directly instead of just manipulating text.

tl;dr

Use Python's boolean short-circuiting.

#@ prefix = data.values.optionalPrefix or ""
labelSelector: #@ prefix + str(data.values.myKey)
Enter fullscreen mode Exit fullscreen mode

long form

Consider a scenario where you have a config-template.yaml file that generates the configuration YAML for your app and a values.yaml that operators can provide to supply their own values. You can do this with ytt via the following command:

ytt -f config-template.yaml -f values.yaml
Enter fullscreen mode Exit fullscreen mode

Let's say you want to get fancy and allow installers of your software to be able to supply their own label selectors with an optional label prefix.

#! config-template.yaml


#@ load("@ytt:data", "data")

---
labelSelector: #@ str(data.values.myPrefix) + str(data.values.myKey)
Enter fullscreen mode Exit fullscreen mode
#! values.yaml


#@data/values

---
myPrefix: null
myKey: "app"
Enter fullscreen mode Exit fullscreen mode

If you allow them to simply supply null for the myPrefix value from their values.yaml file you'll end up generating something like this:

labelSelector: Noneapp
Enter fullscreen mode Exit fullscreen mode

Probably not what they intended. Instead, you can do something like this in your template:

#! config-template.yaml


#@ load("@ytt:data", "data")

---
#@ prefix = data.values.myPrefix or ""
labelSelector: #@ prefix + str(data.values.myKey)
Enter fullscreen mode Exit fullscreen mode

Now it will template out the following:

labelSelector: app
Enter fullscreen mode Exit fullscreen mode

Much better! This approach uses Python's (really Starlark in the case of ytt) boolean short-circuiting to skip past the falsey NoneType value. Warning this also means that other falsey values become empty strings as well. I'd recommend that you add stricter assertions if that distinction is important to you.

Here's a more realistic example where you might want to let someone configure the label prefix on a Kubernetes Service's selector, but not let them override the selectors completely.

#! service-template.yaml

#! This is the way:
#@ xstr = lambda s: s or ""

#@ labelPrefix = xstr(data.values.labelPrefix)
#@ labelSelectors = {}
#@ labelSelectors[labelPrefix+ "process"] = "web"

---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector: #@ labelSelectors    
  ports:
    - protocol: TCP
      name: http
      port: 80
      targetPort: 9001
Enter fullscreen mode Exit fullscreen mode
#! values.yaml


#@data/values
---
labelPrefix: k8s.downey.dev/
Enter fullscreen mode Exit fullscreen mode

This assigns the short-circuiting logic to a lambda names xstr which lets us reuse it. This template + values files produces the following:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    k8s.downey.dev/process: web
  ports:
  - protocol: TCP
    name: http
    port: 80
    targetPort: 9001
Enter fullscreen mode Exit fullscreen mode

If we were to set ~ or null in the values file like this:

#! values.yaml


#@data/values
---
labelPrefix: ~
Enter fullscreen mode Exit fullscreen mode

It would template out the selector without the prefix!

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    process: web
  ports:
  - protocol: TCP
    name: http
    port: 80
    targetPort: 9001
Enter fullscreen mode Exit fullscreen mode

Pretty powerful stuff. To play around with ytt on your own, head over to https://get-ytt.io/. There is a section at the bottom containing sandbox examples where you can try it out for yourself. Good luck! 😌

Top comments (0)