Skip to content

Commit

Permalink
Optionally overlay/insert via function
Browse files Browse the repository at this point in the history
The insert overlay action has an optional 'via' kwarg, which receives
the left and right node and produces the new right node, similar to the replace
overlay action.
  • Loading branch information
mamachanko committed Sep 16, 2022
1 parent f5ffa14 commit 486de37
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#@ load("@ytt:overlay", "overlay")

---
#@ def test1_left():
- item: 1
- item: 2
- item: 3
#@ end

#@ def is_item(indexOrKey, left, right):
#@ return "item" in left
#@ end

#@ def insert_function(left, right):
#@ return {"left": left, "right": right, "combined": {"item": left["item"] + right["item"]}}
#@ end

---
#@ def test1_right():
#@overlay/match by=is_item, expects=3
#@overlay/insert before=True, via=insert_function
- item: 100
#@overlay/match by=is_item, expects=3
#@overlay/insert after=True, via=insert_function
- item: 200
#@ end

---
test1: #@ overlay.apply(test1_left(), test1_right())

+++

test1:
- left:
item: 1
right:
item: 100
combined:
item: 101
- item: 1
- left:
item: 1
right:
item: 200
combined:
item: 201
- left:
item: 2
right:
item: 100
combined:
item: 102
- item: 2
- left:
item: 2
right:
item: 200
combined:
item: 202
- left:
item: 3
right:
item: 100
combined:
item: 103
- item: 3
- left:
item: 3
right:
item: 200
combined:
item: 203
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#@ load("@ytt:template", "template")
#@ load("@ytt:overlay", "overlay")

#@ def test1_left():
---
item: 1
---
item: 2
---
item: 3
#@ end

#@ def is_item(indexOrKey, left, right):
#@ return "item" in left
#@ end

#@ def insert_function(left, right):
#@ return {"left": left, "right": right, "combined": {"item": left["item"] + right["item"]}}
#@ end

#@ def test1_right():
#@overlay/match by=is_item, expects=3
#@overlay/insert before=True, via=insert_function
---
item: 100
#@overlay/match by=is_item, expects=3
#@overlay/insert after=True, via=insert_function
---
item: 200
#@ end

--- #@ template.replace(overlay.apply(test1_left(), test1_right()))

+++

left:
item: 1
right:
item: 100
combined:
item: 101
---
item: 1
---
left:
item: 1
right:
item: 200
combined:
item: 201
---
left:
item: 2
right:
item: 100
combined:
item: 102
---
item: 2
---
left:
item: 2
right:
item: 200
combined:
item: 202
---
left:
item: 3
right:
item: 100
combined:
item: 103
---
item: 3
---
left:
item: 3
right:
item: 200
combined:
item: 203
17 changes: 14 additions & 3 deletions pkg/yttlibrary/overlay/array.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (o Op) insertArrayItem(
return err
}

