# Template function examples

This topic provides examples of how to use Replicated template functions in various common use cases.
For more information about working with Replicated template functions,
including the supported syntax and the types of files that support Replicated template functions,
see [About Replicated Template Functions](template-functions-about).

## Overview

Replicated template functions are based on the Go text/template library. All functionality of the Go templating language, including if statements, loops, and variables, is supported with Replicated template functions. For more information, see [text/template](https://golang.org/pkg/text/template/) in the Go documentation. 

Additionally, Replicated template functions can be used with all functions in the Sprig library. Sprig provides several template functions for the Go templating language, such as type conversion, string, and integer math functions. For more information, see [Sprig Function Documentation](https://masterminds.github.io/sprig/).

Common use cases for Replicated template functions include rendering values during installation or upgrade, such as:
* Customer-specific license field values
* User-provided configuration values
* Information about the customer environment, such the number of nodes or the Kubernetes version in the cluster where the application is installed
* Random strings

Replicated template functions can also be used to work with integer, boolean, float, and string values, such as doing mathematical operations, trimming leading and trailing spaces, or converting string values to integers or booleans.

For examples demonstrating these use cases and more, see the following sections.

## Comparison examples

This section includes examples of how to use Replicated template functions to compare different types of data.

### Boolean comparison

Use boolean values in comparisons to evaluate if a given statement is true or false.
Because many Replicated template functions return string values,
comparing boolean values often requires using the Replicated
[ParseBool](/reference/template-functions-static-context#parsebool) template function
to return the boolean represented by the string.

One common use case for working with boolean values is to check that a given field is present in the customer's license.
For example,
you might need to show a configuration option only when the customer's license includes a certain entitlement.

The following example creates a conditional statement in the Replicated Config custom resource.
The statement evaluates to true when a specified license field is present in the customer's license _and_
the customer enables a specified configuration option.

```yaml
# Replicated Config custom resource
apiVersion: kots.io/v1beta1
kind: Config
metadata:
  name: config-sample
spec:
  groups:  
  - name: example_group
    title: Example Config
    items:
    - name: radio_example
      title: Select One
      type: radio
      items:
      - name: option_one
        title: Option One
      - name: option_two
        title: Option Two  
    - name: conditional_item
      title: Conditional Item
      type: text
      # Display this item only when the customer enables the option_one config field *and*
      # has the feature-1 entitlement in their license
      when: repl{{ and (LicenseFieldValue "feature-1" | ParseBool) (ConfigOptionEquals "radio_example" "option_one")}}  
```

This example uses the following Replicated template functions:
* [LicenseFieldValue](/reference/template-functions-license-context#licensefieldvalue)
  to return the string value of a boolean type license field named `feature-1`
   :::note
   The LicenseFieldValue template function always returns a string, regardless of the license field type.
   :::
* [ParseBool](/reference/template-functions-static-context#parsebool)
  to convert the string returned by the LicenseFieldValue template function to a boolean
* [ConfigOptionEquals](/reference/template-functions-config-context#configoptionequals)
  to return a boolean that evaluates to true if the configuration option value is equal to the supplied value

### Integer comparison

You can compare integer values using operators such as greater than, less than, equal to, and so on.
Because many Replicated template functions return string values,
you might need another function to return the integer represented by the string, such as:
* Replicated [ParseInt](/reference/template-functions-static-context#parseint),
  which returns the integer value represented by the string with the option to provide a `base` other than 10
* Sprig [atoi](https://masterminds.github.io/sprig/conversion.html),
  which is equivalent to ParseInt(s, 10, 0), converted to type integer

A common use case for comparing integer values is to display different configuration options
depending on values from the customer's license.
For example, licenses might include an entitlement that defines the number of seats available to the customer.
In this case,
you can conditionally display or hide certain fields on the configuration screen depending on the customer's team size.

The following example uses:
* Replicated [LicenseFieldValue](/reference/template-functions-license-context#licensefieldvalue) template function to evaluate the number of seats permitted by the license
* Sprig [atoi](https://masterminds.github.io/sprig/conversion.html) function to convert the string values returned by LicenseFieldValue to integers
* [Go binary comparison operators](https://pkg.go.dev/text/template#hdr-Functions) `gt`, `lt`, `ge`, and `le` to compare the integers

```yaml
# Replicated Config custom resource
apiVersion: kots.io/v1beta1
kind: Config
metadata:
  name: config-sample
spec:
  groups:  
  - name: example_group
    title: Example Config
    items:
    - name: small
      title: Small (100 or Fewer Seats)
      type: text
      default: Default for small teams
      # Use le and atoi functions to display this config item 
      # only when the value of the numSeats entitlement is
      # less than or equal to 100
      when: repl{{ le (atoi (LicenseFieldValue "numSeats")) 100 }}
    - name: medium
      title: Medium (101-1000 Seats)
      type: text
      default: Default for medium teams
      # Use ge, le, and atoi functions to display this config item 
      # only when the value of the numSeats entitlement is
      # greater than or equal to 101 and less than or equal to 1000
      when: repl{{ (and (ge (atoi (LicenseFieldValue "numSeats")) 101) (le (atoi (LicenseFieldValue "numSeats")) 1000)) }}
    - name: large
      title: Large (More Than 1000 Seats)
      type: text
      default: Default for large teams
      # Use gt and atoi functions to display this config item 
      # only when the value of the numSeats entitlement is
      # greater than 1000
      when: repl{{ gt (atoi (LicenseFieldValue "numSeats")) 1000 }}
```

As shown in the image below, if the user's license contains `numSeats: 150`, then the `medium` item is displayed on the **Config** page and the `small` and `large` items are not displayed:

<img alt="Config page displaying the Medium (101-1000 Seats) item" src="/images/config-example-numseats.png" width="550px"/>

[View a larger version of this image](/images/config-example-numseats.png)

### String comparison

A common use case for string comparison is to compare the rendered value of a Replicated template function
against a string.
You can use this to conditionally show or hide fields based on details about the customer's environment.
For example, use a string comparison to check the Kubernetes distribution of the cluster where the application runs.

The following example uses:
* Replicated [Distribution](/reference/template-functions-static-context#distribution) template function to return the Kubernetes distribution of the cluster
* [eq](https://pkg.go.dev/text/template#hdr-Functions) (_equal_) Go binary operator to compare the rendered value of the Distribution template function to a string, then return the boolean truth of the comparison

```yaml
# Replicated Config custom resource
apiVersion: kots.io/v1beta1
kind: Config
metadata:
  name: config-sample
spec:
  groups:
    - name: example_settings
      title: My Example Config
      description: Example fields for using Distribution template function
      items:
      - name: gke_distribution
        type: label
        title: "You are deploying to GKE"
        # Use the eq binary operator to check if the rendered value
        # of the Distribution template function is equal to gke
        when: repl{{ eq Distribution "gke" }}
      - name: openshift_distribution
        type: label
        title: "You are deploying to OpenShift"
        when: repl{{ eq Distribution "openShift" }}
      - name: eks_distribution
        type: label
        title: "You are deploying to EKS"
        when: repl{{ eq Distribution "eks" }}
      ...
```

The following image shows how only the `gke_distribution` item appears on the app configuration screen:

<img alt="Config page with the text You are deploying to GKE" src="/images/config-example-distribution-gke.png" width="550px"/>

### Not equal to comparison

It can be useful to compare the rendered value of a Replicated template function against another value
to check if the two values are different.
For example,
you can conditionally show certain fields only when the Kubernetes distribution of the cluster
where the application runs is _not_
[Replicated Embedded Cluster](/embedded-cluster/v3/embedded-overview).

In the following example, the `ingress_type` field appears on the configuration page only when the distribution of the cluster is _not_ [Replicated Embedded Cluster](/embedded-cluster/v3/embedded-overview). This ensures that only users deploying to their own existing cluster are able to select the method for ingress.

The following example uses:
* Replicated [Distribution](/reference/template-functions-static-context#distribution) template function to return the Kubernetes distribution of the cluster
* [ne](https://pkg.go.dev/text/template#hdr-Functions) (_not equal_) Go binary operator to compare the rendered value of the Distribution template function to a string, then return `true` if the values are not equal to one another

```yaml
apiVersion: kots.io/v1beta1
kind: Config
metadata:
  name: config
spec:
  groups:
  # Ingress settings
  - name: ingress_settings
    title: Ingress Settings
    description: Configure Ingress
    items:
    - name: ingress_type
      title: Ingress Type
      help_text: | 
        Select how traffic will ingress to the appliction.
      type: radio
      items:
      - name: ingress_controller
        title: Ingress Controller
      - name: load_balancer
        title: Load Balancer
      default: "ingress_controller"
      required: true
      when: 'repl{{ ne Distribution "embedded-cluster" }}'
  # Database settings  
  - name: database_settings
    title: Database
    items:
    - name: postgres_type
      help_text: Would you like to use an embedded postgres instance, or connect to an external instance that you manage?
      type: radio
      title: Postgres
      default: embedded_postgres
      items:
      - name: embedded_postgres
        title: Embedded Postgres
      - name: external_postgres
        title: External Postgres  
```

The following image shows how the `ingress_type` field does not appear when the distribution of the cluster is `embedded-cluster`. Only the `postgres_type` item appears:

<img alt="Config page with a Postgres field" src="/images/config-example-distribution-not-ec.png" width="550px"/>

[View a larger version of this image](/images/config-example-distribution-not-ec.png)

Conversely, when the distribution of the cluster is not `embedded-cluster`, both fields appear:

<img alt="Config page with Ingress and Postgres fields" src="/images/config-example-distribution-not-ec-2.png" width="550px"/>

[View a larger version of this image](/images/config-example-distribution-not-ec-2.png)

### Logical AND comparison

Logical comparisons such as AND, OR, and NOT work with Replicated template functions.
A common use case for logical AND comparisons is to construct more complex conditional statements
where two different conditions must both be true.

The following example shows how to use an `and` operator that evaluates to true
when two different configuration options are both enabled.
This example uses the Replicated
[ConfigOptionEquals](/reference/template-functions-config-context#configoptionequals) template function
to return a boolean that evaluates to true if the configuration option value is equal to the supplied value.

```yaml
# Replicated Config custom resource
apiVersion: kots.io/v1beta1
kind: Config
metadata:
  name: config-sample
spec:
  groups:  
  - name: example_group
    title: Example Config
    items:
    - name: radio_example
      title: Select One Example
      type: radio
      items:
      - name: option_one
        title: Option One
      - name: option_two
        title: Option Two
    - name: boolean_example
      title: Boolean Example
      type: bool
      default: "0"      
    - name: conditional_item
      title: Conditional Item
      type: text
      # Display this item only when *both* specified config options are enabled
      when: repl{{ and (ConfigOptionEquals "radio_example" "option_one") (ConfigOptionEquals "boolean_example" "1")}}
```

As shown in the following image,
when the user selects both `Option One` and `Boolean Example`,
the conditional statement evaluates to true and the `Conditional Item` field appears:

<img alt="Conditional item displayed" src="/images/conditional-item-true.png" width="550px"/>

[View a larger version of this image](/images/conditional-item-true.png)

Alternatively,
if either `Option One` or `Boolean Example` is not selected,
then the conditional statement evaluates to false and the `Conditional Item` field is not displayed:  

<img alt="Option two selected" src="/images/conditional-item-false-option-two.png" width="550px"/>

[View a larger version of this image](/images/conditional-item-false-option-two.png)

<img alt="Boolean field deselected" src="/images/conditional-item-false-boolean.png" width="550px"/>

[View a larger version of this image](/images/conditional-item-false-boolean.png)

## Conditional statement examples

This section includes examples of using Replicated template functions to construct conditional statements.
Use conditional statements with Replicated template functions to render different values depending on a given condition.

### If-else statements

A common use case for if-else statements is to conditionally set values for application resources or objects,
such as custom annotations or service types.

:::note
For more complex or nested if-else statements,
use templating in your Helm chart `templates` instead of in the Replicated HelmChart custom resource.
For more information, see [If/Else](https://helm.sh/docs/chart_template_guide/control_structures/#ifelse)
in the Helm documentation.
:::

For most use cases, use single-line formatting for if-else statements.
Multi-line formatting can be useful to improve the readability of YAML files
for longer or more complex if-else statements.

You can construct multi-line if-else statements using YAML block scalars and block chomping characters
to ensure the rendered result is valid YAML:
* Use the greater than (`>`) character for a _folded_ block scalar style.
  With the folded style, Go treats single line breaks in the string as a space.
* Use the block chomping minus (`-`) character to remove all the line breaks at the end of a string.

For more information about working with these characters,
see [Block Style Productions](https://yaml.org/spec/1.2.2/#chapter-8-block-style-productions) in the YAML documentation.

The following example shows if-else statements in the Replicated HelmChart custom resource `values` field.
The statements render different values depending on whether the user selects a load balancer or an ingress controller
as the ingress type.
This example uses the Replicated
[ConfigOptionEquals](/reference/template-functions-config-context#configoptionequals) template function
to return a boolean that evaluates to true if the configuration option value is equal to the supplied value.

```yaml
# Replicated HelmChart custom resource
apiVersion: kots.io/v1beta2
kind: HelmChart
metadata:
  name: my-app
spec:
  chart:
    name: my-app
    chartVersion: 0.23.0
  values:
    services:
      my-service:
        enabled: true
        appName: ["my-app"]
        # Render the service type based on the user's selection
        # '{{repl ...}}' syntax is used for `type` to improve readability of the if-else statement and render a string
        type: '{{repl if ConfigOptionEquals "ingress_type" "load_balancer" }}LoadBalancer{{repl else }}ClusterIP{{repl end }}'
        ports:
          http:
            enabled: true
            # Render the HTTP port for the service depending on the user's selection
            # repl{{ ... }} syntax is used for `port` to render an integer value
            port: repl{{ if ConfigOptionEquals "ingress_type" "load_balancer" }}repl{{ ConfigOption "load_balancer_port" }}repl{{ else }}8081repl{{ end }}
            protocol: HTTP
            targetPort: 8081
```

### Ternary operators

Ternary operators are useful for templating strings where certain values must render differently based on a condition.
They work best when you need to render a small portion of a string conditionally,
rather than choosing between entirely different values.
For example, you could use ternary operators to template the path to an image repository based on user-supplied values.

The following example uses ternary operators to render the registry and repository for a private nginx image.
The rendered value depends on whether the customer uses a local image registry.
This example uses the following Replicated template functions:
* [HasLocalRegistry](/reference/template-functions-config-context#haslocalregistry)
  to return true if the environment rewrites images to a local registry
* [LocalRegistryHost](/reference/template-functions-config-context#localregistryhost)
  to return the local registry host configured by the user
* [LocalRegistryNamespace](/reference/template-functions-config-context#localregistrynamespace)
  to return the local registry namespace configured by the user

```yaml
# Replicated HelmChart custom resource
apiVersion: kots.io/v1beta2
kind: HelmChart
metadata:
  name: samplechart
spec:
  values:
    image:
      # If a local registry is configured, use the local registry host.
      # Otherwise, use proxy.replicated.com
      registry: repl{{ HasLocalRegistry | ternary LocalRegistryHost "proxy.replicated.com" }}
      # If a local registry is configured, use the local registry's namespace.
      # Otherwise, use proxy/my-app/quay.io/my-org
      repository: repl{{ HasLocalRegistry | ternary LocalRegistryNamespace "proxy/my-app/quay.io/my-org" }}/nginx
      tag: v1.0.1
```

## Formatting examples

This section includes examples of how to format the rendered output of Replicated template functions.

In addition to the examples in this section,
Replicated template functions in the Static context include several formatting options.
These include converting strings to upper or lower case and trimming leading and trailing space characters.
For more information, see [Static Context](/reference/template-functions-static-context).

### Indentation

When using template functions within nested YAML,
indent the rendered template functions correctly so that the YAML renders.
A common use case for indentation is templating annotations in resource or object metadata
based on user-supplied values.

The [nindent](https://masterminds.github.io/sprig/strings.html) function adds a new line to the beginning of the string
and indents the string by a specified number of spaces.

The following example shows templating a Helm chart value that sets annotations for an Ingress object.
This example uses the Replicated
[ConfigOption](/reference/template-functions-config-context#configoption) template function
to return user-supplied annotations from the configuration screen in the Replicated installer UI.
It also uses [nindent](https://masterminds.github.io/sprig/strings.html) to indent the rendered value ten spaces.

```yaml
# Replicated HelmChart custom resource
apiVersion: kots.io/v1beta2
kind: HelmChart
metadata:
  name: myapp
spec:
  values:
    services:
      myservice:
        annotations: repl{{ ConfigOption "additional_annotations" | nindent 10 }}
```

### Render quoted values

To wrap a rendered value in quotes,
you can pipe the result from Replicated template functions with the `repl{{ ... }}` syntax into quotes using `| quote`.
Or, you can use the `'{{repl ... }}'` syntax instead.

One use case for quoted values in YAML is when values include indicator characters.
In YAML, indicator characters (`-`, `?`, `:`) have special semantics and require escaping when used in values.
For more information, see [Indicator Charactors](https://yaml.org/spec/1.2.2/#53-indicator-characters)
in the YAML documentation.

#### Example with `'{{repl ... }}'` syntax

```yaml
customTag: '{{repl ConfigOption "tag" }}'
```
#### Example with `| quote`

```yaml
customTag: repl{{ ConfigOption "tag" | quote }}
```

The result for both examples is:

```yaml
customTag: 'key: value'
```

## Variables example

This section includes an example of using variables with Replicated template functions.
For more information, see [Variables](https://pkg.go.dev/text/template#hdr-Variables) in the Go documentation.

### Using variables to generate TLS certificates in JSON

You can use the Sprig [genCA](https://masterminds.github.io/sprig/crypto.html) and
[genSignedCert](https://masterminds.github.io/sprig/crypto.html) functions
with Replicated template functions to generate certificate authorities (CAs) and signed certificates in JSON.
One use case for this is to generate default CAs, certificates, and keys that users can override with their own values.

The Sprig [genCA](https://masterminds.github.io/sprig/crypto.html) and
[genSignedCert](https://masterminds.github.io/sprig/crypto.html) functions
require the subject's common name and the certificate's validity duration in days.
The `genSignedCert` function also requires the CA that will sign the certificate.
You can use variables and Replicated template functions
to provide the necessary parameters when calling these functions.

The following example shows how to use variables and Replicated template functions
in the `default` property of a [`hidden`](/reference/custom-resource-config#hidden) item.
The example passes parameters to the `genCA` and `genSignedCert` functions to generate a CA, certificate, and key.
It uses a `hidden` item (not displayed on the configuration screen) to generate the certificate chain.
In the Replicated Config custom resource,
you can only access variables from the same item where you declared them.
For this reason, `hidden` items are useful for evaluating complex templates.

This example uses the following:
* Replicated [ConfigOption](/reference/template-functions-config-context#configoption) template function
  to render the user-supplied value for the ingress hostname.
  Pass this as a parameter to the [genCA](https://masterminds.github.io/sprig/crypto.html) and
  [genSignedCert](https://masterminds.github.io/sprig/crypto.html) functions
* Sprig [genCA](https://masterminds.github.io/sprig/crypto.html) and
  [genSignedCert](https://masterminds.github.io/sprig/crypto.html) functions
  to generate a CA and a certificate signed by the CA
* Sprig [dict](https://masterminds.github.io/sprig/dicts.html),
  [set](https://masterminds.github.io/sprig/dicts.html),
  and [dig](https://masterminds.github.io/sprig/dicts.html) dictionary functions
  to create a dictionary with entries for both the CA and the certificate,
  then traverse the dictionary to return the values of the CA, certificate, and key.
* [toJson](https://masterminds.github.io/sprig/defaults.html) and
  [fromJson](https://masterminds.github.io/sprig/defaults.html) Sprig functions
  to encode the CA and certificate into a JSON string,
  then decode the JSON for the purpose of displaying the values on the configuration screen as defaults

:::important
Replicated treats default values as ephemeral.
Replicated recalculates the following certificate chain each time you modify the application configuration.
Before using this example with your application,
be sure that your application can handle updating these parameters dynamically.
:::

```yaml
apiVersion: kots.io/v1beta1
kind: Config
metadata:
  name: config-sample
spec:
  groups:
    - name: example_settings
      title: My Example Config
      items:
        - name: ingress_hostname
          title: Ingress Hostname
          help_text: Enter a DNS hostname to use as the cert's CN.
          type: text
        - name: tls_json
          title: TLS JSON
          type: textarea
          hidden: true
          default: |-
            repl{{ $ca := genCA (ConfigOption "ingress_hostname") 365 }}
            repl{{ $tls := dict "ca" $ca }}
            repl{{ $cert := genSignedCert (ConfigOption "ingress_hostname") (list ) (list (ConfigOption "ingress_hostname")) 365 $ca }}
            repl{{ $_ := set $tls "cert" $cert }}
            repl{{ toJson $tls }}
        - name: tls_ca
          title: Signing Authority
          type: textarea
          default: repl{{ fromJson (ConfigOption "tls_json") | dig "ca" "Cert" "" }}
        - name: tls_cert
          title: TLS Cert
          type: textarea
          default: repl{{ fromJson (ConfigOption "tls_json") | dig "cert" "Cert" "" }}
        - name: tls_key
          title: TLS Key
          type: textarea
          default: repl{{ fromJson (ConfigOption "tls_json") | dig "cert" "Key" "" }}
```

The following image shows how the default values for the CA, certificate, and key appear on the configuration screen:

<img alt="Default values for CA, certificate, and key on the Config page" src="/images/certificate-chain-default-values.png" width="550px"/>

[View a larger version of this image](/images/certificate-chain-default-values.png)