diff --git a/api/internal/plugins/execplugin/execplugin.go b/api/internal/plugins/execplugin/execplugin.go index d4c7d0069..22b379eb5 100644 --- a/api/internal/plugins/execplugin/execplugin.go +++ b/api/internal/plugins/execplugin/execplugin.go @@ -14,6 +14,7 @@ import ( "github.com/google/shlex" "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/internal/plugins/utils" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/yaml" ) @@ -108,12 +109,12 @@ func (p *ExecPlugin) Generate() (resmap.ResMap, error) { if err != nil { return nil, err } - return UpdateResourceOptions(rm) + return utils.UpdateResourceOptions(rm) } func (p *ExecPlugin) Transform(rm resmap.ResMap) error { // add ResIds as annotations to all objects so that we can add them back - inputRM, err := getResMapWithIdAnnotation(rm) + inputRM, err := utils.GetResMapWithIDAnnotation(rm) if err != nil { return err } @@ -131,7 +132,7 @@ func (p *ExecPlugin) Transform(rm resmap.ResMap) error { } // update the original ResMap based on the output - return updateResMapValues(p.path, p.h, output, rm) + return utils.UpdateResMapValues(p.path, p.h, output, rm) } // invokePlugin writes plugin config to a temp file, then diff --git a/api/internal/plugins/execplugin/utils.go b/api/internal/plugins/execplugin/utils.go deleted file mode 100644 index 4c7ac8e66..000000000 --- a/api/internal/plugins/execplugin/utils.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package execplugin - -import ( - "fmt" - "strconv" - - "sigs.k8s.io/kustomize/api/resid" - "sigs.k8s.io/kustomize/api/resmap" - "sigs.k8s.io/kustomize/api/types" - "sigs.k8s.io/yaml" -) - -const ( - idAnnotation = "kustomize.config.k8s.io/id" - HashAnnotation = "kustomize.config.k8s.io/needs-hash" - BehaviorAnnotation = "kustomize.config.k8s.io/behavior" -) - -// Returns a new copy of the given ResMap with the ResIds annotated in each Resource -func getResMapWithIdAnnotation(rm resmap.ResMap) (resmap.ResMap, error) { - inputRM := rm.DeepCopy() - for _, r := range inputRM.Resources() { - idString, err := yaml.Marshal(r.CurId()) - if err != nil { - return nil, err - } - annotations := r.GetAnnotations() - if annotations == nil { - annotations = make(map[string]string) - } - annotations[idAnnotation] = string(idString) - r.SetAnnotations(annotations) - } - return inputRM, nil -} - -// updateResMapValues updates the Resource value in the given ResMap -// with the emitted Resource values in output. -func updateResMapValues(pluginName string, h *resmap.PluginHelpers, output []byte, rm resmap.ResMap) error { - outputRM, err := h.ResmapFactory().NewResMapFromBytes(output) - if err != nil { - return err - } - for _, r := range outputRM.Resources() { - // for each emitted Resource, find the matching Resource in the original ResMap - // using its id - annotations := r.GetAnnotations() - idString, ok := annotations[idAnnotation] - if !ok { - return fmt.Errorf("the transformer %s should not remove annotation %s", - pluginName, idAnnotation) - } - id := resid.ResId{} - err := yaml.Unmarshal([]byte(idString), &id) - if err != nil { - return err - } - res, err := rm.GetByCurrentId(id) - if err != nil { - return fmt.Errorf("unable to find unique match to %s", id.String()) - } - // remove the annotation set by Kustomize to track the resource - delete(annotations, idAnnotation) - if len(annotations) == 0 { - annotations = nil - } - r.SetAnnotations(annotations) - - // update the resource value with the transformed object - res.ResetPrimaryData(r) - } - return nil -} - -// updateResourceOptions updates the generator options for each resource in the -// given ResMap based on plugin provided annotations. -func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) { - for _, r := range rm.Resources() { - // Disable name hashing by default and require plugin to explicitly - // request it for each resource. - annotations := r.GetAnnotations() - behavior := annotations[BehaviorAnnotation] - var needsHash bool - if val, ok := annotations[HashAnnotation]; ok { - b, err := strconv.ParseBool(val) - if err != nil { - return nil, fmt.Errorf( - "the annotation %q contains an invalid value (%q)", - HashAnnotation, val) - } - needsHash = b - } - delete(annotations, HashAnnotation) - delete(annotations, BehaviorAnnotation) - if len(annotations) == 0 { - annotations = nil - } - r.SetAnnotations(annotations) - r.SetOptions(types.NewGenArgs( - &types.GeneratorArgs{ - Behavior: behavior, - Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}})) - } - return rm, nil -} diff --git a/api/internal/plugins/execplugin/utils_test.go b/api/internal/plugins/execplugin/utils_test.go deleted file mode 100644 index 93f9cb754..000000000 --- a/api/internal/plugins/execplugin/utils_test.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package execplugin_test - -import ( - "fmt" - "testing" - - . "sigs.k8s.io/kustomize/api/internal/plugins/execplugin" - "sigs.k8s.io/kustomize/api/k8sdeps/kunstruct" - "sigs.k8s.io/kustomize/api/resmap" - "sigs.k8s.io/kustomize/api/resource" - "sigs.k8s.io/kustomize/api/types" -) - -func makeConfigMap(rf *resource.Factory, name, behavior string, hashValue *string) *resource.Resource { - r := rf.FromMap(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{"name": name}, - }) - annotations := map[string]string{} - if behavior != "" { - annotations[BehaviorAnnotation] = behavior - } - if hashValue != nil { - annotations[HashAnnotation] = *hashValue - } - if len(annotations) > 0 { - r.SetAnnotations(annotations) - } - return r -} - -func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHash bool) *resource.Resource { - return rf.FromMapAndOption(map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{"name": name}, - }, &types.GeneratorArgs{ - Behavior: behavior, - Options: &types.GeneratorOptions{DisableNameSuffixHash: disableHash}}) -} - -func strptr(s string) *string { - return &s -} - -func TestUpdateResourceOptions(t *testing.T) { - rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()) - in := resmap.New() - expected := resmap.New() - cases := []struct { - behavior string - needsHash bool - hashValue *string - }{ - {hashValue: strptr("false")}, - {hashValue: strptr("true"), needsHash: true}, - {behavior: "replace"}, - {behavior: "merge"}, - {behavior: "create"}, - {behavior: "nonsense"}, - {behavior: "merge", hashValue: strptr("false")}, - {behavior: "merge", hashValue: strptr("true"), needsHash: true}, - } - for i, c := range cases { - name := fmt.Sprintf("test%d", i) - in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue)) - expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash)) - } - actual, err := UpdateResourceOptions(in) - if err != nil { - t.Fatalf("unexpected error: %v", err.Error()) - } - for i, a := range expected.Resources() { - b := actual.GetByIndex(i) - if b == nil { - t.Fatalf("resource %d missing from processed map", i) - } - if !a.Equals(b) { - t.Errorf("expected %v got %v", a, b) - } - if a.NeedHashSuffix() != b.NeedHashSuffix() { - t.Errorf("") - } - if a.Behavior() != b.Behavior() { - t.Errorf("expected %v got %v", a.Behavior(), b.Behavior()) - } - } -} - -func TestUpdateResourceOptionsWithInvalidHashAnnotationValues(t *testing.T) { - rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()) - cases := []string{ - "", - "FaLsE", - "TrUe", - "potato", - } - for i, c := range cases { - name := fmt.Sprintf("test%d", i) - in := resmap.New() - in.Append(makeConfigMap(rf, name, "", &c)) - _, err := UpdateResourceOptions(in) - if err == nil { - t.Errorf("expected error from value %q", c) - } - } -} diff --git a/api/internal/plugins/execplugin/fnplugin.go b/api/internal/plugins/fnplugin/fnplugin.go similarity index 62% rename from api/internal/plugins/execplugin/fnplugin.go rename to api/internal/plugins/fnplugin/fnplugin.go index fa56db953..72dbf4271 100644 --- a/api/internal/plugins/execplugin/fnplugin.go +++ b/api/internal/plugins/fnplugin/fnplugin.go @@ -1,7 +1,7 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package execplugin +package fnplugin import ( "bytes" @@ -9,17 +9,16 @@ import ( "github.com/pkg/errors" + "sigs.k8s.io/kustomize/api/internal/plugins/utils" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/types" - - "sigs.k8s.io/kustomize/kyaml/kio" - "sigs.k8s.io/kustomize/kyaml/yaml" - "sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil" "sigs.k8s.io/kustomize/kyaml/runfn" + "sigs.k8s.io/kustomize/kyaml/yaml" ) +// FnPlugin is the struct to hold function information type FnPlugin struct { // Function runner runFns runfn.RunFns @@ -51,18 +50,14 @@ func resourceToRNode(res *resource.Resource) (*yaml.RNode, error) { return bytesToRNode(yml) } -func GetFunctionSpec(res *resource.Resource) (*runtimeutil.FunctionSpec, error) { +// GetFunctionSpec return function spec is there is. Otherwise return nil +func GetFunctionSpec(res *resource.Resource) *runtimeutil.FunctionSpec { rnode, err := resourceToRNode(res) if err != nil { - return nil, err + return nil } - fSpec := runtimeutil.GetFunctionSpec(rnode) - if fSpec == nil { - return nil, fmt.Errorf("resource %v doesn't contain function spec", res.GetGvk()) - } - - return fSpec, nil + return runtimeutil.GetFunctionSpec(rnode) } func toStorageMounts(mounts []string) []runtimeutil.StorageMount { @@ -73,8 +68,8 @@ func toStorageMounts(mounts []string) []runtimeutil.StorageMount { return sms } +// NewFnPlugin creates a FnPlugin struct func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin { - //log.Printf("options: %v\n", o) return &FnPlugin{ runFns: runfn.RunFns{ Functions: []*yaml.RNode{}, @@ -87,31 +82,33 @@ func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin { } } +// Cfg returns function config func (p *FnPlugin) Cfg() []byte { return p.cfg } +// Config is called by kustomize to pass-in config information func (p *FnPlugin) Config(h *resmap.PluginHelpers, config []byte) error { p.h = h p.cfg = config - rnode, err := bytesToRNode(p.cfg) + fn, err := bytesToRNode(p.cfg) if err != nil { return err } - meta, err := rnode.GetMeta() + meta, err := fn.GetMeta() if err != nil { return err } p.pluginName = fmt.Sprintf("api: %s, kind: %s, name: %s", meta.APIVersion, meta.Kind, meta.Name) - //log.Printf("config based pluginName: %s", p.pluginName) return nil } +// Generate is called when run as generator func (p *FnPlugin) Generate() (resmap.ResMap, error) { output, err := p.invokePlugin(nil) if err != nil { @@ -121,12 +118,13 @@ func (p *FnPlugin) Generate() (resmap.ResMap, error) { if err != nil { return nil, err } - return UpdateResourceOptions(rm) + return utils.UpdateResourceOptions(rm) } +// Transform is called when run as transformer func (p *FnPlugin) Transform(rm resmap.ResMap) error { // add ResIds as annotations to all objects so that we can add them back - inputRM, err := getResMapWithIdAnnotation(rm) + inputRM, err := utils.GetResMapWithIDAnnotation(rm) if err != nil { return err } @@ -144,53 +142,50 @@ func (p *FnPlugin) Transform(rm resmap.ResMap) error { } // update the original ResMap based on the output - return updateResMapValues(p.pluginName, p.h, output, rm) + return utils.UpdateResMapValues(p.pluginName, p.h, output, rm) +} + +func injectAnnotation(input *yaml.RNode, k, v string) error { + err := input.PipeE(yaml.SetAnnotation(k, v)) + if err != nil { + return err + } + return nil } // invokePlugin uses Function runner to run function as plugin func (p *FnPlugin) invokePlugin(input []byte) ([]byte, error) { - // get config rnode - rnode, err := bytesToRNode(p.cfg) - if err != nil { - return nil, err - } - err = rnode.PipeE(yaml.SetAnnotation("config.kubernetes.io/local-config", "true")) + // get function config rnode + functionConfig, err := bytesToRNode(p.cfg) if err != nil { return nil, err } + // This annotation will let kustomize ingnore this item in output + err = injectAnnotation(functionConfig, "config.kubernetes.io/local-config", "true") + if err != nil { + return nil, err + } // we need to add config as input for generators. Some of them don't work with FunctionConfig // and in addition kio.Pipeline won't create anything if there are no objects // see https://github.com/kubernetes-sigs/kustomize/blob/master/kyaml/kio/kio.go#L93 + // Since we added `local-config` annotation so it will be ignored in generator output + // TODO(donnyxia): This is actually not used by generator and only used to bypass a kio limitation. + // Need better solution. if input == nil { - yaml, err := rnode.String() + yaml, err := functionConfig.String() if err != nil { return nil, err } input = []byte(yaml) } - // Transform to ResourceList - var inOut bytes.Buffer - inIn := bytes.NewReader(input) - err = kio.Pipeline{ - Inputs: []kio.Reader{&kio.ByteReader{Reader: inIn}}, - Outputs: []kio.Writer{kio.ByteWriter{ - Writer: &inOut, - WrappingKind: kio.ResourceListKind, - WrappingAPIVersion: kio.ResourceListAPIVersion}}, - }.Execute() - if err != nil { - return nil, errors.Wrap( - err, "couldn't transform to ResourceList") - } - //log.Printf("converted to:\n%s\n", inOut.String()) - - // Configure and Execute Fn - var runFnsOut bytes.Buffer - p.runFns.Input = bytes.NewReader(inOut.Bytes()) - p.runFns.Functions = append(p.runFns.Functions, rnode) - p.runFns.Output = &runFnsOut + // Configure and Execute Fn. We don't need to convert resources to ResourceList here + // because function runtime will do that. See kyaml/fn/runtime/runtimeutil/runtimeutil.go + var ouputBuffer bytes.Buffer + p.runFns.Input = bytes.NewReader(input) + p.runFns.Functions = append(p.runFns.Functions, functionConfig) + p.runFns.Output = &ouputBuffer err = p.runFns.Execute() if err != nil { @@ -198,22 +193,5 @@ func (p *FnPlugin) invokePlugin(input []byte) ([]byte, error) { err, "couldn't execute function") } - //log.Printf("fn returned:\n%s\n", runFnsOut.String()) - - // Convert back to a single multi-yaml doc - var outOut bytes.Buffer - outIn := bytes.NewReader(runFnsOut.Bytes()) - - err = kio.Pipeline{ - Inputs: []kio.Reader{&kio.ByteReader{Reader: outIn}}, - Outputs: []kio.Writer{kio.ByteWriter{Writer: &outOut}}, - }.Execute() - if err != nil { - return nil, errors.Wrap( - err, "couldn't transform from ResourceList") - } - - //log.Printf("converted back to:\n%s\n", outOut.String()) - - return outOut.Bytes(), nil + return ouputBuffer.Bytes(), nil } diff --git a/api/internal/plugins/loader/loader.go b/api/internal/plugins/loader/loader.go index c61f264c4..aaa7c3600 100644 --- a/api/internal/plugins/loader/loader.go +++ b/api/internal/plugins/loader/loader.go @@ -16,6 +16,7 @@ import ( "sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers" "sigs.k8s.io/kustomize/api/internal/plugins/execplugin" + "sigs.k8s.io/kustomize/api/internal/plugins/fnplugin" "sigs.k8s.io/kustomize/api/internal/plugins/utils" "sigs.k8s.io/kustomize/api/konfig" "sigs.k8s.io/kustomize/api/resid" @@ -167,9 +168,9 @@ func (l *Loader) makeBuiltinPlugin(r resid.Gvk) (resmap.Configurable, error) { } func (l *Loader) loadPlugin(res *resource.Resource) (resmap.Configurable, error) { - _, err := execplugin.GetFunctionSpec(res) - if err == nil { - return execplugin.NewFnPlugin(&l.pc.FnpLoadingOptions), nil + spec := fnplugin.GetFunctionSpec(res) + if spec != nil { + return fnplugin.NewFnPlugin(&l.pc.FnpLoadingOptions), nil } return l.loadExecOrGoPlugin(res.OrgId()) } diff --git a/api/internal/plugins/utils/utils.go b/api/internal/plugins/utils/utils.go index af4976cea..46706627b 100644 --- a/api/internal/plugins/utils/utils.go +++ b/api/internal/plugins/utils/utils.go @@ -4,13 +4,25 @@ package utils import ( + "fmt" "os" "path/filepath" "runtime" + "strconv" "time" "sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/resid" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/types" + "sigs.k8s.io/yaml" +) + +const ( + idAnnotation = "kustomize.config.k8s.io/id" + HashAnnotation = "kustomize.config.k8s.io/needs-hash" + BehaviorAnnotation = "kustomize.config.k8s.io/behavior" ) func GoBin() string { @@ -113,3 +125,91 @@ func FileExists(path string) bool { } return true } + +// GetResMapWithIDAnnotation returns a new copy of the given ResMap with the ResIds annotated in each Resource +func GetResMapWithIDAnnotation(rm resmap.ResMap) (resmap.ResMap, error) { + inputRM := rm.DeepCopy() + for _, r := range inputRM.Resources() { + idString, err := yaml.Marshal(r.CurId()) + if err != nil { + return nil, err + } + annotations := r.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations[idAnnotation] = string(idString) + r.SetAnnotations(annotations) + } + return inputRM, nil +} + +// UpdateResMapValues updates the Resource value in the given ResMap +// with the emitted Resource values in output. +func UpdateResMapValues(pluginName string, h *resmap.PluginHelpers, output []byte, rm resmap.ResMap) error { + outputRM, err := h.ResmapFactory().NewResMapFromBytes(output) + if err != nil { + return err + } + for _, r := range outputRM.Resources() { + // for each emitted Resource, find the matching Resource in the original ResMap + // using its id + annotations := r.GetAnnotations() + idString, ok := annotations[idAnnotation] + if !ok { + return fmt.Errorf("the transformer %s should not remove annotation %s", + pluginName, idAnnotation) + } + id := resid.ResId{} + err := yaml.Unmarshal([]byte(idString), &id) + if err != nil { + return err + } + res, err := rm.GetByCurrentId(id) + if err != nil { + return fmt.Errorf("unable to find unique match to %s", id.String()) + } + // remove the annotation set by Kustomize to track the resource + delete(annotations, idAnnotation) + if len(annotations) == 0 { + annotations = nil + } + r.SetAnnotations(annotations) + + // update the resource value with the transformed object + res.ResetPrimaryData(r) + } + return nil +} + +// UpdateResourceOptions updates the generator options for each resource in the +// given ResMap based on plugin provided annotations. +func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) { + for _, r := range rm.Resources() { + // Disable name hashing by default and require plugin to explicitly + // request it for each resource. + annotations := r.GetAnnotations() + behavior := annotations[BehaviorAnnotation] + var needsHash bool + if val, ok := annotations[HashAnnotation]; ok { + b, err := strconv.ParseBool(val) + if err != nil { + return nil, fmt.Errorf( + "the annotation %q contains an invalid value (%q)", + HashAnnotation, val) + } + needsHash = b + } + delete(annotations, HashAnnotation) + delete(annotations, BehaviorAnnotation) + if len(annotations) == 0 { + annotations = nil + } + r.SetAnnotations(annotations) + r.SetOptions(types.NewGenArgs( + &types.GeneratorArgs{ + Behavior: behavior, + Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}})) + } + return rm, nil +} diff --git a/api/internal/plugins/utils/utils_test.go b/api/internal/plugins/utils/utils_test.go index e4341287b..356e7680a 100644 --- a/api/internal/plugins/utils/utils_test.go +++ b/api/internal/plugins/utils/utils_test.go @@ -4,12 +4,17 @@ package utils import ( + "fmt" "path/filepath" "strings" "testing" "sigs.k8s.io/kustomize/api/filesys" + "sigs.k8s.io/kustomize/api/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/api/konfig" + "sigs.k8s.io/kustomize/api/resmap" + "sigs.k8s.io/kustomize/api/resource" + "sigs.k8s.io/kustomize/api/types" ) func TestDeterminePluginSrcRoot(t *testing.T) { @@ -24,3 +29,99 @@ func TestDeterminePluginSrcRoot(t *testing.T) { t.Errorf("expected suffix '%s' in '%s'", konfig.RelPluginHome, actual) } } + +func makeConfigMap(rf *resource.Factory, name, behavior string, hashValue *string) *resource.Resource { + r := rf.FromMap(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{"name": name}, + }) + annotations := map[string]string{} + if behavior != "" { + annotations[BehaviorAnnotation] = behavior + } + if hashValue != nil { + annotations[HashAnnotation] = *hashValue + } + if len(annotations) > 0 { + r.SetAnnotations(annotations) + } + return r +} + +func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHash bool) *resource.Resource { + return rf.FromMapAndOption(map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{"name": name}, + }, &types.GeneratorArgs{ + Behavior: behavior, + Options: &types.GeneratorOptions{DisableNameSuffixHash: disableHash}}) +} + +func strptr(s string) *string { + return &s +} + +func TestUpdateResourceOptions(t *testing.T) { + rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()) + in := resmap.New() + expected := resmap.New() + cases := []struct { + behavior string + needsHash bool + hashValue *string + }{ + {hashValue: strptr("false")}, + {hashValue: strptr("true"), needsHash: true}, + {behavior: "replace"}, + {behavior: "merge"}, + {behavior: "create"}, + {behavior: "nonsense"}, + {behavior: "merge", hashValue: strptr("false")}, + {behavior: "merge", hashValue: strptr("true"), needsHash: true}, + } + for i, c := range cases { + name := fmt.Sprintf("test%d", i) + in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue)) + expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash)) + } + actual, err := UpdateResourceOptions(in) + if err != nil { + t.Fatalf("unexpected error: %v", err.Error()) + } + for i, a := range expected.Resources() { + b := actual.GetByIndex(i) + if b == nil { + t.Fatalf("resource %d missing from processed map", i) + } + if !a.Equals(b) { + t.Errorf("expected %v got %v", a, b) + } + if a.NeedHashSuffix() != b.NeedHashSuffix() { + t.Errorf("") + } + if a.Behavior() != b.Behavior() { + t.Errorf("expected %v got %v", a.Behavior(), b.Behavior()) + } + } +} + +func TestUpdateResourceOptionsWithInvalidHashAnnotationValues(t *testing.T) { + rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()) + cases := []string{ + "", + "FaLsE", + "TrUe", + "potato", + } + for i, c := range cases { + name := fmt.Sprintf("test%d", i) + in := resmap.New() + in.Append(makeConfigMap(rf, name, "", &c)) + _, err := UpdateResourceOptions(in) + if err == nil { + t.Errorf("expected error from value %q", c) + } + } +} diff --git a/api/krusty/fnplugin_test.go b/api/krusty/fnplugin_test.go index 1aaa2ac90..5a2081622 100644 --- a/api/krusty/fnplugin_test.go +++ b/api/krusty/fnplugin_test.go @@ -404,3 +404,63 @@ spec: memory: 50M `) } + +func TestFnContainerTransformerWithConfig(t *testing.T) { + skipIfNoDocker(t) + + th := kusttest_test.MakeEnhancedHarness(t) + defer th.Reset() + + th.WriteK("/app", ` +resources: +- data1.yaml +- data2.yaml +transformers: +- label_namespace.yaml +`) + + th.WriteF("/app/data1.yaml", `apiVersion: v1 +kind: Namespace +metadata: + name: my-namespace +`) + th.WriteF("/app/data2.yaml", `apiVersion: v1 +kind: Namespace +metadata: + name: another-namespace +`) + + th.WriteF("/app/label_namespace.yaml", `apiVersion: v1 +kind: ConfigMap +metadata: + name: label_namespace + annotations: + config.kubernetes.io/function: |- + container: + image: gcr.io/kpt-functions/label-namespace@sha256:4f030738d6d25a207641ca517916431517578bd0eb8d98a8bde04e3bb9315dcd +data: + label_name: my-ns-name + label_value: function-test +`) + + m := th.Run("/app", th.MakeOptionsPluginsEnabled()) + th.AssertActualEqualsExpected(m, ` +apiVersion: v1 +kind: Namespace +metadata: + annotations: + config.kubernetes.io/path: namespace_my-namespace.yaml + labels: + my-ns-name: function-test + name: my-namespace +--- +apiVersion: v1 +kind: Namespace +metadata: + annotations: + config.kubernetes.io/path: namespace_another-namespace.yaml + labels: + my-ns-name: function-test + name: another-namespace +`) +} diff --git a/api/krusty/fnplugin_test/fnexectest.sh b/api/krusty/fnplugin_test/fnexectest.sh index ed98a837c..d8e7c9eab 100755 --- a/api/krusty/fnplugin_test/fnexectest.sh +++ b/api/krusty/fnplugin_test/fnexectest.sh @@ -1,7 +1,5 @@ #!/bin/sh -# not sure if we want to generate bash scripts, since we always want to run -# only trusted executables cat <