Banjer
Banjer

Reputation: 8300

Update nested array value in yaml with yq

Given a yaml file (helmfile) like the following

releases:
  - chart: ../charts/foo
    name: foo
    namespace: '{{ .Values.stack }}'
    values:
      - ../config/templates/foo-values.yaml.gotmpl
    set:
      - name: image.tag
        value: 22
      - name: replicas
        value: 1
  - chart: ../charts/bar
    name: bar
    namespace: '{{ .Values.stack }}'
    values:
      - ../config/templates/bar-values.yaml.gotmpl
    set:
      - name: image.bar_proxy.tag
        value: 46
      - name: image.bar.tag
        value: 29
      - name: replicas
        value: 1

I'm trying to figure out a clean way to update a specific image tag. For example, I'd like to update image.bar_proxy.tag from 46 to 51.

I have the following, which does the job, but it requires that you know the exact index of the array item:

yq -y '.releases[] |= if .name=="bar" then .set[0].value |= 51 else . end' helmfile-example.yaml

So if the array order were to change at some point this would break.

A preferred solution would be: "update image.bar_proxy.tag value from 46 to 51 where set[].name==image.bar_proxy.tag". Any ideas on how to achieve a more specific conditional selection like this?

FYI our yq version:

$ yq --version
yq 2.10.0

Upvotes: 11

Views: 13357

Answers (2)

David Maze
David Maze

Reputation: 158714

Given the context of using Helmfile, there are a couple of ways you can approach this without necessarily editing the helmfile.yaml. Helmfile allows using the Go text/template language in many places, similarly to the underlying Helm tool, and has some other features that can help.

One of the easiest things you can do is take advantage of values: being a list, and of unknown values generally being ignored. You (or your CI/CD system) can write a separate YAML file that contains just the tags (JSON may be easier to write and is valid YAML)

# tags.yaml
image:
  tag: 22
  bar: {tag: 29}
  bar_proxy: {tag: 46}

and then include this file as an additional file in the helmfile.yaml. (This would be equivalent to using helm install -f with multiple local values files, rather than helm install --set individual values.)

releases:
  - name: foo
    values:
      - ../config/templates/foo-values.yaml.gotmpl
      - tags.yaml
    # no `set:`
  - name: bar
    values:
      - ../config/templates/bar-values.yaml.gotmpl
      - tags.yaml
      - replicas: 1
    # no `set:`

Helmfile's templating extensions also include env and requiredEnv to read ordinary environment variables from the host system. Helm proper does not have these to try to minimize the number of implicit inputs to a chart, but for Helmfile it's a possible way to provide values at deploy time.

releases:
  - name: bar
    set:
      - name: image.bar_proxy.tag
        value: {{ env "BAR_PROXY_TAG" | default "46" }}
      - name: image.bar.tag
        value: {{ requiredEnv "BAR_TAG" }}

Upvotes: 0

Inian
Inian

Reputation: 85550

You can use the following filter to make it work. It works by dynamically selecting the index of the object where your tag exists. On the selected object .value=51 will update the value as you wanted. You can also use the -i flag to do in-place modification of the original file.

yq -y '.releases[].set |= map(select(.name == "image.bar_proxy.tag").value=51)' yaml

See the underlying jq filter acting on the JSON object at jq-playground

Upvotes: 16

Related Questions