Merge pull request #2826 from ZhuGongpu/master

Add an option to log which function is running
This commit is contained in:
Kubernetes Prow Robot
2020-08-17 13:25:31 -07:00
committed by GitHub
4 changed files with 132 additions and 4 deletions

View File

@@ -80,6 +80,15 @@ type Pipeline struct {
// Execute executes each step in the sequence, returning immediately after encountering
// any error as part of the Pipeline.
func (p Pipeline) Execute() error {
return p.ExecuteWithCallback(nil)
}
// PipelineExecuteCallbackFunc defines a callback function that will be called each time a step in the pipeline succeeds.
type PipelineExecuteCallbackFunc = func(op Filter)
// ExecuteWithCallback executes each step in the sequence, returning immediately after encountering
// any error as part of the Pipeline. The callback will be called each time a step succeeds.
func (p Pipeline) ExecuteWithCallback(callback PipelineExecuteCallbackFunc) error {
var result []*yaml.RNode
// read from the inputs
@@ -99,6 +108,9 @@ func (p Pipeline) Execute() error {
var err error
for i := range p.Filters {
op := p.Filters[i]
if callback != nil {
callback(op)
}
result, err = op.Filter(result)
if len(result) == 0 || err != nil {
return errors.Wrap(err)

View File

@@ -4,9 +4,14 @@
package kio_test
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"sigs.k8s.io/kustomize/kyaml/yaml"
. "sigs.k8s.io/kustomize/kyaml/kio"
)
@@ -23,6 +28,50 @@ func TestPipe(t *testing.T) {
}
}
func TestSlice_Write(t *testing.T) {
type mockCallback struct {
mock.Mock
}
func (c *mockCallback) Callback(op Filter) {
c.Called(op)
}
func TestPipelineWithCallback(t *testing.T) {
input := ResourceNodeSlice{yaml.MakeNullNode()}
noopFilter1 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return nodes, nil
}
noopFilter2 := func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return nodes, nil
}
filters := []Filter{
FilterFunc(noopFilter1),
FilterFunc(noopFilter2),
}
p := Pipeline{
Inputs: []Reader{input},
Filters: filters,
Outputs: []Writer{},
}
callback := mockCallback{}
// setup expectations. `Times` means the function is called no more than `times`.
callback.On("Callback", mock.Anything).Times(len(filters))
err := p.ExecuteWithCallback(callback.Callback)
if !assert.NoError(t, err) {
assert.FailNow(t, err.Error())
}
callback.AssertNumberOfCalls(t, "Callback", len(filters))
// assert filters are called in the order they are defined.
for i, filter := range filters {
assert.Equal(
t,
reflect.ValueOf(callback.Calls[i].Arguments[0]).Pointer(),
reflect.ValueOf(filter).Pointer(),
)
}
}

View File

@@ -74,6 +74,12 @@ type RunFns struct {
// ResultsDir is where to write each functions results
ResultsDir string
// LogSteps enables logging the function that is running.
LogSteps bool
// LogWriter can be set to write the logs to LogWriter rather than stderr if LogSteps is enabled.
LogWriter io.Writer
// resultsCount is used to generate the results filename for each container
resultsCount uint32
@@ -169,8 +175,33 @@ func (r RunFns) runFunctions(
// the output is nil (reading from Input)
outputs = append(outputs, kio.ByteWriter{Writer: r.Output})
}
err := kio.Pipeline{
Inputs: []kio.Reader{input}, Filters: fltrs, Outputs: outputs}.Execute()
var err error
pipeline := kio.Pipeline{
Inputs: []kio.Reader{input},
Filters: fltrs,
Outputs: outputs,
}
if r.LogSteps {
err = pipeline.ExecuteWithCallback(func(op kio.Filter) {
var identifier string
switch filter := op.(type) {
case *container.Filter:
identifier = filter.Image
case *exec.Filter:
identifier = filter.Path
case *starlark.Filter:
identifier = filter.String()
default:
identifier = "unknown-type function"
}
_, _ = fmt.Fprintf(r.LogWriter, "Running %s\n", identifier)
})
} else {
err = pipeline.Execute()
}
if err != nil {
return err
}
@@ -333,6 +364,11 @@ func (r *RunFns) init() {
if r.functionFilterProvider == nil {
r.functionFilterProvider = r.ffp
}
// if LogSteps is enabled and LogWriter is not specified, use stderr
if r.LogSteps && r.LogWriter == nil {
r.LogWriter = os.Stderr
}
}
// ffp provides function filters

View File

@@ -14,6 +14,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/container"
@@ -906,6 +907,36 @@ func TestCmd_Execute_setInput(t *testing.T) {
assert.Contains(t, string(b), "kind: StatefulSet")
}
// TestCmd_Execute_enableLogSteps tests the execution of a filter with LogSteps enabled.
func TestCmd_Execute_enableLogSteps(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, "filter.yaml"), []byte(ValueReplacerYAMLData), 0600)) {
return
}
logs := &bytes.Buffer{}
instance := RunFns{
Path: dir,
functionFilterProvider: getFilterProvider(t),
LogSteps: true,
LogWriter: logs,
}
if !assert.NoError(t, instance.Execute()) {
t.FailNow()
}
b, err := ioutil.ReadFile(
filepath.Join(dir, "java", "java-deployment.resource.yaml"))
if !assert.NoError(t, err) {
t.FailNow()
}
assert.Contains(t, string(b), "kind: StatefulSet")
assert.Equal(t, "Running unknown-type function\n", logs.String())
}
// setupTest initializes a temp test directory containing test data
func setupTest(t *testing.T) string {
dir, err := ioutil.TempDir("", "kustomize-kyaml-test")