be build fail when parse failed to FunctionSpec

This commit is contained in:
yugo kobayashi
2022-08-19 00:58:22 +00:00
parent e2e9181bed
commit f086269d6e
6 changed files with 79 additions and 47 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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])
}

View File

@@ -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))
}
}

View File

@@ -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

View File

@@ -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)
}