mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-12 01:14:22 +00:00
Create shared functions filter library
This commit is contained in:
5
kyaml/fn/runtime/runtimeutil/doc.go
Normal file
5
kyaml/fn/runtime/runtimeutil/doc.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package runtimeutil contains libraries for implementing function runtimes.
|
||||||
|
package runtimeutil
|
||||||
1
kyaml/fn/runtime/runtimeutil/example_test.go
Normal file
1
kyaml/fn/runtime/runtimeutil/example_test.go
Normal file
@@ -0,0 +1 @@
|
|||||||
|
package runtimeutil
|
||||||
198
kyaml/fn/runtime/runtimeutil/runtimeutil.go
Normal file
198
kyaml/fn/runtime/runtimeutil/runtimeutil.go
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package runtimeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FunctionFilter wraps another filter to be invoked in the context of a function.
|
||||||
|
// FunctionFilter manages scoping the function, deferring failures, and saving results
|
||||||
|
// to files.
|
||||||
|
type FunctionFilter struct {
|
||||||
|
// Run implements the function.
|
||||||
|
Run func(reader io.Reader, writer io.Writer) error
|
||||||
|
|
||||||
|
// FunctionConfig is passed to the function through ResourceList.functionConfig.
|
||||||
|
FunctionConfig *yaml.RNode `yaml:"functionConfig,omitempty"`
|
||||||
|
|
||||||
|
// GlobalScope explicitly scopes the function to all input resources rather than only those
|
||||||
|
// resources scoped to it by path.
|
||||||
|
GlobalScope bool
|
||||||
|
|
||||||
|
// ResultsFile is the file to write function ResourceList.results to.
|
||||||
|
// If unset, results will not be written.
|
||||||
|
ResultsFile string
|
||||||
|
|
||||||
|
// DeferFailure will cause the Filter to return a nil error even if Run returns an error.
|
||||||
|
// The Run error will be available through GetExit().
|
||||||
|
DeferFailure bool
|
||||||
|
|
||||||
|
// results saves the results emitted from Run
|
||||||
|
results *yaml.RNode
|
||||||
|
|
||||||
|
// exit saves the error returned from Run
|
||||||
|
exit error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetExit returns the error from Run
|
||||||
|
func (c FunctionFilter) GetExit() error {
|
||||||
|
return c.exit
|
||||||
|
}
|
||||||
|
|
||||||
|
// functionsDirectoryName is keyword directory name for functions scoped 1 directory higher
|
||||||
|
const functionsDirectoryName = "functions"
|
||||||
|
|
||||||
|
// getFunctionScope returns the path of the directory containing the function config,
|
||||||
|
// or its parent directory if the base directory is named "functions"
|
||||||
|
func (c *FunctionFilter) getFunctionScope() (string, error) {
|
||||||
|
m, err := c.FunctionConfig.GetMeta()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err)
|
||||||
|
}
|
||||||
|
p, found := m.Annotations[kioutil.PathAnnotation]
|
||||||
|
if !found {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
functionDir := path.Clean(path.Dir(p))
|
||||||
|
|
||||||
|
if path.Base(functionDir) == functionsDirectoryName {
|
||||||
|
// the scope of functions in a directory called "functions" is 1 level higher
|
||||||
|
// this is similar to how the golang "internal" directory scoping works
|
||||||
|
functionDir = path.Dir(functionDir)
|
||||||
|
}
|
||||||
|
return functionDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// scope partitions the input nodes into 2 slices. The first slice contains only Resources
|
||||||
|
// which are scoped under dir, and the second slice contains the Resources which are not.
|
||||||
|
func (c *FunctionFilter) scope(dir string, nodes []*yaml.RNode) ([]*yaml.RNode, []*yaml.RNode, error) {
|
||||||
|
// scope container filtered Resources to Resources under that directory
|
||||||
|
var input, saved []*yaml.RNode
|
||||||
|
if c.GlobalScope {
|
||||||
|
return nodes, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// global function
|
||||||
|
if dir == "" || dir == "." {
|
||||||
|
return nodes, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// identify Resources read from directories under the function configuration
|
||||||
|
for i := range nodes {
|
||||||
|
m, err := nodes[i].GetMeta()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
p, found := m.Annotations[kioutil.PathAnnotation]
|
||||||
|
if !found {
|
||||||
|
// this Resource isn't scoped under the function -- don't know where it came from
|
||||||
|
// consider it out of scope
|
||||||
|
saved = append(saved, nodes[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceDir := path.Clean(path.Dir(p))
|
||||||
|
if path.Base(resourceDir) == functionsDirectoryName {
|
||||||
|
// Functions in the `functions` directory are scoped to
|
||||||
|
// themselves, and should see themselves as input
|
||||||
|
resourceDir = path.Dir(resourceDir)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(resourceDir, dir) {
|
||||||
|
// this Resource doesn't fall under the function scope if it
|
||||||
|
// isn't in a subdirectory of where the function lives
|
||||||
|
saved = append(saved, nodes[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// this input is scoped under the function
|
||||||
|
input = append(input, nodes[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return input, saved, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
in := &bytes.Buffer{}
|
||||||
|
out := &bytes.Buffer{}
|
||||||
|
|
||||||
|
// only process Resources scoped to this function, save the others
|
||||||
|
functionDir, err := c.getFunctionScope()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
input, saved, err := c.scope(functionDir, nodes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the input
|
||||||
|
err = kio.ByteWriter{
|
||||||
|
WrappingAPIVersion: kio.ResourceListAPIVersion,
|
||||||
|
WrappingKind: kio.ResourceListKind,
|
||||||
|
Writer: in,
|
||||||
|
KeepReaderAnnotations: true,
|
||||||
|
FunctionConfig: c.FunctionConfig}.Write(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// capture the command stdout for the return value
|
||||||
|
r := &kio.ByteReader{Reader: out}
|
||||||
|
|
||||||
|
// don't exit immediately if the function fails -- write out the validation
|
||||||
|
c.exit = c.Run(in, out)
|
||||||
|
|
||||||
|
output, err := r.Read()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.doResults(r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.exit != nil && !c.DeferFailure {
|
||||||
|
return append(output, saved...), c.exit
|
||||||
|
}
|
||||||
|
|
||||||
|
// annotate any generated Resources with a path and index if they don't already have one
|
||||||
|
if err := kioutil.DefaultPathAnnotation(functionDir, output); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// emit both the Resources output from the function, and the out-of-scope Resources
|
||||||
|
// which were not provided to the function
|
||||||
|
return append(output, saved...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *FunctionFilter) doResults(r *kio.ByteReader) error {
|
||||||
|
// Write the results to a file if configured to do so
|
||||||
|
if c.ResultsFile != "" && r.Results != nil {
|
||||||
|
results, err := r.Results.String()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(c.ResultsFile, []byte(results), 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.Results != nil {
|
||||||
|
c.results = r.Results
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
998
kyaml/fn/runtime/runtimeutil/runtimeutil_test.go
Normal file
998
kyaml/fn/runtime/runtimeutil/runtimeutil_test.go
Normal file
@@ -0,0 +1,998 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package runtimeutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testRun struct {
|
||||||
|
err error
|
||||||
|
expectedInput string
|
||||||
|
output string
|
||||||
|
t *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r testRun) run(reader io.Reader, writer io.Writer) error {
|
||||||
|
if r.expectedInput != "" {
|
||||||
|
input, err := ioutil.ReadAll(reader)
|
||||||
|
if !assert.NoError(r.t, err) {
|
||||||
|
r.t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify input matches expected
|
||||||
|
if !assert.Equal(r.t, r.expectedInput, string(input)) {
|
||||||
|
r.t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := writer.Write([]byte(r.output))
|
||||||
|
if !assert.NoError(r.t, err) {
|
||||||
|
r.t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFunctionFilter_Filter(t *testing.T) {
|
||||||
|
var tests = []struct {
|
||||||
|
run testRun
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
functionConfig string
|
||||||
|
expectedOutput []string
|
||||||
|
expectedError string
|
||||||
|
expectedSavedError string
|
||||||
|
expectedResults string
|
||||||
|
noMakeResultsFile bool
|
||||||
|
instance FunctionFilter
|
||||||
|
}{
|
||||||
|
// verify that resources emitted from the function have a file path defaulted
|
||||||
|
// if none already exists
|
||||||
|
{
|
||||||
|
name: "default_file_path_annotation",
|
||||||
|
run: testRun{
|
||||||
|
output: `
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// verify that resources emitted from the function do not have a file path defaulted
|
||||||
|
// if one already exists
|
||||||
|
{
|
||||||
|
name: "no_default_file_path_annotation",
|
||||||
|
run: testRun{
|
||||||
|
output: `
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// verify the FunctionFilter correctly writes the inputs and reads the outputs
|
||||||
|
// of Run
|
||||||
|
{
|
||||||
|
name: "write_read",
|
||||||
|
run: testRun{
|
||||||
|
output: `
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo.yaml'
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: configmap-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
input: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedOutput: []string{`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo.yaml'
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: configmap-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// verify that the results file is written
|
||||||
|
//
|
||||||
|
{
|
||||||
|
name: "write_results_file",
|
||||||
|
run: testRun{
|
||||||
|
output: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
results:
|
||||||
|
- apiVersion: config.k8s.io/v1alpha1
|
||||||
|
kind: ObjectError
|
||||||
|
name: "some-validator"
|
||||||
|
items:
|
||||||
|
- type: error
|
||||||
|
message: "some message"
|
||||||
|
resourceRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: foo
|
||||||
|
namespace: bar
|
||||||
|
file:
|
||||||
|
path: deploy.yaml
|
||||||
|
index: 0
|
||||||
|
field:
|
||||||
|
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||||
|
currentValue: "200"
|
||||||
|
suggestedValue: "2"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedOutput: []string{`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||||
|
`, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedResults: `
|
||||||
|
- apiVersion: config.k8s.io/v1alpha1
|
||||||
|
kind: ObjectError
|
||||||
|
name: "some-validator"
|
||||||
|
items:
|
||||||
|
- type: error
|
||||||
|
message: "some message"
|
||||||
|
resourceRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: foo
|
||||||
|
namespace: bar
|
||||||
|
file:
|
||||||
|
path: deploy.yaml
|
||||||
|
index: 0
|
||||||
|
field:
|
||||||
|
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||||
|
currentValue: "200"
|
||||||
|
suggestedValue: "2"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// verify that the results file is written for functions that exist non-0
|
||||||
|
// and the FunctionFilter returns the error
|
||||||
|
{
|
||||||
|
name: "write_results_file_function_exit_non_0",
|
||||||
|
expectedError: "failed",
|
||||||
|
run: testRun{
|
||||||
|
err: fmt.Errorf("failed"),
|
||||||
|
output: `
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
results:
|
||||||
|
- apiVersion: config.k8s.io/v1alpha1
|
||||||
|
kind: ObjectError
|
||||||
|
name: "some-validator"
|
||||||
|
items:
|
||||||
|
- type: error
|
||||||
|
message: "some message"
|
||||||
|
resourceRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: foo
|
||||||
|
namespace: bar
|
||||||
|
file:
|
||||||
|
path: deploy.yaml
|
||||||
|
index: 0
|
||||||
|
field:
|
||||||
|
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||||
|
currentValue: "200"
|
||||||
|
suggestedValue: "2"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||||
|
`, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedResults: `
|
||||||
|
- apiVersion: config.k8s.io/v1alpha1
|
||||||
|
kind: ObjectError
|
||||||
|
name: "some-validator"
|
||||||
|
items:
|
||||||
|
- type: error
|
||||||
|
message: "some message"
|
||||||
|
resourceRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: foo
|
||||||
|
namespace: bar
|
||||||
|
file:
|
||||||
|
path: deploy.yaml
|
||||||
|
index: 0
|
||||||
|
field:
|
||||||
|
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||||
|
currentValue: "200"
|
||||||
|
suggestedValue: "2"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// verify that if deferFailure is set, the results file is written and the
|
||||||
|
// exit error is saved, but the FunctionFilter does not return an error.
|
||||||
|
{
|
||||||
|
name: "write_results_defer_failure",
|
||||||
|
instance: FunctionFilter{DeferFailure: true},
|
||||||
|
expectedSavedError: "failed",
|
||||||
|
run: testRun{
|
||||||
|
err: fmt.Errorf("failed"),
|
||||||
|
output: `
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
results:
|
||||||
|
- apiVersion: config.k8s.io/v1alpha1
|
||||||
|
kind: ObjectError
|
||||||
|
name: "some-validator"
|
||||||
|
items:
|
||||||
|
- type: error
|
||||||
|
message: "some message"
|
||||||
|
resourceRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: foo
|
||||||
|
namespace: bar
|
||||||
|
file:
|
||||||
|
path: deploy.yaml
|
||||||
|
index: 0
|
||||||
|
field:
|
||||||
|
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||||
|
currentValue: "200"
|
||||||
|
suggestedValue: "2"`,
|
||||||
|
},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||||
|
`, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedResults: `
|
||||||
|
- apiVersion: config.k8s.io/v1alpha1
|
||||||
|
kind: ObjectError
|
||||||
|
name: "some-validator"
|
||||||
|
items:
|
||||||
|
- type: error
|
||||||
|
message: "some message"
|
||||||
|
resourceRef:
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
name: foo
|
||||||
|
namespace: bar
|
||||||
|
file:
|
||||||
|
path: deploy.yaml
|
||||||
|
index: 0
|
||||||
|
field:
|
||||||
|
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||||
|
currentValue: "200"
|
||||||
|
suggestedValue: "2"`,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "write_results_bad_results_file",
|
||||||
|
expectedError: "open /not/real/file: no such file or directory",
|
||||||
|
noMakeResultsFile: true,
|
||||||
|
run: testRun{
|
||||||
|
output: `
|
||||||
|
apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
results:
|
||||||
|
- apiVersion: config.k8s.io/v1alpha1
|
||||||
|
name: "some-validator"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||||
|
`, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
// these aren't written, expect an error
|
||||||
|
expectedResults: `
|
||||||
|
- apiVersion: config.k8s.io/v1alpha1
|
||||||
|
name: "some-validator"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// verify the function only sees resources scoped to it based on the directory
|
||||||
|
// containing the functionConfig and the directory containing each resource.
|
||||||
|
// resources not provided to the function should still appear in the FunctionFilter
|
||||||
|
// output
|
||||||
|
{
|
||||||
|
name: "scope_resources_by_directory",
|
||||||
|
run: testRun{
|
||||||
|
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
output: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
new: annotation
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
functionConfig: `
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
input: []string{
|
||||||
|
// this should not be in scope
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
`,
|
||||||
|
// this should be in scope
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
`},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
new: annotation
|
||||||
|
`, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// verify functions without file path annotation are not scoped to functions
|
||||||
|
{
|
||||||
|
name: "scope_resources_by_directory_resources_missing_path",
|
||||||
|
run: testRun{
|
||||||
|
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
output: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
new: annotation
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
functionConfig: `
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
input: []string{
|
||||||
|
// this should not be in scope
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
`,
|
||||||
|
// this should be in scope
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
`},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
new: annotation
|
||||||
|
`, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// verify the functions can see all resources if global scope is set
|
||||||
|
{
|
||||||
|
name: "scope_resources_global",
|
||||||
|
instance: FunctionFilter{GlobalScope: true},
|
||||||
|
run: testRun{
|
||||||
|
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
output: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
new: annotation
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
functionConfig: `
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
input: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
`},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
`, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
new: annotation
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "scope_no_resources",
|
||||||
|
run: testRun{
|
||||||
|
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items: []
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
output: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items: []
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
functionConfig: `
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar.yaml'
|
||||||
|
`,
|
||||||
|
input: []string{
|
||||||
|
// these should not be in scope
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
`, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'biz/bar/s.yaml'
|
||||||
|
`},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
`, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'biz/bar/s.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "scope_functions_dir",
|
||||||
|
run: testRun{
|
||||||
|
expectedInput: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/functions/bar.yaml'
|
||||||
|
`,
|
||||||
|
output: `apiVersion: config.kubernetes.io/v1alpha1
|
||||||
|
kind: ResourceList
|
||||||
|
items:
|
||||||
|
- apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
new: annotation
|
||||||
|
functionConfig:
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/functions/bar.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
functionConfig: `
|
||||||
|
apiVersion: example.com/v1
|
||||||
|
kind: Example
|
||||||
|
metadata:
|
||||||
|
name: foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/functions/bar.yaml'
|
||||||
|
`,
|
||||||
|
input: []string{
|
||||||
|
// this should not be in scope
|
||||||
|
`
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
`,
|
||||||
|
// this should be in scope
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
`},
|
||||||
|
expectedOutput: []string{
|
||||||
|
`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: service-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'foo/bar/s.yaml'
|
||||||
|
new: annotation
|
||||||
|
`, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: deployment-foo
|
||||||
|
annotations:
|
||||||
|
config.kubernetes.io/path: 'baz/bar/d.yaml'
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range tests {
|
||||||
|
tt := tests[i]
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// results file setup
|
||||||
|
if len(tt.expectedResults) > 0 && !tt.noMakeResultsFile {
|
||||||
|
// expect result files to be written -- create a directory for them
|
||||||
|
f, err := ioutil.TempFile("", "test-kyaml-*.yaml")
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(f.Name())
|
||||||
|
tt.instance.ResultsFile = f.Name()
|
||||||
|
} else if len(tt.expectedResults) > 0 {
|
||||||
|
// failure case for writing to bad results location
|
||||||
|
tt.instance.ResultsFile = "/not/real/file"
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the inputs for the FunctionFilter
|
||||||
|
var inputs []*yaml.RNode
|
||||||
|
for i := range tt.input {
|
||||||
|
node, err := yaml.Parse(tt.input[i])
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
inputs = append(inputs, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// run the FunctionFilter
|
||||||
|
tt.run.t = t
|
||||||
|
tt.instance.Run = tt.run.run
|
||||||
|
if tt.functionConfig != "" {
|
||||||
|
fc, err := yaml.Parse(tt.functionConfig)
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
tt.instance.FunctionConfig = fc
|
||||||
|
}
|
||||||
|
output, err := tt.instance.Filter(inputs)
|
||||||
|
if tt.expectedError != "" {
|
||||||
|
if !assert.EqualError(t, err, tt.expectedError) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for saved error
|
||||||
|
if tt.expectedSavedError != "" {
|
||||||
|
if !assert.EqualError(t, tt.instance.exit, tt.expectedSavedError) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify function output
|
||||||
|
var actual []string
|
||||||
|
for i := range output {
|
||||||
|
s, err := output[i].String()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
actual = append(actual, strings.TrimSpace(s))
|
||||||
|
}
|
||||||
|
var expected []string
|
||||||
|
for i := range tt.expectedOutput {
|
||||||
|
expected = append(expected, strings.TrimSpace(tt.expectedOutput[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !assert.Equal(t, expected, actual) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify results files
|
||||||
|
if len(tt.instance.ResultsFile) > 0 {
|
||||||
|
tt.expectedResults = strings.TrimSpace(tt.expectedResults)
|
||||||
|
|
||||||
|
results, err := tt.instance.results.String()
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.Equal(t, tt.expectedResults, strings.TrimSpace(results)) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadFile(tt.instance.ResultsFile)
|
||||||
|
writtenResults := strings.TrimSpace(string(b))
|
||||||
|
if !assert.NoError(t, err) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if !assert.Equal(t, tt.expectedResults, writtenResults) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
5
kyaml/fn/runtime/runtimeutil/types.go
Normal file
5
kyaml/fn/runtime/runtimeutil/types.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package runtimeutil
|
||||||
|
|
||||||
|
type DeferFailureFunction interface {
|
||||||
|
GetExit() error
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user