diff --git a/api/internal/plugins/fnplugin/fnplugin.go b/api/internal/plugins/fnplugin/fnplugin.go index 84bc0ac05..67d1803a3 100644 --- a/api/internal/plugins/fnplugin/fnplugin.go +++ b/api/internal/plugins/fnplugin/fnplugin.go @@ -51,13 +51,16 @@ func resourceToRNode(res *resource.Resource) (*yaml.RNode, error) { } // GetFunctionSpec return function spec is there is. Otherwise return nil -func GetFunctionSpec(res *resource.Resource) *runtimeutil.FunctionSpec { +func GetFunctionSpec(res *resource.Resource) (*runtimeutil.FunctionSpec, error) { rnode, err := resourceToRNode(res) if err != nil { - return nil + return nil, fmt.Errorf("%w", err) } - - return runtimeutil.GetFunctionSpec(rnode) + functionSpec, err := runtimeutil.GetFunctionSpec(rnode) + if err != nil { + return nil, fmt.Errorf("%w", err) + } + return functionSpec, nil } func toStorageMounts(mounts []string) []runtimeutil.StorageMount { diff --git a/api/internal/plugins/loader/loader.go b/api/internal/plugins/loader/loader.go index 82cb93965..0d1d601c6 100644 --- a/api/internal/plugins/loader/loader.go +++ b/api/internal/plugins/loader/loader.go @@ -58,11 +58,11 @@ func (l *Loader) LoadGenerators( for _, res := range rm.Resources() { g, err := l.LoadGenerator(ldr, v, res) if err != nil { - return nil, err + return nil, fmt.Errorf("%w", err) } generatorOrigin, err := resource.OriginFromCustomPlugin(res) if err != nil { - return nil, err + return nil, fmt.Errorf("%w", err) } result = append(result, &resmap.GeneratorWithProperties{Generator: g, Origin: generatorOrigin}) } @@ -130,15 +130,19 @@ func (l *Loader) AbsolutePluginPath(id resid.ResId) (string, error) { // absPluginHome is the home of kustomize Exec and Go plugins. // Kustomize plugin configuration files are k8s-style objects // containing the fields 'apiVersion' and 'kind', e.g. -// apiVersion: apps/v1 -// kind: Deployment +// +// apiVersion: apps/v1 +// kind: Deployment +// // kustomize reads plugin configuration data from a file path // specified in the 'generators:' or 'transformers:' field of a // kustomization file. For Exec and Go plugins, kustomize // uses this data to both locate the plugin and configure it. // Each Exec or Go plugin (its code, its tests, its supporting data // files, etc.) must be housed in its own directory at -// ${absPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind}) +// +// ${absPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind}) +// // where // - ${absPluginHome} is an absolute path, defined below. // - ${pluginApiVersion} is taken from the plugin config file. @@ -226,7 +230,10 @@ func (l *Loader) makeBuiltinPlugin(r resid.Gvk) (resmap.Configurable, error) { } func (l *Loader) loadPlugin(res *resource.Resource) (resmap.Configurable, error) { - spec := fnplugin.GetFunctionSpec(res) + spec, err := fnplugin.GetFunctionSpec(res) + if err != nil { + return nil, fmt.Errorf("loader: %w", err) + } if spec != nil { // validation check that function mounts are under the current kustomization directory for _, mount := range spec.Container.StorageMounts { diff --git a/kyaml/fn/runtime/runtimeutil/functiontypes.go b/kyaml/fn/runtime/runtimeutil/functiontypes.go index edadff204..80f6197ce 100644 --- a/kyaml/fn/runtime/runtimeutil/functiontypes.go +++ b/kyaml/fn/runtime/runtimeutil/functiontypes.go @@ -201,50 +201,51 @@ func (s *StorageMount) String() string { // // The FunctionSpec is read from the resource metadata.annotation // "config.kubernetes.io/function" -func GetFunctionSpec(n *yaml.RNode) *FunctionSpec { +func GetFunctionSpec(n *yaml.RNode) (*FunctionSpec, error) { meta, err := n.GetMeta() if err != nil { - return nil + return nil, nil } - if fn := getFunctionSpecFromAnnotation(n, meta); fn != nil { - return fn + if fn, err := getFunctionSpecFromAnnotation(n, meta); err != nil { + return nil, err + } else if fn != nil { + return fn, nil } // legacy function specification for backwards compatibility container := meta.Annotations["config.kubernetes.io/container"] if container != "" { - return &FunctionSpec{Container: ContainerSpec{Image: container}} + return &FunctionSpec{Container: ContainerSpec{Image: container}}, nil } - return nil + return nil, nil } // getFunctionSpecFromAnnotation parses the config function from an annotation // if it is found -func getFunctionSpecFromAnnotation(n *yaml.RNode, meta yaml.ResourceMeta) *FunctionSpec { +func getFunctionSpecFromAnnotation(n *yaml.RNode, meta yaml.ResourceMeta) (*FunctionSpec, error) { var fs FunctionSpec for _, s := range functionAnnotationKeys { fn := meta.Annotations[s] if fn != "" { - err := k8syaml.UnmarshalStrict([]byte(fn), &fs) - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + if err := k8syaml.UnmarshalStrict([]byte(fn), &fs); err != nil { + return nil, fmt.Errorf("%s unmarshal error: %w", s, err) } - return &fs + return &fs, nil } } n, err := n.Pipe(yaml.Lookup("metadata", "configFn")) if err != nil || yaml.IsMissingOrNull(n) { - return nil + return nil, nil } s, err := n.String() if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + fmt.Fprintf(os.Stderr, "configFn parse error: %v\n", err) + return nil, fmt.Errorf("configFn parse error: %w", err) } - err = k8syaml.UnmarshalStrict([]byte(s), &fs) - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) + if err := k8syaml.UnmarshalStrict([]byte(s), &fs); err != nil { + return nil, fmt.Errorf("%s unmarshal error: %w", "configFn", err) } - return &fs + return &fs, nil } func StringToStorageMount(s string) StorageMount { @@ -289,7 +290,11 @@ type IsReconcilerFilter struct { func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) { var out []*yaml.RNode for i := range inputs { - isFnResource := GetFunctionSpec(inputs[i]) != nil + functionSpec, err := GetFunctionSpec(inputs[i]) + if err != nil { + return nil, err + } + isFnResource := functionSpec != nil if isFnResource && !c.ExcludeReconcilers { out = append(out, inputs[i]) } diff --git a/kyaml/fn/runtime/runtimeutil/runtimeutil_test.go b/kyaml/fn/runtime/runtimeutil/runtimeutil_test.go index 8fe8ac53b..d0cead777 100644 --- a/kyaml/fn/runtime/runtimeutil/runtimeutil_test.go +++ b/kyaml/fn/runtime/runtimeutil/runtimeutil_test.go @@ -1242,7 +1242,7 @@ container: }, { - name: "path", + name: "path_with_uncorrect_position", resource: ` apiVersion: v1beta1 kind: Example @@ -1253,15 +1253,11 @@ metadata: container: image: foo:v1.0.0 `, - // path should be erased - expectedFn: ` -container: - image: foo:v1.0.0 -`, + missingFn: true, }, { - name: "network", + name: "network_with_uncorrect_position", resource: ` apiVersion: v1beta1 kind: Example @@ -1272,11 +1268,7 @@ metadata: container: image: foo:v1.0.0 `, - // network should be erased - expectedFn: ` -container: - image: foo:v1.0.0 -`, + missingFn: true, }, { name: "fn missing spell", @@ -1289,7 +1281,7 @@ metadata: containeer: image: foo:v1.0.0 `, - expectedFn: `{}`, + missingFn: true, }, // legacy fn style @@ -1310,7 +1302,8 @@ container: }, // no fn - {name: "no fn", + { + name: "no fn", resource: ` apiVersion: v1beta1 kind: Example @@ -1327,7 +1320,7 @@ metadata: tt := tests[i] t.Run(tt.name, func(t *testing.T) { resource := yaml.MustParse(tt.resource) - fn := GetFunctionSpec(resource) + fn, _ := GetFunctionSpec(resource) if tt.missingFn { if !assert.Nil(t, fn) { t.FailNow() @@ -1408,7 +1401,7 @@ metadata: if !assert.NoError(t, err) { return } - fn := GetFunctionSpec(cfg) + fn, _ := GetFunctionSpec(cfg) assert.Equal(t, tc.required, fn.Container.Network) } } @@ -1546,7 +1539,7 @@ metadata: if !assert.NoError(t, err) { return } - fn := GetFunctionSpec(cfg) + fn, _ := GetFunctionSpec(cfg) assert.Equal(t, tc.expected, *NewContainerEnvFromStringSlice(fn.Container.Env)) } } diff --git a/kyaml/runfn/runfn.go b/kyaml/runfn/runfn.go index c855144d7..e1564581b 100644 --- a/kyaml/runfn/runfn.go +++ b/kyaml/runfn/runfn.go @@ -308,7 +308,10 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) ( var fltrs []kio.Filter for i := range fns { api := fns[i] - spec := runtimeutil.GetFunctionSpec(api) + spec, err := runtimeutil.GetFunctionSpec(api) + if err != nil { + return nil, fmt.Errorf("%w", err) + } if spec == nil { // resource doesn't have function spec continue diff --git a/kyaml/runfn/runfn_test.go b/kyaml/runfn/runfn_test.go index ebdb56984..0ea0779df 100644 --- a/kyaml/runfn/runfn_test.go +++ b/kyaml/runfn/runfn_test.go @@ -314,6 +314,24 @@ metadata: }, out: []string{"gcr.io/example.com/image:v1.0.0 deferFailure: true"}, }, + { + name: "parse_failure", + in: []f{ + { + path: filepath.Join("foo", "bar.yaml"), + value: ` +apiVersion: example.com/v1alpha1 +kind: ExampleFunction +metadata: + annotations: + config.kubernetes.io/function: | + containeeer: + image: gcr.io/example.com/image:v1.0.0 +`, + }, + }, + error: "config.kubernetes.io/function unmarshal error: error unmarshaling JSON: while decoding JSON: json: unknown field \"containeeer\"", + }, {name: "disable containers", in: []f{ @@ -807,7 +825,10 @@ metadata: var images []string for _, n := range packageBuff.Nodes { - spec := runtimeutil.GetFunctionSpec(n) + spec, err := runtimeutil.GetFunctionSpec(n) + if !assert.NoError(t, err) { + t.FailNow() + } images = append(images, spec.Container.Image) }