mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-29 01:30:51 +00:00
Compare commits
116 Commits
cmd/config
...
kyaml/v0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
243394002f | ||
|
|
b9053655db | ||
|
|
1f35b37712 | ||
|
|
d7484aacad | ||
|
|
98414e8bf6 | ||
|
|
c1f2dd3688 | ||
|
|
982b9e77d4 | ||
|
|
c75a47322b | ||
|
|
bb05d19a5a | ||
|
|
2c615d78a2 | ||
|
|
45a9805656 | ||
|
|
504029281a | ||
|
|
2d699f93fc | ||
|
|
1653a70693 | ||
|
|
4667372114 | ||
|
|
6d9702561a | ||
|
|
4533e43009 | ||
|
|
c7b00733c1 | ||
|
|
b023570fae | ||
|
|
8c315ab874 | ||
|
|
014db05f9c | ||
|
|
d8f601248b | ||
|
|
9c75afca58 | ||
|
|
8f2f2c201f | ||
|
|
e66235f380 | ||
|
|
636167ac7f | ||
|
|
4eac2b10d6 | ||
|
|
d15a573544 | ||
|
|
8d22cbdcca | ||
|
|
b72db9e783 | ||
|
|
a446a4f4fe | ||
|
|
f81d766584 | ||
|
|
ef592606a0 | ||
|
|
4e322fb501 | ||
|
|
b1e48a46c7 | ||
|
|
83700461fc | ||
|
|
853181e36f | ||
|
|
e7e7648c55 | ||
|
|
20643c933e | ||
|
|
0227582684 | ||
|
|
c4db0f9a60 | ||
|
|
5479d32aa3 | ||
|
|
6aabe72fce | ||
|
|
ee110a8e02 | ||
|
|
65b6e1c2bb | ||
|
|
826da4b19e | ||
|
|
630ac1b318 | ||
|
|
23589cc2af | ||
|
|
b349351e6e | ||
|
|
7bddd14419 | ||
|
|
894e21acbf | ||
|
|
524d672307 | ||
|
|
d051588789 | ||
|
|
ea807fcdcc | ||
|
|
a7f0af939b | ||
|
|
4f926df7cf | ||
|
|
1dad3f0975 | ||
|
|
885a9db52e | ||
|
|
8d40ead9b7 | ||
|
|
86bcb47b7d | ||
|
|
f08594cc22 | ||
|
|
56b6fa90b3 | ||
|
|
292d950465 | ||
|
|
ee431f755c | ||
|
|
e00d0b98de | ||
|
|
a8b9741866 | ||
|
|
7855031ecc | ||
|
|
e12d57e6f2 | ||
|
|
0d2ae19c80 | ||
|
|
804cf6d71c | ||
|
|
e342b68f0a | ||
|
|
022805b56b | ||
|
|
eb57d4b510 | ||
|
|
a5a51ba76a | ||
|
|
32efef71f4 | ||
|
|
98c08b2b66 | ||
|
|
5ea34b2efb | ||
|
|
0f3d5c80e4 | ||
|
|
cabaccb9fd | ||
|
|
4d1fe6678f | ||
|
|
04af0e6648 | ||
|
|
7a696ef616 | ||
|
|
691c11d520 | ||
|
|
109ffdaec5 | ||
|
|
aac1b7dc24 | ||
|
|
96782d9584 | ||
|
|
b17ea88bf7 | ||
|
|
091964c2c4 | ||
|
|
38973a80c3 | ||
|
|
4a7b22cf23 | ||
|
|
21a544f188 | ||
|
|
43f44b88ce | ||
|
|
0086470fe1 | ||
|
|
6a3eaf8ba0 | ||
|
|
26e801d2f2 | ||
|
|
d5d908610d | ||
|
|
8a22efb213 | ||
|
|
8bd989abb1 | ||
|
|
a3f59f2f4f | ||
|
|
a4ee1c2e72 | ||
|
|
39fe903498 | ||
|
|
78abd4193a | ||
|
|
96926fecce | ||
|
|
7d852c7bab | ||
|
|
d36a5a9c2f | ||
|
|
f81cc75925 | ||
|
|
ab39586960 | ||
|
|
17503ae9d8 | ||
|
|
24988df444 | ||
|
|
11ce0128ad | ||
|
|
4c0ab89c87 | ||
|
|
50cc1968e4 | ||
|
|
51ba40d39b | ||
|
|
20184e9835 | ||
|
|
8e3b7b195e | ||
|
|
1503b4c834 |
42
Makefile
42
Makefile
@@ -188,8 +188,14 @@ lint-kustomize: install-tools $(builtinplugins)
|
||||
cd pluginator; \
|
||||
$(MYGOBIN)/golangci-lint-kustomize -c ../.golangci-kustomize.yml run ./...
|
||||
|
||||
# Used to add non-default compilation flags when experimenting with
|
||||
# plugin-to-api compatibility checks.
|
||||
.PHONY: build-kustomize-api
|
||||
build-kustomize-api: $(builtinplugins)
|
||||
cd api; go build ./...
|
||||
|
||||
.PHONY: test-unit-kustomize-api
|
||||
test-unit-kustomize-api: $(builtinplugins)
|
||||
test-unit-kustomize-api: build-kustomize-api
|
||||
cd api; go test ./...
|
||||
|
||||
.PHONY: test-unit-kustomize-plugins
|
||||
@@ -255,18 +261,31 @@ $(MYGOBIN)/kubeval:
|
||||
)
|
||||
|
||||
# linux only.
|
||||
# This is for testing an example plugin that
|
||||
# uses helm to inflate a chart for subsequent kustomization.
|
||||
# Don't want to add a hard dependence in go.mod file
|
||||
# to helm.
|
||||
# Instead, download the binary.
|
||||
$(MYGOBIN)/helm:
|
||||
# This is for testing an example plugin that uses helm to inflate a chart
|
||||
# for subsequent kustomization.
|
||||
# Don't want to add a hard dependence in go.mod file to helm.
|
||||
# Instead, download the binaries.
|
||||
$(MYGOBIN)/helmV2:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
wget https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz; \
|
||||
tar -xvzf helm-v2.13.1-linux-amd64.tar.gz; \
|
||||
mv linux-amd64/helm $(MYGOBIN); \
|
||||
tgzFile=helm-v2.13.1-linux-amd64.tar.gz; \
|
||||
wget https://storage.googleapis.com/kubernetes-helm/$$tgzFile; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv linux-amd64/helm $(MYGOBIN)/helmV2; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
|
||||
# Helm V3 differs from helm V2; downloading it to provide coverage for the
|
||||
# chart inflator plugin under helm v3.
|
||||
$(MYGOBIN)/helmV3:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
tgzFile=helm-v3.2.0-rc.1-linux-amd64.tar.gz; \
|
||||
wget https://get.helm.sh/$$tgzFile; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv linux-amd64/helm $(MYGOBIN)/helmV3; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
|
||||
@@ -288,6 +307,7 @@ clean: kustomize-external-go-plugin-clean
|
||||
rm -f $(MYGOBIN)/kustomize
|
||||
rm -f $(MYGOBIN)/golangci-lint-kustomize
|
||||
|
||||
# Nuke the site from orbit. It's the only way to be sure.
|
||||
.PHONY: nuke
|
||||
nuke: clean
|
||||
sudo rm -rf $(shell go env GOPATH)/pkg/mod/sigs.k8s.io
|
||||
go clean --modcache
|
||||
|
||||
@@ -9,3 +9,4 @@ aliases:
|
||||
- mengqiy
|
||||
- monopole
|
||||
- pwittrock
|
||||
- mortent
|
||||
|
||||
@@ -12,8 +12,7 @@ and it's like [`sed`], in that it emits edited text.
|
||||
This tool is sponsored by [sig-cli] ([KEP]), and
|
||||
inspired by [DAM].
|
||||
|
||||
|
||||
[](https://travis-ci.org/kubernetes-sigs/kustomize)
|
||||
[](https://prow.k8s.io/job-history/kubernetes-jenkins/pr-logs/directory/kustomize-presubmit-master)
|
||||
[](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
|
||||
|
||||
Download a binary from the [release page], or see
|
||||
|
||||
@@ -5,10 +5,10 @@ package builtins
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/filters/annotations"
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/transform"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ type AnnotationsTransformerPlugin struct {
|
||||
|
||||
// YAMLSupport can be set to true to use the kyaml filter instead of the
|
||||
// kunstruct transformer
|
||||
YAMLSupport bool
|
||||
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
|
||||
}
|
||||
|
||||
func (p *AnnotationsTransformerPlugin) Config(
|
||||
|
||||
@@ -13,13 +13,10 @@ import (
|
||||
type ConfigMapGeneratorPlugin struct {
|
||||
h *resmap.PluginHelpers
|
||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
types.GeneratorOptions
|
||||
types.ConfigMapArgs
|
||||
}
|
||||
|
||||
func (p *ConfigMapGeneratorPlugin) Config(
|
||||
h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
p.GeneratorOptions = types.GeneratorOptions{}
|
||||
func (p *ConfigMapGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
p.ConfigMapArgs = types.ConfigMapArgs{}
|
||||
err = yaml.Unmarshal(config, p)
|
||||
if p.ConfigMapArgs.Name == "" {
|
||||
@@ -34,8 +31,7 @@ func (p *ConfigMapGeneratorPlugin) Config(
|
||||
|
||||
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||
return p.h.ResmapFactory().FromConfigMapArgs(
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()),
|
||||
&p.GeneratorOptions, p.ConfigMapArgs)
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), p.ConfigMapArgs)
|
||||
}
|
||||
|
||||
func NewConfigMapGeneratorPlugin() resmap.GeneratorPlugin {
|
||||
|
||||
@@ -55,26 +55,23 @@ func (p *InventoryTransformerPlugin) Config(
|
||||
// (e.g. some App object) become more desirable
|
||||
// for this purpose.
|
||||
func (p *InventoryTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
|
||||
inv, h, err := makeInventory(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := types.ConfigMapArgs{}
|
||||
args.Name = p.Name
|
||||
args.Namespace = p.Namespace
|
||||
opts := &types.GeneratorOptions{
|
||||
Annotations: make(map[string]string),
|
||||
args.Options = &types.GeneratorOptions{
|
||||
Annotations: map[string]string{inventory.HashAnnotation: h},
|
||||
}
|
||||
opts.Annotations[inventory.HashAnnotation] = h
|
||||
err = inv.UpdateAnnotations(opts.Annotations)
|
||||
err = inv.UpdateAnnotations(args.Options.Annotations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cm, err := p.h.ResmapFactory().RF().MakeConfigMap(
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), opts, &args)
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), &args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,14 +6,13 @@ package builtins
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/api/filters/namespace"
|
||||
"sigs.k8s.io/kustomize/api/transform"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/transform"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -23,8 +22,9 @@ type NamespaceTransformerPlugin struct {
|
||||
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
|
||||
YAMLSupport bool
|
||||
// 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(
|
||||
|
||||
@@ -8,10 +8,12 @@ import (
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -21,6 +23,8 @@ type PatchJson6902TransformerPlugin struct {
|
||||
Target types.PatchTarget `json:"target,omitempty" yaml:"target,omitempty"`
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"`
|
||||
|
||||
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PatchJson6902TransformerPlugin) Config(
|
||||
@@ -83,16 +87,22 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawObj, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
if !p.YAMLSupport {
|
||||
rawObj, err := obj.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modifiedObj, err := p.decodedPatch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
err, "failed to apply json patch '%s'", p.JsonOp)
|
||||
}
|
||||
return obj.UnmarshalJSON(modifiedObj)
|
||||
} else {
|
||||
return filtersutil.ApplyToJSON(patchjson6902.Filter{
|
||||
Patch: p.JsonOp,
|
||||
}, obj.Kunstructured)
|
||||
}
|
||||
modifiedObj, err := p.decodedPatch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
err, "failed to apply json patch '%s'", p.JsonOp)
|
||||
}
|
||||
return obj.UnmarshalJSON(modifiedObj)
|
||||
}
|
||||
|
||||
func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin {
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -17,6 +22,8 @@ type PatchStrategicMergeTransformerPlugin struct {
|
||||
loadedPatches []*resource.Resource
|
||||
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
|
||||
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
|
||||
|
||||
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PatchStrategicMergeTransformerPlugin) Config(
|
||||
@@ -69,22 +76,42 @@ func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = target.Patch(patch.Kunstructured)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove the resource from resmap
|
||||
// when the patch is to $patch: delete that target
|
||||
if len(target.Map()) == 0 {
|
||||
err = m.Remove(target.CurId())
|
||||
if !p.YAMLSupport {
|
||||
err = target.Patch(patch.Kunstructured)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// remove the resource from resmap
|
||||
// when the patch is to $patch: delete that target
|
||||
if len(target.Map()) == 0 {
|
||||
err = m.Remove(target.CurId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node, err := getRNode(patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
|
||||
Patch: node,
|
||||
}, target.Kunstructured)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO: Remove this once the next version of kyaml is released which
|
||||
// exposes GetRNode from the filutersutil package.
|
||||
func getRNode(k json.Marshaler) (*kyaml.RNode, error) {
|
||||
j, err := k.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kyaml.Parse(string(j))
|
||||
}
|
||||
|
||||
func NewPatchStrategicMergeTransformerPlugin() resmap.TransformerPlugin {
|
||||
return &PatchStrategicMergeTransformerPlugin{}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,16 @@ package builtins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
||||
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filtersutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -20,69 +24,112 @@ type PatchTransformerPlugin struct {
|
||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
|
||||
|
||||
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
|
||||
}
|
||||
|
||||
func (p *PatchTransformerPlugin) Config(
|
||||
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||
err = yaml.Unmarshal(c, p)
|
||||
h *resmap.PluginHelpers, c []byte) error {
|
||||
err := yaml.Unmarshal(c, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.Patch = strings.TrimSpace(p.Patch)
|
||||
if p.Patch == "" && p.Path == "" {
|
||||
err = fmt.Errorf(
|
||||
return fmt.Errorf(
|
||||
"must specify one of patch and path in\n%s", string(c))
|
||||
return
|
||||
}
|
||||
if p.Patch != "" && p.Path != "" {
|
||||
err = fmt.Errorf(
|
||||
return fmt.Errorf(
|
||||
"patch and path can't be set at the same time\n%s", string(c))
|
||||
return
|
||||
}
|
||||
var in []byte
|
||||
if p.Path != "" {
|
||||
in, err = h.Loader().Load(p.Path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.Patch != "" {
|
||||
in = []byte(p.Patch)
|
||||
}
|
||||
|
||||
patchSM, errSM := h.ResmapFactory().RF().FromBytes(in)
|
||||
patchJson, errJson := jsonPatchFromBytes(in)
|
||||
if p.Path != "" {
|
||||
loaded, loadErr := h.Loader().Load(p.Path)
|
||||
if loadErr != nil {
|
||||
return loadErr
|
||||
}
|
||||
p.Patch = string(loaded)
|
||||
}
|
||||
|
||||
patchSM, errSM := h.ResmapFactory().RF().FromBytes([]byte(p.Patch))
|
||||
patchJson, errJson := jsonPatchFromBytes([]byte(p.Patch))
|
||||
if (errSM == nil && errJson == nil) ||
|
||||
(patchSM != nil && patchJson != nil) {
|
||||
return fmt.Errorf(
|
||||
"illegally qualifies as both an SM and JSON patch: [%v]",
|
||||
p.Patch)
|
||||
}
|
||||
if errSM != nil && errJson != nil {
|
||||
err = fmt.Errorf(
|
||||
"unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch)
|
||||
return
|
||||
return fmt.Errorf(
|
||||
"unable to parse SM or JSON patch from [%v]", p.Patch)
|
||||
}
|
||||
if errSM == nil && errJson != nil {
|
||||
if errSM == nil {
|
||||
p.loadedPatch = patchSM
|
||||
}
|
||||
if errJson == nil && errSM != nil {
|
||||
} else {
|
||||
p.decodedPatch = patchJson
|
||||
}
|
||||
if patchSM != nil && patchJson != nil {
|
||||
err = fmt.Errorf(
|
||||
"a patch can't be both a Strategic Merge Patch and JSON patch 6902 %s", p.Patch)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
if p.loadedPatch != nil && p.Target == nil {
|
||||
target, err := m.GetById(p.loadedPatch.OrgId())
|
||||
if p.loadedPatch != nil {
|
||||
// The patch was a strategic merge patch
|
||||
return p.transformStrategicMerge(m, p.loadedPatch)
|
||||
} else {
|
||||
return p.transformJson6902(m, p.decodedPatch)
|
||||
}
|
||||
}
|
||||
|
||||
// transformStrategicMerge applies the provided strategic merge patch
|
||||
// to all the resources in the ResMap that match either the Target or
|
||||
// the identifier of the patch.
|
||||
func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap, patch *resource.Resource) error {
|
||||
if p.Target == nil {
|
||||
target, err := m.GetById(patch.OrgId())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = target.Patch(p.loadedPatch.Kunstructured)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return p.applySMPatch(target, patch)
|
||||
}
|
||||
|
||||
resources, err := m.Select(*p.Target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, res := range resources {
|
||||
patchCopy := patch.DeepCopy()
|
||||
patchCopy.SetName(res.GetName())
|
||||
patchCopy.SetNamespace(res.GetNamespace())
|
||||
patchCopy.SetGvk(res.GetGvk())
|
||||
err := p.applySMPatch(res, patchCopy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applySMPatch applies the provided strategic merge patch to the
|
||||
// given resource. Depending on the value of YAMLSupport, it will either
|
||||
// use the legacy implementation or the kyaml-based solution.
|
||||
func (p *PatchTransformerPlugin) applySMPatch(resource, patch *resource.Resource) error {
|
||||
if !p.YAMLSupport {
|
||||
return resource.Patch(patch.Kunstructured)
|
||||
} else {
|
||||
node, err := filtersutil.GetRNode(patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return filtersutil.ApplyToJSON(patchstrategicmerge.Filter{
|
||||
Patch: node,
|
||||
}, resource.Kunstructured)
|
||||
}
|
||||
}
|
||||
|
||||
// transformJson6902 applies the provided json6902 patch
|
||||
// to all the resources in the ResMap that match the Target.
|
||||
func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpatch.Patch) error {
|
||||
if p.Target == nil {
|
||||
return fmt.Errorf("must specify a target for patch %s", p.Patch)
|
||||
}
|
||||
@@ -92,35 +139,36 @@ func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
return err
|
||||
}
|
||||
for _, res := range resources {
|
||||
if p.decodedPatch != nil {
|
||||
rawObj, err := res.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modifiedObj, err := p.decodedPatch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
err, "failed to apply json patch '%s'", p.Patch)
|
||||
}
|
||||
err = res.UnmarshalJSON(modifiedObj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if p.loadedPatch != nil {
|
||||
patchCopy := p.loadedPatch.DeepCopy()
|
||||
patchCopy.SetName(res.GetName())
|
||||
patchCopy.SetNamespace(res.GetNamespace())
|
||||
patchCopy.SetGvk(res.GetGvk())
|
||||
err = res.Patch(patchCopy.Kunstructured)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.applyJson6902Patch(res, patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// applyJson6902Patch applies the provided patch to the given resource.
|
||||
// Depending on the value of YAMLSupport, it will either
|
||||
// use the legacy implementation or the kyaml-based solution.
|
||||
func (p *PatchTransformerPlugin) applyJson6902Patch(resource *resource.Resource, patch jsonpatch.Patch) error {
|
||||
if !p.YAMLSupport {
|
||||
rawObj, err := resource.MarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modifiedObj, err := patch.Apply(rawObj)
|
||||
if err != nil {
|
||||
return errors.Wrapf(
|
||||
err, "failed to apply json patch '%s'", p.Patch)
|
||||
}
|
||||
return resource.UnmarshalJSON(modifiedObj)
|
||||
} else {
|
||||
return filtersutil.ApplyToJSON(patchjson6902.Filter{
|
||||
Patch: p.Patch,
|
||||
}, resource.Kunstructured)
|
||||
}
|
||||
}
|
||||
|
||||
// jsonPatchFromBytes loads a Json 6902 patch from
|
||||
// a bytes input
|
||||
func jsonPatchFromBytes(
|
||||
|
||||
@@ -13,12 +13,10 @@ import (
|
||||
type SecretGeneratorPlugin struct {
|
||||
h *resmap.PluginHelpers
|
||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
types.GeneratorOptions
|
||||
types.SecretArgs
|
||||
}
|
||||
|
||||
func (p *SecretGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
|
||||
p.GeneratorOptions = types.GeneratorOptions{}
|
||||
p.SecretArgs = types.SecretArgs{}
|
||||
err = yaml.Unmarshal(config, p)
|
||||
if p.SecretArgs.Name == "" {
|
||||
@@ -33,8 +31,7 @@ func (p *SecretGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (
|
||||
|
||||
func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||
return p.h.ResmapFactory().FromSecretArgs(
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()),
|
||||
&p.GeneratorOptions, p.SecretArgs)
|
||||
kv.NewLoader(p.h.Loader(), p.h.Validator()), p.SecretArgs)
|
||||
}
|
||||
|
||||
func NewSecretGeneratorPlugin() resmap.GeneratorPlugin {
|
||||
|
||||
@@ -4,11 +4,7 @@
|
||||
package filtersutil
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// SortedMapKeys returns a sorted slice of keys to the given map.
|
||||
@@ -23,69 +19,3 @@ func SortedMapKeys(m map[string]string) []string {
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
// ApplyToJSON applies the filter to the json objects.
|
||||
func ApplyToJSON(filter kio.Filter, objs ...marshalerUnmarshaler) error {
|
||||
var nodes []*yaml.RNode
|
||||
for i := range objs {
|
||||
node, err := getRNode(objs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
l, err := filter.Filter([]*yaml.RNode{node})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = setRNode(objs[i], l[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err := filter.Filter(nodes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range nodes {
|
||||
err = setRNode(objs[i], nodes[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type marshalerUnmarshaler interface {
|
||||
json.Unmarshaler
|
||||
json.Marshaler
|
||||
}
|
||||
|
||||
// getRNode converts k into an RNode
|
||||
func getRNode(k json.Marshaler) (*yaml.RNode, error) {
|
||||
j, err := k.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return yaml.Parse(string(j))
|
||||
}
|
||||
|
||||
// setRNode marshals node into k
|
||||
func setRNode(k json.Unmarshaler, node *yaml.RNode) error {
|
||||
s, err := node.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m := map[string]interface{}{}
|
||||
if err := yaml.Unmarshal([]byte(s), &m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return k.UnmarshalJSON(b)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package filtersutil_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestSortedKeys(t *testing.T) {
|
||||
@@ -36,51 +32,3 @@ func TestSortedKeys(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyToJSON(t *testing.T) {
|
||||
instance1 := bytes.NewBufferString(`{"kind": "Foo"}`)
|
||||
instance2 := bytes.NewBufferString(`{"kind": "Bar"}`)
|
||||
err := filtersutil.ApplyToJSON(
|
||||
kio.FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for i := range nodes {
|
||||
set := yaml.SetField(
|
||||
"foo", yaml.NewScalarRNode("bar"))
|
||||
node := nodes[i]
|
||||
err := node.PipeE(set)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}), buffer{Buffer: instance1}, buffer{Buffer: instance2},
|
||||
)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(`{"foo":"bar","kind":"Foo"}`),
|
||||
strings.TrimSpace(instance1.String())) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(`{"foo":"bar","kind":"Bar"}`),
|
||||
strings.TrimSpace(instance2.String())) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
type buffer struct {
|
||||
*bytes.Buffer
|
||||
}
|
||||
|
||||
func (buff buffer) UnmarshalJSON(b []byte) error {
|
||||
buff.Reset()
|
||||
buff.Write(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (buff buffer) MarshalJSON() ([]byte, error) {
|
||||
return buff.Bytes(), nil
|
||||
}
|
||||
|
||||
12
api/filters/imagetag/doc.go
Normal file
12
api/filters/imagetag/doc.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package imagetag contains two kio.Filter implementations to cover the
|
||||
// functionality of the kustomize imagetag transformer.
|
||||
//
|
||||
// Filter updates fields based on a FieldSpec and an ImageTag.
|
||||
//
|
||||
// LegacyFilter doesn't use a FieldSpec, and instead only updates image
|
||||
// references if the field is name image and it is underneath a field called
|
||||
// either containers or initContainers.
|
||||
package imagetag
|
||||
126
api/filters/imagetag/example_test.go
Normal file
126
api/filters/imagetag/example_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- name: FooBar
|
||||
image: nginx
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- name: BarFoo
|
||||
image: nginx:1.2.1
|
||||
`)}},
|
||||
Filters: []kio.Filter{Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
Digest: "12345",
|
||||
},
|
||||
FsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/containers[]/image",
|
||||
},
|
||||
},
|
||||
}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: FooBar
|
||||
// image: apache@12345
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: BarFoo
|
||||
// image: apache@12345
|
||||
}
|
||||
|
||||
func ExampleLegacyFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- name: FooBar
|
||||
image: nginx
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- name: BarFoo
|
||||
image: nginx:1.2.1
|
||||
`)}},
|
||||
Filters: []kio.Filter{LegacyFilter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
Digest: "12345",
|
||||
},
|
||||
}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: FooBar
|
||||
// image: apache@12345
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// containers:
|
||||
// - name: BarFoo
|
||||
// image: apache@12345
|
||||
}
|
||||
44
api/filters/imagetag/imagetag.go
Normal file
44
api/filters/imagetag/imagetag.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
// imageTag is the tag we want to apply to the inputs
|
||||
ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
|
||||
|
||||
// FsSlice contains the FieldSpecs to locate the namespace field
|
||||
FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
_, err := kio.FilterAll(yaml.FilterFunc(f.filter)).Filter(nodes)
|
||||
return nodes, err
|
||||
}
|
||||
|
||||
func (f Filter) filter(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
if err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: updateImageTagFn(f.ImageTag),
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func updateImageTagFn(imageTag types.Image) fsslice.SetFn {
|
||||
return func(node *yaml.RNode) error {
|
||||
return node.PipeE(imageTagUpdater{
|
||||
ImageTag: imageTag,
|
||||
})
|
||||
}
|
||||
}
|
||||
101
api/filters/imagetag/imagetag_test.go
Normal file
101
api/filters/imagetag/imagetag_test.go
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestImageTagUpdater_Filter(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
filter Filter
|
||||
fsSlice types.FsSlice
|
||||
}{
|
||||
"update with digest": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: nginx:1.2.1
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: apache@12345
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
Digest: "12345",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/image",
|
||||
},
|
||||
},
|
||||
},
|
||||
"multiple matches in sequence": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.2.1
|
||||
- image: not_nginx@54321
|
||||
- image: nginx:1.2.1
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: apache:3.2.1
|
||||
- image: not_nginx@54321
|
||||
- image: apache:3.2.1
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
NewTag: "3.2.1",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/containers/image",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
filter := tc.filter
|
||||
filter.FsSlice = tc.fsSlice
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
113
api/filters/imagetag/legacy.go
Normal file
113
api/filters/imagetag/legacy.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// LegacyFilter is an implementation of the kio.Filter interface
|
||||
// that scans through the provided kyaml data structure and updates
|
||||
// any values of any image fields that is inside a sequence under
|
||||
// a field called either containers or initContainers. The field is only
|
||||
// update if it has a value that matches and image reference and the name
|
||||
// of the image is a match with the provided ImageTag.
|
||||
type LegacyFilter struct {
|
||||
ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = LegacyFilter{}
|
||||
|
||||
func (lf LegacyFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(lf.filter)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (lf LegacyFilter) filter(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
meta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We do not make any changes if the type of the resource
|
||||
// is CustomResourceDefinition.
|
||||
if meta.Kind == `CustomResourceDefinition` {
|
||||
return node, nil
|
||||
}
|
||||
|
||||
fff := findFieldsFilter{
|
||||
fields: []string{"containers", "initContainers"},
|
||||
fieldCallback: checkImageTagsFn(lf.ImageTag),
|
||||
}
|
||||
if err := node.PipeE(fff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
type fieldCallback func(node *yaml.RNode) error
|
||||
|
||||
// findFieldsFilter is an implementation of the kio.Filter
|
||||
// interface. It will walk the data structure and look for fields
|
||||
// that matches the provided list of field names. For each match,
|
||||
// the value of the field will be passed in as a parameter to the
|
||||
// provided fieldCallback.
|
||||
// TODO: move this to kyaml/filterutils
|
||||
type findFieldsFilter struct {
|
||||
fields []string
|
||||
|
||||
fieldCallback fieldCallback
|
||||
}
|
||||
|
||||
func (f findFieldsFilter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
||||
return obj, f.walk(obj)
|
||||
}
|
||||
|
||||
func (f findFieldsFilter) walk(node *yaml.RNode) error {
|
||||
switch node.YNode().Kind {
|
||||
case yaml.MappingNode:
|
||||
return node.VisitFields(func(n *yaml.MapNode) error {
|
||||
err := f.walk(n.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := n.Key.YNode().Value
|
||||
if contains(f.fields, key) {
|
||||
return f.fieldCallback(n.Value)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
case yaml.SequenceNode:
|
||||
return node.VisitElements(func(n *yaml.RNode) error {
|
||||
return f.walk(n)
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(slice []string, str string) bool {
|
||||
for _, s := range slice {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkImageTagsFn(imageTag types.Image) fieldCallback {
|
||||
return func(node *yaml.RNode) error {
|
||||
if node.YNode().Kind != yaml.SequenceNode {
|
||||
return nil
|
||||
}
|
||||
|
||||
return node.VisitElements(func(n *yaml.RNode) error {
|
||||
// Look up any fields on the provided node that is named
|
||||
// image.
|
||||
return n.PipeE(yaml.Get("image"), imageTagUpdater{
|
||||
ImageTag: imageTag,
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
136
api/filters/imagetag/legacy_test.go
Normal file
136
api/filters/imagetag/legacy_test.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestLegacyImageTag_Filter(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
filter LegacyFilter
|
||||
}{
|
||||
"updates multiple images inside containers": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.2.1
|
||||
- image: nginx:2.1.2
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: apache@12345
|
||||
- image: apache@12345
|
||||
`,
|
||||
filter: LegacyFilter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
Digest: "12345",
|
||||
},
|
||||
},
|
||||
},
|
||||
"updates inside both containers and initContainers": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.2.1
|
||||
- image: tomcat:1.2.3
|
||||
initContainers:
|
||||
- image: nginx:1.2.1
|
||||
- image: apache:1.2.3
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: apache:3.2.1
|
||||
- image: tomcat:1.2.3
|
||||
initContainers:
|
||||
- image: apache:3.2.1
|
||||
- image: apache:1.2.3
|
||||
`,
|
||||
filter: LegacyFilter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
NewTag: "3.2.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
"updates on multiple depths": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.2.1
|
||||
- image: tomcat:1.2.3
|
||||
template:
|
||||
spec:
|
||||
initContainers:
|
||||
- image: nginx:1.2.1
|
||||
- image: apache:1.2.3
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
containers:
|
||||
- image: apache:3.2.1
|
||||
- image: tomcat:1.2.3
|
||||
template:
|
||||
spec:
|
||||
initContainers:
|
||||
- image: apache:3.2.1
|
||||
- image: apache:1.2.3
|
||||
`,
|
||||
filter: LegacyFilter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
NewTag: "3.2.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
filter := tc.filter
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
43
api/filters/imagetag/updater.go
Normal file
43
api/filters/imagetag/updater.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/image"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// imageTagUpdater is an implementation of the kio.Filter interface
|
||||
// that will update the value of the yaml node based on the provided
|
||||
// ImageTag if the current value matches the format of an image reference.
|
||||
type imageTagUpdater struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
ImageTag types.Image `yaml:"imageTag,omitempty"`
|
||||
}
|
||||
|
||||
func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) {
|
||||
if err := yaml.ErrorIfInvalid(rn, yaml.ScalarNode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value := rn.YNode().Value
|
||||
|
||||
if !image.IsImageMatched(value, u.ImageTag.Name) {
|
||||
return rn, nil
|
||||
}
|
||||
|
||||
name, tag := image.Split(value)
|
||||
if u.ImageTag.NewName != "" {
|
||||
name = u.ImageTag.NewName
|
||||
}
|
||||
if u.ImageTag.NewTag != "" {
|
||||
tag = ":" + u.ImageTag.NewTag
|
||||
}
|
||||
if u.ImageTag.Digest != "" {
|
||||
tag = "@" + u.ImageTag.Digest
|
||||
}
|
||||
|
||||
return rn.Pipe(yaml.FieldSetter{StringValue: name + tag})
|
||||
}
|
||||
@@ -15,7 +15,7 @@ type Filter struct {
|
||||
Namespace string `yaml:"namespace,omitempty"`
|
||||
|
||||
// FsSlice contains the FieldSpecs to locate the namespace field
|
||||
FsSlice types.FsSlice
|
||||
FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
6
api/filters/patchjson6902/doc.go
Normal file
6
api/filters/patchjson6902/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package namespace contains a kio.Filter implementation of the kustomize
|
||||
// patchjson6902 transformer
|
||||
package patchjson6902
|
||||
55
api/filters/patchjson6902/example_test.go
Normal file
55
api/filters/patchjson6902/example_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchjson6902
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
namespace: bar
|
||||
`)}},
|
||||
Filters: []kio.Filter{
|
||||
Filter{
|
||||
Patch: `
|
||||
- op: replace
|
||||
path: /metadata/namespace
|
||||
value: "ns"
|
||||
`,
|
||||
},
|
||||
},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// namespace: ns
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: instance
|
||||
// namespace: ns
|
||||
}
|
||||
65
api/filters/patchjson6902/patchjson6902.go
Normal file
65
api/filters/patchjson6902/patchjson6902.go
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchjson6902
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
k8syaml "sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
Patch string
|
||||
|
||||
decodedPatch jsonpatch.Patch
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
decodedPatch, err := pf.decodePatch()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pf.decodedPatch = decodedPatch
|
||||
return kio.FilterAll(yaml.FilterFunc(pf.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (pf Filter) decodePatch() (jsonpatch.Patch, error) {
|
||||
patch := pf.Patch
|
||||
// If the patch doesn't look like a JSON6902 patch, we
|
||||
// try to parse it to json.
|
||||
if !strings.HasPrefix(pf.Patch, "[") {
|
||||
p, err := k8syaml.YAMLToJSON([]byte(patch))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patch = string(p)
|
||||
}
|
||||
decodedPatch, err := jsonpatch.DecodePatch([]byte(patch))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decodedPatch, nil
|
||||
}
|
||||
|
||||
func (pf Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
// We don't actually use the kyaml library for manipulating the
|
||||
// yaml here. We just marshal it to json and rely on the
|
||||
// jsonpatch library to take care of applying the patch.
|
||||
// This means ordering might not be preserved with this filter.
|
||||
b, err := node.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := pf.decodedPatch.Apply(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = node.UnmarshalJSON(res)
|
||||
return node, err
|
||||
}
|
||||
173
api/filters/patchjson6902/patchjson6902_test.go
Normal file
173
api/filters/patchjson6902/patchjson6902_test.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchjson6902
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
)
|
||||
|
||||
const input = `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 2
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
`
|
||||
|
||||
func TestSomething(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
input string
|
||||
filter Filter
|
||||
expectedOutput string
|
||||
}{
|
||||
{
|
||||
testName: "single operation, json",
|
||||
input: input,
|
||||
filter: Filter{
|
||||
Patch: `[
|
||||
{"op": "replace", "path": "/spec/replica", "value": 5}
|
||||
]`,
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 5
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
{
|
||||
testName: "multiple operations, json",
|
||||
input: input,
|
||||
filter: Filter{
|
||||
Patch: `[
|
||||
{"op": "replace", "path": "/spec/template/spec/containers/0/name", "value": "my-nginx"},
|
||||
{"op": "add", "path": "/spec/replica", "value": 999},
|
||||
{"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["arg1", "arg2", "arg3"]}
|
||||
]`,
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 999
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- arg1
|
||||
- arg2
|
||||
- arg3
|
||||
image: nginx
|
||||
name: my-nginx
|
||||
`,
|
||||
},
|
||||
{
|
||||
testName: "single operation, yaml",
|
||||
input: input,
|
||||
filter: Filter{
|
||||
Patch: `
|
||||
- op: replace
|
||||
path: /spec/replica
|
||||
value: 5
|
||||
`,
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 5
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
`,
|
||||
},
|
||||
{
|
||||
testName: "multiple operations, yaml",
|
||||
input: input,
|
||||
filter: Filter{
|
||||
Patch: `
|
||||
- op: replace
|
||||
path: /spec/template/spec/containers/0/name
|
||||
value: my-nginx
|
||||
- op: add
|
||||
path: /spec/replica
|
||||
value: 999
|
||||
- op: add
|
||||
path: /spec/template/spec/containers/0/command
|
||||
value:
|
||||
- arg1
|
||||
- arg2
|
||||
- arg3
|
||||
`,
|
||||
},
|
||||
expectedOutput: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: myDeploy
|
||||
spec:
|
||||
replica: 999
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
old-label: old-value
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- arg1
|
||||
- arg2
|
||||
- arg3
|
||||
image: nginx
|
||||
name: my-nginx
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(
|
||||
filtertest.RunFilter(t, tc.input, tc.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
6
api/filters/patchstrategicmerge/doc.go
Normal file
6
api/filters/patchstrategicmerge/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package patchstrategicmerge contains a kio.Filter implementation of the
|
||||
// kustomize strategic merge patch transformer.
|
||||
package patchstrategicmerge
|
||||
49
api/filters/patchstrategicmerge/example_test.go
Normal file
49
api/filters/patchstrategicmerge/example_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchstrategicmerge
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
replicas: 3
|
||||
`)}},
|
||||
Filters: []kio.Filter{Filter{
|
||||
Patch: yaml.MustParse(`
|
||||
spec:
|
||||
template:
|
||||
containers:
|
||||
- image: nginx
|
||||
`),
|
||||
}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// replicas: 3
|
||||
// template:
|
||||
// containers:
|
||||
// - image: nginx
|
||||
}
|
||||
21
api/filters/patchstrategicmerge/patchstrategicmerge.go
Normal file
21
api/filters/patchstrategicmerge/patchstrategicmerge.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package patchstrategicmerge
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
Patch *yaml.RNode
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(pf.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (pf Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
return merge2.Merge(pf.Patch, node)
|
||||
}
|
||||
82
api/filters/patchstrategicmerge/patchstrategicmerge_test.go
Normal file
82
api/filters/patchstrategicmerge/patchstrategicmerge_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package patchstrategicmerge
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
patch *yaml.RNode
|
||||
expected string
|
||||
}{
|
||||
"simple patch": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: myDeploy
|
||||
kind: Deployment
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
metadata:
|
||||
name: yourDeploy
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: yourDeploy
|
||||
kind: Deployment
|
||||
`,
|
||||
},
|
||||
"nested patch": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: myDeploy
|
||||
kind: Deployment
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
args:
|
||||
- abc
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
args:
|
||||
- def
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: myDeploy
|
||||
kind: Deployment
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
args:
|
||||
- def
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
f := Filter{
|
||||
Patch: tc.patch,
|
||||
}
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest.RunFilter(t, tc.input, f))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
6
api/filters/prefixsuffix/doc.go
Normal file
6
api/filters/prefixsuffix/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package prefixsuffix contains a kio.Filter implementation of the kustomize
|
||||
// PrefixSuffixTransformer.
|
||||
package prefixsuffix
|
||||
47
api/filters/prefixsuffix/example_test.go
Normal file
47
api/filters/prefixsuffix/example_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefixsuffix_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
fss := builtinconfig.MakeDefaultConfig().NamePrefix
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`)}},
|
||||
Filters: []kio.Filter{prefixsuffix.Filter{Prefix: "baz-", FsSlice: fss}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: baz-instance
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: baz-instance
|
||||
}
|
||||
43
api/filters/prefixsuffix/prefixsuffix.go
Normal file
43
api/filters/prefixsuffix/prefixsuffix.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefixsuffix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filter applies resource name prefix's and suffix's using the fieldSpecs
|
||||
type Filter struct {
|
||||
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
||||
|
||||
FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(ns.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
// Run runs the filter on a single node rather than a slice
|
||||
func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
// transformations based on data -- :)
|
||||
err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: ns.FsSlice,
|
||||
SetValue: ns.set,
|
||||
CreateKind: yaml.ScalarNode, // Name is a ScalarNode
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
func (ns Filter) set(node *yaml.RNode) error {
|
||||
return fsslice.SetScalar(fmt.Sprintf(
|
||||
"%s%s%s", ns.Prefix, node.YNode().Value, ns.Suffix))(node)
|
||||
}
|
||||
167
api/filters/prefixsuffix/prefixsuffix_test.go
Normal file
167
api/filters/prefixsuffix/prefixsuffix_test.go
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefixsuffix_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
var tests = []TestCase{
|
||||
{
|
||||
name: "prefix",
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo-instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: foo-instance
|
||||
`,
|
||||
filter: prefixsuffix.Filter{Prefix: "foo-"},
|
||||
},
|
||||
|
||||
{
|
||||
name: "suffix",
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance-foo
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance-foo
|
||||
`,
|
||||
filter: prefixsuffix.Filter{Suffix: "-foo"},
|
||||
},
|
||||
|
||||
{
|
||||
name: "prefix-suffix",
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: bar-instance-foo
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: bar-instance-foo
|
||||
`,
|
||||
filter: prefixsuffix.Filter{Prefix: "bar-", Suffix: "-foo"},
|
||||
},
|
||||
|
||||
{
|
||||
name: "data-fieldspecs",
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo-instance
|
||||
a:
|
||||
b:
|
||||
c: foo-d
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: foo-instance
|
||||
a:
|
||||
b:
|
||||
c: foo-d
|
||||
`,
|
||||
filter: prefixsuffix.Filter{Prefix: "foo-"},
|
||||
fsslice: []types.FieldSpec{
|
||||
{
|
||||
Path: "a/b/c",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
filter prefixsuffix.Filter
|
||||
fsslice types.FsSlice
|
||||
}
|
||||
|
||||
var config = builtinconfig.MakeDefaultConfig()
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
for i := range tests {
|
||||
test := tests[i]
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.filter.FsSlice = append(config.NamePrefix, test.fsslice...)
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest_test.RunFilter(t, test.input, test.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
6
api/filters/replicacount/doc.go
Normal file
6
api/filters/replicacount/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package replicacount contains a kio.Filter implementation of the kustomize
|
||||
// ReplicaCountTransformer.
|
||||
package replicacount
|
||||
64
api/filters/replicacount/example_test.go
Normal file
64
api/filters/replicacount/example_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package replicacount
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
template:
|
||||
replicas: 5
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
template:
|
||||
replicas: 5
|
||||
`)}},
|
||||
Filters: []kio.Filter{Filter{
|
||||
Replica: types.Replica{
|
||||
Count: 42,
|
||||
Name: "instance",
|
||||
},
|
||||
FsSlice: types.FsSlice{
|
||||
{
|
||||
Path: "spec/template/replicas",
|
||||
},
|
||||
},
|
||||
}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// template:
|
||||
// replicas: 42
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: instance
|
||||
// spec:
|
||||
// template:
|
||||
// replicas: 42
|
||||
}
|
||||
48
api/filters/replicacount/replicacount.go
Normal file
48
api/filters/replicacount/replicacount.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package replicacount
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filter updates/sets replicas fields using the fieldSpecs
|
||||
type Filter struct {
|
||||
Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"`
|
||||
|
||||
// FsSlice contains the FieldSpecs to locate the namespace field
|
||||
FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (rc Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(rc.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
// run processes each node individually.
|
||||
func (rc Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
meta, err := node.GetMeta()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// only update resources where the name matches the Replica name.
|
||||
if meta.Name != rc.Replica.Name {
|
||||
return node, nil
|
||||
}
|
||||
|
||||
err = node.PipeE(fsslice.Filter{
|
||||
FsSlice: rc.FsSlice,
|
||||
SetValue: rc.set,
|
||||
CreateKind: yaml.ScalarNode, // replicas is a ScalarNode
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
func (rc Filter) set(node *yaml.RNode) error {
|
||||
return fsslice.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node)
|
||||
}
|
||||
164
api/filters/replicacount/replicacount_test.go
Normal file
164
api/filters/replicacount/replicacount_test.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package replicacount
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
var config = builtinconfig.MakeDefaultConfig()
|
||||
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expected string
|
||||
filter Filter
|
||||
fsslice types.FsSlice
|
||||
}{
|
||||
"update field": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 5
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 42
|
||||
`,
|
||||
filter: Filter{
|
||||
Replica: types.Replica{
|
||||
Name: "dep",
|
||||
Count: 42,
|
||||
},
|
||||
},
|
||||
fsslice: types.FsSlice{
|
||||
{
|
||||
Path: "spec/replicas",
|
||||
},
|
||||
},
|
||||
},
|
||||
"add field": {
|
||||
input: `
|
||||
apiVersion: custom/v1
|
||||
kind: Custom
|
||||
metadata:
|
||||
name: cus
|
||||
spec:
|
||||
template:
|
||||
other: something
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: custom/v1
|
||||
kind: Custom
|
||||
metadata:
|
||||
name: cus
|
||||
spec:
|
||||
template:
|
||||
other: something
|
||||
replicas: 42
|
||||
`,
|
||||
filter: Filter{
|
||||
Replica: types.Replica{
|
||||
Name: "cus",
|
||||
Count: 42,
|
||||
},
|
||||
},
|
||||
fsslice: types.FsSlice{
|
||||
{
|
||||
Path: "spec/template/replicas",
|
||||
CreateIfNotPresent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"no update if CreateIfNotPresent is false": {
|
||||
input: `
|
||||
apiVersion: custom/v1
|
||||
kind: Custom
|
||||
metadata:
|
||||
name: cus
|
||||
spec:
|
||||
template:
|
||||
other: something
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: custom/v1
|
||||
kind: Custom
|
||||
metadata:
|
||||
name: cus
|
||||
spec:
|
||||
template:
|
||||
other: something
|
||||
`,
|
||||
filter: Filter{
|
||||
Replica: types.Replica{
|
||||
Name: "cus",
|
||||
Count: 42,
|
||||
},
|
||||
},
|
||||
fsslice: types.FsSlice{
|
||||
{
|
||||
Path: "spec/template/replicas",
|
||||
},
|
||||
},
|
||||
},
|
||||
"update multiple fields": {
|
||||
input: `
|
||||
apiVersion: custom/v1
|
||||
kind: Custom
|
||||
metadata:
|
||||
name: cus
|
||||
spec:
|
||||
replicas: 5
|
||||
template:
|
||||
replicas: 5
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: custom/v1
|
||||
kind: Custom
|
||||
metadata:
|
||||
name: cus
|
||||
spec:
|
||||
replicas: 42
|
||||
template:
|
||||
replicas: 42
|
||||
`,
|
||||
filter: Filter{
|
||||
Replica: types.Replica{
|
||||
Name: "cus",
|
||||
Count: 42,
|
||||
},
|
||||
},
|
||||
fsslice: types.FsSlice{
|
||||
{
|
||||
Path: "spec/template/replicas",
|
||||
},
|
||||
{
|
||||
Path: "spec/replicas",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
tc.filter.FsSlice = append(config.Replicas, tc.fsslice...)
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest_test.RunFilter(t, tc.input, tc.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,6 @@ require (
|
||||
k8s.io/apimachinery v0.17.0
|
||||
k8s.io/client-go v0.17.0
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
19
api/go.sum
19
api/go.sum
@@ -1,6 +1,7 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
@@ -14,6 +15,7 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
@@ -23,6 +25,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
@@ -31,6 +34,9 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ
|
||||
github.com/bombsimon/wsl v1.2.5 h1:9gTOkIwVtoDZywvX802SDHokeX4kW1cKnV8ZTVAPkRs=
|
||||
github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
@@ -47,6 +53,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
@@ -249,6 +256,7 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -263,6 +271,7 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
@@ -281,6 +290,7 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
|
||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@@ -323,6 +333,7 @@ github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -351,6 +362,8 @@ github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7
|
||||
github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
@@ -368,6 +381,7 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -408,6 +422,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -496,6 +511,10 @@ sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbL
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1 h1:nGUNYINljZNmlAS8uoobUv/wx/s3Pg8dNxYo+W7uYh0=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1/go.mod h1:/NdPPfrperSCGjm55cwEro1loBVtbtVIXSb7FguK6uk=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.2 h1:l12+QGl+ETUHhP8/bZAi6TknU7H194fXL/9b2gUxZFY=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3 h1:zbeHVTMCQPtWgjIH/YYJZC45mm7coTdw2TblyJ79BrY=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5 h1:NicBWYTwkuOfVyZDbNkfSBSCwSgin4uirkedtyZltIc=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
@@ -75,14 +75,8 @@ type KunstructuredFactory interface {
|
||||
SliceFromBytes([]byte) ([]Kunstructured, error)
|
||||
FromMap(m map[string]interface{}) Kunstructured
|
||||
Hasher() KunstructuredHasher
|
||||
MakeConfigMap(
|
||||
kvLdr KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.ConfigMapArgs) (Kunstructured, error)
|
||||
MakeSecret(
|
||||
kvLdr KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.SecretArgs) (Kunstructured, error)
|
||||
MakeConfigMap(kvLdr KvLoader, args *types.ConfigMapArgs) (Kunstructured, error)
|
||||
MakeSecret(kvLdr KvLoader, args *types.SecretArgs) (Kunstructured, error)
|
||||
}
|
||||
|
||||
// KunstructuredHasher returns a hash of the argument
|
||||
|
||||
50
api/image/image.go
Normal file
50
api/image/image.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsImageMatched returns true if the value of t is identical to the
|
||||
// image name in the full image name and tag as given by s.
|
||||
func IsImageMatched(s, t string) bool {
|
||||
// Tag values are limited to [a-zA-Z0-9_.{}-].
|
||||
// Some tools like Bazel rules_k8s allow tag patterns with {} characters.
|
||||
// More info: https://github.com/bazelbuild/rules_k8s/pull/423
|
||||
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.{}-]*)?$")
|
||||
return pattern.MatchString(s)
|
||||
}
|
||||
|
||||
// Split separates and returns the name and tag parts
|
||||
// from the image string using either colon `:` or at `@` separators.
|
||||
// Note that the returned tag keeps its separator.
|
||||
func Split(imageName string) (name string, tag string) {
|
||||
// check if image name contains a domain
|
||||
// if domain is present, ignore domain and check for `:`
|
||||
ic := -1
|
||||
if slashIndex := strings.Index(imageName, "/"); slashIndex < 0 {
|
||||
ic = strings.LastIndex(imageName, ":")
|
||||
} else {
|
||||
lastIc := strings.LastIndex(imageName[slashIndex:], ":")
|
||||
// set ic only if `:` is present
|
||||
if lastIc > 0 {
|
||||
ic = slashIndex + lastIc
|
||||
}
|
||||
}
|
||||
ia := strings.LastIndex(imageName, "@")
|
||||
if ic < 0 && ia < 0 {
|
||||
return imageName, ""
|
||||
}
|
||||
|
||||
i := ic
|
||||
if ia > 0 {
|
||||
i = ia
|
||||
}
|
||||
|
||||
name = imageName[:i]
|
||||
tag = imageName[i:]
|
||||
return
|
||||
}
|
||||
80
api/image/image_test.go
Normal file
80
api/image/image_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package image
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsImageMatched(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
value string
|
||||
name string
|
||||
isMatched bool
|
||||
}{
|
||||
{
|
||||
testName: "identical",
|
||||
value: "nginx",
|
||||
name: "nginx",
|
||||
isMatched: true,
|
||||
},
|
||||
{
|
||||
testName: "name is match",
|
||||
value: "nginx:12345",
|
||||
name: "nginx",
|
||||
isMatched: true,
|
||||
},
|
||||
{
|
||||
testName: "name is not a match",
|
||||
value: "apache:12345",
|
||||
name: "nginx",
|
||||
isMatched: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
assert.Equal(t, tc.isMatched, IsImageMatched(tc.value, tc.name))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
value string
|
||||
name string
|
||||
tag string
|
||||
}{
|
||||
{
|
||||
testName: "no tag",
|
||||
value: "nginx",
|
||||
name: "nginx",
|
||||
tag: "",
|
||||
},
|
||||
{
|
||||
testName: "with tag",
|
||||
value: "nginx:1.2.3",
|
||||
name: "nginx",
|
||||
tag: ":1.2.3",
|
||||
},
|
||||
{
|
||||
testName: "with digest",
|
||||
value: "nginx@12345",
|
||||
name: "nginx",
|
||||
tag: "@12345",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
name, tag := Split(tc.value)
|
||||
assert.Equal(t, tc.name, name)
|
||||
assert.Equal(t, tc.tag, tag)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,7 @@ func makeFreshConfigMap(
|
||||
}
|
||||
|
||||
// MakeConfigMap returns a new ConfigMap, or nil and an error.
|
||||
func (f *Factory) MakeConfigMap(
|
||||
args *types.ConfigMapArgs) (*corev1.ConfigMap, error) {
|
||||
func (f *Factory) MakeConfigMap(args *types.ConfigMapArgs) (*corev1.ConfigMap, error) {
|
||||
all, err := f.kvLdr.Load(args.KvPairSources)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "loading KV pairs")
|
||||
@@ -38,7 +37,7 @@ func (f *Factory) MakeConfigMap(
|
||||
return nil, errors.Wrap(err, "trouble mapping")
|
||||
}
|
||||
}
|
||||
f.setLabelsAndAnnnotations(cm, args.GeneratorOptions)
|
||||
f.copyLabelsAndAnnotations(cm, args.Options)
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
input types.ConfigMapArgs
|
||||
options *types.GeneratorOptions
|
||||
expected *corev1.ConfigMap
|
||||
}
|
||||
|
||||
@@ -99,7 +98,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: nil,
|
||||
expected: makeEnvConfigMap("envConfigMap"),
|
||||
},
|
||||
{
|
||||
@@ -115,7 +113,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: nil,
|
||||
expected: makeFileConfigMap("fileConfigMap"),
|
||||
},
|
||||
{
|
||||
@@ -126,11 +123,11 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
KvPairSources: types.KvPairSources{
|
||||
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||
},
|
||||
},
|
||||
},
|
||||
options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
Options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: makeLiteralConfigMap("literalConfigMap", map[string]string{
|
||||
@@ -145,7 +142,7 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
KvPairSources: types.KvPairSources{
|
||||
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||
},
|
||||
GeneratorOptions: &types.GeneratorOptions{
|
||||
Options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
@@ -157,18 +154,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
// GeneratorOptions from the ConfigMapArgs take precedence over the
|
||||
// factory level GeneratorOptions and should overwrite
|
||||
// labels/annotations set in the factory level if there are common
|
||||
// labels/annotations
|
||||
expected: makeLiteralConfigMap("literalConfigMap", map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
@@ -193,8 +178,7 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
loader.NewFileLoaderAtRoot(fSys),
|
||||
valtest_test.MakeFakeValidator())
|
||||
for _, tc := range testCases {
|
||||
f := NewFactory(kvLdr, tc.options)
|
||||
cm, err := f.MakeConfigMap(&tc.input)
|
||||
cm, err := NewFactory(kvLdr).MakeConfigMap(&tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
@@ -11,44 +11,26 @@ import (
|
||||
|
||||
// Factory makes ConfigMaps and Secrets.
|
||||
type Factory struct {
|
||||
kvLdr ifc.KvLoader
|
||||
options *types.GeneratorOptions
|
||||
kvLdr ifc.KvLoader
|
||||
}
|
||||
|
||||
// NewFactory returns a new factory that makes ConfigMaps and Secrets.
|
||||
func NewFactory(
|
||||
kvLdr ifc.KvLoader, o *types.GeneratorOptions) *Factory {
|
||||
return &Factory{kvLdr: kvLdr, options: o}
|
||||
func NewFactory(kvLdr ifc.KvLoader) *Factory {
|
||||
return &Factory{kvLdr: kvLdr}
|
||||
}
|
||||
|
||||
// setLabelsAndAnnnotations will take the labels and annotations from
|
||||
// global GeneratorOptions and resource level GeneratorOptions and merge them
|
||||
// with the resource level taking precedence, and then set them on the provided
|
||||
// obj.
|
||||
func (f *Factory) setLabelsAndAnnnotations(obj metav1.Object, opts *types.GeneratorOptions) {
|
||||
labels := make(map[string]string)
|
||||
annotations := make(map[string]string)
|
||||
if f.options != nil {
|
||||
for k, v := range f.options.Labels {
|
||||
labels[k] = v
|
||||
}
|
||||
for k, v := range f.options.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
// copyLabelsAndAnnotations copies labels and annotations from
|
||||
// GeneratorOptions into the given object.
|
||||
func (f *Factory) copyLabelsAndAnnotations(
|
||||
obj metav1.Object, opts *types.GeneratorOptions) {
|
||||
if opts == nil {
|
||||
return
|
||||
}
|
||||
if opts != nil {
|
||||
for k, v := range opts.Labels {
|
||||
labels[k] = v
|
||||
}
|
||||
for k, v := range opts.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
if opts.Labels != nil {
|
||||
obj.SetLabels(types.CopyMap(opts.Labels))
|
||||
}
|
||||
if len(labels) != 0 {
|
||||
obj.SetLabels(labels)
|
||||
}
|
||||
if len(annotations) != 0 {
|
||||
obj.SetAnnotations(annotations)
|
||||
if opts.Annotations != nil {
|
||||
obj.SetAnnotations(types.CopyMap(opts.Annotations))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ func makeFreshSecret(
|
||||
}
|
||||
|
||||
// MakeSecret returns a new secret.
|
||||
func (f *Factory) MakeSecret(
|
||||
args *types.SecretArgs) (*corev1.Secret, error) {
|
||||
func (f *Factory) MakeSecret(args *types.SecretArgs) (*corev1.Secret, error) {
|
||||
all, err := f.kvLdr.Load(args.KvPairSources)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -39,7 +38,7 @@ func (f *Factory) MakeSecret(
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
f.setLabelsAndAnnnotations(s, args.GeneratorOptions)
|
||||
f.copyLabelsAndAnnotations(s, args.Options)
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ func TestConstructSecret(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
input types.SecretArgs
|
||||
options *types.GeneratorOptions
|
||||
expected *corev1.Secret
|
||||
}
|
||||
|
||||
@@ -94,7 +93,6 @@ func TestConstructSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: nil,
|
||||
expected: makeEnvSecret("envSecret"),
|
||||
},
|
||||
{
|
||||
@@ -107,7 +105,6 @@ func TestConstructSecret(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
options: nil,
|
||||
expected: makeFileSecret("fileSecret"),
|
||||
},
|
||||
{
|
||||
@@ -118,55 +115,22 @@ func TestConstructSecret(t *testing.T) {
|
||||
KvPairSources: types.KvPairSources{
|
||||
LiteralSources: []string{"a=x", "b=y"},
|
||||
},
|
||||
},
|
||||
},
|
||||
options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
Options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"fruit": "banana",
|
||||
"pet": "dog",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: makeLiteralSecret("literalSecret", map[string]string{
|
||||
"foo": "bar",
|
||||
}, nil),
|
||||
},
|
||||
{
|
||||
description: "construct secret from literal with GeneratorOptions in SecretArgs",
|
||||
input: types.SecretArgs{
|
||||
GeneratorArgs: types.GeneratorArgs{
|
||||
Name: "literalSecret",
|
||||
KvPairSources: types.KvPairSources{
|
||||
LiteralSources: []string{"a=x", "b=y"},
|
||||
},
|
||||
GeneratorOptions: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
options: &types.GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
// GeneratorOptions from the SecretArgs take precedence over the
|
||||
// factory level GeneratorOptions and should overwrite
|
||||
// labels/annotations set in the factory level if there are common
|
||||
// labels/annotations
|
||||
expected: makeLiteralSecret("literalSecret", map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
}, map[string]string{
|
||||
"foo": "changed",
|
||||
"cat": "dog",
|
||||
"fruit": "banana",
|
||||
"pet": "dog",
|
||||
}),
|
||||
},
|
||||
}
|
||||
@@ -178,7 +142,7 @@ func TestConstructSecret(t *testing.T) {
|
||||
loader.NewFileLoaderAtRoot(fSys),
|
||||
valtest_test.MakeFakeValidator())
|
||||
for _, tc := range testCases {
|
||||
f := NewFactory(kvLdr, tc.options)
|
||||
f := NewFactory(kvLdr)
|
||||
cm, err := f.MakeSecret(&tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
||||
@@ -6,143 +6,85 @@ package compiler
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
)
|
||||
|
||||
// Compiler creates Go plugin object files.
|
||||
//
|
||||
// Source code is read from
|
||||
// ${srcRoot}/${g}/${v}/${k}.go
|
||||
//
|
||||
// Object code is written to
|
||||
// ${objRoot}/${g}/${v}/${k}.so
|
||||
type Compiler struct {
|
||||
srcRoot string
|
||||
objRoot string
|
||||
}
|
||||
|
||||
// DeterminePluginSrcRoot guesses where the user
|
||||
// has her ${g}/${v}/$lower(${k})/${k}.go files.
|
||||
func DeterminePluginSrcRoot(fSys filesys.FileSystem) (string, error) {
|
||||
return konfig.FirstDirThatExistsElseError(
|
||||
"source directory", fSys, []konfig.NotedFunc{
|
||||
{
|
||||
Note: "relative to unit test",
|
||||
F: func() string {
|
||||
return filepath.Clean(
|
||||
filepath.Join(
|
||||
os.Getenv("PWD"),
|
||||
"..", "..",
|
||||
konfig.RelPluginHome))
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "relative to unit test (internal pkg)",
|
||||
F: func() string {
|
||||
return filepath.Clean(
|
||||
filepath.Join(
|
||||
os.Getenv("PWD"),
|
||||
"..", "..", "..", "..",
|
||||
konfig.RelPluginHome))
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "relative to api package",
|
||||
F: func() string {
|
||||
return filepath.Clean(
|
||||
filepath.Join(
|
||||
os.Getenv("PWD"),
|
||||
"..", "..", "..",
|
||||
konfig.RelPluginHome))
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "old style $GOPATH",
|
||||
F: func() string {
|
||||
return filepath.Join(
|
||||
os.Getenv("GOPATH"),
|
||||
"src", konfig.DomainName,
|
||||
konfig.ProgramName, konfig.RelPluginHome)
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "HOME with literal 'gopath'",
|
||||
F: func() string {
|
||||
return filepath.Join(
|
||||
konfig.HomeDir(), "gopath",
|
||||
"src", konfig.DomainName,
|
||||
konfig.ProgramName, konfig.RelPluginHome)
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "home directory",
|
||||
F: func() string {
|
||||
return filepath.Join(
|
||||
konfig.HomeDir(), konfig.DomainName,
|
||||
konfig.ProgramName, konfig.RelPluginHome)
|
||||
},
|
||||
},
|
||||
})
|
||||
// pluginRoot is where the user
|
||||
// has her ${g}/${v}/$lower(${k})/${k}.go files.
|
||||
pluginRoot string
|
||||
// Where compilation happens.
|
||||
workDir string
|
||||
// Used as the root file name for src and object.
|
||||
rawKind string
|
||||
// Capture compiler output.
|
||||
stderr bytes.Buffer
|
||||
// Capture compiler output.
|
||||
stdout bytes.Buffer
|
||||
}
|
||||
|
||||
// NewCompiler returns a new compiler instance.
|
||||
func NewCompiler(srcRoot, objRoot string) *Compiler {
|
||||
return &Compiler{srcRoot: srcRoot, objRoot: objRoot}
|
||||
func NewCompiler(root string) *Compiler {
|
||||
return &Compiler{pluginRoot: root}
|
||||
}
|
||||
|
||||
// ObjRoot is root of compilation target tree.
|
||||
func (b *Compiler) ObjRoot() string {
|
||||
return b.objRoot
|
||||
// Set GVK converts g,v,k tuples to file path components.
|
||||
func (b *Compiler) SetGVK(g, v, k string) {
|
||||
b.rawKind = k
|
||||
b.workDir = filepath.Join(b.pluginRoot, g, v, strings.ToLower(k))
|
||||
}
|
||||
|
||||
// SrcRoot is where to find src.
|
||||
func (b *Compiler) SrcRoot() string {
|
||||
return b.srcRoot
|
||||
func (b *Compiler) srcPath() string {
|
||||
return filepath.Join(b.workDir, b.rawKind+".go")
|
||||
}
|
||||
|
||||
func goBin() string {
|
||||
return filepath.Join(runtime.GOROOT(), "bin", "go")
|
||||
func (b *Compiler) objFile() string {
|
||||
return b.rawKind + ".so"
|
||||
}
|
||||
|
||||
// Compile reads ${srcRoot}/${g}/${v}/${k}.go
|
||||
// and writes ${objRoot}/${g}/${v}/${k}.so
|
||||
func (b *Compiler) Compile(g, v, k string) error {
|
||||
lowK := strings.ToLower(k)
|
||||
objDir := filepath.Join(b.objRoot, g, v, lowK)
|
||||
objFile := filepath.Join(objDir, k) + ".so"
|
||||
if RecentFileExists(objFile) {
|
||||
// Skip rebuilding it.
|
||||
// Absolute path to the compiler output (the .so file).
|
||||
func (b *Compiler) ObjPath() string {
|
||||
return filepath.Join(b.workDir, b.objFile())
|
||||
}
|
||||
|
||||
// Cleanup provides a hook to delete the .so file.
|
||||
// Ignore errors.
|
||||
func (b *Compiler) Cleanup() {
|
||||
_ = os.Remove(b.ObjPath())
|
||||
}
|
||||
|
||||
// Compile changes its working directory to
|
||||
// ${pluginRoot}/${g}/${v}/$lower(${k} and places
|
||||
// object code next to source code.
|
||||
func (b *Compiler) Compile() error {
|
||||
if FileYoungerThan(b.ObjPath(), 8*time.Second) {
|
||||
// Skip rebuilding it, to save time in a plugin test file
|
||||
// that has many distinct calls to make a harness and compile
|
||||
// the plugin (only the first compile will happen).
|
||||
// Make it a short time to avoid tricking someone who's actively
|
||||
// developing a plugin.
|
||||
return nil
|
||||
}
|
||||
err := os.MkdirAll(objDir, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcFile := filepath.Join(b.srcRoot, g, v, lowK, k) + ".go"
|
||||
if !FileExists(srcFile) {
|
||||
// Handy for tests of lone plugins.
|
||||
s := k + ".go"
|
||||
if !FileExists(s) {
|
||||
return fmt.Errorf(
|
||||
"cannot find source at '%s' or '%s'", srcFile, s)
|
||||
|
||||
}
|
||||
srcFile = s
|
||||
if !FileExists(b.srcPath()) {
|
||||
return fmt.Errorf("cannot find source at '%s'", b.srcPath())
|
||||
}
|
||||
// If you use an IDE, make sure it's go build and test flags
|
||||
// match those used below. Same goes for Makefile targets.
|
||||
commands := []string{
|
||||
"build",
|
||||
// "-trimpath", This flag used to make it better, now it makes it worse,
|
||||
// see https://github.com/golang/go/issues/31354
|
||||
"-buildmode",
|
||||
"plugin",
|
||||
"-o", objFile, srcFile,
|
||||
"-o", b.objFile(),
|
||||
}
|
||||
goBin := goBin()
|
||||
if !FileExists(goBin) {
|
||||
@@ -150,34 +92,26 @@ func (b *Compiler) Compile(g, v, k string) error {
|
||||
"cannot find go compiler %s", goBin)
|
||||
}
|
||||
cmd := exec.Command(goBin, commands...)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
b.stderr.Reset()
|
||||
cmd.Stderr = &b.stderr
|
||||
b.stdout.Reset()
|
||||
cmd.Stdout = &b.stdout
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Dir = b.workDir
|
||||
if err := cmd.Run(); err != nil {
|
||||
b.report()
|
||||
return errors.Wrapf(
|
||||
err, "cannot compile %s:\nSTDERR\n%s\n", srcFile, stderr.String())
|
||||
err, "cannot compile %s:\nSTDERR\n%s\n",
|
||||
b.srcPath(), b.stderr.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// True if file less than 3 minutes old, i.e. not
|
||||
// accidentally left over from some earlier build.
|
||||
func RecentFileExists(path string) bool {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
age := time.Since(fi.ModTime())
|
||||
return age.Minutes() < 3
|
||||
}
|
||||
|
||||
func FileExists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
func (b *Compiler) report() {
|
||||
log.Println("stdout: -------")
|
||||
log.Println(b.stdout.String())
|
||||
log.Println("----------------")
|
||||
log.Println("stderr: -------")
|
||||
log.Println(b.stderr.String())
|
||||
log.Println("----------------")
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
package compiler_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
. "sigs.k8s.io/kustomize/api/internal/plugins/compiler"
|
||||
@@ -15,53 +14,46 @@ import (
|
||||
|
||||
// Regression coverage over compiler behavior.
|
||||
func TestCompiler(t *testing.T) {
|
||||
configRoot, err := ioutil.TempDir("", "kustomize-compiler-test")
|
||||
if err != nil {
|
||||
t.Errorf("failed to make temp dir: %v", err)
|
||||
}
|
||||
srcRoot, err := DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
c := NewCompiler(srcRoot, configRoot)
|
||||
if configRoot != c.ObjRoot() {
|
||||
t.Errorf("unexpected objRoot %s", c.ObjRoot())
|
||||
}
|
||||
c := NewCompiler(srcRoot)
|
||||
|
||||
c.SetGVK("someteam.example.com", "v1", "DatePrefixer")
|
||||
expectObj := filepath.Join(
|
||||
c.ObjRoot(),
|
||||
"someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("obj file should not exist yet: %s", expectObj)
|
||||
srcRoot, "someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
|
||||
if expectObj != c.ObjPath() {
|
||||
t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath())
|
||||
}
|
||||
err = c.Compile("someteam.example.com", "v1", "DatePrefixer")
|
||||
err = c.Compile()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !RecentFileExists(expectObj) {
|
||||
if !FileYoungerThan(expectObj, time.Second) {
|
||||
t.Errorf("didn't find expected obj file %s", expectObj)
|
||||
}
|
||||
c.Cleanup()
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("obj file '%s' should be gone", expectObj)
|
||||
}
|
||||
|
||||
c.SetGVK("builtin", "", "SecretGenerator")
|
||||
expectObj = filepath.Join(
|
||||
c.ObjRoot(),
|
||||
srcRoot,
|
||||
"builtin", "", "secretgenerator", "SecretGenerator.so")
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("obj file should not exist yet: %s", expectObj)
|
||||
if expectObj != c.ObjPath() {
|
||||
t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath())
|
||||
}
|
||||
err = c.Compile("builtin", "", "SecretGenerator")
|
||||
err = c.Compile()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !RecentFileExists(expectObj) {
|
||||
if !FileYoungerThan(expectObj, time.Second) {
|
||||
t.Errorf("didn't find expected obj file %s", expectObj)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(c.ObjRoot())
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"removing temp dir: %s %v", c.ObjRoot(), err)
|
||||
}
|
||||
c.Cleanup()
|
||||
if FileExists(expectObj) {
|
||||
t.Errorf("cleanup failed; still see: %s", expectObj)
|
||||
t.Errorf("obj file '%s' should be gone", expectObj)
|
||||
}
|
||||
}
|
||||
|
||||
102
api/internal/plugins/compiler/utils.go
Normal file
102
api/internal/plugins/compiler/utils.go
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
)
|
||||
|
||||
func goBin() string {
|
||||
return filepath.Join(runtime.GOROOT(), "bin", "go")
|
||||
}
|
||||
|
||||
// DeterminePluginSrcRoot guesses where the user
|
||||
// has her ${g}/${v}/$lower(${k})/${k}.go files.
|
||||
func DeterminePluginSrcRoot(fSys filesys.FileSystem) (string, error) {
|
||||
return konfig.FirstDirThatExistsElseError(
|
||||
"source directory", fSys, []konfig.NotedFunc{
|
||||
{
|
||||
Note: "relative to unit test",
|
||||
F: func() string {
|
||||
return filepath.Clean(
|
||||
filepath.Join(
|
||||
os.Getenv("PWD"),
|
||||
"..", "..",
|
||||
konfig.RelPluginHome))
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "relative to unit test (internal pkg)",
|
||||
F: func() string {
|
||||
return filepath.Clean(
|
||||
filepath.Join(
|
||||
os.Getenv("PWD"),
|
||||
"..", "..", "..", "..",
|
||||
konfig.RelPluginHome))
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "relative to api package",
|
||||
F: func() string {
|
||||
return filepath.Clean(
|
||||
filepath.Join(
|
||||
os.Getenv("PWD"),
|
||||
"..", "..", "..",
|
||||
konfig.RelPluginHome))
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "old style $GOPATH",
|
||||
F: func() string {
|
||||
return filepath.Join(
|
||||
os.Getenv("GOPATH"),
|
||||
"src", konfig.DomainName,
|
||||
konfig.ProgramName, konfig.RelPluginHome)
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "HOME with literal 'gopath'",
|
||||
F: func() string {
|
||||
return filepath.Join(
|
||||
konfig.HomeDir(), "gopath",
|
||||
"src", konfig.DomainName,
|
||||
konfig.ProgramName, konfig.RelPluginHome)
|
||||
},
|
||||
},
|
||||
{
|
||||
Note: "home directory",
|
||||
F: func() string {
|
||||
return filepath.Join(
|
||||
konfig.HomeDir(), konfig.DomainName,
|
||||
konfig.ProgramName, konfig.RelPluginHome)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// FileYoungerThan returns true if the file age is <= the Duration argument.
|
||||
func FileYoungerThan(path string, d time.Duration) bool {
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return time.Since(fi.ModTime()) <= d
|
||||
}
|
||||
|
||||
func FileExists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
26
api/internal/plugins/compiler/utils_test.go
Normal file
26
api/internal/plugins/compiler/utils_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package compiler
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
)
|
||||
|
||||
func TestDeterminePluginSrcRoot(t *testing.T) {
|
||||
actual, err := DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !filepath.IsAbs(actual) {
|
||||
t.Errorf("expected absolute path, but got '%s'", actual)
|
||||
}
|
||||
expectedSuffix := filepath.Join("sigs.k8s.io", "kustomize", "plugin")
|
||||
if !strings.HasSuffix(actual, expectedSuffix) {
|
||||
t.Errorf("expected suffix '%s' in '%s'", expectedSuffix, actual)
|
||||
}
|
||||
}
|
||||
@@ -266,8 +266,9 @@ func (p *ExecPlugin) UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, err
|
||||
}
|
||||
r.SetAnnotations(annotations)
|
||||
r.SetOptions(types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: behavior},
|
||||
&types.GeneratorOptions{DisableNameSuffixHash: !needsHash}))
|
||||
&types.GeneratorArgs{
|
||||
Behavior: behavior,
|
||||
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
|
||||
}
|
||||
return rm, nil
|
||||
}
|
||||
|
||||
@@ -116,7 +116,9 @@ func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHa
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{"name": name},
|
||||
}, &types.GeneratorArgs{Behavior: behavior}, &types.GeneratorOptions{DisableNameSuffixHash: disableHash})
|
||||
}, &types.GeneratorArgs{
|
||||
Behavior: behavior,
|
||||
Options: &types.GeneratorOptions{DisableNameSuffixHash: disableHash}})
|
||||
}
|
||||
|
||||
func strptr(s string) *string {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
// Loader loads plugins using a file loader (a different loader).
|
||||
type Loader struct {
|
||||
pc *types.PluginConfig
|
||||
rf *resmap.Factory
|
||||
@@ -107,17 +108,35 @@ func isBuiltinPlugin(res *resource.Resource) bool {
|
||||
}
|
||||
|
||||
func (l *Loader) loadAndConfigurePlugin(
|
||||
ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (c resmap.Configurable, err error) {
|
||||
ldr ifc.Loader,
|
||||
v ifc.Validator,
|
||||
res *resource.Resource) (c resmap.Configurable, err error) {
|
||||
if isBuiltinPlugin(res) {
|
||||
// Instead of looking for and loading a .so file, just
|
||||
// instantiate the plugin from a generated factory
|
||||
// function (see "pluginator"). Being able to do this
|
||||
// is what makes a plugin "builtin".
|
||||
c, err = l.makeBuiltinPlugin(res.GetGvk())
|
||||
} else if l.pc.PluginRestrictions == types.PluginRestrictionsNone {
|
||||
c, err = l.loadPlugin(res.OrgId())
|
||||
switch l.pc.BpLoadingOptions {
|
||||
case types.BploLoadFromFileSys:
|
||||
c, err = l.loadPlugin(res.OrgId())
|
||||
case types.BploUseStaticallyLinked:
|
||||
// Instead of looking for and loading a .so file,
|
||||
// instantiate the plugin from a generated factory
|
||||
// function (see "pluginator"). Being able to do this
|
||||
// is what makes a plugin "builtin".
|
||||
c, err = l.makeBuiltinPlugin(res.GetGvk())
|
||||
default:
|
||||
err = fmt.Errorf(
|
||||
"unknown plugin loader behavior specified: %v",
|
||||
l.pc.BpLoadingOptions)
|
||||
}
|
||||
} else {
|
||||
err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind)
|
||||
switch l.pc.PluginRestrictions {
|
||||
case types.PluginRestrictionsNone:
|
||||
c, err = l.loadPlugin(res.OrgId())
|
||||
case types.PluginRestrictionsBuiltinsOnly:
|
||||
err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind)
|
||||
default:
|
||||
err = fmt.Errorf(
|
||||
"unknown plugin restriction specified: %v",
|
||||
l.pc.PluginRestrictions)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,22 +59,26 @@ func TestLoader(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c, err := konfig.EnabledPluginConfig()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pLdr := NewLoader(c, rmF)
|
||||
if pLdr == nil {
|
||||
t.Fatal("expect non-nil loader")
|
||||
}
|
||||
m, err := rmF.NewResMapFromBytes([]byte(
|
||||
generatorConfigs, err := rmF.NewResMapFromBytes([]byte(
|
||||
someServiceGenerator + "---\n" + secretGenerator))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = pLdr.LoadGenerators(
|
||||
fLdr, valtest_test.MakeFakeValidator(), m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
for _, behavior := range []types.BuiltinPluginLoadingOptions{
|
||||
types.BploUseStaticallyLinked,
|
||||
types.BploLoadFromFileSys} {
|
||||
c, err := konfig.EnabledPluginConfig(behavior)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pLdr := NewLoader(c, rmF)
|
||||
if pLdr == nil {
|
||||
t.Fatal("expect non-nil loader")
|
||||
}
|
||||
_, err = pLdr.LoadGenerators(
|
||||
fLdr, valtest_test.MakeFakeValidator(), generatorConfigs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,14 +74,12 @@ var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func(
|
||||
builtinhelpers.SecretGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
|
||||
result []resmap.Generator, err error) {
|
||||
var c struct {
|
||||
types.GeneratorOptions
|
||||
types.SecretArgs
|
||||
}
|
||||
if kt.kustomization.GeneratorOptions != nil {
|
||||
c.GeneratorOptions = *kt.kustomization.GeneratorOptions
|
||||
}
|
||||
for _, args := range kt.kustomization.SecretGenerator {
|
||||
c.SecretArgs = args
|
||||
c.SecretArgs.Options = types.MergeGlobalOptionsIntoLocal(
|
||||
c.SecretArgs.Options, kt.kustomization.GeneratorOptions)
|
||||
p := f()
|
||||
err := kt.configureBuiltinPlugin(p, c, bpt)
|
||||
if err != nil {
|
||||
@@ -95,14 +93,12 @@ var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func(
|
||||
builtinhelpers.ConfigMapGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
|
||||
result []resmap.Generator, err error) {
|
||||
var c struct {
|
||||
types.GeneratorOptions
|
||||
types.ConfigMapArgs
|
||||
}
|
||||
if kt.kustomization.GeneratorOptions != nil {
|
||||
c.GeneratorOptions = *kt.kustomization.GeneratorOptions
|
||||
}
|
||||
for _, args := range kt.kustomization.ConfigMapGenerator {
|
||||
c.ConfigMapArgs = args
|
||||
c.ConfigMapArgs.Options = types.MergeGlobalOptionsIntoLocal(
|
||||
c.ConfigMapArgs.Options, kt.kustomization.GeneratorOptions)
|
||||
p := f()
|
||||
err := kt.configureBuiltinPlugin(p, c, bpt)
|
||||
if err != nil {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/api/ifc"
|
||||
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
@@ -19,8 +18,7 @@ import (
|
||||
// high level tests.
|
||||
|
||||
func TestMakeCustomizedResMap(t *testing.T) {
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
th := kusttest_test.MakeHarnessWithFs(t, fSys)
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
th.WriteK("/whatever", `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
@@ -168,7 +166,7 @@ metadata:
|
||||
}
|
||||
|
||||
actual, err := makeKustTargetWithRf(
|
||||
t, fSys, "/whatever", resFactory).MakeCustomizedResMap()
|
||||
t, th.GetFSys(), "/whatever", resFactory).MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected Resources error %v", err)
|
||||
}
|
||||
|
||||
@@ -75,11 +75,8 @@ func (kf *KunstructuredFactoryImpl) FromMap(
|
||||
|
||||
// MakeConfigMap returns an instance of Kunstructured for ConfigMap
|
||||
func (kf *KunstructuredFactoryImpl) MakeConfigMap(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
|
||||
o, err := configmapandsecret.NewFactory(
|
||||
kvLdr, options).MakeConfigMap(args)
|
||||
kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
|
||||
o, err := configmapandsecret.NewFactory(kvLdr).MakeConfigMap(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -88,11 +85,8 @@ func (kf *KunstructuredFactoryImpl) MakeConfigMap(
|
||||
|
||||
// MakeSecret returns an instance of Kunstructured for Secret
|
||||
func (kf *KunstructuredFactoryImpl) MakeSecret(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.SecretArgs) (ifc.Kunstructured, error) {
|
||||
o, err := configmapandsecret.NewFactory(
|
||||
kvLdr, options).MakeSecret(args)
|
||||
kvLdr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
|
||||
o, err := configmapandsecret.NewFactory(kvLdr).MakeSecret(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ func (fs *UnstructAdapter) selectSubtree(path string) (map[string]interface{}, [
|
||||
func (fs *UnstructAdapter) GetFieldValue(path string) (interface{}, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedFieldNoCopy(
|
||||
@@ -141,14 +141,14 @@ func (fs *UnstructAdapter) GetFieldValue(path string) (interface{}, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetString returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetString(path string) (string, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return "", noFieldError{Field: path}
|
||||
return "", NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedString(
|
||||
@@ -156,14 +156,14 @@ func (fs *UnstructAdapter) GetString(path string) (string, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return "", noFieldError{Field: path}
|
||||
return "", NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetStringSlice returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetStringSlice(path string) ([]string, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return []string{}, noFieldError{Field: path}
|
||||
return []string{}, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedStringSlice(
|
||||
@@ -171,14 +171,14 @@ func (fs *UnstructAdapter) GetStringSlice(path string) ([]string, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return []string{}, noFieldError{Field: path}
|
||||
return []string{}, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetBool returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetBool(path string) (bool, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return false, noFieldError{Field: path}
|
||||
return false, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedBool(
|
||||
@@ -186,7 +186,7 @@ func (fs *UnstructAdapter) GetBool(path string) (bool, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return false, noFieldError{Field: path}
|
||||
return false, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetFloat64 returns value at the given fieldpath.
|
||||
@@ -201,14 +201,14 @@ func (fs *UnstructAdapter) GetFloat64(path string) (float64, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return 0, noFieldError{Field: path}
|
||||
return 0, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetInt64 returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetInt64(path string) (int64, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return 0, noFieldError{Field: path}
|
||||
return 0, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedInt64(
|
||||
@@ -216,14 +216,14 @@ func (fs *UnstructAdapter) GetInt64(path string) (int64, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return 0, noFieldError{Field: path}
|
||||
return 0, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetSlice returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetSlice(path string) ([]interface{}, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedSlice(
|
||||
@@ -231,14 +231,14 @@ func (fs *UnstructAdapter) GetSlice(path string) ([]interface{}, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetStringMap returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetStringMap(path string) (map[string]string, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedStringMap(
|
||||
@@ -246,14 +246,14 @@ func (fs *UnstructAdapter) GetStringMap(path string) (map[string]string, error)
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
// GetMap returns value at the given fieldpath.
|
||||
func (fs *UnstructAdapter) GetMap(path string) (map[string]interface{}, error) {
|
||||
content, fields, found, err := fs.selectSubtree(path)
|
||||
if !found || err != nil {
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
s, found, err := unstructured.NestedMap(
|
||||
@@ -261,7 +261,7 @@ func (fs *UnstructAdapter) GetMap(path string) (map[string]interface{}, error) {
|
||||
if found || err != nil {
|
||||
return s, err
|
||||
}
|
||||
return nil, noFieldError{Field: path}
|
||||
return nil, NoFieldError{Field: path}
|
||||
}
|
||||
|
||||
func (fs *UnstructAdapter) MatchesLabelSelector(selector string) (bool, error) {
|
||||
@@ -340,11 +340,11 @@ func toSchemaGvk(x resid.Gvk) schema.GroupVersionKind {
|
||||
}
|
||||
}
|
||||
|
||||
// noFieldError is returned when a field is expected, but missing.
|
||||
type noFieldError struct {
|
||||
// NoFieldError is returned when a field is expected, but missing.
|
||||
type NoFieldError struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
func (e noFieldError) Error() string {
|
||||
func (e NoFieldError) Error() string {
|
||||
return fmt.Sprintf("no field named '%s'", e.Field)
|
||||
}
|
||||
|
||||
@@ -345,5 +345,24 @@ nameReference:
|
||||
kind: PersistentVolumeClaim
|
||||
- path: spec/volumeClaimTemplates/spec/storageClassName
|
||||
kind: StatefulSet
|
||||
|
||||
- kind: PriorityClass
|
||||
version: v1
|
||||
group: scheduling.k8s.io
|
||||
fieldSpecs:
|
||||
- path: spec/priorityClassName
|
||||
kind: Pod
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: StatefulSet
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: Deployment
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: ReplicationController
|
||||
- path: spec/jobTemplate/spec/template/spec/priorityClassName
|
||||
kind: CronJob
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: Job
|
||||
- path: spec/template/spec/priorityClassName
|
||||
kind: DaemonSet
|
||||
`
|
||||
)
|
||||
|
||||
@@ -35,37 +35,46 @@ const (
|
||||
// Domain from which kustomize code is imported, for locating
|
||||
// plugin source code under $GOPATH when GOPATH is defined.
|
||||
DomainName = "sigs.k8s.io"
|
||||
|
||||
// Injected into plugin paths when plugins are disabled.
|
||||
// Provides a clue in flows that shouldn't happen.
|
||||
NoPluginHomeSentinal = "/No/non-builtin/plugins!"
|
||||
)
|
||||
|
||||
func EnabledPluginConfig() (*types.PluginConfig, error) {
|
||||
func EnabledPluginConfig(b types.BuiltinPluginLoadingOptions) (*types.PluginConfig, error) {
|
||||
dir, err := DefaultAbsPluginHome(filesys.MakeFsOnDisk())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return MakePluginConfig(types.PluginRestrictionsNone, dir), nil
|
||||
return MakePluginConfig(types.PluginRestrictionsNone, b, dir), nil
|
||||
}
|
||||
|
||||
func DisabledPluginConfig() *types.PluginConfig {
|
||||
return MakePluginConfig(
|
||||
types.PluginRestrictionsBuiltinsOnly, NoPluginHomeSentinal)
|
||||
types.PluginRestrictionsBuiltinsOnly,
|
||||
types.BploUseStaticallyLinked,
|
||||
NoPluginHomeSentinal)
|
||||
}
|
||||
|
||||
func MakePluginConfig(
|
||||
pr types.PluginRestrictions, home string) *types.PluginConfig {
|
||||
pr types.PluginRestrictions,
|
||||
b types.BuiltinPluginLoadingOptions,
|
||||
home string) *types.PluginConfig {
|
||||
return &types.PluginConfig{
|
||||
PluginRestrictions: pr,
|
||||
AbsPluginHome: home,
|
||||
BpLoadingOptions: b,
|
||||
}
|
||||
}
|
||||
|
||||
// Use an obviously erroneous path, in case it's accidentally used.
|
||||
const NoPluginHomeSentinal = "/no/non-builtin/plugins!"
|
||||
|
||||
type NotedFunc struct {
|
||||
Note string
|
||||
F func() string
|
||||
}
|
||||
|
||||
// DefaultAbsPluginHome returns the absolute path in the given file
|
||||
// system to first directory that looks like a good candidate for
|
||||
// the home of kustomize plugins.
|
||||
func DefaultAbsPluginHome(fSys filesys.FileSystem) (string, error) {
|
||||
return FirstDirThatExistsElseError(
|
||||
"plugin home directory", fSys, []NotedFunc{
|
||||
|
||||
@@ -143,7 +143,8 @@ apiVersion: builtin
|
||||
kind: ConfigMapGenerator
|
||||
metadata:
|
||||
name: my-config
|
||||
disableNameSuffixHash: true
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
literals:
|
||||
- MY_ENV=foo
|
||||
`)
|
||||
|
||||
@@ -12,10 +12,12 @@ import (
|
||||
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||
)
|
||||
|
||||
func findSecret(m resmap.ResMap) *resource.Resource {
|
||||
func findSecret(m resmap.ResMap, prefix string) *resource.Resource {
|
||||
for _, r := range m.Resources() {
|
||||
if r.OrgId().Kind == "Secret" {
|
||||
return r
|
||||
if prefix == "" || strings.HasPrefix(r.GetName(), prefix) {
|
||||
return r
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -77,7 +79,7 @@ metadata:
|
||||
|
||||
m := th.Run("/whatever", th.MakeDefaultOptions())
|
||||
|
||||
secret := findSecret(m)
|
||||
secret := findSecret(m, "")
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
@@ -90,7 +92,7 @@ metadata:
|
||||
"disableNameSuffixHash: false",
|
||||
"disableNameSuffixHash: true", -1))
|
||||
m = th.Run("/whatever", th.MakeDefaultOptions())
|
||||
secret = findSecret(m)
|
||||
secret = findSecret(m, "")
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
@@ -98,3 +100,47 @@ metadata:
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
}
|
||||
func TestDisableNameSuffixHashPerObject(t *testing.T) {
|
||||
th := kusttest_test.MakeHarness(t)
|
||||
const kustomizationContent = `
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
generatorOptions:
|
||||
disableNameSuffixHash: false
|
||||
secretGenerator:
|
||||
- name: nohash
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
literals:
|
||||
- DB_USERNAME=admin
|
||||
- DB_PASSWORD=somepw
|
||||
type: Opaque
|
||||
- name: yeshash
|
||||
options:
|
||||
disableNameSuffixHash: false
|
||||
literals:
|
||||
- DB_USERNAME=admin
|
||||
- DB_PASSWORD=somepw
|
||||
type: Opaque
|
||||
`
|
||||
|
||||
th.WriteK("/whatever", kustomizationContent)
|
||||
|
||||
m := th.Run("/whatever", th.MakeDefaultOptions())
|
||||
|
||||
secret := findSecret(m, "nohash")
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
if secret.GetName() != "nohash" {
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
|
||||
secret = findSecret(m, "yeshash")
|
||||
if secret == nil {
|
||||
t.Errorf("Expected to find a Secret")
|
||||
}
|
||||
if secret.GetName() != "yeshash-mcgcmdcm69" {
|
||||
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,12 +66,10 @@ func (rmF *Factory) NewResMapFromBytes(b []byte) (ResMap, error) {
|
||||
// NewResMapFromConfigMapArgs returns a Resource slice given
|
||||
// a configmap metadata slice from kustomization file.
|
||||
func (rmF *Factory) NewResMapFromConfigMapArgs(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
argList []types.ConfigMapArgs) (ResMap, error) {
|
||||
kvLdr ifc.KvLoader, argList []types.ConfigMapArgs) (ResMap, error) {
|
||||
var resources []*resource.Resource
|
||||
for _, args := range argList {
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, options, &args)
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewResMapFromConfigMapArgs")
|
||||
}
|
||||
@@ -81,10 +79,8 @@ func (rmF *Factory) NewResMapFromConfigMapArgs(
|
||||
}
|
||||
|
||||
func (rmF *Factory) FromConfigMapArgs(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args types.ConfigMapArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, options, &args)
|
||||
kvLdr ifc.KvLoader, args types.ConfigMapArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -94,12 +90,10 @@ func (rmF *Factory) FromConfigMapArgs(
|
||||
// NewResMapFromSecretArgs takes a SecretArgs slice, generates
|
||||
// secrets from each entry, and accumulates them in a ResMap.
|
||||
func (rmF *Factory) NewResMapFromSecretArgs(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
argsList []types.SecretArgs) (ResMap, error) {
|
||||
kvLdr ifc.KvLoader, argsList []types.SecretArgs) (ResMap, error) {
|
||||
var resources []*resource.Resource
|
||||
for _, args := range argsList {
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, options, &args)
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "NewResMapFromSecretArgs")
|
||||
}
|
||||
@@ -109,10 +103,8 @@ func (rmF *Factory) NewResMapFromSecretArgs(
|
||||
}
|
||||
|
||||
func (rmF *Factory) FromSecretArgs(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args types.SecretArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, options, &args)
|
||||
kvLdr ifc.KvLoader, args types.SecretArgs) (ResMap, error) {
|
||||
res, err := rmF.resF.MakeSecret(kvLdr, &args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ BAR=baz
|
||||
t.Fatalf("error adding file '%s': %v\n", tc.filepath, fErr)
|
||||
}
|
||||
}
|
||||
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, nil, tc.input)
|
||||
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -258,7 +258,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
|
||||
actual, err := rmF.NewResMapFromSecretArgs(
|
||||
kv.NewLoader(
|
||||
loader.NewFileLoaderAtRoot(fSys),
|
||||
valtest_test.MakeFakeValidator()), nil, secrets)
|
||||
valtest_test.MakeFakeValidator()), secrets)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
@@ -826,7 +826,7 @@ func makeMap1() ResMap {
|
||||
},
|
||||
}, &types.GeneratorArgs{
|
||||
Behavior: "create",
|
||||
}, nil))
|
||||
}))
|
||||
}
|
||||
|
||||
func makeMap2(b types.GenerationBehavior) ResMap {
|
||||
@@ -844,7 +844,7 @@ func makeMap2(b types.GenerationBehavior) ResMap {
|
||||
},
|
||||
}, &types.GeneratorArgs{
|
||||
Behavior: b.String(),
|
||||
}, nil))
|
||||
}))
|
||||
}
|
||||
|
||||
func TestAbsorbAll(t *testing.T) {
|
||||
@@ -864,7 +864,7 @@ func TestAbsorbAll(t *testing.T) {
|
||||
},
|
||||
}, &types.GeneratorArgs{
|
||||
Behavior: "create",
|
||||
}, nil))
|
||||
}))
|
||||
w := makeMap1()
|
||||
if err := w.AbsorbAll(makeMap2(types.BehaviorMerge)); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
|
||||
@@ -50,8 +50,8 @@ func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string
|
||||
|
||||
// FromMapAndOption returns a new instance of Resource with given options.
|
||||
func (rf *Factory) FromMapAndOption(
|
||||
m map[string]interface{}, args *types.GeneratorArgs, option *types.GeneratorOptions) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args, option))
|
||||
m map[string]interface{}, args *types.GeneratorArgs) *Resource {
|
||||
return rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args))
|
||||
}
|
||||
|
||||
// FromKunstructured returns a new instance of Resource.
|
||||
@@ -66,7 +66,7 @@ func (rf *Factory) makeOne(
|
||||
log.Fatal("unstruct ifc must not be null")
|
||||
}
|
||||
if o == nil {
|
||||
o = types.NewGenArgs(nil, nil)
|
||||
o = types.NewGenArgs(nil)
|
||||
}
|
||||
r := &Resource{
|
||||
Kunstructured: u,
|
||||
@@ -147,33 +147,19 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
|
||||
}
|
||||
|
||||
// MakeConfigMap makes an instance of Resource for ConfigMap
|
||||
func (rf *Factory) MakeConfigMap(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.ConfigMapArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeConfigMap(kvLdr, options, args)
|
||||
func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeConfigMap(kvLdr, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.makeOne(
|
||||
u,
|
||||
types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: args.Behavior},
|
||||
options)), nil
|
||||
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
|
||||
}
|
||||
|
||||
// MakeSecret makes an instance of Resource for Secret
|
||||
func (rf *Factory) MakeSecret(
|
||||
kvLdr ifc.KvLoader,
|
||||
options *types.GeneratorOptions,
|
||||
args *types.SecretArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeSecret(kvLdr, options, args)
|
||||
func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Resource, error) {
|
||||
u, err := rf.kf.MakeSecret(kvLdr, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rf.makeOne(
|
||||
u,
|
||||
types.NewGenArgs(
|
||||
&types.GeneratorArgs{Behavior: args.Behavior},
|
||||
options)), nil
|
||||
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func (th Harness) MakeOptionsPluginsDisabled() krusty.Options {
|
||||
|
||||
// Enables use of non-builtin plugins.
|
||||
func (th Harness) MakeOptionsPluginsEnabled() krusty.Options {
|
||||
c, err := konfig.EnabledPluginConfig()
|
||||
c, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "unable to find plugin root") {
|
||||
th.t.Log(
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
package kusttest_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
@@ -16,36 +19,50 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// HarnessEnhanced manages a full plugin environment for tests.
|
||||
type HarnessEnhanced struct {
|
||||
// An instance of *testing.T, and a filesystem (likely in-memory)
|
||||
// for loading test data - plugin config, resources to transform, etc.
|
||||
Harness
|
||||
|
||||
// plugintestEnv holds the plugin compiler and data needed to
|
||||
// create compilation sub-processes.
|
||||
pte *pluginTestEnv
|
||||
rf *resmap.Factory
|
||||
|
||||
// rf creates Resources from byte streams.
|
||||
rf *resmap.Factory
|
||||
|
||||
// A file loader using the Harness.fSys to read test data.
|
||||
ldr ifc.Loader
|
||||
pl *pLdr.Loader
|
||||
|
||||
// A plugin loader that loads plugins from a (real) file system.
|
||||
pl *pLdr.Loader
|
||||
}
|
||||
|
||||
func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
|
||||
pte := newPluginTestEnv(t).set()
|
||||
|
||||
pc, err := konfig.EnabledPluginConfig()
|
||||
pc, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
|
||||
rf := resmap.NewFactory(
|
||||
resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()),
|
||||
transformer.NewFactoryImpl())
|
||||
|
||||
result := &HarnessEnhanced{
|
||||
Harness: Harness{t: t, fSys: fSys},
|
||||
Harness: MakeHarness(t),
|
||||
pte: pte,
|
||||
rf: rf,
|
||||
pl: pLdr.NewLoader(pc, rf)}
|
||||
|
||||
// Point the file loader to the root ('/') of the in-memory file system.
|
||||
result.ResetLoaderRoot(filesys.Separator)
|
||||
|
||||
return result
|
||||
@@ -55,21 +72,23 @@ func (th *HarnessEnhanced) Reset() {
|
||||
th.pte.reset()
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced {
|
||||
return th.BuildGoPlugin(konfig.BuiltinPluginPackage, "", k)
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) BuildGoPlugin(g, v, k string) *HarnessEnhanced {
|
||||
th.pte.buildGoPlugin(g, v, k)
|
||||
th.pte.prepareGoPlugin(g, v, k)
|
||||
return th
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) PrepExecPlugin(g, v, k string) *HarnessEnhanced {
|
||||
th.pte.prepExecPlugin(g, v, k)
|
||||
return th
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced {
|
||||
th.pte.buildGoPlugin(konfig.BuiltinPluginPackage, "", k)
|
||||
th.pte.prepareExecPlugin(g, v, k)
|
||||
return th
|
||||
}
|
||||
|
||||
// ResetLoaderRoot interprets its argument as an absolute directory path.
|
||||
// It creates the directory, and creates the harness's file loader
|
||||
// rooted in that directory.
|
||||
func (th *HarnessEnhanced) ResetLoaderRoot(root string) {
|
||||
if err := th.fSys.Mkdir(root); err != nil {
|
||||
th.t.Fatal(err)
|
||||
@@ -109,12 +128,68 @@ func (th *HarnessEnhanced) LoadAndRunTransformer(
|
||||
return resMap
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) RunTransformerAndCheckResult(
|
||||
config, input, expected string) {
|
||||
for _, b := range []bool{true, false} {
|
||||
th.t.Run(fmt.Sprintf("yaml-%v", b), func(t *testing.T) {
|
||||
c, err := toggleYamlSupportField(config, b)
|
||||
if err != nil {
|
||||
th.t.Fatalf("Err: %v", err)
|
||||
}
|
||||
resMap, err := th.RunTransformer(c, input)
|
||||
if err != nil {
|
||||
th.t.Fatalf("Err: %v", err)
|
||||
}
|
||||
th.AssertActualEqualsExpected(resMap, expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func toggleYamlSupportField(config string, yamlSupport bool) (string, error) {
|
||||
var out bytes.Buffer
|
||||
rw := kio.ByteReadWriter{
|
||||
Reader: bytes.NewBufferString(config),
|
||||
Writer: &out,
|
||||
}
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&rw},
|
||||
Filters: []kio.Filter{
|
||||
kio.FilterAll(yaml.FilterFunc(
|
||||
func(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
return node.Pipe(yaml.FieldSetter{
|
||||
Name: "yamlSupport",
|
||||
StringValue: strconv.FormatBool(yamlSupport),
|
||||
})
|
||||
}),
|
||||
),
|
||||
},
|
||||
Outputs: []kio.Writer{&rw},
|
||||
}.Execute()
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer(
|
||||
config, input string) error {
|
||||
_, err := th.RunTransformer(config, input)
|
||||
return err
|
||||
}
|
||||
|
||||
type AssertFunc func(t *testing.T, err error)
|
||||
|
||||
func (th *HarnessEnhanced) RunTransformerAndCheckError(
|
||||
config, input string, assertFn AssertFunc) {
|
||||
for _, b := range []bool{true, false} {
|
||||
th.t.Run(fmt.Sprintf("yaml-%v", b), func(t *testing.T) {
|
||||
c, err := toggleYamlSupportField(config, b)
|
||||
if err != nil {
|
||||
th.t.Fatalf("Err: %v", err)
|
||||
}
|
||||
_, err = th.RunTransformer(c, input)
|
||||
assertFn(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (th *HarnessEnhanced) RunTransformer(
|
||||
config, input string) (resmap.ResMap, error) {
|
||||
resMap, err := th.rf.NewResMapFromBytes([]byte(input))
|
||||
|
||||
@@ -4,11 +4,7 @@
|
||||
package kusttest_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
@@ -16,17 +12,14 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/konfig"
|
||||
)
|
||||
|
||||
// pluginTestEnv manages plugins for tests.
|
||||
// It manages a Go plugin compiler,
|
||||
// makes and removes a temporary working directory,
|
||||
// and sets/resets shell env vars as needed.
|
||||
// pluginTestEnv manages compiling plugins for tests.
|
||||
// It manages a Go plugin compiler, and sets/resets shell env vars as needed.
|
||||
type pluginTestEnv struct {
|
||||
t *testing.T
|
||||
compiler *compiler.Compiler
|
||||
srcRoot string
|
||||
workDir string
|
||||
oldXdg string
|
||||
wasSet bool
|
||||
t *testing.T
|
||||
compiler *compiler.Compiler
|
||||
pluginRoot string
|
||||
oldXdg string
|
||||
wasSet bool
|
||||
}
|
||||
|
||||
// newPluginTestEnv returns a new instance of pluginTestEnv.
|
||||
@@ -39,76 +32,44 @@ func newPluginTestEnv(t *testing.T) *pluginTestEnv {
|
||||
// plugin code - this FileSystem has nothing to do with
|
||||
// the FileSystem used for loading config yaml in the tests.
|
||||
func (x *pluginTestEnv) set() *pluginTestEnv {
|
||||
x.createWorkDir()
|
||||
var err error
|
||||
x.srcRoot, err = compiler.DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
|
||||
x.pluginRoot, err = compiler.DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
|
||||
if err != nil {
|
||||
x.t.Error(err)
|
||||
}
|
||||
x.compiler = compiler.NewCompiler(x.srcRoot, x.workDir)
|
||||
x.compiler = compiler.NewCompiler(x.pluginRoot)
|
||||
x.setEnv()
|
||||
return x
|
||||
}
|
||||
|
||||
// reset restores the environment to pre-test state.
|
||||
func (x *pluginTestEnv) reset() {
|
||||
// Calling Cleanup forces recompilation in a test file with multiple
|
||||
// calls to MakeEnhancedHarness - so leaving it out. Your .gitignore
|
||||
// should ignore .so files anyway.
|
||||
// x.compiler.Cleanup()
|
||||
x.resetEnv()
|
||||
x.removeWorkDir()
|
||||
}
|
||||
|
||||
// buildGoPlugin compiles a Go plugin, leaving the newly
|
||||
// created object code in the right place - a temporary
|
||||
// working directory pointed to by KustomizePluginHomeEnv.
|
||||
// This avoids overwriting anything the user/developer has
|
||||
// otherwise created.
|
||||
func (x *pluginTestEnv) buildGoPlugin(g, v, k string) {
|
||||
err := x.compiler.Compile(g, v, k)
|
||||
// prepareGoPlugin compiles a Go plugin, leaving the newly
|
||||
// created object code alongside the src code.
|
||||
func (x *pluginTestEnv) prepareGoPlugin(g, v, k string) {
|
||||
x.compiler.SetGVK(g, v, k)
|
||||
err := x.compiler.Compile()
|
||||
if err != nil {
|
||||
x.t.Errorf("compile failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// prepExecPlugin copies an exec plugin from it's
|
||||
// home in the discovered srcRoot to the same temp
|
||||
// directory where Go plugin object code is placed.
|
||||
// Kustomize (and its tests) expect to find plugins
|
||||
// (Go or Exec) in the same spot, and since the test
|
||||
// framework is compiling Go plugins to a temp dir,
|
||||
// it must likewise copy Exec plugins to that same
|
||||
// temp dir.
|
||||
func (x *pluginTestEnv) prepExecPlugin(g, v, k string) {
|
||||
lowK := strings.ToLower(k)
|
||||
src := filepath.Join(x.srcRoot, g, v, lowK, k)
|
||||
tmp := filepath.Join(x.workDir, g, v, lowK, k)
|
||||
if err := os.MkdirAll(filepath.Dir(tmp), 0755); err != nil {
|
||||
x.t.Errorf("error making directory: %s", filepath.Dir(tmp))
|
||||
}
|
||||
cmd := exec.Command("cp", src, tmp)
|
||||
cmd.Env = os.Environ()
|
||||
if err := cmd.Run(); err != nil {
|
||||
x.t.Errorf("error copying %s to %s: %v", src, tmp, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *pluginTestEnv) createWorkDir() {
|
||||
var err error
|
||||
x.workDir, err = ioutil.TempDir("", "kustomize-plugin-tests")
|
||||
if err != nil {
|
||||
x.t.Errorf("failed to make work dir: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *pluginTestEnv) removeWorkDir() {
|
||||
err := os.RemoveAll(x.workDir)
|
||||
if err != nil {
|
||||
x.t.Errorf(
|
||||
"removing work dir: %s %v", x.workDir, err)
|
||||
}
|
||||
func (x *pluginTestEnv) prepareExecPlugin(_, _, _ string) {
|
||||
// Do nothing. At one point this method
|
||||
// copied the exec plugin directory to a temp dir
|
||||
// and ran it from there. Left as a hook.
|
||||
}
|
||||
|
||||
func (x *pluginTestEnv) setEnv() {
|
||||
x.oldXdg, x.wasSet = os.LookupEnv(konfig.KustomizePluginHomeEnv)
|
||||
os.Setenv(konfig.KustomizePluginHomeEnv, x.workDir)
|
||||
os.Setenv(konfig.KustomizePluginHomeEnv, x.pluginRoot)
|
||||
}
|
||||
|
||||
func (x *pluginTestEnv) resetEnv() {
|
||||
|
||||
@@ -8,18 +8,14 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GenArgs contains both GeneratorArgs and GeneratorOptions.
|
||||
// GenArgs is a facade over GeneratorArgs, exposing a few readonly properties.
|
||||
type GenArgs struct {
|
||||
args *GeneratorArgs
|
||||
opts *GeneratorOptions
|
||||
}
|
||||
|
||||
// NewGenArgs returns a new object of GenArgs
|
||||
func NewGenArgs(args *GeneratorArgs, opts *GeneratorOptions) *GenArgs {
|
||||
return &GenArgs{
|
||||
args: args,
|
||||
opts: opts,
|
||||
}
|
||||
// NewGenArgs returns a new instance of GenArgs.
|
||||
func NewGenArgs(args *GeneratorArgs) *GenArgs {
|
||||
return &GenArgs{args: args}
|
||||
}
|
||||
|
||||
func (g *GenArgs) String() string {
|
||||
@@ -38,7 +34,7 @@ func (g *GenArgs) String() string {
|
||||
// content hash should be appended to the name of the resource.
|
||||
func (g *GenArgs) ShouldAddHashSuffixToName() bool {
|
||||
return g.args != nil &&
|
||||
(g.opts == nil || !g.opts.DisableNameSuffixHash)
|
||||
(g.args.Options == nil || !g.args.Options.DisableNameSuffixHash)
|
||||
}
|
||||
|
||||
// Behavior returns Behavior field of GeneratorArgs
|
||||
|
||||
@@ -24,8 +24,10 @@ func TestGenArgs_String(t *testing.T) {
|
||||
},
|
||||
{
|
||||
ga: NewGenArgs(
|
||||
&GeneratorArgs{Behavior: "merge"},
|
||||
&GeneratorOptions{DisableNameSuffixHash: false}),
|
||||
&GeneratorArgs{
|
||||
Behavior: "merge",
|
||||
Options: &GeneratorOptions{DisableNameSuffixHash: false},
|
||||
}),
|
||||
expected: "{nsfx:true,beh:merge}",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -22,6 +22,6 @@ type GeneratorArgs struct {
|
||||
// KvPairSources for the generator.
|
||||
KvPairSources `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
|
||||
// GeneratorOptions modify this generator
|
||||
GeneratorOptions *GeneratorOptions `json:"generatorOptions,omitempty" yaml:"generatorOptions,omitempty"`
|
||||
// Local overrides to global generatorOptions field.
|
||||
Options *GeneratorOptions `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
}
|
||||
|
||||
@@ -16,3 +16,55 @@ type GeneratorOptions struct {
|
||||
// resource contents.
|
||||
DisableNameSuffixHash bool `json:"disableNameSuffixHash,omitempty" yaml:"disableNameSuffixHash,omitempty"`
|
||||
}
|
||||
|
||||
// MergeGlobalOptionsIntoLocal merges two instances of GeneratorOptions.
|
||||
// Values in the first 'local' argument cannot be overridden by the second
|
||||
// 'global' argument, except in the case of booleans.
|
||||
//
|
||||
// With booleans, there's no way to distinguish an 'intentional'
|
||||
// false from 'default' false. So the rule is, if the global value
|
||||
// of the value of a boolean is true, i.e. disable, it trumps the
|
||||
// local value. If the global value is false, then the local value is
|
||||
// respected. Bottom line: a local false cannot override a global true.
|
||||
//
|
||||
// boolean fields are always a bad idea; should always use enums instead.
|
||||
func MergeGlobalOptionsIntoLocal(
|
||||
localOpts *GeneratorOptions,
|
||||
globalOpts *GeneratorOptions) *GeneratorOptions {
|
||||
if globalOpts == nil {
|
||||
return localOpts
|
||||
}
|
||||
if localOpts == nil {
|
||||
localOpts = &GeneratorOptions{}
|
||||
}
|
||||
overrideMap(&localOpts.Labels, globalOpts.Labels)
|
||||
overrideMap(&localOpts.Annotations, globalOpts.Annotations)
|
||||
if globalOpts.DisableNameSuffixHash {
|
||||
localOpts.DisableNameSuffixHash = true
|
||||
}
|
||||
return localOpts
|
||||
}
|
||||
|
||||
func overrideMap(localMap *map[string]string, globalMap map[string]string) {
|
||||
if *localMap == nil {
|
||||
if globalMap != nil {
|
||||
*localMap = CopyMap(globalMap)
|
||||
}
|
||||
return
|
||||
}
|
||||
for k, v := range globalMap {
|
||||
_, ok := (*localMap)[k]
|
||||
if !ok {
|
||||
(*localMap)[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CopyMap copies a map.
|
||||
func CopyMap(in map[string]string) map[string]string {
|
||||
out := make(map[string]string)
|
||||
for k, v := range in {
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
125
api/types/generatoroptions_test.go
Normal file
125
api/types/generatoroptions_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestMergeGlobalOptionsIntoLocal(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
local *GeneratorOptions
|
||||
global *GeneratorOptions
|
||||
expected *GeneratorOptions
|
||||
}{
|
||||
{
|
||||
name: "everything nil",
|
||||
local: nil,
|
||||
global: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "nil global",
|
||||
local: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{"fruit": "apple"},
|
||||
},
|
||||
global: nil,
|
||||
expected: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{"fruit": "apple"},
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil local",
|
||||
local: nil,
|
||||
global: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{"fruit": "apple"},
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{"fruit": "apple"},
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global doesn't damage local",
|
||||
local: &GeneratorOptions{
|
||||
Labels: map[string]string{"pet": "dog"},
|
||||
Annotations: map[string]string{
|
||||
"fruit": "apple"},
|
||||
},
|
||||
global: &GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"pet": "cat",
|
||||
"simpson": "homer",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"fruit": "peach",
|
||||
"tesla": "Y",
|
||||
},
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
Labels: map[string]string{
|
||||
"pet": "dog",
|
||||
"simpson": "homer",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"fruit": "apple",
|
||||
"tesla": "Y",
|
||||
},
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "global disable trumps local",
|
||||
local: &GeneratorOptions{
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
global: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "local disable works",
|
||||
local: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
global: &GeneratorOptions{
|
||||
DisableNameSuffixHash: false,
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "everyone wants disable",
|
||||
local: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
global: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
expected: &GeneratorOptions{
|
||||
DisableNameSuffixHash: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
actual := MergeGlobalOptionsIntoLocal(tc.local, tc.global)
|
||||
if !reflect.DeepEqual(tc.expected, actual) {
|
||||
t.Fatalf("%s annotations: Expected '%v', got '%v'",
|
||||
tc.name, tc.expected, *actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,22 +10,23 @@ type PluginConfig struct {
|
||||
// containing the fields 'apiVersion' and 'kind', e.g.
|
||||
// apiVersion: apps/v1
|
||||
// kind: Deployment
|
||||
// When kustomize reads a plugin configuration file (as as result
|
||||
// of seeing the file name in the 'generators:' or 'transformers:'
|
||||
// field in a kustomization file), it must then locate the plugin
|
||||
// code (Go plugin or exec plugin).
|
||||
// Every kustomize plugin (its code, its tests, supporting data
|
||||
// kustomize reads plugin configuration data from a file path
|
||||
// specified in the 'generators:' or 'transformers:' field of a
|
||||
// kustomization file. kustomize must then use this data to both
|
||||
// locate the plugin and configure it.
|
||||
// Every kustomize plugin (its code, its tests, its supporting data
|
||||
// files, etc.) must be housed in its own directory at
|
||||
// ${AbsPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind})
|
||||
// where
|
||||
// - ${AbsPluginHome} is an absolute path, defined below.
|
||||
// - ${pluginApiVersion} is taken from the plugin config file.
|
||||
// - ${pluginKind} is taken from the plugin config file.
|
||||
// The value of AbsPluginHome can be any absolute path, but might
|
||||
// default to $XDG_CONFIG_HOME/kustomize/plugin.
|
||||
// The value of AbsPluginHome can be any absolute path.
|
||||
AbsPluginHome string
|
||||
|
||||
// PluginRestrictions defines the plugin restriction state.
|
||||
// See type for more information.
|
||||
// PluginRestrictions distinguishes plugin restrictions.
|
||||
PluginRestrictions PluginRestrictions
|
||||
|
||||
// BpLoadingOptions distinguishes builtin plugin behaviors.
|
||||
BpLoadingOptions BuiltinPluginLoadingOptions
|
||||
}
|
||||
|
||||
@@ -26,3 +26,18 @@ const (
|
||||
// No restrictions, do whatever you want.
|
||||
PluginRestrictionsNone
|
||||
)
|
||||
|
||||
// BuiltinPluginLoadingOptions distinguish ways in which builtin plugins are used.
|
||||
//go:generate stringer -type=BuiltinPluginLoadingOptions
|
||||
type BuiltinPluginLoadingOptions int
|
||||
|
||||
const (
|
||||
BploUndefined BuiltinPluginLoadingOptions = iota
|
||||
|
||||
// Desired in production use for performance.
|
||||
BploUseStaticallyLinked
|
||||
|
||||
// Desired in testing and development cycles where it's undesirable
|
||||
// to generate static code.
|
||||
BploLoadFromFileSys
|
||||
)
|
||||
|
||||
@@ -18,6 +18,6 @@ List setters for Resources.
|
||||
|
||||
Show setters:
|
||||
|
||||
$ config set DIR/
|
||||
$ config list-setters DIR/
|
||||
NAME DESCRIPTION VALUE TYPE COUNT SETBY
|
||||
name-prefix '' PREFIX string 2
|
||||
|
||||
@@ -28,8 +28,8 @@ the configuration as comments.
|
||||
Optional. The value to set on the field.
|
||||
|
||||
|
||||
To print the possible setters for the Resources in a directory, run `set` on
|
||||
a directory -- e.g. `kustomize config set DIR/`.
|
||||
To print the possible setters for the Resources in a directory, run
|
||||
`list-setters` on a directory -- e.g. `kustomize config list-setters DIR/`.
|
||||
|
||||
#### Tips
|
||||
|
||||
@@ -58,7 +58,7 @@ To create a custom setter for a field see: `kustomize help config create-setter`
|
||||
|
||||
List setters: Show the possible setters
|
||||
|
||||
$ config set DIR/
|
||||
$ config list-setters DIR/
|
||||
NAME DESCRIPTION VALUE TYPE COUNT SETBY
|
||||
name-prefix '' PREFIX string 2
|
||||
|
||||
@@ -69,7 +69,7 @@ To create a custom setter for a field see: `kustomize help config create-setter`
|
||||
|
||||
List setters: Show the new values
|
||||
|
||||
$ config set DIR/
|
||||
$ config list-setters DIR/
|
||||
NAME DESCRIPTION VALUE TYPE COUNT SETBY
|
||||
name-prefix 'test environment' test string 2 dev
|
||||
|
||||
|
||||
@@ -10,5 +10,7 @@ require (
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.4.0
|
||||
k8s.io/apimachinery v0.17.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.0
|
||||
)
|
||||
|
||||
replace sigs.k8s.io/kustomize/kyaml v0.0.0 => ../../kyaml
|
||||
|
||||
@@ -185,7 +185,5 @@ k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3 h1:zbeHVTMCQPtWgjIH/YYJZC45mm7coTdw2TblyJ79BrY=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.3/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/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=
|
||||
|
||||
@@ -29,6 +29,8 @@ func NewListSettersRunner(parent string) *ListSettersRunner {
|
||||
PreRunE: r.preRunE,
|
||||
RunE: r.runE,
|
||||
}
|
||||
c.Flags().BoolVar(&r.Markdown, "markdown", false,
|
||||
"output as github markdown")
|
||||
fixDocs(parent, c)
|
||||
r.Command = c
|
||||
return r
|
||||
@@ -39,9 +41,10 @@ func ListSettersCommand(parent string) *cobra.Command {
|
||||
}
|
||||
|
||||
type ListSettersRunner struct {
|
||||
Command *cobra.Command
|
||||
Lookup setters.LookupSetters
|
||||
List setters2.List
|
||||
Command *cobra.Command
|
||||
Lookup setters.LookupSetters
|
||||
List setters2.List
|
||||
Markdown bool
|
||||
}
|
||||
|
||||
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
@@ -64,7 +67,7 @@ func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
|
||||
if err := r.List.List(path, args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
table := newTable(c.OutOrStdout())
|
||||
table := newTable(c.OutOrStdout(), r.Markdown)
|
||||
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT"})
|
||||
for i := range r.List.Setters {
|
||||
s := r.List.Setters[i]
|
||||
@@ -92,13 +95,19 @@ func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
|
||||
return handleError(c, lookup(r.Lookup, c, args))
|
||||
}
|
||||
|
||||
func newTable(o io.Writer) *tablewriter.Table {
|
||||
func newTable(o io.Writer, m bool) *tablewriter.Table {
|
||||
table := tablewriter.NewWriter(o)
|
||||
table.SetRowLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetHeaderLine(false)
|
||||
table.SetColumnSeparator(" ")
|
||||
table.SetCenterSeparator(" ")
|
||||
if m {
|
||||
// markdown format
|
||||
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
|
||||
table.SetCenterSeparator("|")
|
||||
} else {
|
||||
table.SetBorder(false)
|
||||
table.SetHeaderLine(false)
|
||||
table.SetColumnSeparator(" ")
|
||||
table.SetCenterSeparator(" ")
|
||||
}
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
return table
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio/filters"
|
||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -50,10 +51,16 @@ func GetRunFnRunner(name string) *RunFnRunner {
|
||||
&r.StarName, "star-name", "", "name of starlark program.")
|
||||
r.Command.Flags().MarkHidden("star-name")
|
||||
|
||||
r.Command.Flags().StringVar(
|
||||
&r.ResultsDir, "results-dir", "", "write function results to this dir")
|
||||
|
||||
r.Command.Flags().BoolVar(
|
||||
&r.Network, "network", false, "enable network access for functions that declare it")
|
||||
r.Command.Flags().StringVar(
|
||||
&r.NetworkName, "network-name", "bridge", "the docker network to run the container in")
|
||||
r.Command.Flags().StringArrayVar(
|
||||
&r.Mounts, "mount", []string{},
|
||||
"a list of storage options read from the filesystem")
|
||||
return r
|
||||
}
|
||||
|
||||
@@ -73,8 +80,10 @@ type RunFnRunner struct {
|
||||
StarPath string
|
||||
StarName string
|
||||
RunFns runfn.RunFns
|
||||
ResultsDir string
|
||||
Network bool
|
||||
NetworkName string
|
||||
Mounts []string
|
||||
}
|
||||
|
||||
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
|
||||
@@ -199,9 +208,17 @@ data: {}
|
||||
return []*yaml.RNode{rc}, nil
|
||||
}
|
||||
|
||||
func toStorageMounts(mounts []string) []filters.StorageMount {
|
||||
var sms []filters.StorageMount
|
||||
for _, mount := range mounts {
|
||||
sms = append(sms, filters.StringToStorageMount(mount))
|
||||
}
|
||||
return sms
|
||||
}
|
||||
|
||||
func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
if r.EnableStar != (r.StarPath != "") {
|
||||
return errors.Errorf("must specify --star-path with --enable-star")
|
||||
if !r.EnableStar && r.StarPath != "" {
|
||||
return errors.Errorf("must specify --enable-star with --star-path")
|
||||
}
|
||||
|
||||
if c.ArgsLenAtDash() >= 0 && r.Image == "" &&
|
||||
@@ -240,6 +257,9 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
path = args[0]
|
||||
}
|
||||
|
||||
// parse mounts to set storageMounts
|
||||
storageMounts := toStorageMounts(r.Mounts)
|
||||
|
||||
r.RunFns = runfn.RunFns{
|
||||
FunctionPaths: r.FnPaths,
|
||||
GlobalScope: r.GlobalScope,
|
||||
@@ -250,6 +270,8 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
|
||||
Network: r.Network,
|
||||
NetworkName: r.NetworkName,
|
||||
EnableStarlark: r.EnableStar,
|
||||
StorageMounts: storageMounts,
|
||||
ResultsDir: r.ResultsDir,
|
||||
}
|
||||
|
||||
// don't consider args for the function
|
||||
|
||||
@@ -11,22 +11,25 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/kyaml/runfn"
|
||||
)
|
||||
|
||||
// TestRunFnCommand_preRunE verifies that preRunE correctly parses the commandline
|
||||
// flags and arguments into the RunFns structure to be executed.
|
||||
func TestRunFnCommand_preRunE(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expected string
|
||||
err string
|
||||
path string
|
||||
input io.Reader
|
||||
output io.Writer
|
||||
functionPaths []string
|
||||
network bool
|
||||
networkName string
|
||||
name string
|
||||
args []string
|
||||
expected string
|
||||
expectedStruct *runfn.RunFns
|
||||
err string
|
||||
path string
|
||||
input io.Reader
|
||||
output io.Writer
|
||||
functionPaths []string
|
||||
network bool
|
||||
networkName string
|
||||
mount []string
|
||||
}{
|
||||
{
|
||||
name: "config map",
|
||||
@@ -180,7 +183,7 @@ apiVersion: v1
|
||||
"--star-name", "foo",
|
||||
"--", "Foo", "g=h"},
|
||||
path: "dir",
|
||||
err: "must specify --star-path with --enable-star",
|
||||
err: "must specify --enable-star with --star-path",
|
||||
},
|
||||
{
|
||||
name: "image-star-not-enabled",
|
||||
@@ -190,7 +193,17 @@ apiVersion: v1
|
||||
"--star-name", "foo",
|
||||
"--", "Foo", "g=h"},
|
||||
path: "dir",
|
||||
err: "must specify --star-path with --enable-star",
|
||||
err: "must specify --enable-star with --star-path",
|
||||
},
|
||||
{
|
||||
name: "star-enabled",
|
||||
args: []string{"run", "dir", "--enable-star"},
|
||||
path: "dir",
|
||||
expectedStruct: &runfn.RunFns{
|
||||
Path: "dir",
|
||||
NetworkName: "bridge",
|
||||
EnableStarlark: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "function paths",
|
||||
@@ -213,6 +226,46 @@ metadata:
|
||||
data: {g: h, i: j=k}
|
||||
kind: Foo
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "custom kind with storage mounts",
|
||||
args: []string{
|
||||
"run", "dir", "--mount", "type=bind,src=/mount/path,dst=/local/",
|
||||
"--mount", "type=volume,src=myvol,dst=/local/",
|
||||
"--mount", "type=tmpfs,dst=/local/",
|
||||
"--image", "foo:bar", "--", "Foo", "g=h", "i=j=k"},
|
||||
path: "dir",
|
||||
mount: []string{"type=bind,src=/mount/path,dst=/local/", "type=volume,src=myvol,dst=/local/", "type=tmpfs,dst=/local/"},
|
||||
expected: `
|
||||
metadata:
|
||||
name: function-input
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container: {image: 'foo:bar'}
|
||||
data: {g: h, i: j=k}
|
||||
kind: Foo
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "results_dir",
|
||||
args: []string{"run", "dir", "--results-dir", "foo/", "--image", "foo:bar", "--", "a=b", "c=d", "e=f"},
|
||||
path: "dir",
|
||||
expectedStruct: &runfn.RunFns{
|
||||
Path: "dir",
|
||||
NetworkName: "bridge",
|
||||
ResultsDir: "foo/",
|
||||
},
|
||||
expected: `
|
||||
metadata:
|
||||
name: function-input
|
||||
annotations:
|
||||
config.kubernetes.io/function: |
|
||||
container: {image: 'foo:bar'}
|
||||
data: {a: b, c: d, e: f}
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -303,6 +356,14 @@ apiVersion: v1
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, r.RunFns, r.RunFns) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if !assert.Equal(t, toStorageMounts(tt.mount), r.RunFns.StorageMounts) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
// check if Functions were set
|
||||
if tt.expected != "" {
|
||||
if !assert.Len(t, r.RunFns.Functions, 1) {
|
||||
@@ -314,6 +375,14 @@ apiVersion: v1
|
||||
}
|
||||
}
|
||||
|
||||
if tt.expectedStruct != nil {
|
||||
r.RunFns.Functions = nil
|
||||
tt.expectedStruct.FunctionPaths = tt.functionPaths
|
||||
if !assert.Equal(t, *tt.expectedStruct, r.RunFns) {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ List setters for Resources.
|
||||
var ListSettersExamples = `
|
||||
Show setters:
|
||||
|
||||
$ config set DIR/
|
||||
$ config list-setters DIR/
|
||||
NAME DESCRIPTION VALUE TYPE COUNT SETBY
|
||||
name-prefix '' PREFIX string 2`
|
||||
|
||||
@@ -289,8 +289,8 @@ the configuration as comments.
|
||||
Optional. The value to set on the field.
|
||||
|
||||
|
||||
To print the possible setters for the Resources in a directory, run ` + "`" + `set` + "`" + ` on
|
||||
a directory -- e.g. ` + "`" + `kustomize config set DIR/` + "`" + `.
|
||||
To print the possible setters for the Resources in a directory, run
|
||||
` + "`" + `list-setters` + "`" + ` on a directory -- e.g. ` + "`" + `kustomize config list-setters DIR/` + "`" + `.
|
||||
|
||||
#### Tips
|
||||
|
||||
@@ -318,7 +318,7 @@ var SetExamples = `
|
||||
|
||||
List setters: Show the possible setters
|
||||
|
||||
$ config set DIR/
|
||||
$ config list-setters DIR/
|
||||
NAME DESCRIPTION VALUE TYPE COUNT SETBY
|
||||
name-prefix '' PREFIX string 2
|
||||
|
||||
@@ -329,7 +329,7 @@ var SetExamples = `
|
||||
|
||||
List setters: Show the new values
|
||||
|
||||
$ config set DIR/
|
||||
$ config list-setters DIR/
|
||||
NAME DESCRIPTION VALUE TYPE COUNT SETBY
|
||||
name-prefix 'test environment' test string 2 dev
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
# Builtin Plugins
|
||||
|
||||
A list of kustomize's builtin plugins (both
|
||||
generators and transformers).
|
||||
A list of kustomize's builtin plugins - both
|
||||
generators and transformers.
|
||||
|
||||
For each plugin, an example is given for
|
||||
|
||||
@@ -92,16 +92,35 @@ one ConfigMap resource (it's a generator of n maps).
|
||||
|
||||
The example below creates three ConfigMaps. One with the names and contents of
|
||||
the given files, one with key/value as data, and a third which sets an
|
||||
annotation and label via generatorOptions for that single ConfigMap.
|
||||
annotation and label via `options` for that single ConfigMap.
|
||||
|
||||
Each configMapGenerator item accepts a parameter of
|
||||
`behavior: [create|replace|merge]`.
|
||||
This allows an overlay to modify or
|
||||
replace an existing configMap from the parent.
|
||||
|
||||
Also, each entry has an `options` field, that has the
|
||||
same subfields as the kustomization file's `generatorOptions` field.
|
||||
|
||||
This `options` field allows one to add labels and/or
|
||||
annotations to the generated instance, or to individually
|
||||
disable the name suffix hash for that instance.
|
||||
Labels and annotations added here will not be overwritten
|
||||
by the global options associated with the kustomization
|
||||
file `generatorOptions` field. However, due to how
|
||||
booleans behave, if the global `generatorOptions` field
|
||||
specifies `disableNameSuffixHash: true`, this will
|
||||
trump any attempt to locally override it.
|
||||
|
||||
```
|
||||
# These labels are added to all configmaps and secrets.
|
||||
generatorOptions:
|
||||
labels:
|
||||
fruit: apple
|
||||
|
||||
configMapGenerator:
|
||||
- name: my-java-server-props
|
||||
behavior: merge
|
||||
files:
|
||||
- application.properties
|
||||
- more.properties
|
||||
@@ -109,10 +128,14 @@ configMapGenerator:
|
||||
literals:
|
||||
- JAVA_HOME=/opt/java/jdk
|
||||
- JAVA_TOOL_OPTIONS=-agentlib:hprof
|
||||
options:
|
||||
disableNameSuffixHash: true
|
||||
labels:
|
||||
pet: dog
|
||||
- name: dashboards
|
||||
files:
|
||||
- mydashboard.json
|
||||
generatorOptions:
|
||||
options:
|
||||
annotations:
|
||||
dashboard: "1"
|
||||
labels:
|
||||
@@ -138,8 +161,6 @@ configMapGenerator:
|
||||
### Usage via plugin
|
||||
#### Arguments
|
||||
|
||||
> [types.GeneratorOptions]
|
||||
>
|
||||
> [types.ConfigMapArgs]
|
||||
|
||||
#### Example
|
||||
@@ -640,6 +661,9 @@ results in the creation of
|
||||
one Secret resource
|
||||
(it's a generator of n secrets).
|
||||
|
||||
This works like the `configMapGenerator` field
|
||||
described above.
|
||||
|
||||
```
|
||||
secretGenerator:
|
||||
- name: app-tls
|
||||
@@ -663,7 +687,7 @@ secretGenerator:
|
||||
files:
|
||||
- app-config.yaml
|
||||
type: Opaque
|
||||
generatorOptions:
|
||||
options:
|
||||
annotations:
|
||||
app_config: "true"
|
||||
labels:
|
||||
@@ -676,8 +700,6 @@ secretGenerator:
|
||||
|
||||
> [types.ObjectMeta]
|
||||
>
|
||||
> [types.GeneratorOptions]
|
||||
>
|
||||
> [types.SecretArgs]
|
||||
|
||||
#### Example
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
# Demo: applying a json patch
|
||||
# JSON Patching
|
||||
|
||||
A kustomization file supports customizing resources via [JSON patches](https://tools.ietf.org/html/rfc6902).
|
||||
[JSON patches]: https://tools.ietf.org/html/rfc6902
|
||||
[JSON patch]: https://tools.ietf.org/html/rfc6902
|
||||
|
||||
The example below modifies an `Ingress` object with such a patch.
|
||||
A kustomization file supports customizing
|
||||
resources via [JSON patches].
|
||||
|
||||
Make a `kustomization` containing an ingress resource.
|
||||
Make a place to work:
|
||||
|
||||
<!-- @createIngress @testAgainstLatestRelease -->
|
||||
<!-- @placeToWork @testAgainstLatestRelease -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- ingress.yaml
|
||||
EOF
|
||||
We'll be editting an `Ingress` object:
|
||||
|
||||
<!-- @ingress @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress.yaml
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
@@ -25,94 +27,175 @@ spec:
|
||||
- host: foo.bar.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: homepage
|
||||
servicePort: 8888
|
||||
- path: /api
|
||||
backend:
|
||||
serviceName: my-api
|
||||
servicePort: 80
|
||||
servicePort: 7701
|
||||
- path: /test
|
||||
backend:
|
||||
serviceName: hello
|
||||
servicePort: 7702
|
||||
EOF
|
||||
```
|
||||
|
||||
Declare a JSON patch file to update two fields of the Ingress object:
|
||||
The edits we want to make are:
|
||||
|
||||
- change host from `foo.bar.com` to `foo.bar.io`
|
||||
- change servicePort from `80` to `8080`
|
||||
- change the value of `host` to _foo.bar.io_
|
||||
- change the port for `'/'` from _8888_ to _80_
|
||||
- insert an entirely new serving path `/healthz`
|
||||
at a particular point in the `paths` list,
|
||||
rather than at the end or the beginning.
|
||||
|
||||
Here's the patch file to do that:
|
||||
|
||||
<!-- @addJsonPatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress_patch.json
|
||||
[
|
||||
{"op": "replace", "path": "/spec/rules/0/host", "value": "foo.bar.io"},
|
||||
{"op": "replace", "path": "/spec/rules/0/http/paths/0/backend/servicePort", "value": 8080}
|
||||
{"op": "replace",
|
||||
"path": "/spec/rules/0/host",
|
||||
"value": "foo.bar.io"},
|
||||
|
||||
{"op": "replace",
|
||||
"path": "/spec/rules/0/http/paths/0/backend/servicePort",
|
||||
"value": 80},
|
||||
|
||||
{"op": "add",
|
||||
"path": "/spec/rules/0/http/paths/1",
|
||||
"value": { "path": "/healthz", "backend": {"servicePort":7700} }}
|
||||
]
|
||||
EOF
|
||||
```
|
||||
|
||||
You can also write the patch in YAML format. This example also shows the "add" operation:
|
||||
We'll of course need a `kustomization` file
|
||||
referring to the `Ingress`:
|
||||
|
||||
<!-- @addYamlPatch @testAgainstLatestRelease -->
|
||||
<!-- @kustomization @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress_patch.yaml
|
||||
- op: replace
|
||||
path: /spec/rules/0/host
|
||||
value: foo.bar.io
|
||||
|
||||
- op: add
|
||||
path: /spec/rules/0/http/paths/-
|
||||
value:
|
||||
path: '/test'
|
||||
backend:
|
||||
serviceName: my-test
|
||||
servicePort: 8081
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- ingress.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
Apply the patch by adding _patchesJson6902_ field in kustomization.yaml
|
||||
To this same `kustomization` file, add a
|
||||
`patches` field refering to
|
||||
the patch file we just made and
|
||||
target it to the `Ingress` object:
|
||||
|
||||
<!-- @applyJsonPatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >>$DEMO_HOME/kustomization.yaml
|
||||
patchesJson6902:
|
||||
- target:
|
||||
patches:
|
||||
- path: ingress_patch.json
|
||||
target:
|
||||
group: networking.k8s.io
|
||||
version: v1beta1
|
||||
kind: Ingress
|
||||
name: my-ingress
|
||||
path: ingress_patch.json
|
||||
EOF
|
||||
```
|
||||
|
||||
Running `kustomize build $DEMO_HOME`, in the output confirm that host has been updated correctly.
|
||||
<!-- @confirmHost @testAgainstLatestRelease -->
|
||||
Define the expected output:
|
||||
<!-- @expected @testAgainstLatestRelease -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "host: foo.bar.io" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
Running `kustomize build $DEMO_HOME`, in the output confirm that the servicePort has been updated correctly.
|
||||
<!-- @confirmServicePort @testAgainstLatestRelease -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "servicePort: 8080" | wc -l); \
|
||||
echo $?
|
||||
cat <<EOF >$DEMO_HOME/out_expected.yaml
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: my-ingress
|
||||
spec:
|
||||
rules:
|
||||
- host: foo.bar.io
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: homepage
|
||||
servicePort: 80
|
||||
path: /
|
||||
- backend:
|
||||
servicePort: 7700
|
||||
path: /healthz
|
||||
- backend:
|
||||
serviceName: my-api
|
||||
servicePort: 7701
|
||||
path: /api
|
||||
- backend:
|
||||
serviceName: hello
|
||||
servicePort: 7702
|
||||
path: /test
|
||||
EOF
|
||||
```
|
||||
|
||||
If the patch is YAML-formatted, it will be parsed correctly:
|
||||
Run the build:
|
||||
<!-- @runIt @testAgainstLatestRelease -->
|
||||
```
|
||||
kustomize build $DEMO_HOME >$DEMO_HOME/out_actual.yaml
|
||||
```
|
||||
|
||||
<!-- @applyYamlPatch @testAgainstLatestRelease -->
|
||||
Confirm they match:
|
||||
|
||||
<!-- @diffShouldExitZero @testAgainstLatestRelease -->
|
||||
```
|
||||
diff $DEMO_HOME/out_actual.yaml $DEMO_HOME/out_expected.yaml
|
||||
```
|
||||
|
||||
If you prefer YAML to JSON, the patch can be expressed
|
||||
in YAML format (neverthless following [JSON patch] rules):
|
||||
|
||||
<!-- @writeYamlPatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress_patch.yaml
|
||||
- op: add
|
||||
path: /spec/rules/0/http/paths/-
|
||||
value:
|
||||
path: '/canada'
|
||||
backend:
|
||||
serviceName: hoser
|
||||
servicePort: 7703
|
||||
EOF
|
||||
```
|
||||
|
||||
Now add this to the list of patches in the `kustomization` file:
|
||||
|
||||
<!-- @addYamlPatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >>$DEMO_HOME/kustomization.yaml
|
||||
patchesJson6902:
|
||||
- target:
|
||||
- path: ingress_patch.yaml
|
||||
target:
|
||||
group: networking.k8s.io
|
||||
version: v1beta1
|
||||
kind: Ingress
|
||||
name: my-ingress
|
||||
path: ingress_patch.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
<!-- @confirmYamlPatch @testAgainstLatestRelease -->
|
||||
We expect the following at the end of the output:
|
||||
<!-- @expected @testAgainstLatestRelease -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "path: /test" | wc -l); \
|
||||
echo $?
|
||||
cat <<EOF >$DEMO_HOME/out_expected.yaml
|
||||
- backend:
|
||||
serviceName: hello
|
||||
servicePort: 7702
|
||||
path: /test
|
||||
- backend:
|
||||
serviceName: hoser
|
||||
servicePort: 7703
|
||||
path: /canada
|
||||
EOF
|
||||
```
|
||||
|
||||
Try it:
|
||||
|
||||
<!-- @runIt @testAgainstLatestRelease -->
|
||||
```
|
||||
kustomize build $DEMO_HOME | tail -n 8 |\
|
||||
diff $DEMO_HOME/out_expected.yaml -
|
||||
```
|
||||
|
||||
To see how to apply one JSON patch to many resources,
|
||||
see the [multi-patch](patchMultipleObjects.md) demo.
|
||||
|
||||
@@ -1,43 +1,78 @@
|
||||
[Strategic Merge Patch]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
|
||||
[JSON patches]: https://tools.ietf.org/html/rfc6902
|
||||
[label selector]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
|
||||
# Patching multiple resources at once.
|
||||
|
||||
kustomize supports patching via either a
|
||||
[strategic merge patch] (wherein you
|
||||
partially re-specify the thing you want to
|
||||
modify, with in-place changes) or a
|
||||
[JSON patch] (wherein you specify specific
|
||||
operation/target/value tuples in a particular
|
||||
syntax).
|
||||
|
||||
A kustomize file lets one specify many
|
||||
patches. Each patch must be associated with
|
||||
a _target selector_:
|
||||
|
||||
[strategic merge patch]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
|
||||
[JSON patch]: jsonpatch.md
|
||||
|
||||
> ```yaml
|
||||
> patches:
|
||||
> - path: <relative path to file containing patch>
|
||||
> target:
|
||||
> group: <optional group>
|
||||
> version: <optional version>
|
||||
> kind: <optional kind>
|
||||
> name: <optional name>
|
||||
> namespace: <optional namespace>
|
||||
> labelSelector: <optional label selector>
|
||||
> annotationSelector: <optional annotation selector>
|
||||
> ```
|
||||
|
||||
E.g. select resources with _name_ matching `foo*`:
|
||||
|
||||
> ```yaml
|
||||
> target:
|
||||
> name: foo*
|
||||
> ```
|
||||
|
||||
Select all resources of _kind_ `Deployment`:
|
||||
|
||||
> ```yaml
|
||||
> target:
|
||||
> kind: Deployment
|
||||
> ```
|
||||
|
||||
[label/annotation selector rules]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
|
||||
|
||||
Using multiple fields just makes the target
|
||||
more specific. The following selects only
|
||||
Deployments that also have the _label_ `app=hello`
|
||||
(full [label/annotation selector rules]):
|
||||
|
||||
> ```yaml
|
||||
> target:
|
||||
> kind: Deployment
|
||||
> labelSelector: app=hello
|
||||
> ```
|
||||
|
||||
### Demo
|
||||
|
||||
The example below shows how to inject a
|
||||
sidecar container for multiple Deployment
|
||||
resources.
|
||||
|
||||
|
||||
# Demo: applying a patch to multiple resources
|
||||
Make a place to work:
|
||||
|
||||
A kustomization file supports customizing resources via both
|
||||
[Strategic Merge Patch] and [JSON patches]. Now one patch can be
|
||||
applied to multiple resources.
|
||||
|
||||
This can be done by specifying a patch and a target selector as follows:
|
||||
```
|
||||
patches:
|
||||
- path: <PatchFile>
|
||||
target:
|
||||
group: <Group>
|
||||
version: <Version>
|
||||
kind: <Kind>
|
||||
name: <Name>
|
||||
namespace: <Namespace>
|
||||
labelSelector: <LabelSelector>
|
||||
annotationSelector: <AnnotationSelector>
|
||||
```
|
||||
Both `labelSelector` and `annotationSelector` should follow the convention in [label selector].
|
||||
Kustomize selects the targets which match all the fields in `target` to apply the patch.
|
||||
|
||||
The example below shows how to inject a sidecar container for all deployment resources.
|
||||
|
||||
Make a `kustomization` containing a Deployment resource.
|
||||
|
||||
<!-- @createDeployment @testAgainstLatestRelease -->
|
||||
<!-- @demoHome @testAgainstLatestRelease -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- deployments.yaml
|
||||
EOF
|
||||
Make a file describing two Deployments:
|
||||
|
||||
<!-- @createDeployments @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/deployments.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -72,9 +107,10 @@ spec:
|
||||
EOF
|
||||
```
|
||||
|
||||
Declare a Strategic Merge Patch file to inject a sidecar container:
|
||||
Declare a [strategic merge patch] file
|
||||
to inject a sidecar container:
|
||||
|
||||
<!-- @addPatch @testAgainstLatestRelease -->
|
||||
<!-- @definePatch @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/patch.yaml
|
||||
apiVersion: apps/v1
|
||||
@@ -93,11 +129,16 @@ spec:
|
||||
EOF
|
||||
```
|
||||
|
||||
Apply the patch by adding _patches_ field in kustomization.yaml
|
||||
Finally, define a kustomization file
|
||||
that specifies both a `patches` and `resources`
|
||||
entry:
|
||||
|
||||
<!-- @applyPatch @testAgainstLatestRelease -->
|
||||
<!-- @createKustomization @testAgainstLatestRelease -->
|
||||
```
|
||||
cat <<EOF >>$DEMO_HOME/kustomization.yaml
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- deployments.yaml
|
||||
|
||||
patches:
|
||||
- path: patch.yaml
|
||||
target:
|
||||
@@ -105,18 +146,11 @@ patches:
|
||||
EOF
|
||||
```
|
||||
|
||||
Running `kustomize build $DEMO_HOME`, in the output confirm that both Deployment resources are patched correctly.
|
||||
The expected result is:
|
||||
|
||||
<!-- @confirmPatch @testAgainstLatestRelease -->
|
||||
<!-- @definedExpectedOutput @testAgainstLatestRelease -->
|
||||
```
|
||||
test 2 == \
|
||||
$(kustomize build $DEMO_HOME | grep "image: docker.io/istio/proxyv2" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
The output is as follows:
|
||||
|
||||
```yaml
|
||||
cat <<EOF >$DEMO_HOME/out_expected.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -157,32 +191,21 @@ spec:
|
||||
name: istio-proxy
|
||||
- image: busybox
|
||||
name: busybox
|
||||
EOF
|
||||
```
|
||||
|
||||
## Target selector
|
||||
- Select resources with name matching `name*`
|
||||
```yaml
|
||||
target:
|
||||
name: name*
|
||||
```
|
||||
- Select all Deployment resources
|
||||
```yaml
|
||||
target:
|
||||
kind: Deployment
|
||||
```
|
||||
- Select resources matching label `app=hello`
|
||||
```yaml
|
||||
target:
|
||||
labelSelector: app=hello
|
||||
```
|
||||
- Select resources matching annotation `app=hello`
|
||||
```yaml
|
||||
target:
|
||||
annotationSelector: app=hello
|
||||
```
|
||||
- Select all Deployment resources matching label `app=hello`
|
||||
```yaml
|
||||
target:
|
||||
kind: Deployment
|
||||
labelSelector: app=hello
|
||||
```
|
||||
Run the build:
|
||||
<!-- @runIt @testAgainstLatestRelease -->
|
||||
```
|
||||
kustomize build $DEMO_HOME >$DEMO_HOME/out_actual.yaml
|
||||
```
|
||||
|
||||
Confirm expectations:
|
||||
|
||||
<!-- @diffShouldExitZero @testAgainstLatestRelease -->
|
||||
```
|
||||
diff $DEMO_HOME/out_actual.yaml $DEMO_HOME/out_expected.yaml
|
||||
```
|
||||
|
||||
To see how to do this with JSON patches,
|
||||
try the [JSON patch] demo.
|
||||
|
||||
@@ -109,6 +109,7 @@ func getGroupKinds(in []*yaml.RNode) ([]metav1.GroupKind, error) {
|
||||
found := false
|
||||
for _, gk := range groupKinds {
|
||||
if gk.Group == gvk.Group && gk.Kind == gvk.Kind {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ spec:
|
||||
strict: true
|
||||
ignoreMissingSchemas: true
|
||||
|
||||
# TODO: Remove these once function container network/volumes features are
|
||||
# stabilized.
|
||||
# TODO: Update this to use network/volumes features.
|
||||
# Relevant issues:
|
||||
# - https://github.com/kubernetes-sigs/kustomize/issues/1901
|
||||
# - https://github.com/kubernetes-sigs/kustomize/issues/1902
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Builds or removes Go plugin object code.
|
||||
@@ -40,8 +40,8 @@ function removePlugin {
|
||||
}
|
||||
|
||||
goPlugins=$(
|
||||
find $root -name "*.go" |
|
||||
grep -v builtin/ |
|
||||
find $root -name "*.go" |
|
||||
grep -v builtin/ |
|
||||
xargs grep -l "var KustomizePlugin")
|
||||
|
||||
for p in $goPlugins; do
|
||||
|
||||
@@ -15,7 +15,13 @@ set -o pipefail
|
||||
rcAccumulator=0
|
||||
|
||||
function onLinuxAndNotOnTravis {
|
||||
[[ ("linux" == "$(go env GOOS)") && (-z ${TRAVIS+x}) ]] && return
|
||||
# TODO: Make the code ignorant of the CI environment "brand name".
|
||||
# We used to run CI tests on travis, and disabled certain tests
|
||||
# when running there. Now we run on Prow, so look for that.
|
||||
# https://github.com/kubernetes/test-infra/blob/master/prow/jobs.md
|
||||
# Should eschew using the brand name of the CI environment
|
||||
# (replace "travis" with "CI_env" or something - not just switch to "prow").
|
||||
[[ ("linux" == "$(go env GOOS)") && (-z ${$PROW_JOB_ID+x}) ]] && return
|
||||
false
|
||||
}
|
||||
|
||||
@@ -24,7 +30,7 @@ function runTest {
|
||||
local code=0
|
||||
if grep -q "// +build notravis" "$file"; then
|
||||
if onLinuxAndNotOnTravis; then
|
||||
go test -v -tags=notravis $file
|
||||
go test -v -tags=notravis $file
|
||||
code=$?
|
||||
else
|
||||
# TODO: make work for non-linux
|
||||
@@ -51,7 +57,8 @@ function scanDir {
|
||||
|
||||
if onLinuxAndNotOnTravis; then
|
||||
# Some of these tests have special deps.
|
||||
make $(go env GOPATH)/bin/helm
|
||||
make $(go env GOPATH)/bin/helmV2
|
||||
make $(go env GOPATH)/bin/helmV3
|
||||
make $(go env GOPATH)/bin/kubeval
|
||||
fi
|
||||
|
||||
@@ -60,7 +67,7 @@ for goMod in $(find ./plugin -name 'go.mod'); do
|
||||
if [[ "$d" == "./plugin/someteam.example.com/v1/gogetter" ]]; then
|
||||
echo "Skipping broken $d"
|
||||
else
|
||||
scanDir $d
|
||||
scanDir $d
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ require (
|
||||
sigs.k8s.io/kustomize/cmd/config v0.0.5
|
||||
sigs.k8s.io/kustomize/cmd/kubectl v0.0.3
|
||||
sigs.k8s.io/kustomize/kstatus v0.0.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5
|
||||
sigs.k8s.io/yaml v1.1.0
|
||||
)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0 h1:MRvx8gncNaXJqOoLmhNjUAKh33JJF8LyxPhomEtOsjs=
|
||||
@@ -26,6 +27,7 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
|
||||
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
@@ -38,6 +40,7 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpH
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
@@ -52,6 +55,9 @@ github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgM
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
|
||||
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.1-coreos.6/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
@@ -82,6 +88,7 @@ github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
|
||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
@@ -146,8 +153,6 @@ github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsd
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
@@ -370,6 +375,7 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
@@ -391,6 +397,7 @@ github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
@@ -426,6 +433,8 @@ github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7z
|
||||
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
|
||||
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/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
@@ -472,6 +481,7 @@ github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -503,6 +513,9 @@ github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7
|
||||
github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
@@ -537,6 +550,7 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180112015858-5ccada7d0a7b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -588,6 +602,8 @@ golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c h1:Vco5b+cuG5NNfORVxZy6bYZQ7rsigisU1WQFkvQ0L5E=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20171227012246-e19ae1496984/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -739,10 +755,8 @@ sigs.k8s.io/kustomize/cmd/config v0.0.5 h1:mFJowsk9IGvwm5dUpVB+ZM63on2JjgaCy+YcV
|
||||
sigs.k8s.io/kustomize/cmd/config v0.0.5/go.mod h1:L47nDnZDfGFQG3gnPJLG2UABn0nVb9v+ndceyMH0jjU=
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.2/go.mod h1:rywm/rcR5LmCBghz9956tE45OdUPChFoXVVs+WmhMTI=
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.5/go.mod h1:waxTrzQRK9i6/5fR5HNo8xa4YwvWn8t85vMnOGFEZik=
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.6 h1:KhQr7JwpCseFTSWCwqp4CJ4mY6Kx+i34tF4e0eNkcXw=
|
||||
sigs.k8s.io/kustomize/kyaml v0.0.6/go.mod h1:tDOfJjL6slQVBLHJ76XfXAFgAOEdfm04AW2HehYOp8k=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1 h1:nGUNYINljZNmlAS8uoobUv/wx/s3Pg8dNxYo+W7uYh0=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.1/go.mod h1:/NdPPfrperSCGjm55cwEro1loBVtbtVIXSb7FguK6uk=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5 h1:NicBWYTwkuOfVyZDbNkfSBSCwSgin4uirkedtyZltIc=
|
||||
sigs.k8s.io/kustomize/kyaml v0.1.5/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
|
||||
sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/krusty"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -107,7 +108,7 @@ func (o *Options) makeOptions() *krusty.Options {
|
||||
DoPrune: false,
|
||||
}
|
||||
if isFlagEnablePluginsSet() {
|
||||
c, err := konfig.EnabledPluginConfig()
|
||||
c, err := konfig.EnabledPluginConfig(types.BploUseStaticallyLinked)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -95,11 +95,10 @@ func addConfigMap(
|
||||
args := findOrMakeConfigMapArgs(k, flags.Name)
|
||||
mergeFlagsIntoCmArgs(args, flags)
|
||||
// Validate by trying to create corev1.configmap.
|
||||
_, err := kf.MakeConfigMap(ldr, k.GeneratorOptions, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
args.Options = types.MergeGlobalOptionsIntoLocal(
|
||||
args.Options, k.GeneratorOptions)
|
||||
_, err := kf.MakeConfigMap(ldr, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func findOrMakeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs {
|
||||
|
||||
@@ -105,11 +105,10 @@ func addSecret(
|
||||
args := findOrMakeSecretArgs(k, flags.Name, flags.Namespace, flags.Type)
|
||||
mergeFlagsIntoGeneratorArgs(&args.GeneratorArgs, flags)
|
||||
// Validate by trying to create corev1.secret.
|
||||
_, err := kf.MakeSecret(ldr, k.GeneratorOptions, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
args.Options = types.MergeGlobalOptionsIntoLocal(
|
||||
args.Options, k.GeneratorOptions)
|
||||
_, err := kf.MakeSecret(ldr, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func findOrMakeSecretArgs(m *types.Kustomization, name, namespace, secretType string) *types.SecretArgs {
|
||||
|
||||
@@ -28,7 +28,7 @@ var (
|
||||
errImageNoArgs = errors.New("no image specified")
|
||||
errImageInvalidArgs = errors.New(`invalid format of image, use one of the following options:
|
||||
- <image>=<newimage>:<newtag>
|
||||
- <image>=<newimage>@<newtag>
|
||||
- <image>=<newimage>@<digest>
|
||||
- <image>=<newimage>
|
||||
- <image>:<newtag>
|
||||
- <image>@<digest>`)
|
||||
|
||||
@@ -35,4 +35,5 @@ vet:
|
||||
|
||||
openapi:
|
||||
(which $(GOPATH)/bin/go-bindata || go get -v github.com/go-bindata/go-bindata)
|
||||
go-bindata --pkg openapi -o openapi/swagger.go openapi/swagger.json
|
||||
$(GOPATH)/bin/go-bindata --pkg kubernetesapi -o openapi/kubernetesapi/swagger.go openapi/kubernetesapi/swagger.json
|
||||
$(GOPATH)/bin/go-bindata --pkg kustomizationapi -o openapi/kustomizationapi/swagger.go openapi/kustomizationapi/swagger.json
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
// CopyComments recursively copies the comments on fields in from to fields in to
|
||||
func CopyComments(from, to *yaml.RNode) error {
|
||||
copy(from, to)
|
||||
// walk the fields copying comments
|
||||
_, err := walk.Walker{
|
||||
Sources: []*yaml.RNode{from, to},
|
||||
@@ -44,13 +45,13 @@ func copy(from, to *yaml.RNode) {
|
||||
if from == nil || to == nil {
|
||||
return
|
||||
}
|
||||
if from.YNode().LineComment != "" {
|
||||
to.YNode().LineComment = from.YNode().LineComment
|
||||
if from.Document().LineComment != "" {
|
||||
to.Document().LineComment = from.Document().LineComment
|
||||
}
|
||||
if from.YNode().HeadComment != "" {
|
||||
to.YNode().HeadComment = from.YNode().HeadComment
|
||||
if from.Document().HeadComment != "" {
|
||||
to.Document().HeadComment = from.Document().HeadComment
|
||||
}
|
||||
if from.YNode().FootComment != "" {
|
||||
to.YNode().FootComment = from.YNode().FootComment
|
||||
if from.Document().FootComment != "" {
|
||||
to.Document().FootComment = from.Document().FootComment
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user