mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-29 01:30:51 +00:00
Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1
This commit is contained in:
@@ -9,3 +9,4 @@ aliases:
|
||||
- mengqiy
|
||||
- monopole
|
||||
- pwittrock
|
||||
- mortent
|
||||
|
||||
@@ -12,8 +12,7 @@ and it's like [`sed`], in that it emits edited text.
|
||||
This tool is sponsored by [sig-cli] ([KEP]), and
|
||||
inspired by [DAM].
|
||||
|
||||
|
||||
[](https://travis-ci.org/kubernetes-sigs/kustomize)
|
||||
[](https://prow.k8s.io/job-history/kubernetes-jenkins/pr-logs/directory/kustomize-presubmit-master)
|
||||
[](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
|
||||
|
||||
Download a binary from the [release page], or see
|
||||
|
||||
@@ -13,13 +13,10 @@ import (
|
||||
type ConfigMapGeneratorPlugin struct {
|
||||
h *resmap.PluginHelpers
|
||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
types.GeneratorOptions
|
||||
types.ConfigMapArgs
|
||||
}
|
||||
|
||||
func (p *ConfigMapGeneratorPlugin) Config(
|
||||
h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
p.GeneratorOptions = types.GeneratorOptions{}
|
||||
func (p *ConfigMapGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
p.ConfigMapArgs = types.ConfigMapArgs{}
|
||||
err = yaml.Unmarshal(config, p)
|
||||
if p.ConfigMapArgs.Name == "" {
|
||||
@@ -34,8 +31,7 @@ func (p *ConfigMapGeneratorPlugin) Config(
|
||||
|
||||
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||
return p.h.ResmapFactory().FromConfigMapArgs(
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()),
|
||||
&p.GeneratorOptions, p.ConfigMapArgs)
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), p.ConfigMapArgs)
|
||||
}
|
||||
|
||||
func NewConfigMapGeneratorPlugin() resmap.GeneratorPlugin {
|
||||
|
||||
@@ -55,26 +55,23 @@ func (p *InventoryTransformerPlugin) Config(
|
||||
// (e.g. some App object) become more desirable
|
||||
// for this purpose.
|
||||
func (p *InventoryTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
|
||||
inv, h, err := makeInventory(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := types.ConfigMapArgs{}
|
||||
args.Name = p.Name
|
||||
args.Namespace = p.Namespace
|
||||
opts := &types.GeneratorOptions{
|
||||
Annotations: make(map[string]string),
|
||||
args.Options = &types.GeneratorOptions{
|
||||
Annotations: map[string]string{inventory.HashAnnotation: h},
|
||||
}
|
||||
opts.Annotations[inventory.HashAnnotation] = h
|
||||
err = inv.UpdateAnnotations(opts.Annotations)
|
||||
err = inv.UpdateAnnotations(args.Options.Annotations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cm, err := p.h.ResmapFactory().RF().MakeConfigMap(
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), opts, &args)
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), &args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@ import (
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -21,6 +23,8 @@ type PatchJson6902TransformerPlugin struct {
|
||||
Target types.PatchTarget `json:"target,omitempty" yaml:"target,omitempty"`
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"`
|
||||
|
||||
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PatchJson6902TransformerPlugin) Config(
|
||||
@@ -83,16 +87,22 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawObj, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
if !p.YAMLSupport {
|
||||
rawObj, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modifiedObj, err := p.decodedPatch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
err, "failed to apply json patch '%s'", p.JsonOp)
|
||||
}
|
||||
return obj.UnmarshalJSON(modifiedObj)
|
||||
} else {
|
||||
return filtersutil.ApplyToJSON(patchjson6902.Filter{
|
||||
Patch: p.JsonOp,
|
||||
}, obj.Kunstructured)
|
||||
}
|
||||
modifiedObj, err := p.decodedPatch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
err, "failed to apply json patch '%s'", p.JsonOp)
|
||||
}
|
||||
return obj.UnmarshalJSON(modifiedObj)
|
||||
}
|
||||
|
||||
func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin {
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -17,6 +22,8 @@ type PatchStrategicMergeTransformerPlugin struct {
|
||||
loadedPatches []*resource.Resource
|
||||
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
|
||||
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
|
||||
|
||||
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PatchStrategicMergeTransformerPlugin) Config(
|
||||
@@ -69,22 +76,42 @@ func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = target.Patch(patch.Kunstructured)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove the resource from resmap
|
||||
// when the patch is to $patch: delete that target
|
||||
if len(target.Map()) == 0 {
|
||||
err = m.Remove(target.CurId())
|
||||
if !p.YAMLSupport {
|
||||
err = target.Patch(patch.Kunstructured)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove the resource from resmap
|
||||
// when the patch is to $patch: delete that target
|
||||
if len(target.Map()) == 0 {
|
||||
err = m.Remove(target.CurId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node, err := getRNode(patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
|
||||
Patch: node,
|
||||
}, target.Kunstructured)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO: Remove this once the next version of kyaml is released which
|
||||
// exposes GetRNode from the filutersutil package.
|
||||
func getRNode(k json.Marshaler) (*kyaml.RNode, error) {
|
||||
j, err := k.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kyaml.Parse(string(j))
|
||||
}
|
||||
|
||||
func NewPatchStrategicMergeTransformerPlugin() resmap.TransformerPlugin {
|
||||
return &PatchStrategicMergeTransformerPlugin{}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@ package builtins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
||||
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -20,69 +24,112 @@ type PatchTransformerPlugin struct {
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
|
||||
|
||||
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PatchTransformerPlugin) Config(
|
||||
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||
err = yaml.Unmarshal(c, p)
|
||||
h *resmap.PluginHelpers, c []byte) error {
|
||||
err := yaml.Unmarshal(c, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Patch = strings.TrimSpace(p.Patch)
|
||||
if p.Patch == "" && p.Path == "" {
|
||||
err = fmt.Errorf(
|
||||
return fmt.Errorf(
|
||||
"must specify one of patch and path in\n%s", string(c))
|
||||
return
|
||||
}
|
||||
if p.Patch != "" && p.Path != "" {
|
||||
err = fmt.Errorf(
|
||||
return fmt.Errorf(
|
||||
"patch and path can't be set at the same time\n%s", string(c))
|
||||
return
|
||||
}
|
||||
var in []byte
|
||||
if p.Path != "" {
|
||||
in, err = h.Loader().Load(p.Path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.Patch != "" {
|
||||
in = []byte(p.Patch)
|
||||
}
|
||||
|
||||
patchSM, errSM := h.ResmapFactory().RF().FromBytes(in)
|
||||
patchJson, errJson := jsonPatchFromBytes(in)
|
||||
if p.Path != "" {
|
||||
loaded, loadErr := h.Loader().Load(p.Path)
|
||||
if loadErr != nil {
|
||||
return loadErr
|
||||
}
|
||||
p.Patch = string(loaded)
|
||||
}
|
||||
|
||||
patchSM, errSM := h.ResmapFactory().RF().FromBytes([]byte(p.Patch))
|
||||
patchJson, errJson := jsonPatchFromBytes([]byte(p.Patch))
|
||||
if (errSM == nil && errJson == nil) ||
|
||||
(patchSM != nil && patchJson != nil) {
|
||||
return fmt.Errorf(
|
||||
"illegally qualifies as both an SM and JSON patch: [%v]",
|
||||
p.Patch)
|
||||
}
|
||||
if errSM != nil && errJson != nil {
|
||||
err = fmt.Errorf(
|
||||
"unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch)
|
||||
return
|
||||
return fmt.Errorf(
|
||||
"unable to parse SM or JSON patch from [%v]", p.Patch)
|
||||
}
|
||||
if errSM == nil && errJson != nil {
|
||||
if errSM == nil {
|
||||
p.loadedPatch = patchSM
|
||||
}
|
||||
if errJson == nil && errSM != nil {
|
||||
} else {
|
||||
p.decodedPatch = patchJson
|
||||
}
|
||||
if patchSM != nil && patchJson != nil {
|
||||
err = fmt.Errorf(
|
||||
"a patch can't be both a Strategic Merge Patch and JSON patch 6902 %s", p.Patch)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
if p.loadedPatch != nil && p.Target == nil {
|
||||
target, err := m.GetById(p.loadedPatch.OrgId())
|
||||
if p.loadedPatch != nil {
|
||||
// The patch was a strategic merge patch
|
||||
return p.transformStrategicMerge(m, p.loadedPatch)
|
||||
} else {
|
||||
return p.transformJson6902(m, p.decodedPatch)
|
||||
}
|
||||
}
|
||||
|
||||
// transformStrategicMerge applies the provided strategic merge patch
|
||||
// to all the resources in the ResMap that match either the Target or
|
||||
// the identifier of the patch.
|
||||
func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap, patch *resource.Resource) error {
|
||||
if p.Target == nil {
|
||||
target, err := m.GetById(patch.OrgId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = target.Patch(p.loadedPatch.Kunstructured)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return p.applySMPatch(target, patch)
|
||||
}
|
||||
|
||||
resources, err := m.Select(*p.Target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, res := range resources {
|
||||
patchCopy := patch.DeepCopy()
|
||||
patchCopy.SetName(res.GetName())
|
||||
patchCopy.SetNamespace(res.GetNamespace())
|
||||
patchCopy.SetGvk(res.GetGvk())
|
||||
err := p.applySMPatch(res, patchCopy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applySMPatch applies the provided strategic merge patch to the
|
||||
// given resource. Depending on the value of YAMLSupport, it will either
|
||||
// use the legacy implementation or the kyaml-based solution.
|
||||
func (p *PatchTransformerPlugin) applySMPatch(resource, patch *resource.Resource) error {
|
||||
if !p.YAMLSupport {
|
||||
return resource.Patch(patch.Kunstructured)
|
||||
} else {
|
||||
node, err := filtersutil.GetRNode(patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
|
||||
Patch: node,
|
||||
}, resource.Kunstructured)
|
||||
}
|
||||
}
|
||||
|
||||
// transformJson6902 applies the provided json6902 patch
|
||||
// to all the resources in the ResMap that match the Target.
|
||||
func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpatch.Patch) error {
|
||||
if p.Target == nil {
|
||||
return fmt.Errorf("must specify a target for patch %s", p.Patch)
|
||||
}
|
||||
@@ -92,35 +139,36 @@ func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
return err
|
||||
}
|
||||
for _, res := range resources {
|
||||
if p.decodedPatch != nil {
|
||||
rawObj, err := res.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modifiedObj, err := p.decodedPatch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
err, "failed to apply json patch '%s'", p.Patch)
|
||||
}
|
||||
err = res.UnmarshalJSON(modifiedObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if p.loadedPatch != nil {
|
||||
patchCopy := p.loadedPatch.DeepCopy()
|
||||
patchCopy.SetName(res.GetName())
|
||||
patchCopy.SetNamespace(res.GetNamespace())
|
||||
patchCopy.SetGvk(res.GetGvk())
|
||||
err = res.Patch(patchCopy.Kunstructured)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.applyJson6902Patch(res, patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyJson6902Patch applies the provided patch to the given resource.
|
||||
// Depending on the value of YAMLSupport, it will either
|
||||
// use the legacy implementation or the kyaml-based solution.
|
||||
func (p *PatchTransformerPlugin) applyJson6902Patch(resource *resource.Resource, patch jsonpatch.Patch) error {
|
||||
if !p.YAMLSupport {
|
||||
rawObj, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modifiedObj, err := patch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
err, "failed to apply json patch '%s'", p.Patch)
|
||||
}
|
||||
return resource.UnmarshalJSON(modifiedObj)
|
||||
} else {
|
||||
return filtersutil.ApplyToJSON(patchjson6902.Filter{
|
||||
Patch: p.Patch,
|
||||
}, resource.Kunstructured)
|
||||
}
|
||||
}
|
||||
|
||||
// jsonPatchFromBytes loads a Json 6902 patch from
|
||||
// a bytes input
|
||||
func jsonPatchFromBytes(
|
||||
|
||||
@@ -13,12 +13,10 @@ import (
|
||||
type SecretGeneratorPlugin struct {
|
||||
h *resmap.PluginHelpers
|
||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
types.GeneratorOptions
|
||||
types.SecretArgs
|
||||
}
|
||||
|
||||
func (p *SecretGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
p.GeneratorOptions = types.GeneratorOptions{}
|
||||
p.SecretArgs = types.SecretArgs{}
|
||||
err = yaml.Unmarshal(config, p)
|
||||
if p.SecretArgs.Name == "" {
|
||||
@@ -33,8 +31,7 @@ func (p *SecretGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (
|
||||
|
||||
func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||
return p.h.ResmapFactory().FromSecretArgs(
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()),
|
||||
&p.GeneratorOptions, p.SecretArgs)
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), p.SecretArgs)
|
||||
}
|
||||
|
||||
func NewSecretGeneratorPlugin() resmap.GeneratorPlugin {
|
||||
|
||||
12
api/filters/imagetag/doc.go
Normal file
12
api/filters/imagetag/doc.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package imagetag contains two kio.Filter implementations to cover the
|
||||
// functionality of the kustomize imagetag transformer.
|
||||
//
|
||||
// Filter updates fields based on a FieldSpec and an ImageTag.
|
||||
//
|
||||
// LegacyFilter doesn't use a FieldSpec, and instead only updates image
|
||||
// references if the field is name image and it is underneath a field called
|
||||
// either containers or initContainers.
|
||||
package imagetag
|
||||
126
api/filters/imagetag/example_test.go
Normal file
126
api/filters/imagetag/example_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- name: FooBar
|
||||
image: nginx
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- name: BarFoo
|
||||
image: nginx:1.2.1
|
||||
`)}},
|
||||
Filters: []kio.Filter{Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
Digest: "12345",
|
||||
},
|
||||
FsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/containers[]/image",
|
||||
},
|
||||
},
|
||||
}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: FooBar
|
||||
// image: apache@12345
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: BarFoo
|
||||
// image: apache@12345
|
||||
}
|
||||
|
||||
func ExampleLegacyFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- name: FooBar
|
||||
image: nginx
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- name: BarFoo
|
||||
image: nginx:1.2.1
|
||||
`)}},
|
||||
Filters: []kio.Filter{LegacyFilter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
Digest: "12345",
|
||||
},
|
||||
}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: FooBar
|
||||
// image: apache@12345
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: BarFoo
|
||||
// image: apache@12345
|
||||
}
|
||||
44
api/filters/imagetag/imagetag.go
Normal file
44
api/filters/imagetag/imagetag.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
// imageTag is the tag we want to apply to the inputs
|
||||
ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
|
||||
|
||||
// FsSlice contains the FieldSpecs to locate the namespace field
|
||||
FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
_, err := kio.FilterAll(yaml.FilterFunc(f.filter)).Filter(nodes)
|
||||
return nodes, err
|
||||
}
|
||||
|
||||
func (f Filter) filter(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
if err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: updateImageTagFn(f.ImageTag),
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func updateImageTagFn(imageTag types.Image) fsslice.SetFn {
|
||||
return func(node *yaml.RNode) error {
|
||||
return node.PipeE(imageTagUpdater{
|
||||
ImageTag: imageTag,
|
||||
})
|
||||
}
|
||||
}
|
||||
101
api/filters/imagetag/imagetag_test.go
Normal file
101
api/filters/imagetag/imagetag_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestImageTagUpdater_Filter(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
filter Filter
|
||||
fsSlice types.FsSlice
|
||||
}{
|
||||
"update with digest": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: nginx:1.2.1
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: apache@12345
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
Digest: "12345",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/image",
|
||||
},
|
||||
},
|
||||
},
|
||||
"multiple matches in sequence": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.2.1
|
||||
- image: not_nginx@54321
|
||||
- image: nginx:1.2.1
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: apache:3.2.1
|
||||
- image: not_nginx@54321
|
||||
- image: apache:3.2.1
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
NewTag: "3.2.1",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/containers/image",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
filter := tc.filter
|
||||
filter.FsSlice = tc.fsSlice
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
113
api/filters/imagetag/legacy.go
Normal file
113
api/filters/imagetag/legacy.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// LegacyFilter is an implementation of the kio.Filter interface
|
||||
// that scans through the provided kyaml data structure and updates
|
||||
// any values of any image fields that is inside a sequence under
|
||||
// a field called either containers or initContainers. The field is only
|
||||
// update if it has a value that matches and image reference and the name
|
||||
// of the image is a match with the provided ImageTag.
|
||||
type LegacyFilter struct {
|
||||
ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = LegacyFilter{}
|
||||
|
||||
func (lf LegacyFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(lf.filter)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (lf LegacyFilter) filter(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
meta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We do not make any changes if the type of the resource
|
||||
// is CustomResourceDefinition.
|
||||
if meta.Kind == `CustomResourceDefinition` {
|
||||
return node, nil
|
||||
}
|
||||
|
||||
fff := findFieldsFilter{
|
||||
fields: []string{"containers", "initContainers"},
|
||||
fieldCallback: checkImageTagsFn(lf.ImageTag),
|
||||
}
|
||||
if err := node.PipeE(fff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
type fieldCallback func(node *yaml.RNode) error
|
||||
|
||||
// findFieldsFilter is an implementation of the kio.Filter
|
||||
// interface. It will walk the data structure and look for fields
|
||||
// that matches the provided list of field names. For each match,
|
||||
// the value of the field will be passed in as a parameter to the
|
||||
// provided fieldCallback.
|
||||
// TODO: move this to kyaml/filterutils
|
||||
type findFieldsFilter struct {
|
||||
fields []string
|
||||
|
||||
fieldCallback fieldCallback
|
||||
}
|
||||
|
||||
func (f findFieldsFilter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
||||
return obj, f.walk(obj)
|
||||
}
|
||||
|
||||
func (f findFieldsFilter) walk(node *yaml.RNode) error {
|
||||
switch node.YNode().Kind {
|
||||
case yaml.MappingNode:
|
||||
return node.VisitFields(func(n *yaml.MapNode) error {
|
||||
err := f.walk(n.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := n.Key.YNode().Value
|
||||
if contains(f.fields, key) {
|
||||
return f.fieldCallback(n.Value)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
case yaml.SequenceNode:
|
||||
return node.VisitElements(func(n *yaml.RNode) error {
|
||||
return f.walk(n)
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(slice []string, str string) bool {
|
||||
for _, s := range slice {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkImageTagsFn(imageTag types.Image) fieldCallback {
|
||||
return func(node *yaml.RNode) error {
|
||||
if node.YNode().Kind != yaml.SequenceNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
return node.VisitElements(func(n *yaml.RNode) error {
|
||||
// Look up any fields on the provided node that is named
|
||||
// image.
|
||||
return n.PipeE(yaml.Get("image"), imageTagUpdater{
|
||||
ImageTag: imageTag,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
136
api/filters/imagetag/legacy_test.go
Normal file
136
api/filters/imagetag/legacy_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestLegacyImageTag_Filter(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
filter LegacyFilter
|
||||
}{
|
||||
"updates multiple images inside containers": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.2.1
|
||||
- image: nginx:2.1.2
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: apache@12345
|
||||
- image: apache@12345
|
||||
`,
|
||||
filter: LegacyFilter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
Digest: "12345",
|
||||
},
|
||||
},
|
||||
},
|
||||
"updates inside both containers and initContainers": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.2.1
|
||||
- image: tomcat:1.2.3
|
||||
initContainers:
|
||||
- image: nginx:1.2.1
|
||||
- image: apache:1.2.3
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: apache:3.2.1
|
||||
- image: tomcat:1.2.3
|
||||
initContainers:
|
||||
- image: apache:3.2.1
|
||||
- image: apache:1.2.3
|
||||
`,
|
||||
filter: LegacyFilter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
NewTag: "3.2.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
"updates on multiple depths": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.2.1
|
||||
- image: tomcat:1.2.3
|
||||
template:
|
||||
spec:
|
||||
initContainers:
|
||||
- image: nginx:1.2.1
|
||||
- image: apache:1.2.3
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: apache:3.2.1
|
||||
- image: tomcat:1.2.3
|
||||
template:
|
||||
spec:
|
||||
initContainers:
|
||||
- image: apache:3.2.1
|
||||
- image: apache:1.2.3
|
||||
`,
|
||||
filter: LegacyFilter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
NewTag: "3.2.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
filter := tc.filter
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
43
api/filters/imagetag/updater.go
Normal file
43
api/filters/imagetag/updater.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/image"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// imageTagUpdater is an implementation of the kio.Filter interface
|
||||
// that will update the value of the yaml node based on the provided
|
||||
// ImageTag if the current value matches the format of an image reference.
|
||||
type imageTagUpdater struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
ImageTag types.Image `yaml:"imageTag,omitempty"`
|
||||
}
|
||||
|
||||
func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) {
|
||||
if err := yaml.ErrorIfInvalid(rn, yaml.ScalarNode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value := rn.YNode().Value
|
||||
|
||||
if !image.IsImageMatched(value, u.ImageTag.Name) {
|
||||
return rn, nil
|
||||
}
|
||||
|
||||
name, tag := image.Split(value)
|
||||
if u.ImageTag.NewName != "" {
|
||||
name = u.ImageTag.NewName
|
||||
}
|
||||
if u.ImageTag.NewTag != "" {
|
||||
tag = ":" + u.ImageTag.NewTag
|
||||
}
|
||||
if u.ImageTag.Digest != "" {
|
||||
tag = "@" + u.ImageTag.Digest
|
||||
}
|
||||
|
||||
return rn.Pipe(yaml.FieldSetter{StringValue: name + tag})
|
||||
}
|
||||
6
api/filters/patchjson6902/doc.go
Normal file
6
api/filters/patchjson6902/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package namespace contains a kio.Filter implementation of the kustomize
|
||||
// patchjson6902 transformer
|
||||
package patchjson6902
|
||||
55
api/filters/patchjson6902/example_test.go
Normal file
55
api/filters/patchjson6902/example_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchjson6902
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
namespace: bar
|
||||
`)}},
|
||||
Filters: []kio.Filter{
|
||||
Filter{
|
||||
Patch: `
|
||||
- op: replace
|
||||
path: /metadata/namespace
|
||||
value: "ns"
|
||||
`,
|
||||
},
|
||||
},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// namespace: ns
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: instance
|
||||
// namespace: ns
|
||||
}
|
||||
65
api/filters/patchjson6902/patchjson6902.go
Normal file
65
api/filters/patchjson6902/patchjson6902.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchjson6902
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
k8syaml "sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
Patch string
|
||||
|
||||
decodedPatch jsonpatch.Patch
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
decodedPatch, err := pf.decodePatch()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pf.decodedPatch = decodedPatch
|
||||
return kio.FilterAll(yaml.FilterFunc(pf.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (pf Filter) decodePatch() (jsonpatch.Patch, error) {
|
||||
patch := pf.Patch
|
||||
// If the patch doesn't look like a JSON6902 patch, we
|
||||
// try to parse it to json.
|
||||
if !strings.HasPrefix(pf.Patch, "[") {
|
||||
p, err := k8syaml.YAMLToJSON([]byte(patch))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patch = string(p)
|
||||
}
|
||||
decodedPatch, err := jsonpatch.DecodePatch([]byte(patch))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decodedPatch, nil
|
||||
}
|
||||
|
||||
func (pf Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
// We don't actually use the kyaml library for manipulating the
|
||||
// yaml here. We just marshal it to json and rely on the
|
||||
// jsonpatch library to take care of applying the patch.
|
||||
// This means ordering might not be preserved with this filter.
|
||||
b, err := node.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := pf.decodedPatch.Apply(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = node.UnmarshalJSON(res)
|
||||
return node, err
|
||||
}
|
||||
173
api/filters/patchjson6902/patchjson6902_test.go
Normal file
173
api/filters/patchjson6902/patchjson6902_test.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchjson6902
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
)
|
||||
|
||||
const input = `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 2
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
`
|
||||
|
||||
func TestSomething(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
input string
|
||||
filter Filter
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
testName: "single operation, json",
|
||||
input: input,
|
||||
filter: Filter{
|
||||
Patch: `[
|
||||
{"op": "replace", "path": "/spec/replica", "value": 5}
|
||||
]`,
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 5
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
{
|
||||
testName: "multiple operations, json",
|
||||
input: input,
|
||||
filter: Filter{
|
||||
Patch: `[
|
||||
{"op": "replace", "path": "/spec/template/spec/containers/0/name", "value": "my-nginx"},
|
||||
{"op": "add", "path": "/spec/replica", "value": 999},
|
||||
{"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["arg1", "arg2", "arg3"]}
|
||||
]`,
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 999
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- arg1
|
||||
- arg2
|
||||
- arg3
|
||||
image: nginx
|
||||
name: my-nginx
|
||||
`,
|
||||
},
|
||||
{
|
||||
testName: "single operation, yaml",
|
||||
input: input,
|
||||
filter: Filter{
|
||||
Patch: `
|
||||
- op: replace
|
||||
path: /spec/replica
|
||||
value: 5
|
||||
`,
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 5
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
{
|
||||
testName: "multiple operations, yaml",
|
||||
input: input,
|
||||
filter: Filter{
|
||||
Patch: `
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/name
|
||||
value: my-nginx
|
||||
- op: add
|
||||
path: /spec/replica
|
||||
value: 999
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/command
|
||||
value:
|
||||
- arg1
|
||||
- arg2
|
||||
- arg3
|
||||
`,
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 999
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- arg1
|
||||
- arg2
|
||||
- arg3
|
||||
image: nginx
|
||||
name: my-nginx
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(
|
||||
filtertest.RunFilter(t, tc.input, tc.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
6
api/filters/patchstrategicmerge/doc.go
Normal file
6
api/filters/patchstrategicmerge/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package patchstrategicmerge contains a kio.Filter implementation of the
|
||||
// kustomize strategic merge patch transformer.
|
||||
package patchstrategicmerge
|
||||
49
api/filters/patchstrategicmerge/example_test.go
Normal file
49
api/filters/patchstrategicmerge/example_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchstrategicmerge
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
replicas: 3
|
||||
`)}},
|
||||
Filters: []kio.Filter{Filter{
|
||||
Patch: yaml.MustParse(`
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- image: nginx
|
||||
`),
|
||||
}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// replicas: 3
|
||||
// template:
|
||||
// containers:
|
||||
// - image: nginx
|
||||
}
|
||||
21
api/filters/patchstrategicmerge/patchstrategicmerge.go
Normal file
21
api/filters/patchstrategicmerge/patchstrategicmerge.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package patchstrategicmerge
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
Patch *yaml.RNode
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(pf.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (pf Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
return merge2.Merge(pf.Patch, node)
|
||||
}
|
||||
82
api/filters/patchstrategicmerge/patchstrategicmerge_test.go
Normal file
82
api/filters/patchstrategicmerge/patchstrategicmerge_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package patchstrategicmerge
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
patch *yaml.RNode
|
||||
expected string
|
||||
}{
|
||||
"simple patch": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: myDeploy
|
||||
kind: Deployment
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
metadata:
|
||||
name: yourDeploy
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: yourDeploy
|
||||
kind: Deployment
|
||||
`,
|
||||
},
|
||||
"nested patch": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: myDeploy
|
||||
kind: Deployment
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
args:
|
||||
- abc
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
args:
|
||||
- def
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: myDeploy
|
||||
kind: Deployment
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
args:
|
||||
- def
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
f := Filter{
|
||||
Patch: tc.patch,
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest.RunFilter(t, tc.input, f))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,6 @@ require (
|
||||
k8s.io/apimachinery v0.17.0
|
||||
k8s.io/client-go v0.17.0
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
@@ -513,6 +513,8 @@ sigs.k8s.io/kustomize/kyaml v0.1.1/go.mod h1:/NdPPfrperSCGjm55cwEro1loBVtbtVIXSb
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.2 h1:l12+QGl+ETUHhP8/bZAi6TknU7H194fXL/9b2gUxZFY=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3 h1:zbeHVTMCQPtWgjIH/YYJZC45mm7coTdw2TblyJ79BrY=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5 h1:NicBWYTwkuOfVyZDbNkfSBSCwSgin4uirkedtyZltIc=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
@@ -75,14 +75,8 @@ type KunstructuredFactory interface {
|
||||
SliceFromBytes([]byte) ([]Kunstructured, error)
|
||||
FromMap(m map[string]interface{}) Kunstructured
|
||||
Hasher() KunstructuredHasher
|
||||
MakeConfigMap(
|
||||
kvLdr KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.ConfigMapArgs) (Kunstructured, error)
|
||||
MakeSecret(
|
||||
kvLdr KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.SecretArgs) (Kunstructured, error)
|
||||
MakeConfigMap(kvLdr KvLoader, args *types.ConfigMapArgs) (Kunstructured, error)
|
||||
MakeSecret(kvLdr KvLoader, args *types.SecretArgs) (Kunstructured, error)
|
||||
}
|
||||
|
||||
// KunstructuredHasher returns a hash of the argument
|
||||
|
||||
50
api/image/image.go
Normal file
50
api/image/image.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsImageMatched returns true if the value of t is identical to the
|
||||
// image name in the full image name and tag as given by s.
|
||||
func IsImageMatched(s, t string) bool {
|
||||
// Tag values are limited to [a-zA-Z0-9_.{}-].
|
||||
// Some tools like Bazel rules_k8s allow tag patterns with {} characters.
|
||||
// More info: https://github.com/bazelbuild/rules_k8s/pull/423
|
||||
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.{}-]*)?$")
|
||||
return pattern.MatchString(s)
|
||||
}
|
||||
|
||||
// Split separates and returns the name and tag parts
|
||||
// from the image string using either colon `:` or at `@` separators.
|
||||
// Note that the returned tag keeps its separator.
|
||||
func Split(imageName string) (name string, tag string) {
|
||||
// check if image name contains a domain
|
||||
// if domain is present, ignore domain and check for `:`
|
||||
ic := -1
|
||||
if slashIndex := strings.Index(imageName, "/"); slashIndex < 0 {
|
||||
ic = strings.LastIndex(imageName, ":")
|
||||
} else {
|
||||
lastIc := strings.LastIndex(imageName[slashIndex:], ":")
|
||||
// set ic only if `:` is present
|
||||
if lastIc > 0 {
|
||||
ic = slashIndex + lastIc
|
||||
}
|
||||
}
|
||||
ia := strings.LastIndex(imageName, "@")
|
||||
if ic < 0 && ia < 0 {
|
||||
return imageName, ""
|
||||
}
|
||||
|
||||
i := ic
|
||||
if ia > 0 {
|
||||
i = ia
|
||||
}
|
||||
|
||||
name = imageName[:i]
|
||||
tag = imageName[i:]
|
||||
return
|
||||
}
|
||||
80
api/image/image_test.go
Normal file
80
api/image/image_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsImageMatched(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
value string
|
||||
name string
|
||||
isMatched bool
|
||||
}{
|
||||
{
|
||||
testName: "identical",
|
||||
value: "nginx",
|
||||
name: "nginx",
|
||||
isMatched: true,
|
||||
},
|
||||
{
|
||||
testName: "name is match",
|
||||
value: "nginx:12345",
|
||||
name: "nginx",
|
||||
isMatched: true,
|
||||
},
|
||||
{
|
||||
testName: "name is not a match",
|
||||
value: "apache:12345",
|
||||
name: "nginx",
|
||||
isMatched: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
assert.Equal(t, tc.isMatched, IsImageMatched(tc.value, tc.name))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
value string
|
||||
name string
|
||||
tag string
|
||||
}{
|
||||
{
|
||||
testName: "no tag",
|
||||
value: "nginx",
|
||||
name: "nginx",
|
||||
tag: "",
|
||||
},
|
||||
{
|
||||
testName: "with tag",
|
||||
value: "nginx:1.2.3",
|
||||
name: "nginx",
|
||||
tag: ":1.2.3",
|
||||
},
|
||||
{
|
||||
testName: "with digest",
|
||||
value: "nginx@12345",
|
||||
name: "nginx",
|
||||
tag: "@12345",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
name, tag := Split(tc.value)
|
||||
assert.Equal(t, tc.name, name)
|
||||
assert.Equal(t, tc.tag, tag)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,7 @@ func makeFreshConfigMap(
|
||||
}
|
||||
|
||||
// MakeConfigMap returns a new ConfigMap, or nil and an error.
|
||||
func (f *Factory) MakeConfigMap(
|
||||
args *types.ConfigMapArgs) (*corev1.ConfigMap, error) {
|
||||
func (f *Factory) MakeConfigMap(args *types.ConfigMapArgs) (*corev1.ConfigMap, error) {
|
||||
all, err := f.kvLdr.Load(args.KvPairSources)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "loading KV pairs")
|
||||
@@ -38,7 +37,7 @@ func (f *Factory) MakeConfigMap(
|
||||
return nil, errors.Wrap(err, "trouble mapping")
|
||||
}
|
||||
}
|
||||
f.setLabelsAndAnnnotations(cm, args.GeneratorOptions)
|
||||
f.copyLabelsAndAnnotations(cm, args.Options)
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
input types.ConfigMapArgs
|
||||
options *types.GeneratorOptions
|
||||
expected *corev1.ConfigMap
|
||||
}
|
||||
|
||||
@@ -99,7 +98,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: nil,
|
||||
expected: makeEnvConfigMap("envConfigMap"),
|
||||
},
|
||||
{
|
||||
@@ -115,7 +113,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: nil,
|
||||
expected: makeFileConfigMap("fileConfigMap"),
|
||||
},
|
||||
{
|
||||
@@ -126,11 +123,11 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
KvPairSources: types.KvPairSources{
|
||||
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||
},
|
||||
},
|
||||
},
|
||||
options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
Options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: makeLiteralConfigMap("literalConfigMap", map[string]string{
|
||||
@@ -145,7 +142,7 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
KvPairSources: types.KvPairSources{
|
||||
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||
},
|
||||
GeneratorOptions: &types.GeneratorOptions{
|
||||
Options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
@@ -157,18 +154,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
// GeneratorOptions from the ConfigMapArgs take precedence over the
|
||||
// factory level GeneratorOptions and should overwrite
|
||||
// labels/annotations set in the factory level if there are common
|
||||
// labels/annotations
|
||||
expected: makeLiteralConfigMap("literalConfigMap", map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
@@ -193,8 +178,7 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
loader.NewFileLoaderAtRoot(fSys),
|
||||
valtest_test.MakeFakeValidator())
|
||||
for _, tc := range testCases {
|
||||
f := NewFactory(kvLdr, tc.options)
|
||||
cm, err := f.MakeConfigMap(&tc.input)
|
||||
cm, err := NewFactory(kvLdr).MakeConfigMap(&tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
@@ -11,44 +11,26 @@ import (
|
||||
|
||||
// Factory makes ConfigMaps and Secrets.
|
||||
type Factory struct {
|
||||
kvLdr ifc.KvLoader
|
||||
options *types.GeneratorOptions
|
||||
kvLdr ifc.KvLoader
|
||||
}
|
||||
|
||||
// NewFactory returns a new factory that makes ConfigMaps and Secrets.
|
||||
func NewFactory(
|
||||
kvLdr ifc.KvLoader, o *types.GeneratorOptions) *Factory {
|
||||
return &Factory{kvLdr: kvLdr, options: o}
|
||||
func NewFactory(kvLdr ifc.KvLoader) *Factory {
|
||||
return &Factory{kvLdr: kvLdr}
|
||||
}
|
||||
|
||||
// setLabelsAndAnnnotations will take the labels and annotations from
|
||||
// global GeneratorOptions and resource level GeneratorOptions and merge them
|
||||
// with the resource level taking precedence, and then set them on the provided
|
||||
// obj.
|
||||
func (f *Factory) setLabelsAndAnnnotations(obj metav1.Object, opts *types.GeneratorOptions) {
|
||||
labels := make(map[string]string)
|
||||
annotations := make(map[string]string)
|
||||
if f.options != nil {
|
||||
for k, v := range f.options.Labels {
|
||||
labels[k] = v
|
||||
}
|
||||
for k, v := range f.options.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
// copyLabelsAndAnnotations copies labels and annotations from
|
||||
// GeneratorOptions into the given object.
|
||||
func (f *Factory) copyLabelsAndAnnotations(
|
||||
obj metav1.Object, opts *types.GeneratorOptions) {
|
||||
if opts == nil {
|
||||
return
|
||||
}
|
||||
if opts != nil {
|
||||
for k, v := range opts.Labels {
|
||||
labels[k] = v
|
||||
}
|
||||
for k, v := range opts.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
if opts.Labels != nil {
|
||||
obj.SetLabels(types.CopyMap(opts.Labels))
|
||||
}
|
||||
if len(labels) != 0 {
|
||||
obj.SetLabels(labels)
|
||||
}
|
||||
if len(annotations) != 0 {
|
||||
obj.SetAnnotations(annotations)
|
||||
if opts.Annotations != nil {
|
||||
obj.SetAnnotations(types.CopyMap(opts.Annotations))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ func makeFreshSecret(
|
||||
}
|
||||
|
||||
// MakeSecret returns a new secret.
|
||||
func (f *Factory) MakeSecret(
|
||||
args *types.SecretArgs) (*corev1.Secret, error) {
|
||||
func (f *Factory) MakeSecret(args *types.SecretArgs) (*corev1.Secret, error) {
|
||||
all, err := f.kvLdr.Load(args.KvPairSources)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -39,7 +38,7 @@ func (f *Factory) MakeSecret(
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
f.setLabelsAndAnnnotations(s, args.GeneratorOptions)
|
||||
f.copyLabelsAndAnnotations(s, args.Options)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ func TestConstructSecret(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
input types.SecretArgs
|
||||
options *types.GeneratorOptions
|
||||
expected *corev1.Secret
|
||||
}
|
||||
|
||||
@@ -94,7 +93,6 @@ func TestConstructSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: nil,
|
||||
expected: makeEnvSecret("envSecret"),
|
||||
},
|
||||
{
|
||||
@@ -107,7 +105,6 @@ func TestConstructSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: nil,
|
||||
expected: makeFileSecret("fileSecret"),
|
||||
},
|
||||
{
|
||||
@@ -118,55 +115,22 @@ func TestConstructSecret(t *testing.T) {
|
||||
KvPairSources: types.KvPairSources{
|
||||
LiteralSources: []string{"a=x", "b=y"},
|
||||
},
|
||||
},
|
||||
},
|
||||
options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
Options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"fruit": "banana",
|
||||
"pet": "dog",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: makeLiteralSecret("literalSecret", map[string]string{
|
||||
"foo": "bar",
|
||||
}, nil),
|
||||
},
|
||||
{
|
||||
description: "construct secret from literal with GeneratorOptions in SecretArgs",
|
||||
input: types.SecretArgs{
|
||||
GeneratorArgs: types.GeneratorArgs{
|
||||
Name: "literalSecret",
|
||||
KvPairSources: types.KvPairSources{
|
||||
LiteralSources: []string{"a=x", "b=y"},
|
||||
},
|
||||
GeneratorOptions: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
// GeneratorOptions from the SecretArgs take precedence over the
|
||||
// factory level GeneratorOptions and should overwrite
|
||||
// labels/annotations set in the factory level if there are common
|
||||
// labels/annotations
|
||||
expected: makeLiteralSecret("literalSecret", map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
}, map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
"fruit": "banana",
|
||||
"pet": "dog",
|
||||
}),
|
||||
},
|
||||
}
|
||||
@@ -178,7 +142,7 @@ func TestConstructSecret(t *testing.T) {
|
||||
loader.NewFileLoaderAtRoot(fSys),
|
||||
valtest_test.MakeFakeValidator())
|
||||
for _, tc := range testCases {
|
||||
f := NewFactory(kvLdr, tc.options)
|
||||
f := NewFactory(kvLdr)
|
||||
cm, err := f.MakeSecret(&tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
||||
@@ -266,8 +266,9 @@ func (p *ExecPlugin) UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, err
|
||||
}
|
||||
r.SetAnnotations(annotations)
|
||||
r.SetOptions(types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: behavior},
|
||||
&types.GeneratorOptions{DisableNameSuffixHash: !needsHash}))
|
||||
&types.GeneratorArgs{
|
||||
Behavior: behavior,
|
||||
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
|
||||
}
|
||||
return rm, nil
|
||||
}
|
||||
|
||||
@@ -116,7 +116,9 @@ func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHa
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{"name": name},
|
||||
}, &types.GeneratorArgs{Behavior: behavior}, &types.GeneratorOptions{DisableNameSuffixHash: disableHash})
|
||||
}, &types.GeneratorArgs{
|
||||
Behavior: behavior,
|
||||
Options: &types.GeneratorOptions{DisableNameSuffixHash: disableHash}})
|
||||
}
|
||||
|
||||
func strptr(s string) *string {
|
||||
|
||||
@@ -74,14 +74,12 @@ var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func(
|
||||
builtinhelpers.SecretGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
|
||||
result []resmap.Generator, err error) {
|
||||
var c struct {
|
||||
types.GeneratorOptions
|
||||
types.SecretArgs
|
||||
}
|
||||
if kt.kustomization.GeneratorOptions != nil {
|
||||
c.GeneratorOptions = *kt.kustomization.GeneratorOptions
|
||||
}
|
||||
for _, args := range kt.kustomization.SecretGenerator {
|
||||
c.SecretArgs = args
|
||||
c.SecretArgs.Options = types.MergeGlobalOptionsIntoLocal(
|
||||
c.SecretArgs.Options, kt.kustomization.GeneratorOptions)
|
||||
p := f()
|
||||
err := kt.configureBuiltinPlugin(p, c, bpt)
|
||||
if err != nil {
|
||||
@@ -95,14 +93,12 @@ var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func(
|
||||
builtinhelpers.ConfigMapGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
|
||||
result []resmap.Generator, err error) {
|
||||
var c struct {
|
||||
types.GeneratorOptions
|
||||
types.ConfigMapArgs
|
||||
}
|
||||
if kt.kustomization.GeneratorOptions != nil {
|
||||
c.GeneratorOptions = *kt.kustomization.GeneratorOptions
|
||||
}
|
||||
for _, args := range kt.kustomization.ConfigMapGenerator {
|
||||
c.ConfigMapArgs = args
|
||||
c.ConfigMapArgs.Options = types.MergeGlobalOptionsIntoLocal(
|
||||
c.ConfigMapArgs.Options, kt.kustomization.GeneratorOptions)
|
||||
p := f()
|
||||
err := kt.configureBuiltinPlugin(p, c, bpt)
|
||||
if err != nil {
|
||||
|
||||
@@ -75,11 +75,8 @@ func (kf *KunstructuredFactoryImpl) FromMap(
|
||||
|
||||
// MakeConfigMap returns an instance of Kunstructured for ConfigMap
|
||||
func (kf *KunstructuredFactoryImpl) MakeConfigMap(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
|
||||
o, err := configmapandsecret.NewFactory(
|
||||
kvLdr, options).MakeConfigMap(args)
|
||||
kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
|
||||
o, err := configmapandsecret.NewFactory(kvLdr).MakeConfigMap(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -88,11 +85,8 @@ func (kf *KunstructuredFactoryImpl) MakeConfigMap(
|
||||
|
||||
// MakeSecret returns an instance of Kunstructured for Secret
|
||||
func (kf *KunstructuredFactoryImpl) MakeSecret(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.SecretArgs) (ifc.Kunstructured, error) {
|
||||
o, err := configmapandsecret.NewFactory(
|
||||
kvLdr, options).MakeSecret(args)
|
||||
kvLdr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
|
||||
o, err := configmapandsecret.NewFactory(kvLdr).MakeSecret(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ func (fs *UnstructAdapter) selectSubtree(path string) (map[string]interface{}, [
|
||||
func (fs *UnstructAdapter) GetFieldValue(path string) (interface{}, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedFieldNoCopy(
|
||||
@@ -141,14 +141,14 @@ func (fs *UnstructAdapter) GetFieldValue(path string) (interface{}, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetString returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetString(path string) (string, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return "", noFieldError{Field: path}
|
||||
return "", NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedString(
|
||||
@@ -156,14 +156,14 @@ func (fs *UnstructAdapter) GetString(path string) (string, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return "", noFieldError{Field: path}
|
||||
return "", NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetStringSlice returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetStringSlice(path string) ([]string, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return []string{}, noFieldError{Field: path}
|
||||
return []string{}, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedStringSlice(
|
||||
@@ -171,14 +171,14 @@ func (fs *UnstructAdapter) GetStringSlice(path string) ([]string, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return []string{}, noFieldError{Field: path}
|
||||
return []string{}, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetBool returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetBool(path string) (bool, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return false, noFieldError{Field: path}
|
||||
return false, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedBool(
|
||||
@@ -186,7 +186,7 @@ func (fs *UnstructAdapter) GetBool(path string) (bool, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return false, noFieldError{Field: path}
|
||||
return false, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetFloat64 returns value at the given fieldpath.
|
||||
@@ -201,14 +201,14 @@ func (fs *UnstructAdapter) GetFloat64(path string) (float64, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return 0, noFieldError{Field: path}
|
||||
return 0, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetInt64 returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetInt64(path string) (int64, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return 0, noFieldError{Field: path}
|
||||
return 0, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedInt64(
|
||||
@@ -216,14 +216,14 @@ func (fs *UnstructAdapter) GetInt64(path string) (int64, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return 0, noFieldError{Field: path}
|
||||
return 0, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetSlice returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetSlice(path string) ([]interface{}, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedSlice(
|
||||
@@ -231,14 +231,14 @@ func (fs *UnstructAdapter) GetSlice(path string) ([]interface{}, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetStringMap returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetStringMap(path string) (map[string]string, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedStringMap(
|
||||
@@ -246,14 +246,14 @@ func (fs *UnstructAdapter) GetStringMap(path string) (map[string]string, error)
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetMap returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetMap(path string) (map[string]interface{}, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedMap(
|
||||
@@ -261,7 +261,7 @@ func (fs *UnstructAdapter) GetMap(path string) (map[string]interface{}, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
func (fs *UnstructAdapter) MatchesLabelSelector(selector string) (bool, error) {
|
||||
@@ -340,11 +340,11 @@ func toSchemaGvk(x resid.Gvk) schema.GroupVersionKind {
|
||||
}
|
||||
}
|
||||
|
||||
// noFieldError is returned when a field is expected, but missing.
|
||||
type noFieldError struct {
|
||||
// NoFieldError is returned when a field is expected, but missing.
|
||||
type NoFieldError struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
func (e noFieldError) Error() string {
|
||||
func (e NoFieldError) Error() string {
|
||||
return fmt.Sprintf("no field named '%s'", e.Field)
|
||||
}
|
||||
|
||||
@@ -345,5 +345,24 @@ nameReference:
|
||||
kind: PersistentVolumeClaim
|
||||
- path: spec/volumeClaimTemplates/spec/storageClassName
|
||||
kind: StatefulSet
|
||||
|
||||
- kind: PriorityClass
|
||||
version: v1
|
||||
group: scheduling.k8s.io
|
||||
fieldSpecs:
|
||||
- path: spec/priorityClassName
|
||||
kind: Pod
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: StatefulSet
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: Deployment
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: ReplicationController
|
||||
- path: spec/jobTemplate/spec/template/spec/priorityClassName
|
||||
kind: CronJob
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: Job
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: DaemonSet
|
||||
`
|
||||
)
|
||||
|
||||
@@ -143,7 +143,8 @@ apiVersion: builtin
|
||||
kind: ConfigMapGenerator
|
||||
metadata:
|
||||
name: my-config
|
||||
disableNameSuffixHash: true
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
literals:
|
||||
- MY_ENV=foo
|
||||
`)
|
||||
|
||||
@@ -12,10 +12,12 @@ import (
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
)
|
||||
|
||||
func findSecret(m resmap.ResMap) *resource.Resource {
|
||||
func findSecret(m resmap.ResMap, prefix string) *resource.Resource {
|
||||
for _, r := range m.Resources() {
|
||||
if r.OrgId().Kind == "Secret" {
|
||||
return r
|
||||
if prefix == "" || strings.HasPrefix(r.GetName(), prefix) {
|
||||
return r
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -77,7 +79,7 @@ metadata:
|
||||
|
||||
m := th.Run("/whatever", th.MakeDefaultOptions())
|
||||
|
||||
secret := findSecret(m)
|
||||
secret := findSecret(m, "")
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
@@ -90,7 +92,7 @@ metadata:
|
||||
"disableNameSuffixHash: false",
|
||||
"disableNameSuffixHash: true", -1))
|
||||
m = th.Run("/whatever", th.MakeDefaultOptions())
|
||||
secret = findSecret(m)
|
||||
secret = findSecret(m, "")
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
@@ -98,3 +100,47 @@ metadata:
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
}
|
||||
func TestDisableNameSuffixHashPerObject(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
const kustomizationContent = `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
generatorOptions:
|
||||
disableNameSuffixHash: false
|
||||
secretGenerator:
|
||||
- name: nohash
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
literals:
|
||||
- DB_USERNAME=admin
|
||||
- DB_PASSWORD=somepw
|
||||
type: Opaque
|
||||
- name: yeshash
|
||||
options:
|
||||
disableNameSuffixHash: false
|
||||
literals:
|
||||
- DB_USERNAME=admin
|
||||
- DB_PASSWORD=somepw
|
||||
type: Opaque
|
||||
`
|
||||
|
||||
th.WriteK("/whatever", kustomizationContent)
|
||||
|
||||
m := th.Run("/whatever", th.MakeDefaultOptions())
|
||||
|
||||
secret := findSecret(m, "nohash")
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
if secret.GetName() != "nohash" {
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
|
||||
secret = findSecret(m, "yeshash")
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
if secret.GetName() != "yeshash-mcgcmdcm69" {
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,12 +66,10 @@ func (rmF *Factory) NewResMapFromBytes(b []byte) (ResMap, error) {
|
||||
// NewResMapFromConfigMapArgs returns a Resource slice given
|
||||
// a configmap metadata slice from kustomization file.
|
||||
func (rmF *Factory) NewResMapFromConfigMapArgs(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
argList []types.ConfigMapArgs) (ResMap, error) {
|
||||
kvLdr ifc.KvLoader, argList []types.ConfigMapArgs) (ResMap, error) {
|
||||
var resources []*resource.Resource
|
||||
for _, args := range argList {
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, options, &args)
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewResMapFromConfigMapArgs")
|
||||
}
|
||||
@@ -81,10 +79,8 @@ func (rmF *Factory) NewResMapFromConfigMapArgs(
|
||||
}
|
||||
|
||||
func (rmF *Factory) FromConfigMapArgs(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args types.ConfigMapArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, options, &args)
|
||||
kvLdr ifc.KvLoader, args types.ConfigMapArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -94,12 +90,10 @@ func (rmF *Factory) FromConfigMapArgs(
|
||||
// NewResMapFromSecretArgs takes a SecretArgs slice, generates
|
||||
// secrets from each entry, and accumulates them in a ResMap.
|
||||
func (rmF *Factory) NewResMapFromSecretArgs(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
argsList []types.SecretArgs) (ResMap, error) {
|
||||
kvLdr ifc.KvLoader, argsList []types.SecretArgs) (ResMap, error) {
|
||||
var resources []*resource.Resource
|
||||
for _, args := range argsList {
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, options, &args)
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewResMapFromSecretArgs")
|
||||
}
|
||||
@@ -109,10 +103,8 @@ func (rmF *Factory) NewResMapFromSecretArgs(
|
||||
}
|
||||
|
||||
func (rmF *Factory) FromSecretArgs(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args types.SecretArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, options, &args)
|
||||
kvLdr ifc.KvLoader, args types.SecretArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ BAR=baz
|
||||
t.Fatalf("error adding file '%s': %v\n", tc.filepath, fErr)
|
||||
}
|
||||
}
|
||||
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, nil, tc.input)
|
||||
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -258,7 +258,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
|
||||
actual, err := rmF.NewResMapFromSecretArgs(
|
||||
kv.NewLoader(
|
||||
loader.NewFileLoaderAtRoot(fSys),
|
||||
valtest_test.MakeFakeValidator()), nil, secrets)
|
||||
valtest_test.MakeFakeValidator()), secrets)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
@@ -826,7 +826,7 @@ func makeMap1() ResMap {
|
||||
},
|
||||
}, &types.GeneratorArgs{
|
||||
Behavior: "create",
|
||||
}, nil))
|
||||
}))
|
||||
}
|
||||
|
||||
func makeMap2(b types.GenerationBehavior) ResMap {
|
||||
@@ -844,7 +844,7 @@ func makeMap2(b types.GenerationBehavior) ResMap {
|
||||
},
|
||||
}, &types.GeneratorArgs{
|
||||
Behavior: b.String(),
|
||||
}, nil))
|
||||
}))
|
||||
}
|
||||
|
||||
func TestAbsorbAll(t *testing.T) {
|
||||
@@ -864,7 +864,7 @@ func TestAbsorbAll(t *testing.T) {
|
||||
},
|
||||
}, &types.GeneratorArgs{
|
||||
Behavior: "create",
|
||||
}, nil))
|
||||
}))
|
||||
w := makeMap1()
|
||||
if err := w.AbsorbAll(makeMap2(types.BehaviorMerge)); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
||||
@@ -50,8 +50,8 @@ func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string
|
||||
|
||||
// FromMapAndOption returns a new instance of Resource with given options.
|
||||
func (rf *Factory) FromMapAndOption(
|
||||
m map[string]interface{}, args *types.GeneratorArgs, option *types.GeneratorOptions) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args, option))
|
||||
m map[string]interface{}, args *types.GeneratorArgs) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args))
|
||||
}
|
||||
|
||||
// FromKunstructured returns a new instance of Resource.
|
||||
@@ -66,7 +66,7 @@ func (rf *Factory) makeOne(
|
||||
log.Fatal("unstruct ifc must not be null")
|
||||
}
|
||||
if o == nil {
|
||||
o = types.NewGenArgs(nil, nil)
|
||||
o = types.NewGenArgs(nil)
|
||||
}
|
||||
r := &Resource{
|
||||
Kunstructured: u,
|
||||
@@ -147,33 +147,19 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
||||
}
|
||||
|
||||
// MakeConfigMap makes an instance of Resource for ConfigMap
|
||||
func (rf *Factory) MakeConfigMap(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.ConfigMapArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeConfigMap(kvLdr, options, args)
|
||||
func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeConfigMap(kvLdr, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.makeOne(
|
||||
u,
|
||||
types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: args.Behavior},
|
||||
options)), nil
|
||||
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
|
||||
}
|
||||
|
||||
// MakeSecret makes an instance of Resource for Secret
|
||||
func (rf *Factory) MakeSecret(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.SecretArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeSecret(kvLdr, options, args)
|
||||
func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeSecret(kvLdr, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.makeOne(
|
||||
u,
|
||||
types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: args.Behavior},
|
||||
options)), nil
|
||||
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
package kusttest_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
@@ -16,6 +19,8 @@ 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/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// HarnessEnhanced manages a full plugin environment for tests.
|
||||
@@ -109,12 +114,69 @@ func (th *HarnessEnhanced) LoadAndRunTransformer(
|
||||
return resMap
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) RunTransformerAndCheckResult(
|
||||
config, input, expected string) {
|
||||
for _, b := range []bool{true, false} {
|
||||
th.t.Run(fmt.Sprintf("yaml-%v", b), func(t *testing.T) {
|
||||
c, err := toggleYamlSupportField(config, b)
|
||||
if err != nil {
|
||||
th.t.Fatalf("Err: %v", err)
|
||||
}
|
||||
resMap, err := th.RunTransformer(c, input)
|
||||
if err != nil {
|
||||
th.t.Fatalf("Err: %v", err)
|
||||
}
|
||||
th.AssertActualEqualsExpected(resMap, expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func toggleYamlSupportField(config string, yamlSupport bool) (string, error) {
|
||||
var out bytes.Buffer
|
||||
rw := kio.ByteReadWriter{
|
||||
Reader: bytes.NewBufferString(config),
|
||||
Writer: &out,
|
||||
}
|
||||
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&rw},
|
||||
Filters: []kio.Filter{
|
||||
kio.FilterAll(yaml.FilterFunc(
|
||||
func(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
return node.Pipe(yaml.FieldSetter{
|
||||
Name: "yamlSupport",
|
||||
StringValue: strconv.FormatBool(yamlSupport),
|
||||
})
|
||||
}),
|
||||
),
|
||||
},
|
||||
Outputs: []kio.Writer{&rw},
|
||||
}.Execute()
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer(
|
||||
config, input string) error {
|
||||
_, err := th.RunTransformer(config, input)
|
||||
return err
|
||||
}
|
||||
|
||||
type AssertFunc func(t *testing.T, err error)
|
||||
|
||||
func (th *HarnessEnhanced) RunTransformerAndCheckError(
|
||||
config, input string, assertFn AssertFunc) {
|
||||
for _, b := range []bool{true, false} {
|
||||
th.t.Run(fmt.Sprintf("yaml-%v", b), func(t *testing.T) {
|
||||
c, err := toggleYamlSupportField(config, b)
|
||||
if err != nil {
|
||||
th.t.Fatalf("Err: %v", err)
|
||||
}
|
||||
_, err = th.RunTransformer(c, input)
|
||||
assertFn(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) RunTransformer(
|
||||
config, input string) (resmap.ResMap, error) {
|
||||
resMap, err := th.rf.NewResMapFromBytes([]byte(input))
|
||||
|
||||
@@ -8,18 +8,14 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GenArgs contains both GeneratorArgs and GeneratorOptions.
|
||||
// GenArgs is a facade over GeneratorArgs, exposing a few readonly properties.
|
||||
type GenArgs struct {
|
||||
args *GeneratorArgs
|
||||
opts *GeneratorOptions
|
||||
}
|
||||
|
||||
// NewGenArgs returns a new object of GenArgs
|
||||
func NewGenArgs(args *GeneratorArgs, opts *GeneratorOptions) *GenArgs {
|
||||
return &GenArgs{
|
||||
args: args,
|
||||
opts: opts,
|
||||
}
|
||||
// NewGenArgs returns a new instance of GenArgs.
|
||||
func NewGenArgs(args *GeneratorArgs) *GenArgs {
|
||||
return &GenArgs{args: args}
|
||||
}
|
||||
|
||||
func (g *GenArgs) String() string {
|
||||
@@ -38,7 +34,7 @@ func (g *GenArgs) String() string {
|
||||
// content hash should be appended to the name of the resource.
|
||||
func (g *GenArgs) ShouldAddHashSuffixToName() bool {
|
||||
return g.args != nil &&
|
||||
(g.opts == nil || !g.opts.DisableNameSuffixHash)
|
||||
(g.args.Options == nil || !g.args.Options.DisableNameSuffixHash)
|
||||
}
|
||||
|
||||
// Behavior returns Behavior field of GeneratorArgs
|
||||
|
||||
@@ -24,8 +24,10 @@ func TestGenArgs_String(t *testing.T) {
|
||||
},
|
||||
{
|
||||
ga: NewGenArgs(
|
||||
&GeneratorArgs{Behavior: "merge"},
|
||||
&GeneratorOptions{DisableNameSuffixHash: false}),
|
||||
&GeneratorArgs{
|
||||
Behavior: "merge",
|
||||
Options: &GeneratorOptions{DisableNameSuffixHash: false},
|
||||
}),
|
||||
expected: "{nsfx:true,beh:merge}",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -22,6 +22,6 @@ type GeneratorArgs struct {
|
||||
// KvPairSources for the generator.
|
||||
KvPairSources `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
|
||||
// GeneratorOptions modify this generator
|
||||
GeneratorOptions *GeneratorOptions `json:"generatorOptions,omitempty" yaml:"generatorOptions,omitempty"`
|
||||
// Local overrides to global generatorOptions field.
|
||||
Options *GeneratorOptions `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
}
|
||||
|
||||
@@ -16,3 +16,55 @@ type GeneratorOptions struct {
|
||||
// resource contents.
|
||||
DisableNameSuffixHash bool `json:"disableNameSuffixHash,omitempty" yaml:"disableNameSuffixHash,omitempty"`
|
||||
}
|
||||
|
||||
// MergeGlobalOptionsIntoLocal merges two instances of GeneratorOptions.
|
||||
// Values in the first 'local' argument cannot be overridden by the second
|
||||
// 'global' argument, except in the case of booleans.
|
||||
//
|
||||
// With booleans, there's no way to distinguish an 'intentional'
|
||||
// false from 'default' false. So the rule is, if the global value
|
||||
// of the value of a boolean is true, i.e. disable, it trumps the
|
||||
// local value. If the global value is false, then the local value is
|
||||
// respected. Bottom line: a local false cannot override a global true.
|
||||
//
|
||||
// boolean fields are always a bad idea; should always use enums instead.
|
||||
func MergeGlobalOptionsIntoLocal(
|
||||
localOpts *GeneratorOptions,
|
||||
globalOpts *GeneratorOptions) *GeneratorOptions {
|
||||
if globalOpts == nil {
|
||||
return localOpts
|
||||
}
|
||||
if localOpts == nil {
|
||||
localOpts = &GeneratorOptions{}
|
||||
}
|
||||
overrideMap(&localOpts.Labels, globalOpts.Labels)
|
||||
overrideMap(&localOpts.Annotations, globalOpts.Annotations)
|
||||
if globalOpts.DisableNameSuffixHash {
|
||||
localOpts.DisableNameSuffixHash = true
|
||||
}
|
||||
return localOpts
|
||||
}
|
||||
|
||||
func overrideMap(localMap *map[string]string, globalMap map[string]string) {
|
||||
if *localMap == nil {
|
||||
if globalMap != nil {
|
||||
*localMap = CopyMap(globalMap)
|
||||
}
|
||||
return
|
||||
}
|
||||
for k, v := range globalMap {
|
||||
_, ok := (*localMap)[k]
|
||||
if !ok {
|
||||
(*localMap)[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CopyMap copies a map.
|
||||
func CopyMap(in map[string]string) map[string]string {
|
||||
out := make(map[string]string)
|
||||
for k, v := range in {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
125
api/types/generatoroptions_test.go
Normal file
125
api/types/generatoroptions_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestMergeGlobalOptionsIntoLocal(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
local *GeneratorOptions
|
||||
global *GeneratorOptions
|
||||
expected *GeneratorOptions
|
||||
}{
|
||||
{
|
||||
name: "everything nil",
|
||||
local: nil,
|
||||
global: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "nil global",
|
||||
local: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{"fruit": "apple"},
|
||||
},
|
||||
global: nil,
|
||||
expected: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{"fruit": "apple"},
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil local",
|
||||
local: nil,
|
||||
global: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{"fruit": "apple"},
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{"fruit": "apple"},
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global doesn't damage local",
|
||||
local: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{
|
||||
"fruit": "apple"},
|
||||
},
|
||||
global: &GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"pet": "cat",
|
||||
"simpson": "homer",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"fruit": "peach",
|
||||
"tesla": "Y",
|
||||
},
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"pet": "dog",
|
||||
"simpson": "homer",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"fruit": "apple",
|
||||
"tesla": "Y",
|
||||
},
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global disable trumps local",
|
||||
local: &GeneratorOptions{
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
global: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "local disable works",
|
||||
local: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
global: &GeneratorOptions{
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "everyone wants disable",
|
||||
local: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
global: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
actual := MergeGlobalOptionsIntoLocal(tc.local, tc.global)
|
||||
if !reflect.DeepEqual(tc.expected, actual) {
|
||||
t.Fatalf("%s annotations: Expected '%v', got '%v'",
|
||||
tc.name, tc.expected, *actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -50,10 +51,16 @@ func GetRunFnRunner(name string) *RunFnRunner {
|
||||
&r.StarName, "star-name", "", "name of starlark program.")
|
||||
r.Command.Flags().MarkHidden("star-name")
|
||||
|
||||
r.Command.Flags().StringVar(
|
||||
&r.ResultsDir, "results-dir", "", "write function results to this dir")
|
||||
|
||||
r.Command.Flags().BoolVar(
|
||||
&r.Network, "network", false, "enable network access for functions that declare it")
|
||||
r.Command.Flags().StringVar(
|
||||
&r.NetworkName, "network-name", "bridge", "the docker network to run the container in")
|
||||
r.Command.Flags().StringArrayVar(
|
||||
&r.Mounts, "mount", []string{},
|
||||
"a list of storage options read from the filesystem")
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -73,8 +80,10 @@ type RunFnRunner struct {
|
||||
StarPath string
|
||||
StarName string
|
||||
RunFns runfn.RunFns
|
||||
ResultsDir string
|
||||
Network bool
|
||||
NetworkName string
|
||||
Mounts []string
|
||||
}
|
||||
|
||||
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
||||
@@ -199,6 +208,14 @@ data: {}
|
||||
return []*yaml.RNode{rc}, nil
|
||||
}
|
||||
|
||||
func toStorageMounts(mounts []string) []filters.StorageMount {
|
||||
var sms []filters.StorageMount
|
||||
for _, mount := range mounts {
|
||||
sms = append(sms, filters.StringToStorageMount(mount))
|
||||
}
|
||||
return sms
|
||||
}
|
||||
|
||||
func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
if r.EnableStar != (r.StarPath != "") {
|
||||
return errors.Errorf("must specify --star-path with --enable-star")
|
||||
@@ -240,6 +257,9 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
path = args[0]
|
||||
}
|
||||
|
||||
// parse mounts to set storageMounts
|
||||
storageMounts := toStorageMounts(r.Mounts)
|
||||
|
||||
r.RunFns = runfn.RunFns{
|
||||
FunctionPaths: r.FnPaths,
|
||||
GlobalScope: r.GlobalScope,
|
||||
@@ -250,6 +270,8 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
Network: r.Network,
|
||||
NetworkName: r.NetworkName,
|
||||
EnableStarlark: r.EnableStar,
|
||||
StorageMounts: storageMounts,
|
||||
ResultsDir: r.ResultsDir,
|
||||
}
|
||||
|
||||
// don't consider args for the function
|
||||
|
||||
@@ -11,22 +11,25 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||
)
|
||||
|
||||
// TestRunFnCommand_preRunE verifies that preRunE correctly parses the commandline
|
||||
// flags and arguments into the RunFns structure to be executed.
|
||||
func TestRunFnCommand_preRunE(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expected string
|
||||
err string
|
||||
path string
|
||||
input io.Reader
|
||||
output io.Writer
|
||||
functionPaths []string
|
||||
network bool
|
||||
networkName string
|
||||
name string
|
||||
args []string
|
||||
expected string
|
||||
expectedStruct *runfn.RunFns
|
||||
err string
|
||||
path string
|
||||
input io.Reader
|
||||
output io.Writer
|
||||
functionPaths []string
|
||||
network bool
|
||||
networkName string
|
||||
mount []string
|
||||
}{
|
||||
{
|
||||
name: "config map",
|
||||
@@ -213,6 +216,46 @@ metadata:
|
||||
data: {g: h, i: j=k}
|
||||
kind: Foo
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "custom kind with storage mounts",
|
||||
args: []string{
|
||||
"run", "dir", "--mount", "type=bind,src=/mount/path,dst=/local/",
|
||||
"--mount", "type=volume,src=myvol,dst=/local/",
|
||||
"--mount", "type=tmpfs,dst=/local/",
|
||||
"--image", "foo:bar", "--", "Foo", "g=h", "i=j=k"},
|
||||
path: "dir",
|
||||
mount: []string{"type=bind,src=/mount/path,dst=/local/", "type=volume,src=myvol,dst=/local/", "type=tmpfs,dst=/local/"},
|
||||
expected: `
|
||||
metadata:
|
||||
name: function-input
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container: {image: 'foo:bar'}
|
||||
data: {g: h, i: j=k}
|
||||
kind: Foo
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "results_dir",
|
||||
args: []string{"run", "dir", "--results-dir", "foo/", "--image", "foo:bar", "--", "a=b", "c=d", "e=f"},
|
||||
path: "dir",
|
||||
expectedStruct: &runfn.RunFns{
|
||||
Path: "dir",
|
||||
NetworkName: "bridge",
|
||||
ResultsDir: "foo/",
|
||||
},
|
||||
expected: `
|
||||
metadata:
|
||||
name: function-input
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container: {image: 'foo:bar'}
|
||||
data: {a: b, c: d, e: f}
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -303,6 +346,14 @@ apiVersion: v1
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, r.RunFns, r.RunFns) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, toStorageMounts(tt.mount), r.RunFns.StorageMounts) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// check if Functions were set
|
||||
if tt.expected != "" {
|
||||
if !assert.Len(t, r.RunFns.Functions, 1) {
|
||||
@@ -314,6 +365,14 @@ apiVersion: v1
|
||||
}
|
||||
}
|
||||
|
||||
if tt.expectedStruct != nil {
|
||||
r.RunFns.Functions = nil
|
||||
tt.expectedStruct.FunctionPaths = tt.functionPaths
|
||||
if !assert.Equal(t, *tt.expectedStruct, r.RunFns) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
# Builtin Plugins
|
||||
|
||||
A list of kustomize's builtin plugins (both
|
||||
generators and transformers).
|
||||
A list of kustomize's builtin plugins - both
|
||||
generators and transformers.
|
||||
|
||||
For each plugin, an example is given for
|
||||
|
||||
@@ -92,16 +92,35 @@ one ConfigMap resource (it's a generator of n maps).
|
||||
|
||||
The example below creates three ConfigMaps. One with the names and contents of
|
||||
the given files, one with key/value as data, and a third which sets an
|
||||
annotation and label via generatorOptions for that single ConfigMap.
|
||||
annotation and label via `options` for that single ConfigMap.
|
||||
|
||||
Each configMapGenerator item accepts a parameter of
|
||||
`behavior: [create|replace|merge]`.
|
||||
This allows an overlay to modify or
|
||||
replace an existing configMap from the parent.
|
||||
|
||||
Also, each entry has an `options` field, that has the
|
||||
same subfields as the kustomization file's `generatorOptions` field.
|
||||
|
||||
This `options` field allows one to add labels and/or
|
||||
annotations to the generated instance, or to individually
|
||||
disable the name suffix hash for that instance.
|
||||
Labels and annotations added here will not be overwritten
|
||||
by the global options associated with the kustomization
|
||||
file `generatorOptions` field. However, due to how
|
||||
booleans behave, if the global `generatorOptions` field
|
||||
specifies `disableNameSuffixHash: true`, this will
|
||||
trump any attempt to locally override it.
|
||||
|
||||
```
|
||||
# These labels are added to all configmaps and secrets.
|
||||
generatorOptions:
|
||||
labels:
|
||||
fruit: apple
|
||||
|
||||
configMapGenerator:
|
||||
- name: my-java-server-props
|
||||
behavior: merge
|
||||
files:
|
||||
- application.properties
|
||||
- more.properties
|
||||
@@ -109,10 +128,14 @@ configMapGenerator:
|
||||
literals:
|
||||
- JAVA_HOME=/opt/java/jdk
|
||||
- JAVA_TOOL_OPTIONS=-agentlib:hprof
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
labels:
|
||||
pet: dog
|
||||
- name: dashboards
|
||||
files:
|
||||
- mydashboard.json
|
||||
generatorOptions:
|
||||
options:
|
||||
annotations:
|
||||
dashboard: "1"
|
||||
labels:
|
||||
@@ -138,8 +161,6 @@ configMapGenerator:
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> [types.GeneratorOptions]
|
||||
>
|
||||
> [types.ConfigMapArgs]
|
||||
|
||||
#### Example
|
||||
@@ -640,6 +661,9 @@ results in the creation of
|
||||
one Secret resource
|
||||
(it's a generator of n secrets).
|
||||
|
||||
This works like the `configMapGenerator` field
|
||||
described above.
|
||||
|
||||
```
|
||||
secretGenerator:
|
||||
- name: app-tls
|
||||
@@ -663,7 +687,7 @@ secretGenerator:
|
||||
files:
|
||||
- app-config.yaml
|
||||
type: Opaque
|
||||
generatorOptions:
|
||||
options:
|
||||
annotations:
|
||||
app_config: "true"
|
||||
labels:
|
||||
@@ -676,8 +700,6 @@ secretGenerator:
|
||||
|
||||
> [types.ObjectMeta]
|
||||
>
|
||||
> [types.GeneratorOptions]
|
||||
>
|
||||
> [types.SecretArgs]
|
||||
|
||||
#### Example
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
# Demo: applying a json patch
|
||||
# JSON Patching
|
||||
|
||||
A kustomization file supports customizing resources via [JSON patches](https://tools.ietf.org/html/rfc6902).
|
||||
[JSON patches]: https://tools.ietf.org/html/rfc6902
|
||||
[JSON patch]: https://tools.ietf.org/html/rfc6902
|
||||
|
||||
The example below modifies an `Ingress` object with such a patch.
|
||||
A kustomization file supports customizing
|
||||
resources via [JSON patches].
|
||||
|
||||
Make a `kustomization` containing an ingress resource.
|
||||
Make a place to work:
|
||||
|
||||
<!-- @createIngress @testAgainstLatestRelease -->
|
||||
<!-- @placeToWork @testAgainstLatestRelease -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- ingress.yaml
|
||||
EOF
|
||||
We'll be editting an `Ingress` object:
|
||||
|
||||
<!-- @ingress @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress.yaml
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
@@ -25,94 +27,175 @@ spec:
|
||||
- host: foo.bar.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: homepage
|
||||
servicePort: 8888
|
||||
- path: /api
|
||||
backend:
|
||||
serviceName: my-api
|
||||
servicePort: 80
|
||||
servicePort: 7701
|
||||
- path: /test
|
||||
backend:
|
||||
serviceName: hello
|
||||
servicePort: 7702
|
||||
EOF
|
||||
```
|
||||
|
||||
Declare a JSON patch file to update two fields of the Ingress object:
|
||||
The edits we want to make are:
|
||||
|
||||
- change host from `foo.bar.com` to `foo.bar.io`
|
||||
- change servicePort from `80` to `8080`
|
||||
- change the value of `host` to _foo.bar.io_
|
||||
- change the port for `'/'` from _8888_ to _80_
|
||||
- insert an entirely new serving path `/healthz`
|
||||
at a particular point in the `paths` list,
|
||||
rather than at the end or the beginning.
|
||||
|
||||
Here's the patch file to do that:
|
||||
|
||||
<!-- @addJsonPatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress_patch.json
|
||||
[
|
||||
{"op": "replace", "path": "/spec/rules/0/host", "value": "foo.bar.io"},
|
||||
{"op": "replace", "path": "/spec/rules/0/http/paths/0/backend/servicePort", "value": 8080}
|
||||
{"op": "replace",
|
||||
"path": "/spec/rules/0/host",
|
||||
"value": "foo.bar.io"},
|
||||
|
||||
{"op": "replace",
|
||||
"path": "/spec/rules/0/http/paths/0/backend/servicePort",
|
||||
"value": 80},
|
||||
|
||||
{"op": "add",
|
||||
"path": "/spec/rules/0/http/paths/1",
|
||||
"value": { "path": "/healthz", "backend": {"servicePort":7700} }}
|
||||
]
|
||||
EOF
|
||||
```
|
||||
|
||||
You can also write the patch in YAML format. This example also shows the "add" operation:
|
||||
We'll of course need a `kustomization` file
|
||||
referring to the `Ingress`:
|
||||
|
||||
<!-- @addYamlPatch @testAgainstLatestRelease -->
|
||||
<!-- @kustomization @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress_patch.yaml
|
||||
- op: replace
|
||||
path: /spec/rules/0/host
|
||||
value: foo.bar.io
|
||||
|
||||
- op: add
|
||||
path: /spec/rules/0/http/paths/-
|
||||
value:
|
||||
path: '/test'
|
||||
backend:
|
||||
serviceName: my-test
|
||||
servicePort: 8081
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- ingress.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
Apply the patch by adding _patchesJson6902_ field in kustomization.yaml
|
||||
To this same `kustomization` file, add a
|
||||
`patches` field refering to
|
||||
the patch file we just made and
|
||||
target it to the `Ingress` object:
|
||||
|
||||
<!-- @applyJsonPatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >>$DEMO_HOME/kustomization.yaml
|
||||
patchesJson6902:
|
||||
- target:
|
||||
patches:
|
||||
- path: ingress_patch.json
|
||||
target:
|
||||
group: networking.k8s.io
|
||||
version: v1beta1
|
||||
kind: Ingress
|
||||
name: my-ingress
|
||||
path: ingress_patch.json
|
||||
EOF
|
||||
```
|
||||
|
||||
Running `kustomize build $DEMO_HOME`, in the output confirm that host has been updated correctly.
|
||||
<!-- @confirmHost @testAgainstLatestRelease -->
|
||||
Define the expected output:
|
||||
<!-- @expected @testAgainstLatestRelease -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "host: foo.bar.io" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
Running `kustomize build $DEMO_HOME`, in the output confirm that the servicePort has been updated correctly.
|
||||
<!-- @confirmServicePort @testAgainstLatestRelease -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "servicePort: 8080" | wc -l); \
|
||||
echo $?
|
||||
cat <<EOF >$DEMO_HOME/out_expected.yaml
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: my-ingress
|
||||
spec:
|
||||
rules:
|
||||
- host: foo.bar.io
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: homepage
|
||||
servicePort: 80
|
||||
path: /
|
||||
- backend:
|
||||
servicePort: 7700
|
||||
path: /healthz
|
||||
- backend:
|
||||
serviceName: my-api
|
||||
servicePort: 7701
|
||||
path: /api
|
||||
- backend:
|
||||
serviceName: hello
|
||||
servicePort: 7702
|
||||
path: /test
|
||||
EOF
|
||||
```
|
||||
|
||||
If the patch is YAML-formatted, it will be parsed correctly:
|
||||
Run the build:
|
||||
<!-- @runIt @testAgainstLatestRelease -->
|
||||
```
|
||||
kustomize build $DEMO_HOME >$DEMO_HOME/out_actual.yaml
|
||||
```
|
||||
|
||||
<!-- @applyYamlPatch @testAgainstLatestRelease -->
|
||||
Confirm they match:
|
||||
|
||||
<!-- @diffShouldExitZero @testAgainstLatestRelease -->
|
||||
```
|
||||
diff $DEMO_HOME/out_actual.yaml $DEMO_HOME/out_expected.yaml
|
||||
```
|
||||
|
||||
If you prefer YAML to JSON, the patch can be expressed
|
||||
in YAML format (neverthless following [JSON patch] rules):
|
||||
|
||||
<!-- @writeYamlPatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress_patch.yaml
|
||||
- op: add
|
||||
path: /spec/rules/0/http/paths/-
|
||||
value:
|
||||
path: '/canada'
|
||||
backend:
|
||||
serviceName: hoser
|
||||
servicePort: 7703
|
||||
EOF
|
||||
```
|
||||
|
||||
Now add this to the list of patches in the `kustomization` file:
|
||||
|
||||
<!-- @addYamlPatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >>$DEMO_HOME/kustomization.yaml
|
||||
patchesJson6902:
|
||||
- target:
|
||||
- path: ingress_patch.yaml
|
||||
target:
|
||||
group: networking.k8s.io
|
||||
version: v1beta1
|
||||
kind: Ingress
|
||||
name: my-ingress
|
||||
path: ingress_patch.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
<!-- @confirmYamlPatch @testAgainstLatestRelease -->
|
||||
We expect the following at the end of the output:
|
||||
<!-- @expected @testAgainstLatestRelease -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "path: /test" | wc -l); \
|
||||
echo $?
|
||||
cat <<EOF >$DEMO_HOME/out_expected.yaml
|
||||
- backend:
|
||||
serviceName: hello
|
||||
servicePort: 7702
|
||||
path: /test
|
||||
- backend:
|
||||
serviceName: hoser
|
||||
servicePort: 7703
|
||||
path: /canada
|
||||
EOF
|
||||
```
|
||||
|
||||
Try it:
|
||||
|
||||
<!-- @runIt @testAgainstLatestRelease -->
|
||||
```
|
||||
kustomize build $DEMO_HOME | tail -n 8 |\
|
||||
diff $DEMO_HOME/out_expected.yaml -
|
||||
```
|
||||
|
||||
To see how to apply one JSON patch to many resources,
|
||||
see the [multi-patch](patchMultipleObjects.md) demo.
|
||||
|
||||
@@ -1,43 +1,78 @@
|
||||
[Strategic Merge Patch]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
|
||||
[JSON patches]: https://tools.ietf.org/html/rfc6902
|
||||
[label selector]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
|
||||
# Patching multiple resources at once.
|
||||
|
||||
kustomize supports patching via either a
|
||||
[strategic merge patch] (wherein you
|
||||
partially re-specify the thing you want to
|
||||
modify, with in-place changes) or a
|
||||
[JSON patch] (wherein you specify specific
|
||||
operation/target/value tuples in a particular
|
||||
syntax).
|
||||
|
||||
A kustomize file lets one specify many
|
||||
patches. Each patch must be associated with
|
||||
a _target selector_:
|
||||
|
||||
[strategic merge patch]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
|
||||
[JSON patch]: jsonpatch.md
|
||||
|
||||
> ```yaml
|
||||
> patches:
|
||||
> - path: <relative path to file containing patch>
|
||||
> target:
|
||||
> group: <optional group>
|
||||
> version: <optional version>
|
||||
> kind: <optional kind>
|
||||
> name: <optional name>
|
||||
> namespace: <optional namespace>
|
||||
> labelSelector: <optional label selector>
|
||||
> annotationSelector: <optional annotation selector>
|
||||
> ```
|
||||
|
||||
E.g. select resources with _name_ matching `foo*`:
|
||||
|
||||
> ```yaml
|
||||
> target:
|
||||
> name: foo*
|
||||
> ```
|
||||
|
||||
Select all resources of _kind_ `Deployment`:
|
||||
|
||||
> ```yaml
|
||||
> target:
|
||||
> kind: Deployment
|
||||
> ```
|
||||
|
||||
[label/annotation selector rules]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
|
||||
|
||||
Using multiple fields just makes the target
|
||||
more specific. The following selects only
|
||||
Deployments that also have the _label_ `app=hello`
|
||||
(full [label/annotation selector rules]):
|
||||
|
||||
> ```yaml
|
||||
> target:
|
||||
> kind: Deployment
|
||||
> labelSelector: app=hello
|
||||
> ```
|
||||
|
||||
### Demo
|
||||
|
||||
The example below shows how to inject a
|
||||
sidecar container for multiple Deployment
|
||||
resources.
|
||||
|
||||
|
||||
# Demo: applying a patch to multiple resources
|
||||
Make a place to work:
|
||||
|
||||
A kustomization file supports customizing resources via both
|
||||
[Strategic Merge Patch] and [JSON patches]. Now one patch can be
|
||||
applied to multiple resources.
|
||||
|
||||
This can be done by specifying a patch and a target selector as follows:
|
||||
```
|
||||
patches:
|
||||
- path: <PatchFile>
|
||||
target:
|
||||
group: <Group>
|
||||
version: <Version>
|
||||
kind: <Kind>
|
||||
name: <Name>
|
||||
namespace: <Namespace>
|
||||
labelSelector: <LabelSelector>
|
||||
annotationSelector: <AnnotationSelector>
|
||||
```
|
||||
Both `labelSelector` and `annotationSelector` should follow the convention in [label selector].
|
||||
Kustomize selects the targets which match all the fields in `target` to apply the patch.
|
||||
|
||||
The example below shows how to inject a sidecar container for all deployment resources.
|
||||
|
||||
Make a `kustomization` containing a Deployment resource.
|
||||
|
||||
<!-- @createDeployment @testAgainstLatestRelease -->
|
||||
<!-- @demoHome @testAgainstLatestRelease -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- deployments.yaml
|
||||
EOF
|
||||
Make a file describing two Deployments:
|
||||
|
||||
<!-- @createDeployments @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/deployments.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -72,9 +107,10 @@ spec:
|
||||
EOF
|
||||
```
|
||||
|
||||
Declare a Strategic Merge Patch file to inject a sidecar container:
|
||||
Declare a [strategic merge patch] file
|
||||
to inject a sidecar container:
|
||||
|
||||
<!-- @addPatch @testAgainstLatestRelease -->
|
||||
<!-- @definePatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/patch.yaml
|
||||
apiVersion: apps/v1
|
||||
@@ -93,11 +129,16 @@ spec:
|
||||
EOF
|
||||
```
|
||||
|
||||
Apply the patch by adding _patches_ field in kustomization.yaml
|
||||
Finally, define a kustomization file
|
||||
that specifies both a `patches` and `resources`
|
||||
entry:
|
||||
|
||||
<!-- @applyPatch @testAgainstLatestRelease -->
|
||||
<!-- @createKustomization @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >>$DEMO_HOME/kustomization.yaml
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- deployments.yaml
|
||||
|
||||
patches:
|
||||
- path: patch.yaml
|
||||
target:
|
||||
@@ -105,18 +146,11 @@ patches:
|
||||
EOF
|
||||
```
|
||||
|
||||
Running `kustomize build $DEMO_HOME`, in the output confirm that both Deployment resources are patched correctly.
|
||||
The expected result is:
|
||||
|
||||
<!-- @confirmPatch @testAgainstLatestRelease -->
|
||||
<!-- @definedExpectedOutput @testAgainstLatestRelease -->
|
||||
```
|
||||
test 2 == \
|
||||
$(kustomize build $DEMO_HOME | grep "image: docker.io/istio/proxyv2" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
The output is as follows:
|
||||
|
||||
```yaml
|
||||
cat <<EOF >$DEMO_HOME/out_expected.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -157,32 +191,21 @@ spec:
|
||||
name: istio-proxy
|
||||
- image: busybox
|
||||
name: busybox
|
||||
EOF
|
||||
```
|
||||
|
||||
## Target selector
|
||||
- Select resources with name matching `name*`
|
||||
```yaml
|
||||
target:
|
||||
name: name*
|
||||
```
|
||||
- Select all Deployment resources
|
||||
```yaml
|
||||
target:
|
||||
kind: Deployment
|
||||
```
|
||||
- Select resources matching label `app=hello`
|
||||
```yaml
|
||||
target:
|
||||
labelSelector: app=hello
|
||||
```
|
||||
- Select resources matching annotation `app=hello`
|
||||
```yaml
|
||||
target:
|
||||
annotationSelector: app=hello
|
||||
```
|
||||
- Select all Deployment resources matching label `app=hello`
|
||||
```yaml
|
||||
target:
|
||||
kind: Deployment
|
||||
labelSelector: app=hello
|
||||
```
|
||||
Run the build:
|
||||
<!-- @runIt @testAgainstLatestRelease -->
|
||||
```
|
||||
kustomize build $DEMO_HOME >$DEMO_HOME/out_actual.yaml
|
||||
```
|
||||
|
||||
Confirm expectations:
|
||||
|
||||
<!-- @diffShouldExitZero @testAgainstLatestRelease -->
|
||||
```
|
||||
diff $DEMO_HOME/out_actual.yaml $DEMO_HOME/out_expected.yaml
|
||||
```
|
||||
|
||||
To see how to do this with JSON patches,
|
||||
try the [JSON patch] demo.
|
||||
|
||||
@@ -12,8 +12,7 @@ spec:
|
||||
strict: true
|
||||
ignoreMissingSchemas: true
|
||||
|
||||
# TODO: Remove these once function container network/volumes features are
|
||||
# stabilized.
|
||||
# TODO: Update this to use network/volumes features.
|
||||
# Relevant issues:
|
||||
# - https://github.com/kubernetes-sigs/kustomize/issues/1901
|
||||
# - https://github.com/kubernetes-sigs/kustomize/issues/1902
|
||||
|
||||
@@ -16,7 +16,7 @@ require (
|
||||
sigs.k8s.io/kustomize/cmd/config v0.0.5
|
||||
sigs.k8s.io/kustomize/cmd/kubectl v0.0.3
|
||||
sigs.k8s.io/kustomize/kstatus v0.0.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
|
||||
@@ -153,8 +153,6 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
@@ -757,12 +755,8 @@ sigs.k8s.io/kustomize/cmd/config v0.0.5 h1:mFJowsk9IGvwm5dUpVB+ZM63on2JjgaCy+YcV
|
||||
sigs.k8s.io/kustomize/cmd/config v0.0.5/go.mod h1:L47nDnZDfGFQG3gnPJLG2UABn0nVb9v+ndceyMH0jjU=
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.2/go.mod h1:rywm/rcR5LmCBghz9956tE45OdUPChFoXVVs+WmhMTI=
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.5/go.mod h1:waxTrzQRK9i6/5fR5HNo8xa4YwvWn8t85vMnOGFEZik=
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.6 h1:KhQr7JwpCseFTSWCwqp4CJ4mY6Kx+i34tF4e0eNkcXw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.6/go.mod h1:tDOfJjL6slQVBLHJ76XfXAFgAOEdfm04AW2HehYOp8k=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1 h1:nGUNYINljZNmlAS8uoobUv/wx/s3Pg8dNxYo+W7uYh0=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1/go.mod h1:/NdPPfrperSCGjm55cwEro1loBVtbtVIXSb7FguK6uk=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3 h1:zbeHVTMCQPtWgjIH/YYJZC45mm7coTdw2TblyJ79BrY=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5 h1:NicBWYTwkuOfVyZDbNkfSBSCwSgin4uirkedtyZltIc=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=
|
||||
|
||||
@@ -95,11 +95,10 @@ func addConfigMap(
|
||||
args := findOrMakeConfigMapArgs(k, flags.Name)
|
||||
mergeFlagsIntoCmArgs(args, flags)
|
||||
// Validate by trying to create corev1.configmap.
|
||||
_, err := kf.MakeConfigMap(ldr, k.GeneratorOptions, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
args.Options = types.MergeGlobalOptionsIntoLocal(
|
||||
args.Options, k.GeneratorOptions)
|
||||
_, err := kf.MakeConfigMap(ldr, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func findOrMakeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs {
|
||||
|
||||
@@ -105,11 +105,10 @@ func addSecret(
|
||||
args := findOrMakeSecretArgs(k, flags.Name, flags.Namespace, flags.Type)
|
||||
mergeFlagsIntoGeneratorArgs(&args.GeneratorArgs, flags)
|
||||
// Validate by trying to create corev1.secret.
|
||||
_, err := kf.MakeSecret(ldr, k.GeneratorOptions, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
args.Options = types.MergeGlobalOptionsIntoLocal(
|
||||
args.Options, k.GeneratorOptions)
|
||||
_, err := kf.MakeSecret(ldr, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func findOrMakeSecretArgs(m *types.Kustomization, name, namespace, secretType string) *types.SecretArgs {
|
||||
|
||||
@@ -35,4 +35,5 @@ vet:
|
||||
|
||||
openapi:
|
||||
(which $(GOPATH)/bin/go-bindata || go get -v github.com/go-bindata/go-bindata)
|
||||
go-bindata --pkg openapi -o openapi/swagger.go openapi/swagger.json
|
||||
$(GOPATH)/bin/go-bindata --pkg kubernetesapi -o openapi/kubernetesapi/swagger.go openapi/kubernetesapi/swagger.json
|
||||
$(GOPATH)/bin/go-bindata --pkg kustomizationapi -o openapi/kustomizationapi/swagger.go openapi/kustomizationapi/swagger.json
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// CopyComments recursively copies the comments on fields in from to fields in to
|
||||
func CopyComments(from, to *yaml.RNode) error {
|
||||
copy(from, to)
|
||||
// walk the fields copying comments
|
||||
_, err := walk.Walker{
|
||||
Sources: []*yaml.RNode{from, to},
|
||||
@@ -44,13 +45,13 @@ func copy(from, to *yaml.RNode) {
|
||||
if from == nil || to == nil {
|
||||
return
|
||||
}
|
||||
if from.YNode().LineComment != "" {
|
||||
to.YNode().LineComment = from.YNode().LineComment
|
||||
if from.Document().LineComment != "" {
|
||||
to.Document().LineComment = from.Document().LineComment
|
||||
}
|
||||
if from.YNode().HeadComment != "" {
|
||||
to.YNode().HeadComment = from.YNode().HeadComment
|
||||
if from.Document().HeadComment != "" {
|
||||
to.Document().HeadComment = from.Document().HeadComment
|
||||
}
|
||||
if from.YNode().FootComment != "" {
|
||||
to.YNode().FootComment = from.YNode().FootComment
|
||||
if from.Document().FootComment != "" {
|
||||
to.Document().FootComment = from.Document().FootComment
|
||||
}
|
||||
}
|
||||
|
||||
65
kyaml/comments/comments_test.go
Normal file
65
kyaml/comments/comments_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package comments
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestCopyComments(t *testing.T) {
|
||||
from, err := yaml.Parse(`# A
|
||||
#
|
||||
# B
|
||||
|
||||
# C
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec: # comment 1
|
||||
# comment 2
|
||||
replicas: 3 # comment 3
|
||||
# comment 4
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
to, err := yaml.Parse(`apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 4
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
err = CopyComments(from, to)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actual, err := to.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `# A
|
||||
#
|
||||
# B
|
||||
|
||||
# C
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
spec: # comment 1
|
||||
# comment 2
|
||||
replicas: 4 # comment 3
|
||||
# comment 4
|
||||
`
|
||||
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
@@ -131,3 +131,49 @@ func Diff(sourceDir, destDir string) (sets.String, error) {
|
||||
// return the differing files
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
// SyncFile copies file from src file path to a dst file path by replacement
|
||||
// deletes dst file if src file doesn't exist
|
||||
func SyncFile(src, dst string) error {
|
||||
srcFileInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
// delete dst if source doesn't exist
|
||||
if err = deleteFile(dst); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
input, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var filePerm os.FileMode
|
||||
|
||||
// get the destination file perm if file exists
|
||||
dstFileInfo, err := os.Stat(dst)
|
||||
if err != nil {
|
||||
// get source file perm if destination file doesn't exist
|
||||
filePerm = srcFileInfo.Mode().Perm()
|
||||
} else {
|
||||
filePerm = dstFileInfo.Mode().Perm()
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(dst, input, filePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteFile deletes file from path, returns no error if file doesn't exist
|
||||
func deleteFile(path string) error {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
// return nil if file doesn't exist
|
||||
return nil
|
||||
}
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -203,3 +204,60 @@ func TestDiff_skipGitDest(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, diff.List())
|
||||
}
|
||||
|
||||
// TestSyncFile tests if destination file is replaced by source file content
|
||||
func TestSyncFile(t *testing.T) {
|
||||
d1, err := ioutil.TempDir("", "")
|
||||
assert.NoError(t, err)
|
||||
d2, err := ioutil.TempDir("", "")
|
||||
assert.NoError(t, err)
|
||||
f1Name := d1 + "/temp.txt"
|
||||
f2Name := d2 + "/temp.txt"
|
||||
err = ioutil.WriteFile(f1Name, []byte("abc"), 0600)
|
||||
assert.NoError(t, err)
|
||||
err = ioutil.WriteFile(f2Name, []byte("def"), 0644)
|
||||
assert.NoError(t, err)
|
||||
err = SyncFile(f1Name, f2Name)
|
||||
assert.NoError(t, err)
|
||||
actual, err := ioutil.ReadFile(f2Name)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "abc", string(actual))
|
||||
dstFileInfo, _ := os.Stat(f2Name)
|
||||
assert.Equal(t, "-rw-r--r--", dstFileInfo.Mode().String())
|
||||
}
|
||||
|
||||
// TestSyncFileNoDestFile tests if new file is created at destination with source file content
|
||||
func TestSyncFileNoDestFile(t *testing.T) {
|
||||
d1, err := ioutil.TempDir("", "")
|
||||
assert.NoError(t, err)
|
||||
d2, err := ioutil.TempDir("", "")
|
||||
assert.NoError(t, err)
|
||||
f1Name := d1 + "/temp.txt"
|
||||
f2Name := d2 + "/temp.txt"
|
||||
err = ioutil.WriteFile(f1Name, []byte("abc"), 0644)
|
||||
assert.NoError(t, err)
|
||||
err = SyncFile(f1Name, f2Name)
|
||||
assert.NoError(t, err)
|
||||
actual, err := ioutil.ReadFile(f2Name)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "abc", string(actual))
|
||||
dstFileInfo, _ := os.Stat(f2Name)
|
||||
assert.Equal(t, "-rw-r--r--", dstFileInfo.Mode().String())
|
||||
}
|
||||
|
||||
// TestSyncFileNoSrcFile tests if destination file is deleted if source file doesn't exist
|
||||
func TestSyncFileNoSrcFile(t *testing.T) {
|
||||
d1, err := ioutil.TempDir("", "")
|
||||
assert.NoError(t, err)
|
||||
d2, err := ioutil.TempDir("", "")
|
||||
assert.NoError(t, err)
|
||||
f1Name := d1 + "/temp.txt"
|
||||
f2Name := d2 + "/temp.txt"
|
||||
err = ioutil.WriteFile(f2Name, []byte("abc"), 0644)
|
||||
assert.NoError(t, err)
|
||||
err = SyncFile(f1Name, f2Name)
|
||||
assert.NoError(t, err)
|
||||
_, err = ioutil.ReadFile(f2Name)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, strings.Contains(err.Error(), "no such file or directory"))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func ApplyToJSON(filter kio.Filter, objs ...marshalerUnmarshaler) error {
|
||||
|
||||
// convert the json objects to rnodes
|
||||
for i := range objs {
|
||||
node, err := getRNode(objs[i])
|
||||
node, err := GetRNode(objs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -55,8 +55,8 @@ type marshalerUnmarshaler interface {
|
||||
json.Marshaler
|
||||
}
|
||||
|
||||
// getRNode converts k into an RNode
|
||||
func getRNode(k json.Marshaler) (*yaml.RNode, error) {
|
||||
// GetRNode converts k into an RNode
|
||||
func GetRNode(k json.Marshaler) (*yaml.RNode, error) {
|
||||
j, err := k.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -41,6 +41,8 @@ type ByteReadWriter struct {
|
||||
|
||||
FunctionConfig *yaml.RNode
|
||||
|
||||
Results *yaml.RNode
|
||||
|
||||
WrappingAPIVersion string
|
||||
WrappingKind string
|
||||
}
|
||||
@@ -52,6 +54,7 @@ func (rw *ByteReadWriter) Read() ([]*yaml.RNode, error) {
|
||||
}
|
||||
val, err := b.Read()
|
||||
rw.FunctionConfig = b.FunctionConfig
|
||||
rw.Results = b.Results
|
||||
rw.WrappingAPIVersion = b.WrappingAPIVersion
|
||||
rw.WrappingKind = b.WrappingKind
|
||||
return val, errors.Wrap(err)
|
||||
@@ -63,6 +66,7 @@ func (rw *ByteReadWriter) Write(nodes []*yaml.RNode) error {
|
||||
KeepReaderAnnotations: rw.KeepReaderAnnotations,
|
||||
Style: rw.Style,
|
||||
FunctionConfig: rw.FunctionConfig,
|
||||
Results: rw.Results,
|
||||
WrappingAPIVersion: rw.WrappingAPIVersion,
|
||||
WrappingKind: rw.WrappingKind,
|
||||
}.Write(nodes)
|
||||
@@ -85,6 +89,8 @@ type ByteReader struct {
|
||||
|
||||
FunctionConfig *yaml.RNode
|
||||
|
||||
Results *yaml.RNode
|
||||
|
||||
// DisableUnwrapping prevents Resources in Lists and ResourceLists from being unwrapped
|
||||
DisableUnwrapping bool
|
||||
|
||||
@@ -142,10 +148,12 @@ func (r *ByteReader) Read() ([]*yaml.RNode, error) {
|
||||
r.WrappingAPIVersion = meta.APIVersion
|
||||
|
||||
// unwrap the list
|
||||
fc := node.Field("functionConfig")
|
||||
if fc != nil {
|
||||
if fc := node.Field("functionConfig"); fc != nil {
|
||||
r.FunctionConfig = fc.Value
|
||||
}
|
||||
if res := node.Field("results"); res != nil {
|
||||
r.Results = res.Value
|
||||
}
|
||||
|
||||
items := node.Field("items")
|
||||
if items != nil {
|
||||
|
||||
@@ -5,36 +5,64 @@ package kio_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
. "sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
// getByteReaderTestInput returns test input
|
||||
func getByteReaderTestInput(t *testing.T) *bytes.Buffer {
|
||||
b := &bytes.Buffer{}
|
||||
_, err := b.WriteString(`
|
||||
---
|
||||
a: b # first resource
|
||||
c: d
|
||||
---
|
||||
# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
---
|
||||
---
|
||||
i: j
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
assert.FailNow(t, "")
|
||||
func TestByteReader(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
input string
|
||||
err string
|
||||
expectedItems []string
|
||||
expectedFunctionConfig string
|
||||
expectedResults string
|
||||
wrappingAPIVersion string
|
||||
wrappingAPIKind string
|
||||
instance ByteReader
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func TestByteReader_Read_wrappedResourceßßList(t *testing.T) {
|
||||
r := &ByteReader{Reader: bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1
|
||||
testCases := []testCase{
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "wrapped_resource_list",
|
||||
input: `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
expectedItems: []string{
|
||||
`kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
`,
|
||||
`kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
wrappingAPIVersion: ResourceListAPIVersion,
|
||||
wrappingAPIKind: ResourceListKind,
|
||||
},
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "wrapped_resource_list_function_config",
|
||||
input: `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
functionConfig:
|
||||
foo: bar
|
||||
@@ -50,109 +78,87 @@ items:
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`)}
|
||||
nodes, err := r.Read()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// verify the contents
|
||||
if !assert.Len(t, nodes, 2) {
|
||||
return
|
||||
}
|
||||
expected := []string{
|
||||
`kind: Deployment
|
||||
`,
|
||||
expectedItems: []string{
|
||||
`kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
`,
|
||||
`kind: Service
|
||||
`kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
if !assert.Equal(t, expected[i], nodes[i].MustString()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// verify the function config
|
||||
assert.Equal(t, `foo: bar
|
||||
},
|
||||
expectedFunctionConfig: `foo: bar
|
||||
elems:
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
`, r.FunctionConfig.MustString())
|
||||
- c`,
|
||||
wrappingAPIVersion: ResourceListAPIVersion,
|
||||
wrappingAPIKind: ResourceListKind,
|
||||
},
|
||||
|
||||
assert.Equal(t, ResourceListKind, r.WrappingKind)
|
||||
assert.Equal(t, ResourceListAPIVersion, r.WrappingAPIVersion)
|
||||
}
|
||||
|
||||
func TestByteReader_Read_wrappedList(t *testing.T) {
|
||||
r := &ByteReader{Reader: bytes.NewBufferString(`apiVersion: v1
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "wrapped_list",
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`)}
|
||||
nodes, err := r.Read()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// verify the contents
|
||||
if !assert.Len(t, nodes, 2) {
|
||||
return
|
||||
}
|
||||
expected := []string{
|
||||
`kind: Deployment
|
||||
`,
|
||||
expectedItems: []string{
|
||||
`
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
`,
|
||||
`kind: Service
|
||||
`
|
||||
kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
if !assert.Equal(t, expected[i], nodes[i].MustString()) {
|
||||
return
|
||||
}
|
||||
}
|
||||
},
|
||||
wrappingAPIKind: "List",
|
||||
wrappingAPIVersion: "v1",
|
||||
},
|
||||
|
||||
// verify the function config
|
||||
assert.Nil(t, r.FunctionConfig)
|
||||
assert.Equal(t, "List", r.WrappingKind)
|
||||
assert.Equal(t, "v1", r.WrappingAPIVersion)
|
||||
}
|
||||
|
||||
// TestByteReader_Read tests the default Read behavior
|
||||
// - Resources are read into a slice
|
||||
// - ReaderAnnotations are set on the ResourceNodes
|
||||
func TestByteReader_Read(t *testing.T) {
|
||||
nodes, err := (&ByteReader{Reader: getByteReaderTestInput(t)}).Read()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Len(t, nodes, 3) {
|
||||
return
|
||||
}
|
||||
expected := []string{
|
||||
`a: b # first resource
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "unwrapped_items",
|
||||
input: `
|
||||
---
|
||||
a: b # first resource
|
||||
c: d
|
||||
---
|
||||
# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
---
|
||||
---
|
||||
i: j
|
||||
`,
|
||||
expectedItems: []string{
|
||||
`a: b # first resource
|
||||
c: d
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
`,
|
||||
`# second resource
|
||||
`# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
@@ -160,150 +166,209 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
`,
|
||||
`i: j
|
||||
`i: j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '2'
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
val, err := nodes[i].String()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, expected[i], val) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// TestByteReader_Read_omitReaderAnnotations tests
|
||||
// - Resources are read into a slice
|
||||
// - ReaderAnnotations are not set on the ResourceNodes
|
||||
func TestByteReader_Read_omitReaderAnnotations(t *testing.T) {
|
||||
nodes, err := (&ByteReader{
|
||||
Reader: getByteReaderTestInput(t),
|
||||
OmitReaderAnnotations: true}).Read()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
// should have parsed 3 resources
|
||||
if !assert.Len(t, nodes, 3) {
|
||||
return
|
||||
}
|
||||
expected := []string{
|
||||
"a: b # first resource\nc: d\n",
|
||||
"# second resource\ne: f\ng:\n- h\n",
|
||||
"i: j\n",
|
||||
}
|
||||
for i := range nodes {
|
||||
val, err := nodes[i].String()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, expected[i], val) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestByteReader_Read_omitReaderAnnotations tests
|
||||
// - Resources are read into a slice
|
||||
// - ReaderAnnotations are NOT set on the ResourceNodes
|
||||
// - Additional annotations ARE set on the ResourceNodes
|
||||
func TestByteReader_Read_setAnnotationsOmitReaderAnnotations(t *testing.T) {
|
||||
nodes, err := (&ByteReader{
|
||||
Reader: getByteReaderTestInput(t),
|
||||
SetAnnotations: map[string]string{"foo": "bar"},
|
||||
OmitReaderAnnotations: true,
|
||||
}).Read()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Len(t, nodes, 3) {
|
||||
return
|
||||
}
|
||||
expected := []string{
|
||||
`a: b # first resource
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "omit_annotations",
|
||||
input: `
|
||||
---
|
||||
a: b # first resource
|
||||
c: d
|
||||
metadata:
|
||||
annotations:
|
||||
foo: 'bar'
|
||||
`,
|
||||
`# second resource
|
||||
---
|
||||
# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
metadata:
|
||||
annotations:
|
||||
foo: 'bar'
|
||||
---
|
||||
---
|
||||
i: j
|
||||
`,
|
||||
`i: j
|
||||
metadata:
|
||||
annotations:
|
||||
foo: 'bar'
|
||||
expectedItems: []string{
|
||||
`
|
||||
a: b # first resource
|
||||
c: d
|
||||
`,
|
||||
}
|
||||
for i := range nodes {
|
||||
val, err := nodes[i].String()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, expected[i], val) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
`,
|
||||
`
|
||||
i: j
|
||||
`,
|
||||
},
|
||||
instance: ByteReader{OmitReaderAnnotations: true},
|
||||
},
|
||||
|
||||
// TestByteReader_Read_omitReaderAnnotations tests
|
||||
// - Resources are read into a slice
|
||||
// - ReaderAnnotations ARE set on the ResourceNodes
|
||||
// - Additional annotations ARE set on the ResourceNodes
|
||||
func TestByteReader_Read_setAnnotations(t *testing.T) {
|
||||
nodes, err := (&ByteReader{
|
||||
Reader: getByteReaderTestInput(t),
|
||||
SetAnnotations: map[string]string{"foo": "bar"},
|
||||
}).Read()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Len(t, nodes, 3) {
|
||||
return
|
||||
}
|
||||
expected := []string{
|
||||
`a: b # first resource
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "no_omit_annotations",
|
||||
input: `
|
||||
---
|
||||
a: b # first resource
|
||||
c: d
|
||||
---
|
||||
# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
---
|
||||
---
|
||||
i: j
|
||||
`,
|
||||
expectedItems: []string{
|
||||
`
|
||||
a: b # first resource
|
||||
c: d
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
foo: 'bar'
|
||||
`,
|
||||
`# second resource
|
||||
`
|
||||
# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
foo: 'bar'
|
||||
`,
|
||||
`i: j
|
||||
`
|
||||
i: j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '2'
|
||||
`,
|
||||
},
|
||||
instance: ByteReader{},
|
||||
},
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "set_annotation",
|
||||
input: `
|
||||
---
|
||||
a: b # first resource
|
||||
c: d
|
||||
---
|
||||
# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
---
|
||||
---
|
||||
i: j
|
||||
`,
|
||||
expectedItems: []string{
|
||||
`a: b # first resource
|
||||
c: d
|
||||
metadata:
|
||||
annotations:
|
||||
foo: 'bar'
|
||||
`,
|
||||
`# second resource
|
||||
e: f
|
||||
g:
|
||||
- h
|
||||
metadata:
|
||||
annotations:
|
||||
foo: 'bar'
|
||||
`,
|
||||
`i: j
|
||||
metadata:
|
||||
annotations:
|
||||
foo: 'bar'
|
||||
`,
|
||||
},
|
||||
instance: ByteReader{
|
||||
OmitReaderAnnotations: true,
|
||||
SetAnnotations: map[string]string{"foo": "bar"}},
|
||||
},
|
||||
}
|
||||
for i := range nodes {
|
||||
val, err := nodes[i].String()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
if !assert.Equal(t, expected[i], val) {
|
||||
return
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := tc.instance
|
||||
r.Reader = bytes.NewBufferString(tc.input)
|
||||
nodes, err := r.Read()
|
||||
if tc.err != "" {
|
||||
if !assert.EqualError(t, err, tc.err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// verify the contents
|
||||
if !assert.Len(t, nodes, len(tc.expectedItems)) {
|
||||
t.FailNow()
|
||||
}
|
||||
for i := range nodes {
|
||||
actual, err := nodes[i].String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedItems[i]),
|
||||
strings.TrimSpace(actual)) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
// verify the function config
|
||||
if tc.expectedFunctionConfig != "" {
|
||||
actual, err := r.FunctionConfig.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedFunctionConfig),
|
||||
strings.TrimSpace(actual)) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else if !assert.Nil(t, r.FunctionConfig) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if tc.expectedResults != "" {
|
||||
actual, err := r.Results.String()
|
||||
actual = strings.TrimSpace(actual)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
tc.expectedResults = strings.TrimSpace(tc.expectedResults)
|
||||
if !assert.Equal(t, tc.expectedResults, actual) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else if !assert.Nil(t, r.Results) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, tc.wrappingAPIKind, r.WrappingKind) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, tc.wrappingAPIVersion, r.WrappingAPIVersion) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
263
kyaml/kio/byteio_readwriter_test.go
Normal file
263
kyaml/kio/byteio_readwriter_test.go
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func TestByteReadWriter(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
err string
|
||||
input string
|
||||
expectedOutput string
|
||||
instance kio.ByteReadWriter
|
||||
}
|
||||
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "round_trip",
|
||||
input: `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "function_config",
|
||||
input: `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
functionConfig:
|
||||
a: b # something
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
functionConfig:
|
||||
a: b # something
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "results",
|
||||
input: `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
results:
|
||||
a: b # something
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
results:
|
||||
a: b # something
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "drop_invalid_resource_list_field",
|
||||
input: `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
foo:
|
||||
a: b # something
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "list",
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: v1
|
||||
kind: List
|
||||
items:
|
||||
- kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
- kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "multiple_documents",
|
||||
input: `
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
expectedOutput: `
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "keep_annotations",
|
||||
input: `
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
---
|
||||
kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
`,
|
||||
expectedOutput: `
|
||||
kind: Deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
---
|
||||
kind: Service
|
||||
spec:
|
||||
selectors:
|
||||
foo: bar
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: '1'
|
||||
`,
|
||||
instance: kio.ByteReadWriter{KeepReaderAnnotations: true},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var in, out bytes.Buffer
|
||||
in.WriteString(tc.input)
|
||||
w := tc.instance
|
||||
w.Writer = &out
|
||||
w.Reader = &in
|
||||
|
||||
nodes, err := w.Read()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
err = w.Write(nodes)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if tc.err != "" {
|
||||
if !assert.EqualError(t, err, tc.err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(out.String())) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,8 @@ type ByteWriter struct {
|
||||
// wrap the results in an ResourceList.
|
||||
FunctionConfig *yaml.RNode
|
||||
|
||||
Results *yaml.RNode
|
||||
|
||||
// WrappingKind if set will cause ByteWriter to wrap the Resources in
|
||||
// an 'items' field in this kind. e.g. if WrappingKind is 'List',
|
||||
// ByteWriter will wrap the Resources in a List .items field.
|
||||
@@ -112,6 +114,11 @@ func (w ByteWriter) Write(nodes []*yaml.RNode) error {
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: "functionConfig"},
|
||||
w.FunctionConfig.YNode())
|
||||
}
|
||||
if w.Results != nil {
|
||||
list.Content = append(list.Content,
|
||||
&yaml.Node{Kind: yaml.ScalarNode, Value: "results"},
|
||||
w.Results.YNode())
|
||||
}
|
||||
doc := &yaml.Node{
|
||||
Kind: yaml.DocumentNode,
|
||||
Content: []*yaml.Node{list}}
|
||||
|
||||
@@ -1,96 +1,84 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package kio
|
||||
package kio_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
. "sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// TestByteWriter_Write_withoutAnnotations tests:
|
||||
// - Resource Config ordering is preserved if no annotations are present
|
||||
func TestByteWriter_Write_wrapped(t *testing.T) {
|
||||
node1, err := yaml.Parse(`a: b #first
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
func TestByteWriter(t *testing.T) {
|
||||
type testCase struct {
|
||||
name string
|
||||
err string
|
||||
items []string
|
||||
functionConfig string
|
||||
results string
|
||||
expectedOutput string
|
||||
instance kio.ByteWriter
|
||||
}
|
||||
node2, err := yaml.Parse(`c: d # second
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node3, err := yaml.Parse(`e: f
|
||||
|
||||
testCases := []testCase{
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "wrap_resource_list",
|
||||
instance: ByteWriter{
|
||||
Sort: true,
|
||||
WrappingKind: ResourceListKind,
|
||||
WrappingAPIVersion: ResourceListAPIVersion,
|
||||
},
|
||||
items: []string{
|
||||
`a: b #first`,
|
||||
`c: d # second`,
|
||||
},
|
||||
functionConfig: `
|
||||
e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
- j
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
err = ByteWriter{
|
||||
Sort: true,
|
||||
Writer: buff,
|
||||
FunctionConfig: node3,
|
||||
WrappingKind: ResourceListKind,
|
||||
WrappingAPIVersion: ResourceListAPIVersion}.
|
||||
Write([]*yaml.RNode{node2, node1})
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
- j`,
|
||||
expectedOutput: `apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- c: d # second
|
||||
- a: b #first
|
||||
- c: d # second
|
||||
functionConfig:
|
||||
e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
- j
|
||||
`, buff.String())
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
// TestByteWriter_Write_withoutAnnotations tests:
|
||||
// - Resource Config ordering is preserved if no annotations are present
|
||||
func TestByteWriter_Write_withoutAnnotations(t *testing.T) {
|
||||
node1, err := yaml.Parse(`a: b #first
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node2, err := yaml.Parse(`c: d # second
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node3, err := yaml.Parse(`e: f
|
||||
//
|
||||
//
|
||||
//
|
||||
{
|
||||
name: "multiple_items",
|
||||
items: []string{
|
||||
`c: d # second`,
|
||||
`e: f
|
||||
g:
|
||||
h:
|
||||
# has a list
|
||||
- i : [i1, i2] # line comment
|
||||
# has a list 2
|
||||
- j : j1
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
err = ByteWriter{Writer: buff}.
|
||||
Write([]*yaml.RNode{node2, node3, node1})
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, `c: d # second
|
||||
`,
|
||||
`a: b #first`,
|
||||
},
|
||||
expectedOutput: `
|
||||
c: d # second
|
||||
---
|
||||
e: f
|
||||
g:
|
||||
@@ -101,32 +89,23 @@ g:
|
||||
- j: j1
|
||||
---
|
||||
a: b #first
|
||||
`, buff.String())
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
// TestByteWriter_Write_withAnnotationsKeepAnnotations tests:
|
||||
// - Resource Config is sorted by annotations if present
|
||||
// - IndexAnnotations are retained
|
||||
func TestByteWriter_Write_withAnnotationsKeepAnnotations(t *testing.T) {
|
||||
node1, err := yaml.Parse(`a: b #first
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{
|
||||
name: "sort_keep_annotation",
|
||||
instance: ByteWriter{Sort: true, KeepReaderAnnotations: true},
|
||||
items: []string{
|
||||
`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node2, err := yaml.Parse(`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node3, err := yaml.Parse(`e: f
|
||||
`,
|
||||
`e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
@@ -135,18 +114,16 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`,
|
||||
},
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
err = ByteWriter{Sort: true, Writer: buff, KeepReaderAnnotations: true}.
|
||||
Write([]*yaml.RNode{node2, node3, node1})
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, `a: b #first
|
||||
expectedOutput: `a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
@@ -167,109 +144,36 @@ metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
`, buff.String())
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
// TestByteWriter_Write_withAnnotations tests:
|
||||
// - Resource Config is sorted by annotations if present
|
||||
// - IndexAnnotations are pruned
|
||||
func TestByteWriter_Write_withAnnotations(t *testing.T) {
|
||||
node1, err := yaml.Parse(`a: b #first
|
||||
//
|
||||
// Test Case
|
||||
//
|
||||
{
|
||||
name: "sort_partial_annotations",
|
||||
instance: ByteWriter{Sort: true},
|
||||
items: []string{
|
||||
`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node2, err := yaml.Parse(`c: d # second
|
||||
`,
|
||||
`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node3, err := yaml.Parse(`e: f
|
||||
`,
|
||||
`e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
- j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 0
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
`,
|
||||
},
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
err = ByteWriter{Sort: true, Writer: buff}.
|
||||
Write([]*yaml.RNode{node2, node3, node1})
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, `a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
---
|
||||
c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
---
|
||||
e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
- j
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: "a/b/b_test.yaml"
|
||||
`, buff.String())
|
||||
}
|
||||
|
||||
// TestByteWriter_Write_partialValues tests:
|
||||
// - Resource Config is sorted when annotations are present on some but not all ResourceNodes
|
||||
func TestByteWriter_Write_partialAnnotations(t *testing.T) {
|
||||
node1, err := yaml.Parse(`a: b #first
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node2, err := yaml.Parse(`c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/index: 1
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
node3, err := yaml.Parse(`e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
- j
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
rw := ByteWriter{Sort: true, Writer: buff}
|
||||
err = rw.Write([]*yaml.RNode{node2, node3, node1})
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, `e: f
|
||||
expectedOutput: `e: f
|
||||
g:
|
||||
h:
|
||||
- i # has a list
|
||||
@@ -284,5 +188,45 @@ c: d # second
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/path: "a/b/a_test.yaml"
|
||||
`, buff.String())
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actual := &bytes.Buffer{}
|
||||
w := tc.instance
|
||||
w.Writer = actual
|
||||
|
||||
if tc.functionConfig != "" {
|
||||
w.FunctionConfig = yaml.MustParse(tc.functionConfig)
|
||||
}
|
||||
|
||||
if tc.results != "" {
|
||||
w.Results = yaml.MustParse(tc.results)
|
||||
}
|
||||
|
||||
var items []*yaml.RNode
|
||||
for i := range tc.items {
|
||||
items = append(items, yaml.MustParse(tc.items[i]))
|
||||
}
|
||||
err := w.Write(items)
|
||||
|
||||
if tc.err != "" {
|
||||
if !assert.EqualError(t, err, tc.err) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(actual.String())) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package filters
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
@@ -135,7 +136,7 @@ type ContainerFilter struct {
|
||||
Network string `yaml:"network,omitempty"`
|
||||
|
||||
// StorageMounts is a list of storage options that the container will have mounted.
|
||||
StorageMounts []StorageMount
|
||||
StorageMounts []StorageMount `yaml:"mounts,omitempty"`
|
||||
|
||||
// Config is the API configuration for the container and passed through the
|
||||
// API_CONFIG env var to the container.
|
||||
@@ -146,6 +147,13 @@ type ContainerFilter struct {
|
||||
// nodes instead of only nodes scoped under the function.
|
||||
GlobalScope bool
|
||||
|
||||
ResultsFile string
|
||||
|
||||
Results *yaml.RNode
|
||||
|
||||
// SetFlowStyleForConfig sets the style for config to Flow when serializing it
|
||||
SetFlowStyleForConfig bool
|
||||
|
||||
// args may be specified by tests to override how a container is spawned
|
||||
args []string
|
||||
|
||||
@@ -156,25 +164,31 @@ func (c ContainerFilter) String() string {
|
||||
return c.Image
|
||||
}
|
||||
|
||||
// StorageMount represents a container's mounted storage option(s)
|
||||
type StorageMount struct {
|
||||
// Type of mount e.g. bind mount, local volume, etc.
|
||||
MountType string
|
||||
|
||||
// Source for the storage to be mounted.
|
||||
// For named volumes, this is the name of the volume.
|
||||
// For anonymous volumes, this field is omitted (empty string).
|
||||
// For bind mounts, this is the path to the file or directory on the host.
|
||||
Src string
|
||||
|
||||
// The path where the file or directory is mounted in the container.
|
||||
DstPath string
|
||||
}
|
||||
|
||||
func (s *StorageMount) String() string {
|
||||
return fmt.Sprintf("type=%s,src=%s,dst=%s:ro", s.MountType, s.Src, s.DstPath)
|
||||
}
|
||||
|
||||
func StringToStorageMount(s string) StorageMount {
|
||||
m := make(map[string]string)
|
||||
options := strings.Split(s, ",")
|
||||
for _, option := range options {
|
||||
keyVal := strings.SplitN(option, "=", 2)
|
||||
m[keyVal[0]] = keyVal[1]
|
||||
}
|
||||
var sm StorageMount
|
||||
for key, value := range m {
|
||||
switch {
|
||||
case key == "type":
|
||||
sm.MountType = value
|
||||
case key == "src":
|
||||
sm.Src = value
|
||||
case key == "dst":
|
||||
sm.DstPath = value
|
||||
}
|
||||
}
|
||||
return sm
|
||||
}
|
||||
|
||||
// functionsDirectoryName is keyword directory name for functions scoped 1 directory higher
|
||||
const functionsDirectoryName = "functions"
|
||||
|
||||
@@ -251,10 +265,7 @@ func (c *ContainerFilter) scope(dir string, nodes []*yaml.RNode) ([]*yaml.RNode,
|
||||
// GrepFilter implements kio.GrepFilter
|
||||
func (c *ContainerFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
// get the command to filter the Resources
|
||||
cmd, err := c.getCommand()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := c.getCommand()
|
||||
|
||||
in := &bytes.Buffer{}
|
||||
out := &bytes.Buffer{}
|
||||
@@ -290,7 +301,16 @@ func (c *ContainerFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
cmd.Stdin = in
|
||||
cmd.Stdout = out
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, err
|
||||
// write the results file on failure
|
||||
results, e := r.Read()
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
if e = c.doResults(r); e != nil {
|
||||
return nil, e
|
||||
}
|
||||
// return the results from the function even on failure
|
||||
return results, err
|
||||
}
|
||||
|
||||
output, err := r.Read()
|
||||
@@ -298,6 +318,10 @@ func (c *ContainerFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.doResults(r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// annotate any generated Resources with a path and index if they don't already have one
|
||||
if err := kioutil.DefaultPathAnnotation(functionDir, output); err != nil {
|
||||
return nil, err
|
||||
@@ -308,6 +332,25 @@ func (c *ContainerFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return append(output, saved...), nil
|
||||
}
|
||||
|
||||
func (c *ContainerFilter) doResults(r *kio.ByteReader) error {
|
||||
// Write the results to a file if configured to do so
|
||||
if c.ResultsFile != "" && r.Results != nil {
|
||||
results, err := r.Results.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(c.ResultsFile, []byte(results), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if r.Results != nil {
|
||||
c.Results = r.Results
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getArgs returns the command + args to run to spawn the container
|
||||
func (c *ContainerFilter) getArgs() []string {
|
||||
// run the container using docker. this is simpler than using the docker
|
||||
@@ -335,6 +378,9 @@ func (c *ContainerFilter) getArgs() []string {
|
||||
args = append(args, "--mount", storageMount.String())
|
||||
}
|
||||
|
||||
// tell functions to write error messages to stderr as well as results
|
||||
os.Setenv("LOG_TO_STDERR", "true")
|
||||
|
||||
// export the local environment vars to the container
|
||||
for _, pair := range os.Environ() {
|
||||
tokens := strings.Split(pair, "=")
|
||||
@@ -347,17 +393,9 @@ func (c *ContainerFilter) getArgs() []string {
|
||||
}
|
||||
|
||||
// getCommand returns a command which will apply the Filter using the container image
|
||||
func (c *ContainerFilter) getCommand() (*exec.Cmd, error) {
|
||||
// encode the filter command API configuration
|
||||
cfg := &bytes.Buffer{}
|
||||
if err := func() error {
|
||||
e := yaml.NewEncoder(cfg)
|
||||
defer e.Close()
|
||||
// make it fit on a single line
|
||||
func (c *ContainerFilter) getCommand() *exec.Cmd {
|
||||
if c.SetFlowStyleForConfig {
|
||||
c.Config.YNode().Style = yaml.FlowStyle
|
||||
return e.Encode(c.Config.YNode())
|
||||
}(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(c.args) == 0 {
|
||||
@@ -369,7 +407,7 @@ func (c *ContainerFilter) getCommand() (*exec.Cmd, error) {
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
// set stderr for err messaging
|
||||
return cmd, nil
|
||||
return cmd
|
||||
}
|
||||
|
||||
// IsReconcilerFilter filters Resources based on whether or not they are Reconciler Resource.
|
||||
|
||||
@@ -6,6 +6,7 @@ package filters
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -15,6 +16,367 @@ import (
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestContainerFilter_Filter(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
input []string
|
||||
expectedOutput []string
|
||||
expectedError string
|
||||
expectedResults string
|
||||
noMakeResultsFile bool
|
||||
instance ContainerFilter
|
||||
}{
|
||||
{
|
||||
name: "add_path_annotation",
|
||||
instance: ContainerFilter{args: []string{
|
||||
"echo", `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
`,
|
||||
},
|
||||
},
|
||||
expectedOutput: []string{
|
||||
`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
`,
|
||||
`
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "write_results",
|
||||
instance: ContainerFilter{args: []string{
|
||||
"echo", `
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
results:
|
||||
- apiVersion: config.k8s.io/v1alpha1
|
||||
kind: ObjectError
|
||||
name: "some-validator"
|
||||
items:
|
||||
- type: error
|
||||
message: "some message"
|
||||
resourceRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: foo
|
||||
namespace: bar
|
||||
file:
|
||||
path: deploy.yaml
|
||||
index: 0
|
||||
field:
|
||||
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||
currentValue: "200"
|
||||
suggestedValue: "2"
|
||||
`,
|
||||
},
|
||||
},
|
||||
expectedOutput: []string{
|
||||
`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
`,
|
||||
`
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`,
|
||||
},
|
||||
expectedResults: `
|
||||
- apiVersion: config.k8s.io/v1alpha1
|
||||
kind: ObjectError
|
||||
name: "some-validator"
|
||||
items:
|
||||
- type: error
|
||||
message: "some message"
|
||||
resourceRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: foo
|
||||
namespace: bar
|
||||
file:
|
||||
path: deploy.yaml
|
||||
index: 0
|
||||
field:
|
||||
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||
currentValue: "200"
|
||||
suggestedValue: "2"
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "write_results_non_0_exit",
|
||||
expectedError: "exit status 1",
|
||||
instance: ContainerFilter{args: []string{"sh", "-c",
|
||||
`echo '
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
results:
|
||||
- apiVersion: config.k8s.io/v1alpha1
|
||||
kind: ObjectError
|
||||
name: "some-validator"
|
||||
items:
|
||||
- type: error
|
||||
message: "some message"
|
||||
resourceRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: foo
|
||||
namespace: bar
|
||||
file:
|
||||
path: deploy.yaml
|
||||
index: 0
|
||||
field:
|
||||
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||
currentValue: "200"
|
||||
suggestedValue: "2"
|
||||
' && cat not-real-dir
|
||||
`,
|
||||
},
|
||||
},
|
||||
expectedOutput: []string{
|
||||
`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
`,
|
||||
`
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`,
|
||||
},
|
||||
expectedResults: `
|
||||
- apiVersion: config.k8s.io/v1alpha1
|
||||
kind: ObjectError
|
||||
name: "some-validator"
|
||||
items:
|
||||
- type: error
|
||||
message: "some message"
|
||||
resourceRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: foo
|
||||
namespace: bar
|
||||
file:
|
||||
path: deploy.yaml
|
||||
index: 0
|
||||
field:
|
||||
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||
currentValue: "200"
|
||||
suggestedValue: "2"
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "write_results_non_0_exit_missing_file",
|
||||
expectedError: "open /not/real/file: no such file or directory",
|
||||
noMakeResultsFile: true,
|
||||
instance: ContainerFilter{args: []string{"sh", "-c",
|
||||
`echo '
|
||||
apiVersion: config.kubernetes.io/v1alpha1
|
||||
kind: ResourceList
|
||||
items:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
- apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
results:
|
||||
- apiVersion: config.k8s.io/v1alpha1
|
||||
kind: ObjectError
|
||||
name: "some-validator"
|
||||
items:
|
||||
- type: error
|
||||
message: "some message"
|
||||
resourceRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: foo
|
||||
namespace: bar
|
||||
file:
|
||||
path: deploy.yaml
|
||||
index: 0
|
||||
field:
|
||||
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||
currentValue: "200"
|
||||
suggestedValue: "2"
|
||||
' && cat not-real-dir
|
||||
`,
|
||||
},
|
||||
},
|
||||
expectedOutput: []string{
|
||||
`
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deployment-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'deployment_deployment-foo.yaml'
|
||||
`,
|
||||
`
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-foo
|
||||
annotations:
|
||||
config.kubernetes.io/path: 'service_service-foo.yaml'
|
||||
`,
|
||||
},
|
||||
expectedResults: `
|
||||
- apiVersion: config.k8s.io/v1alpha1
|
||||
kind: ObjectError
|
||||
name: "some-validator"
|
||||
items:
|
||||
- type: error
|
||||
message: "some message"
|
||||
resourceRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: foo
|
||||
namespace: bar
|
||||
file:
|
||||
path: deploy.yaml
|
||||
index: 0
|
||||
field:
|
||||
path: "spec.template.spec.containers[3].resources.limits.cpu"
|
||||
currentValue: "200"
|
||||
suggestedValue: "2"
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
tt := tests[i]
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if len(tt.expectedResults) > 0 && !tt.noMakeResultsFile {
|
||||
f, err := ioutil.TempFile("", "test-kyaml-*.yaml")
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
defer os.RemoveAll(f.Name())
|
||||
tt.instance.ResultsFile = f.Name()
|
||||
} else if len(tt.expectedResults) > 0 {
|
||||
tt.instance.ResultsFile = "/not/real/file"
|
||||
}
|
||||
|
||||
var inputs []*yaml.RNode
|
||||
for i := range tt.input {
|
||||
node, err := yaml.Parse(tt.input[i])
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
inputs = append(inputs, node)
|
||||
}
|
||||
|
||||
output, err := tt.instance.Filter(inputs)
|
||||
if tt.expectedError != "" {
|
||||
if !assert.EqualError(t, err, tt.expectedError) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
var actual []string
|
||||
for i := range output {
|
||||
s, err := output[i].String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
actual = append(actual, strings.TrimSpace(s))
|
||||
}
|
||||
var expected []string
|
||||
for i := range tt.expectedOutput {
|
||||
expected = append(expected, strings.TrimSpace(tt.expectedOutput[i]))
|
||||
}
|
||||
|
||||
if !assert.Equal(t, expected, actual) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if len(tt.instance.ResultsFile) > 0 {
|
||||
tt.expectedResults = strings.TrimSpace(tt.expectedResults)
|
||||
|
||||
results, err := tt.instance.Results.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, tt.expectedResults, strings.TrimSpace(results)) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(tt.instance.ResultsFile)
|
||||
writtenResults := strings.TrimSpace(string(b))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, tt.expectedResults, writtenResults) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilter_command(t *testing.T) {
|
||||
cfg, err := yaml.Parse(`apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -29,10 +391,7 @@ metadata:
|
||||
Config: cfg,
|
||||
}
|
||||
os.Setenv("KYAML_TEST", "FOO")
|
||||
cmd, err := instance.getCommand()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
cmd := instance.getCommand()
|
||||
|
||||
expected := []string{
|
||||
"docker", "run",
|
||||
@@ -78,10 +437,7 @@ metadata:
|
||||
Config: cfg,
|
||||
StorageMounts: []StorageMount{bindMount, localVol, tmpfs},
|
||||
}
|
||||
cmd, err := instance.getCommand()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
cmd := instance.getCommand()
|
||||
|
||||
expected := []string{
|
||||
"docker", "run",
|
||||
@@ -116,10 +472,7 @@ metadata:
|
||||
Network: "test-net",
|
||||
Config: cfg,
|
||||
}
|
||||
cmd, err := instance.getCommand()
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
cmd := instance.getCommand()
|
||||
|
||||
expected := []string{
|
||||
"docker", "run",
|
||||
@@ -168,9 +521,10 @@ metadata:
|
||||
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
SetFlowStyleForConfig: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
@@ -252,9 +606,10 @@ metadata:
|
||||
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sh", "-c", "cat <&0"},
|
||||
SetFlowStyleForConfig: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sh", "-c", "cat <&0"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
@@ -334,6 +689,68 @@ container:
|
||||
image: foo:v1.0.0`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "storage mounts json style",
|
||||
resource: `
|
||||
apiVersion: v1beta1
|
||||
kind: Example
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/function: |-
|
||||
container:
|
||||
image: foo:v1.0.0
|
||||
mounts: [ {type: bind, src: /mount/path, dst: /local/}, {src: myvol, dst: /local/, type: volume}, {dst: /local/, type: tmpfs} ]
|
||||
`,
|
||||
expectedFn: `
|
||||
container:
|
||||
image: foo:v1.0.0
|
||||
mounts:
|
||||
- type: bind
|
||||
src: /mount/path
|
||||
dst: /local/
|
||||
- type: volume
|
||||
src: myvol
|
||||
dst: /local/
|
||||
- type: tmpfs
|
||||
dst: /local/
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "storage mounts yaml style",
|
||||
resource: `
|
||||
apiVersion: v1beta1
|
||||
kind: Example
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/function: |-
|
||||
container:
|
||||
image: foo:v1.0.0
|
||||
mounts:
|
||||
- src: /mount/path
|
||||
type: bind
|
||||
dst: /local/
|
||||
- dst: /local/
|
||||
src: myvol
|
||||
type: volume
|
||||
- type: tmpfs
|
||||
dst: /local/
|
||||
`,
|
||||
expectedFn: `
|
||||
container:
|
||||
image: foo:v1.0.0
|
||||
mounts:
|
||||
- type: bind
|
||||
src: /mount/path
|
||||
dst: /local/
|
||||
- type: volume
|
||||
src: myvol
|
||||
dst: /local/
|
||||
- type: tmpfs
|
||||
dst: /local/
|
||||
`,
|
||||
},
|
||||
|
||||
{
|
||||
name: "network",
|
||||
resource: `
|
||||
@@ -535,8 +952,9 @@ metadata:
|
||||
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
SetFlowStyleForConfig: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"echo", `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -609,8 +1027,9 @@ metadata:
|
||||
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
SetFlowStyleForConfig: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"echo", `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -694,9 +1113,10 @@ metadata:
|
||||
// no resources match the scope
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
SetFlowStyleForConfig: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
@@ -769,10 +1189,11 @@ metadata:
|
||||
// no resources match the scope
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
GlobalScope: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
SetFlowStyleForConfig: true,
|
||||
GlobalScope: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
@@ -864,9 +1285,10 @@ metadata:
|
||||
// no resources match the scope
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
SetFlowStyleForConfig: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
@@ -960,9 +1382,10 @@ metadata:
|
||||
// no resources match the scope
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
SetFlowStyleForConfig: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
@@ -1056,9 +1479,10 @@ metadata:
|
||||
// no resources match the scope
|
||||
called := false
|
||||
result, err := (&ContainerFilter{
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
SetFlowStyleForConfig: true,
|
||||
Image: "example.com:version",
|
||||
Config: cfg,
|
||||
args: []string{"sed", "s/Deployment/StatefulSet/g"},
|
||||
checkInput: func(s string) {
|
||||
called = true
|
||||
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package filters
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -17,9 +16,6 @@ var functionAnnotationKeys = []string{FunctionAnnotationKey, oldFunctionAnnotati
|
||||
|
||||
// FunctionSpec defines a spec for running a function
|
||||
type FunctionSpec struct {
|
||||
// Path defines the path for scoped functions
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
|
||||
// Network is the name of the network to use from a container
|
||||
Network string `json:"network,omitempty" yaml:"network,omitempty"`
|
||||
|
||||
@@ -28,6 +24,9 @@ type FunctionSpec struct {
|
||||
|
||||
// Starlark is the spec for running a function as a starlark script
|
||||
Starlark StarlarkSpec `json:"starlark,omitempty" yaml:"starlark,omitempty"`
|
||||
|
||||
// Mounts are the storage or directories to mount into the container
|
||||
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
||||
}
|
||||
|
||||
// ContainerSpec defines a spec for running a function as a container
|
||||
@@ -37,6 +36,9 @@ type ContainerSpec struct {
|
||||
|
||||
// Network defines network specific configuration
|
||||
Network ContainerNetwork `json:"network,omitempty" yaml:"network,omitempty"`
|
||||
|
||||
// Mounts are the storage or directories to mount into the container
|
||||
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
||||
}
|
||||
|
||||
// ContainerNetwork
|
||||
@@ -53,6 +55,21 @@ type StarlarkSpec struct {
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
}
|
||||
|
||||
// StorageMount represents a container's mounted storage option(s)
|
||||
type StorageMount struct {
|
||||
// Type of mount e.g. bind mount, local volume, etc.
|
||||
MountType string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
|
||||
// Source for the storage to be mounted.
|
||||
// For named volumes, this is the name of the volume.
|
||||
// For anonymous volumes, this field is omitted (empty string).
|
||||
// For bind mounts, this is the path to the file or directory on the host.
|
||||
Src string `json:"src,omitempty" yaml:"src,omitempty"`
|
||||
|
||||
// The path where the file or directory is mounted in the container.
|
||||
DstPath string `json:"dst,omitempty" yaml:"dst,omitempty"`
|
||||
}
|
||||
|
||||
// GetFunctionSpec returns the FunctionSpec for a resource. Returns
|
||||
// nil if the resource does not have a FunctionSpec.
|
||||
//
|
||||
@@ -64,19 +81,16 @@ func GetFunctionSpec(n *yaml.RNode) *FunctionSpec {
|
||||
return nil
|
||||
}
|
||||
|
||||
// path to the function, this will be mounted into the container
|
||||
path := meta.Annotations[kioutil.PathAnnotation]
|
||||
if fn := getFunctionSpecFromAnnotation(n, meta); fn != nil {
|
||||
fn.Network = ""
|
||||
fn.Path = path
|
||||
fn.StorageMounts = []StorageMount{}
|
||||
return fn
|
||||
}
|
||||
|
||||
// legacy function specification for backwards compatibility
|
||||
container := meta.Annotations["config.kubernetes.io/container"]
|
||||
if container != "" {
|
||||
return &FunctionSpec{
|
||||
Path: path, Container: ContainerSpec{Image: container}}
|
||||
return &FunctionSpec{Container: ContainerSpec{Image: container}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func (m Merge3) Merge() error {
|
||||
|
||||
return kio.Pipeline{
|
||||
Inputs: inputs,
|
||||
Filters: []kio.Filter{m, FormatFilter{}}, // format the merged output
|
||||
Filters: []kio.Filter{m},
|
||||
Outputs: []kio.Writer{dest},
|
||||
}.Execute()
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ type LocalPackageReader struct {
|
||||
|
||||
var _ Reader = LocalPackageReader{}
|
||||
|
||||
var defaultMatch = []string{"*.yaml", "*.yml"}
|
||||
var DefaultMatch = []string{"*.yaml", "*.yml"}
|
||||
|
||||
// Read reads the Resources.
|
||||
func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
|
||||
@@ -174,7 +174,7 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
|
||||
return nil, fmt.Errorf("must specify package path")
|
||||
}
|
||||
if len(r.MatchFilesGlob) == 0 {
|
||||
r.MatchFilesGlob = defaultMatch
|
||||
r.MatchFilesGlob = DefaultMatch
|
||||
}
|
||||
|
||||
var operand ResourceNodeSlice
|
||||
@@ -201,9 +201,9 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
|
||||
|
||||
// check if we should skip the directory or file
|
||||
if info.IsDir() {
|
||||
return r.shouldSkipDir(path)
|
||||
return r.ShouldSkipDir(path)
|
||||
}
|
||||
if match, err := r.shouldSkipFile(info); err != nil {
|
||||
if match, err := r.ShouldSkipFile(info); err != nil {
|
||||
return err
|
||||
} else if !match {
|
||||
// skip this file
|
||||
@@ -244,8 +244,8 @@ func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode
|
||||
return rr.Read()
|
||||
}
|
||||
|
||||
// shouldSkipFile returns true if the file should be skipped
|
||||
func (r *LocalPackageReader) shouldSkipFile(info os.FileInfo) (bool, error) {
|
||||
// ShouldSkipFile returns true if the file should be skipped
|
||||
func (r *LocalPackageReader) ShouldSkipFile(info os.FileInfo) (bool, error) {
|
||||
// check if the files are in scope
|
||||
for _, g := range r.MatchFilesGlob {
|
||||
if match, err := filepath.Match(g, info.Name()); err != nil {
|
||||
@@ -267,8 +267,8 @@ func (r *LocalPackageReader) initReaderAnnotations(path string, _ os.FileInfo) {
|
||||
}
|
||||
}
|
||||
|
||||
// shouldSkipDir returns a filepath.SkipDir if the directory should be skipped
|
||||
func (r *LocalPackageReader) shouldSkipDir(path string) error {
|
||||
// ShouldSkipDir returns a filepath.SkipDir if the directory should be skipped
|
||||
func (r *LocalPackageReader) ShouldSkipDir(path string) error {
|
||||
if r.PackageFileName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
251
kyaml/openapi/kubernetesapi/swagger.go
Normal file
251
kyaml/openapi/kubernetesapi/swagger.go
Normal file
File diff suppressed because one or more lines are too long
251
kyaml/openapi/kustomizationapi/swagger.go
Normal file
251
kyaml/openapi/kustomizationapi/swagger.go
Normal file
@@ -0,0 +1,251 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated for package kustomizationapi by go-bindata DO NOT EDIT. (@generated)
|
||||
// sources:
|
||||
// openapi/kustomizationapi/swagger.json
|
||||
package kustomizationapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
// Name return file name
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
|
||||
// Size return file size
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
|
||||
// Mode return file mode
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
|
||||
// Mode return file modify time
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
|
||||
// IsDir return file whether a directory
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return fi.mode&os.ModeDir != 0
|
||||
}
|
||||
|
||||
// Sys return file is sys mode
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _openapiKustomizationapiSwaggerJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xdc\x54\xb1\x6e\xdb\x30\x10\xdd\xfd\x15\x04\xdb\xd1\x52\xe0\xad\xf0\x56\x74\xe8\x10\x04\x08\x90\x6e\x45\x86\xb3\x7c\x52\xae\x92\x49\xf6\x78\x12\xea\x16\xfe\xf7\x42\xac\xa5\x88\xb6\xd4\xb4\x46\x1c\x24\x1e\x0c\x18\xd4\xdd\x7b\xbc\x7b\x8f\xef\xd7\x4c\x29\xbd\xc6\x9c\x0c\x09\x59\xe3\xf5\x52\xb5\x47\x4a\x69\xb2\x69\xf9\xc1\xa7\xe0\x28\x05\xe7\x7c\xda\x2c\xd2\x4f\xd6\xe4\x54\xdc\x80\xfb\xc8\xc5\x63\xa5\x52\xda\xb1\x75\xc8\x42\x38\x3c\x55\x4a\x7f\x46\x83\x0c\x62\xf9\xa0\x21\x7c\x7c\xcf\x98\xeb\xa5\xd2\xef\xae\x06\xfc\x57\x23\xb4\x31\x4a\x0f\xb1\xdb\xff\xdb\xcd\xbb\x6b\xc0\x7a\x1d\x50\xa0\xba\x1d\x5e\x28\x87\xca\x63\x5f\x24\x5b\x87\x2d\xad\x5d\x7d\xc3\x4c\x74\x7f\xfe\x23\x29\xeb\x15\xb2\x41\x41\x9f\x14\x6c\x6b\x97\x34\xc8\x9e\xac\x49\x4a\x32\x6b\xbd\x54\x5f\x7b\xea\x68\x8e\x50\xdb\x22\x96\xb5\x17\xbb\xa1\x9f\x98\x66\x61\x51\x61\x10\xb2\x3d\x45\xa8\xde\x63\xe9\x78\x97\x51\xc9\x9e\xb6\xad\x6a\x16\x2b\x14\x58\x1c\x0f\x7d\x3f\x1b\x8c\x3e\xa6\xd5\x1d\x66\x8c\xf2\x3a\x84\x7a\x9c\xae\xdb\x7e\x84\xdf\x29\xe2\x85\xc9\x14\x97\x22\xf0\x40\x80\xe7\x57\x77\x4a\xaf\x49\x81\x0d\x6c\xd0\x3b\xc8\xfe\x7d\xf9\xf3\xb8\xf9\x94\xbe\x15\x3e\x40\x43\x96\x4f\xe9\xbd\x6e\x6e\x81\xf8\xce\xd6\x9c\xe1\xe9\x8e\x8c\x51\x2e\xc4\x59\xb1\xf8\xcf\x6f\xae\xeb\xfd\x65\x40\xfe\x40\xf5\xe6\x62\xfc\x5e\x13\x63\x3c\x90\xfe\xb2\x75\x78\x83\x02\x1d\xd3\xfd\xfc\x29\x33\x66\x5d\xf6\xf5\x93\x1c\x0a\x4c\x82\x9b\x43\xd5\xff\x47\xf7\x38\x5d\x07\x20\xbb\xf9\x98\x11\x81\x19\xb6\xf1\x26\x23\x4d\x1d\x48\xf6\x90\x6c\x90\x0b\x4c\x4a\xdc\xb6\x2d\xe1\x4d\x3c\xd5\xe1\x85\x41\xb0\x08\x0d\xa1\x7b\xdc\xeb\x3e\x44\xc5\xd9\x96\x31\x48\xa2\x57\xb9\x89\x37\xfd\x18\xe3\xc7\x72\x86\xc7\x38\x91\x83\x93\x8f\xab\x22\x41\x86\xea\x28\x33\x27\x5c\x34\x95\xc5\x7f\x37\xc8\xa8\x8d\x73\xaa\x8e\xa3\xfa\xfc\xb4\x68\x9a\x97\x62\x7d\xdb\x4e\x8d\x9c\x74\xaa\x53\x67\xed\x6f\xf7\x3b\x00\x00\xff\xff\xfe\x97\xce\xec\x37\x0c\x00\x00")
|
||||
|
||||
func openapiKustomizationapiSwaggerJsonBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_openapiKustomizationapiSwaggerJson,
|
||||
"openapi/kustomizationapi/swagger.json",
|
||||
)
|
||||
}
|
||||
|
||||
func openapiKustomizationapiSwaggerJson() (*asset, error) {
|
||||
bytes, err := openapiKustomizationapiSwaggerJsonBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "openapi/kustomizationapi/swagger.json", size: 3127, mode: os.FileMode(420), modTime: time.Unix(1586844916, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"openapi/kustomizationapi/swagger.json": openapiKustomizationapiSwaggerJson,
|
||||
}
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(cannonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"openapi": &bintree{nil, map[string]*bintree{
|
||||
"kustomizationapi": &bintree{nil, map[string]*bintree{
|
||||
"swagger.json": &bintree{openapiKustomizationapiSwaggerJson, map[string]*bintree{}},
|
||||
}},
|
||||
}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||
}
|
||||
127
kyaml/openapi/kustomizationapi/swagger.json
Normal file
127
kyaml/openapi/kustomizationapi/swagger.json
Normal file
@@ -0,0 +1,127 @@
|
||||
{
|
||||
"definitions": {
|
||||
"io.k8s.api.apps.v1.ConfigMapArgs": {
|
||||
"properties": {
|
||||
"GeneratorArgs": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1.GeneratorArgs"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "kustomize.config.k8s.io",
|
||||
"kind": "ConfigMapArgs",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.apps.v1.SecretArgs": {
|
||||
"properties": {
|
||||
"GeneratorArgs": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1.GeneratorArgs"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "kustomize.config.k8s.io",
|
||||
"kind": "SecretArgs",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.apps.v1.GeneratorArgs": {
|
||||
"properties": {
|
||||
"namespace": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"behavior": {
|
||||
"type": "string"
|
||||
},
|
||||
"KvPairSources": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1.KvPairSources"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "kustomize.config.k8s.io",
|
||||
"kind": "GeneratorArgs",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.apps.v1.Kustomization": {
|
||||
"required": [
|
||||
"TypeMeta"
|
||||
],
|
||||
"properties": {
|
||||
"configMapGenerator": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1.ConfigMapArgs"
|
||||
},
|
||||
"type": "array",
|
||||
"x-kubernetes-patch-merge-key": "name",
|
||||
"x-kubernetes-patch-strategy": "merge"
|
||||
},
|
||||
"secretGenerator": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/io.k8s.api.apps.v1.SecretArgs"
|
||||
},
|
||||
"type": "array",
|
||||
"x-kubernetes-patch-merge-key": "name",
|
||||
"x-kubernetes-patch-strategy": "merge"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "kustomize.config.k8s.io",
|
||||
"kind": "Kustomization",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"io.k8s.api.apps.v1.KvPairSources": {
|
||||
"properties": {
|
||||
"literals": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"envs": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "kustomize.config.k8s.io",
|
||||
"kind": "KvPairSources",
|
||||
"version": "v1beta1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi/kustomizationapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -296,9 +298,13 @@ func (rs *ResourceSchema) PatchStrategyAndKey() (string, string) {
|
||||
}
|
||||
|
||||
const (
|
||||
// openAPIAssetName is the name of the asset containing the statically compiled in
|
||||
// kubernetesAPIAssetName is the name of the asset containing the statically compiled in
|
||||
// OpenAPI definitions for Kubernetes built-in types
|
||||
openAPIAssetName = "openapi/swagger.json"
|
||||
kubernetesAPIAssetName = "openapi/kubernetesapi/swagger.json"
|
||||
|
||||
// kustomizationAPIAssetName is the name of the asset containing the statically compiled in
|
||||
// OpenAPI definitions for Kustomization built-in types
|
||||
kustomizationAPIAssetName = "openapi/kustomizationapi/swagger.json"
|
||||
|
||||
// kubernetesGVKExtensionKey is the key to lookup the kubernetes group version kind extension
|
||||
// -- the extension is an array of objects containing a gvk
|
||||
@@ -327,7 +333,12 @@ func initSchema() {
|
||||
}
|
||||
|
||||
// parse the swagger, this should never fail
|
||||
if _, err := parse(MustAsset(openAPIAssetName)); err != nil {
|
||||
if _, err := parse(kubernetesapi.MustAsset(kubernetesAPIAssetName)); err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if _, err := parse(kustomizationapi.MustAsset(kustomizationAPIAssetName)); err != nil {
|
||||
// this should never happen
|
||||
panic(err)
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,12 +4,14 @@
|
||||
package runfn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
@@ -64,10 +66,16 @@ type RunFns struct {
|
||||
// DisableContainers will disable functions run as containers
|
||||
DisableContainers bool
|
||||
|
||||
// ResultsDir is where to write each functions results
|
||||
ResultsDir string
|
||||
|
||||
// resultsCount is used to generate the results filename for each container
|
||||
resultsCount uint32
|
||||
|
||||
// functionFilterProvider provides a filter to perform the function.
|
||||
// this is a variable so it can be mocked in tests
|
||||
functionFilterProvider func(
|
||||
filter filters.FunctionSpec, api *yaml.RNode) kio.Filter
|
||||
filter filters.FunctionSpec, api *yaml.RNode) (kio.Filter, error)
|
||||
}
|
||||
|
||||
// Execute runs the command
|
||||
@@ -215,7 +223,11 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
|
||||
}
|
||||
spec.Network = r.NetworkName
|
||||
}
|
||||
c := r.functionFilterProvider(*spec, api)
|
||||
c, err := r.functionFilterProvider(*spec, api)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
continue
|
||||
}
|
||||
@@ -299,22 +311,48 @@ func (r *RunFns) init() {
|
||||
}
|
||||
|
||||
// ffp provides function filters
|
||||
func (r *RunFns) ffp(spec filters.FunctionSpec, api *yaml.RNode) kio.Filter {
|
||||
func (r *RunFns) ffp(spec filters.FunctionSpec, api *yaml.RNode) (kio.Filter, error) {
|
||||
if !r.DisableContainers && spec.Container.Image != "" {
|
||||
var resultsFile string
|
||||
// TODO: Add a test for this behavior
|
||||
|
||||
if r.ResultsDir != "" {
|
||||
resultsFile = filepath.Join(r.ResultsDir, fmt.Sprintf(
|
||||
"results-%v.yaml", r.resultsCount))
|
||||
atomic.AddUint32(&r.resultsCount, 1)
|
||||
}
|
||||
return &filters.ContainerFilter{
|
||||
Image: spec.Container.Image,
|
||||
Config: api,
|
||||
Network: spec.Network,
|
||||
StorageMounts: r.StorageMounts,
|
||||
GlobalScope: r.GlobalScope,
|
||||
}
|
||||
ResultsFile: resultsFile,
|
||||
}, nil
|
||||
}
|
||||
if r.EnableStarlark && spec.Starlark.Path != "" {
|
||||
// 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)
|
||||
|
||||
return &starlark.Filter{
|
||||
Name: spec.Starlark.Name,
|
||||
Path: spec.Starlark.Path,
|
||||
Path: p,
|
||||
FunctionConfig: api,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ kind:
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
filter := instance.functionFilterProvider(spec, api)
|
||||
filter, _ := instance.functionFilterProvider(spec, api)
|
||||
assert.Equal(t, &filters.ContainerFilter{Image: "example.com:version", Config: api}, filter)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ kind:
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
filter := instance.functionFilterProvider(spec, api)
|
||||
filter, _ := instance.functionFilterProvider(spec, api)
|
||||
assert.Equal(t, &filters.ContainerFilter{
|
||||
Image: "example.com:version", Config: api, GlobalScope: true}, filter)
|
||||
}
|
||||
@@ -140,6 +140,16 @@ func TestRunFns_Execute__initDefault(t *testing.T) {
|
||||
FunctionPaths: []string{"foo"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "explicit directories in mounts",
|
||||
instance: RunFns{StorageMounts: []filters.StorageMount{{MountType: "volume", Src: "myvol", DstPath: "/local/"}}},
|
||||
expected: RunFns{
|
||||
Output: os.Stdout,
|
||||
Input: os.Stdin,
|
||||
NoFunctionsFromInput: getFalse(),
|
||||
StorageMounts: []filters.StorageMount{{MountType: "volume", Src: "myvol", DstPath: "/local/"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for i := range tests {
|
||||
tt := tests[i]
|
||||
@@ -186,6 +196,13 @@ func TestRunFns_getFilters(t *testing.T) {
|
||||
in []f
|
||||
// images to be run in a specific order
|
||||
out []string
|
||||
|
||||
// images to be run in a specific order -- computed from directory path
|
||||
outFn func(string) []string
|
||||
|
||||
// expected Error
|
||||
error string
|
||||
|
||||
// name of the test
|
||||
name string
|
||||
// value to set for NoFunctionsFromInput
|
||||
@@ -476,7 +493,54 @@ metadata:
|
||||
},
|
||||
},
|
||||
enableStarlark: true,
|
||||
out: []string{"name: path: a/b/c url: program:"},
|
||||
outFn: func(path string) []string {
|
||||
return []string{
|
||||
fmt.Sprintf("name: path: %s/foo/a/b/c url: program:", path)}
|
||||
},
|
||||
},
|
||||
|
||||
// Test
|
||||
//
|
||||
//
|
||||
{name: "starlark-function-absolute",
|
||||
in: []f{
|
||||
{
|
||||
path: filepath.Join("foo", "bar.yaml"),
|
||||
value: `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: ExampleFunction
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
starlark:
|
||||
path: /a/b/c
|
||||
`,
|
||||
},
|
||||
},
|
||||
enableStarlark: true,
|
||||
error: "absolute function path /a/b/c not allowed",
|
||||
},
|
||||
|
||||
// Test
|
||||
//
|
||||
//
|
||||
{name: "starlark-function-escape-parent",
|
||||
in: []f{
|
||||
{
|
||||
path: filepath.Join("foo", "bar.yaml"),
|
||||
value: `
|
||||
apiVersion: example.com/v1alpha1
|
||||
kind: ExampleFunction
|
||||
metadata:
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
starlark:
|
||||
path: ../a/b/c
|
||||
`,
|
||||
},
|
||||
},
|
||||
enableStarlark: true,
|
||||
error: "function path ../a/b/c not allowed to start with ../",
|
||||
},
|
||||
|
||||
{name: "starlark-function-disabled",
|
||||
@@ -559,6 +623,14 @@ metadata:
|
||||
// get the filters which would be run
|
||||
var results []string
|
||||
_, fltrs, _, err := r.getNodesAndFilters()
|
||||
|
||||
if tt.error != "" {
|
||||
if !assert.EqualError(t, err, tt.error) {
|
||||
t.FailNow()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
@@ -567,8 +639,14 @@ metadata:
|
||||
}
|
||||
|
||||
// compare the actual ordering to the expected ordering
|
||||
if !assert.Equal(t, tt.out, results) {
|
||||
t.FailNow()
|
||||
if tt.outFn != nil {
|
||||
if !assert.Equal(t, tt.outFn(d), results) {
|
||||
t.FailNow()
|
||||
}
|
||||
} else {
|
||||
if !assert.Equal(t, tt.out, results) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -738,8 +816,8 @@ func setupTest(t *testing.T) string {
|
||||
// getFilterProvider fakes the creation of a filter, replacing the ContainerFiler with
|
||||
// a filter to s/kind: Deployment/kind: StatefulSet/g.
|
||||
// this can be used to simulate running a filter.
|
||||
func getFilterProvider(t *testing.T) func(filters.FunctionSpec, *yaml.RNode) kio.Filter {
|
||||
return func(f filters.FunctionSpec, node *yaml.RNode) kio.Filter {
|
||||
func getFilterProvider(t *testing.T) func(filters.FunctionSpec, *yaml.RNode) (kio.Filter, error) {
|
||||
return func(f filters.FunctionSpec, node *yaml.RNode) (kio.Filter, error) {
|
||||
// parse the filter from the input
|
||||
filter := yaml.YFilter{}
|
||||
b := &bytes.Buffer{}
|
||||
@@ -755,6 +833,6 @@ func getFilterProvider(t *testing.T) func(filters.FunctionSpec, *yaml.RNode) kio
|
||||
|
||||
return filters.Modifier{
|
||||
Filters: []yaml.YFilter{{Filter: yaml.Lookup("kind")}, filter},
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
95
kyaml/starlark/context.go
Normal file
95
kyaml/starlark/context.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package starlark
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/qri-io/starlib/util"
|
||||
"go.starlark.net/starlark"
|
||||
"go.starlark.net/starlarkstruct"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/openapi"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
resourceList starlark.Value
|
||||
}
|
||||
|
||||
func (c *Context) predeclared() (starlark.StringDict, error) {
|
||||
e, err := env()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oa, err := oa()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dict := starlark.StringDict{
|
||||
"resource_list": c.resourceList,
|
||||
"open_api": oa,
|
||||
"environment": e,
|
||||
}
|
||||
|
||||
return starlark.StringDict{
|
||||
"ctx": starlarkstruct.FromStringDict(starlarkstruct.Default, dict),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func oa() (starlark.Value, error) {
|
||||
return interfaceToValue(openapi.Schema())
|
||||
}
|
||||
|
||||
func env() (starlark.Value, error) {
|
||||
env := map[string]interface{}{}
|
||||
for _, e := range os.Environ() {
|
||||
pair := strings.SplitN(e, "=", 2)
|
||||
if len(pair) < 2 {
|
||||
continue
|
||||
}
|
||||
env[pair[0]] = pair[1]
|
||||
}
|
||||
value, err := util.Marshal(env)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func nodeToValue(node *yaml.RNode) (starlark.Value, error) {
|
||||
s, err := node.String()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
var in map[string]interface{}
|
||||
if err := yaml.Unmarshal([]byte(s), &in); err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
value, err := util.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func interfaceToValue(i interface{}) (starlark.Value, error) {
|
||||
b, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var in map[string]interface{}
|
||||
if err := yaml.Unmarshal(b, &in); err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
value, err := util.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
@@ -50,7 +50,7 @@ def run(items):
|
||||
for item in items:
|
||||
item["metadata"]["annotations"]["foo"] = "bar"
|
||||
|
||||
run(resourceList["items"])
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ def run(items, value):
|
||||
for item in items:
|
||||
item["metadata"]["annotations"]["foo"] = value
|
||||
|
||||
run(resourceList["items"], resourceList["functionConfig"]["spec"]["value"])
|
||||
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]["spec"]["value"])
|
||||
`,
|
||||
FunctionConfig: fc,
|
||||
}
|
||||
@@ -233,7 +233,7 @@ def run(items):
|
||||
for item in items:
|
||||
item["metadata"]["annotations"]["foo"] = "bar"
|
||||
|
||||
run(resourceList["items"])
|
||||
run(ctx.resource_list["items"])
|
||||
`), 0600)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -84,8 +84,15 @@ func (sf *Filter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
|
||||
// run the starlark as program as transformation function
|
||||
thread := &starlark.Thread{Name: sf.Name}
|
||||
predeclared := starlark.StringDict{"resourceList": value}
|
||||
_, err = starlark.ExecFile(thread, sf.Name, sf.Program, predeclared)
|
||||
|
||||
ctx := &Context{
|
||||
resourceList: value,
|
||||
}
|
||||
pd, err := ctx.predeclared()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
_, err = starlark.ExecFile(thread, sf.Name, sf.Program, pd)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
@@ -151,18 +158,7 @@ func (sf *Filter) inputToResourceList(
|
||||
|
||||
// convert the ResourceList into a starlark dictionary by
|
||||
// first converting it into a map[string]interface{}
|
||||
s, err := resourceList.String()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err)
|
||||
}
|
||||
var in map[string]interface{}
|
||||
if err := yaml.Unmarshal([]byte(s), &in); err != nil {
|
||||
return nil, nil, errors.Wrap(err)
|
||||
}
|
||||
value, err := util.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err)
|
||||
}
|
||||
value, err := nodeToValue(resourceList)
|
||||
return value, ids, err
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ func TestFilter_Filter(t *testing.T) {
|
||||
script string
|
||||
expected string
|
||||
expectedFunctionConfig string
|
||||
env map[string]string
|
||||
}{
|
||||
{
|
||||
name: "add_annotation",
|
||||
@@ -46,7 +47,7 @@ def run(r):
|
||||
for resource in r:
|
||||
resource["metadata"]["annotations"]["foo"] = "bar"
|
||||
|
||||
run(resourceList["items"])
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
@@ -62,6 +63,83 @@ spec:
|
||||
- name: nginx
|
||||
# head comment
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add_annotation_from_env",
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
# head comment
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
script: `
|
||||
def run(r):
|
||||
for resource in r:
|
||||
resource["metadata"]["annotations"]["foo"] = ctx.environment["ANNOTATION"]
|
||||
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
env: map[string]string{"ANNOTATION": "annotation-value"},
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: annotation-value
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
# head comment
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "add_annotation_from_open_api",
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
# head comment
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
script: `
|
||||
def run(r):
|
||||
for resource in r:
|
||||
resource["metadata"]["annotations"]["foo"] = ctx.open_api["definitions"]["io.k8s.api.apps.v1.Deployment"]["description"]
|
||||
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: Deployment enables declarative updates for Pods and ReplicaSets.
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
# head comment
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -87,7 +165,7 @@ def run(r):
|
||||
for resource in r:
|
||||
resource["metadata"]["annotations"]["foo"] = "bar"
|
||||
|
||||
run(resourceList["items"])
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
@@ -103,6 +181,45 @@ spec:
|
||||
- name: nginx
|
||||
# head comment
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "delete_annotation",
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
annotations:
|
||||
foo: baz
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
# head comment
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
script: `
|
||||
# set the foo annotation on each resource
|
||||
def run(r):
|
||||
for resource in r:
|
||||
resource["metadata"]["annotations"].pop("foo")
|
||||
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
# head comment
|
||||
image: nginx:1.8.1 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -140,7 +257,7 @@ def run(r):
|
||||
for resource in r:
|
||||
resource["metadata"]["annotations"]["foo"] = "bar"
|
||||
|
||||
run(resourceList["items"])
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
@@ -196,7 +313,7 @@ def run(r):
|
||||
},
|
||||
}
|
||||
r.append(d)
|
||||
run(resourceList["items"])
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
@@ -233,7 +350,7 @@ metadata:
|
||||
script: `
|
||||
def run(r):
|
||||
r.pop()
|
||||
run(resourceList["items"])
|
||||
run(ctx.resource_list["items"])
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
@@ -268,8 +385,8 @@ def run(r, an):
|
||||
for resource in r:
|
||||
resource["metadata"]["annotations"]["foo"] = an
|
||||
|
||||
an = resourceList["functionConfig"]["spec"]["value"]
|
||||
run(resourceList["items"], an)
|
||||
an = ctx.resource_list["functionConfig"]["spec"]["value"]
|
||||
run(ctx.resource_list["items"], an)
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
@@ -319,9 +436,9 @@ def run(r, an):
|
||||
for resource in r:
|
||||
resource["metadata"]["annotations"]["foo"] = an
|
||||
|
||||
an = resourceList["functionConfig"]["spec"]["value"]
|
||||
run(resourceList["items"], an)
|
||||
resourceList["functionConfig"]["spec"]["value"] = "updated"
|
||||
an = ctx.resource_list["functionConfig"]["spec"]["value"]
|
||||
run(ctx.resource_list["items"], an)
|
||||
ctx.resource_list["functionConfig"]["spec"]["value"] = "updated"
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
@@ -348,6 +465,10 @@ spec:
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
os.Clearenv()
|
||||
for k, v := range test.env {
|
||||
os.Setenv(k, v)
|
||||
}
|
||||
f := &Filter{Name: test.name, Program: test.script}
|
||||
|
||||
if test.functionConfig != "" {
|
||||
|
||||
92
kyaml/yaml/merge3/kustomization_test.go
Normal file
92
kyaml/yaml/merge3/kustomization_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package merge3_test
|
||||
|
||||
var kustomizationTestCases = []testCase{
|
||||
// Kustomization Test Cases
|
||||
|
||||
{description: `ConfigMapGenerator merge`,
|
||||
origin: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
configMapGenerator:
|
||||
- name: a-configmap1
|
||||
files:
|
||||
- configs/configfile1
|
||||
- configkey=configs/another_configfile1`,
|
||||
update: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
configMapGenerator:
|
||||
- files:
|
||||
- configs/configfile2
|
||||
- configkey=configs/another_configfile2
|
||||
name: a-configmap2`,
|
||||
local: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
configMapGenerator:
|
||||
- name: a-configmap1
|
||||
files:
|
||||
- configs/configfile1
|
||||
- configkey=configs/another_configfile1
|
||||
- name: a-configmap3
|
||||
files:
|
||||
- configs/configfile3
|
||||
- configkey=configs/another_configfile3`,
|
||||
expected: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
configMapGenerator:
|
||||
- name: a-configmap3
|
||||
files:
|
||||
- configs/configfile3
|
||||
- configkey=configs/another_configfile3
|
||||
- files:
|
||||
- configs/configfile2
|
||||
- configkey=configs/another_configfile2
|
||||
name: a-configmap2`},
|
||||
|
||||
{description: `SecretGenerator merge`,
|
||||
origin: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
secretGenerator:
|
||||
- name: a-secret1
|
||||
files:
|
||||
- configs/configfile1
|
||||
- configkey=configs/another_configfile1`,
|
||||
update: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
secretGenerator:
|
||||
- files:
|
||||
- configs/configfile2
|
||||
- configkey=configs/another_configfile2
|
||||
name: a-secret2`,
|
||||
local: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
secretGenerator:
|
||||
- name: a-secret1
|
||||
files:
|
||||
- configs/configfile1
|
||||
- configkey=configs/another_configfile1
|
||||
- name: a-secret3
|
||||
files:
|
||||
- configs/configfile3
|
||||
- configkey=configs/another_configfile3`,
|
||||
expected: `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
secretGenerator:
|
||||
- name: a-secret3
|
||||
files:
|
||||
- configs/configfile3
|
||||
- configkey=configs/another_configfile3
|
||||
- files:
|
||||
- configs/configfile2
|
||||
- configkey=configs/another_configfile2
|
||||
name: a-secret2`},
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
. "sigs.k8s.io/kustomize/kyaml/yaml/merge3"
|
||||
)
|
||||
|
||||
var testCases = [][]testCase{scalarTestCases, listTestCases, mapTestCases, elementTestCases}
|
||||
var testCases = [][]testCase{scalarTestCases, listTestCases, mapTestCases, elementTestCases, kustomizationTestCases}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
for i := range testCases {
|
||||
|
||||
@@ -5,6 +5,7 @@ package yaml
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
@@ -681,6 +682,39 @@ func (rn *RNode) GetAssociativeKey() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (rn *RNode) MarshalJSON() ([]byte, error) {
|
||||
s, err := rn.String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m := map[string]interface{}{}
|
||||
if err := Unmarshal([]byte(s), &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (rn *RNode) UnmarshalJSON(b []byte) error {
|
||||
m := map[string]interface{}{}
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := Parse(string(c))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rn.value = r.value
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkKey returns true if all elems have the key
|
||||
func checkKey(key string, elems []*Node) bool {
|
||||
count := 0
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -47,3 +48,78 @@ spec:
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRNode_UnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
input string
|
||||
output string
|
||||
}{
|
||||
{
|
||||
testName: "simple document",
|
||||
input: `{"hello":"world"}`,
|
||||
output: `hello: world`,
|
||||
},
|
||||
{
|
||||
testName: "nested structure",
|
||||
input: `
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "my-deployment",
|
||||
"namespace": "default"
|
||||
}
|
||||
}
|
||||
`,
|
||||
output: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: my-deployment
|
||||
namespace: default
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range testCases {
|
||||
tc := testCases[i]
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
instance := &RNode{}
|
||||
err := instance.UnmarshalJSON([]byte(tc.input))
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actual, err := instance.String()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.output), strings.TrimSpace(actual)) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRNode_MarshalJSON(t *testing.T) {
|
||||
instance, err := Parse(`
|
||||
hello: world
|
||||
`)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
actual, err := instance.MarshalJSON()
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
expected := `{"hello":"world"}`
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(expected), strings.TrimSpace(string(actual))) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
@@ -19,7 +18,6 @@ metadata:
|
||||
annotations:
|
||||
app: myApp
|
||||
greeting/morning: a string with blanks
|
||||
yamlSupport: %v
|
||||
fieldSpecs:
|
||||
- path: metadata/annotations
|
||||
create: true
|
||||
@@ -48,16 +46,9 @@ spec:
|
||||
)
|
||||
|
||||
func TestAnnotationsTransformer(t *testing.T) {
|
||||
for _, b := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("yaml-%v", b), func(t *testing.T) {
|
||||
th := kusttest_test.MakeEnhancedHarness(t).
|
||||
PrepBuiltin("AnnotationsTransformer")
|
||||
defer th.Reset()
|
||||
th := kusttest_test.MakeEnhancedHarness(t).
|
||||
PrepBuiltin("AnnotationsTransformer")
|
||||
defer th.Reset()
|
||||
|
||||
cfg := fmt.Sprintf(config, b)
|
||||
rm := th.LoadAndRunTransformer(cfg, input)
|
||||
|
||||
th.AssertActualEqualsExpected(rm, expectedOutput)
|
||||
})
|
||||
}
|
||||
th.RunTransformerAndCheckResult(config, input, expectedOutput)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.13
|
||||
|
||||
require (
|
||||
sigs.k8s.io/kustomize/api v0.0.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
|
||||
@@ -252,6 +252,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
@@ -444,10 +445,8 @@ k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1 h1:nGUNYINljZNmlAS8uoobUv/wx/s3Pg8dNxYo+W7uYh0=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1/go.mod h1:/NdPPfrperSCGjm55cwEro1loBVtbtVIXSb7FguK6uk=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3 h1:zbeHVTMCQPtWgjIH/YYJZC45mm7coTdw2TblyJ79BrY=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5 h1:NicBWYTwkuOfVyZDbNkfSBSCwSgin4uirkedtyZltIc=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
@@ -14,16 +14,13 @@ import (
|
||||
type plugin struct {
|
||||
h *resmap.PluginHelpers
|
||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
types.GeneratorOptions
|
||||
types.ConfigMapArgs
|
||||
}
|
||||
|
||||
//noinspection GoUnusedGlobalVariable
|
||||
var KustomizePlugin plugin
|
||||
|
||||
func (p *plugin) Config(
|
||||
h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
p.GeneratorOptions = types.GeneratorOptions{}
|
||||
func (p *plugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
p.ConfigMapArgs = types.ConfigMapArgs{}
|
||||
err = yaml.Unmarshal(config, p)
|
||||
if p.ConfigMapArgs.Name == "" {
|
||||
@@ -38,6 +35,5 @@ func (p *plugin) Config(
|
||||
|
||||
func (p *plugin) Generate() (resmap.ResMap, error) {
|
||||
return p.h.ResmapFactory().FromConfigMapArgs(
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()),
|
||||
&p.GeneratorOptions, p.ConfigMapArgs)
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), p.ConfigMapArgs)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@ module sigs.k8s.io/kustomize/plugin/builtin/configmapgenerator
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
gopkg.in/yaml.v2 v2.2.7 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.3.1
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/api v0.3.1 => ../../../api
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
@@ -12,6 +13,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@@ -21,11 +23,17 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
@@ -42,6 +50,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
@@ -54,6 +63,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
@@ -68,8 +79,8 @@ github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1
|
||||
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
@@ -143,6 +154,12 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
@@ -168,7 +185,6 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
@@ -184,8 +200,12 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
@@ -194,6 +214,7 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -207,6 +228,7 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -224,11 +246,14 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
@@ -254,6 +279,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -261,6 +287,8 @@ github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiff
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
|
||||
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
|
||||
@@ -269,9 +297,15 @@ github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk
|
||||
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7tIdhOM=
|
||||
github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
@@ -280,7 +314,6 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -290,6 +323,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -328,10 +362,11 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c h1:Vco5b+cuG5NNfORVxZy6bYZQ7rsigisU1WQFkvQ0L5E=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -353,7 +388,6 @@ golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -389,6 +423,8 @@ gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
@@ -409,8 +445,8 @@ k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl
|
||||
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||
sigs.k8s.io/kustomize/api v0.3.1 h1:oqMIXvS6tFEUVuKIRUKDa05eC4Hh+cb9JYg8Zhp2d24=
|
||||
sigs.k8s.io/kustomize/api v0.3.1/go.mod h1:A+ATnlHqzictQfQC1q3KB/T6MSr0UWQsrrLxMWkge2E=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5 h1:NicBWYTwkuOfVyZDbNkfSBSCwSgin4uirkedtyZltIc=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
@@ -3,3 +3,5 @@ module sigs.k8s.io/kustomize/plugin/builtin/hashtransformer
|
||||
go 1.13
|
||||
|
||||
require sigs.k8s.io/kustomize/api v0.3.1
|
||||
|
||||
replace sigs.k8s.io/kustomize/api v0.3.1 => ../../../api
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user