Merge pull request #10514 from tanguofu/main

fea(#10511): add --post-renderer-args to support assign args to PostR…
pull/10648/head
Martin Hickey 4 years ago committed by GitHub
commit df7c1c39bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -36,8 +36,11 @@ import (
"helm.sh/helm/v3/pkg/repo" "helm.sh/helm/v3/pkg/repo"
) )
const outputFlag = "output" const (
const postRenderFlag = "post-renderer" outputFlag = "output"
postRenderFlag = "post-renderer"
postRenderArgsFlag = "post-renderer-args"
)
func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) { func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)") f.StringSliceVarP(&v.ValueFiles, "values", "f", []string{}, "specify values in a YAML file or a URL (can specify multiple)")
@ -110,31 +113,83 @@ func (o *outputValue) Set(s string) error {
} }
func bindPostRenderFlag(cmd *cobra.Command, varRef *postrender.PostRenderer) { func bindPostRenderFlag(cmd *cobra.Command, varRef *postrender.PostRenderer) {
cmd.Flags().Var(&postRenderer{varRef}, postRenderFlag, "the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path") p := &postRendererOptions{varRef, "", []string{}}
cmd.Flags().Var(&postRendererString{p}, postRenderFlag, "the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path")
cmd.Flags().Var(&postRendererArgsSlice{p}, postRenderArgsFlag, "an argument to the post-renderer (can specify multiple)")
} }
type postRenderer struct { type postRendererOptions struct {
renderer *postrender.PostRenderer renderer *postrender.PostRenderer
binaryPath string
args []string
}
type postRendererString struct {
options *postRendererOptions
} }
func (p postRenderer) String() string { func (p *postRendererString) String() string {
return "exec" return p.options.binaryPath
} }
func (p postRenderer) Type() string { func (p *postRendererString) Type() string {
return "postrenderer" return "postRendererString"
} }
func (p postRenderer) Set(s string) error { func (p *postRendererString) Set(val string) error {
if s == "" { if val == "" {
return nil return nil
} }
pr, err := postrender.NewExec(s) p.options.binaryPath = val
pr, err := postrender.NewExec(p.options.binaryPath, p.options.args...)
if err != nil { if err != nil {
return err return err
} }
*p.renderer = pr *p.options.renderer = pr
return nil
}
type postRendererArgsSlice struct {
options *postRendererOptions
}
func (p *postRendererArgsSlice) String() string {
return "[" + strings.Join(p.options.args, ",") + "]"
}
func (p *postRendererArgsSlice) Type() string {
return "postRendererArgsSlice"
}
func (p *postRendererArgsSlice) Set(val string) error {
// a post-renderer defined by a user may accept empty arguments
p.options.args = append(p.options.args, val)
if p.options.binaryPath == "" {
return nil return nil
}
// overwrite if already create PostRenderer by `post-renderer` flags
pr, err := postrender.NewExec(p.options.binaryPath, p.options.args...)
if err != nil {
return err
}
*p.options.renderer = pr
return nil
}
func (p *postRendererArgsSlice) Append(val string) error {
p.options.args = append(p.options.args, val)
return nil
}
func (p *postRendererArgsSlice) Replace(val []string) error {
p.options.args = val
return nil
}
func (p *postRendererArgsSlice) GetSlice() []string {
return p.options.args
} }
func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellCompDirective) { func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellCompDirective) {

@ -27,23 +27,24 @@ import (
type execRender struct { type execRender struct {
binaryPath string binaryPath string
args []string
} }
// NewExec returns a PostRenderer implementation that calls the provided binary. // NewExec returns a PostRenderer implementation that calls the provided binary.
// It returns an error if the binary cannot be found. If the path does not // It returns an error if the binary cannot be found. If the path does not
// contain any separators, it will search in $PATH, otherwise it will resolve // contain any separators, it will search in $PATH, otherwise it will resolve
// any relative paths to a fully qualified path // any relative paths to a fully qualified path
func NewExec(binaryPath string) (PostRenderer, error) { func NewExec(binaryPath string, args ...string) (PostRenderer, error) {
fullPath, err := getFullPath(binaryPath) fullPath, err := getFullPath(binaryPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &execRender{fullPath}, nil return &execRender{fullPath, args}, nil
} }
// Run the configured binary for the post render // Run the configured binary for the post render
func (p *execRender) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { func (p *execRender) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) {
cmd := exec.Command(p.binaryPath) cmd := exec.Command(p.binaryPath, p.args...)
stdin, err := cmd.StdinPipe() stdin, err := cmd.StdinPipe()
if err != nil { if err != nil {
return nil, err return nil, err

@ -31,7 +31,11 @@ import (
) )
const testingScript = `#!/bin/sh const testingScript = `#!/bin/sh
if [ $# -eq 0 ]; then
sed s/FOOTEST/BARTEST/g <&0 sed s/FOOTEST/BARTEST/g <&0
else
sed s/FOOTEST/"$*"/g <&0
fi
` `
func TestGetFullPath(t *testing.T) { func TestGetFullPath(t *testing.T) {
@ -124,6 +128,40 @@ func TestExecRun(t *testing.T) {
is.Contains(output.String(), "BARTEST") is.Contains(output.String(), "BARTEST")
} }
func TestNewExecWithOneArgsRun(t *testing.T) {
if runtime.GOOS == "windows" {
// the actual Run test uses a basic sed example, so skip this test on windows
t.Skip("skipping on windows")
}
is := assert.New(t)
testpath, cleanup := setupTestingScript(t)
defer cleanup()
renderer, err := NewExec(testpath, "ARG1")
require.NoError(t, err)
output, err := renderer.Run(bytes.NewBufferString("FOOTEST"))
is.NoError(err)
is.Contains(output.String(), "ARG1")
}
func TestNewExecWithTwoArgsRun(t *testing.T) {
if runtime.GOOS == "windows" {
// the actual Run test uses a basic sed example, so skip this test on windows
t.Skip("skipping on windows")
}
is := assert.New(t)
testpath, cleanup := setupTestingScript(t)
defer cleanup()
renderer, err := NewExec(testpath, "ARG1", "ARG2")
require.NoError(t, err)
output, err := renderer.Run(bytes.NewBufferString("FOOTEST"))
is.NoError(err)
is.Contains(output.String(), "ARG1 ARG2")
}
func setupTestingScript(t *testing.T) (filepath string, cleanup func()) { func setupTestingScript(t *testing.T) (filepath string, cleanup func()) {
t.Helper() t.Helper()

Loading…
Cancel
Save