diff --git a/cmd/config/internal/commands/e2e/e2e_test.go b/cmd/config/internal/commands/e2e/e2e_test.go index a324eea87..747ac28f4 100644 --- a/cmd/config/internal/commands/e2e/e2e_test.go +++ b/cmd/config/internal/commands/e2e/e2e_test.go @@ -563,6 +563,90 @@ metadata: "deployment.yaml": ` apiVersion: apps/v1 kind: Deployment +metadata: + name: foo + annotations: + a-bool-value: true + a-int-value: 2 + a-string-value: a +`, + } + }, + }, + + { + name: "starlark_function_url", + args: func(d string) []string { + return []string{ + "--enable-star", "--star-url", "https://storage.googleapis.com/kustomize-starlark-functions/annotate.star", + "--star-name", "annotate", + "--", "stringValue=a", "intValue=2", "boolValue=true", + } + }, + files: func(d string) map[string]string { + return map[string]string{ + "deployment.yaml": ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: foo +`, + } + }, + expectedFiles: func(d string) map[string]string { + return map[string]string{ + "deployment.yaml": ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: foo + annotations: + a-bool-value: true + a-int-value: 2 + a-string-value: a +`, + } + }, + }, + + { + name: "starlark_function_url_config", + args: func(d string) []string { + return []string{"--enable-star"} + }, + files: func(d string) map[string]string { + return map[string]string{ + "config.yaml": ` +apiVersion: example.com/v1alpha1 +kind: Input +metadata: + name: foo + annotations: + a-bool-value: true + a-int-value: 2 + a-string-value: a + config.kubernetes.io/function: | + starlark: + url: https://storage.googleapis.com/kustomize-starlark-functions/annotate.star + name: fn +data: + boolValue: true + intValue: 2 + stringValue: a +`, + "deployment.yaml": ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: foo +`, + } + }, + expectedFiles: func(d string) map[string]string { + return map[string]string{ + "deployment.yaml": ` +apiVersion: apps/v1 +kind: Deployment metadata: name: foo annotations: @@ -607,7 +691,7 @@ metadata: err = cmd.Run() if tt.expectedErr != "" { - if !assert.Contains(t, stdErr.String(), tt.expectedErr) { + if !assert.Contains(t, stdErr.String(), tt.expectedErr, stdErr.String()) { t.FailNow() } return @@ -618,10 +702,10 @@ metadata: for path, data := range tt.expectedFiles(binDir) { b, err := ioutil.ReadFile(path) - if !assert.NoError(t, err) { + if !assert.NoError(t, err, stdErr.String()) { t.FailNow() } - if !assert.Equal(t, strings.TrimSpace(data), strings.TrimSpace(string(b))) { + if !assert.Equal(t, strings.TrimSpace(data), strings.TrimSpace(string(b)), stdErr.String()) { t.FailNow() } } diff --git a/cmd/config/internal/commands/e2e/starlark/annotate.star b/cmd/config/internal/commands/e2e/starlark/annotate.star new file mode 100644 index 000000000..660fadb91 --- /dev/null +++ b/cmd/config/internal/commands/e2e/starlark/annotate.star @@ -0,0 +1,7 @@ +def run(r, fc): + for resource in r: + resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"] + resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"] + resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"] + +run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]) diff --git a/cmd/config/internal/commands/run-fns.go b/cmd/config/internal/commands/run-fns.go index 0a2734e55..7e3856b63 100644 --- a/cmd/config/internal/commands/run-fns.go +++ b/cmd/config/internal/commands/run-fns.go @@ -50,6 +50,8 @@ func GetRunFnRunner(name string) *RunFnRunner { &r.EnableStar, "enable-star", false, "enable support for starlark functions. (Alpha)") r.Command.Flags().StringVar( &r.StarPath, "star-path", "", "run a starlark script as a function. (Alpha)") + r.Command.Flags().StringVar( + &r.StarURL, "star-url", "", "run a starlark script as a function. (Alpha)") r.Command.Flags().StringVar( &r.StarName, "star-name", "", "name of starlark program. (Alpha)") @@ -80,6 +82,7 @@ type RunFnRunner struct { Image string EnableStar bool StarPath string + StarURL string StarName string EnableExec bool ExecPath string @@ -98,7 +101,8 @@ func (r *RunFnRunner) runE(c *cobra.Command, args []string) error { // Functions to run. func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []string) ( []*yaml.RNode, error) { - if r.Image == "" && r.StarPath == "" && r.ExecPath == "" { + + if r.Image == "" && r.StarPath == "" && r.ExecPath == "" && r.StarURL == "" { return nil, nil } @@ -126,26 +130,36 @@ func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems [] return nil, err } } - } else if r.EnableStar && r.StarPath != "" { + } else if r.EnableStar && (r.StarPath != "" || r.StarURL != "") { // create the function spec to set as an annotation fn, err = yaml.Parse(`starlark: {}`) if err != nil { return nil, err } - err = fn.PipeE( - yaml.Lookup("starlark"), - yaml.SetField("path", yaml.NewScalarRNode(r.StarPath))) - if err != nil { - return nil, err + if r.StarPath != "" { + err = fn.PipeE( + yaml.Lookup("starlark"), + yaml.SetField("path", yaml.NewScalarRNode(r.StarPath))) + if err != nil { + return nil, err + } + } + if r.StarURL != "" { + err = fn.PipeE( + yaml.Lookup("starlark"), + yaml.SetField("url", yaml.NewScalarRNode(r.StarURL))) + if err != nil { + return nil, err + } } - err = fn.PipeE( yaml.Lookup("starlark"), yaml.SetField("name", yaml.NewScalarRNode(r.StarName))) if err != nil { return nil, err } + } else if r.EnableExec && r.ExecPath != "" { // create the function spec to set as an annotation fn, err = yaml.Parse(`exec: {}`) @@ -231,8 +245,8 @@ func toStorageMounts(mounts []string) []runtimeutil.StorageMount { } func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error { - if !r.EnableStar && r.StarPath != "" { - return errors.Errorf("must specify --enable-star with --star-path") + if !r.EnableStar && (r.StarPath != "" || r.StarURL != "") { + return errors.Errorf("must specify --enable-star with --star-path and --star-url") } if !r.EnableExec && r.ExecPath != "" { @@ -240,7 +254,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error { } if c.ArgsLenAtDash() >= 0 && r.Image == "" && - !(r.EnableStar && r.StarPath != "") && !(r.EnableExec && r.ExecPath != "") { + !(r.EnableStar && (r.StarPath != "" || r.StarURL != "")) && !(r.EnableExec && r.ExecPath != "") { return errors.Errorf("must specify --image") } diff --git a/kyaml/fn/runtime/runtimeutil/functiontypes.go b/kyaml/fn/runtime/runtimeutil/functiontypes.go index a5a0887ee..d068f4b2d 100644 --- a/kyaml/fn/runtime/runtimeutil/functiontypes.go +++ b/kyaml/fn/runtime/runtimeutil/functiontypes.go @@ -65,6 +65,9 @@ type StarlarkSpec struct { // Path specifies a path to a starlark script Path string `json:"path,omitempty" yaml:"path,omitempty"` + + // URL specifies a url containing a starlark script + URL string `json:"url,omitempty" yaml:"url,omitempty"` } // StorageMount represents a container's mounted storage option(s) diff --git a/kyaml/fn/runtime/starlark/starlark.go b/kyaml/fn/runtime/starlark/starlark.go index de251007a..584237cac 100644 --- a/kyaml/fn/runtime/starlark/starlark.go +++ b/kyaml/fn/runtime/starlark/starlark.go @@ -50,9 +50,9 @@ func (sf *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { } func (sf *Filter) setup() error { - if sf.URL != "" && sf.Path != "" || - sf.URL != "" && sf.Program != "" || - sf.Path != "" && sf.Program != "" { + if (sf.URL != "" && sf.Path != "") || + (sf.URL != "" && sf.Program != "") || + (sf.Path != "" && sf.Program != "") { return errors.Errorf("Filter Path, Program and URL are mutually exclusive") } diff --git a/kyaml/runfn/runfn.go b/kyaml/runfn/runfn.go index 5043b37c6..21d85b690 100644 --- a/kyaml/runfn/runfn.go +++ b/kyaml/runfn/runfn.go @@ -356,25 +356,29 @@ func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode) (kio.Filter cf.Exec.DeferFailure = spec.DeferFailure return cf, nil } - if r.EnableStarlark && spec.Starlark.Path != "" { + if r.EnableStarlark && (spec.Starlark.Path != "" || spec.Starlark.URL != "") { // the script path is relative to the function config file m, err := api.GetMeta() if err != nil { return nil, errors.Wrap(err) } - p := m.Annotations[kioutil.PathAnnotation] - spec.Starlark.Path = path.Clean(spec.Starlark.Path) - if path.IsAbs(spec.Starlark.Path) { - return nil, errors.Errorf( - "absolute function path %s not allowed", spec.Starlark.Path) - } - if strings.HasPrefix(spec.Starlark.Path, "..") { - return nil, errors.Errorf( - "function path %s not allowed to start with ../", spec.Starlark.Path) - } - p = path.Join(r.Path, path.Dir(p), spec.Starlark.Path) - sf := &starlark.Filter{Name: spec.Starlark.Name, Path: p} + var p string + if spec.Starlark.Path != "" { + p = m.Annotations[kioutil.PathAnnotation] + spec.Starlark.Path = path.Clean(spec.Starlark.Path) + if path.IsAbs(spec.Starlark.Path) { + return nil, errors.Errorf( + "absolute function path %s not allowed", spec.Starlark.Path) + } + if strings.HasPrefix(spec.Starlark.Path, "..") { + return nil, errors.Errorf( + "function path %s not allowed to start with ../", spec.Starlark.Path) + } + p = path.Join(r.Path, path.Dir(p), spec.Starlark.Path) + } + + sf := &starlark.Filter{Name: spec.Starlark.Name, Path: p, URL: spec.Starlark.URL} sf.FunctionConfig = api sf.GlobalScope = r.GlobalScope