Use Go Templating in Kubernetes Manifests

Updated 2 weeks ago by Michael Cretzman

To make your Kubernetes manifest reusable and dynamic, you can use Go templating and Harness built-in variables in combination in your Manifests files.

The inline values.yaml file used in a Harness Service does not support Helm templating, only Go templating. Helm templating is fully supported in the remote Helm charts you add to your Harness Service.

In this topic:

Before You Begin

Ensue you are familiar with the following:

Step 1: Review the Default Values File

  1. Look at the default values.yaml file to see the variables used in the default configuration files:
# This will be used as {{.Values.name}}
name: harness-example

# This will be used as {{int .Values.replicas}}
replicas: 1

# This will be used as {{.Values.image}}
image: ${artifact.metadata.image}

The variable ${artifact.metadata.image} is a Harness variable for referencing the metadata of the Artifact Source. For more information about Harness variables, see Variables and Expressions in Harness.

  1. Look at the default object descriptions to understand how easy it is to use Kubernetes in Harness.
apiVersion: v1 # for versions before 1.9.0 use apps/v1beta2
kind: ConfigMap # store non-confidential data in key-value pairs
metadata:
name: {{.Values.name}}-config # name is taken from values.yaml
data:
key: value # example key-value pair
---
apiVersion: apps/v1
kind: Deployment # describe the desired state of the cluster
metadata:
name: {{.Values.name}}-deployment # name is taken from values.yaml
spec:
replicas: {{int .Values.replicas}} # tells deployment to run pods matching the template
selector:
matchLabels:
app: {{.Values.name}} # name is taken from values.yaml
template:
metadata:
labels:
app: {{.Values.name}} # name is taken from values.yaml
spec:
containers:
- name: {{.Values.name}} # name is taken from values.yaml
image: {{.Values.image}} # image is taken from values.yaml
envFrom:
- configMapRef:
name: {{.Values.name}}-config # name is taken from values.yaml
ports:
- containerPort: 80

Step 2: Use Expression Builder

When you edit manifests in the Harness Service, you can enter expressions by entering {{. and Harness will fetch the values available in the values.yaml file.

This expression builder helps to ensure that you do not accidentally enter an incorrect value in your manifests.

Example 1: Use a Harness Variable in a Manifest

Harness built-in variables can be used in values.yaml file, and are evaluated at runtime. For a list of Harness variables, see Variables and Expressions in Harness.

In the values.yaml file, it will look like this:

name: ${serviceVariable.serviceName}

In a manifest file, it will be used like this:

apiVersion: apps/v1
kind: Deployment
metadata:
name: {{.Values.name}} # ${serviceVariable.serviceName}
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80

Go Templating Examples

You can use piping and Go actions, arguments, pipelines, and variables in your manifests.

Let's look at some examples.

Quotation Marks

The following example puts quotations around whatever string is in the something value. This can handle values that could otherwise be interpreted as numbers, or empty values, which would cause an error.

{{.Values.something | quote}}

Verbatim

Use indent and toYaml to put something from the values file into the manifest verbatim.

{{.Values.env.config | toYaml | indent 2}}

Indexing Structures in Templates

If the data passed to the template is a map, slice, or array it can be indexed from the template.

You can use {{index x number}} where index is the keyword, x is the data, and number is an integer for the index value.

If we had {{index names 2}} it is equivalent to names[2]. We can add more integers to index deeper into data. {{index names 2 3 4}} is equivalent to names[2][3][4].

Let's look at an example:

{{- if .Values.env.config}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Values.name}}-{{.Values.track}}
labels:
app: {{.Values.name}}
track: {{.Values.track}}
annotations:
harness.io/skip-versioning: "true"
data:
{{- if hasKey .Values.env .Values.track}}
{{index .Values.env .Values.track "config" | mergeOverwrite .Values.env.config | toYaml | indent 2}}
{{- else }}
{{.Values.env.config | toYaml | indent 2}}
{{- end }}
---
{{- end}}

{{- if .Values.env.secrets}}
apiVersion: v1
kind: Secret
metadata:
name: {{.Values.name}}-{{.Values.track}}
labels:
app: {{.Values.name}}
track: {{.Values.track}}
stringData:
{{- if hasKey .Values.env .Values.track}}
{{index .Values.env .Values.track "secrets" | mergeOverwrite .Values.env.secrets | toYaml | indent 2}}
{{- else }}
{{.Values.env.secrets | toYaml | indent 2}}
{{- end }}
---
{{- end}}

Iterate Over Existing Items

Here is example inserting an element into an existing list in a manifest for Istio VirtualService and the Destination rule.

The critical line is:

{{- range $track := split " " .Values.nonPrimary }}

This line iterates over a list of existing items, where the list was computed with a simple Shell Script command and output to the context prior to the rollout.

VirtualService:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: {{ .Values.name }}-gateway-vs
labels:
app: {{ .Values.name }}
group: multiservice
spec:
hosts:
- "*"
gateways:
- ingressgateway
http:
{{- if .Values.nonPrimary }}
{{- range $track := split " " .Values.nonPrimary }}
{{- range $uri := $.Values.uri }}
- name: {{ $track }}
match:
- headers:
x-pcln-track:
exact: {{ $track }}
uri:
{{ $uri.matchType }}: {{ $uri.matchString }}
{{- if $.Values.rewrite }}
rewrite:
uri: {{ $.Values.rewrite }}
{{- end }}
route:
- destination:
host: {{ $.Values.name }}
subset: {{ $.Values.name }}-{{ $track }}
{{- end }}
{{- end }}
{{- end }}
{{- if .Values.hasPrimary }}
- name: primary
match:
{{- range $uri := .Values.uri }}
- uri:
{{ $uri.matchType }}: {{ $uri.matchString }}
{{- end }}
{{- if .Values.rewrite }}
rewrite:
uri: {{ .Values.rewrite }}
{{- end }}
route:
- destination:
host: {{ .Values.name }}
subset: {{ .Values.name }}-primary
{{- end }}

DestinationRule:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: {{ .Values.name }}
labels:
app: {{ .Values.name }}
group: multiservice
spec:
host: {{ .Values.name }}
subsets:
{{- if .Values.nonPrimary }}
{{- range $track := split " " .Values.nonPrimary }}
- name: {{ $.Values.name }}-{{ $track }}
labels:
track: {{ $track }}
{{- end }}
{{- end }}
{{- if .Values.hasPrimary }}
- name: {{ .Values.name }}-primary
labels:
track: primary
{{- end }}

For more information, see the Go text template documentation.

Next Steps


How did we do?