insertAnn, err := NewInsertAnnotation(newItem)
insertAnn, err := NewInsertAnnotation(newItem, o.Thread)
if err != nil {
return err
}
Expand All @@ -171,12 +171,23 @@ func (o Op) insertArrayItem(
for _, leftIdx := range leftIdxs {
if i == leftIdx {
matched = true

newVal, err := insertAnn.Value(leftItem)
if err != nil {
return err
}
insertItem := newItem.DeepCopy()
err = insertItem.SetValue(newVal)
if err != nil {
return err
}

if insertAnn.IsBefore() {
updatedItems = append(updatedItems, newItem.DeepCopy())
updatedItems = append(updatedItems, insertItem)
}
updatedItems = append(updatedItems, leftItem)
if insertAnn.IsAfter() {
updatedItems = append(updatedItems, newItem.DeepCopy())
updatedItems = append(updatedItems, insertItem)
}
break
}
Expand Down
17 changes: 14 additions & 3 deletions pkg/yttlibrary/overlay/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func (o Op) insertDocument(
return err
}

insertAnn, err := NewInsertAnnotation(newDoc)
insertAnn, err := NewInsertAnnotation(newDoc, o.Thread)
if err != nil {
return err
}
Expand All @@ -169,12 +169,23 @@ func (o Op) insertDocument(
for _, leftIdx := range leftIdxs {
if leftIdx[0] == i && leftIdx[1] == j {
matched = true

newVal, err := insertAnn.Value(leftItem)
if err != nil {
return err
}
insertDoc := newDoc.DeepCopy()
err = insertDoc.SetValue(newVal)
if err != nil {
return err
}

if insertAnn.IsBefore() {
updatedDocs = append(updatedDocs, newDoc.DeepCopy())
updatedDocs = append(updatedDocs, insertDoc)
}
updatedDocs = append(updatedDocs, leftItem)
if insertAnn.IsAfter() {
updatedDocs = append(updatedDocs, newDoc.DeepCopy())
updatedDocs = append(updatedDocs, insertDoc)
}
break
}
Expand Down
61 changes: 56 additions & 5 deletions pkg/yttlibrary/overlay/insert_annotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,33 @@ package overlay

import (
"fmt"

"github.com/k14s/starlark-go/starlark"
"github.com/vmware-tanzu/carvel-ytt/pkg/template"
tplcore "github.com/vmware-tanzu/carvel-ytt/pkg/template/core"
"github.com/vmware-tanzu/carvel-ytt/pkg/yamltemplate"
)

// Kwargs of overlay/insert
const (
InsertAnnotationKwargBefore string = "before"
InsertAnnotationKwargAfter string = "after"
InsertAnnotationKwargVia string = "via"
)

type InsertAnnotation struct {
newItem template.EvaluationNode
before bool
after bool
via *starlark.Value
thread *starlark.Thread
}

func NewInsertAnnotation(newItem template.EvaluationNode) (InsertAnnotation, error) {
annotation := InsertAnnotation{newItem: newItem}
// NewInsertAnnotation returns a new InsertAnnotation for the given node and with the given Starlark thread
func NewInsertAnnotation(newItem template.EvaluationNode, thread *starlark.Thread) (InsertAnnotation, error) {
annotation := InsertAnnotation{
newItem: newItem,
thread: thread,
}
anns := template.NewAnnotations(newItem)

if !anns.Has(AnnotationInsert) {
Expand All @@ -36,20 +49,23 @@ func NewInsertAnnotation(newItem template.EvaluationNode) (InsertAnnotation, err
kwargName := string(kwarg[0].(starlark.String))

switch kwargName {
case "before":
case InsertAnnotationKwargBefore:
resultBool, err := tplcore.NewStarlarkValue(kwarg[1]).AsBool()
if err != nil {
return InsertAnnotation{}, err
}
annotation.before = resultBool

case "after":
case InsertAnnotationKwargAfter:
resultBool, err := tplcore.NewStarlarkValue(kwarg[1]).AsBool()
if err != nil {
return InsertAnnotation{}, err
}
annotation.after = resultBool

case InsertAnnotationKwargVia:
annotation.via = &kwarg[1]

default:
return annotation, fmt.Errorf(
"Unknown '%s' annotation keyword argument '%s'", AnnotationInsert, kwargName)
Expand All @@ -61,3 +77,38 @@ func NewInsertAnnotation(newItem template.EvaluationNode) (InsertAnnotation, err

func (a InsertAnnotation) IsBefore() bool { return a.before }
func (a InsertAnnotation) IsAfter() bool { return a.after }

// Value returns the new value for the given, existing node. If `via` is not provided, the value of the existing
// node is returned, otherwise the result of `via`.
func (a InsertAnnotation) Value(existingNode template.EvaluationNode) (interface{}, error) {
newNode := a.newItem.DeepCopyAsInterface().(template.EvaluationNode)
if a.via == nil {
return newNode.GetValues()[0], nil
}

switch typedVal := (*a.via).(type) {
case starlark.Callable:
var existingVal interface{}
if existingNode != nil {
existingVal = existingNode.DeepCopyAsInterface().(template.EvaluationNode).GetValues()[0]
} else {
existingVal = nil
}

viaArgs := starlark.Tuple{
yamltemplate.NewGoValueWithYAML(existingVal).AsStarlarkValue(),
yamltemplate.NewGoValueWithYAML(newNode.GetValues()[0]).AsStarlarkValue(),
}

result, err := starlark.Call(a.thread, *a.via, viaArgs, []starlark.Tuple{})
if err != nil {
return nil, err
}

return tplcore.NewStarlarkValue(result).AsGoValue()

default:
return nil, fmt.Errorf("Expected '%s' annotation keyword argument 'via'"+
" to be function, but was %T", AnnotationInsert, typedVal)
}
}

0 comments on commit 486de37

Please sign in to comment.