Multiple declarative functions in the same file should execute in order

This commit is contained in:
Morten Torkildsen
2020-12-17 12:13:42 -08:00
parent 0aa250c6e2
commit 3c25584658
2 changed files with 116 additions and 4 deletions

View File

@@ -11,6 +11,7 @@ import (
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"sync/atomic"
@@ -252,7 +253,10 @@ func (r RunFns) getFunctionsFromInput(nodes []*yaml.RNode) ([]kio.Filter, error)
if err != nil {
return nil, err
}
sortFns(buff)
err = sortFns(buff)
if err != nil {
return nil, err
}
return r.getFunctionFilters(false, buff.Nodes...)
}
@@ -331,12 +335,33 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
}
// sortFns sorts functions so that functions with the longest paths come first
func sortFns(buff *kio.PackageBuffer) {
func sortFns(buff *kio.PackageBuffer) error {
var outerErr error
// sort the nodes so that we traverse them depth first
// functions deeper in the file system tree should be run first
sort.Slice(buff.Nodes, func(i, j int) bool {
mi, _ := buff.Nodes[i].GetMeta()
pi := filepath.ToSlash(mi.Annotations[kioutil.PathAnnotation])
mj, _ := buff.Nodes[j].GetMeta()
pj := filepath.ToSlash(mj.Annotations[kioutil.PathAnnotation])
// If the path is the same, we decide the ordering based on the
// index annotation.
if pi == pj {
iIndex, err := strconv.Atoi(mi.Annotations[kioutil.IndexAnnotation])
if err != nil {
outerErr = err
return false
}
jIndex, err := strconv.Atoi(mj.Annotations[kioutil.IndexAnnotation])
if err != nil {
outerErr = err
return false
}
return iIndex < jIndex
}
if filepath.Base(path.Dir(pi)) == "functions" {
// don't count the functions dir, the functions are scoped 1 level above
pi = filepath.Dir(path.Dir(pi))
@@ -344,8 +369,6 @@ func sortFns(buff *kio.PackageBuffer) {
pi = filepath.Dir(pi)
}
mj, _ := buff.Nodes[j].GetMeta()
pj := filepath.ToSlash(mj.Annotations[kioutil.PathAnnotation])
if filepath.Base(path.Dir(pj)) == "functions" {
// don't count the functions dir, the functions are scoped 1 level above
pj = filepath.Dir(path.Dir(pj))
@@ -374,6 +397,7 @@ func sortFns(buff *kio.PackageBuffer) {
// sort by path names if depths are equal
return pi < pj
})
return outerErr
}
// init initializes the RunFns with a containerFilterProvider.

View File

@@ -734,6 +734,94 @@ metadata:
}
}
func TestRunFns_sortFns(t *testing.T) {
testCases := []struct {
name string
nodes []*yaml.RNode
expectedImages []string
expectedErrMsg string
}{
{
name: "multiple functions in the same file are ordered by index",
nodes: []*yaml.RNode{
yaml.MustParse(`
metadata:
annotations:
config.kubernetes.io/path: functions.yaml
config.kubernetes.io/index: 1
config.kubernetes.io/function: |
container:
image: a
`),
yaml.MustParse(`
metadata:
annotations:
config.kubernetes.io/path: functions.yaml
config.kubernetes.io/index: 0
config.kubernetes.io/function: |
container:
image: b
`),
},
expectedImages: []string{"b", "a"},
},
{
name: "non-integer value in index annotation is an error",
nodes: []*yaml.RNode{
yaml.MustParse(`
metadata:
annotations:
config.kubernetes.io/path: functions.yaml
config.kubernetes.io/index: 0
config.kubernetes.io/function: |
container:
image: a
`),
yaml.MustParse(`
metadata:
annotations:
config.kubernetes.io/path: functions.yaml
config.kubernetes.io/index: abc
config.kubernetes.io/function: |
container:
image: b
`),
},
expectedErrMsg: "strconv.Atoi: parsing \"abc\": invalid syntax",
},
}
for i := range testCases {
test := testCases[i]
t.Run(test.name, func(t *testing.T) {
packageBuff := &kio.PackageBuffer{
Nodes: test.nodes,
}
err := sortFns(packageBuff)
if test.expectedErrMsg != "" {
if !assert.Error(t, err) {
t.FailNow()
}
assert.Equal(t, test.expectedErrMsg, err.Error())
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
var images []string
for _, n := range packageBuff.Nodes {
spec := runtimeutil.GetFunctionSpec(n)
images = append(images, spec.Container.Image)
}
assert.Equal(t, test.expectedImages, images)
})
}
}
func TestRunFns_network(t *testing.T) {
tests := []struct {
name string