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 node and produces the right node, similar to the replace
overlay action.
  • Loading branch information
mamachanko committed Sep 11, 2022
1 parent a787f84 commit 392a438
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 11 deletions.
@@ -0,0 +1,40 @@
#@ load("@ytt:overlay", "overlay")

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

---
#@ def test1_right():
#@overlay/match by=lambda i, l, r: "item" in l, expects=3
#@overlay/insert before=True, via=lambda l: {"from_item": l["item"], "before": True}
- {}
#@overlay/match by=lambda i, l, r: "item" in l, expects=3
#@overlay/insert after=True, via=lambda l: {"from_item": l["item"], "after": True}
- {}
#@ end

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

+++

test1:
- from_item: 1
before: true
- item: 1
- from_item: 1
after: true
- from_item: 2
before: true
- item: 2
- from_item: 2
after: true
- from_item: 3
before: true
- item: 3
- from_item: 3
after: true
@@ -0,0 +1,48 @@
#@ load("@ytt:template", "template")
#@ load("@ytt:overlay", "overlay")

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

#@ def test1_right():
#@overlay/match by=lambda i, l, r: "item" in l, expects=3
#@overlay/insert before=True, via=lambda l: {"from_item": l["item"], "before": True}
---
#@overlay/match by=lambda i, l, r: "item" in l, expects=3
#@overlay/insert after=True, via=lambda l: {"from_item": l["item"], "after": True}
---
#@ end

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

+++

from_item: 1
before: true
---
item: 1
---
from_item: 1
after: true
---
from_item: 2
before: true
---
item: 2
---
from_item: 2
after: true
---
from_item: 3
before: true
---
item: 3
---
from_item: 3
after: true
17 changes: 14 additions & 3 deletions pkg/yttlibrary/overlay/array.go
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
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
56 changes: 51 additions & 5 deletions pkg/yttlibrary/overlay/insert_annotation.go
Expand Up @@ -5,20 +5,31 @@ 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"
)

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}
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 +47,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 +75,35 @@ func NewInsertAnnotation(newItem template.EvaluationNode) (InsertAnnotation, err

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

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(),
}

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 392a438

Please sign in to comment.