How Can I update node of one yaml file from content of another yaml file?

107 Views Asked by At

I have two yaml files:

key: value
configmap:
  data:
    foo: bar
app: test
config:
  testKey: testValue

I want to update file1.yaml's configmap.data with file2.yaml's config and save it back in file1.yaml so file1.yaml becomes:

key: value
configmap:
 data:
  testKey: testValue

How can I do this?

I tried the yq command

yq -i e '(select(fileIndex==0).configmap.data = select(fileIndex==1).config)' 'file1.yaml' 'file2.yaml'

But I got the following output, which appended the content of file1.yaml instead of updating it

key: value
configmap:
  data:
    foo: bar
---
app: test
config:
  testKey: testValue

1

There are 1 best solutions below

0
On

Please note that you are using mikefarah/yq (as opposed to kislyuk/yq which goes by the same name and can also be referenced by the yq tag, but using it would require a very different solution of this problem).

You can use the load function to load the contents of another file, then just assign the values of and to the right fields:

yq '.configmap.data = load("file2.yaml").config' file1.yaml
key: value
configmap:
  data:
    testKey: testValue

Add the -i flag to save the changes back into the primary input file (file1.yaml).

Tested with mikefarah/yq version v4.34.2. (Note that since version 4.18.1, the eval/e command is the default command, and no longer needs to be specified.)


If you needed to specify the file names on shell level, e.g. from variables (which is trivial if they are plain arguments, but not so much if they have become part of the filter code), be aware that injecting them into the code string isn't good practice and creates unnecessary vulnerabilities. Instead, set environment variables, and use the strenv function to read them:

src=file2.yaml yq '.configmap.data = load(strenv(src)).config' file1.yaml

However, if you're for some reason restricted to provide all files as parameters, your approach of using fileIndex/file_index/fi is indeed the way to go. Use the eval-all/ea command to gather all sources into the same main context, and either reduce eventually to the target file's sub-context by appending another select filter (closest to your approach), or reduce before the assignment (break select(fi==0).xxx = into select(fi==0) | .xxx =) yet after presetting a variable to carry over a value from another sub-context:

yq ea 'select(fi==0).configmap.data = select(fi==1).config | select(fi==0)' file1.yaml file2.yaml
# or
yq ea 'select(fi==1).config as $cfg | select(fi==0) | .configmap.data = $cfg' file1.yaml file2.yaml