Compare commits

..

1 Commits

Author SHA1 Message Date
Morten Torkildsen
4d14be985e pin kyaml version to v0.3.0 for release 2020-06-17 13:01:23 -07:00
556 changed files with 9839 additions and 65421 deletions

View File

@@ -15,7 +15,7 @@ verify-kustomize: \
lint-kustomize \ lint-kustomize \
test-unit-kustomize-all \ test-unit-kustomize-all \
test-examples-kustomize-against-HEAD \ test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-3.8.0 test-examples-kustomize-against-3.6.1
# The following target referenced by a file in # The following target referenced by a file in
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize # https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
@@ -23,10 +23,10 @@ verify-kustomize: \
prow-presubmit-check: \ prow-presubmit-check: \
lint-kustomize \ lint-kustomize \
test-unit-kustomize-all \ test-unit-kustomize-all \
test-unit-cmd-all \
test-go-mod \
test-examples-kustomize-against-HEAD \ test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-3.8.0 test-examples-kustomize-against-3.6.1 \
test-unit-cmd-all \
test-go-mod
.PHONY: verify-kustomize-e2e .PHONY: verify-kustomize-e2e
verify-kustomize-e2e: test-examples-e2e-kustomize verify-kustomize-e2e: test-examples-e2e-kustomize
@@ -233,10 +233,10 @@ test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD ./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY: .PHONY:
test-examples-kustomize-against-3.8.0: $(MYGOBIN)/mdrip test-examples-kustomize-against-3.6.1: $(MYGOBIN)/mdrip
( \ ( \
set -e; \ set -e; \
tag=v3.8.0; \ tag=v3.6.1; \
/bin/rm -f $(MYGOBIN)/kustomize; \ /bin/rm -f $(MYGOBIN)/kustomize; \
echo "Installing kustomize $$tag."; \ echo "Installing kustomize $$tag."; \
GO111MODULE=on go get sigs.k8s.io/kustomize/kustomize/v3@$${tag}; \ GO111MODULE=on go get sigs.k8s.io/kustomize/kustomize/v3@$${tag}; \

View File

@@ -29,7 +29,7 @@ func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
err := filtersutil.ApplyToJSON(annotations.Filter{ err := filtersutil.ApplyToJSON(annotations.Filter{
Annotations: p.Annotations, Annotations: p.Annotations,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,
}, r) }, r.Kunstructured)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -29,7 +29,7 @@ func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error {
err := filtersutil.ApplyToJSON(labels.Filter{ err := filtersutil.ApplyToJSON(labels.Filter{
Labels: p.Labels, Labels: p.Labels,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,
}, r) }, r.Kunstructured)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -10,6 +10,7 @@ import (
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil" "sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
@@ -19,6 +20,11 @@ import (
type NamespaceTransformerPlugin struct { type NamespaceTransformerPlugin struct {
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
// YAMLSupport can be set to true to use the kyaml filter instead of the
// kunstruct transformer.
// TODO: change the default to use kyaml when it is stable
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
} }
func (p *NamespaceTransformerPlugin) Config( func (p *NamespaceTransformerPlugin) Config(
@@ -37,13 +43,31 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
// Don't mutate empty objects? // Don't mutate empty objects?
continue continue
} }
err := filtersutil.ApplyToJSON(namespace.Filter{
Namespace: p.Namespace, id := r.OrgId()
FsSlice: p.FieldSpecs,
}, r) if p.YAMLSupport {
if err != nil { // use the new style transform
return err err := filtersutil.ApplyToJSON(namespace.Filter{
Namespace: p.Namespace,
FsSlice: p.FieldSpecs,
}, r.Kunstructured)
if err != nil {
return err
}
} else {
// use the old style transform
applicableFs := p.applicableFieldSpecs(id)
for _, fs := range applicableFs {
err := transform.MutateField(
r.Map(), fs.PathSlice(), fs.CreateIfNotPresent,
p.changeNamespace(r))
if err != nil {
return err
}
}
} }
matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals) matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals)
if len(matches) != 1 { if len(matches) != 1 {
return fmt.Errorf( return fmt.Errorf(

View File

@@ -23,6 +23,8 @@ type PatchJson6902TransformerPlugin struct {
Target types.PatchTarget `json:"target,omitempty" yaml:"target,omitempty"` Target types.PatchTarget `json:"target,omitempty" yaml:"target,omitempty"`
Path string `json:"path,omitempty" yaml:"path,omitempty"` Path string `json:"path,omitempty" yaml:"path,omitempty"`
JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"` JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"`
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
} }
func (p *PatchJson6902TransformerPlugin) Config( func (p *PatchJson6902TransformerPlugin) Config(
@@ -85,9 +87,22 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
if err != nil { if err != nil {
return err return err
} }
return filtersutil.ApplyToJSON(patchjson6902.Filter{ if !p.YAMLSupport {
Patch: p.JsonOp, rawObj, err := obj.MarshalJSON()
}, obj) 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)
}
} }
func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin { func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin {

View File

@@ -4,10 +4,11 @@
package builtins package builtins
import ( import (
"encoding/json"
"fmt" "fmt"
"strings"
"github.com/pkg/errors" kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge" "sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
@@ -21,6 +22,8 @@ type PatchStrategicMergeTransformerPlugin struct {
loadedPatches []*resource.Resource loadedPatches []*resource.Resource
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"` Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"` Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
} }
func (p *PatchStrategicMergeTransformerPlugin) Config( func (p *PatchStrategicMergeTransformerPlugin) Config(
@@ -73,48 +76,42 @@ func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error
if err != nil { if err != nil {
return err return err
} }
patchCopy := patch.DeepCopy() if !p.YAMLSupport {
patchCopy.SetName(target.GetName()) err = target.Patch(patch.Kunstructured)
patchCopy.SetNamespace(target.GetNamespace())
patchCopy.SetGvk(target.GetGvk())
node, err := filtersutil.GetRNode(patchCopy)
if err != nil {
return err
}
err = filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
Patch: node,
}, target)
if err != nil {
// Check for an error string from UnmarshalJSON that's indicative
// of an object that's missing basic KRM fields, and thus may have been
// entirely deleted (an acceptable outcome). This error handling should
// be deleted along with use of ResMap and apimachinery functions like
// UnmarshalJSON.
if !strings.Contains(err.Error(), "Object 'Kind' is missing") {
// Some unknown error, let it through.
return err
}
if len(target.Map()) != 0 {
return errors.Wrapf(
err, "with unexpectedly non-empty object map of size %d",
len(target.Map()))
}
// Fall through to handle deleted object.
}
if len(target.Map()) == 0 {
// This means all fields have been removed from the object.
// This can happen if a patch required deletion of the
// entire resource (not just a part of it). This means
// the overall resmap must shrink by one.
err = m.Remove(target.CurId())
if err != nil { if err != nil {
return err 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 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 { func NewPatchStrategicMergeTransformerPlugin() resmap.TransformerPlugin {
return &PatchStrategicMergeTransformerPlugin{} return &PatchStrategicMergeTransformerPlugin{}
} }

View File

@@ -8,6 +8,7 @@ import (
"strings" "strings"
jsonpatch "github.com/evanphx/json-patch" jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/patchjson6902" "sigs.k8s.io/kustomize/api/filters/patchjson6902"
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge" "sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
@@ -23,6 +24,8 @@ type PatchTransformerPlugin struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"` Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"` Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"` Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
} }
func (p *PatchTransformerPlugin) Config( func (p *PatchTransformerPlugin) Config(
@@ -70,11 +73,11 @@ func (p *PatchTransformerPlugin) Config(
} }
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error { func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
if p.loadedPatch == nil { if p.loadedPatch != nil {
return p.transformJson6902(m, p.decodedPatch)
} else {
// The patch was a strategic merge patch // The patch was a strategic merge patch
return p.transformStrategicMerge(m, p.loadedPatch) return p.transformStrategicMerge(m, p.loadedPatch)
} else {
return p.transformJson6902(m, p.decodedPatch)
} }
} }
@@ -108,15 +111,20 @@ func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap, patch
} }
// applySMPatch applies the provided strategic merge patch to the // applySMPatch applies the provided strategic merge patch to the
// given resource. // 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 { func (p *PatchTransformerPlugin) applySMPatch(resource, patch *resource.Resource) error {
node, err := filtersutil.GetRNode(patch) if !p.YAMLSupport {
if err != nil { return resource.Patch(patch.Kunstructured)
return err } else {
node, err := filtersutil.GetRNode(patch)
if err != nil {
return err
}
return filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
Patch: node,
}, resource.Kunstructured)
} }
return filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
Patch: node,
}, resource)
} }
// transformJson6902 applies the provided json6902 patch // transformJson6902 applies the provided json6902 patch
@@ -125,14 +133,13 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
if p.Target == nil { if p.Target == nil {
return fmt.Errorf("must specify a target for patch %s", p.Patch) return fmt.Errorf("must specify a target for patch %s", p.Patch)
} }
resources, err := m.Select(*p.Target) resources, err := m.Select(*p.Target)
if err != nil { if err != nil {
return err return err
} }
for _, res := range resources { for _, res := range resources {
err = filtersutil.ApplyToJSON(patchjson6902.Filter{ err = p.applyJson6902Patch(res, patch)
Patch: p.Patch,
}, res)
if err != nil { if err != nil {
return err return err
} }
@@ -140,6 +147,28 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
return nil 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 // jsonPatchFromBytes loads a Json 6902 patch from
// a bytes input // a bytes input
func jsonPatchFromBytes( func jsonPatchFromBytes(

View File

@@ -5,27 +5,31 @@ package builtins
import ( import (
"errors" "errors"
"fmt"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
// Add the given prefix and suffix to the field. // Add the given prefix and suffix to the field.
type PrefixSuffixTransformerPlugin struct { type PrefixSuffixTransformerPlugin struct {
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
FieldSpecs types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
} }
// A Gvk skip list for prefix/suffix modification. // Not placed in a file yet due to lack of demand.
// hard coded for now - eventually should be part of config. var prefixSuffixFieldSpecsToSkip = []types.FieldSpec{
var prefixSuffixFieldSpecsToSkip = types.FsSlice{ {
{Gvk: resid.Gvk{Kind: "CustomResourceDefinition"}}, Gvk: resid.Gvk{Kind: "CustomResourceDefinition"},
{Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"}}, },
{
Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"},
},
} }
func (p *PrefixSuffixTransformerPlugin) Config( func (p *PrefixSuffixTransformerPlugin) Config(
@@ -44,24 +48,29 @@ func (p *PrefixSuffixTransformerPlugin) Config(
} }
func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error { func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
// Even if both the Prefix and Suffix are empty we want // Even if both the Prefix and Suffix are empty we want
// to proceed with the transformation. This allows to add contextual // to proceed with the transformation. This allows to add contextual
// information to the resources (AddNamePrefix and AddNameSuffix). // information to the resources (AddNamePrefix and AddNameSuffix).
for _, r := range m.Resources() { for _, r := range m.Resources() {
// TODO: move this test into the filter (i.e. make a better filter)
if p.shouldSkip(r.OrgId()) { if p.shouldSkip(r.OrgId()) {
// Don't change the actual definition
// of a CRD.
continue continue
} }
id := r.OrgId() id := r.OrgId()
// current default configuration contains // current default configuration contains
// only one entry: "metadata/name" with no GVK // only one entry: "metadata/name" with no GVK
for _, fs := range p.FieldSpecs { for _, path := range p.FieldSpecs {
// TODO: this is redundant to filter (but needed for now) if !id.IsSelected(&path.Gvk) {
if !id.IsSelected(&fs.Gvk) { // With the currrent default configuration,
// because no Gvk is specified, so a wild
// card
continue continue
} }
// TODO: move this test into the filter.
if smellsLikeANameChange(&fs) { if smellsLikeANameChange(&path) {
// "metadata/name" is the only field. // "metadata/name" is the only field.
// this will add a prefix and a suffix // this will add a prefix and a suffix
// to the resource even if those are // to the resource even if those are
@@ -69,11 +78,15 @@ func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
r.AddNamePrefix(p.Prefix) r.AddNamePrefix(p.Prefix)
r.AddNameSuffix(p.Suffix) r.AddNameSuffix(p.Suffix)
} }
err := filtersutil.ApplyToJSON(prefixsuffix.Filter{
Prefix: p.Prefix, // the addPrefixSuffix method will not
Suffix: p.Suffix, // change the name if both the prefix and suffix
FieldSpec: fs, // are empty.
}, r) err := transform.MutateField(
r.Map(),
path.PathSlice(),
path.CreateIfNotPresent,
p.addPrefixSuffix)
if err != nil { if err != nil {
return err return err
} }
@@ -86,7 +99,8 @@ func smellsLikeANameChange(fs *types.FieldSpec) bool {
return fs.Path == "metadata/name" return fs.Path == "metadata/name"
} }
func (p *PrefixSuffixTransformerPlugin) shouldSkip(id resid.ResId) bool { func (p *PrefixSuffixTransformerPlugin) shouldSkip(
id resid.ResId) bool {
for _, path := range prefixSuffixFieldSpecsToSkip { for _, path := range prefixSuffixFieldSpecsToSkip {
if id.IsSelected(&path.Gvk) { if id.IsSelected(&path.Gvk) {
return true return true
@@ -95,6 +109,15 @@ func (p *PrefixSuffixTransformerPlugin) shouldSkip(id resid.ResId) bool {
return false return false
} }
func (p *PrefixSuffixTransformerPlugin) addPrefixSuffix(
in interface{}) (interface{}, error) {
s, ok := in.(string)
if !ok {
return nil, fmt.Errorf("%#v is expected to be %T", in, s)
}
return fmt.Sprintf("%s%s%s", p.Prefix, s, p.Suffix), nil
}
func NewPrefixSuffixTransformerPlugin() resmap.TransformerPlugin { func NewPrefixSuffixTransformerPlugin() resmap.TransformerPlugin {
return &PrefixSuffixTransformerPlugin{} return &PrefixSuffixTransformerPlugin{}
} }

View File

@@ -121,13 +121,13 @@ func (p *ValueAddTransformerPlugin) Transform(m resmap.ResMap) (err error) {
if t.FieldPath == types.MetadataNamespacePath { if t.FieldPath == types.MetadataNamespacePath {
err = filtersutil.ApplyToJSON(namespace.Filter{ err = filtersutil.ApplyToJSON(namespace.Filter{
Namespace: p.Value, Namespace: p.Value,
}, res) }, res.Kunstructured)
} else { } else {
err = filtersutil.ApplyToJSON(valueadd.Filter{ err = filtersutil.ApplyToJSON(valueadd.Filter{
Value: p.Value, Value: p.Value,
FieldPath: t.FieldPath, FieldPath: t.FieldPath,
FilePathPosition: t.FilePathPosition, FilePathPosition: t.FilePathPosition,
}, res) }, res.Kunstructured)
} }
if err != nil { if err != nil {
return err return err

View File

@@ -30,7 +30,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
for _, k := range keys { for _, k := range keys {
if err := node.PipeE(fsslice.Filter{ if err := node.PipeE(fsslice.Filter{
FsSlice: f.FsSlice, FsSlice: f.FsSlice,
SetValue: filtersutil.SetEntry(k, f.Annotations[k], yaml.StringTag), SetValue: fsslice.SetEntry(k, f.Annotations[k], yaml.StringTag),
CreateKind: yaml.MappingNode, // Annotations are MappingNodes. CreateKind: yaml.MappingNode, // Annotations are MappingNodes.
CreateTag: "!!map", CreateTag: "!!map",
}); err != nil { }); err != nil {

View File

@@ -1,6 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package fieldspec contains a yaml.Filter to modify a resource
// that matches the FieldSpec.
package fieldspec

View File

@@ -1,61 +0,0 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fieldspec_test
import (
"bytes"
"log"
"os"
. "sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func ExampleFilter() {
in := &kio.ByteReader{
Reader: bytes.NewBufferString(`
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
---
apiVersion: example.com/v1
kind: Bar
metadata:
name: instance
`),
}
fltr := Filter{
CreateKind: yaml.ScalarNode,
SetValue: filtersutil.SetScalar("green"),
FieldSpec: types.FieldSpec{Path: "a/b", CreateIfNotPresent: true},
}
err := kio.Pipeline{
Inputs: []kio.Reader{in},
Filters: []kio.Filter{kio.FilterAll(fltr)},
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
// a:
// b: green
// ---
// apiVersion: example.com/v1
// kind: Bar
// metadata:
// name: instance
// a:
// b: green
}

View File

@@ -1,380 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fieldspec_test
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
type TestCase struct {
name string
input string
expected string
filter fieldspec.Filter
fieldSpec string
error string
}
var tests = []TestCase{
{
name: "update",
fieldSpec: `
path: a/b
group: foo
kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a:
b: c
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a:
b: e
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
{
name: "update-kind-not-match",
fieldSpec: `
path: a/b
group: foo
kind: Bar1
`,
input: `
apiVersion: foo/v1beta1
kind: Bar2
a:
b: c
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar2
a:
b: c
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
{
name: "update-group-not-match",
fieldSpec: `
path: a/b
group: foo1
kind: Bar
`,
input: `
apiVersion: foo2/v1beta1
kind: Bar
a:
b: c
`,
expected: `
apiVersion: foo2/v1beta1
kind: Bar
a:
b: c
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
{
name: "update-version-not-match",
fieldSpec: `
path: a/b
group: foo
version: v1beta1
kind: Bar
`,
input: `
apiVersion: foo/v1beta2
kind: Bar
a:
b: c
`,
expected: `
apiVersion: foo/v1beta2
kind: Bar
a:
b: c
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
{
name: "bad-version",
fieldSpec: `
path: a/b
group: foo
version: v1beta1
kind: Bar
`,
input: `
apiVersion: foo/v1beta2/something
kind: Bar
a:
b: c
`,
expected: `
apiVersion: foo/v1beta2/something
kind: Bar
a:
b: c
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
{
name: "bad-meta",
fieldSpec: `
path: a/b
group: foo
version: v1beta1
kind: Bar
`,
input: `
a:
b: c
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
error: "missing Resource metadata",
},
{
name: "miss-match-type",
fieldSpec: `
path: a/b/c
kind: Bar
`,
input: `
kind: Bar
a:
b: a
`,
error: "obj kind: Bar\na:\n b: a\n at path a/b/c: unsupported yaml node",
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
{
name: "add",
fieldSpec: `
path: a/b/c/d
group: foo
create: true
kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a: {}
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a: {b: {c: {d: e}}}
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "update-in-sequence",
fieldSpec: `
path: a/b[]/c/d
group: foo
kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a:
b:
- c:
d: a
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a:
b:
- c:
d: e
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
// Don't create a sequence
{
name: "empty-sequence-no-create",
fieldSpec: `
path: a/b[]/c/d
group: foo
create: true
kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a: {}
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a: {}
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
// Create a new field for an element in a sequence
{
name: "empty-sequence-create",
fieldSpec: `
path: a/b[]/c/d
group: foo
create: true
kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a:
b:
- c: {}
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a:
b:
- c: {d: e}
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "group v1",
fieldSpec: `
path: a/b
group: v1
create: true
kind: Bar
`,
input: `
apiVersion: v1
kind: Bar
`,
expected: `
apiVersion: v1
kind: Bar
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "version v1",
fieldSpec: `
path: a/b
version: v1
create: true
kind: Bar
`,
input: `
apiVersion: v1
kind: Bar
`,
expected: `
apiVersion: v1
kind: Bar
a:
b: e
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
}
func TestFilter_Filter(t *testing.T) {
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
err := yaml.Unmarshal([]byte(test.fieldSpec), &test.filter.FieldSpec)
if !assert.NoError(t, err) {
t.FailNow()
}
out := &bytes.Buffer{}
rw := &kio.ByteReadWriter{
Reader: bytes.NewBufferString(test.input),
Writer: out,
OmitReaderAnnotations: true,
}
// run the filter
err = kio.Pipeline{
Inputs: []kio.Reader{rw},
Filters: []kio.Filter{kio.FilterAll(test.filter)},
Outputs: []kio.Writer{rw},
}.Execute()
if test.error != "" {
if !assert.EqualError(t, err, test.error) {
t.FailNow()
}
// stop rest of test
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
// check results
if !assert.Equal(t,
strings.TrimSpace(test.expected),
strings.TrimSpace(out.String())) {
t.FailNow()
}
})
}
}

View File

@@ -1,33 +0,0 @@
package filtersutil
import (
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// SetFn is a function that accepts an RNode to possibly modify.
type SetFn func(*yaml.RNode) error
// SetScalar returns a SetFn to set a scalar value
func SetScalar(value string) SetFn {
return func(node *yaml.RNode) error {
return node.PipeE(yaml.FieldSetter{StringValue: value})
}
}
// SetEntry returns a SetFn to set an entry in a map
func SetEntry(key, value, tag string) SetFn {
n := &yaml.Node{
Kind: yaml.ScalarNode,
Value: value,
Tag: tag,
}
if tag == yaml.StringTag && yaml.IsYaml1_1NonString(n) {
n.Style = yaml.DoubleQuotedStyle
}
return func(node *yaml.RNode) error {
return node.PipeE(yaml.FieldSetter{
Name: key,
Value: yaml.NewRNode(n),
})
}
}

View File

@@ -1,6 +1,6 @@
// Copyright 2020 The Kubernetes Authors. // Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package fsslice contains a yaml.Filter to modify a resource if // Package fsslice contains a yaml.Filter to modify a resource using an
// it matches one or more FieldSpec entries. // FsSlice to identify fields to be updated within the resource.
package fsslice package fsslice

View File

@@ -8,7 +8,6 @@ import (
"log" "log"
"os" "os"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/filters/fsslice" "sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
@@ -31,7 +30,7 @@ metadata:
} }
fltr := fsslice.Filter{ fltr := fsslice.Filter{
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
SetValue: filtersutil.SetScalar("green"), SetValue: fsslice.SetScalar("green"),
FsSlice: []types.FieldSpec{ FsSlice: []types.FieldSpec{
{Path: "a/b", CreateIfNotPresent: true}, {Path: "a/b", CreateIfNotPresent: true},
}, },

View File

@@ -1,27 +1,24 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package fieldspec package fsslice
import ( import (
"strings" "strings"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
var _ yaml.Filter = Filter{} // fieldSpecFilter applies a single fieldSpec to a single object
// fieldSpecFilter stores internal state and should not be reused
// Filter applies a single fieldSpec to a single object type fieldSpecFilter struct {
// Filter stores internal state and should not be reused
type Filter struct {
// FieldSpec contains the path to the value to set. // FieldSpec contains the path to the value to set.
FieldSpec types.FieldSpec `yaml:"fieldSpec"` FieldSpec types.FieldSpec `yaml:"fieldSpec"`
// Set the field using this function // Set the field using this function
SetValue filtersutil.SetFn SetValue SetFn
// CreateKind defines the type of node to create if the field is not found // CreateKind defines the type of node to create if the field is not found
CreateKind yaml.Kind CreateKind yaml.Kind
@@ -32,7 +29,7 @@ type Filter struct {
path []string path []string
} }
func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) { func (fltr fieldSpecFilter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
// check if the FieldSpec applies to the object // check if the FieldSpec applies to the object
if match, err := isMatchGVK(fltr.FieldSpec, obj); !match || err != nil { if match, err := isMatchGVK(fltr.FieldSpec, obj); !match || err != nil {
return obj, errors.Wrap(err) return obj, errors.Wrap(err)
@@ -46,7 +43,7 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
return obj, nil return obj, nil
} }
func (fltr Filter) filter(obj *yaml.RNode) error { func (fltr fieldSpecFilter) filter(obj *yaml.RNode) error {
if len(fltr.path) == 0 { if len(fltr.path) == 0 {
// found the field -- set its value // found the field -- set its value
return fltr.SetValue(obj) return fltr.SetValue(obj)
@@ -63,7 +60,7 @@ func (fltr Filter) filter(obj *yaml.RNode) error {
} }
// field calls filter on the field matching the next path element // field calls filter on the field matching the next path element
func (fltr Filter) field(obj *yaml.RNode) error { func (fltr fieldSpecFilter) field(obj *yaml.RNode) error {
fieldName, isSeq := isSequenceField(fltr.path[0]) fieldName, isSeq := isSequenceField(fltr.path[0])
// lookup the field matching the next path element // lookup the field matching the next path element
@@ -107,9 +104,9 @@ func (fltr Filter) field(obj *yaml.RNode) error {
} }
// seq calls filter on all sequence elements // seq calls filter on all sequence elements
func (fltr Filter) seq(obj *yaml.RNode) error { func (fltr fieldSpecFilter) seq(obj *yaml.RNode) error {
if err := obj.VisitElements(func(node *yaml.RNode) error { if err := obj.VisitElements(func(node *yaml.RNode) error {
// recurse on each element -- re-allocating a Filter is // recurse on each element -- re-allocating a fieldSpecFilter is
// not strictly required, but is more consistent with field // not strictly required, but is more consistent with field
// and less likely to have side effects // and less likely to have side effects
// keep the entire path -- it does not contain parts for sequences // keep the entire path -- it does not contain parts for sequences

View File

@@ -4,22 +4,48 @@
package fsslice package fsslice
import ( import (
"sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
// SetFn sets a value
type SetFn func(*yaml.RNode) error
// SetScalar returns a SetFn to set a scalar value
func SetScalar(value string) SetFn {
return func(node *yaml.RNode) error {
return node.PipeE(yaml.FieldSetter{StringValue: value})
}
}
// SetEntry returns a SetFn to set an entry in a map
func SetEntry(key, value, tag string) SetFn {
n := &yaml.Node{
Kind: yaml.ScalarNode,
Value: value,
Tag: tag,
}
if tag == yaml.StringTag && yaml.IsYaml1_1NonString(n) {
n.Style = yaml.DoubleQuotedStyle
}
return func(node *yaml.RNode) error {
return node.PipeE(yaml.FieldSetter{
Name: key,
Value: yaml.NewRNode(n),
})
}
}
var _ yaml.Filter = Filter{} var _ yaml.Filter = Filter{}
// Filter ranges over an FsSlice to modify fields on a single object. // Filter uses an FsSlice to modify fields on a single object
// An FsSlice is a range of FieldSpecs. A FieldSpec is a GVK plus a path.
type Filter struct { type Filter struct {
// FieldSpecList list of FieldSpecs to set // FieldSpecList list of FieldSpecs to set
FsSlice types.FsSlice `yaml:"fsSlice"` FsSlice types.FsSlice `yaml:"fsSlice"`
// SetValue is called on each field that matches one of the FieldSpecs // SetValue is called on each field that matches one of the FieldSpecs
SetValue filtersutil.SetFn SetValue SetFn
// CreateKind is used to create fields that do not exist // CreateKind is used to create fields that do not exist
CreateKind yaml.Kind CreateKind yaml.Kind
@@ -33,7 +59,7 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
// apply this FieldSpec // apply this FieldSpec
// create a new filter for each iteration because they // create a new filter for each iteration because they
// store internal state about the field paths // store internal state about the field paths
_, err := (&fieldspec.Filter{ _, err := (&fieldSpecFilter{
FieldSpec: fltr.FsSlice[i], FieldSpec: fltr.FsSlice[i],
SetValue: fltr.SetValue, SetValue: fltr.SetValue,
CreateKind: fltr.CreateKind, CreateKind: fltr.CreateKind,

View File

@@ -9,77 +9,335 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/filters/filtersutil" "sigs.k8s.io/kustomize/api/filters/fsslice"
. "sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
type TestCase struct { type TestCase struct {
name string
input string input string
expected string expected string
filter Filter filter fsslice.Filter
fsSlice string fsSlice string
error string error string
} }
var tests = map[string]TestCase{ var tests = []TestCase{
"empty": { {
fsSlice: ` name: "update",
`,
input: `
apiVersion: foo/v1
kind: Bar
`,
expected: `
apiVersion: foo/v1
kind: Bar
`,
filter: Filter{
SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
"two": {
fsSlice: ` fsSlice: `
- path: a/b - path: a/b
group: foo group: foo
version: v1 kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a:
b: c
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a:
b: e
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
},
},
{
name: "update-kind-not-match",
fsSlice: `
- path: a/b
group: foo
kind: Bar1
`,
input: `
apiVersion: foo/v1beta1
kind: Bar2
a:
b: c
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar2
a:
b: c
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
},
},
{
name: "update-group-not-match",
fsSlice: `
- path: a/b
group: foo1
kind: Bar
`,
input: `
apiVersion: foo2/v1beta1
kind: Bar
a:
b: c
`,
expected: `
apiVersion: foo2/v1beta1
kind: Bar
a:
b: c
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
},
},
{
name: "update-version-not-match",
fsSlice: `
- path: a/b
group: foo
version: v1beta1
kind: Bar
`,
input: `
apiVersion: foo/v1beta2
kind: Bar
a:
b: c
`,
expected: `
apiVersion: foo/v1beta2
kind: Bar
a:
b: c
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
},
},
{
name: "bad-version",
fsSlice: `
- path: a/b
group: foo
version: v1beta1
kind: Bar
`,
input: `
apiVersion: foo/v1beta2/something
kind: Bar
a:
b: c
`,
expected: `
apiVersion: foo/v1beta2/something
kind: Bar
a:
b: c
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
},
},
{
name: "bad-meta",
fsSlice: `
- path: a/b
group: foo
version: v1beta1
kind: Bar
`,
input: `
a:
b: c
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
},
error: "missing Resource metadata",
},
{
name: "miss-match-type",
fsSlice: `
- path: a/b/c
kind: Bar
`,
input: `
kind: Bar
a:
b: a
`,
error: "obj kind: Bar\na:\n b: a\n at path a/b/c: unsupported yaml node",
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
},
},
{
name: "add",
fsSlice: `
- path: a/b/c/d
group: foo
create: true create: true
kind: Bar kind: Bar
- path: q/r[]/s/t `,
input: `
apiVersion: foo/v1beta1
kind: Bar
a: {}
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a: {b: {c: {d: e}}}
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "update-in-sequence",
fsSlice: `
- path: a/b[]/c/d
group: foo group: foo
kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a:
b:
- c:
d: a
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a:
b:
- c:
d: e
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
},
},
// Don't create a sequence
{
name: "empty-sequence-no-create",
fsSlice: `
- path: a/b[]/c/d
group: foo
create: true
kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a: {}
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a: {}
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
// Create a new field for an element in a sequence
{
name: "empty-sequence-create",
fsSlice: `
- path: a/b[]/c/d
group: foo
create: true
kind: Bar
`,
input: `
apiVersion: foo/v1beta1
kind: Bar
a:
b:
- c: {}
`,
expected: `
apiVersion: foo/v1beta1
kind: Bar
a:
b:
- c: {d: e}
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "group v1",
fsSlice: `
- path: a/b
group: v1
create: true
kind: Bar
`,
input: `
apiVersion: v1
kind: Bar
`,
expected: `
apiVersion: v1
kind: Bar
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "version v1",
fsSlice: `
- path: a/b
version: v1 version: v1
create: true create: true
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo/v1 apiVersion: v1
kind: Bar kind: Bar
q:
r:
- s: {}
`, `,
expected: ` expected: `
apiVersion: foo/v1 apiVersion: v1
kind: Bar kind: Bar
q:
r:
- s: {t: e}
a: a:
b: e b: e
`, `,
filter: Filter{ filter: fsslice.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
}, },
}, },
} }
func TestFilter(t *testing.T) { func TestFilter_Filter(t *testing.T) {
for name := range tests { for i := range tests {
test := tests[name] test := tests[i]
t.Run(name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
err := yaml.Unmarshal([]byte(test.fsSlice), &test.filter.FsSlice) err := yaml.Unmarshal([]byte(test.fsSlice), &test.filter.FsSlice)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()

View File

@@ -1,6 +1,6 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package fieldspec package fsslice
import ( import (
"strings" "strings"

View File

@@ -1,6 +1,6 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package fieldspec package fsslice
import ( import (
"strings" "strings"

View File

@@ -4,7 +4,6 @@
package imagetag package imagetag
import ( import (
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/filters/fsslice" "sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
@@ -36,7 +35,7 @@ func (f Filter) filter(node *yaml.RNode) (*yaml.RNode, error) {
return node, nil return node, nil
} }
func updateImageTagFn(imageTag types.Image) filtersutil.SetFn { func updateImageTagFn(imageTag types.Image) fsslice.SetFn {
return func(node *yaml.RNode) error { return func(node *yaml.RNode) error {
return node.PipeE(imageTagUpdater{ return node.PipeE(imageTagUpdater{
ImageTag: imageTag, ImageTag: imageTag,

View File

@@ -31,7 +31,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
for _, k := range keys { for _, k := range keys {
if err := node.PipeE(fsslice.Filter{ if err := node.PipeE(fsslice.Filter{
FsSlice: f.FsSlice, FsSlice: f.FsSlice,
SetValue: filtersutil.SetEntry(k, f.Labels[k], yaml.StringTag), SetValue: fsslice.SetEntry(k, f.Labels[k], yaml.StringTag),
CreateKind: yaml.MappingNode, // Labels are MappingNodes. CreateKind: yaml.MappingNode, // Labels are MappingNodes.
CreateTag: "!!map", CreateTag: "!!map",
}); err != nil { }); err != nil {

View File

@@ -4,8 +4,6 @@
package namespace package namespace
import ( import (
"sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/filters/fsslice" "sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
@@ -44,7 +42,7 @@ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
// transformations based on data -- :) // transformations based on data -- :)
err := node.PipeE(fsslice.Filter{ err := node.PipeE(fsslice.Filter{
FsSlice: ns.FsSlice, FsSlice: ns.FsSlice,
SetValue: filtersutil.SetScalar(ns.Namespace), SetValue: fsslice.SetScalar(ns.Namespace),
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
CreateTag: yaml.StringTag, CreateTag: yaml.StringTag,
}) })
@@ -75,7 +73,7 @@ func (ns Filter) hacks(obj *yaml.RNode) error {
// if they are cluster scoped through either an annotation on the resources, // if they are cluster scoped through either an annotation on the resources,
// or through inlined OpenAPI on the resource as a YAML comment. // or through inlined OpenAPI on the resource as a YAML comment.
func (ns Filter) metaNamespaceHack(obj *yaml.RNode, meta yaml.ResourceMeta) error { func (ns Filter) metaNamespaceHack(obj *yaml.RNode, meta yaml.ResourceMeta) error {
gvk := fieldspec.GetGVK(meta) gvk := fsslice.GetGVK(meta)
if !gvk.IsNamespaceableKind() { if !gvk.IsNamespaceableKind() {
return nil return nil
} }
@@ -83,7 +81,7 @@ func (ns Filter) metaNamespaceHack(obj *yaml.RNode, meta yaml.ResourceMeta) erro
FsSlice: []types.FieldSpec{ FsSlice: []types.FieldSpec{
{Path: types.MetadataNamespacePath, CreateIfNotPresent: true}, {Path: types.MetadataNamespacePath, CreateIfNotPresent: true},
}, },
SetValue: filtersutil.SetScalar(ns.Namespace), SetValue: fsslice.SetScalar(ns.Namespace),
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
} }
_, err := f.Filter(obj) _, err := f.Filter(obj)

View File

@@ -1,6 +1,3 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package patchstrategicmerge package patchstrategicmerge
import ( import (
@@ -16,13 +13,9 @@ type Filter struct {
var _ kio.Filter = Filter{} var _ kio.Filter = Filter{}
func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
var result []*yaml.RNode return kio.FilterAll(yaml.FilterFunc(pf.run)).Filter(nodes)
for i := range nodes { }
r, err := merge2.Merge(pf.Patch, nodes[i])
if err != nil { func (pf Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
return nil, err return merge2.Merge(pf.Patch, node)
}
result = append(result, r)
}
return result, nil
} }

View File

@@ -31,86 +31,6 @@ apiVersion: apps/v1
metadata: metadata:
name: yourDeploy name: yourDeploy
kind: Deployment kind: Deployment
`,
},
"container patch": {
input: `
apiVersion: apps/v1
metadata:
name: myDeploy
kind: Deployment
spec:
template:
spec:
containers:
- name: foo1
- name: foo2
- name: foo3
`,
patch: yaml.MustParse(`
apiVersion: apps/v1
metadata:
name: myDeploy
kind: Deployment
spec:
template:
spec:
containers:
- name: foo0
`),
expected: `
apiVersion: apps/v1
metadata:
name: myDeploy
kind: Deployment
spec:
template:
spec:
containers:
- name: foo1
- name: foo2
- name: foo3
- name: foo0
`,
},
"volumes patch": {
input: `
apiVersion: apps/v1
metadata:
name: myDeploy
kind: Deployment
spec:
template:
spec:
volumes:
- name: foo1
- name: foo2
- name: foo3
`,
patch: yaml.MustParse(`
apiVersion: apps/v1
metadata:
name: myDeploy
kind: Deployment
spec:
template:
spec:
volumes:
- name: foo0
`),
expected: `
apiVersion: apps/v1
metadata:
name: myDeploy
kind: Deployment
spec:
template:
spec:
volumes:
- name: foo1
- name: foo2
- name: foo3
- name: foo0
`, `,
}, },
"nested patch": { "nested patch": {

View File

@@ -9,11 +9,12 @@ import (
"os" "os"
"sigs.k8s.io/kustomize/api/filters/prefixsuffix" "sigs.k8s.io/kustomize/api/filters/prefixsuffix"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
) )
func ExampleFilter() { func ExampleFilter() {
fss := builtinconfig.MakeDefaultConfig().NamePrefix
err := kio.Pipeline{ err := kio.Pipeline{
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(` Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
apiVersion: example.com/v1 apiVersion: example.com/v1
@@ -26,8 +27,7 @@ kind: Bar
metadata: metadata:
name: instance name: instance
`)}}, `)}},
Filters: []kio.Filter{prefixsuffix.Filter{ Filters: []kio.Filter{prefixsuffix.Filter{Prefix: "baz-", FsSlice: fss}},
Prefix: "baz-", FieldSpec: types.FieldSpec{Path: "metadata/name"}}},
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}}, Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
}.Execute() }.Execute()
if err != nil { if err != nil {

View File

@@ -6,8 +6,7 @@ package prefixsuffix
import ( import (
"fmt" "fmt"
"sigs.k8s.io/kustomize/api/filters/fieldspec" "sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
@@ -18,26 +17,28 @@ type Filter struct {
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
} }
var _ kio.Filter = Filter{} var _ kio.Filter = Filter{}
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) return kio.FilterAll(yaml.FilterFunc(ns.run)).Filter(nodes)
} }
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { // Run runs the filter on a single node rather than a slice
err := node.PipeE(fieldspec.Filter{ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
FieldSpec: f.FieldSpec, // transformations based on data -- :)
SetValue: f.evaluateField, err := node.PipeE(fsslice.Filter{
FsSlice: ns.FsSlice,
SetValue: ns.set,
CreateKind: yaml.ScalarNode, // Name is a ScalarNode CreateKind: yaml.ScalarNode, // Name is a ScalarNode
CreateTag: yaml.StringTag, CreateTag: yaml.StringTag,
}) })
return node, err return node, err
} }
func (f Filter) evaluateField(node *yaml.RNode) error { func (ns Filter) set(node *yaml.RNode) error {
return filtersutil.SetScalar(fmt.Sprintf( return fsslice.SetScalar(fmt.Sprintf(
"%s%s%s", f.Prefix, node.YNode().Value, f.Suffix))(node) "%s%s%s", ns.Prefix, node.YNode().Value, ns.Suffix))(node)
} }

View File

@@ -9,12 +9,14 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/filters/prefixsuffix" "sigs.k8s.io/kustomize/api/filters/prefixsuffix"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
var tests = map[string]TestCase{ var tests = []TestCase{
"prefix": { {
name: "prefix",
input: ` input: `
apiVersion: example.com/v1 apiVersion: example.com/v1
kind: Foo kind: Foo
@@ -38,10 +40,10 @@ metadata:
name: foo-instance name: foo-instance
`, `,
filter: prefixsuffix.Filter{Prefix: "foo-"}, filter: prefixsuffix.Filter{Prefix: "foo-"},
fs: types.FieldSpec{Path: "metadata/name"},
}, },
"suffix": { {
name: "suffix",
input: ` input: `
apiVersion: example.com/v1 apiVersion: example.com/v1
kind: Foo kind: Foo
@@ -65,10 +67,10 @@ metadata:
name: instance-foo name: instance-foo
`, `,
filter: prefixsuffix.Filter{Suffix: "-foo"}, filter: prefixsuffix.Filter{Suffix: "-foo"},
fs: types.FieldSpec{Path: "metadata/name"},
}, },
"prefix-suffix": { {
name: "prefix-suffix",
input: ` input: `
apiVersion: example.com/v1 apiVersion: example.com/v1
kind: Foo kind: Foo
@@ -92,10 +94,10 @@ metadata:
name: bar-instance-foo name: bar-instance-foo
`, `,
filter: prefixsuffix.Filter{Prefix: "bar-", Suffix: "-foo"}, filter: prefixsuffix.Filter{Prefix: "bar-", Suffix: "-foo"},
fs: types.FieldSpec{Path: "metadata/name"},
}, },
"data-fieldspecs": { {
name: "data-fieldspecs",
input: ` input: `
apiVersion: example.com/v1 apiVersion: example.com/v1
kind: Foo kind: Foo
@@ -117,7 +119,7 @@ a:
apiVersion: example.com/v1 apiVersion: example.com/v1
kind: Foo kind: Foo
metadata: metadata:
name: instance name: foo-instance
a: a:
b: b:
c: foo-d c: foo-d
@@ -125,28 +127,35 @@ a:
apiVersion: example.com/v1 apiVersion: example.com/v1
kind: Bar kind: Bar
metadata: metadata:
name: instance name: foo-instance
a: a:
b: b:
c: foo-d c: foo-d
`, `,
filter: prefixsuffix.Filter{Prefix: "foo-"}, filter: prefixsuffix.Filter{Prefix: "foo-"},
fs: types.FieldSpec{Path: "a/b/c"}, fsslice: []types.FieldSpec{
{
Path: "a/b/c",
},
},
}, },
} }
type TestCase struct { type TestCase struct {
name string
input string input string
expected string expected string
filter prefixsuffix.Filter filter prefixsuffix.Filter
fs types.FieldSpec fsslice types.FsSlice
} }
var config = builtinconfig.MakeDefaultConfig()
func TestFilter(t *testing.T) { func TestFilter(t *testing.T) {
for name := range tests { for i := range tests {
test := tests[name] test := tests[i]
t.Run(name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
test.filter.FieldSpec = test.fs test.filter.FsSlice = append(config.NamePrefix, test.fsslice...)
if !assert.Equal(t, if !assert.Equal(t,
strings.TrimSpace(test.expected), strings.TrimSpace(test.expected),
strings.TrimSpace( strings.TrimSpace(

View File

@@ -3,7 +3,6 @@ package replicacount
import ( import (
"strconv" "strconv"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/filters/fsslice" "sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
@@ -46,5 +45,5 @@ func (rc Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
} }
func (rc Filter) set(node *yaml.RNode) error { func (rc Filter) set(node *yaml.RNode) error {
return filtersutil.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node) return fsslice.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node)
} }

View File

@@ -11,13 +11,11 @@ require (
github.com/stretchr/testify v1.4.0 github.com/stretchr/testify v1.4.0
github.com/yujunz/go-getter v1.4.1-lite github.com/yujunz/go-getter v1.4.1-lite
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff golang.org/x/tools v0.0.0-20191010075000-0337d82405ff
gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v2 v2.2.8
k8s.io/api v0.17.0 k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0 k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0 k8s.io/client-go v0.17.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
sigs.k8s.io/kustomize/kyaml v0.4.1 sigs.k8s.io/kustomize/kyaml v0.1.11
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
replace sigs.k8s.io/kustomize/kyaml v0.4.1 => ../kyaml

View File

@@ -340,7 +340,6 @@ 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-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 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/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA= 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/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/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@@ -424,7 +423,6 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 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-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= 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/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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@@ -550,10 +548,9 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/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 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -580,6 +577,8 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphD
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/kyaml v0.1.11 h1:/VvWxVIgH5gG1K4A7trgbyLgO3tRBiAWNhLFVU1HEmo=
sigs.k8s.io/kustomize/kyaml v0.1.11/go.mod h1:72/rLkSi+L/pHM1oCjwrf3ClU+tH5kZQvvdLSqIHwWU=
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-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=

View File

@@ -41,33 +41,33 @@ type Loader interface {
// Kunstructured allows manipulation of k8s objects // Kunstructured allows manipulation of k8s objects
// that do not have Golang structs. // that do not have Golang structs.
type Kunstructured interface { type Kunstructured interface {
Copy() Kunstructured
GetAnnotations() map[string]string
GetBool(path string) (bool, error)
GetFieldValue(string) (interface{}, error)
GetFloat64(path string) (float64, error)
GetGvk() resid.Gvk
GetInt64(path string) (int64, error)
GetKind() string
GetLabels() map[string]string
GetMap(path string) (map[string]interface{}, error)
GetName() string
GetSlice(path string) ([]interface{}, error)
GetString(string) (string, error)
GetStringMap(path string) (map[string]string, error)
GetStringSlice(string) ([]string, error)
Map() map[string]interface{} Map() map[string]interface{}
MarshalJSON() ([]byte, error)
MatchesAnnotationSelector(selector string) (bool, error)
MatchesLabelSelector(selector string) (bool, error)
Patch(Kunstructured) error
SetAnnotations(map[string]string)
SetGvk(resid.Gvk)
SetLabels(map[string]string)
SetMap(map[string]interface{}) SetMap(map[string]interface{})
Copy() Kunstructured
GetFieldValue(string) (interface{}, error)
GetString(string) (string, error)
GetStringSlice(string) ([]string, error)
GetBool(path string) (bool, error)
GetFloat64(path string) (float64, error)
GetInt64(path string) (int64, error)
GetSlice(path string) ([]interface{}, error)
GetStringMap(path string) (map[string]string, error)
GetMap(path string) (map[string]interface{}, error)
MarshalJSON() ([]byte, error)
UnmarshalJSON([]byte) error
GetGvk() resid.Gvk
SetGvk(resid.Gvk)
GetKind() string
GetName() string
SetName(string) SetName(string)
SetNamespace(string) SetNamespace(string)
UnmarshalJSON([]byte) error GetLabels() map[string]string
SetLabels(map[string]string)
GetAnnotations() map[string]string
SetAnnotations(map[string]string)
MatchesLabelSelector(selector string) (bool, error)
MatchesAnnotationSelector(selector string) (bool, error)
Patch(Kunstructured) error
} }
// KunstructuredFactory makes instances of Kunstructured. // KunstructuredFactory makes instances of Kunstructured.

View File

@@ -1,4 +1,4 @@
FROM golang:1.14 AS build FROM golang:1.11 AS build
ARG GO111MODULE=on ARG GO111MODULE=on

View File

@@ -159,31 +159,40 @@ func main() {
} }
} }
query := []byte(`{ "query":{ "match_all":{} } }`) seedDocs := make(crawler.CrawlSeed, 0)
it := idx.IterateQuery(query, 10000, 60*time.Second)
// get all the documents in the index
getSeedDocsFunc := func() {
query := []byte(`{ "query":{ "match_all":{} } }`)
it := idx.IterateQuery(query, 10000, 60*time.Second)
for it.Next() {
for _, hit := range it.Value().Hits.Hits {
seedDocs = append(seedDocs, hit.Document.Document.Copy())
}
}
if err := it.Err(); err != nil {
log.Fatalf("getSeedDocsFunc Error iterating: %v\n", err)
}
}
switch mode { switch mode {
case CrawlIndexAndGithub: case CrawlIndexAndGithub:
getSeedDocsFunc()
crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")} crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")}
crawler.CrawlFromSeedIterator(ctx, it, crawlers, docConverter, indexFunc, seen) crawler.CrawlFromSeed(ctx, seedDocs, crawlers, docConverter, indexFunc, seen)
crawler.CrawlGithub(ctx, crawlers, docConverter, indexFunc, seen) crawler.CrawlGithub(ctx, crawlers, docConverter, indexFunc, seen)
case CrawlIndex: case CrawlIndex:
getSeedDocsFunc()
crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")} crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")}
crawler.CrawlFromSeedIterator(ctx, it, crawlers, docConverter, indexFunc, seen) crawler.CrawlFromSeed(ctx, seedDocs, crawlers, docConverter, indexFunc, seen)
case CrawlGithub: case CrawlGithub:
crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")} crawlers := []crawler.Crawler{ghCrawlerConstructor("", "")}
// add all the documents in the index into seen. // add all the documents in the index into seen.
// this greatly reduces the time overhead of CrawlGithub. // this greatly reduces the time overhead of CrawlGithub.
for it.Next() { getSeedDocsFunc()
for _, hit := range it.Value().Hits.Hits { for _, d := range seedDocs {
d := hit.Document.Document seen[d.ID()] = d.FileType
seen.Set(d.ID(), d.FileType)
}
} }
if err := it.Err(); err != nil {
log.Fatalf("Error iterating the index: %v\n", err)
}
crawler.CrawlGithub(ctx, crawlers, docConverter, indexFunc, seen) crawler.CrawlGithub(ctx, crawlers, docConverter, indexFunc, seen)
case CrawlUser: case CrawlUser:
if *githubUserPtr == "" { if *githubUserPtr == "" {

View File

@@ -213,36 +213,21 @@ func doCrawl(ctx context.Context, docsPtr *CrawlSeed, crawlers []Crawler, conv C
logger.Printf("\t%d documents cannot be converted but still were inserted or updated in the index\n", convErrCount) logger.Printf("\t%d documents cannot be converted but still were inserted or updated in the index\n", convErrCount)
} }
// CrawlFromSeedIterator iterates all the documents in the index and call CrawlFromSeed for each document.
func CrawlFromSeedIterator(ctx context.Context, it *index.KustomizeIterator, crawlers []Crawler,
conv Converter, indx IndexFunc, seen utils.SeenMap) {
docCount := 0
for it.Next() {
for _, hit := range it.Value().Hits.Hits {
docCount++
logger.Printf("updating document %d from seed\n", docCount)
singleSeed := CrawlSeed{&(hit.Document.Document)}
CrawlFromSeed(ctx, singleSeed, crawlers, conv, indx, seen)
}
}
if err := it.Err(); err != nil {
log.Fatalf("Error iterating the index: %v\n", err)
}
}
// CrawlFromSeed updates all the documents in seed, and crawls all the new // CrawlFromSeed updates all the documents in seed, and crawls all the new
// documents referred in the seed. // documents referred in the seed.
func CrawlFromSeed(ctx context.Context, seed CrawlSeed, crawlers []Crawler, func CrawlFromSeed(ctx context.Context, seed CrawlSeed, crawlers []Crawler,
conv Converter, indx IndexFunc, seen utils.SeenMap) { conv Converter, indx IndexFunc, seen utils.SeenMap) {
// stack tracks the documents directly referred in the seed. // stack tracks the documents directly referred in other documents.
stack := make(CrawlSeed, 0) stack := make(CrawlSeed, 0)
// Exploit seed to update bulk of corpus.
logger.Printf("updating %d documents from seed\n", len(seed))
// each unique document in seed will be crawled once. // each unique document in seed will be crawled once.
doCrawl(ctx, &seed, crawlers, conv, indx, seen, &stack, true, false) doCrawl(ctx, &seed, crawlers, conv, indx, seen, &stack, true, false)
logger.Printf("crawling %d new documents referred by doc\n", len(stack)) // Traverse any new documents added while updating corpus.
logger.Printf("crawling %d new documents found in the seed\n", len(stack))
// While crawling each document in stack, the documents directly referred in the document // While crawling each document in stack, the documents directly referred in the document
// will be added into stack. // will be added into stack.
// After this statement is done, stack will become empty. // After this statement is done, stack will become empty.
@@ -312,6 +297,8 @@ func CrawlGithubRunner(ctx context.Context, output chan<- CrawledDocument,
// CrawlGithub crawls all the kustomization files on Github. // CrawlGithub crawls all the kustomization files on Github.
func CrawlGithub(ctx context.Context, crawlers []Crawler, conv Converter, func CrawlGithub(ctx context.Context, crawlers []Crawler, conv Converter,
indx IndexFunc, seen utils.SeenMap) { indx IndexFunc, seen utils.SeenMap) {
// stack tracks the documents directly referred in other documents.
stack := make(CrawlSeed, 0)
// ch is channel where all the crawlers sends the crawled documents to. // ch is channel where all the crawlers sends the crawled documents to.
ch := make(chan CrawledDocument, 1<<10) ch := make(chan CrawledDocument, 1<<10)
@@ -337,20 +324,7 @@ func CrawlGithub(ctx context.Context, crawlers []Crawler, conv Converter,
"%v could not match any crawler", cdoc)) "%v could not match any crawler", cdoc))
continue continue
} }
// stack tracks the documents directly referred in the document.
stack := make(CrawlSeed, 0)
addBranches(cdoc, match, indx, seen, &stack) addBranches(cdoc, match, indx, seen, &stack)
if len(stack) > 0 {
// here the documents referred in a kustomization file are crawled separately,
// to avoid accumulating all the referred documents into a single gigantic
// mem-inentive stack.
logger.Printf("crawling the %d new documents referred in doc %d",
len(stack), docCount)
doCrawl(ctx, &stack, crawlers, conv, indx, seen, &stack, false, true)
}
} }
}() }()
@@ -362,4 +336,9 @@ func CrawlGithub(ctx context.Context, crawlers []Crawler, conv Converter,
} }
close(ch) close(ch)
wg.Wait() wg.Wait()
// Handle deps of newly discovered documents.
logger.Printf("crawling the %d new documents referred by other documents",
len(stack))
doCrawl(ctx, &stack, crawlers, conv, indx, seen, &stack, false, true)
} }

View File

@@ -82,15 +82,14 @@ func (gc githubCrawler) Crawl(ctx context.Context,
ranges := []RangeWithin{ ranges := []RangeWithin{
RangeWithin{ RangeWithin{
start: uint64(0), start: uint64(0),
end: githubMaxFileSize, end: githubMaxFileSize,
}, },
} }
errs := make(multiError, 0) errs := make(multiError, 0)
for len(ranges) > 0 { for len(ranges) > 0 {
logger.Printf("Current ranges: %v (len: %d)\n", ranges, len(ranges)) tailRange := ranges[len(ranges) - 1]
tailRange := ranges[len(ranges)-1]
ranges = ranges[:(len(ranges) - 1)] ranges = ranges[:(len(ranges) - 1)]
reProcessQueryRanges, err := gc.CrawlSingleRange(ctx, output, seen, tailRange.start, tailRange.end) reProcessQueryRanges, err := gc.CrawlSingleRange(ctx, output, seen, tailRange.start, tailRange.end)
if err != nil { if err != nil {
@@ -152,15 +151,7 @@ func (gc githubCrawler) CrawlSingleRange(ctx context.Context,
} }
queryResult.Add(rangeResult) queryResult.Add(rangeResult)
if reProcessQuery { if reProcessQuery {
// if the size of a range is 0, such as [245, 245], and reProcessQuery is true, reProcessQueryRanges = append(reProcessQueryRanges, RangeSizes(query))
// it means that there are more than 1000 results for the query range.
// Reprocessing the query range will not help because the GitHub Search API
// only provides up to 1,000 results for each search.
if RangeSizes(query).Size() == 0 {
logger.Printf("range size is 0 includes more than 1000 results: %s", query)
} else {
reProcessQueryRanges = append(reProcessQueryRanges, RangeSizes(query))
}
} }
} }

View File

@@ -225,7 +225,3 @@ type RangeWithin struct {
func (r RangeWithin) RangeString() string { func (r RangeWithin) RangeString() string {
return fmt.Sprintf("%d..%d", r.start, r.end) return fmt.Sprintf("%d..%d", r.start, r.end)
} }
func (r RangeWithin) Size() uint64 {
return r.end - r.start
}

View File

@@ -189,8 +189,13 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v0.0.0-20160422134519-667fe4e3466a/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v0.0.0-20160922145804-ca9ada445741/go.mod h1:+WVp8kdw6VhyKExm03PAMRn2ZxnPtm58pV0dBVPdhHE=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
@@ -263,6 +268,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 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/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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/monopole/mdrip v1.0.1/go.mod h1:/7E04hlzRG9Jrp6WILZfYYm/REoJWL2l+MlsCO1eH74=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= 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/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= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -483,6 +489,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/russross/blackfriday.v2 v2.0.0/go.mod h1:6sSBNz/GtOm/pJTuh5UmBK2ZHfmnxGbl2NZg1UliSOI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@@ -490,10 +497,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= 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.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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-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.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -515,7 +521,7 @@ 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/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/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= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/kyaml v0.4.1/go.mod h1:XJL84E6sOFeNrQ7CADiemc1B0EjIxHo3OhW4o1aJYNw= sigs.k8s.io/kustomize/kyaml v0.1.11/go.mod h1:72/rLkSi+L/pHM1oCjwrf3ClU+tH5kZQvvdLSqIHwWU=
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-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 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

File diff suppressed because it is too large Load Diff

View File

@@ -1,37 +1,21 @@
package utils package utils
import "sync" type SeenMap map[string]string
type SeenMap struct {
data map[string]string
lock sync.RWMutex
}
// TODO: add lock to avoid race condition
func (seen SeenMap) Seen(item string) bool { func (seen SeenMap) Seen(item string) bool {
seen.lock.RLock() _, ok := seen[item]
_, ok := seen.data[item]
seen.lock.RUnlock()
return ok return ok
} }
func (seen SeenMap) Set(k, v string) { func (seen SeenMap) Set(k, v string) {
seen.lock.Lock() seen[k] = v
seen.data[k] = v
seen.lock.Unlock()
} }
// The caller should make sure that key is in the map. // The caller should make sure that key is in the map.
func (seen SeenMap) Value(k string) string { func (seen SeenMap) Value(k string) string {
seen.lock.RLock() return seen[k]
v := seen.data[k]
seen.lock.RUnlock()
return v
} }
func NewSeenMap() SeenMap { func NewSeenMap() SeenMap {
return SeenMap{ return make(map[string]string)
data: make(map[string]string),
lock: sync.RWMutex{},
}
} }

View File

@@ -9,17 +9,22 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
"github.com/google/shlex" "github.com/google/shlex"
"github.com/pkg/errors" "github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/internal/plugins/utils" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
const ( const (
idAnnotation = "kustomize.config.k8s.io/id"
HashAnnotation = "kustomize.config.k8s.io/needs-hash"
BehaviorAnnotation = "kustomize.config.k8s.io/behavior"
tmpConfigFilePrefix = "kust-plugin-config-" tmpConfigFilePrefix = "kust-plugin-config-"
) )
@@ -109,12 +114,12 @@ func (p *ExecPlugin) Generate() (resmap.ResMap, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return utils.UpdateResourceOptions(rm) return p.UpdateResourceOptions(rm)
} }
func (p *ExecPlugin) Transform(rm resmap.ResMap) error { func (p *ExecPlugin) Transform(rm resmap.ResMap) error {
// add ResIds as annotations to all objects so that we can add them back // add ResIds as annotations to all objects so that we can add them back
inputRM, err := utils.GetResMapWithIDAnnotation(rm) inputRM, err := p.getResMapWithIdAnnotation(rm)
if err != nil { if err != nil {
return err return err
} }
@@ -132,7 +137,7 @@ func (p *ExecPlugin) Transform(rm resmap.ResMap) error {
} }
// update the original ResMap based on the output // update the original ResMap based on the output
return utils.UpdateResMapValues(p.path, p.h, output, rm) return p.updateResMapValues(output, rm)
} }
// invokePlugin writes plugin config to a temp file, then // invokePlugin writes plugin config to a temp file, then
@@ -179,3 +184,91 @@ func (p *ExecPlugin) getEnv() []string {
"KUSTOMIZE_PLUGIN_CONFIG_ROOT="+p.h.Loader().Root()) "KUSTOMIZE_PLUGIN_CONFIG_ROOT="+p.h.Loader().Root())
return env return env
} }
// Returns a new copy of the given ResMap with the ResIds annotated in each Resource
func (p *ExecPlugin) getResMapWithIdAnnotation(rm resmap.ResMap) (resmap.ResMap, error) {
inputRM := rm.DeepCopy()
for _, r := range inputRM.Resources() {
idString, err := yaml.Marshal(r.CurId())
if err != nil {
return nil, err
}
annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
annotations[idAnnotation] = string(idString)
r.SetAnnotations(annotations)
}
return inputRM, nil
}
// updateResMapValues updates the Resource value in the given ResMap
// with the emitted Resource values in output.
func (p *ExecPlugin) updateResMapValues(output []byte, rm resmap.ResMap) error {
outputRM, err := p.h.ResmapFactory().NewResMapFromBytes(output)
if err != nil {
return err
}
for _, r := range outputRM.Resources() {
// for each emitted Resource, find the matching Resource in the original ResMap
// using its id
annotations := r.GetAnnotations()
idString, ok := annotations[idAnnotation]
if !ok {
return fmt.Errorf("the transformer %s should not remove annotation %s",
p.path, idAnnotation)
}
id := resid.ResId{}
err := yaml.Unmarshal([]byte(idString), &id)
if err != nil {
return err
}
res, err := rm.GetByCurrentId(id)
if err != nil {
return fmt.Errorf("unable to find unique match to %s", id.String())
}
// remove the annotation set by Kustomize to track the resource
delete(annotations, idAnnotation)
if len(annotations) == 0 {
annotations = nil
}
r.SetAnnotations(annotations)
// update the ResMap resource value with the transformed object
res.Kunstructured = r.Kunstructured
}
return nil
}
// updateResourceOptions updates the generator options for each resource in the
// given ResMap based on plugin provided annotations.
func (p *ExecPlugin) UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {
for _, r := range rm.Resources() {
// Disable name hashing by default and require plugin to explicitly
// request it for each resource.
annotations := r.GetAnnotations()
behavior := annotations[BehaviorAnnotation]
var needsHash bool
if val, ok := annotations[HashAnnotation]; ok {
b, err := strconv.ParseBool(val)
if err != nil {
return nil, fmt.Errorf(
"the annotation %q contains an invalid value (%q)",
HashAnnotation, val)
}
needsHash = b
}
delete(annotations, HashAnnotation)
delete(annotations, BehaviorAnnotation)
if len(annotations) == 0 {
annotations = nil
}
r.SetAnnotations(annotations)
r.SetOptions(types.NewGenArgs(
&types.GeneratorArgs{
Behavior: behavior,
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
}
return rm, nil
}

View File

@@ -4,6 +4,7 @@
package execplugin_test package execplugin_test
import ( import (
"fmt"
"strings" "strings"
"testing" "testing"
@@ -16,6 +17,7 @@ import (
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
"sigs.k8s.io/kustomize/api/types"
) )
func TestExecPluginConfig(t *testing.T) { func TestExecPluginConfig(t *testing.T) {
@@ -89,3 +91,107 @@ metadata:
t.Fatalf("unexpected arg array: %#v", p.Args()) t.Fatalf("unexpected arg array: %#v", p.Args())
} }
} }
func makeConfigMap(rf *resource.Factory, name, behavior string, hashValue *string) *resource.Resource {
r := rf.FromMap(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{"name": name},
})
annotations := map[string]string{}
if behavior != "" {
annotations[BehaviorAnnotation] = behavior
}
if hashValue != nil {
annotations[HashAnnotation] = *hashValue
}
if len(annotations) > 0 {
r.SetAnnotations(annotations)
}
return r
}
func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHash bool) *resource.Resource {
return rf.FromMapAndOption(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{"name": name},
}, &types.GeneratorArgs{
Behavior: behavior,
Options: &types.GeneratorOptions{DisableNameSuffixHash: disableHash}})
}
func strptr(s string) *string {
return &s
}
func TestUpdateResourceOptions(t *testing.T) {
p := NewExecPlugin("")
if err := p.ErrIfNotExecutable(); err == nil {
t.Fatalf("expected unexecutable error")
}
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
in := resmap.New()
expected := resmap.New()
cases := []struct {
behavior string
needsHash bool
hashValue *string
}{
{hashValue: strptr("false")},
{hashValue: strptr("true"), needsHash: true},
{behavior: "replace"},
{behavior: "merge"},
{behavior: "create"},
{behavior: "nonsense"},
{behavior: "merge", hashValue: strptr("false")},
{behavior: "merge", hashValue: strptr("true"), needsHash: true},
}
for i, c := range cases {
name := fmt.Sprintf("test%d", i)
in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue))
expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash))
}
actual, err := p.UpdateResourceOptions(in)
if err != nil {
t.Fatalf("unexpected error: %v", err.Error())
}
for i, a := range expected.Resources() {
b := actual.GetByIndex(i)
if b == nil {
t.Fatalf("resource %d missing from processed map", i)
}
if !a.Equals(b) {
t.Errorf("expected %v got %v", a, b)
}
if a.NeedHashSuffix() != b.NeedHashSuffix() {
t.Errorf("")
}
if a.Behavior() != b.Behavior() {
t.Errorf("expected %v got %v", a.Behavior(), b.Behavior())
}
}
}
func TestUpdateResourceOptionsWithInvalidHashAnnotationValues(t *testing.T) {
p := NewExecPlugin("")
if err := p.ErrIfNotExecutable(); err == nil {
t.Fatalf("expected unexecutable error")
}
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
cases := []string{
"",
"FaLsE",
"TrUe",
"potato",
}
for i, c := range cases {
name := fmt.Sprintf("test%d", i)
in := resmap.New()
in.Append(makeConfigMap(rf, name, "", &c))
_, err := p.UpdateResourceOptions(in)
if err == nil {
t.Errorf("expected error from value %q", c)
}
}
}

View File

@@ -1,197 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fnplugin
import (
"bytes"
"fmt"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/internal/plugins/utils"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/runfn"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// FnPlugin is the struct to hold function information
type FnPlugin struct {
// Function runner
runFns runfn.RunFns
// Plugin configuration data.
cfg []byte
// Plugin name cache for error output
pluginName string
// PluginHelpers
h *resmap.PluginHelpers
}
func bytesToRNode(yml []byte) (*yaml.RNode, error) {
rnode, err := yaml.Parse(string(yml))
if err != nil {
return nil, err
}
return rnode, nil
}
func resourceToRNode(res *resource.Resource) (*yaml.RNode, error) {
yml, err := res.AsYAML()
if err != nil {
return nil, err
}
return bytesToRNode(yml)
}
// GetFunctionSpec return function spec is there is. Otherwise return nil
func GetFunctionSpec(res *resource.Resource) *runtimeutil.FunctionSpec {
rnode, err := resourceToRNode(res)
if err != nil {
return nil
}
return runtimeutil.GetFunctionSpec(rnode)
}
func toStorageMounts(mounts []string) []runtimeutil.StorageMount {
var sms []runtimeutil.StorageMount
for _, mount := range mounts {
sms = append(sms, runtimeutil.StringToStorageMount(mount))
}
return sms
}
// NewFnPlugin creates a FnPlugin struct
func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin {
return &FnPlugin{
runFns: runfn.RunFns{
Functions: []*yaml.RNode{},
Network: o.Network,
NetworkName: o.NetworkName,
EnableStarlark: o.EnableStar,
EnableExec: o.EnableExec,
StorageMounts: toStorageMounts(o.Mounts),
},
}
}
// Cfg returns function config
func (p *FnPlugin) Cfg() []byte {
return p.cfg
}
// Config is called by kustomize to pass-in config information
func (p *FnPlugin) Config(h *resmap.PluginHelpers, config []byte) error {
p.h = h
p.cfg = config
fn, err := bytesToRNode(p.cfg)
if err != nil {
return err
}
meta, err := fn.GetMeta()
if err != nil {
return err
}
p.pluginName = fmt.Sprintf("api: %s, kind: %s, name: %s",
meta.APIVersion, meta.Kind, meta.Name)
return nil
}
// Generate is called when run as generator
func (p *FnPlugin) Generate() (resmap.ResMap, error) {
output, err := p.invokePlugin(nil)
if err != nil {
return nil, err
}
rm, err := p.h.ResmapFactory().NewResMapFromBytes(output)
if err != nil {
return nil, err
}
return utils.UpdateResourceOptions(rm)
}
// Transform is called when run as transformer
func (p *FnPlugin) Transform(rm resmap.ResMap) error {
// add ResIds as annotations to all objects so that we can add them back
inputRM, err := utils.GetResMapWithIDAnnotation(rm)
if err != nil {
return err
}
// encode the ResMap so it can be fed to the plugin
resources, err := inputRM.AsYaml()
if err != nil {
return err
}
// invoke the plugin with resources as the input
output, err := p.invokePlugin(resources)
if err != nil {
return fmt.Errorf("%v %s", err, string(output))
}
// update the original ResMap based on the output
return utils.UpdateResMapValues(p.pluginName, p.h, output, rm)
}
func injectAnnotation(input *yaml.RNode, k, v string) error {
err := input.PipeE(yaml.SetAnnotation(k, v))
if err != nil {
return err
}
return nil
}
// invokePlugin uses Function runner to run function as plugin
func (p *FnPlugin) invokePlugin(input []byte) ([]byte, error) {
// get function config rnode
functionConfig, err := bytesToRNode(p.cfg)
if err != nil {
return nil, err
}
// This annotation will let kustomize ingnore this item in output
err = injectAnnotation(functionConfig, "config.kubernetes.io/local-config", "true")
if err != nil {
return nil, err
}
// we need to add config as input for generators. Some of them don't work with FunctionConfig
// and in addition kio.Pipeline won't create anything if there are no objects
// see https://github.com/kubernetes-sigs/kustomize/blob/master/kyaml/kio/kio.go#L93
// Since we added `local-config` annotation so it will be ignored in generator output
// TODO(donnyxia): This is actually not used by generator and only used to bypass a kio limitation.
// Need better solution.
if input == nil {
yaml, err := functionConfig.String()
if err != nil {
return nil, err
}
input = []byte(yaml)
}
// Configure and Execute Fn. We don't need to convert resources to ResourceList here
// because function runtime will do that. See kyaml/fn/runtime/runtimeutil/runtimeutil.go
var ouputBuffer bytes.Buffer
p.runFns.Input = bytes.NewReader(input)
p.runFns.Functions = append(p.runFns.Functions, functionConfig)
p.runFns.Output = &ouputBuffer
err = p.runFns.Execute()
if err != nil {
return nil, errors.Wrap(
err, "couldn't execute function")
}
return ouputBuffer.Bytes(), nil
}

View File

@@ -16,7 +16,6 @@ import (
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers" "sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/internal/plugins/execplugin" "sigs.k8s.io/kustomize/api/internal/plugins/execplugin"
"sigs.k8s.io/kustomize/api/internal/plugins/fnplugin"
"sigs.k8s.io/kustomize/api/internal/plugins/utils" "sigs.k8s.io/kustomize/api/internal/plugins/utils"
"sigs.k8s.io/kustomize/api/konfig" "sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
@@ -117,7 +116,7 @@ func (l *Loader) loadAndConfigurePlugin(
if isBuiltinPlugin(res) { if isBuiltinPlugin(res) {
switch l.pc.BpLoadingOptions { switch l.pc.BpLoadingOptions {
case types.BploLoadFromFileSys: case types.BploLoadFromFileSys:
c, err = l.loadPlugin(res) c, err = l.loadPlugin(res.OrgId())
case types.BploUseStaticallyLinked: case types.BploUseStaticallyLinked:
// Instead of looking for and loading a .so file, // Instead of looking for and loading a .so file,
// instantiate the plugin from a generated factory // instantiate the plugin from a generated factory
@@ -132,7 +131,7 @@ func (l *Loader) loadAndConfigurePlugin(
} else { } else {
switch l.pc.PluginRestrictions { switch l.pc.PluginRestrictions {
case types.PluginRestrictionsNone: case types.PluginRestrictionsNone:
c, err = l.loadPlugin(res) c, err = l.loadPlugin(res.OrgId())
case types.PluginRestrictionsBuiltinsOnly: case types.PluginRestrictionsBuiltinsOnly:
err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind) err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind)
default: default:
@@ -167,15 +166,7 @@ func (l *Loader) makeBuiltinPlugin(r resid.Gvk) (resmap.Configurable, error) {
return nil, errors.Errorf("unable to load builtin %s", r) return nil, errors.Errorf("unable to load builtin %s", r)
} }
func (l *Loader) loadPlugin(res *resource.Resource) (resmap.Configurable, error) { func (l *Loader) loadPlugin(resId resid.ResId) (resmap.Configurable, error) {
spec := fnplugin.GetFunctionSpec(res)
if spec != nil {
return fnplugin.NewFnPlugin(&l.pc.FnpLoadingOptions), nil
}
return l.loadExecOrGoPlugin(res.OrgId())
}
func (l *Loader) loadExecOrGoPlugin(resId resid.ResId) (resmap.Configurable, error) {
// First try to load the plugin as an executable. // First try to load the plugin as an executable.
p := execplugin.NewExecPlugin(l.absolutePluginPath(resId)) p := execplugin.NewExecPlugin(l.absolutePluginPath(resId))
err := p.ErrIfNotExecutable() err := p.ErrIfNotExecutable()

View File

@@ -4,25 +4,13 @@
package utils package utils
import ( import (
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"time" "time"
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/konfig" "sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
const (
idAnnotation = "kustomize.config.k8s.io/id"
HashAnnotation = "kustomize.config.k8s.io/needs-hash"
BehaviorAnnotation = "kustomize.config.k8s.io/behavior"
) )
func GoBin() string { func GoBin() string {
@@ -125,91 +113,3 @@ func FileExists(path string) bool {
} }
return true return true
} }
// GetResMapWithIDAnnotation returns a new copy of the given ResMap with the ResIds annotated in each Resource
func GetResMapWithIDAnnotation(rm resmap.ResMap) (resmap.ResMap, error) {
inputRM := rm.DeepCopy()
for _, r := range inputRM.Resources() {
idString, err := yaml.Marshal(r.CurId())
if err != nil {
return nil, err
}
annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
annotations[idAnnotation] = string(idString)
r.SetAnnotations(annotations)
}
return inputRM, nil
}
// UpdateResMapValues updates the Resource value in the given ResMap
// with the emitted Resource values in output.
func UpdateResMapValues(pluginName string, h *resmap.PluginHelpers, output []byte, rm resmap.ResMap) error {
outputRM, err := h.ResmapFactory().NewResMapFromBytes(output)
if err != nil {
return err
}
for _, r := range outputRM.Resources() {
// for each emitted Resource, find the matching Resource in the original ResMap
// using its id
annotations := r.GetAnnotations()
idString, ok := annotations[idAnnotation]
if !ok {
return fmt.Errorf("the transformer %s should not remove annotation %s",
pluginName, idAnnotation)
}
id := resid.ResId{}
err := yaml.Unmarshal([]byte(idString), &id)
if err != nil {
return err
}
res, err := rm.GetByCurrentId(id)
if err != nil {
return fmt.Errorf("unable to find unique match to %s", id.String())
}
// remove the annotation set by Kustomize to track the resource
delete(annotations, idAnnotation)
if len(annotations) == 0 {
annotations = nil
}
r.SetAnnotations(annotations)
// update the resource value with the transformed object
res.ResetPrimaryData(r)
}
return nil
}
// UpdateResourceOptions updates the generator options for each resource in the
// given ResMap based on plugin provided annotations.
func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {
for _, r := range rm.Resources() {
// Disable name hashing by default and require plugin to explicitly
// request it for each resource.
annotations := r.GetAnnotations()
behavior := annotations[BehaviorAnnotation]
var needsHash bool
if val, ok := annotations[HashAnnotation]; ok {
b, err := strconv.ParseBool(val)
if err != nil {
return nil, fmt.Errorf(
"the annotation %q contains an invalid value (%q)",
HashAnnotation, val)
}
needsHash = b
}
delete(annotations, HashAnnotation)
delete(annotations, BehaviorAnnotation)
if len(annotations) == 0 {
annotations = nil
}
r.SetAnnotations(annotations)
r.SetOptions(types.NewGenArgs(
&types.GeneratorArgs{
Behavior: behavior,
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
}
return rm, nil
}

View File

@@ -4,17 +4,12 @@
package utils package utils
import ( import (
"fmt"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/konfig" "sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
) )
func TestDeterminePluginSrcRoot(t *testing.T) { func TestDeterminePluginSrcRoot(t *testing.T) {
@@ -29,99 +24,3 @@ func TestDeterminePluginSrcRoot(t *testing.T) {
t.Errorf("expected suffix '%s' in '%s'", konfig.RelPluginHome, actual) t.Errorf("expected suffix '%s' in '%s'", konfig.RelPluginHome, actual)
} }
} }
func makeConfigMap(rf *resource.Factory, name, behavior string, hashValue *string) *resource.Resource {
r := rf.FromMap(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{"name": name},
})
annotations := map[string]string{}
if behavior != "" {
annotations[BehaviorAnnotation] = behavior
}
if hashValue != nil {
annotations[HashAnnotation] = *hashValue
}
if len(annotations) > 0 {
r.SetAnnotations(annotations)
}
return r
}
func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHash bool) *resource.Resource {
return rf.FromMapAndOption(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{"name": name},
}, &types.GeneratorArgs{
Behavior: behavior,
Options: &types.GeneratorOptions{DisableNameSuffixHash: disableHash}})
}
func strptr(s string) *string {
return &s
}
func TestUpdateResourceOptions(t *testing.T) {
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
in := resmap.New()
expected := resmap.New()
cases := []struct {
behavior string
needsHash bool
hashValue *string
}{
{hashValue: strptr("false")},
{hashValue: strptr("true"), needsHash: true},
{behavior: "replace"},
{behavior: "merge"},
{behavior: "create"},
{behavior: "nonsense"},
{behavior: "merge", hashValue: strptr("false")},
{behavior: "merge", hashValue: strptr("true"), needsHash: true},
}
for i, c := range cases {
name := fmt.Sprintf("test%d", i)
in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue))
expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash))
}
actual, err := UpdateResourceOptions(in)
if err != nil {
t.Fatalf("unexpected error: %v", err.Error())
}
for i, a := range expected.Resources() {
b := actual.GetByIndex(i)
if b == nil {
t.Fatalf("resource %d missing from processed map", i)
}
if !a.Equals(b) {
t.Errorf("expected %v got %v", a, b)
}
if a.NeedHashSuffix() != b.NeedHashSuffix() {
t.Errorf("")
}
if a.Behavior() != b.Behavior() {
t.Errorf("expected %v got %v", a.Behavior(), b.Behavior())
}
}
}
func TestUpdateResourceOptionsWithInvalidHashAnnotationValues(t *testing.T) {
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
cases := []string{
"",
"FaLsE",
"TrUe",
"potato",
}
for i, c := range cases {
name := fmt.Sprintf("test%d", i)
in := resmap.New()
in.Append(makeConfigMap(rf, name, "", &c))
_, err := UpdateResourceOptions(in)
if err == nil {
t.Errorf("expected error from value %q", c)
}
}
}

View File

@@ -4,6 +4,7 @@
package target package target
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings" "strings"
@@ -59,7 +60,7 @@ func (kt *KustTarget) Load() error {
return err return err
} }
var k types.Kustomization var k types.Kustomization
err = k.Unmarshal(content) err = unmarshal(content, &k)
if err != nil { if err != nil {
return err return err
} }
@@ -103,8 +104,18 @@ func loadKustFile(ldr ifc.Loader) ([]byte, error) {
} }
} }
func unmarshal(y []byte, o interface{}) error {
j, err := yaml.YAMLToJSON(y)
if err != nil {
return err
}
dec := json.NewDecoder(bytes.NewReader(j))
dec.DisallowUnknownFields()
return dec.Decode(o)
}
// MakeCustomizedResMap creates a fully customized ResMap // MakeCustomizedResMap creates a fully customized ResMap
// per the instructions contained in its kustomization instance. // per the instructions contained in its kustomiztion instance.
func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) { func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
return kt.makeCustomizedResMap() return kt.makeCustomizedResMap()
} }
@@ -198,10 +209,6 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = kt.runValidators(ra)
if err != nil {
return nil, err
}
err = ra.MergeVars(kt.kustomization.Vars) err = ra.MergeVars(kt.kustomization.Vars)
if err != nil { if err != nil {
return nil, errors.Wrapf( return nil, errors.Wrapf(
@@ -253,7 +260,7 @@ func (kt *KustTarget) runTransformers(ra *accumulator.ResAccumulator) error {
return err return err
} }
r = append(r, lts...) r = append(r, lts...)
lts, err = kt.configureExternalTransformers(kt.kustomization.Transformers) lts, err = kt.configureExternalTransformers()
if err != nil { if err != nil {
return err return err
} }
@@ -262,54 +269,15 @@ func (kt *KustTarget) runTransformers(ra *accumulator.ResAccumulator) error {
return ra.Transform(t) return ra.Transform(t)
} }
func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]resmap.Transformer, error) { func (kt *KustTarget) configureExternalTransformers() ([]resmap.Transformer, error) {
ra := accumulator.MakeEmptyAccumulator() ra := accumulator.MakeEmptyAccumulator()
ra, err := kt.accumulateResources(ra, transformers) ra, err := kt.accumulateResources(ra, kt.kustomization.Transformers)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return kt.pLdr.LoadTransformers(kt.ldr, kt.validator, ra.ResMap()) return kt.pLdr.LoadTransformers(kt.ldr, kt.validator, ra.ResMap())
} }
func (kt *KustTarget) runValidators(ra *accumulator.ResAccumulator) error {
validators, err := kt.configureExternalTransformers(kt.kustomization.Validators)
if err != nil {
return err
}
for _, v := range validators {
// Validators shouldn't modify the resource map
orignal := ra.ResMap().DeepCopy()
err = v.Transform(ra.ResMap())
if err != nil {
return err
}
new := ra.ResMap().DeepCopy()
kt.removeValidatedByLabel(new)
if err = orignal.ErrorIfNotEqualSets(new); err != nil {
return fmt.Errorf("validator shouldn't modify the resource map: %v", err)
}
}
return nil
}
func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) {
resources := rm.Resources()
for _, r := range resources {
labels := r.GetLabels()
if _, found := labels[konfig.ValidatedByLabelKey]; !found {
continue
}
delete(labels, konfig.ValidatedByLabelKey)
if len(labels) == 0 {
r.SetLabels(nil)
} else {
r.SetLabels(labels)
}
}
}
// accumulateResources fills the given resourceAccumulator // accumulateResources fills the given resourceAccumulator
// with resources read from the given list of paths. // with resources read from the given list of paths.
func (kt *KustTarget) accumulateResources( func (kt *KustTarget) accumulateResources(

View File

@@ -252,10 +252,6 @@ nameReference:
kind: Role kind: Role
- path: rules/resourceNames - path: rules/resourceNames
kind: ClusterRole kind: ClusterRole
- path: spec/template/spec/containers/env/valueFrom/secretKeyRef/name
kind: Service
group: serving.knative.dev
version: v1
- kind: Service - kind: Service
version: v1 version: v1

View File

@@ -36,7 +36,4 @@ const (
// An environment variable to turn on/off adding the ManagedByLabelKey // An environment variable to turn on/off adding the ManagedByLabelKey
EnableManagedbyLabelEnv = "KUSTOMIZE_ENABLE_MANAGEDBY_LABEL" EnableManagedbyLabelEnv = "KUSTOMIZE_ENABLE_MANAGEDBY_LABEL"
// Label key that indicates the resources are validated by a validator
ValidatedByLabelKey = "validated-by"
) )

View File

@@ -225,13 +225,13 @@ spec:
spec: spec:
containers: containers:
- env: - env:
- name: foo
value: bar
- name: FOO - name: FOO
valueFrom: valueFrom:
configMapKeyRef: configMapKeyRef:
key: somekey key: somekey
name: test-infra-app-env-ffmd9b969m name: test-infra-app-env-ffmd9b969m
- name: foo
value: bar
image: nginx:1.8.0 image: nginx:1.8.0
name: nginx name: nginx
ports: ports:
@@ -292,7 +292,8 @@ metadata:
--- ---
apiVersion: v1 apiVersion: v1
data: data:
nonsense: "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit, sed do eiusmod tempor\nincididunt ut labore et dolore magna aliqua. \n" nonsense: "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit, sed do eiusmod
tempor\nincididunt ut labore et dolore magna aliqua. \n"
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations: annotations:

View File

@@ -132,54 +132,6 @@ spec:
`) `)
} }
func TestTinyOverlay(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
namePrefix: a-
resources:
- deployment.yaml
`)
th.WriteF("base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
th.WriteK("overlay", `
namePrefix: b-
resources:
- ../base
patchesStrategicMerge:
- depPatch.yaml
`)
th.WriteF("overlay/depPatch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
replicas: 999
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: b-a-myDeployment
spec:
replicas: 999
template:
spec:
containers:
- image: whatever
`)
}
func writeSmallBase(th kusttest_test.Harness) { func writeSmallBase(th kusttest_test.Harness) {
th.WriteK("/app/base", ` th.WriteK("/app/base", `
namePrefix: a- namePrefix: a-

View File

@@ -39,8 +39,8 @@ resources:
configMapGenerator: configMapGenerator:
- name: my-configmap - name: my-configmap
literals: literals:
- testValue=1 - testValue=1
- otherValue=10 - otherValue=10
`) `)
th.WriteF("/app/base/deploy.yaml", ` th.WriteF("/app/base/deploy.yaml", `
apiVersion: v1 apiVersion: v1
@@ -59,13 +59,13 @@ replicas:
- name: storefront - name: storefront
count: 3 count: 3
resources: resources:
- stub.yaml - stub.yaml
configMapGenerator: configMapGenerator:
- name: my-configmap - name: my-configmap
behavior: merge behavior: merge
literals: literals:
- testValue=2 - testValue=2
- compValue=5 - compValue=5
`) `)
th.WriteF("/app/comp/stub.yaml", ` th.WriteF("/app/comp/stub.yaml", `
apiVersion: v1 apiVersion: v1
@@ -156,7 +156,7 @@ configMapGenerator:
- name: my-configmap - name: my-configmap
behavior: merge behavior: merge
literals: literals:
- otherValue=9 - otherValue=9
`), `),
writeK("/app/prod", ` writeK("/app/prod", `
resources: resources:
@@ -211,8 +211,8 @@ components:
configMapGenerator: configMapGenerator:
- name: my-configmap - name: my-configmap
behavior: merge behavior: merge
literals: literals:
- otherValue=9 - otherValue=9
`), `),
writeK("/app/prod", ` writeK("/app/prod", `
resources: resources:
@@ -327,8 +327,8 @@ configMapGenerator:
- name: my-configmap - name: my-configmap
behavior: merge behavior: merge
literals: literals:
- compValue=5 - compValue=5
- testValue=2 - testValue=2
`), `),
}, },
runPath: "/app/direct-component", runPath: "/app/direct-component",
@@ -360,7 +360,7 @@ configMapGenerator:
- name: my-configmap - name: my-configmap
behavior: merge behavior: merge
literals: literals:
- otherValue=9 - otherValue=9
`), `),
}, },
runPath: "/app/prod", runPath: "/app/prod",
@@ -574,7 +574,7 @@ configMapGenerator:
- name: my-configmap - name: my-configmap
behavior: merge behavior: merge
literals: literals:
- otherValue=9 - otherValue=9
`), `),
}, },
runPath: "/app/prod", runPath: "/app/prod",

View File

@@ -85,7 +85,8 @@ metadata:
--- ---
apiVersion: v1 apiVersion: v1
data: data:
druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize": 32000000000, "freeSpacePercent": 1.0}]' druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize":
32000000000, "freeSpacePercent": 1.0}]'
v2: '[{"path": "var/druid/segment-cache"}]' v2: '[{"path": "var/druid/segment-cache"}]'
kind: ConfigMap kind: ConfigMap
metadata: metadata:

View File

@@ -59,7 +59,6 @@ spec:
location: SW location: SW
`) `)
th.WriteF("/app/base/animalPark.yaml", ` th.WriteF("/app/base/animalPark.yaml", `
apiVersion: foo
kind: AnimalPark kind: AnimalPark
metadata: metadata:
name: sandiego name: sandiego
@@ -94,7 +93,6 @@ varReference:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: foo
kind: AnimalPark kind: AnimalPark
metadata: metadata:
labels: labels:
@@ -163,7 +161,6 @@ varReference:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: foo
kind: AnimalPark kind: AnimalPark
metadata: metadata:
labels: labels:
@@ -215,17 +212,14 @@ func TestFixedBug605_BaseCustomizationAvailableInOverlay(t *testing.T) {
nameReference: nameReference:
- kind: Gorilla - kind: Gorilla
fieldSpecs: fieldSpecs:
- apiVersion: foo - kind: AnimalPark
kind: AnimalPark
path: spec/gorillaRef/name path: spec/gorillaRef/name
- kind: Giraffe - kind: Giraffe
fieldSpecs: fieldSpecs:
- apiVersion: foo - kind: AnimalPark
kind: AnimalPark
path: spec/giraffeRef/name path: spec/giraffeRef/name
varReference: varReference:
- path: spec/food - path: spec/food
apiVersion: foo
kind: AnimalPark kind: AnimalPark
`) `)
th.WriteK("/app/overlay", ` th.WriteK("/app/overlay", `
@@ -248,7 +242,6 @@ spec:
`) `)
// The following replaces the gorillaRef in the AnimalPark. // The following replaces the gorillaRef in the AnimalPark.
th.WriteF("/app/overlay/animalPark.yaml", ` th.WriteF("/app/overlay/animalPark.yaml", `
apiVersion: foo
kind: AnimalPark kind: AnimalPark
metadata: metadata:
name: sandiego name: sandiego
@@ -258,7 +251,6 @@ spec:
`) `)
m := th.Run("/app/overlay", th.MakeDefaultOptions()) m := th.Run("/app/overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: foo
kind: AnimalPark kind: AnimalPark
metadata: metadata:
labels: labels:

View File

@@ -11,7 +11,7 @@ import (
func makeCommonFileForExtendedPatchTest(th kusttest_test.Harness) { func makeCommonFileForExtendedPatchTest(th kusttest_test.Harness) {
th.WriteF("/app/base/deployment.yaml", ` th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -36,7 +36,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -101,7 +101,7 @@ patches:
name: busybox name: busybox
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -110,7 +110,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -135,7 +135,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -156,7 +156,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: busybox-persistent-storage name: busybox-persistent-storage
volumes: volumes:
- name: busybox-persistent-storage - emptyDir: {}
name: busybox-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
@@ -202,7 +203,7 @@ patches:
kind: Deployment kind: Deployment
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -211,7 +212,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -232,12 +233,13 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: nginx-persistent-storage name: nginx-persistent-storage
volumes: volumes:
- name: nginx-persistent-storage - emptyDir: {}
name: nginx-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -258,7 +260,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: busybox-persistent-storage name: busybox-persistent-storage
volumes: volumes:
- name: busybox-persistent-storage - emptyDir: {}
name: busybox-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
@@ -302,7 +305,7 @@ patches:
labelSelector: app=nginx labelSelector: app=nginx
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -311,7 +314,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -332,12 +335,13 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: nginx-persistent-storage name: nginx-persistent-storage
volumes: volumes:
- name: nginx-persistent-storage - emptyDir: {}
name: nginx-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -404,7 +408,7 @@ patches:
kind: Deployment kind: Deployment
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -413,7 +417,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -438,7 +442,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -459,7 +463,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: busybox-persistent-storage name: busybox-persistent-storage
volumes: volumes:
- name: busybox-persistent-storage - emptyDir: {}
name: busybox-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
@@ -504,7 +509,7 @@ patches:
labelSelector: app=busybox labelSelector: app=busybox
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -513,7 +518,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -538,7 +543,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -559,7 +564,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: busybox-persistent-storage name: busybox-persistent-storage
volumes: volumes:
- name: busybox-persistent-storage - emptyDir: {}
name: busybox-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
@@ -606,7 +612,7 @@ patches:
labelSelector: app=busybox labelSelector: app=busybox
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -615,7 +621,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -640,7 +646,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -661,7 +667,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: busybox-persistent-storage name: busybox-persistent-storage
volumes: volumes:
- name: busybox-persistent-storage - emptyDir: {}
name: busybox-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
@@ -707,7 +714,7 @@ patches:
labelSelector: app=busybox labelSelector: app=busybox
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -716,7 +723,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -741,7 +748,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -762,7 +769,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: busybox-persistent-storage name: busybox-persistent-storage
volumes: volumes:
- name: busybox-persistent-storage - emptyDir: {}
name: busybox-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
@@ -806,7 +814,7 @@ patches:
name: no-match name: no-match
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -815,7 +823,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -840,7 +848,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -902,7 +910,7 @@ patches:
- path: patch.yaml - path: patch.yaml
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -911,7 +919,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -936,7 +944,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -957,7 +965,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: busybox-persistent-storage name: busybox-persistent-storage
volumes: volumes:
- name: busybox-persistent-storage - emptyDir: {}
name: busybox-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
@@ -1005,7 +1014,7 @@ patches:
kind: Job kind: Job
`) `)
th.WriteF("/app/base/patch.yaml", ` th.WriteF("/app/base/patch.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -1014,7 +1023,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -1039,7 +1048,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -1107,7 +1116,7 @@ patches:
kind: Deployment kind: Deployment
`) `)
th.WriteF("/app/base/patch1.yaml", ` th.WriteF("/app/base/patch1.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -1115,7 +1124,7 @@ metadata:
new-key-from-patch1: new-value new-key-from-patch1: new-value
`) `)
th.WriteF("/app/base/patch2.yaml", ` th.WriteF("/app/base/patch2.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: busybox name: busybox
@@ -1124,7 +1133,7 @@ metadata:
`) `)
m := th.Run("/app/base", th.MakeDefaultOptions()) m := th.Run("/app/base", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
labels: labels:
@@ -1149,7 +1158,7 @@ spec:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -1171,7 +1180,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: busybox-persistent-storage name: busybox-persistent-storage
volumes: volumes:
- name: busybox-persistent-storage - emptyDir: {}
name: busybox-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base

View File

@@ -1,466 +0,0 @@
package krusty_test
import (
"os/exec"
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestFnExecGenerator(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK("/app", `
resources:
- short_secret.yaml
generators:
- gener.yaml
`)
// Create some additional resource just to make sure everything is added
th.WriteF("/app/short_secret.yaml", `
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-user-data: "true"
name: node1-bmc-secret
type: Opaque
stringData:
userData: |
bootcmd:
- mkdir /mnt/vda
`)
th.WriteF("/app/gener.yaml", `
kind: executable
metadata:
name: demo
annotations:
config.kubernetes.io/function: |
exec:
path: ./fnplugin_test/fnexectest.sh
spec:
`)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true
m := th.Run("/app", o)
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-user-data: "true"
name: node1-bmc-secret
stringData:
userData: |
bootcmd:
- mkdir /mnt/vda
type: Opaque
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
config.kubernetes.io/path: deployment_nginx.yaml
tshirt-size: small
labels:
app: nginx
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
`)
}
func skipIfNoDocker(t *testing.T) {
if _, err := exec.LookPath("docker"); err != nil {
t.Skip("skipping because docker binary wasn't found in PATH")
}
}
func TestFnContainerGenerator(t *testing.T) {
skipIfNoDocker(t)
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK("/app", `
resources:
- short_secret.yaml
generators:
- gener.yaml
`)
// Create generator config
th.WriteF("/app/gener.yaml", `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: CockroachDB
metadata:
name: demo
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/kustomize-functions/example-cockroachdb:v0.1.0
spec:
replicas: 3
`)
// Create some additional resource just to make sure everything is added
th.WriteF("/app/short_secret.yaml", `
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-user-data: "true"
name: node1-bmc-secret
type: Opaque
stringData:
userData: |
bootcmd:
- mkdir /mnt/vda
`)
m := th.Run("/app", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-user-data: "true"
name: node1-bmc-secret
stringData:
userData: |
bootcmd:
- mkdir /mnt/vda
type: Opaque
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
annotations:
config.kubernetes.io/path: config/demo-budget_poddisruptionbudget.yaml
labels:
app: cockroachdb
name: demo
name: demo-budget
spec:
minAvailable: 67%
selector:
matchLabels:
app: cockroachdb
name: demo
---
apiVersion: v1
kind: Service
metadata:
annotations:
config.kubernetes.io/path: config/demo-public_service.yaml
labels:
app: cockroachdb
name: demo
name: demo-public
spec:
ports:
- name: grpc
port: 26257
targetPort: 26257
- name: http
port: 8080
targetPort: 8080
selector:
app: cockroachdb
name: demo
---
apiVersion: v1
kind: Service
metadata:
annotations:
config.kubernetes.io/path: config/demo_service.yaml
prometheus.io/path: _status/vars
prometheus.io/port: "8080"
prometheus.io/scrape: "true"
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
labels:
app: cockroachdb
name: demo
name: demo
spec:
clusterIP: None
ports:
- name: grpc
port: 26257
targetPort: 26257
- name: http
port: 8080
targetPort: 8080
selector:
app: cockroachdb
name: demo
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
config.kubernetes.io/path: config/demo_statefulset.yaml
labels:
app: cockroachdb
name: demo
name: demo
spec:
replicas: 3
selector:
matchLabels:
app: cockroachdb
name: demo
serviceName: demo
template:
metadata:
labels:
app: cockroachdb
name: demo
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- cockroachdb
topologyKey: kubernetes.io/hostname
weight: 100
containers:
- command:
- /bin/bash
- -ecx
- |
# The use of qualified `+"`hostname -f`"+` is crucial:
# Other nodes aren't able to look up the unqualified hostname.
CRARGS=("start" "--logtostderr" "--insecure" "--host" "$(hostname -f)" "--http-host" "0.0.0.0")
# We only want to initialize a new cluster (by omitting the join flag)
# if we're sure that we're the first node (i.e. index 0) and that
# there aren't any other nodes running as part of the cluster that
# this is supposed to be a part of (which indicates that a cluster
# already exists and we should make sure not to create a new one).
# It's fine to run without --join on a restart if there aren't any
# other nodes.
if [ ! "$(hostname)" == "cockroachdb-0" ] || [ -e "/cockroach/cockroach-data/cluster_exists_marker" ]
then
# We don't join cockroachdb in order to avoid a node attempting
# to join itself, which currently doesn't work
# (https://github.com/cockroachdb/cockroach/issues/9625).
CRARGS+=("--join" "cockroachdb-public")
fi
exec /cockroach/cockroach ${CRARGS[*]}
image: cockroachdb/cockroach:v1.1.0
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 26257
name: grpc
- containerPort: 8080
name: http
volumeMounts:
- mountPath: /cockroach/cockroach-data
name: datadir
initContainers:
- args:
- -on-start=/on-start.sh
- -service=cockroachdb
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: cockroachdb/cockroach-k8s-init:0.1
imagePullPolicy: IfNotPresent
name: bootstrap
volumeMounts:
- mountPath: /cockroach/cockroach-data
name: datadir
terminationGracePeriodSeconds: 60
volumes:
- name: datadir
persistentVolumeClaim:
claimName: datadir
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
`)
}
func TestFnContainerTransformer(t *testing.T) {
skipIfNoDocker(t)
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK("/app", `
resources:
- data.yaml
transformers:
- transf1.yaml
- transf2.yaml
`)
th.WriteF("/app/data.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
annotations:
tshirt-size: small # this injects the resource reservations
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
`)
// This transformer should add resource reservations based on annotation in data.yaml
// See https://github.com/kubernetes-sigs/kustomize/tree/master/functions/examples/injection-tshirt-sizes
th.WriteF("/app/transf1.yaml", `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: Validator
metadata:
name: valid
annotations:
config.kubernetes.io/function: |-
container:
image: gcr.io/kustomize-functions/example-tshirt:v0.2.0
`)
// This transformer will check resources without and won't do any changes
// See https://github.com/kubernetes-sigs/kustomize/tree/master/functions/examples/validator-kubeval
th.WriteF("/app/transf2.yaml", `
apiVersion: examples.config.kubernetes.io/v1beta1
kind: Kubeval
metadata:
name: validate
annotations:
config.kubernetes.io/function: |
container:
image: gcr.io/kustomize-functions/example-validator-kubeval:v0.1.0
spec:
strict: true
ignoreMissingSchemas: true
# 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
kubernetesVersion: "1.16.0"
schemaLocation: "file:///schemas"
`)
m := th.Run("/app", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
config.kubernetes.io/path: deployment_nginx.yaml
tshirt-size: small
labels:
app: nginx
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
resources:
requests:
cpu: 200m
memory: 50M
`)
}
func TestFnContainerTransformerWithConfig(t *testing.T) {
skipIfNoDocker(t)
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK("/app", `
resources:
- data1.yaml
- data2.yaml
transformers:
- label_namespace.yaml
`)
th.WriteF("/app/data1.yaml", `apiVersion: v1
kind: Namespace
metadata:
name: my-namespace
`)
th.WriteF("/app/data2.yaml", `apiVersion: v1
kind: Namespace
metadata:
name: another-namespace
`)
th.WriteF("/app/label_namespace.yaml", `apiVersion: v1
kind: ConfigMap
metadata:
name: label_namespace
annotations:
config.kubernetes.io/function: |-
container:
image: gcr.io/kpt-functions/label-namespace@sha256:4f030738d6d25a207641ca517916431517578bd0eb8d98a8bde04e3bb9315dcd
data:
label_name: my-ns-name
label_value: function-test
`)
m := th.Run("/app", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Namespace
metadata:
annotations:
config.kubernetes.io/path: namespace_my-namespace.yaml
labels:
my-ns-name: function-test
name: my-namespace
---
apiVersion: v1
kind: Namespace
metadata:
annotations:
config.kubernetes.io/path: namespace_another-namespace.yaml
labels:
my-ns-name: function-test
name: another-namespace
`)
}

View File

@@ -1,24 +0,0 @@
#!/bin/sh
cat <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
annotations:
tshirt-size: small # this injects the resource reservations
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
EOF

View File

@@ -56,7 +56,7 @@ spec:
app: nginx app: nginx
`) `)
th.WriteF("/app/base/deployment.yaml", ` th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -92,7 +92,7 @@ spec:
org: example.com org: example.com
team: foo team: foo
--- ---
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -150,6 +150,8 @@ spec:
func makeBaseWithGenerators(th kusttest_test.Harness) { func makeBaseWithGenerators(th kusttest_test.Harness) {
th.WriteK("/app", ` th.WriteK("/app", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: team-foo- namePrefix: team-foo-
commonLabels: commonLabels:
app: mynginx app: mynginx
@@ -171,7 +173,7 @@ secretGenerator:
- password=somepw - password=somepw
`) `)
th.WriteF("/app/deployment.yaml", ` th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -216,7 +218,7 @@ func TestBaseWithGeneratorsAlone(t *testing.T) {
makeBaseWithGenerators(th) makeBaseWithGenerators(th)
m := th.Run("/app", th.MakeDefaultOptions()) m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -306,7 +308,7 @@ func TestMergeAndReplaceGenerators(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
makeBaseWithGenerators(th) makeBaseWithGenerators(th)
th.WriteF("/overlay/deployment.yaml", ` th.WriteF("/overlay/deployment.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -323,6 +325,8 @@ spec:
name: configmap-in-overlay name: configmap-in-overlay
`) `)
th.WriteK("/overlay", ` th.WriteK("/overlay", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: staging- namePrefix: staging-
commonLabels: commonLabels:
env: staging env: staging
@@ -347,7 +351,7 @@ secretGenerator:
`) `)
m := th.Run("/overlay", th.MakeDefaultOptions()) m := th.Run("/overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -385,12 +389,12 @@ spec:
- gcePersistentDisk: - gcePersistentDisk:
pdName: nginx-persistent-storage pdName: nginx-persistent-storage
name: nginx-persistent-storage name: nginx-persistent-storage
- configMap:
name: staging-team-foo-configmap-in-base-gh9d7t85gb
name: configmap-in-base
- configMap: - configMap:
name: staging-configmap-in-overlay-k7cbc75tg8 name: staging-configmap-in-overlay-k7cbc75tg8
name: configmap-in-overlay name: configmap-in-overlay
- configMap:
name: staging-team-foo-configmap-in-base-gh9d7t85gb
name: configmap-in-base
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service

View File

@@ -79,7 +79,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: nginx-persistent-storage name: nginx-persistent-storage
volumes: volumes:
- name: nginx-persistent-storage - emptyDir: {}
name: nginx-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base
@@ -222,7 +223,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: nginx-persistent-storage name: nginx-persistent-storage
volumes: volumes:
- name: nginx-persistent-storage - emptyDir: {}
name: nginx-persistent-storage
- configMap: - configMap:
name: configmap-in-base name: configmap-in-base
name: configmap-in-base name: configmap-in-base

View File

@@ -1,48 +0,0 @@
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestKeepEmptyArray(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("/app/resources.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: testing123
spec:
replicas: 1
selector: null
template:
spec:
containers:
- name: event
image: testing123
imagePullPolicy: IfNotPresent
imagePullSecrets: []`)
th.WriteK("/app", `
resources:
- resources.yaml`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: testing123
spec:
replicas: 1
selector: null
template:
spec:
containers:
- image: testing123
imagePullPolicy: IfNotPresent
name: event
imagePullSecrets: []
`)
}

View File

@@ -10,186 +10,10 @@ import (
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
) )
func TestSimpleMultiplePatches(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
namePrefix: b-
commonLabels:
team: foo
resources:
- deployment.yaml
- service.yaml
configMapGenerator:
- name: configmap-in-base
literals:
- foo=bar
`)
th.WriteF("base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: nginx-persistent-storage
mountPath: /tmp/ps
- name: sidecar
image: sidecar:latest
volumes:
- name: nginx-persistent-storage
emptyDir: {}
- configMap:
name: configmap-in-base
name: configmap-in-base
`)
th.WriteF("base/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
ports:
- port: 80
`)
th.WriteK("overlay", `
namePrefix: a-
commonLabels:
env: staging
patchesStrategicMerge:
- deployment-patch1.yaml
- deployment-patch2.yaml
resources:
- ../base
configMapGenerator:
- name: configmap-in-overlay
literals:
- hello=world
`)
th.WriteF("overlay/deployment-patch1.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: nginx:latest
env:
- name: ENVKEY
value: ENVVALUE
volumes:
- name: nginx-persistent-storage
emptyDir: null
gcePersistentDisk:
pdName: nginx-persistent-storage
- configMap:
name: configmap-in-overlay
name: configmap-in-overlay
`)
th.WriteF("overlay/deployment-patch2.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
env:
- name: ANOTHERENV
value: FOO
volumes:
- name: nginx-persistent-storage
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
env: staging
team: foo
name: a-b-nginx
spec:
selector:
matchLabels:
env: staging
team: foo
template:
metadata:
labels:
env: staging
team: foo
spec:
containers:
- env:
- name: ANOTHERENV
value: FOO
- name: ENVKEY
value: ENVVALUE
image: nginx:latest
name: nginx
volumeMounts:
- mountPath: /tmp/ps
name: nginx-persistent-storage
- image: sidecar:latest
name: sidecar
volumes:
- gcePersistentDisk:
pdName: nginx-persistent-storage
name: nginx-persistent-storage
- configMap:
name: a-b-configmap-in-base-fm96mhk4dt
name: configmap-in-base
- configMap:
name: a-configmap-in-overlay-ffm9hf78mc
name: configmap-in-overlay
---
apiVersion: v1
kind: Service
metadata:
labels:
env: staging
team: foo
name: a-b-nginx
spec:
ports:
- port: 80
selector:
env: staging
team: foo
---
apiVersion: v1
data:
foo: bar
kind: ConfigMap
metadata:
labels:
env: staging
team: foo
name: a-b-configmap-in-base-fm96mhk4dt
---
apiVersion: v1
data:
hello: world
kind: ConfigMap
metadata:
labels:
env: staging
name: a-configmap-in-overlay-ffm9hf78mc
`)
}
func makeCommonFileForMultiplePatchTest(th kusttest_test.Harness) { func makeCommonFileForMultiplePatchTest(th kusttest_test.Harness) {
th.WriteK("/app/base", ` th.WriteK("/app/base", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: team-foo- namePrefix: team-foo-
commonLabels: commonLabels:
app: mynginx app: mynginx
@@ -206,7 +30,7 @@ configMapGenerator:
- foo=bar - foo=bar
`) `)
th.WriteF("/app/base/deployment.yaml", ` th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -247,6 +71,8 @@ spec:
app: nginx app: nginx
`) `)
th.WriteK("/app/overlay/staging", ` th.WriteK("/app/overlay/staging", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: staging- namePrefix: staging-
commonLabels: commonLabels:
env: staging env: staging
@@ -266,7 +92,7 @@ func TestMultiplePatchesNoConflict(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
makeCommonFileForMultiplePatchTest(th) makeCommonFileForMultiplePatchTest(th)
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", ` th.WriteF("/app/overlay/staging/deployment-patch1.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -289,7 +115,7 @@ spec:
name: configmap-in-overlay name: configmap-in-overlay
`) `)
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", ` th.WriteF("/app/overlay/staging/deployment-patch2.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -306,7 +132,7 @@ spec:
`) `)
m := th.Run("/app/overlay/staging", th.MakeDefaultOptions()) m := th.Run("/app/overlay/staging", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -351,12 +177,12 @@ spec:
- gcePersistentDisk: - gcePersistentDisk:
pdName: nginx-persistent-storage pdName: nginx-persistent-storage
name: nginx-persistent-storage name: nginx-persistent-storage
- configMap:
name: staging-team-foo-configmap-in-base-g7k6gt2889
name: configmap-in-base
- configMap: - configMap:
name: staging-configmap-in-overlay-k7cbc75tg8 name: staging-configmap-in-overlay-k7cbc75tg8
name: configmap-in-overlay name: configmap-in-overlay
- configMap:
name: staging-team-foo-configmap-in-base-g7k6gt2889
name: configmap-in-base
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
@@ -407,7 +233,7 @@ func TestMultiplePatchesWithConflict(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
makeCommonFileForMultiplePatchTest(th) makeCommonFileForMultiplePatchTest(th)
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", ` th.WriteF("/app/overlay/staging/deployment-patch1.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -429,7 +255,7 @@ spec:
name: configmap-in-overlay name: configmap-in-overlay
`) `)
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", ` th.WriteF("/app/overlay/staging/deployment-patch2.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -453,7 +279,7 @@ spec:
} }
func TestMultiplePatchesWithOnePatchDeleteDirective(t *testing.T) { func TestMultiplePatchesWithOnePatchDeleteDirective(t *testing.T) {
additivePatch := `apiVersion: apps/v1 additivePatch := `apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -466,7 +292,7 @@ spec:
- name: SOME_NAME - name: SOME_NAME
value: somevalue value: somevalue
` `
deletePatch := `apiVersion: apps/v1 deletePatch := `apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -502,7 +328,7 @@ spec:
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", c.patch1) th.WriteF("/app/overlay/staging/deployment-patch1.yaml", c.patch1)
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", c.patch2) th.WriteF("/app/overlay/staging/deployment-patch2.yaml", c.patch2)
m := th.Run("/app/overlay/staging", th.MakeDefaultOptions()) m := th.Run("/app/overlay/staging", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1 th.AssertActualEqualsExpected(m, `apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
@@ -540,7 +366,8 @@ spec:
- mountPath: /tmp/ps - mountPath: /tmp/ps
name: nginx-persistent-storage name: nginx-persistent-storage
volumes: volumes:
- name: nginx-persistent-storage - emptyDir: {}
name: nginx-persistent-storage
- configMap: - configMap:
name: staging-team-foo-configmap-in-base-g7k6gt2889 name: staging-team-foo-configmap-in-base-g7k6gt2889
name: configmap-in-base name: configmap-in-base
@@ -596,7 +423,7 @@ func TestMultiplePatchesBothWithPatchDeleteDirective(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
makeCommonFileForMultiplePatchTest(th) makeCommonFileForMultiplePatchTest(th)
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", ` th.WriteF("/app/overlay/staging/deployment-patch1.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx
@@ -608,7 +435,7 @@ spec:
name: sidecar name: sidecar
`) `)
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", ` th.WriteF("/app/overlay/staging/deployment-patch2.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1beta2
kind: Deployment kind: Deployment
metadata: metadata:
name: nginx name: nginx

View File

@@ -10,6 +10,24 @@ import (
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
func writeDeployment(th *kusttest_test.HarnessEnhanced, path string) {
th.WriteF(path, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
metadata:
labels:
backend: awesome
spec:
containers:
- name: whatever
image: whatever
`)
}
func writeStringPrefixer(th *kusttest_test.HarnessEnhanced, path, name string) { func writeStringPrefixer(th *kusttest_test.HarnessEnhanced, path, name string) {
th.WriteF(path, ` th.WriteF(path, `
apiVersion: someteam.example.com/v1 apiVersion: someteam.example.com/v1
@@ -19,6 +37,53 @@ metadata:
`) `)
} }
func writeDatePrefixer(th *kusttest_test.HarnessEnhanced, path, name string) {
th.WriteF(path, `
apiVersion: someteam.example.com/v1
kind: DatePrefixer
metadata:
name: `+name+`
`)
}
func TestOrderedTransformers(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
BuildGoPlugin("someteam.example.com", "v1", "StringPrefixer").
BuildGoPlugin("someteam.example.com", "v1", "DatePrefixer")
defer th.Reset()
th.WriteK("/app", `
resources:
- deployment.yaml
transformers:
- peachPrefixer.yaml
- date1Prefixer.yaml
- applePrefixer.yaml
- date2Prefixer.yaml
`)
writeDeployment(th, "/app/deployment.yaml")
writeStringPrefixer(th, "/app/applePrefixer.yaml", "apple")
writeStringPrefixer(th, "/app/peachPrefixer.yaml", "peach")
writeDatePrefixer(th, "/app/date1Prefixer.yaml", "date1")
writeDatePrefixer(th, "/app/date2Prefixer.yaml", "date2")
m := th.Run("/app", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: 2018-05-11-apple-2018-05-11-peach-myDeployment
spec:
template:
metadata:
labels:
backend: awesome
spec:
containers:
- image: whatever
name: whatever
`)
}
func TestPluginsNotEnabled(t *testing.T) { func TestPluginsNotEnabled(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t). th := kusttest_test.MakeEnhancedHarness(t).
BuildGoPlugin("someteam.example.com", "v1", "StringPrefixer") BuildGoPlugin("someteam.example.com", "v1", "StringPrefixer")
@@ -100,78 +165,6 @@ metadata:
`) `)
} }
/*
The tests below are disabled until the StringPrefixer and DatePrefixer
can be rewritten using kyaml, instead of depending on the
PrefixSuffixTransformerPlugin. That dependency is causing
failures in the test loader.
func writeDeployment(th *kusttest_test.HarnessEnhanced, path string) {
th.WriteF(path, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
metadata:
labels:
backend: awesome
spec:
containers:
- name: whatever
image: whatever
`)
}
func writeDatePrefixer(th *kusttest_test.HarnessEnhanced, path, name string) {
th.WriteF(path, `
apiVersion: someteam.example.com/v1
kind: DatePrefixer
metadata:
name: `+name+`
`)
}
func TestOrderedTransformers(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t).
BuildGoPlugin("someteam.example.com", "v1", "StringPrefixer").
BuildGoPlugin("someteam.example.com", "v1", "DatePrefixer")
defer th.Reset()
th.WriteK("/app", `
resources:
- deployment.yaml
transformers:
- peachPrefixer.yaml
- date1Prefixer.yaml
- applePrefixer.yaml
- date2Prefixer.yaml
`)
writeDeployment(th, "/app/deployment.yaml")
writeStringPrefixer(th, "/app/applePrefixer.yaml", "apple")
writeStringPrefixer(th, "/app/peachPrefixer.yaml", "peach")
writeDatePrefixer(th, "/app/date1Prefixer.yaml", "date1")
writeDatePrefixer(th, "/app/date2Prefixer.yaml", "date2")
m := th.Run("/app", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: 2018-05-11-apple-2018-05-11-peach-myDeployment
spec:
template:
metadata:
labels:
backend: awesome
spec:
containers:
- image: whatever
name: whatever
`)
}
func TestTransformedTransformers(t *testing.T) { func TestTransformedTransformers(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t). th := kusttest_test.MakeEnhancedHarness(t).
BuildGoPlugin("someteam.example.com", "v1", "StringPrefixer"). BuildGoPlugin("someteam.example.com", "v1", "StringPrefixer").
@@ -212,4 +205,3 @@ spec:
name: whatever name: whatever
`) `)
} }
*/

View File

@@ -844,7 +844,8 @@ spec:
- -namespace=${POD_NAMESPACE} - -namespace=${POD_NAMESPACE}
- -certs-dir=/cockroach-certs - -certs-dir=/cockroach-certs
- -type=node - -type=node
- -addresses=localhost,127.0.0.1,${POD_IP},$(hostname -f),$(hostname -f|cut -f 1-2 -d '.'),dev-base-cockroachdb-public - -addresses=localhost,127.0.0.1,${POD_IP},$(hostname -f),$(hostname -f|cut
-f 1-2 -d '.'),dev-base-cockroachdb-public
- -symlink-ca-from=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt - -symlink-ca-from=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
env: env:
- name: POD_IP - name: POD_IP

View File

@@ -69,10 +69,10 @@ func (rf *Factory) makeOne(
o = types.NewGenArgs(nil) o = types.NewGenArgs(nil)
} }
r := &Resource{ r := &Resource{
kunStr: u, Kunstructured: u,
options: o, options: o,
} }
return r.setOriginalName(r.kunStr.GetName()).setOriginalNs(r.GetNamespace()) return r.setOriginalName(r.GetName()).setOriginalNs(r.GetNamespace())
} }
// SliceFromPatches returns a slice of resources given a patch path // SliceFromPatches returns a slice of resources given a patch path

View File

@@ -13,178 +13,6 @@ import (
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
func TestSliceFromBytes(t *testing.T) {
type testCase struct {
input string
expected []string
}
testCases := map[string]testCase{
"empty1": {
input: "",
expected: []string{},
},
"empty2": {
input: `
---
---
`,
expected: []string{},
},
"deployment1": {
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: pooh
---
`,
expected: []string{
`apiVersion: apps/v1
kind: Deployment
metadata:
name: pooh
`,
},
},
"deployment2": {
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
baseAnno: This is a base annotation
labels:
app: mungebot
foo: bar
name: baseprefix-mungebot
spec:
replicas: 1
selector:
matchLabels:
foo: bar
template:
metadata:
annotations:
baseAnno: This is a base annotation
labels:
app: mungebot
foo: bar
spec:
containers:
- env:
- name: foo
value: bar
image: nginx
name: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
annotations:
baseAnno: This is a base annotation
labels:
app: mungebot
foo: bar
name: baseprefix-mungebot-service
spec:
ports:
- port: 7002
selector:
app: mungebot
foo: bar
`,
expected: []string{
`apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
baseAnno: This is a base annotation
labels:
app: mungebot
foo: bar
name: baseprefix-mungebot
spec:
replicas: 1
selector:
matchLabels:
foo: bar
template:
metadata:
annotations:
baseAnno: This is a base annotation
labels:
app: mungebot
foo: bar
spec:
containers:
- env:
- name: foo
value: bar
image: nginx
name: nginx
ports:
- containerPort: 80
`,
`apiVersion: v1
kind: Service
metadata:
annotations:
baseAnno: This is a base annotation
labels:
app: mungebot
foo: bar
name: baseprefix-mungebot-service
spec:
ports:
- port: 7002
selector:
app: mungebot
foo: bar
`,
},
},
}
for name := range testCases {
tc := testCases[name]
t.Run(name, func(t *testing.T) {
result, err := factory.SliceFromBytes([]byte(tc.input))
if err != nil {
t.Fatalf("%v: fails with err: %v", name, err)
}
if len(result) != len(tc.expected) {
for i := range result {
bytes, err := result[i].AsYAML()
if err != nil {
t.Fatalf("%v: result to YAML fails with err: %v", name, err)
}
tmp := string(bytes)
t.Logf("--- %d:\n%s", i, tmp)
}
t.Fatalf(
"%v: actual len %d != expected len %d",
name, len(result), len(tc.expected))
}
for i := range tc.expected {
bytes, err := result[i].AsYAML()
if err != nil {
t.Fatalf("%v: result to YAML fails with err: %v", name, err)
}
tmp := string(bytes)
if tmp != tc.expected[i] {
t.Fatalf(
"%v: string mismatch in item %d\n"+
"actual:\n-----\n%s\n-----\n"+
"expected:\n-----\n%s\n-----\n",
name, i, tmp, tc.expected[i])
}
}
})
}
}
func TestSliceFromPatches(t *testing.T) { func TestSliceFromPatches(t *testing.T) {
patchGood1 := types.PatchStrategicMerge("patch1.yaml") patchGood1 := types.PatchStrategicMerge("patch1.yaml")
patch1 := ` patch1 := `

View File

@@ -1,7 +1,7 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package resource implements representations of k8s API resources. // Package resource implements representations of k8s API resources as "unstructured" objects.
package resource package resource
import ( import (
@@ -14,57 +14,10 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
// Resource is a representation of a Kubernetes Resource Model object paired // Resource is map representation of a Kubernetes API resource object
// with metadata used by kustomize. // paired with a GenerationBehavior.
//
// At time of writing Resource is changing from being based on an object
// sitting behind
//
// sigs.k8s.io/kustomize/api/ifc.Unstructured
//
// to being based on an instance of
//
// sigs.k8s.io/kustomize/kyaml/yaml.RNode
//
// Ultimately, use of the Resource struct in kustomize should be entirely
// replaced by instances of RNode for direct use in kyaml, with all metadata
// stored directly in the RNodes as short-lived annotations, to be deleted
// before final output as part of a final filtration step. Using annotations
// will allow pipelined KRM Config Functions to share metadata that would
// otherwise be hidden in kustomize-only structures.
//
// ifc.Unstructured is an interface hiding an instance of
// sigs.k8s.io/kustomize/api/k8sdeps/kunstruct.UnstructAdapter
// which in turn adapts an instance of
// k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
// to the ifc.Unstructured interface. This latter code handles
// mutations in the old code.
//
// The rule in kustomize development has been
// api/
// k8sdeps/ ifc/ krusty/ theRest/
//
// 1) Depend on k8s.io/ only via sigs.k8s.io/kustomize/api/k8sdeps.
//
// 2) Instances created in k8sdeps/ can only be injected into theRest/
// behind interfaces defined in ifc/, arranged by a small bit of code
// in krusty/. Nothing in theRest/ can import k8sdeps/.
//
// This was to allow for importing kustomize code into kubectl, which also
// depends on k8s.io/apimachinery, albeit via a strange, old arrangement
// predating Go modules. The idea was to copy the k8sdeps/ tree into kubectl
// via a large PR, and depend on the rest via vendoring (and a small copy of
// krusty/ code). Over 2019, however, kubectl underwent large code changes
// including a switch to Go modules, and an attempt to extract it from the
// k8s repo, and this made large kustomize integration PRs hard to get through
// code review.
//
// The new plan is to eliminate k8sdeps/ entirely, along with its k8s.io/
// dependence, switch to kyaml, and thus allow kustomize to be imported into
// kubectl via normal Go module imports.
//
type Resource struct { type Resource struct {
kunStr ifc.Kunstructured ifc.Kunstructured
originalName string originalName string
originalNs string originalNs string
options *types.GenArgs options *types.GenArgs
@@ -74,118 +27,6 @@ type Resource struct {
nameSuffixes []string nameSuffixes []string
} }
func (r *Resource) ResetPrimaryData(incoming *Resource) {
r.kunStr = incoming.kunStr.Copy()
}
func (r *Resource) GetAnnotations() map[string]string {
return r.kunStr.GetAnnotations()
}
func (r *Resource) Copy() ifc.Kunstructured {
return r.kunStr.Copy()
}
func (r *Resource) GetBool(p string) (bool, error) {
return r.kunStr.GetBool(p)
}
func (r *Resource) GetFieldValue(f string) (interface{}, error) {
return r.kunStr.GetFieldValue(f)
}
func (r *Resource) GetFloat64(p string) (float64, error) {
return r.kunStr.GetFloat64(p)
}
func (r *Resource) GetGvk() resid.Gvk {
return r.kunStr.GetGvk()
}
func (r *Resource) GetInt64(p string) (int64, error) {
return r.kunStr.GetInt64(p)
}
func (r *Resource) GetKind() string {
return r.kunStr.GetKind()
}
func (r *Resource) GetLabels() map[string]string {
return r.kunStr.GetLabels()
}
func (r *Resource) GetMap(p string) (map[string]interface{}, error) {
return r.kunStr.GetMap(p)
}
func (r *Resource) GetName() string {
return r.kunStr.GetName()
}
func (r *Resource) GetSlice(p string) ([]interface{}, error) {
return r.kunStr.GetSlice(p)
}
func (r *Resource) GetString(p string) (string, error) {
return r.kunStr.GetString(p)
}
func (r *Resource) GetStringMap(p string) (map[string]string, error) {
return r.kunStr.GetStringMap(p)
}
func (r *Resource) GetStringSlice(p string) ([]string, error) {
return r.kunStr.GetStringSlice(p)
}
func (r *Resource) Map() map[string]interface{} {
return r.kunStr.Map()
}
func (r *Resource) MarshalJSON() ([]byte, error) {
return r.kunStr.MarshalJSON()
}
func (r *Resource) MatchesLabelSelector(selector string) (bool, error) {
return r.kunStr.MatchesLabelSelector(selector)
}
func (r *Resource) MatchesAnnotationSelector(selector string) (bool, error) {
return r.kunStr.MatchesAnnotationSelector(selector)
}
func (r *Resource) Patch(other ifc.Kunstructured) error {
return r.kunStr.Patch(other)
}
func (r *Resource) SetAnnotations(m map[string]string) {
r.kunStr.SetAnnotations(m)
}
func (r *Resource) SetGvk(gvk resid.Gvk) {
r.kunStr.SetGvk(gvk)
}
func (r *Resource) SetLabels(m map[string]string) {
r.kunStr.SetLabels(m)
}
func (r *Resource) SetMap(m map[string]interface{}) {
r.kunStr.SetMap(m)
}
func (r *Resource) SetName(n string) {
r.kunStr.SetName(n)
}
func (r *Resource) SetNamespace(n string) {
r.kunStr.SetNamespace(n)
}
func (r *Resource) UnmarshalJSON(s []byte) error {
return r.kunStr.UnmarshalJSON(s)
}
// ResCtx is an interface describing the contextual added // ResCtx is an interface describing the contextual added
// kept kustomize in the context of each Resource object. // kept kustomize in the context of each Resource object.
// Currently mainly the name prefix and name suffix are added. // Currently mainly the name prefix and name suffix are added.
@@ -205,7 +46,7 @@ type ResCtxMatcher func(ResCtx) bool
// DeepCopy returns a new copy of resource // DeepCopy returns a new copy of resource
func (r *Resource) DeepCopy() *Resource { func (r *Resource) DeepCopy() *Resource {
rc := &Resource{ rc := &Resource{
kunStr: r.kunStr.Copy(), Kunstructured: r.Kunstructured.Copy(),
} }
rc.copyOtherFields(r) rc.copyOtherFields(r)
return rc return rc
@@ -213,11 +54,11 @@ func (r *Resource) DeepCopy() *Resource {
// Replace performs replace with other resource. // Replace performs replace with other resource.
func (r *Resource) Replace(other *Resource) { func (r *Resource) Replace(other *Resource) {
r.kunStr.SetLabels(mergeStringMaps(other.kunStr.GetLabels(), r.kunStr.GetLabels())) r.SetLabels(mergeStringMaps(other.GetLabels(), r.GetLabels()))
r.kunStr.SetAnnotations( r.SetAnnotations(
mergeStringMaps(other.kunStr.GetAnnotations(), r.kunStr.GetAnnotations())) mergeStringMaps(other.GetAnnotations(), r.GetAnnotations()))
r.kunStr.SetName(other.GetName()) r.SetName(other.GetName())
r.kunStr.SetNamespace(other.GetNamespace()) r.SetNamespace(other.GetNamespace())
r.copyOtherFields(other) r.copyOtherFields(other)
} }
@@ -233,7 +74,7 @@ func (r *Resource) copyOtherFields(other *Resource) {
func (r *Resource) Equals(o *Resource) bool { func (r *Resource) Equals(o *Resource) bool {
return r.ReferencesEqual(o) && return r.ReferencesEqual(o) &&
reflect.DeepEqual(r.kunStr, o.kunStr) reflect.DeepEqual(r.Kunstructured, o.Kunstructured)
} }
func (r *Resource) ReferencesEqual(o *Resource) bool { func (r *Resource) ReferencesEqual(o *Resource) bool {
@@ -252,13 +93,13 @@ func (r *Resource) ReferencesEqual(o *Resource) bool {
} }
func (r *Resource) KunstructEqual(o *Resource) bool { func (r *Resource) KunstructEqual(o *Resource) bool {
return reflect.DeepEqual(r.kunStr, o.kunStr) return reflect.DeepEqual(r.Kunstructured, o.Kunstructured)
} }
// Merge performs merge with other resource. // Merge performs merge with other resource.
func (r *Resource) Merge(other *Resource) { func (r *Resource) Merge(other *Resource) {
r.Replace(other) r.Replace(other)
mergeConfigmap(r.kunStr.Map(), other.Map(), r.Map()) mergeConfigmap(r.Map(), other.Map(), r.Map())
} }
func (r *Resource) copyRefBy() []resid.ResId { func (r *Resource) copyRefBy() []resid.ResId {
@@ -384,7 +225,7 @@ func (r *Resource) setOriginalNs(n string) *Resource {
// String returns resource as JSON. // String returns resource as JSON.
func (r *Resource) String() string { func (r *Resource) String() string {
bs, err := r.kunStr.MarshalJSON() bs, err := r.MarshalJSON()
if err != nil { if err != nil {
return "<" + err.Error() + ">" return "<" + err.Error() + ">"
} }
@@ -394,7 +235,7 @@ func (r *Resource) String() string {
// AsYAML returns the resource in Yaml form. // AsYAML returns the resource in Yaml form.
// Easier to read than JSON. // Easier to read than JSON.
func (r *Resource) AsYAML() ([]byte, error) { func (r *Resource) AsYAML() ([]byte, error) {
json, err := r.kunStr.MarshalJSON() json, err := r.MarshalJSON()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -419,7 +260,7 @@ func (r *Resource) NeedHashSuffix() bool {
// GetNamespace returns the namespace the resource thinks it's in. // GetNamespace returns the namespace the resource thinks it's in.
func (r *Resource) GetNamespace() string { func (r *Resource) GetNamespace() string {
namespace, _ := r.kunStr.GetString("metadata.namespace") namespace, _ := r.GetString("metadata.namespace")
// if err, namespace is empty, so no need to check. // if err, namespace is empty, so no need to check.
return namespace return namespace
} }
@@ -429,7 +270,7 @@ func (r *Resource) GetNamespace() string {
// TODO: compute this once and save it in the resource. // TODO: compute this once and save it in the resource.
func (r *Resource) OrgId() resid.ResId { func (r *Resource) OrgId() resid.ResId {
return resid.NewResIdWithNamespace( return resid.NewResIdWithNamespace(
r.kunStr.GetGvk(), r.GetOriginalName(), r.GetOriginalNs()) r.GetGvk(), r.GetOriginalName(), r.GetOriginalNs())
} }
// CurId returns a ResId for the resource using the // CurId returns a ResId for the resource using the
@@ -437,7 +278,7 @@ func (r *Resource) OrgId() resid.ResId {
// This should be unique in any ResMap. // This should be unique in any ResMap.
func (r *Resource) CurId() resid.ResId { func (r *Resource) CurId() resid.ResId {
return resid.NewResIdWithNamespace( return resid.NewResIdWithNamespace(
r.kunStr.GetGvk(), r.kunStr.GetName(), r.GetNamespace()) r.GetGvk(), r.GetName(), r.GetNamespace())
} }
// GetRefBy returns the ResIds that referred to current resource // GetRefBy returns the ResIds that referred to current resource

View File

@@ -4,6 +4,9 @@
package kusttest_test package kusttest_test
import ( import (
"bytes"
"fmt"
"strconv"
"testing" "testing"
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
@@ -17,6 +20,8 @@ import (
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest" valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
) )
// HarnessEnhanced manages a full plugin environment for tests. // HarnessEnhanced manages a full plugin environment for tests.
@@ -125,8 +130,42 @@ func (th *HarnessEnhanced) LoadAndRunTransformer(
func (th *HarnessEnhanced) RunTransformerAndCheckResult( func (th *HarnessEnhanced) RunTransformerAndCheckResult(
config, input, expected string) { config, input, expected string) {
resMap := th.LoadAndRunTransformer(config, input) for _, b := range []bool{true, false} {
th.AssertActualEqualsExpected(resMap, expected) 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( func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer(
@@ -139,8 +178,16 @@ type AssertFunc func(t *testing.T, err error)
func (th *HarnessEnhanced) RunTransformerAndCheckError( func (th *HarnessEnhanced) RunTransformerAndCheckError(
config, input string, assertFn AssertFunc) { config, input string, assertFn AssertFunc) {
_, err := th.RunTransformer(config, input) for _, b := range []bool{true, false} {
assertFn(th.t, err) 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( func (th *HarnessEnhanced) RunTransformer(
@@ -156,7 +203,6 @@ func (th *HarnessEnhanced) RunTransformerFromResMap(
config string, resMap resmap.ResMap) (resmap.ResMap, error) { config string, resMap resmap.ResMap) (resmap.ResMap, error) {
transConfig, err := th.rf.RF().FromBytes([]byte(config)) transConfig, err := th.rf.RF().FromBytes([]byte(config))
if err != nil { if err != nil {
th.t.Logf("config: '%s'", config)
th.t.Fatalf("Err: %v", err) th.t.Fatalf("Err: %v", err)
} }
g, err := th.pl.LoadTransformer( g, err := th.pl.LoadTransformer(

View File

@@ -3,13 +3,6 @@
package types package types
import (
"bytes"
"encoding/json"
"sigs.k8s.io/yaml"
)
const ( const (
KustomizationVersion = "kustomize.config.k8s.io/v1beta1" KustomizationVersion = "kustomize.config.k8s.io/v1beta1"
KustomizationKind = "Kustomization" KustomizationKind = "Kustomization"
@@ -131,9 +124,6 @@ type Kustomization struct {
// Transformers is a list of files containing transformers // Transformers is a list of files containing transformers
Transformers []string `json:"transformers,omitempty" yaml:"transformers,omitempty"` Transformers []string `json:"transformers,omitempty" yaml:"transformers,omitempty"`
// Validators is a list of files containing validators
Validators []string `json:"validators,omitempty" yaml:"validators,omitempty"`
// Inventory appends an object that contains the record // Inventory appends an object that contains the record
// of all other objects, which can be used in apply, prune and delete // of all other objects, which can be used in apply, prune and delete
Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"` Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"`
@@ -172,20 +162,3 @@ func (k *Kustomization) EnforceFields() []string {
} }
return errs return errs
} }
// Unmarshal replace k with the content in YAML input y
func (k *Kustomization) Unmarshal(y []byte) error {
j, err := yaml.YAMLToJSON(y)
if err != nil {
return err
}
dec := json.NewDecoder(bytes.NewReader(j))
dec.DisallowUnknownFields()
var nk Kustomization
err = dec.Decode(&nk)
if err != nil {
return err
}
*k = nk
return nil
}

View File

@@ -1,184 +0,0 @@
package types
import (
"testing"
)
func fixKustomizationPostUnmarshallingCheck(k, e *Kustomization) bool {
return (k.Kind == e.Kind && k.APIVersion == e.APIVersion &&
len(k.Resources) == len(e.Resources) && k.Resources[0] == e.Resources[0] &&
k.Bases == nil)
}
func TestFixKustomizationPostUnmarshalling(t *testing.T) {
var k Kustomization
k.Bases = append(k.Bases, "foo")
k.FixKustomizationPostUnmarshalling()
expected := Kustomization{
TypeMeta: TypeMeta{
Kind: KustomizationKind,
APIVersion: KustomizationVersion,
},
Resources: []string{"foo"},
}
if !fixKustomizationPostUnmarshallingCheck(&k, &expected) {
t.Fatalf("unexpected output: %v", k)
}
}
func TestFixKustomizationPostUnmarshalling_2(t *testing.T) {
k := Kustomization{
TypeMeta: TypeMeta{
Kind: ComponentKind,
},
}
k.Bases = append(k.Bases, "foo")
k.FixKustomizationPostUnmarshalling()
expected := Kustomization{
TypeMeta: TypeMeta{
Kind: ComponentKind,
APIVersion: ComponentVersion,
},
Resources: []string{"foo"},
}
if !fixKustomizationPostUnmarshallingCheck(&k, &expected) {
t.Fatalf("unexpected output: %v", k)
}
}
func TestEnforceFields_InvalidKindAndVersion(t *testing.T) {
k := Kustomization{
TypeMeta: TypeMeta{
Kind: "foo",
APIVersion: "bar",
},
}
errs := k.EnforceFields()
if len(errs) != 2 {
t.Fatalf("number of errors should be 2 but got: %v", errs)
}
}
func TestEnforceFields_InvalidKind(t *testing.T) {
k := Kustomization{
TypeMeta: TypeMeta{
Kind: "foo",
APIVersion: KustomizationVersion,
},
}
errs := k.EnforceFields()
if len(errs) != 1 {
t.Fatalf("number of errors should be 1 but got: %v", errs)
}
expected := "kind should be " + KustomizationKind + " or " + ComponentKind
if errs[0] != expected {
t.Fatalf("error should be %v but got: %v", expected, errs[0])
}
}
func TestEnforceFields_InvalidVersion(t *testing.T) {
k := Kustomization{
TypeMeta: TypeMeta{
Kind: KustomizationKind,
APIVersion: "bar",
},
}
errs := k.EnforceFields()
if len(errs) != 1 {
t.Fatalf("number of errors should be 1 but got: %v", errs)
}
expected := "apiVersion for " + k.Kind + " should be " + KustomizationVersion
if errs[0] != expected {
t.Fatalf("error should be %v but got: %v", expected, errs[0])
}
}
func TestEnforceFields_ComponentKind(t *testing.T) {
k := Kustomization{
TypeMeta: TypeMeta{
Kind: ComponentKind,
APIVersion: "bar",
},
}
errs := k.EnforceFields()
if len(errs) != 1 {
t.Fatalf("number of errors should be 1 but got: %v", errs)
}
expected := "apiVersion for " + k.Kind + " should be " + ComponentVersion
if errs[0] != expected {
t.Fatalf("error should be %v but got: %v", expected, errs[0])
}
}
func TestEnforceFields(t *testing.T) {
k := Kustomization{
TypeMeta: TypeMeta{
Kind: KustomizationKind,
APIVersion: KustomizationVersion,
},
}
errs := k.EnforceFields()
if len(errs) != 0 {
t.Fatalf("number of errors should be 0 but got: %v", errs)
}
}
func TestUnmarshal(t *testing.T) {
y := []byte(`
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- foo
- bar
nameSuffix: dog
namePrefix: cat`)
var k Kustomization
err := k.Unmarshal(y)
if err != nil {
t.Fatal(err)
}
if k.Kind != KustomizationKind || k.APIVersion != KustomizationVersion ||
len(k.Resources) != 2 || k.NamePrefix != "cat" || k.NameSuffix != "dog" {
t.Fatalf("wrong unmarshal result: %v", k)
}
}
func TestUnmarshal_UnkownField(t *testing.T) {
y := []byte(`
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
unknown: foo`)
var k Kustomization
err := k.Unmarshal(y)
if err == nil {
t.Fatalf("expect an error")
}
expect := "json: unknown field \"unknown\""
if err.Error() != expect {
t.Fatalf("expect %v but got: %v", expect, err.Error())
}
}
func TestUnmarshal_InvalidYaml(t *testing.T) {
y := []byte(`
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
unknown`)
var k Kustomization
err := k.Unmarshal(y)
if err == nil {
t.Fatalf("expect an error")
}
}

View File

@@ -29,7 +29,4 @@ type PluginConfig struct {
// BpLoadingOptions distinguishes builtin plugin behaviors. // BpLoadingOptions distinguishes builtin plugin behaviors.
BpLoadingOptions BuiltinPluginLoadingOptions BpLoadingOptions BuiltinPluginLoadingOptions
// FnpLoadingOptions sets the way function-based plugin behaviors.
FnpLoadingOptions FnPluginLoadingOptions
} }

View File

@@ -41,16 +41,3 @@ const (
// to generate static code. // to generate static code.
BploLoadFromFileSys BploLoadFromFileSys
) )
// FnPluginLoadingOptions set way functions-based pluing are restricted
type FnPluginLoadingOptions struct {
// Allow to run executables
EnableExec bool
// Allow to run starlark
EnableStar bool
// Allow container access to network
Network bool
NetworkName string
// list of mounts
Mounts []string
}

View File

@@ -19,7 +19,6 @@ var (
Count = commands.CountCommand Count = commands.CountCommand
CreateSetter = commands.CreateSetterCommand CreateSetter = commands.CreateSetterCommand
CreateSubstitution = commands.CreateSubstitutionCommand CreateSubstitution = commands.CreateSubstitutionCommand
DeleteSetter = commands.DeleteSetterCommand
Fmt = commands.FmtCommand Fmt = commands.FmtCommand
Grep = commands.GrepCommand Grep = commands.GrepCommand
Init = commands.InitCommand Init = commands.InitCommand

View File

@@ -116,14 +116,14 @@ metadata:
name: my-instance name: my-instance
annotations: annotations:
config.kubernetes.io/local-config: "true" config.kubernetes.io/local-config: "true"
config.kubernetes.io/function: | config.k8s.io/function: |
container: container:
image: gcr.io/example-functions/nginx-template:v1.0.0 image: gcr.io/example-functions/nginx-template:v1.0.0
spec: spec:
replicas: 5 replicas: 5
``` ```
- `annotations[config.kubernetes.io/function].container.image`: the image to use for this API - `annotations[config.k8s.io/function].container.image`: the image to use for this API
- `annotations[config.kubernetes.io/local-config]`: mark this as not a Resource that should - `annotations[config.kubernetes.io/local-config]`: mark this as not a Resource that should
be applied be applied

View File

@@ -7,7 +7,7 @@ containers that can be chained together as part of a configuration management pi
The end result of such a pipeline are fully rendered configurations that can then be The end result of such a pipeline are fully rendered configurations that can then be
applied to a control plane (e.g. Using kubectl apply for Kubernetes control plane). applied to a control plane (e.g. Using kubectl apply for Kubernetes control plane).
As such, although this document references Kubernetes Resource Model and API conventions, As such, although this document references Kubernetes Resource Model and API conventions,
it is completely decoupled from Kubernetes API machinery and does not depend on any it is completely decoupled from Kuberentes API machinery and does not depend on any
in-cluster components. in-cluster components.
This document references terms described in [Kubernetes API Conventions][1]. This document references terms described in [Kubernetes API Conventions][1].
@@ -33,7 +33,7 @@ _Configuration functions_ enable shift-left practices (client-side) through:
Performing these on the client rather than the server enables: Performing these on the client rather than the server enables:
- Configuration to be reviewed prior to being sent to the API server - Configuration to be reviewed prior to being sent to the API server
- Configuration to be validated as part of the CI/CD pipeline - Configuration to be validated as part of the CI?CD pipeline
- Configuration for Resources to validated holistically rather than individually - Configuration for Resources to validated holistically rather than individually
per-Resource per-Resource
- e.g. ensure the `Service.selector` and `Deployment.spec.template` labels - e.g. ensure the `Service.selector` and `Deployment.spec.template` labels
@@ -114,7 +114,7 @@ functionConfig:
name: staging name: staging
metadata: metadata:
annotations: annotations:
config.kubernetes.io/function: | config.k8s.io/function: |
container: container:
image: gcr.io/example/foo:v1.0.0 image: gcr.io/example/foo:v1.0.0
spec: spec:

View File

@@ -1,65 +0,0 @@
## delete-setter
[Alpha] Delete a custom setter for a Resource field
### Synopsis
Delete a custom setter for a Resource field.
DIR
A directory containing Resource configuration.
NAME
The name of the setter to create.
### Deleting a Custom Setter
**Given the YAML:**
# resource.yaml
apiVersion: v1
kind: Service
metadata:
...
spec:
...
ports:
...
- name: http
port: 8080 # {"type":"integer","x-kustomize":{"partialFieldSetters":[{"name":"http-port","value":"8080"}]}}
...
**Delete setter:**
# delete a setter for ports
$ kustomize cfg set create DIR/ http-port
comment will be removed for this field is not settable any more.
**Newly modified YAML:**
# resource.yaml
apiVersion: v1
kind: Service
metadata:
...
spec:
...
ports:
...
- name: http
port: 8080
...
### Deleting a setter used in substitution
If the setter is also used in substitution, it will ask you to delete the substitution first.
### Examples
# delete a setter for port
kustomize cfg create-setter DIR/ port

View File

@@ -14,8 +14,6 @@ require (
k8s.io/cli-runtime v0.17.3 k8s.io/cli-runtime v0.17.3
k8s.io/client-go v0.17.3 k8s.io/client-go v0.17.3
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
sigs.k8s.io/cli-utils v0.16.0 sigs.k8s.io/cli-utils v0.12.0
sigs.k8s.io/kustomize/kyaml v0.4.1 sigs.k8s.io/kustomize/kyaml v0.3.0
) )
replace sigs.k8s.io/kustomize/kyaml => ../../kyaml

View File

@@ -550,10 +550,9 @@ gopkg.in/yaml.v2 v2.0.0/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo= gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 h1:Xe2gvTZUJpsvOWUnvmL/tmhVBZUmHSvLbMjRj6NUUKo=
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -616,12 +615,16 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/cli-utils v0.16.0 h1:Wr32m1oxjIqc9G9l+igr13PeIM9LCyq8jQ8KjXKelvg= sigs.k8s.io/cli-utils v0.12.0 h1:+CvYwQAEtKvcx/NaUVF9rDKvY91VwJj+i7D2lWBMYc0=
sigs.k8s.io/cli-utils v0.16.0/go.mod h1:9Jqm9K2W6ShhCxsEuaz6HSRKKOXigPUx3ZfypGgxBLY= sigs.k8s.io/cli-utils v0.12.0/go.mod h1:H35YA5iJIM7EVNgqDTjX2dgt4wE23zmnXOTSTlyD+PE=
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg= sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/kustomize/kyaml v0.1.4 h1:cDG2u7v6CTAZmWKzCjk0hKG7AIN+2mCHx2ifwPbvKrs=
sigs.k8s.io/kustomize/kyaml v0.1.4/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
sigs.k8s.io/kustomize/kyaml v0.3.0 h1:Q0xajrIrKlt2tljTB3It2fnL9HXa7AfF3dxU6a01+vg=
sigs.k8s.io/kustomize/kyaml v0.3.0/go.mod h1:LjuK+fiy63C7cz/6I0OqxIth8nZzytzzDOgpjBHCtG8=
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-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/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
@@ -630,6 +633,4 @@ sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI= vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=

View File

@@ -50,8 +50,6 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
set.Flags().MarkHidden("partial") set.Flags().MarkHidden("partial")
set.Flags().StringVar(&setterVersion, "version", "", set.Flags().StringVar(&setterVersion, "version", "",
"use this version of the setter format") "use this version of the setter format")
set.Flags().BoolVar(&r.CreateSetter.Required, "required", false,
"indicates that this setter must be set by package consumer before live apply/preview")
set.Flags().StringVar(&r.CreateSetter.SchemaPath, "schema-path", "", set.Flags().StringVar(&r.CreateSetter.SchemaPath, "schema-path", "",
`openAPI schema file path for setter constraints -- file content `+ `openAPI schema file path for setter constraints -- file content `+
`e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`) `e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`)

View File

@@ -65,44 +65,6 @@ spec:
replicas: 3 # {"$openapi":"replicas"} replicas: 3 # {"$openapi":"replicas"}
`, `,
}, },
{
name: "add replicas no match",
args: []string{"replicas", "3", "--description", "hello world", "--set-by", "me"},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
foo: 2
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
`,
expectedOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
description: hello world
x-k8s-cli:
setter:
name: replicas
value: "3"
setBy: me
`,
expectedResources: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
foo: 2
`,
},
{ {
name: "error if substitution with same name exists", name: "error if substitution with same name exists",
args: []string{"my-image", "3", "--description", "hello world", "--set-by", "me"}, args: []string{"my-image", "3", "--description", "hello world", "--set-by", "me"},
@@ -186,21 +148,6 @@ spec:
- "a" - "a"
- "b" - "b"
- "c" - "c"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: myspace
spec:
replicas: 3
template:
spec:
containers:
- name: sidecar
image: nginx:1.7.9
- name: nginx
image: otherspace/nginx:1.7.9
`, `,
inputOpenAPI: ` inputOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
@@ -243,21 +190,6 @@ spec:
- "a" - "a"
- "b" - "b"
- "c" - "c"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: myspace
spec:
replicas: 3
template:
spec:
containers:
- name: sidecar
image: nginx:1.7.9
- name: nginx
image: otherspace/nginx:1.7.9
`, `,
}, },
@@ -372,41 +304,6 @@ metadata:
name: nginx-deployment name: nginx-deployment
spec: spec:
replicas: 3 # {"$openapi":"replicas"} replicas: 3 # {"$openapi":"replicas"}
`,
},
{
name: "add setter with . in the name",
args: []string{"foo.bar", "3"},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
`,
expectedOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.foo.bar:
x-k8s-cli:
setter:
name: foo.bar
value: "3"
`,
expectedResources: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$openapi":"foo.bar"}
`, `,
}, },
} }

View File

@@ -86,17 +86,14 @@ func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) erro
// extract setter name tokens from pattern enclosed in ${} // extract setter name tokens from pattern enclosed in ${}
re := regexp.MustCompile(`\$\{([^}]*)\}`) re := regexp.MustCompile(`\$\{([^}]*)\}`)
markers := re.FindAllString(r.CreateSubstitution.Pattern, -1) markers := re.FindAll([]byte(r.CreateSubstitution.Pattern), -1)
if len(markers) == 0 { if len(markers) == 0 {
return errors.Errorf("unable to find setter or substitution names in pattern, " + return errors.Errorf("unable to find setter or substitution names in pattern, " +
"setter names must be enclosed in ${}") "setter names must be enclosed in ${}")
} }
for _, marker := range markers { for _, marker := range markers {
name := strings.TrimSuffix(strings.TrimPrefix(marker, "${"), "}") name := strings.TrimSuffix(strings.TrimPrefix(string(marker), "${"), "}")
if name == r.CreateSubstitution.Name {
return fmt.Errorf("setters must have different name than the substitution: %s", name)
}
ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + name) ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + name)
if err != nil { if err != nil {
@@ -115,7 +112,7 @@ func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) erro
r.CreateSubstitution.Values = append( r.CreateSubstitution.Values = append(
r.CreateSubstitution.Values, r.CreateSubstitution.Values,
setters2.Value{Marker: marker, Ref: markerRef}, setters2.Value{Marker: string(marker), Ref: markerRef},
) )
} }

View File

@@ -408,39 +408,6 @@ spec:
`, `,
err: "cyclic substitution detected with name my-nested-subst", err: "cyclic substitution detected with name my-nested-subst",
}, },
{
name: "substitution with non-existing setter with same name",
args: []string{
"foo", "--field-value", "prefix-1234", "--pattern", "prefix-${foo}"},
input: `
apiVersion: test/v1
kind: Foo
metadata:
name: foo
spec:
setterVal: 1234
substVal: prefix-1234
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
`,
expectedOpenAPI: `
apiVersion: v1alpha1
kind: Example
`,
expectedResources: `
apiVersion: test/v1
kind: Foo
metadata:
name: foo
spec:
setterVal: 1234
substVal: prefix-1234
`,
err: "setters must have different name than the substitution: foo",
},
} }
for i := range tests { for i := range tests {
test := tests[i] test := tests[i]

View File

@@ -1,64 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
)
// NewDeleteRunner returns a command runner.
func NewDeleteSetterRunner(parent string) *DeleteSetterRunner {
r := &DeleteSetterRunner{}
c := &cobra.Command{
Use: "delete-setter DIR NAME",
Args: cobra.MinimumNArgs(2),
Short: commands.DeleteSetterShort,
Long: commands.DeleteSetterLong,
Example: commands.DeleteSetterExamples,
PreRunE: r.preRunE,
RunE: r.runE,
}
fixDocs(parent, c)
r.Command = c
return r
}
func DeleteSetterCommand(parent string) *cobra.Command {
return NewDeleteSetterRunner(parent).Command
}
type DeleteSetterRunner struct {
Command *cobra.Command
DeleteSetter settersutil.DeleterCreator
OpenAPIFile string
}
func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
var err error
r.DeleteSetter.Name = args[1]
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
if err != nil {
return err
}
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
return err
}
return nil
}
func (r *DeleteSetterRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, r.delete(c, args))
}
func (r *DeleteSetterRunner) delete(c *cobra.Command, args []string) error {
return r.DeleteSetter.Delete(r.OpenAPIFile, args[0])
}

View File

@@ -1,302 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands_test
import (
"bytes"
"io/ioutil"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/openapi"
)
func TestDeleteSetterCommand(t *testing.T) {
var tests = []struct {
name string
input string
args []string
schema string
out string
inputOpenAPI string
expectedOpenAPI string
expectedResources string
err string
}{
{
name: "delete replicas",
args: []string{"replicas", "hello world"},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$openapi" : "replicas"}}
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
description: hello world
x-k8s-cli:
setter:
name: replicas
value: "3"
setBy: me
`,
expectedOpenAPI: `
apiVersion: v1alpha1
kind: Example
`,
expectedResources: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
`,
},
{
name: "delete only one setter",
args: []string{"replicas", "hello world"},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$openapi" : "replicas"}}
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
description: hello world
x-k8s-cli:
setter:
name: replicas
value: "3"
setBy: me
io.k8s.cli.setters.image:
x-k8s-cli:
setter:
name: image
value: 1.0
`,
expectedOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.image:
x-k8s-cli:
setter:
name: image
value: 1.0
`,
expectedResources: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
`,
},
{
name: "delete non exist setter error",
args: []string{"image", "hello world"},
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$openapi" : "replicas"}}
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
description: hello world
x-k8s-cli:
setter:
name: replicas
value: "3"
setBy: me
`,
expectedOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.replicas:
description: hello world
x-k8s-cli:
setter:
name: replicas
value: "3"
setBy: me
`,
expectedResources: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$openapi" : "replicas"}}
`,
err: `setter does not exist`,
},
{
name: "delete setter used in substitution error",
args: []string{"image-name", "hello world"},
input: `
apiVersion: apps/v1
kind: Deployment
`,
inputOpenAPI: `
openAPI:
definitions:
io.k8s.cli.setters.image-name:
x-k8s-cli:
setter:
name: image-name
value: "nginx"
io.k8s.cli.setters.image-tag:
x-k8s-cli:
setter:
name: image-tag
value: "1.8.1"
io.k8s.cli.substitutions.image:
x-k8s-cli:
substitution:
name: image
pattern: IMAGE_NAME:IMAGE_TAG
values:
- marker: "IMAGE_NAME"
ref: "#/definitions/io.k8s.cli.setters.image-name"
- marker: "IMAGE_TAG"
ref: "#/definitions/io.k8s.cli.setters.image-tag"
`,
expectedOpenAPI: `
openAPI:
definitions:
io.k8s.cli.setters.image-name:
x-k8s-cli:
setter:
name: image-name
value: "nginx"
io.k8s.cli.setters.image-tag:
x-k8s-cli:
setter:
name: image-tag
value: "1.8.1"
io.k8s.cli.substitutions.image:
x-k8s-cli:
substitution:
name: image
pattern: IMAGE_NAME:IMAGE_TAG
values:
- marker: "IMAGE_NAME"
ref: "#/definitions/io.k8s.cli.setters.image-name"
- marker: "IMAGE_TAG"
ref: "#/definitions/io.k8s.cli.setters.image-tag"
`,
expectedResources: `
apiVersion: apps/v1
kind: Deployment
`,
err: `setter is used in substitution image, please delete the substitution first`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
f, err := ioutil.TempFile("", "k8s-cli-")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.Remove(f.Name())
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
if !assert.NoError(t, err) {
t.FailNow()
}
old := ext.GetOpenAPIFile
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.Remove(r.Name())
err = ioutil.WriteFile(r.Name(), []byte(test.input), 0600)
if !assert.NoError(t, err) {
t.FailNow()
}
runner := commands.NewDeleteSetterRunner("")
out := &bytes.Buffer{}
runner.Command.SetOut(out)
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
err = runner.Command.Execute()
if test.err != "" {
if !assert.NotNil(t, err) {
t.FailNow()
} else {
assert.Equal(t, err.Error(), test.err)
return
}
}
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, test.out, out.String()) {
t.FailNow()
}
actualResources, err := ioutil.ReadFile(r.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(test.expectedResources),
strings.TrimSpace(string(actualResources))) {
t.FailNow()
}
actualOpenAPI, err := ioutil.ReadFile(f.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(test.expectedOpenAPI),
strings.TrimSpace(string(actualOpenAPI))) {
t.FailNow()
}
})
}
}

View File

@@ -32,8 +32,6 @@ func NewListSettersRunner(parent string) *ListSettersRunner {
} }
c.Flags().BoolVar(&r.Markdown, "markdown", false, c.Flags().BoolVar(&r.Markdown, "markdown", false,
"output as github markdown") "output as github markdown")
c.Flags().BoolVar(&r.IncludeSubst, "include-subst", false,
"include substitutions in the output")
fixDocs(parent, c) fixDocs(parent, c)
r.Command = c r.Command = c
return r return r
@@ -44,11 +42,10 @@ func ListSettersCommand(parent string) *cobra.Command {
} }
type ListSettersRunner struct { type ListSettersRunner struct {
Command *cobra.Command Command *cobra.Command
Lookup setters.LookupSetters Lookup setters.LookupSetters
List setters2.List List setters2.List
Markdown bool Markdown bool
IncludeSubst bool
} }
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error { func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
@@ -66,10 +63,7 @@ func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
if err := r.ListSetters(c, args); err != nil { if err := r.ListSetters(c, args); err != nil {
return err return err
} }
if r.IncludeSubst { return r.ListSubstitutions(c, args)
return r.ListSubstitutions(c, args)
}
return nil
} }
return handleError(c, lookup(r.Lookup, c, args)) return handleError(c, lookup(r.Lookup, c, args))
@@ -85,7 +79,7 @@ func (r *ListSettersRunner) ListSetters(c *cobra.Command, args []string) error {
return err return err
} }
table := newTable(c.OutOrStdout(), r.Markdown) table := newTable(c.OutOrStdout(), r.Markdown)
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT", "REQUIRED"}) table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT"})
for i := range r.List.Setters { for i := range r.List.Setters {
s := r.List.Setters[i] s := r.List.Setters[i]
v := s.Value v := s.Value
@@ -95,14 +89,8 @@ func (r *ListSettersRunner) ListSetters(c *cobra.Command, args []string) error {
v = strings.Join(s.ListValues, ",") v = strings.Join(s.ListValues, ",")
v = fmt.Sprintf("[%s]", v) v = fmt.Sprintf("[%s]", v)
} }
var required string
if s.Required {
required = "Yes"
} else {
required = "No"
}
table.Append([]string{ table.Append([]string{
s.Name, v, s.SetBy, s.Description, fmt.Sprintf("%d", s.Count), required}) s.Name, v, s.SetBy, s.Description, fmt.Sprintf("%d", s.Count)})
} }
table.Render() table.Render()
@@ -125,9 +113,6 @@ func (r *ListSettersRunner) ListSubstitutions(c *cobra.Command, args []string) e
return err return err
} }
table := newTable(c.OutOrStdout(), r.Markdown) table := newTable(c.OutOrStdout(), r.Markdown)
b := tablewriter.Border{Top: true}
table.SetBorders(b)
table.SetHeader([]string{"SUBSTITUTION", "PATTERN", "REFERENCES"}) table.SetHeader([]string{"SUBSTITUTION", "PATTERN", "REFERENCES"})
for i := range r.List.Substitutions { for i := range r.List.Substitutions {
s := r.List.Substitutions[i] s := r.List.Substitutions[i]

View File

@@ -7,7 +7,6 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -35,7 +34,6 @@ openAPI:
name: replicas name: replicas
value: "3" value: "3"
setBy: me setBy: me
required: true
description: "hello world" description: "hello world"
`, `,
input: ` input: `
@@ -46,39 +44,12 @@ metadata:
spec: spec:
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`, `,
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED expected: ` NAME VALUE SET BY DESCRIPTION COUNT
replicas 3 me hello world 1 Yes replicas 3 me hello world 1
`,
},
{
name: "list-replicas inconsistent with openapi",
openapi: `
openAPI:
definitions:
io.k8s.cli.setters.replicas:
x-k8s-cli:
setter:
name: replicas
value: "4"
setBy: me
description: "hello world"
`,
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
`,
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
replicas 4 me hello world 1 No
`, `,
}, },
{ {
name: "list-multiple", name: "list-multiple",
args: []string{"--include-subst"},
openapi: ` openapi: `
openAPI: openAPI:
definitions: definitions:
@@ -103,8 +74,6 @@ openAPI:
name: tag name: tag
value: "1.7.9" value: "1.7.9"
setBy: me3 setBy: me3
required: true
isSet: false
io.k8s.cli.substitutions.image: io.k8s.cli.substitutions.image:
x-k8s-cli: x-k8s-cli:
substitution: substitution:
@@ -131,18 +100,16 @@ spec:
- name: nginx2 - name: nginx2
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"} image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
`, `,
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED expected: ` NAME VALUE SET BY DESCRIPTION COUNT
image nginx me2 hello world 2 2 No image nginx me2 hello world 2 2
replicas 3 me1 hello world 1 1 No replicas 3 me1 hello world 1 1
tag 1.7.9 me3 hello world 3 1 Yes tag 1.7.9 me3 hello world 3 1
--------------- ----------- --------------
SUBSTITUTION PATTERN REFERENCES SUBSTITUTION PATTERN REFERENCES
image IMAGE:TAG [image,tag] image IMAGE:TAG [image,tag]
`, `,
}, },
{ {
name: "list-multiple-resources", name: "list-multiple-resources",
args: []string{"--include-subst"},
openapi: ` openapi: `
openAPI: openAPI:
definitions: definitions:
@@ -207,11 +174,10 @@ spec:
- name: nginx2 - name: nginx2
image: nginx image: nginx
`, `,
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED expected: ` NAME VALUE SET BY DESCRIPTION COUNT
image nginx me2 hello world 2 3 No image nginx me2 hello world 2 3
replicas 3 me1 hello world 1 2 No replicas 3 me1 hello world 1 2
tag 1.7.9 me3 hello world 3 2 No tag 1.7.9 me3 hello world 3 2
--------------- ----------- --------------
SUBSTITUTION PATTERN REFERENCES SUBSTITUTION PATTERN REFERENCES
image IMAGE:TAG [image,tag] image IMAGE:TAG [image,tag]
`, `,
@@ -236,7 +202,6 @@ openAPI:
name: image name: image
value: "nginx" value: "nginx"
setBy: me2 setBy: me2
required: true
io.k8s.cli.setters.tag: io.k8s.cli.setters.tag:
description: "hello world 3" description: "hello world 3"
x-k8s-cli: x-k8s-cli:
@@ -244,10 +209,10 @@ openAPI:
name: tag name: tag
value: "1.7.9" value: "1.7.9"
setBy: me3 setBy: me3
io.k8s.cli.substitutions.image-subst: io.k8s.cli.substitutions.image:
x-k8s-cli: x-k8s-cli:
substitution: substitution:
name: image-subst name: image
pattern: IMAGE:TAG pattern: IMAGE:TAG
values: values:
- marker: IMAGE - marker: IMAGE
@@ -266,7 +231,7 @@ spec:
spec: spec:
containers: containers:
- name: nginx - name: nginx
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-subst"} image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
- name: nginx2 - name: nginx2
image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"} image: nginx # {"$ref": "#/definitions/io.k8s.cli.setters.image"}
--- ---
@@ -280,12 +245,14 @@ spec:
spec: spec:
containers: containers:
- name: nginx - name: nginx
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image-subst"} image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
- name: nginx2 - name: nginx2
image: nginx image: nginx
`, `,
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED expected: ` NAME VALUE SET BY DESCRIPTION COUNT
image nginx me2 hello world 2 3 Yes image nginx me2 hello world 2 3
SUBSTITUTION PATTERN REFERENCES
image IMAGE:TAG [image,tag]
`, `,
}, },
@@ -310,7 +277,6 @@ openAPI:
- b - b
- c - c
setBy: me setBy: me
required: true
`, `,
input: ` input: `
apiVersion: example.com/v1beta1 apiVersion: example.com/v1beta1
@@ -324,14 +290,13 @@ spec:
- "b" - "b"
- "c" - "c"
`, `,
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED expected: ` NAME VALUE SET BY DESCRIPTION COUNT
list [a,b,c] me hello world 1 Yes list [a,b,c] me hello world 1
`, `,
}, },
{ {
name: "nested substitution", name: "nested substitution",
args: []string{"--include-subst"},
input: ` input: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
@@ -362,8 +327,6 @@ openAPI:
setter: setter:
name: my-tag-setter name: my-tag-setter
value: 1.7.9 value: 1.7.9
required: true
isSet: true
io.k8s.cli.substitutions.my-image-subst: io.k8s.cli.substitutions.my-image-subst:
x-k8s-cli: x-k8s-cli:
substitution: substitution:
@@ -390,11 +353,10 @@ openAPI:
name: my-other-setter name: my-other-setter
value: nginxotherthing value: nginxotherthing
`, `,
expected: ` NAME VALUE SET BY DESCRIPTION COUNT REQUIRED expected: ` NAME VALUE SET BY DESCRIPTION COUNT
my-image-setter nginx 2 No my-image-setter nginx 2
my-other-setter nginxotherthing 1 No my-other-setter nginxotherthing 1
my-tag-setter 1.7.9 2 Yes my-tag-setter 1.7.9 2
------------------ ------------------------------------------------ -----------------------------------
SUBSTITUTION PATTERN REFERENCES SUBSTITUTION PATTERN REFERENCES
my-image-subst ${my-image-setter}::${my-tag-setter} [my-image-setter,my-tag-setter] my-image-subst ${my-image-setter}::${my-tag-setter} [my-image-setter,my-tag-setter]
my-nested-subst something/${my-image-subst}/${my-other-setter} [my-image-subst,my-other-setter] my-nested-subst something/${my-image-subst}/${my-other-setter} [my-image-subst,my-other-setter]
@@ -445,27 +407,6 @@ openAPI:
if !assert.Equal(t, test.expected, actual.String()) { if !assert.Equal(t, test.expected, actual.String()) {
t.FailNow() t.FailNow()
} }
// make sure that the resources are not altered
actualResources, err := ioutil.ReadFile(r.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(test.input),
strings.TrimSpace(string(actualResources))) {
t.FailNow()
}
actualOpenAPI, err := ioutil.ReadFile(f.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(test.openapi),
strings.TrimSpace(string(actualOpenAPI))) {
t.FailNow()
}
}) })
} }
} }

View File

@@ -64,7 +64,6 @@ openAPI:
name: replicas name: replicas
value: "4" value: "4"
setBy: pw setBy: pw
isSet: true
`, `,
expectedResources: ` expectedResources: `
apiVersion: apps/v1 apiVersion: apps/v1
@@ -159,7 +158,6 @@ openAPI:
setter: setter:
name: replicas name: replicas
value: "4" value: "4"
isSet: true
`, `,
expectedResources: ` expectedResources: `
apiVersion: apps/v1 apiVersion: apps/v1
@@ -231,7 +229,6 @@ openAPI:
setter: setter:
name: tag name: tag
value: "1.8.1" value: "1.8.1"
isSet: true
io.k8s.cli.substitutions.image: io.k8s.cli.substitutions.image:
x-k8s-cli: x-k8s-cli:
substitution: substitution:
@@ -542,7 +539,6 @@ openAPI:
setter: setter:
name: replicas name: replicas
value: "4" value: "4"
isSet: true
`, `,
expectedResources: ` expectedResources: `
apiVersion: apps/v1 apiVersion: apps/v1
@@ -643,7 +639,6 @@ openAPI:
listValues: listValues:
- "10" - "10"
- "11" - "11"
isSet: true
`, `,
expectedResources: ` expectedResources: `
apiVersion: example.com/v1beta1 apiVersion: example.com/v1beta1
@@ -776,7 +771,6 @@ openAPI:
setter: setter:
name: my-image-setter name: my-image-setter
value: ubuntu value: ubuntu
isSet: true
io.k8s.cli.setters.my-tag-setter: io.k8s.cli.setters.my-tag-setter:
x-k8s-cli: x-k8s-cli:
setter: setter:

View File

@@ -30,7 +30,6 @@ func TestRunE2e(t *testing.T) {
expectedErr string expectedErr string
skipIfFalseEnv string skipIfFalseEnv string
}{ }{
{ {
name: "exec_function_no_args", name: "exec_function_no_args",
args: func(d string) []string { args: func(d string) []string {
@@ -64,36 +63,6 @@ metadata:
}, },
}, },
{
name: "exec_function_no_args_json",
args: func(d string) []string {
return []string{
"--enable-exec", "--exec-path", filepath.Join(d, "e2econtainerconfig"),
}
},
files: func(d string) map[string]string {
return map[string]string{
"deployment.json": `
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "foo"
}
}
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.json": `
{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "foo", annotations: {
a-string-value: '', a-int-value: '0', a-bool-value: 'false'}}}
`,
}
},
},
{ {
name: "exec_function_args", name: "exec_function_args",
args: func(d string) []string { args: func(d string) []string {
@@ -499,6 +468,7 @@ def run(r, fc):
resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"] resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"]
resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"] resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"]
resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"] resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"]
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]) run(ctx.resource_list["items"], ctx.resource_list["functionConfig"])
`, `,
"config.yaml": ` "config.yaml": `
@@ -575,6 +545,7 @@ def run(r, fc):
resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"] resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"]
resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"] resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"]
resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"] resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"]
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"]) run(ctx.resource_list["items"], ctx.resource_list["functionConfig"])
`, `,
"deployment.yaml": ` "deployment.yaml": `

View File

@@ -34,8 +34,8 @@ openAPI:
`, `,
}, },
expectedStdOut: ` expectedStdOut: `
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED NAME VALUE SET BY DESCRIPTION COUNT
replicas 3 1 No replicas 3 1
`, `,
}, },
} }

View File

@@ -54,7 +54,6 @@ openAPI:
setter: setter:
name: replicas name: replicas
value: "4" value: "4"
isSet: true
`, `,
}, },
}, },

View File

@@ -31,8 +31,8 @@ func GetGrepRunner(name string) *GrepRunner {
"also print resources from subpackages.") "also print resources from subpackages.")
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", true, c.Flags().BoolVar(&r.KeepAnnotations, "annotate", true,
"annotate resources with their file origins.") "annotate resources with their file origins.")
c.Flags().BoolVarP(&r.InvertMatch, "invert-match", "", false, c.Flags().BoolVarP(&r.InvertMatch, "invert-match", "v", false,
"Selected Resources are those not matching any of the specified patterns..") " Selected Resources are those not matching any of the specified patterns..")
r.Command = c r.Command = c
return r return r

View File

@@ -136,51 +136,6 @@ spec:
} }
} }
func TestSinkCommandJSON(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-source-test")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.RemoveAll(d)
r := commands.GetSinkRunner("")
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo",
"annotations": {"app": "nginx2", config.kubernetes.io/index: '0',
config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}}
`))
r.Command.SetArgs([]string{d})
if !assert.NoError(t, r.Command.Execute()) {
t.FailNow()
}
actual, err := ioutil.ReadFile(filepath.Join(d, "f1.json"))
if !assert.NoError(t, err) {
t.FailNow()
}
expected := `{
"kind": "Deployment",
"metadata": {
"annotations": {
"app": "nginx2"
},
"labels": {
"app": "nginx2"
},
"name": "foo"
},
"spec": {
"replicas": 1
}
}
`
if !assert.Equal(t, expected, string(actual)) {
t.FailNow()
}
}
func TestSinkCommand_Stdout(t *testing.T) { func TestSinkCommand_Stdout(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-source-test") d, err := ioutil.TempDir("", "kustomize-source-test")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@@ -294,49 +249,3 @@ spec:
t.FailNow() t.FailNow()
} }
} }
func TestSinkCommandJSON_Stdout(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-source-test")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.RemoveAll(d)
// fmt the files
out := &bytes.Buffer{}
r := commands.GetSinkRunner("")
r.Command.SetIn(bytes.NewBufferString(`apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo",
"annotations": {"app": "nginx2", config.kubernetes.io/index: '0',
config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}}
`))
r.Command.SetOut(out)
r.Command.SetArgs([]string{})
if !assert.NoError(t, r.Command.Execute()) {
t.FailNow()
}
expected := `{
"kind": "Deployment",
"metadata": {
"annotations": {
"app": "nginx2"
},
"labels": {
"app": "nginx2"
},
"name": "foo"
},
"spec": {
"replicas": 1
}
}
`
if !assert.Equal(t, expected, out.String()) {
println(out.String())
t.FailNow()
}
}

View File

@@ -71,7 +71,7 @@ func (r *SourceRunner) runE(c *cobra.Command, args []string) error {
var inputs []kio.Reader var inputs []kio.Reader
for _, a := range args { for _, a := range args {
inputs = append(inputs, kio.LocalPackageReader{PackagePath: a, MatchFilesGlob: kio.MatchAll}) inputs = append(inputs, kio.LocalPackageReader{PackagePath: a})
} }
if len(inputs) == 0 { if len(inputs) == 0 {
inputs = []kio.Reader{&kio.ByteReader{Reader: c.InOrStdin()}} inputs = []kio.Reader{&kio.ByteReader{Reader: c.InOrStdin()}}

View File

@@ -135,77 +135,6 @@ items:
} }
} }
func TestSourceCommandJSON(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-source-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
err = ioutil.WriteFile(filepath.Join(d, "f1.json"), []byte(`
{
"kind": "Deployment",
"metadata": {
"labels": {
"app": "nginx2"
},
"name": "foo",
"annotations": {
"app": "nginx2"
}
},
"spec": {
"replicas": 1
}
}
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "f2.json"), []byte(`
{
"apiVersion": "v1",
"kind": "Abstraction",
"metadata": {
"name": "foo",
"annotations": {
"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n",
"config.kubernetes.io/local-config": "true"
}
},
"spec": {
"replicas": 3
}
}
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := commands.GetSourceRunner("")
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo",
"annotations": {"app": "nginx2", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f1.json'}},
"spec": {"replicas": 1}}
- {"apiVersion": "v1", "kind": "Abstraction", "metadata": {"name": "foo", "annotations": {
"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n",
"config.kubernetes.io/local-config": "true", config.kubernetes.io/index: '0',
config.kubernetes.io/path: 'f2.json'}}, "spec": {"replicas": 3}}
`, b.String()) {
return
}
}
func TestSourceCommand_Stdin(t *testing.T) { func TestSourceCommand_Stdin(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-source-test") d, err := ioutil.TempDir("", "kustomize-source-test")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
@@ -269,48 +198,3 @@ items:
return return
} }
} }
func TestSourceCommandJSON_Stdin(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-source-test")
if !assert.NoError(t, err) {
return
}
defer os.RemoveAll(d)
in := bytes.NewBufferString(`
{
"kind": "Deployment",
"metadata": {
"labels": {
"app": "nginx2"
},
"name": "foo",
"annotations": {
"app": "nginx2"
}
},
"spec": {
"replicas": 1
}
}
`)
out := &bytes.Buffer{}
r := commands.GetSourceRunner("")
r.Command.SetArgs([]string{})
r.Command.SetIn(in)
r.Command.SetOut(out)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo",
"annotations": {"app": "nginx2", config.kubernetes.io/index: '0'}}, "spec": {
"replicas": 1}}
`, out.String()) {
return
}
}

View File

@@ -175,13 +175,13 @@ are passed to the Function through the ` + "`" + `ResourceList.functionConfig` +
name: my-instance name: my-instance
annotations: annotations:
config.kubernetes.io/local-config: "true" config.kubernetes.io/local-config: "true"
config.kubernetes.io/function: | config.k8s.io/function: |
container: container:
image: gcr.io/example-functions/nginx-template:v1.0.0 image: gcr.io/example-functions/nginx-template:v1.0.0
spec: spec:
replicas: 5 replicas: 5
- ` + "`" + `annotations[config.kubernetes.io/function].container.image` + "`" + `: the image to use for this API - ` + "`" + `annotations[config.k8s.io/function].container.image` + "`" + `: the image to use for this API
- ` + "`" + `annotations[config.kubernetes.io/local-config]` + "`" + `: mark this as not a Resource that should - ` + "`" + `annotations[config.kubernetes.io/local-config]` + "`" + `: mark this as not a Resource that should
be applied be applied
@@ -244,7 +244,7 @@ containers that can be chained together as part of a configuration management pi
The end result of such a pipeline are fully rendered configurations that can then be The end result of such a pipeline are fully rendered configurations that can then be
applied to a control plane (e.g. Using kubectl apply for Kubernetes control plane). applied to a control plane (e.g. Using kubectl apply for Kubernetes control plane).
As such, although this document references Kubernetes Resource Model and API conventions, As such, although this document references Kubernetes Resource Model and API conventions,
it is completely decoupled from Kubernetes API machinery and does not depend on any it is completely decoupled from Kuberentes API machinery and does not depend on any
in-cluster components. in-cluster components.
This document references terms described in [Kubernetes API Conventions][1]. This document references terms described in [Kubernetes API Conventions][1].
@@ -267,7 +267,7 @@ interpreted as described in [RFC 2119][2].
Performing these on the client rather than the server enables: Performing these on the client rather than the server enables:
- Configuration to be reviewed prior to being sent to the API server - Configuration to be reviewed prior to being sent to the API server
- Configuration to be validated as part of the CI/CD pipeline - Configuration to be validated as part of the CI?CD pipeline
- Configuration for Resources to validated holistically rather than individually - Configuration for Resources to validated holistically rather than individually
per-Resource per-Resource
- e.g. ensure the ` + "`" + `Service.selector` + "`" + ` and ` + "`" + `Deployment.spec.template` + "`" + ` labels - e.g. ensure the ` + "`" + `Service.selector` + "`" + ` and ` + "`" + `Deployment.spec.template` + "`" + ` labels
@@ -343,7 +343,7 @@ An example using ` + "`" + `config.kubernetes.io/v1beta1/ResourceList` + "`" + `
name: staging name: staging
metadata: metadata:
annotations: annotations:
config.kubernetes.io/function: | config.k8s.io/function: |
container: container:
image: gcr.io/example/foo:v1.0.0 image: gcr.io/example/foo:v1.0.0
spec: spec:

View File

@@ -89,22 +89,6 @@ var CreateSetterExamples = `
kustomize cfg create-setter DIR/ image-tag v1.0.1 --type "string" \ kustomize cfg create-setter DIR/ image-tag v1.0.1 --type "string" \
--field image --description "current stable release"` --field image --description "current stable release"`
var DeleteSetterShort = `[Alpha] Delete a custom setter for a Resource field`
var DeleteSetterLong = `
Delete a custom setter for a Resource field.
DIR
A directory containing Resource configuration.
NAME
The name of the setter to create.
`
var DeleteSetterExamples = `
# delete a setter for port
kustomize cfg create-setter DIR/ port`
var FmtShort = `[Alpha] Format yaml configuration files.` var FmtShort = `[Alpha] Format yaml configuration files.`
var FmtLong = ` var FmtLong = `
[Alpha] Format yaml configuration files. [Alpha] Format yaml configuration files.

View File

@@ -1,2 +1,3 @@
English | [简体中文](zh/README.md)
Moved to [https://kubernetes-sigs.github.io/kustomize](https://kubernetes-sigs.github.io/kustomize) Moved to [https://kubernetes-sigs.github.io/kustomize](https://kubernetes-sigs.github.io/kustomize)

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="generator" content="Hugo 0.68.3" /> <meta name="generator" content="Hugo 0.73.0-DEV" />
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW"> <META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
@@ -99,14 +99,6 @@
<a class="nav-link" href="https://github.com/kubernetes-sigs/kustomize/tree/master/examples" target="_blank" ><span>Example</span></a>
</li>
<li class="nav-item mr-4 mb-2 mb-lg-0">
<a class="nav-link" href="/kustomize/faq/" ><span>FAQ</span></a> <a class="nav-link" href="/kustomize/faq/" ><span>FAQ</span></a>
@@ -134,19 +126,6 @@
<li class="nav-item dropdown d-none d-lg-block">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
English
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="/kustomize/zh/api-reference/glossary/">简体中文</a>
</div>
</li>
</ul> </ul>
</div> </div>
<div class="navbar-nav d-none d-lg-block"> <div class="navbar-nav d-none d-lg-block">
@@ -174,19 +153,6 @@
<nav class="collapse td-sidebar-nav pt-2 pl-4" id="td-section-nav"> <nav class="collapse td-sidebar-nav pt-2 pl-4" id="td-section-nav">
<div class="nav-item dropdown d-block d-lg-none">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
English
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="/kustomize/zh/api-reference/glossary/">简体中文</a>
</div>
</div>
@@ -911,8 +877,8 @@ absolute path, or by relative path.</p>
<li><strong>B</strong> may not <em>depend on</em> <strong>A</strong>, even transitively.</li> <li><strong>B</strong> may not <em>depend on</em> <strong>A</strong>, even transitively.</li>
</ul> </ul>
<p><strong>A</strong> may contain <strong>B</strong>, but in this case it might be <p><strong>A</strong> may contain <strong>B</strong>, but in this case it might be
simplest to have <strong>A</strong> directly depend on <strong>B</strong>&lsquo;s simplest to have <strong>A</strong> directly depend on <strong>B</strong>&rsquo;s
resources and eliminate <strong>B</strong>&lsquo;s kustomization.yaml file resources and eliminate <strong>B</strong>&rsquo;s kustomization.yaml file
(i.e. absorb <strong>B</strong> into <strong>A</strong>).</p> (i.e. absorb <strong>B</strong> into <strong>A</strong>).</p>
<p>Conventionally, <strong>B</strong> is in a directory that&rsquo;s sibling <p>Conventionally, <strong>B</strong> is in a directory that&rsquo;s sibling
to <strong>A</strong>, or <strong>B</strong> is off in a completely independent to <strong>A</strong>, or <strong>B</strong> is off in a completely independent
@@ -1046,7 +1012,7 @@ syntax. See this <a href="https://github.com/kubernetes-sigs/kustomize/tree/mas
<p>A chunk of code used by kustomize, but not necessarily <p>A chunk of code used by kustomize, but not necessarily
compiled into kustomize, to generate and/or transform a compiled into kustomize, to generate and/or transform a
kubernetes resource as part of a kustomization.</p> kubernetes resource as part of a kustomization.</p>
<p>Details <a href="../../guides/plugins">here</a>.</p> <p>Details <a href="plugins">here</a>.</p>
<h2 id="resource">resource</h2> <h2 id="resource">resource</h2>
<p>A <em>resource</em> in the context of a REST-ful API is the <p>A <em>resource</em> in the context of a REST-ful API is the
target object of an HTTP operation like <em>GET</em>, <em>PUT</em> or target object of an HTTP operation like <em>GET</em>, <em>PUT</em> or
@@ -1147,7 +1113,7 @@ cpu and memory requests.</p>
<div class="text-muted mt-5 pt-3 border-top">Last modified June 15, 2020: <a href="https://github.com/kubernetes-sigs/kustomize/commit/518147c129f831913d33226cf17068d0d4270f41">add zh docsy (518147c1)</a> <div class="text-muted mt-5 pt-3 border-top">Last modified June 7, 2020: <a href="https://github.com/kubernetes-sigs/kustomize/commit/42497c664f619a36cc86156e366b53099bd633cb">Convert docs to docsy (42497c66)</a>
</div> </div>
</div> </div>

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="generator" content="Hugo 0.68.3" /> <meta name="generator" content="Hugo 0.73.0-DEV" />
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW"> <META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
@@ -99,14 +99,6 @@
<a class="nav-link" href="https://github.com/kubernetes-sigs/kustomize/tree/master/examples" target="_blank" ><span>Example</span></a>
</li>
<li class="nav-item mr-4 mb-2 mb-lg-0">
<a class="nav-link" href="/kustomize/faq/" ><span>FAQ</span></a> <a class="nav-link" href="/kustomize/faq/" ><span>FAQ</span></a>
@@ -134,19 +126,6 @@
<li class="nav-item dropdown d-none d-lg-block">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
English
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="/kustomize/zh/api-reference/">简体中文</a>
</div>
</li>
</ul> </ul>
</div> </div>
<div class="navbar-nav d-none d-lg-block"> <div class="navbar-nav d-none d-lg-block">
@@ -174,19 +153,6 @@
<nav class="collapse td-sidebar-nav pt-2 pl-4" id="td-section-nav"> <nav class="collapse td-sidebar-nav pt-2 pl-4" id="td-section-nav">
<div class="nav-item dropdown d-block d-lg-none">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
English
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="/kustomize/zh/api-reference/">简体中文</a>
</div>
</div>

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="generator" content="Hugo 0.68.3" /> <meta name="generator" content="Hugo 0.73.0-DEV" />
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW"> <META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
@@ -99,14 +99,6 @@
<a class="nav-link" href="https://github.com/kubernetes-sigs/kustomize/tree/master/examples" target="_blank" ><span>Example</span></a>
</li>
<li class="nav-item mr-4 mb-2 mb-lg-0">
<a class="nav-link" href="/kustomize/faq/" ><span>FAQ</span></a> <a class="nav-link" href="/kustomize/faq/" ><span>FAQ</span></a>
@@ -134,19 +126,6 @@
<li class="nav-item dropdown d-none d-lg-block">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
English
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="/kustomize/zh/api-reference/kustomization/bases/">简体中文</a>
</div>
</li>
</ul> </ul>
</div> </div>
<div class="navbar-nav d-none d-lg-block"> <div class="navbar-nav d-none d-lg-block">
@@ -174,19 +153,6 @@
<nav class="collapse td-sidebar-nav pt-2 pl-4" id="td-section-nav"> <nav class="collapse td-sidebar-nav pt-2 pl-4" id="td-section-nav">
<div class="nav-item dropdown d-block d-lg-none">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
English
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
<a class="dropdown-item" href="/kustomize/zh/api-reference/kustomization/bases/">简体中文</a>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More