carvel-dev / ytt

YAML templating tool that works on YAML structure instead of text
https://carvel.dev/ytt
Apache License 2.0
1.68k stars 137 forks source link

Inline Overlay Append #574

Closed alexyao2015 closed 2 years ago

alexyao2015 commented 2 years ago

Is there someway to inline append to a list using overlay?

Say I have this overlay append which works but is in a separate document:

#@ load("@ytt:overlay", "overlay")
#@ load("@ytt:template", "template")
#@ def test():
foo:
  bar:
    - item1
#@ end
---
_: #@ template.replace(test())

#@overlay/match by=overlay.all
---
foo:
  bar:
    #@overlay/append
    - item2

Is there some way to convert this to something like this below? The below does not work as an error occurs: - __ytt_tpl2_start_node: expected key 'foo' to not be specified again (unless 'yaml/map-key-override' annotation is added)

#@ load("@ytt:overlay", "overlay")
#@ load("@ytt:template", "template")
#@ def test():
foo:
  bar:
    - item1
#@ end
---
_: #@ template.replace(test())
foo:
  bar:
    #@overlay/append
    - item2

I would like the output to look like below:

foo:
  bar:
  - item1
  - item2
cari-lynn commented 2 years ago

If the base and the overlay are in the same file, I would recommend using the overlay.apply(left(), right()) function. You can do that by wrapping the base, and the overlay in a function like below. If you're using v0.32.0+ you can omit the overlay/append:

#@ load("@ytt:overlay", "overlay")
#@ load("@ytt:template", "template")

#! 'left' is the yaml being modified
#@ def left():
foo:
  bar:
    - item1
#@ end
---

#! 'right' is the overlay describing the modification
#@ def right():
foo:
  bar:
    - item2
#@ end
---

#! 'template.replace' replaces the existing yaml node with the result from the overlay
_: #@ template.replace(overlay.apply(left(), right()))

Does this solution fit what you need? If not, can you share a bit more on your use case?

cari-lynn commented 2 years ago

There is an alternative using just template.replace() and no overlays.

Overlays match on yaml structure, which may be helpful if you want to append the same items to multiple arrays with the same structure, and without modifying each one. Alternatively, if you use the below solution, each place you want to append to an array will need to be modified.

#@ def additional_items():
- item2
- item3
#@ end 
---

foo:
  bar:
    - item1
    - #@ template.replace(additional_items())
alexyao2015 commented 2 years ago

Thanks for the prompt reply!

That doesn't quite match what I'm looking for. Specifically, I'm working with a config file where I need to call a function that defines a base set of parameters and expands on those by adding to a list.

I see that I can create two separate functions and call overlay.apply, but it would force me to have to separate the parameters which are specific to each entry in a separate function. Ideally, I would have them inline so that it is easier to read and modify if needed.

Consider this where each entry has a different additional entry. A separate function for each entry would clutter the file and result in constantly having to jump around the file to find what goes to what. Removing foo and calling overlay.apply with a separate function does work, but it results in it being hard to follow because there would be one unique function for each config entry.

#@ load("@ytt:overlay", "overlay")
#@ load("@ytt:template", "template")
#@ def test(config_string):
foo:
  - default_item
  - #@ "str(config_string)"
#@ end
---
entry_1:
  _: #@ template.replace(test("entry_1"))
  foo:
    - entry_1_extra
entry_2:
  _: #@ template.replace(test("entry_2"))
  foo:
    - entry_2_extra
entry_1:
  foo:
  - default_item
  - entry_1
  - entry_1_extra
entry_2:
  foo:
  - default_item
  - entry_2
  - entry_2_extra
alexyao2015 commented 2 years ago

Perhaps is there some way to achieve the following?

#@ def test(*config_string):
foo:
  - default_item
  - # append all config_string passed
#@ end
---
entry_1:
  _: #@ template.replace(test("{"entry_1", "entry_1_extra"}"))
entry_2:
  _: #@ template.replace(test("{"entry_2", "entry_2_extra"}"))
pivotaljohn commented 2 years ago

Something like...

#@ load("@ytt:template", "template")

#@ def foo(additional):
foo:
  - default_item
  - #@ template.replace(additional)
#@ end
---
entry_1:
  _: #@ template.replace(foo(["entry_1", "entry_1_extra"]))
entry_2:
  _: #@ template.replace(foo(["entry_2", "entry_2_extra"]))
gcheadle-vmware commented 2 years ago

Hi @alexyao2015, it looks like this thread has been wrapped up, so I am going to close it. If you have additional comments/questions, please open a new issue or reopen this one. Thanks!

alexyao2015 commented 2 years ago

Hi yes this has been resolved. Thank you!