diff --git a/api/internal/plugins/execplugin/execplugin.go b/api/internal/plugins/execplugin/execplugin.go index 5e63457fd..d4c7d0069 100644 --- a/api/internal/plugins/execplugin/execplugin.go +++ b/api/internal/plugins/execplugin/execplugin.go @@ -9,22 +9,16 @@ import ( "io/ioutil" "os" "os/exec" - "strconv" "strings" "github.com/google/shlex" "github.com/pkg/errors" - "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" tmpConfigFilePrefix = "kust-plugin-config-" ) @@ -114,12 +108,12 @@ func (p *ExecPlugin) Generate() (resmap.ResMap, error) { if err != nil { return nil, err } - return p.UpdateResourceOptions(rm) + return 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 := p.getResMapWithIdAnnotation(rm) + inputRM, err := getResMapWithIdAnnotation(rm) if err != nil { return err } @@ -137,7 +131,7 @@ func (p *ExecPlugin) Transform(rm resmap.ResMap) error { } // update the original ResMap based on the output - return p.updateResMapValues(output, rm) + return updateResMapValues(p.path, p.h, output, rm) } // invokePlugin writes plugin config to a temp file, then @@ -184,91 +178,3 @@ func (p *ExecPlugin) getEnv() []string { "KUSTOMIZE_PLUGIN_CONFIG_ROOT="+p.h.Loader().Root()) return env } - -// Returns a new copy of the given ResMap with the ResIds annotated in each Resource -func (p *ExecPlugin) 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 (p *ExecPlugin) updateResMapValues(output []byte, rm resmap.ResMap) error { - outputRM, err := p.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", - p.path, 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 (p *ExecPlugin) 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/execplugin_test.go b/api/internal/plugins/execplugin/execplugin_test.go index 249571dd7..d6b9021cf 100644 --- a/api/internal/plugins/execplugin/execplugin_test.go +++ b/api/internal/plugins/execplugin/execplugin_test.go @@ -4,7 +4,6 @@ package execplugin_test import ( - "fmt" "strings" "testing" @@ -17,7 +16,6 @@ import ( "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resource" valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" - "sigs.k8s.io/kustomize/api/types" ) func TestExecPluginConfig(t *testing.T) { @@ -91,107 +89,3 @@ metadata: t.Fatalf("unexpected arg array: %#v", p.Args()) } } - -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) { - p := NewExecPlugin("") - if err := p.ErrIfNotExecutable(); err == nil { - t.Fatalf("expected unexecutable error") - } - 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 := p.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) { - p := NewExecPlugin("") - if err := p.ErrIfNotExecutable(); err == nil { - t.Fatalf("expected unexecutable error") - } - 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 := p.UpdateResourceOptions(in) - if err == nil { - t.Errorf("expected error from value %q", c) - } - } -} diff --git a/api/internal/plugins/fnplugin/fnplugin.go b/api/internal/plugins/execplugin/fnplugin.go similarity index 56% rename from api/internal/plugins/fnplugin/fnplugin.go rename to api/internal/plugins/execplugin/fnplugin.go index 0b2853407..fa56db953 100644 --- a/api/internal/plugins/fnplugin/fnplugin.go +++ b/api/internal/plugins/execplugin/fnplugin.go @@ -1,34 +1,25 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package fnplugin +package execplugin import ( "bytes" "fmt" - "strconv" "github.com/pkg/errors" - "sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/types" - "sigs.k8s.io/yaml" "sigs.k8s.io/kustomize/kyaml/kio" - kyaml "sigs.k8s.io/kustomize/kyaml/yaml" + "sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil" "sigs.k8s.io/kustomize/kyaml/runfn" ) -const ( - idAnnotation = "kustomize.config.k8s.io/id" - HashAnnotation = "kustomize.config.k8s.io/needs-hash" - BehaviorAnnotation = "kustomize.config.k8s.io/behavior" -) - type FnPlugin struct { // Function runner runFns runfn.RunFns @@ -36,19 +27,22 @@ type FnPlugin struct { // Plugin configuration data. cfg []byte + // Plugin name cache for error output + pluginName string + // PluginHelpers h *resmap.PluginHelpers } -func bytesToRNode(yml []byte) (*kyaml.RNode, error) { - rnode, err := kyaml.Parse(string(yml)) +func bytesToRNode(yml []byte) (*yaml.RNode, error) { + rnode, err := yaml.Parse(string(yml)) if err != nil { return nil, err } return rnode, nil } -func resourceToRNode(res *resource.Resource) (*kyaml.RNode, error) { +func resourceToRNode(res *resource.Resource) (*yaml.RNode, error) { yml, err := res.AsYAML() if err != nil { return nil, err @@ -83,7 +77,7 @@ func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin { //log.Printf("options: %v\n", o) return &FnPlugin{ runFns: runfn.RunFns{ - Functions: []*kyaml.RNode{}, + Functions: []*yaml.RNode{}, Network: o.Network, NetworkName: o.NetworkName, EnableStarlark: o.EnableStar, @@ -100,6 +94,21 @@ func (p *FnPlugin) Cfg() []byte { func (p *FnPlugin) Config(h *resmap.PluginHelpers, config []byte) error { p.h = h p.cfg = config + + rnode, err := bytesToRNode(p.cfg) + if err != nil { + return err + } + + meta, err := rnode.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 } @@ -112,12 +121,12 @@ func (p *FnPlugin) Generate() (resmap.ResMap, error) { if err != nil { return nil, err } - return p.UpdateResourceOptions(rm) + return UpdateResourceOptions(rm) } func (p *FnPlugin) Transform(rm resmap.ResMap) error { // add ResIds as annotations to all objects so that we can add them back - inputRM, err := p.getResMapWithIdAnnotation(rm) + inputRM, err := getResMapWithIdAnnotation(rm) if err != nil { return err } @@ -135,7 +144,7 @@ func (p *FnPlugin) Transform(rm resmap.ResMap) error { } // update the original ResMap based on the output - return p.updateResMapValues(output, rm) + return updateResMapValues(p.pluginName, p.h, output, rm) } // invokePlugin uses Function runner to run function as plugin @@ -145,7 +154,7 @@ func (p *FnPlugin) invokePlugin(input []byte) ([]byte, error) { if err != nil { return nil, err } - err = rnode.PipeE(kyaml.SetAnnotation("config.kubernetes.io/local-config", "true")) + err = rnode.PipeE(yaml.SetAnnotation("config.kubernetes.io/local-config", "true")) if err != nil { return nil, err } @@ -208,91 +217,3 @@ func (p *FnPlugin) invokePlugin(input []byte) ([]byte, error) { return outOut.Bytes(), nil } - -// Returns a new copy of the given ResMap with the ResIds annotated in each Resource -func (p *FnPlugin) 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 (p *FnPlugin) updateResMapValues(output []byte, rm resmap.ResMap) error { - outputRM, err := p.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 should not remove annotation %s", - 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 ResMap resource value with the transformed object - res.Kunstructured = r.Kunstructured - } - return nil -} - -// updateResourceOptions updates the generator options for each resource in the -// given ResMap based on plugin provided annotations. -func (p *FnPlugin) 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.go b/api/internal/plugins/execplugin/utils.go new file mode 100644 index 000000000..4c7ac8e66 --- /dev/null +++ b/api/internal/plugins/execplugin/utils.go @@ -0,0 +1,108 @@ +// 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 new file mode 100644 index 000000000..93f9cb754 --- /dev/null +++ b/api/internal/plugins/execplugin/utils_test.go @@ -0,0 +1,111 @@ +// 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/loader/loader.go b/api/internal/plugins/loader/loader.go index 393f665f9..c61f264c4 100644 --- a/api/internal/plugins/loader/loader.go +++ b/api/internal/plugins/loader/loader.go @@ -16,7 +16,6 @@ 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" @@ -168,9 +167,9 @@ func (l *Loader) makeBuiltinPlugin(r resid.Gvk) (resmap.Configurable, error) { } func (l *Loader) loadPlugin(res *resource.Resource) (resmap.Configurable, error) { - _, err := fnplugin.GetFunctionSpec(res) + _, err := execplugin.GetFunctionSpec(res) if err == nil { - return fnplugin.NewFnPlugin(&l.pc.FnpLoadingOptions), nil + return execplugin.NewFnPlugin(&l.pc.FnpLoadingOptions), nil } return l.loadExecOrGoPlugin(res.OrgId()) }