Update nested array value in yaml with yq
Asked Answered
M

2

10

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
Mortarboard answered 29/5, 2020 at 13:44 Comment(0)
A
15

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

Ashil answered 29/5, 2020 at 14:18 Comment(4)
using yq=4.9.1, unknown shorthand flag: 'y' in -ySubtotal
@SHAHS: This answer is with github.com/kislyuk/yq, if you have the Go version mikefarah/yq, please ask a separate questionAshil
In case it helps others, I was trying to update repository in requirements.yaml file of a helm chart, managed to do it via yq e '.dependencies[] |= select(.name == "test-chart") .repository=<repo-url> requirements.yamlSubtotal
ok thanks for clarification, I did not realize it was different yq implementation.Subtotal
C
0

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" }}
Cedar answered 14/1, 2023 at 12:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.