From a6987107bd8453b3ffbc65084e35a77a428cd022 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kalynovskyi Date: Wed, 11 Nov 2020 19:20:28 -0600 Subject: [PATCH] Allow to perform phase render using document entrypoint In current setup, we rely on executor to perform render operation, however there are many usecases, when we want to have a simple `kustomize build` performed against document entrypoint in phase. Now this will be a default behaviour and if a user would like executor to perform rendering to actually understand what it's doing, he will be able to specify --executor boolean flag. Relates-To: #403 Change-Id: I181830f9814ff48a19ba0a1284e89900187bc7d8 --- cmd/phase/render.go | 14 ++++++++--- .../render-with-help.golden | 1 + docs/source/cli/airshipctl_phase_render.md | 1 + pkg/phase/client.go | 23 ++++++++++++++++--- pkg/phase/ifc/phase.go | 2 +- pkg/phase/render.go | 13 ++++++----- pkg/phase/render_test.go | 21 ++++++++++++----- 7 files changed, 56 insertions(+), 19 deletions(-) diff --git a/cmd/phase/render.go b/cmd/phase/render.go index 6c3c83810..ad7451753 100644 --- a/cmd/phase/render.go +++ b/cmd/phase/render.go @@ -35,14 +35,14 @@ airshipctl phase render initinfra -l app=helm,service=tiller -k Deployment // NewRenderCommand create a new command for document rendering func NewRenderCommand(cfgFactory config.Factory) *cobra.Command { - filterOptions := &phase.FilterOptions{} + filterOptions := &phase.RenderCommand{} renderCmd := &cobra.Command{ Use: "render PHASE_NAME", Short: "Render phase documents from model", Example: renderExample, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return filterOptions.Render(cfgFactory, args[0], cmd.OutOrStdout()) + return filterOptions.RunE(cfgFactory, args[0], cmd.OutOrStdout()) }, } @@ -51,7 +51,7 @@ func NewRenderCommand(cfgFactory config.Factory) *cobra.Command { } // addRenderFlags adds flags for document render sub-command -func addRenderFlags(filterOptions *phase.FilterOptions, cmd *cobra.Command) { +func addRenderFlags(filterOptions *phase.RenderCommand, cmd *cobra.Command) { flags := cmd.Flags() flags.StringVarP( @@ -81,4 +81,12 @@ func addRenderFlags(filterOptions *phase.FilterOptions, cmd *cobra.Command) { "k", "", "filter documents by Kinds") + flags.BoolVarP( + &filterOptions.Executor, + "executor", + "e", + false, + "if set to true rendering will be performed by executor "+ + "otherwise phase entrypoint will be rendered by kustomize, if entrypoint is not specified "+ + "error will be returned") } diff --git a/cmd/phase/testdata/TestRenderGoldenOutput/render-with-help.golden b/cmd/phase/testdata/TestRenderGoldenOutput/render-with-help.golden index de23dc9a4..c7d0dec26 100644 --- a/cmd/phase/testdata/TestRenderGoldenOutput/render-with-help.golden +++ b/cmd/phase/testdata/TestRenderGoldenOutput/render-with-help.golden @@ -17,6 +17,7 @@ airshipctl phase render initinfra -l app=helm,service=tiller -k Deployment Flags: -a, --annotation string filter documents by Annotations -g, --apiversion string filter documents by API version + -e, --executor if set to true rendering will be performed by executor otherwise phase entrypoint will be rendered by kustomize, if entrypoint is not specified error will be returned -h, --help help for render -k, --kind string filter documents by Kinds -l, --label string filter documents by Labels diff --git a/docs/source/cli/airshipctl_phase_render.md b/docs/source/cli/airshipctl_phase_render.md index 4300bfa18..a2afb8867 100644 --- a/docs/source/cli/airshipctl_phase_render.md +++ b/docs/source/cli/airshipctl_phase_render.md @@ -29,6 +29,7 @@ airshipctl phase render initinfra -l app=helm,service=tiller -k Deployment ``` -a, --annotation string filter documents by Annotations -g, --apiversion string filter documents by API version + -e, --executor if set to true rendering will be performed by executor otherwise phase entrypoint will be rendered by kustomize, if entrypoint is not specified error will be returned -h, --help help for render -k, --kind string filter documents by Kinds -l, --label string filter documents by Labels diff --git a/pkg/phase/client.go b/pkg/phase/client.go index cc7656896..4eb8968fe 100644 --- a/pkg/phase/client.go +++ b/pkg/phase/client.go @@ -134,13 +134,30 @@ func (p *phase) Validate() error { } // Render executor documents -func (p *phase) Render(w io.Writer, options ifc.RenderOptions) error { - executor, err := p.Executor() +func (p *phase) Render(w io.Writer, executorRender bool, options ifc.RenderOptions) error { + if executorRender { + executor, err := p.Executor() + if err != nil { + return err + } + return executor.Render(w, options) + } + + root, err := p.DocumentRoot() if err != nil { return err } - return executor.Render(w, options) + bundle, err := document.NewBundleByPath(root) + if err != nil { + return err + } + + rendered, err := bundle.SelectBundle(options.FilterSelector) + if err != nil { + return err + } + return rendered.Write(w) } // DocumentRoot root that holds all the documents associated with the phase diff --git a/pkg/phase/ifc/phase.go b/pkg/phase/ifc/phase.go index 2cdb07ea4..4429d0e2a 100644 --- a/pkg/phase/ifc/phase.go +++ b/pkg/phase/ifc/phase.go @@ -28,7 +28,7 @@ type Phase interface { DocumentRoot() (string, error) Details() (string, error) Executor() (Executor, error) - Render(io.Writer, RenderOptions) error + Render(io.Writer, bool, RenderOptions) error } // ID uniquely identifies the phase diff --git a/pkg/phase/render.go b/pkg/phase/render.go index ccc24cac1..bdcf37f9b 100644 --- a/pkg/phase/render.go +++ b/pkg/phase/render.go @@ -23,8 +23,8 @@ import ( "opendev.org/airship/airshipctl/pkg/phase/ifc" ) -// FilterOptions holds filters for selector -type FilterOptions struct { +// RenderCommand holds filters for selector +type RenderCommand struct { // Label filters documents by label string Label string // Annotation filters documents by annotation string @@ -33,10 +33,12 @@ type FilterOptions struct { APIVersion string // Kind filters documents by document kind Kind string + // Executor identifies if executor should perform rendering + Executor bool } -// Render prints out filtered documents -func (fo *FilterOptions) Render(cfgFactory config.Factory, phaseName string, out io.Writer) error { +// RunE prints out filtered documents +func (fo *RenderCommand) RunE(cfgFactory config.Factory, phaseName string, out io.Writer) error { cfg, err := cfgFactory() if err != nil { return err @@ -62,6 +64,5 @@ func (fo *FilterOptions) Render(cfgFactory config.Factory, phaseName string, out } sel := document.NewSelector().ByLabel(fo.Label).ByAnnotation(fo.Annotation).ByGvk(group, version, fo.Kind) - - return phase.Render(out, ifc.RenderOptions{FilterSelector: sel}) + return phase.Render(out, fo.Executor, ifc.RenderOptions{FilterSelector: sel}) } diff --git a/pkg/phase/render_test.go b/pkg/phase/render_test.go index 161cb817f..018c86adb 100644 --- a/pkg/phase/render_test.go +++ b/pkg/phase/render_test.go @@ -43,19 +43,19 @@ func TestRender(t *testing.T) { fixturePath := "phase" tests := []struct { name string - settings *phase.FilterOptions + settings *phase.RenderCommand expResFile string expErr error }{ { name: "No Filters", - settings: &phase.FilterOptions{}, + settings: &phase.RenderCommand{}, expResFile: "noFilter.yaml", expErr: nil, }, { name: "All Filters", - settings: &phase.FilterOptions{ + settings: &phase.RenderCommand{ Label: "airshipit.org/deploy-k8s=false", Annotation: "airshipit.org/clustertype=ephemeral", APIVersion: "metal3.io/v1alpha1", @@ -66,7 +66,7 @@ func TestRender(t *testing.T) { }, { name: "Multiple Labels", - settings: &phase.FilterOptions{ + settings: &phase.RenderCommand{ Label: "airshipit.org/deploy-k8s=false, airshipit.org/ephemeral-node=true", }, expResFile: "multiLabels.yaml", @@ -74,12 +74,21 @@ func TestRender(t *testing.T) { }, { name: "Malformed Label", - settings: &phase.FilterOptions{ + settings: &phase.RenderCommand{ Label: "app=(", }, expResFile: "", expErr: fmt.Errorf("unable to parse requirement: found '(', expected: identifier"), }, + { + name: "Malformed Label", + settings: &phase.RenderCommand{ + Label: "app=(", + Executor: true, + }, + expResFile: "", + expErr: fmt.Errorf("unable to parse requirement: found '(', expected: identifier"), + }, } for _, tt := range tests { @@ -92,7 +101,7 @@ func TestRender(t *testing.T) { require.NoError(t, err) } out := &bytes.Buffer{} - err = tt.settings.Render(func() (*config.Config, error) { + err = tt.settings.RunE(func() (*config.Config, error) { return rs, nil }, fixturePath, out) assert.Equal(t, tt.expErr, err)