Enable functions to defer failures

so multiple validators can be run sequentially without shortcircuiting the pipeline
This commit is contained in:
Phillip Wittrock
2020-04-17 20:26:56 -07:00
parent c7b00733c1
commit 6d9702561a
5 changed files with 254 additions and 20 deletions

View File

@@ -164,7 +164,27 @@ func (r RunFns) runFunctions(
// the output is nil (reading from Input)
outputs = append(outputs, kio.ByteWriter{Writer: r.Output})
}
return kio.Pipeline{Inputs: []kio.Reader{input}, Filters: fltrs, Outputs: outputs}.Execute()
err := kio.Pipeline{
Inputs: []kio.Reader{input}, Filters: fltrs, Outputs: outputs}.Execute()
if err != nil {
return err
}
// check for deferred function errors
var errs []string
for i := range fltrs {
cf, ok := fltrs[i].(filters.DeferFailureFunction)
if !ok {
continue
}
if cf.GetExit() != nil {
errs = append(errs, cf.GetExit().Error())
}
}
if len(errs) > 0 {
return fmt.Errorf(strings.Join(errs, "\n---\n"))
}
return nil
}
// getFunctionsFromInput scans the input for functions and runs them
@@ -328,6 +348,7 @@ func (r *RunFns) ffp(spec filters.FunctionSpec, api *yaml.RNode) (kio.Filter, er
StorageMounts: r.StorageMounts,
GlobalScope: r.GlobalScope,
ResultsFile: resultsFile,
DeferFailure: spec.DeferFailure,
}, nil
}
if r.EnableStarlark && spec.Starlark.Path != "" {

View File

@@ -15,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
"sigs.k8s.io/kustomize/kyaml/yaml"
@@ -234,6 +235,29 @@ metadata:
out: []string{"gcr.io/example.com/image:v1.0.0"},
},
// Test
//
//
{name: "defer_failure",
in: []f{
{
path: filepath.Join("foo", "bar.yaml"),
value: `
apiVersion: example.com/v1alpha1
kind: ExampleFunction
metadata:
annotations:
config.kubernetes.io/function: |
deferFailure: true
container:
image: gcr.io/example.com/image:v1.0.0
config.kubernetes.io/local-config: "true"
`,
},
},
out: []string{"gcr.io/example.com/image:v1.0.0 deferFailure: true"},
},
{name: "disable containers",
in: []f{
{
@@ -674,6 +698,93 @@ func TestCmd_Execute(t *testing.T) {
assert.Contains(t, string(b), "kind: StatefulSet")
}
type TestFilter struct {
invoked bool
Exit error
}
func (f *TestFilter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
f.invoked = true
return input, nil
}
func (f *TestFilter) GetExit() error {
return f.Exit
}
func TestCmd_Execute_deferFailure(t *testing.T) {
dir := setupTest(t)
defer os.RemoveAll(dir)
// write a test filter to the directory of configuration
if !assert.NoError(t, ioutil.WriteFile(
filepath.Join(dir, "filter1.yaml"), []byte(`apiVersion: v1
kind: ValueReplacer
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: 1
config.kubernetes.io/local-config: "true"
stringMatch: Deployment
replace: StatefulSet
`), 0600)) {
t.FailNow()
}
// write a test filter to the directory of configuration
if !assert.NoError(t, ioutil.WriteFile(
filepath.Join(dir, "filter2.yaml"), []byte(`apiVersion: v1
kind: ValueReplacer
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: 2
config.kubernetes.io/local-config: "true"
stringMatch: Deployment
replace: StatefulSet
`), 0600)) {
t.FailNow()
}
var fltrs []*TestFilter
instance := RunFns{
Path: dir,
functionFilterProvider: func(f filters.FunctionSpec, node *yaml.RNode) (kio.Filter, error) {
tf := &TestFilter{
Exit: errors.Errorf("message: %s", f.Container.Image),
}
fltrs = append(fltrs, tf)
return tf, nil
},
}
instance.init()
err := instance.Execute()
// make sure all filters were run
if !assert.Equal(t, 2, len(fltrs)) {
t.FailNow()
}
for i := range fltrs {
if !assert.True(t, fltrs[i].invoked) {
t.FailNow()
}
}
if !assert.EqualError(t, err, "message: 1\n---\nmessage: 2") {
t.FailNow()
}
b, err := ioutil.ReadFile(
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
if !assert.NoError(t, err) {
t.FailNow()
}
// files weren't changed because there was an error
assert.Contains(t, string(b), "kind: Deployment")
}
// TestCmd_Execute_setOutput tests the execution of a filter reading and writing to a dir
func TestCmd_Execute_setFunctionPaths(t *testing.T) {
dir := setupTest(t)