From 44619d5ca27b442e03299e3c54c0a86e8138664d Mon Sep 17 00:00:00 2001 From: Katrina Verey Date: Fri, 16 Oct 2020 11:28:07 -0700 Subject: [PATCH] Option to continue pipeline processing when filter returns empty result --- kyaml/kio/kio.go | 12 +++++++++++- kyaml/kio/kio_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/kyaml/kio/kio.go b/kyaml/kio/kio.go index 91efde1d9..12271aa81 100644 --- a/kyaml/kio/kio.go +++ b/kyaml/kio/kio.go @@ -75,6 +75,16 @@ type Pipeline struct { // Outputs are where the transformed Resource Configuration is written. Outputs []Writer `yaml:"outputs,omitempty"` + + // ContinueOnEmptyResult configures what happens when a filter in the pipeline + // returns an empty result. + // If it is false (default), subsequent filters will be skipped and the result + // will be returned immediately. This is useful as an optimization when you + // know that subsequent filters will not alter the empty result. + // If it is true, the empty result will be provided as input to the next + // filter in the list. This is useful when subsequent functions in the + // pipeline may generate new resources. + ContinueOnEmptyResult bool `yaml:"continueOnEmptyResult,omitempty"` } // Execute executes each step in the sequence, returning immediately after encountering @@ -111,7 +121,7 @@ func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) erro // TODO (issue 2872): This len(result) == 0 should be removed and empty result list should be // handled by outputs. However currently some writer like LocalPackageReadWriter // will clear the output directory and which will cause unpredictable results - if len(result) == 0 || err != nil { + if len(result) == 0 && !p.ContinueOnEmptyResult || err != nil { return errors.Wrap(err) } } diff --git a/kyaml/kio/kio_test.go b/kyaml/kio/kio_test.go index 9a3a9ea27..0529c97d7 100644 --- a/kyaml/kio/kio_test.go +++ b/kyaml/kio/kio_test.go @@ -149,3 +149,41 @@ items: t.FailNow() } } + +func TestContinueOnEmptyBehavior(t *testing.T) { + cases := map[string]struct { + continueOnEmptyResult bool + expected string + }{ + "quit on empty": {continueOnEmptyResult: false, expected: ""}, + "continue on empty": {continueOnEmptyResult: true, expected: "foo: bar"}, + } + for _, tc := range cases { + actual := &bytes.Buffer{} + output := ByteWriter{Writer: actual} + + generatorFunc := FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) { + nodes = append(nodes, yaml.NewMapRNode(&map[string]string{ + "foo": "bar", + })) + return nodes, nil + }) + emptyFunc := FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) { return nodes, nil }) + + p := Pipeline{ + Outputs: []Writer{output}, + Filters: []Filter{emptyFunc, generatorFunc}, + ContinueOnEmptyResult: tc.continueOnEmptyResult, + } + + err := p.Execute() + if err != nil { + t.Fatal(err) + } + + if !assert.Equal(t, + tc.expected, strings.TrimSpace(actual.String())) { + t.Fail() + } + } +}