Conditional YAML Objects
# 🌱|help-and-getting-started
c
When writing Garden configuration, is it possible to conditionally include a YAML object using the variable syntax? If for example I need to provide a
configurationValues
object when a certain environment is detected I would expect to be able to use Garden's
$if; $then; $else
syntax to conditionally include or exclude that object. i.e. Starting from this:
Copy code
SomeParentValue:
  configurationValues:
    debug: true
    application_url: "localhost"
    log_level: debug
I might then want to transition to a config like this:
Copy code
SomeParentValue:
  $if: ${local.env.DEBUG_MODE}
  $then:
    configurationValues:
      debug: true
      application_url: "localhost"
      log_level: debug
This doesn't appear to function or validate. Is there a canonical way to achieve this?
b
Hi @clever-policeman-58407 thanks for asking the question here! $if: $then: $else: does work exactly like you described, I'm always making a small mini-project to reproduce issues like that:
Copy code
apiVersion: garden.io/v1
kind: Project
name: foo

environments:
  - name: dev

providers: []

---
kind: Test
type: exec
name: test
spec:
  command: [echo, "${yamlEncode(var.conditionalObject)}"]
variables:
  conditionalObject:
    $if: ${local.env.FOO == "1"}
    $then:
      foo: bar
    $else:
      bar: baz
When you run
FOO=1 garden test -l3
it behaves as expected (printing
foo: bar
) I guess where things become interesting if
SomeParentValue
also has other keys:
Copy code
conditionalObject:
    hello: world
    $if: ${local.env.FOO == "1"}
    $then:
      foo: bar
    $else:
      bar: baz
This leads to the error
Found one or more unexpected keys on $if object: "hello". Expected: $if, $then and $else
You can solve that by using `$merge`:
Copy code
conditionalObject:
    hello: world
    $merge:
      $if: ${local.env.FOO == "1"}
      $then:
        foo: bar
      $else:
        bar: baz
The command
FOO=1 garden test -l3
with this config results in
Copy code
hello: world
foo: bar
@clever-policeman-58407 I hope that helped! If not, please elaborate on what you mean by "This doesn't appear to function or validate"
c
This does actually seem to work when used within a child resource, and I wonder if maybe my issue is actually specific to root configuration instead. I wanted to optionally include a value for
providers.deploymentRegistry
based on which Kubernetes cluster tool was running and attempting to do something similar results in the following:
Copy code
Found one or more unexpected keys on $if object: "name", "environments", "namespace", "defaultHostname", "context", "setupIngressController", "sync" and "dependencies". Expected: $if, $then and $else
You can reproduce that issue with a configuration like this:
Copy code
apiVersion: garden.io/v1
kind: Project
name: foobar
defaultEnvironment: local

environments:
  - name: local
    defaultNamespace: default

providers:
  - name: local-kubernetes
    environments: [local]
    namespace: foobar
    defaultHostname: localhost
    context: "k3d-default"

    setupIngressController: nginx

    $if: ${local.env.FOO == "1"}
    $then:
      deploymentRegistry:
        hostname: k3d-registry
        port: 5000
        insecure: true
        namespace: ${kebabCase(local.username)}
Theoretically you might do something like this with local clusters that are nominally similar in usage but that aren't quite 1:1 on details like the registry (k3d / k3s / Rancher)
You would want to keep them as one provider with conditional variables for downstream logic that does things like
$if: ${environment.name} == local
rather than being forced to do something like
$if: ${environment.name} in [ 'local-k3d', 'local-rancher', 'local-k3s' ]
and needing to maintain that list an undefined number of times in every place environment name based logic is used
Potential workaround: include an
is-local
or similarly named environment variable. Functional, but messier and more maintenance than just being able to have them all function as literally the same environment. Every user and every tool now has to specify which environment it is using rather than being able to rely on the default local implementation.
b
Thanks for the feedback. Looking at your code, I understand why you expect it to work this way. At the moment you need to write your config like this:
Copy code
YAML
apiVersion: garden.io/v1
kind: Project
name: foobar
defaultEnvironment: local

environments:
  - name: local
    defaultNamespace: default

providers:
  - name: local-kubernetes
    environments: [local]
    namespace: foobar
    defaultHostname: localhost
    context: "k3d-default"

    setupIngressController: nginx

    $merge:
      $if: ${local.env.FOO == "1"}
      $then:
        deploymentRegistry:
          hostname: k3d-registry
          port: 5000
          insecure: true
          namespace: ${kebabCase(local.username)}
For differing configs across different environments, one can declare environment-specific variables like so; Maybe this is the way to go in your case:
Copy code
YAML
apiVersion: garden.io/v1
kind: Project
name: foobar
defaultEnvironment: local

environments:
  - name: local
    defaultNamespace: default
    variables:
      deploymentRegistry:
        $if: ${local.env.FOO == "1"}
        $then:
          hostname: k3d-registry
          port: 5000
          insecure: true
          namespace: ${kebabCase(local.username)}

providers:
  - name: local-kubernetes
    environments: [local]
    namespace: foobar
    defaultHostname: localhost
    context: "k3d-default"

    setupIngressController: nginx

    deploymentRegistry: ${var.deploymentRegistry}
c
Not what I'd expect, but that works too 👍
4 Views