Compare commits

...

1 Commits

Author SHA1 Message Date
Morten Torkildsen
7432392a51 Multiple declarative functions in the same file should execute in order 2020-12-22 11:22:22 -08:00
2 changed files with 116 additions and 4 deletions

View File

@@ -11,6 +11,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"sort" "sort"
"strconv"
"strings" "strings"
"sync/atomic" "sync/atomic"
@@ -243,7 +244,10 @@ func (r RunFns) getFunctionsFromInput(nodes []*yaml.RNode) ([]kio.Filter, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
sortFns(buff) err = sortFns(buff)
if err != nil {
return nil, err
}
return r.getFunctionFilters(false, buff.Nodes...) return r.getFunctionFilters(false, buff.Nodes...)
} }
@@ -322,12 +326,33 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
} }
// sortFns sorts functions so that functions with the longest paths come first // 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 // sort the nodes so that we traverse them depth first
// functions deeper in the file system tree should be run first // functions deeper in the file system tree should be run first
sort.Slice(buff.Nodes, func(i, j int) bool { sort.Slice(buff.Nodes, func(i, j int) bool {
mi, _ := buff.Nodes[i].GetMeta() mi, _ := buff.Nodes[i].GetMeta()
pi := filepath.ToSlash(mi.Annotations[kioutil.PathAnnotation]) 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" { if filepath.Base(path.Dir(pi)) == "functions" {
// don't count the functions dir, the functions are scoped 1 level above // don't count the functions dir, the functions are scoped 1 level above
pi = filepath.Dir(path.Dir(pi)) pi = filepath.Dir(path.Dir(pi))
@@ -335,8 +360,6 @@ func sortFns(buff *kio.PackageBuffer) {
pi = filepath.Dir(pi) pi = filepath.Dir(pi)
} }
mj, _ := buff.Nodes[j].GetMeta()
pj := filepath.ToSlash(mj.Annotations[kioutil.PathAnnotation])
if filepath.Base(path.Dir(pj)) == "functions" { if filepath.Base(path.Dir(pj)) == "functions" {
// don't count the functions dir, the functions are scoped 1 level above // don't count the functions dir, the functions are scoped 1 level above
pj = filepath.Dir(path.Dir(pj)) pj = filepath.Dir(path.Dir(pj))
@@ -365,6 +388,7 @@ func sortFns(buff *kio.PackageBuffer) {
// sort by path names if depths are equal // sort by path names if depths are equal
return pi < pj return pi < pj
}) })
return outerErr
} }
// init initializes the RunFns with a containerFilterProvider. // 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) { func TestRunFns_network(t *testing.T) {
tests := []struct { tests := []struct {
name string name string