diff --git a/kyaml/fn/framework/framework.go b/kyaml/fn/framework/framework.go index 3af4d3854..f5c6b36b1 100644 --- a/kyaml/fn/framework/framework.go +++ b/kyaml/fn/framework/framework.go @@ -95,11 +95,21 @@ type ResourceList struct { // NoPrintError if set will prevent the error from being printed NoPrintError bool + + Command *cobra.Command } // Read reads the ResourceList func (r *ResourceList) Read() error { + var in io.Reader = os.Stdin + var out io.Writer = os.Stdout + if r.Command != nil { + in = r.Command.InOrStdin() + out = r.Command.OutOrStdout() + } + // parse the inputs from the args + var readStdinStandalone bool if len(r.Args) > 0 && !r.DisableStandalone { // write the files as input var buf bytes.Buffer @@ -109,6 +119,12 @@ func (r *ResourceList) Read() error { if i == 0 { continue } + if r.Args[i] == "-" { + // Read stdin separately + readStdinStandalone = true + continue + } + b, err := ioutil.ReadFile(r.Args[i]) if err != nil { return errors.WrapPrefixf(err, "unable to read input file %s", r.Args[i]) @@ -120,10 +136,10 @@ func (r *ResourceList) Read() error { } if r.Reader == nil { - r.Reader = os.Stdin + r.Reader = in } if r.Writer == nil { - r.Writer = os.Stdout + r.Writer = out } r.rw = &kio.ByteReadWriter{ Reader: r.Reader, @@ -157,6 +173,16 @@ func (r *ResourceList) Read() error { return errors.Wrap(err) } + if readStdinStandalone { + br := kio.ByteReader{Reader: in} + items, err := br.Read() + if err != nil { + return errors.Wrap(err) + } + // stdin always comes first so files are patches + r.Items = append(items, r.Items...) + } + // parse the functionConfig return func() error { if r.rw.FunctionConfig == nil { @@ -223,8 +249,9 @@ func (r *ResourceList) Write() error { // a Dockerfile to build the function into a container image // // go run main.go gen DIR/ -func Command(resourceList *ResourceList, function Function) cobra.Command { +func Command(resourceList *ResourceList, function Function) *cobra.Command { cmd := cobra.Command{} + resourceList.Command = &cmd AddGenerateDockerfile(&cmd) var printStack bool cmd.RunE = func(cmd *cobra.Command, args []string) error { @@ -242,7 +269,7 @@ func Command(resourceList *ResourceList, function Function) cobra.Command { cmd.Args = cobra.MinimumNArgs(0) cmd.SilenceErrors = true cmd.SilenceUsage = true - return cmd + return &cmd } // TemplateCommand provides a cobra command to invoke a template @@ -310,7 +337,7 @@ func (tc TemplateCommand) doTemplate(t *template.Template, rl *ResourceList) err } // GetCommand returns a new cobra command -func (tc TemplateCommand) GetCommand() cobra.Command { +func (tc TemplateCommand) GetCommand() *cobra.Command { rl := ResourceList{ FunctionConfig: tc.API, NoPrintError: true, diff --git a/kyaml/fn/framework/framework_test.go b/kyaml/fn/framework/framework_test.go index 85440f4dc..2e3106c87 100644 --- a/kyaml/fn/framework/framework_test.go +++ b/kyaml/fn/framework/framework_test.go @@ -4,13 +4,16 @@ package framework_test import ( + "bytes" "io/ioutil" "os" "path/filepath" + "strings" "testing" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "sigs.k8s.io/kustomize/kyaml/fn/framework" "sigs.k8s.io/kustomize/kyaml/fn/framework/frameworktestutil" "sigs.k8s.io/kustomize/kyaml/testutil" @@ -66,7 +69,7 @@ func TestCommand_standalone(t *testing.T) { var config api resourceList := &framework.ResourceList{FunctionConfig: &config} - cmdFn := func() cobra.Command { + cmdFn := func() *cobra.Command { return framework.Command(resourceList, func() error { resourceList.Items = append(resourceList.Items, yaml.MustParse(` apiVersion: apps/v1 @@ -90,3 +93,72 @@ metadata: frameworktestutil.ResultsChecker{Command: cmdFn}.Assert(t) } + +func TestCommand_standalonestdin(t *testing.T) { + // TODO: make this test pass on windows -- currently failure seems spurious + testutil.SkipWindows(t) + + type api = struct { + A string `json:"a" yaml:"a"` + } + var config api + + resourceList := &framework.ResourceList{FunctionConfig: &config} + cmd := framework.Command(resourceList, func() error { + resourceList.Items = append(resourceList.Items, yaml.MustParse(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bar2 + namespace: default + annotations: + foo: bar2 +`)) + for i := range resourceList.Items { + err := resourceList.Items[i].PipeE(yaml.SetAnnotation("a", config.A)) + if err != nil { + return err + } + } + + return nil + }) + cmd.SetIn(bytes.NewBufferString(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bar1 + namespace: default + annotations: + foo: bar1 +spec: + replicas: 1 +`)) + var out bytes.Buffer + cmd.SetOut(&out) + cmd.SetArgs([]string{filepath.Join("testdata", "command", "config.yaml"), "-"}) + + require.NoError(t, cmd.Execute()) + + require.Equal(t, strings.TrimSpace(` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bar1 + namespace: default + annotations: + foo: bar1 + a: 'b' +spec: + replicas: 1 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bar2 + namespace: default + annotations: + foo: bar2 + a: 'b' +`), strings.TrimSpace(out.String())) +} diff --git a/kyaml/fn/framework/frameworktestutil/frameworktestutil.go b/kyaml/fn/framework/frameworktestutil/frameworktestutil.go index 7e22e19d0..3bd41bf17 100644 --- a/kyaml/fn/framework/frameworktestutil/frameworktestutil.go +++ b/kyaml/fn/framework/frameworktestutil/frameworktestutil.go @@ -46,7 +46,7 @@ type ResultsChecker struct { ExpectedErrorFilename string // Command provides the function to run. - Command func() cobra.Command + Command func() *cobra.Command } // Assert asserts the results for functions diff --git a/kyaml/fn/framework/patch_test.go b/kyaml/fn/framework/patch_test.go index 6860edfc5..3af567889 100644 --- a/kyaml/fn/framework/patch_test.go +++ b/kyaml/fn/framework/patch_test.go @@ -21,7 +21,7 @@ func TestPatchTemplate(t *testing.T) { // TODO: make this test pass on windows -- current failure seems spurious testutil.SkipWindows(t) - cmdFn := func() cobra.Command { + cmdFn := func() *cobra.Command { type api struct { Selector framework.Selector `json:"selector" yaml:"selector"` A string `json:"a" yaml:"a"`