Compare commits

..

34 Commits

Author SHA1 Message Date
Phillip Wittrock
fd6207b1c3 update go.mod for release 2020-04-02 11:31:23 -07:00
Phillip Wittrock
cbe85f482d Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-04-02 11:31:14 -07:00
Phillip Wittrock
a1c7330331 update go.mod for release 2020-03-28 08:34:06 -07:00
Phillip Wittrock
08ceab40dd Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-03-28 08:34:01 -07:00
Phillip Wittrock
f8fb00cb13 update go.mod for release 2020-03-23 12:08:52 -07:00
Phillip Wittrock
8a1c841420 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-03-23 12:08:31 -07:00
Phillip Wittrock
730d1f2473 update go.mod for release 2020-03-06 09:29:07 -08:00
Phillip Wittrock
29d1a37858 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-03-06 09:29:02 -08:00
Phillip Wittrock
c467f48bf2 update go.mod for release 2020-02-27 12:21:10 -08:00
Phillip Wittrock
054f56ceaf Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-02-27 12:21:05 -08:00
Phillip Wittrock
d5a107074d update go.mod for release 2020-02-21 10:18:34 -08:00
Phillip Wittrock
d8bd6db880 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-02-21 10:18:15 -08:00
Phillip Wittrock
d27135e3a3 update go.mod for release 2020-02-19 15:00:57 -08:00
Phillip Wittrock
282b1fa49a Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-02-19 15:00:38 -08:00
Phillip Wittrock
584eb236fd update go.mod for release 2020-02-05 17:46:10 -08:00
Phillip Wittrock
31a193f60f Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-02-05 17:46:04 -08:00
Phillip Wittrock
bf17b244e5 update go.mod for release 2020-01-16 16:24:55 -08:00
Phillip Wittrock
8338299529 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-16 16:24:35 -08:00
Phillip Wittrock
7adf7eb271 update go.mod for release 2020-01-15 18:10:02 -08:00
Phillip Wittrock
06b091b175 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-15 18:09:55 -08:00
Phillip Wittrock
bf03669e94 update go.mod for release 2020-01-13 12:49:07 -08:00
Phillip Wittrock
7f52c814a8 release cmd/config
Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0
2020-01-13 12:47:01 -08:00
Phillip Wittrock
d5aac922d9 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-10 16:38:39 -08:00
Phillip Wittrock
9a62d866f3 update go.mod for release 2020-01-09 17:02:34 -08:00
Phillip Wittrock
b2812838bf release cmd/config 0.0.6
Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0
2020-01-09 17:02:08 -08:00
Phillip Wittrock
e59e477702 update go.mod for release 2020-01-03 10:01:16 -08:00
Phillip Wittrock
4264ad0ad4 release cmd/config 0.0.5 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-03 10:01:06 -08:00
Phillip Wittrock
2554cd00eb update go.mod for release 2020-01-02 09:02:06 -08:00
Phillip Wittrock
383244cd63 Release cmd/config 0.0.4
Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0
2020-01-02 09:01:56 -08:00
Phillip Wittrock
b9da33afd4 update go.mod for release 2019-12-20 09:44:25 -08:00
Phillip Wittrock
23e339b86c Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2019-12-20 09:44:21 -08:00
Phillip Wittrock
9555009df8 update go.mod for release 2019-12-16 14:21:02 -08:00
Phillip Wittrock
91a10c560c Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2019-12-16 14:20:58 -08:00
Phillip Wittrock
780cb19c4d drop replace 2019-12-13 13:50:17 -08:00
270 changed files with 6634 additions and 12775 deletions

View File

@@ -1,28 +0,0 @@
name: Go
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build
run: ./travis/kyaml-pre-commit.sh
env:
KUSTOMIZE_DOCKER_E2E: true

View File

@@ -116,6 +116,7 @@ _builtinplugins = \
ConfigMapGenerator.go \
HashTransformer.go \
ImageTagTransformer.go \
InventoryTransformer.go \
LabelTransformer.go \
LegacyOrderTransformer.go \
NamespaceTransformer.go \
@@ -140,6 +141,7 @@ $(pGen)/AnnotationsTransformer.go: $(pSrc)/annotationstransformer/AnnotationsTra
$(pGen)/ConfigMapGenerator.go: $(pSrc)/configmapgenerator/ConfigMapGenerator.go
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
$(pGen)/InventoryTransformer.go: $(pSrc)/inventorytransformer/InventoryTransformer.go
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
$(pGen)/LegacyOrderTransformer.go: $(pSrc)/legacyordertransformer/LegacyOrderTransformer.go
$(pGen)/NamespaceTransformer.go: $(pSrc)/namespacetransformer/NamespaceTransformer.go
@@ -186,14 +188,8 @@ 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: build-kustomize-api
test-unit-kustomize-api: $(builtinplugins)
cd api; go test ./...
.PHONY: test-unit-kustomize-plugins
@@ -216,6 +212,10 @@ test-unit-cmd-all:
test-go-mod:
./travis/check-go-mod.sh
.PHONY:
test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY:
test-examples-e2e-kustomize: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
( \
@@ -226,17 +226,13 @@ test-examples-e2e-kustomize: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
./hack/testExamplesE2EAgainstKustomize.sh .; \
)
.PHONY:
test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY:
test-examples-kustomize-against-latest: $(MYGOBIN)/mdrip
( \
set -e; \
/bin/rm -f $(MYGOBIN)/kustomize; \
echo "Installing kustomize from latest."; \
GO111MODULE=on go get sigs.k8s.io/kustomize/kustomize/v3@v3.5.4; \
GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v3; \
./hack/testExamplesAgainstKustomize.sh latest; \
echo "Reinstalling kustomize from HEAD."; \
cd kustomize; go install .; \
@@ -259,38 +255,21 @@ $(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 binaries.
$(MYGOBIN)/helmV2:
# 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:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
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; \
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); \
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 \
)
# Default version of helm is v2 for the time being.
$(MYGOBIN)/helm: $(MYGOBIN)/helmV2
ln -s $(MYGOBIN)/helmV2 $(MYGOBIN)/helm
$(MYGOBIN)/kind:
( \
set -e; \
@@ -309,7 +288,6 @@ 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
go clean --modcache
sudo rm -rf $(shell go env GOPATH)/pkg/mod/sigs.k8s.io

View File

@@ -9,4 +9,3 @@ aliases:
- mengqiy
- monopole
- pwittrock
- mortent

View File

@@ -12,7 +12,8 @@ and it's like [`sed`], in that it emits edited text.
This tool is sponsored by [sig-cli] ([KEP]), and
inspired by [DAM].
[![Build Status](https://prow.k8s.io/badge.svg?jobs=kustomize-presubmit-master)](https://prow.k8s.io/job-history/kubernetes-jenkins/pr-logs/directory/kustomize-presubmit-master)
[![Build Status](https://travis-ci.org/kubernetes-sigs/kustomize.svg?branch=master)](https://travis-ci.org/kubernetes-sigs/kustomize)
[![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-sigs/kustomize)](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
Download a binary from the [release page], or see

View File

@@ -6,6 +6,7 @@ package builtins
import (
"sigs.k8s.io/kustomize/api/filters/annotations"
"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"
@@ -15,6 +16,10 @@ import (
type AnnotationsTransformerPlugin struct {
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
// YAMLSupport can be set to true to use the kyaml filter instead of the
// kunstruct transformer
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
}
func (p *AnnotationsTransformerPlugin) Config(
@@ -25,16 +30,27 @@ func (p *AnnotationsTransformerPlugin) Config(
}
func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(annotations.Filter{
Annotations: p.Annotations,
FsSlice: p.FieldSpecs,
}, r.Kunstructured)
if p.YAMLSupport {
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(annotations.Filter{
Annotations: p.Annotations,
FsSlice: p.FieldSpecs,
}, r.Kunstructured)
if err != nil {
return err
}
}
return nil
} else {
t, err := transform.NewMapTransformer(
p.FieldSpecs,
p.Annotations,
)
if err != nil {
return err
}
return t.Transform(m)
}
return nil
}
func NewAnnotationsTransformerPlugin() resmap.TransformerPlugin {

View File

@@ -13,10 +13,13 @@ 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) {
func (p *ConfigMapGeneratorPlugin) Config(
h *resmap.PluginHelpers, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.ConfigMapArgs = types.ConfigMapArgs{}
err = yaml.Unmarshal(config, p)
if p.ConfigMapArgs.Name == "" {
@@ -31,7 +34,8 @@ func (p *ConfigMapGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
return p.h.ResmapFactory().FromConfigMapArgs(
kv.NewLoader(p.h.Loader(), p.h.Validator()), p.ConfigMapArgs)
kv.NewLoader(p.h.Loader(), p.h.Validator()),
&p.GeneratorOptions, p.ConfigMapArgs)
}
func NewConfigMapGeneratorPlugin() resmap.GeneratorPlugin {

View File

@@ -0,0 +1,128 @@
// Code generated by pluginator on InventoryTransformer; DO NOT EDIT.
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/api/hasher"
"sigs.k8s.io/kustomize/api/inventory"
"sigs.k8s.io/kustomize/api/kv"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
type InventoryTransformerPlugin struct {
h *resmap.PluginHelpers
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Policy string `json:"policy,omitempty" yaml:"policy,omitempty"`
}
func (p *InventoryTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.h = h
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if p.Policy == "" {
p.Policy = types.GarbageIgnore.String()
}
if p.Policy != types.GarbageCollect.String() &&
p.Policy != types.GarbageIgnore.String() {
return fmt.Errorf(
"unrecognized garbagePolicy '%s'", p.Policy)
}
return nil
}
// Transform generates an inventory object from the input ResMap.
// This ConfigMap supports the pruning command in
// the client side tool proposed here:
// https://github.com/kubernetes/enhancements/pull/810
//
// The inventory data is written to the ConfigMap's
// annotations, rather than to the key-value pairs in
// the ConfigMap's data field, since
// 1. Keys in a ConfigMap's data field are too
// constrained for this purpose.
// 2. Using annotations allow any object to be used,
// not just a ConfigMap, should some other object
// (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),
}
opts.Annotations[inventory.HashAnnotation] = h
err = inv.UpdateAnnotations(opts.Annotations)
if err != nil {
return err
}
cm, err := p.h.ResmapFactory().RF().MakeConfigMap(
kv.NewLoader(p.h.Loader(), p.h.Validator()), opts, &args)
if err != nil {
return err
}
if p.Policy == types.GarbageCollect.String() {
for _, byeBye := range m.AllIds() {
m.Remove(byeBye)
}
}
return m.Append(cm)
}
func makeInventory(m resmap.ResMap) (
inv *inventory.Inventory, hash string, err error) {
inv = inventory.NewInventory()
var keys []string
for _, r := range m.Resources() {
ns := r.GetNamespace()
item := resid.NewResIdWithNamespace(r.GetGvk(), r.GetName(), ns)
if _, ok := inv.Current[item]; ok {
return nil, "", fmt.Errorf(
"item '%v' already in inventory", item)
}
inv.Current[item], err = computeRefs(r, m)
if err != nil {
return nil, "", err
}
keys = append(keys, item.String())
}
h, err := hasher.SortArrayAndComputeHash(keys)
return inv, h, err
}
func computeRefs(
r *resource.Resource, m resmap.ResMap) (refs []resid.ResId, err error) {
for _, refid := range r.GetRefBy() {
ref, err := m.GetByCurrentId(refid)
if err != nil {
return nil, err
}
refs = append(
refs,
resid.NewResIdWithNamespace(
ref.GetGvk(), ref.GetName(), ref.GetNamespace()))
}
return
}
func NewInventoryTransformerPlugin() resmap.TransformerPlugin {
return &InventoryTransformerPlugin{}
}

View File

@@ -4,10 +4,9 @@
package builtins
import (
"sigs.k8s.io/kustomize/api/filters/labels"
"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"
)
@@ -25,16 +24,14 @@ func (p *LabelTransformerPlugin) Config(
}
func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(labels.Filter{
Labels: p.Labels,
FsSlice: p.FieldSpecs,
}, r.Kunstructured)
if err != nil {
return err
}
t, err := transform.NewMapTransformer(
p.FieldSpecs,
p.Labels,
)
if err != nil {
return err
}
return nil
return t.Transform(m)
}
func NewLabelTransformerPlugin() resmap.TransformerPlugin {

View File

@@ -8,12 +8,10 @@ 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"
)
@@ -23,8 +21,6 @@ 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(
@@ -87,22 +83,16 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
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)
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)
}
func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin {

View File

@@ -4,16 +4,11 @@
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"
)
@@ -22,8 +17,6 @@ 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(
@@ -76,42 +69,22 @@ func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error
if err != nil {
return err
}
if !p.YAMLSupport {
err = target.Patch(patch.Kunstructured)
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
}
// 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{}
}

View File

@@ -5,16 +5,12 @@ 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"
)
@@ -24,112 +20,69 @@ 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) error {
err := yaml.Unmarshal(c, p)
h *resmap.PluginHelpers, c []byte) (err error) {
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
p.Patch = strings.TrimSpace(p.Patch)
if p.Patch == "" && p.Path == "" {
return fmt.Errorf(
err = fmt.Errorf(
"must specify one of patch and path in\n%s", string(c))
return
}
if p.Patch != "" && p.Path != "" {
return fmt.Errorf(
err = fmt.Errorf(
"patch and path can't be set at the same time\n%s", string(c))
return
}
var in []byte
if p.Path != "" {
loaded, loadErr := h.Loader().Load(p.Path)
if loadErr != nil {
return loadErr
in, err = h.Loader().Load(p.Path)
if err != nil {
return
}
p.Patch = string(loaded)
}
if p.Patch != "" {
in = []byte(p.Patch)
}
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)
}
patchSM, errSM := h.ResmapFactory().RF().FromBytes(in)
patchJson, errJson := jsonPatchFromBytes(in)
if errSM != nil && errJson != nil {
return fmt.Errorf(
"unable to parse SM or JSON patch from [%v]", p.Patch)
err = fmt.Errorf(
"unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch)
return
}
if errSM == nil {
if errSM == nil && errJson != nil {
p.loadedPatch = patchSM
} else {
}
if errJson == nil && errSM != nil {
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 {
// 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 p.loadedPatch != nil && p.Target == nil {
target, err := m.GetById(p.loadedPatch.OrgId())
if err != nil {
return err
}
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)
err = target.Patch(p.loadedPatch.Kunstructured)
if err != nil {
return err
}
return nil
}
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)
}
@@ -139,36 +92,35 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
return err
}
for _, res := range resources {
err = p.applyJson6902Patch(res, patch)
if err != nil {
return err
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
}
}
}
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(

View File

@@ -13,10 +13,12 @@ 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 == "" {
@@ -31,7 +33,8 @@ 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.SecretArgs)
kv.NewLoader(p.h.Loader(), p.h.Validator()),
&p.GeneratorOptions, p.SecretArgs)
}
func NewSecretGeneratorPlugin() resmap.GeneratorPlugin {

View File

@@ -124,7 +124,12 @@ func isMatchGVK(fs types.FieldSpec, obj *yaml.RNode) (bool, error) {
}
// parse the group and version from the apiVersion field
group, version := parseGV(meta.APIVersion)
var group, version string
parts := strings.SplitN(meta.APIVersion, "/", 2)
group = parts[0]
if len(parts) > 1 {
version = parts[1]
}
if fs.Group != "" && fs.Group != group {
// group doesn't match

View File

@@ -4,6 +4,9 @@
package fsslice
import (
"strings"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -56,3 +59,20 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
}
return obj, nil
}
// GetGVK parses the metadata into a GVK
func GetGVK(meta yaml.ResourceMeta) resid.Gvk {
// parse the group and version from the apiVersion field
var group, version string
parts := strings.SplitN(meta.APIVersion, "/", 2)
group = parts[0]
if len(parts) > 1 {
version = parts[1]
}
return resid.Gvk{
Group: group,
Version: version,
Kind: meta.Kind,
}
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -280,52 +281,6 @@ kind: Bar
a:
b:
- c: {d: e}
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "group v1",
fsSlice: `
- path: a/b
group: v1
create: true
kind: Bar
`,
input: `
apiVersion: v1
kind: Bar
`,
expected: `
apiVersion: v1
kind: Bar
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "version v1",
fsSlice: `
- path: a/b
version: v1
create: true
kind: Bar
`,
input: `
apiVersion: v1
kind: Bar
`,
expected: `
apiVersion: v1
kind: Bar
a:
b: e
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
@@ -377,3 +332,23 @@ func TestFilter_Filter(t *testing.T) {
})
}
}
func TestGetGVK(t *testing.T) {
obj, err := yaml.Parse(`
apiVersion: apps/v1
kind: Deployment
`)
if !assert.NoError(t, err) {
t.FailNow()
}
meta, err := obj.GetMeta()
if !assert.NoError(t, err) {
t.FailNow()
}
gvk := fsslice.GetGVK(meta)
expected := resid.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}
if !assert.Equal(t, expected, gvk) {
t.FailNow()
}
}

View File

@@ -1,49 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fsslice
import (
"strings"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Return true for 'v' followed by a 1 or 2, and don't look at rest.
// I.e. 'v1', 'v1beta1', 'v2', would return true.
func looksLikeACoreApiVersion(s string) bool {
if len(s) < 2 {
return false
}
if s[0:1] != "v" {
return false
}
return s[1:2] == "1" || s[1:2] == "2"
}
// parseGV parses apiVersion field into group and version.
func parseGV(apiVersion string) (group, version string) {
// parse the group and version from the apiVersion field
parts := strings.SplitN(apiVersion, "/", 2)
group = parts[0]
if len(parts) > 1 {
version = parts[1]
}
// Special case the original "apiVersion" of what
// we now call the "core" (empty) group.
if version == "" && looksLikeACoreApiVersion(group) {
version = group
group = ""
}
return
}
// GetGVK parses the metadata into a GVK
func GetGVK(meta yaml.ResourceMeta) resid.Gvk {
group, version := parseGV(meta.APIVersion)
return resid.Gvk{
Group: group,
Version: version,
Kind: meta.Kind,
}
}

View File

@@ -1,156 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fsslice
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestParseGV(t *testing.T) {
testCases := map[string]struct {
input string
expectedGroup string
expectedVersion string
}{
"empty": {
input: "",
expectedGroup: "",
expectedVersion: "",
},
"certSigning": {
input: "certificates.k8s.io/v1beta1",
expectedGroup: "certificates.k8s.io",
expectedVersion: "v1beta1",
},
"extensions": {
input: "extensions/v1beta1",
expectedGroup: "extensions",
expectedVersion: "v1beta1",
},
"normal": {
input: "apps/v1",
expectedGroup: "apps",
expectedVersion: "v1",
},
"justApps": {
input: "apps",
expectedGroup: "apps",
expectedVersion: "",
},
"coreV1": {
input: "v1",
expectedGroup: "",
expectedVersion: "v1",
},
"coreV2": {
input: "v2",
expectedGroup: "",
expectedVersion: "v2",
},
"coreV2Beta1": {
input: "v2beta1",
expectedGroup: "",
expectedVersion: "v2beta1",
},
}
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
group, version := parseGV(tc.input)
if !assert.Equal(t, tc.expectedGroup, group) {
t.FailNow()
}
if !assert.Equal(t, tc.expectedVersion, version) {
t.FailNow()
}
})
}
}
func TestGetGVK(t *testing.T) {
testCases := map[string]struct {
input string
expected resid.Gvk
parseError string
metaError string
}{
"empty": {
input: `
`,
parseError: "EOF",
},
"junk": {
input: `
congress: effective
`,
metaError: "missing Resource metadata",
},
"normal": {
input: `
apiVersion: apps/v1
kind: Deployment
`,
expected: resid.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"},
},
"apiVersionOnlyWithSlash": {
input: `
apiVersion: apps/v1
`,
expected: resid.Gvk{Group: "apps", Version: "v1", Kind: ""},
},
"apiVersionOnlyNoSlash1": {
input: `
apiVersion: apps
`,
expected: resid.Gvk{Group: "apps", Version: "", Kind: ""},
},
"apiVersionOnlyNoSlash2": {
input: `
apiVersion: v1
`,
expected: resid.Gvk{Group: "", Version: "v1", Kind: ""},
},
}
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
obj, err := yaml.Parse(tc.input)
if len(tc.parseError) != 0 {
if err == nil {
t.Error("expected parse error")
return
}
if !strings.Contains(err.Error(), tc.parseError) {
t.Errorf("expected parse err '%s', got '%v'", tc.parseError, err)
}
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
meta, err := obj.GetMeta()
if len(tc.metaError) != 0 {
if err == nil {
t.Error("expected meta error")
return
}
if !strings.Contains(err.Error(), tc.metaError) {
t.Errorf("expected meta err '%s', got '%v'", tc.metaError, err)
}
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
gvk := GetGVK(meta)
if !assert.Equal(t, tc.expected, gvk) {
t.FailNow()
}
})
}
}

View File

@@ -1,12 +0,0 @@
// 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

View File

@@ -1,126 +0,0 @@
// 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
}

View File

@@ -1,44 +0,0 @@
// 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,
})
}
}

View File

@@ -1,101 +0,0 @@
// 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()
}
})
}
}

View File

@@ -1,113 +0,0 @@
// 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,
})
})
}
}

View File

@@ -1,136 +0,0 @@
// 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()
}
})
}
}

View File

@@ -1,43 +0,0 @@
// 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})
}

View File

@@ -8,16 +8,19 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
"sigs.k8s.io/kustomize/api/types"
)
var labelsFs = builtinconfig.MakeDefaultConfig().CommonLabels
func TestLabels_Filter(t *testing.T) {
testCases := map[string]struct {
input string
expectedOutput string
filter Filter
fsSlice types.FsSlice
}{
"add": {
input: `
@@ -42,20 +45,12 @@ metadata:
clown: emmett kelley
dragon: smaug
`,
filter: Filter{
Labels: labelMap{
"clown": "emmett kelley",
"auto": "ford",
"dragon": "smaug",
"bean": "cannellini",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
},
},
filter: Filter{Labels: labelMap{
"clown": "emmett kelley",
"auto": "ford",
"dragon": "smaug",
"bean": "cannellini",
}},
},
"update": {
input: `
@@ -78,21 +73,13 @@ metadata:
bean: cannellini
clown: emmett kelley
`,
filter: Filter{
Labels: labelMap{
"clown": "emmett kelley",
"hero": "superman",
"fiend": "luthor",
"bean": "cannellini",
}, FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
},
},
filter: Filter{Labels: labelMap{
"clown": "emmett kelley",
"hero": "superman",
"fiend": "luthor",
"bean": "cannellini",
}},
},
"data-fieldspecs": {
input: `
apiVersion: example.com/v1
@@ -126,168 +113,13 @@ a:
b:
sleater: kinney
`,
filter: Filter{
Labels: labelMap{
"sleater": "kinney",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
{
Path: "a/b",
CreateIfNotPresent: true,
},
},
},
},
"fieldSpecWithKind": {
input: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
---
apiVersion: example.com/v2
kind: Bar
metadata:
name: instance
`,
expectedOutput: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
labels:
cheese: cheddar
---
apiVersion: example.com/v2
kind: Bar
metadata:
name: instance
labels:
cheese: cheddar
a:
b:
cheese: cheddar
`,
filter: Filter{
Labels: labelMap{
"cheese": "cheddar",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
{
Gvk: resid.Gvk{
Kind: "Bar",
},
Path: "a/b",
CreateIfNotPresent: true,
},
},
},
},
"fieldSpecWithVersion": {
input: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
---
apiVersion: example.com/v2
kind: Bar
metadata:
name: instance
`,
expectedOutput: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
labels:
cheese: cheddar
a:
b:
cheese: cheddar
---
apiVersion: example.com/v2
kind: Bar
metadata:
name: instance
labels:
cheese: cheddar
`,
filter: Filter{
Labels: labelMap{
"cheese": "cheddar",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
{
Gvk: resid.Gvk{
Version: "v1",
},
Path: "a/b",
CreateIfNotPresent: true,
},
},
},
},
"fieldSpecWithVersionInConfigButNoGroupInData": {
input: `
apiVersion: v1
kind: Foo
metadata:
name: instance
---
apiVersion: v2
kind: Bar
metadata:
name: instance
`,
expectedOutput: `
apiVersion: v1
kind: Foo
metadata:
name: instance
labels:
cheese: cheddar
a:
b:
cheese: cheddar
---
apiVersion: v2
kind: Bar
metadata:
name: instance
labels:
cheese: cheddar
`,
filter: Filter{
Labels: labelMap{
"cheese": "cheddar",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
{
Gvk: resid.Gvk{
Version: "v1",
},
Path: "a/b",
CreateIfNotPresent: true,
},
filter: Filter{Labels: labelMap{
"sleater": "kinney",
}},
fsSlice: []types.FieldSpec{
{
Path: "a/b",
CreateIfNotPresent: true,
},
},
},
@@ -295,9 +127,11 @@ metadata:
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
filter := tc.filter
filter.FsSlice = append(labelsFs, tc.fsSlice...)
if !assert.Equal(t,
strings.TrimSpace(tc.expectedOutput),
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, tc.filter))) {
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, filter))) {
t.FailNow()
}
})

View File

@@ -1,6 +0,0 @@
// 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

View File

@@ -1,55 +0,0 @@
// 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
}

View File

@@ -1,65 +0,0 @@
// 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
}

View File

@@ -1,173 +0,0 @@
// 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()
}
})
}
}

View File

@@ -1,6 +0,0 @@
// 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

View File

@@ -1,49 +0,0 @@
// 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
}

View File

@@ -1,21 +0,0 @@
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)
}

View File

@@ -1,82 +0,0 @@
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()
}
})
}
}

View File

@@ -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.5
sigs.k8s.io/kustomize/kyaml v0.1.3
sigs.k8s.io/yaml v1.1.0
)

View File

@@ -297,7 +297,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d h1:BzRvVq1EHuIjxpijCEKpAxzKUUMurOQ4sknehIATRh8=
github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
@@ -423,7 +422,6 @@ 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 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=
@@ -508,8 +506,13 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphD
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
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/kustomize v1.0.11 h1:Yb+6DDt9+aR2AvQApvUaKS/ugteeG4MPyoFeUHiPOjk=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
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/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=

View File

@@ -75,8 +75,14 @@ type KunstructuredFactory interface {
SliceFromBytes([]byte) ([]Kunstructured, error)
FromMap(m map[string]interface{}) Kunstructured
Hasher() KunstructuredHasher
MakeConfigMap(kvLdr KvLoader, args *types.ConfigMapArgs) (Kunstructured, error)
MakeSecret(kvLdr KvLoader, args *types.SecretArgs) (Kunstructured, error)
MakeConfigMap(
kvLdr KvLoader,
options *types.GeneratorOptions,
args *types.ConfigMapArgs) (Kunstructured, error)
MakeSecret(
kvLdr KvLoader,
options *types.GeneratorOptions,
args *types.SecretArgs) (Kunstructured, error)
}
// KunstructuredHasher returns a hash of the argument

View File

@@ -1,50 +0,0 @@
// 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
}

View File

@@ -1,80 +0,0 @@
// 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)
})
}
}

View File

@@ -40,11 +40,7 @@ func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
case []interface{}:
var xs []interface{}
for _, a := range in.([]interface{}) {
x, ok := a.(string)
if !ok {
return nil, fmt.Errorf("expected array of strings, found %v", in)
}
xs = append(xs, expansion2.Expand(x, rv.mappingFunc))
xs = append(xs, expansion2.Expand(a.(string), rv.mappingFunc))
}
return xs, nil
case map[string]interface{}:
@@ -53,7 +49,7 @@ func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
for k, v := range inMap {
s, ok := v.(string)
if !ok {
// This field can not contain a $(VAR) since it is not
// This field not contain a $(VAR) since it is not
// of string type. For instance .spec.replicas: 3 in
// a Deployment object
xs[k] = v
@@ -68,7 +64,7 @@ func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
case interface{}:
s, ok := in.(string)
if !ok {
// This field can not contain a $(VAR) since it is not of string type.
// This field not contain a $(VAR) since it is not of string type.
return in, nil
}
// This field can potentially contain a $(VAR) since it is

View File

@@ -29,7 +29,6 @@ func TestRefVarTransformer(t *testing.T) {
description string
given given
expected expected
errMessage string
}{
{
description: "var replacement in map[string]",
@@ -112,27 +111,6 @@ func TestRefVarTransformer(t *testing.T) {
unused: []string{"BAR"},
},
},
{
description: "var replacement panic in map[string]",
given: given{
varMap: map[string]interface{}{},
fs: []types.FieldSpec{
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/slice"},
},
res: resmaptest_test.NewRmBuilder(
t, resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
},
"data": map[string]interface{}{
"slice": []interface{}{5}, // noticeably *not* a []string
}}).ResMap(),
},
errMessage: "expected array of strings, found [5]",
},
}
for _, tc := range testCases {
@@ -144,23 +122,16 @@ func TestRefVarTransformer(t *testing.T) {
err := tr.Transform(tc.given.res)
// assert
if tc.errMessage != "" {
if err == nil {
t.Fatalf("missing expected error %v", tc.errMessage)
} else if err.Error() != tc.errMessage {
t.Fatalf("actual error doesn't match expected error: \nACTUAL: %v\nEXPECTED: %v", err.Error(), tc.errMessage)
}
} else {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
a, e := tc.given.res, tc.expected.res
if !reflect.DeepEqual(a, e) {
err = e.ErrorIfNotEqualLists(a)
t.Fatalf("actual doesn't match expected: \nACTUAL:\n%v\nEXPECTED:\n%v\nERR: %v", a, e, err)
}
if err != nil {
t.Errorf("unexpected error: %v", err)
}
a, e := tc.given.res, tc.expected.res
if !reflect.DeepEqual(a, e) {
err = e.ErrorIfNotEqualLists(a)
t.Fatalf("actual doesn't match expected: \nACTUAL:\n%v\nEXPECTED:\n%v\nERR: %v", a, e, err)
}
})
}
}

View File

@@ -25,7 +25,8 @@ 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")
@@ -37,7 +38,7 @@ func (f *Factory) MakeConfigMap(args *types.ConfigMapArgs) (*corev1.ConfigMap, e
return nil, errors.Wrap(err, "trouble mapping")
}
}
f.copyLabelsAndAnnotations(cm, args.Options)
f.setLabelsAndAnnnotations(cm, args.GeneratorOptions)
return cm, nil
}

View File

@@ -82,6 +82,7 @@ func TestConstructConfigMap(t *testing.T) {
type testCase struct {
description string
input types.ConfigMapArgs
options *types.GeneratorOptions
expected *corev1.ConfigMap
}
@@ -98,6 +99,7 @@ func TestConstructConfigMap(t *testing.T) {
},
},
},
options: nil,
expected: makeEnvConfigMap("envConfigMap"),
},
{
@@ -113,6 +115,7 @@ func TestConstructConfigMap(t *testing.T) {
},
},
},
options: nil,
expected: makeFileConfigMap("fileConfigMap"),
},
{
@@ -123,11 +126,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{
@@ -142,7 +145,7 @@ func TestConstructConfigMap(t *testing.T) {
KvPairSources: types.KvPairSources{
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
},
Options: &types.GeneratorOptions{
GeneratorOptions: &types.GeneratorOptions{
Labels: map[string]string{
"foo": "changed",
"cat": "dog",
@@ -154,6 +157,18 @@ 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",
@@ -178,7 +193,8 @@ func TestConstructConfigMap(t *testing.T) {
loader.NewFileLoaderAtRoot(fSys),
valtest_test.MakeFakeValidator())
for _, tc := range testCases {
cm, err := NewFactory(kvLdr).MakeConfigMap(&tc.input)
f := NewFactory(kvLdr, tc.options)
cm, err := f.MakeConfigMap(&tc.input)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@@ -11,26 +11,44 @@ import (
// Factory makes ConfigMaps and Secrets.
type Factory struct {
kvLdr ifc.KvLoader
kvLdr ifc.KvLoader
options *types.GeneratorOptions
}
// NewFactory returns a new factory that makes ConfigMaps and Secrets.
func NewFactory(kvLdr ifc.KvLoader) *Factory {
return &Factory{kvLdr: kvLdr}
func NewFactory(
kvLdr ifc.KvLoader, o *types.GeneratorOptions) *Factory {
return &Factory{kvLdr: kvLdr, options: o}
}
// 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
// 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
}
}
if opts.Labels != nil {
obj.SetLabels(types.CopyMap(opts.Labels))
if opts != nil {
for k, v := range opts.Labels {
labels[k] = v
}
for k, v := range opts.Annotations {
annotations[k] = v
}
}
if opts.Annotations != nil {
obj.SetAnnotations(types.CopyMap(opts.Annotations))
if len(labels) != 0 {
obj.SetLabels(labels)
}
if len(annotations) != 0 {
obj.SetAnnotations(annotations)
}
}

View File

@@ -26,7 +26,8 @@ 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
@@ -38,7 +39,7 @@ func (f *Factory) MakeSecret(args *types.SecretArgs) (*corev1.Secret, error) {
return nil, err
}
}
f.copyLabelsAndAnnotations(s, args.Options)
f.setLabelsAndAnnnotations(s, args.GeneratorOptions)
return s, nil
}

View File

@@ -79,6 +79,7 @@ func TestConstructSecret(t *testing.T) {
type testCase struct {
description string
input types.SecretArgs
options *types.GeneratorOptions
expected *corev1.Secret
}
@@ -93,6 +94,7 @@ func TestConstructSecret(t *testing.T) {
},
},
},
options: nil,
expected: makeEnvSecret("envSecret"),
},
{
@@ -105,6 +107,7 @@ func TestConstructSecret(t *testing.T) {
},
},
},
options: nil,
expected: makeFileSecret("fileSecret"),
},
{
@@ -115,22 +118,55 @@ func TestConstructSecret(t *testing.T) {
KvPairSources: types.KvPairSources{
LiteralSources: []string{"a=x", "b=y"},
},
Options: &types.GeneratorOptions{
Labels: map[string]string{
"foo": "bar",
},
Annotations: map[string]string{
"fruit": "banana",
"pet": "dog",
},
},
},
},
options: &types.GeneratorOptions{
Labels: map[string]string{
"foo": "bar",
},
},
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{
"fruit": "banana",
"pet": "dog",
"foo": "changed",
"cat": "dog",
}),
},
}
@@ -142,7 +178,7 @@ func TestConstructSecret(t *testing.T) {
loader.NewFileLoaderAtRoot(fSys),
valtest_test.MakeFakeValidator())
for _, tc := range testCases {
f := NewFactory(kvLdr)
f := NewFactory(kvLdr, tc.options)
cm, err := f.MakeSecret(&tc.input)
if err != nil {
t.Fatalf("unexpected error: %v", err)

View File

@@ -13,20 +13,21 @@ func _() {
_ = x[ConfigMapGenerator-2]
_ = x[HashTransformer-3]
_ = x[ImageTagTransformer-4]
_ = x[LabelTransformer-5]
_ = x[LegacyOrderTransformer-6]
_ = x[NamespaceTransformer-7]
_ = x[PatchJson6902Transformer-8]
_ = x[PatchStrategicMergeTransformer-9]
_ = x[PatchTransformer-10]
_ = x[PrefixSuffixTransformer-11]
_ = x[ReplicaCountTransformer-12]
_ = x[SecretGenerator-13]
_ = x[InventoryTransformer-5]
_ = x[LabelTransformer-6]
_ = x[LegacyOrderTransformer-7]
_ = x[NamespaceTransformer-8]
_ = x[PatchJson6902Transformer-9]
_ = x[PatchStrategicMergeTransformer-10]
_ = x[PatchTransformer-11]
_ = x[PrefixSuffixTransformer-12]
_ = x[ReplicaCountTransformer-13]
_ = x[SecretGenerator-14]
}
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorHashTransformerImageTagTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerReplicaCountTransformerSecretGenerator"
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorHashTransformerImageTagTransformerInventoryTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerReplicaCountTransformerSecretGenerator"
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 62, 81, 97, 119, 139, 163, 193, 209, 232, 255, 270}
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 62, 81, 101, 117, 139, 159, 183, 213, 229, 252, 275, 290}
func (i BuiltinPluginType) String() string {
if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) {

View File

@@ -17,6 +17,7 @@ const (
ConfigMapGenerator
HashTransformer
ImageTagTransformer
InventoryTransformer
LabelTransformer
LegacyOrderTransformer
NamespaceTransformer
@@ -62,6 +63,7 @@ var TransformerFactories = map[BuiltinPluginType]func() resmap.TransformerPlugin
AnnotationsTransformer: builtins.NewAnnotationsTransformerPlugin,
HashTransformer: builtins.NewHashTransformerPlugin,
ImageTagTransformer: builtins.NewImageTagTransformerPlugin,
InventoryTransformer: builtins.NewInventoryTransformerPlugin,
LabelTransformer: builtins.NewLabelTransformerPlugin,
LegacyOrderTransformer: builtins.NewLegacyOrderTransformerPlugin,
NamespaceTransformer: builtins.NewNamespaceTransformerPlugin,

View File

@@ -6,85 +6,143 @@ 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 {
// 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
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)
},
},
})
}
// NewCompiler returns a new compiler instance.
func NewCompiler(root string) *Compiler {
return &Compiler{pluginRoot: root}
func NewCompiler(srcRoot, objRoot string) *Compiler {
return &Compiler{srcRoot: srcRoot, objRoot: 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))
// ObjRoot is root of compilation target tree.
func (b *Compiler) ObjRoot() string {
return b.objRoot
}
func (b *Compiler) srcPath() string {
return filepath.Join(b.workDir, b.rawKind+".go")
// SrcRoot is where to find src.
func (b *Compiler) SrcRoot() string {
return b.srcRoot
}
func (b *Compiler) objFile() string {
return b.rawKind + ".so"
func goBin() string {
return filepath.Join(runtime.GOROOT(), "bin", "go")
}
// 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.
// 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.
return nil
}
if !FileExists(b.srcPath()) {
return fmt.Errorf("cannot find source at '%s'", b.srcPath())
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 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", b.objFile(),
"-o", objFile, srcFile,
}
goBin := goBin()
if !FileExists(goBin) {
@@ -92,26 +150,34 @@ func (b *Compiler) Compile() error {
"cannot find go compiler %s", goBin)
}
cmd := exec.Command(goBin, commands...)
b.stderr.Reset()
cmd.Stderr = &b.stderr
b.stdout.Reset()
cmd.Stdout = &b.stdout
var stderr bytes.Buffer
cmd.Stderr = &stderr
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",
b.srcPath(), b.stderr.String())
err, "cannot compile %s:\nSTDERR\n%s\n", srcFile, stderr.String())
}
return nil
}
func (b *Compiler) report() {
log.Println("stdout: -------")
log.Println(b.stdout.String())
log.Println("----------------")
log.Println("stderr: -------")
log.Println(b.stderr.String())
log.Println("----------------")
// 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
}

View File

@@ -4,6 +4,8 @@
package compiler_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@@ -13,46 +15,53 @@ 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)
c := NewCompiler(srcRoot, configRoot)
if configRoot != c.ObjRoot() {
t.Errorf("unexpected objRoot %s", c.ObjRoot())
}
c.SetGVK("someteam.example.com", "v1", "DatePrefixer")
expectObj := filepath.Join(
srcRoot, "someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
if expectObj != c.ObjPath() {
t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath())
c.ObjRoot(),
"someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
if FileExists(expectObj) {
t.Errorf("obj file should not exist yet: %s", expectObj)
}
err = c.Compile()
err = c.Compile("someteam.example.com", "v1", "DatePrefixer")
if err != nil {
t.Error(err)
}
if !FileExists(expectObj) {
if !RecentFileExists(expectObj) {
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(
srcRoot,
c.ObjRoot(),
"builtin", "", "secretgenerator", "SecretGenerator.so")
if expectObj != c.ObjPath() {
t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath())
if FileExists(expectObj) {
t.Errorf("obj file should not exist yet: %s", expectObj)
}
err = c.Compile()
err = c.Compile("builtin", "", "SecretGenerator")
if err != nil {
t.Error(err)
}
if !FileExists(expectObj) {
if !RecentFileExists(expectObj) {
t.Errorf("didn't find expected obj file %s", expectObj)
}
c.Cleanup()
err = os.RemoveAll(c.ObjRoot())
if err != nil {
t.Errorf(
"removing temp dir: %s %v", c.ObjRoot(), err)
}
if FileExists(expectObj) {
t.Errorf("obj file '%s' should be gone", expectObj)
t.Errorf("cleanup failed; still see: %s", expectObj)
}
}

View File

@@ -1,115 +0,0 @@
// 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 both exists and has an
// 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
}
// FileModifiedAfter returns true if the file both exists and was
// modified after the given time..
func FileModifiedAfter(path string, t time.Time) bool {
fi, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
return fi.ModTime().After(t)
}
func FileExists(path string) bool {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}

View File

@@ -1,26 +0,0 @@
// 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)
}
}

View File

@@ -94,7 +94,7 @@ TO GENERATE CODE
cd $repo/plugin/builtin
go generate ./...
See travis/kyaml-pre-commit.sh for canonical way
See travis/pre-commit.sh for canonical way
to execute the above.
This creates

View File

@@ -266,9 +266,8 @@ func (p *ExecPlugin) UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, err
}
r.SetAnnotations(annotations)
r.SetOptions(types.NewGenArgs(
&types.GeneratorArgs{
Behavior: behavior,
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
&types.GeneratorArgs{Behavior: behavior},
&types.GeneratorOptions{DisableNameSuffixHash: !needsHash}))
}
return rm, nil
}

View File

@@ -116,9 +116,7 @@ func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHa
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{"name": name},
}, &types.GeneratorArgs{
Behavior: behavior,
Options: &types.GeneratorOptions{DisableNameSuffixHash: disableHash}})
}, &types.GeneratorArgs{Behavior: behavior}, &types.GeneratorOptions{DisableNameSuffixHash: disableHash})
}
func strptr(s string) *string {

View File

@@ -22,7 +22,6 @@ 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
@@ -108,35 +107,17 @@ 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) {
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)
}
// 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())
} else {
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)
}
err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind)
}
if err != nil {
return nil, err

View File

@@ -15,7 +15,6 @@ 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 (
@@ -59,26 +58,22 @@ func TestLoader(t *testing.T) {
if err != nil {
t.Fatal(err)
}
generatorConfigs, err := rmF.NewResMapFromBytes([]byte(
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(
someServiceGenerator + "---\n" + secretGenerator))
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)
}
_, err = pLdr.LoadGenerators(
fLdr, valtest_test.MakeFakeValidator(), m)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -106,10 +106,15 @@ func unmarshal(y []byte, o interface{}) error {
// MakeCustomizedResMap creates a fully customized ResMap
// per the instructions contained in its kustomiztion instance.
func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
return kt.makeCustomizedResMap()
return kt.makeCustomizedResMap(types.GarbageIgnore)
}
func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) {
func (kt *KustTarget) MakePruneConfigMap() (resmap.ResMap, error) {
return kt.makeCustomizedResMap(types.GarbageCollect)
}
func (kt *KustTarget) makeCustomizedResMap(
garbagePolicy types.GarbagePolicy) (resmap.ResMap, error) {
ra, err := kt.AccumulateTarget()
if err != nil {
return nil, err
@@ -136,6 +141,11 @@ func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) {
return nil, err
}
err = kt.computeInventory(ra, garbagePolicy)
if err != nil {
return nil, err
}
return ra.ResMap(), nil
}
@@ -149,6 +159,35 @@ func (kt *KustTarget) addHashesToNames(
return ra.Transform(p)
}
func (kt *KustTarget) computeInventory(
ra *accumulator.ResAccumulator, garbagePolicy types.GarbagePolicy) error {
inv := kt.kustomization.Inventory
if inv == nil {
return nil
}
if inv.Type != "ConfigMap" {
return fmt.Errorf("don't know how to do that")
}
if inv.ConfigMap.Namespace != kt.kustomization.Namespace {
return fmt.Errorf("namespace mismatch")
}
var c struct {
Policy string
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
}
c.Name = inv.ConfigMap.Name
c.Namespace = inv.ConfigMap.Namespace
c.Policy = garbagePolicy.String()
p := builtins.NewInventoryTransformerPlugin()
err := kt.configureBuiltinPlugin(p, c, builtinhelpers.InventoryTransformer)
if err != nil {
return err
}
return ra.Transform(p)
}
// AccumulateTarget returns a new ResAccumulator,
// holding customized resources and the data/rules used
// to do so. The name back references and vars are
@@ -325,8 +364,7 @@ func (kt *KustTarget) configureBuiltinPlugin(
}
err = p.Config(resmap.NewPluginHelpers(kt.ldr, kt.validator, kt.rFactory), y)
if err != nil {
return errors.Wrapf(
err, "trouble configuring builtin %s with config: `\n%s`", bpt, string(y))
return errors.Wrapf(err, "builtin %s config: %v", bpt, y)
}
return nil
}

View File

@@ -74,12 +74,14 @@ 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 {
@@ -93,12 +95,14 @@ 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 {

View File

@@ -7,6 +7,7 @@ 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"
@@ -18,7 +19,8 @@ import (
// high level tests.
func TestMakeCustomizedResMap(t *testing.T) {
th := kusttest_test.MakeHarness(t)
fSys := filesys.MakeFsInMemory()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
th.WriteK("/whatever", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
@@ -166,7 +168,7 @@ metadata:
}
actual, err := makeKustTargetWithRf(
t, th.GetFSys(), "/whatever", resFactory).MakeCustomizedResMap()
t, fSys, "/whatever", resFactory).MakeCustomizedResMap()
if err != nil {
t.Fatalf("unexpected Resources error %v", err)
}

View File

@@ -0,0 +1,12 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inventory
const (
// Annotation that contains the inventory content.
ContentAnnotation = "kustomize.config.k8s.io/Inventory"
// Annotation for inventory content hash.
HashAnnotation = "kustomize.config.k8s.io/InventoryHash"
)

235
api/inventory/inventory.go Normal file
View File

@@ -0,0 +1,235 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inventory
import (
"encoding/json"
"sigs.k8s.io/kustomize/api/resid"
)
//Refs is a reference map. Each key is the id
//of a k8s resource, and each value is a list of
//object ids that refer back to the object in the
//key.
//For example, the key could correspond to a
//ConfigMap, and the list of values might include
//several different Deployments that get data from
//that ConfigMap (and thus refer to it).
//References are important in inventory management
//because one may not delete an object before all
//objects referencing it have been removed.
type Refs map[resid.ResId][]resid.ResId
func NewRefs() Refs {
return Refs{}
}
// Merge merges a Refs into an existing Refs
func (rf Refs) Merge(b Refs) Refs {
for key, value := range b {
_, ok := rf[key]
if ok {
rf[key] = append(rf[key], value...)
} else {
rf[key] = value
}
}
return rf
}
// removeIfContains removes the reference relationship
// a --> b
// from the Refs if it exists
func (rf Refs) RemoveIfContains(a, b resid.ResId) {
refs, ok := rf[a]
if !ok {
return
}
for i, ref := range refs {
if ref.Equals(b) {
rf[a] = append(refs[:i], refs[i+1:]...)
break
}
}
}
//Inventory is a an object intended for
//serialization into the annotations of a so-called
//apply-root object (a ConfigMap, an Application,
//etc.) living in the cluster. This apply-root
//object is written as part of an apply operation as
//a means to record overall cluster state changes.
//At the end of a successful apply, the "current"
//field in Inventory will be a map whose keys all
//correspond to an object in the cluster, and
//"previous" will be the previous such set (an empty
//set on the first apply).
//An Inventory allows the Prune method to work.
type Inventory struct {
Current Refs `json:"current,omitempty"`
Previous Refs `json:"previous,omitempty"`
}
// NewInventory returns an Inventory object
func NewInventory() *Inventory {
return &Inventory{
Current: NewRefs(),
Previous: NewRefs(),
}
}
// UpdateCurrent updates the Inventory given a
// new current Refs
// The existing Current refs is merged into
// the Previous refs
func (a *Inventory) UpdateCurrent(curref Refs) *Inventory {
if len(a.Previous) > 0 {
a.Previous.Merge(a.Current)
} else {
a.Previous = a.Current
}
a.Current = curref
return a
}
func (a *Inventory) removeNewlyOrphanedItemsFromPrevious() []resid.ResId {
var results []resid.ResId
for item, refs := range a.Previous {
if _, ok := a.Current[item]; ok {
delete(a.Previous, item)
continue
}
var newRefs []resid.ResId
toDelete := true
for _, ref := range refs {
if _, ok := a.Current[ref]; ok {
toDelete = false
newRefs = append(newRefs, ref)
}
}
if toDelete {
results = append(results, item)
delete(a.Previous, item)
} else {
a.Previous[item] = newRefs
}
}
return results
}
func (a *Inventory) removeOrphanedItemsFromPreviousThatAreNotInCurrent() []resid.ResId {
var results []resid.ResId
for item, refs := range a.Previous {
if _, ok := a.Current[item]; ok {
continue
}
if len(refs) == 0 {
results = append(results, item)
delete(a.Previous, item)
}
}
return results
}
func (a *Inventory) removeOrphanedItemsFromPreviousThatAreInCurrent() {
//Remove references from Previous that are already in Current refs
for item, refs := range a.Current {
for _, ref := range refs {
a.Previous.RemoveIfContains(item, ref)
}
}
//Remove items from Previous that are already in Current refs
for item, refs := range a.Previous {
if len(refs) == 0 {
if _, ok := a.Current[item]; ok {
delete(a.Previous, item)
}
}
}
}
// Prune computes the diff of Current refs and Previous refs
// and returns a list of Items that can be pruned.
// An item that can be pruned shows up only in Previous refs.
// Prune also updates the Previous refs with those items removed
func (a *Inventory) Prune() []resid.ResId {
a.removeOrphanedItemsFromPreviousThatAreInCurrent()
// These are candidates for deletion from the cluster.
removable1 := a.removeOrphanedItemsFromPreviousThatAreNotInCurrent()
removable2 := a.removeNewlyOrphanedItemsFromPrevious()
return append(removable1, removable2...)
}
// inventory is the internal type used for serialization
type inventory struct {
Current map[string][]resid.ResId `json:"current,omitempty"`
Previous map[string][]resid.ResId `json:"previous,omitempty"`
}
func (a *Inventory) toInternalType() inventory {
prev := map[string][]resid.ResId{}
curr := map[string][]resid.ResId{}
for id, refs := range a.Current {
curr[id.String()] = refs
}
for id, refs := range a.Previous {
prev[id.String()] = refs
}
return inventory{
Current: curr,
Previous: prev,
}
}
func (a *Inventory) fromInternalType(i *inventory) {
for s, refs := range i.Previous {
a.Previous[resid.FromString(s)] = refs
}
for s, refs := range i.Current {
a.Current[resid.FromString(s)] = refs
}
}
func (a *Inventory) marshal() ([]byte, error) {
return json.Marshal(a.toInternalType())
}
func (a *Inventory) unMarshal(data []byte) error {
inv := &inventory{
Current: map[string][]resid.ResId{},
Previous: map[string][]resid.ResId{},
}
err := json.Unmarshal(data, inv)
if err != nil {
return err
}
a.fromInternalType(inv)
return nil
}
// UpdateAnnotations update the annotation map
func (a *Inventory) UpdateAnnotations(annot map[string]string) error {
data, err := a.marshal()
if err != nil {
return err
}
annot[ContentAnnotation] = string(data)
return nil
}
// LoadFromAnnotation loads the Inventory date from the annotation map
func (a *Inventory) LoadFromAnnotation(annot map[string]string) error {
value, ok := annot[ContentAnnotation]
if ok {
return a.unMarshal([]byte(value))
}
return nil
}

View File

@@ -0,0 +1,60 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inventory_test
import (
"testing"
. "sigs.k8s.io/kustomize/api/inventory"
"sigs.k8s.io/kustomize/api/resid"
)
func makeRefs() (Refs, Refs) {
a := resid.FromString("G1_V1_K1|ns1|nm1")
b := resid.FromString("G2_V2_K2|ns2|nm2")
c := resid.FromString("G3_V3_K3|ns3|nm3")
current := NewRefs()
current[a] = []resid.ResId{b, c}
current[b] = []resid.ResId{}
current[c] = []resid.ResId{}
newRefs := NewRefs()
newRefs[a] = []resid.ResId{b}
newRefs[b] = []resid.ResId{}
return current, newRefs
}
func TestInventory(t *testing.T) {
inventory := NewInventory()
curref, _ := makeRefs()
inventory.UpdateCurrent(curref)
if len(inventory.Current) != 3 {
t.Fatalf("not getting the correct inventory %v", inventory)
}
curref, newref := makeRefs()
inventory.UpdateCurrent(curref)
if len(inventory.Current) != 3 {
t.Fatalf("not getting the corrent inventory %v", inventory)
}
if len(inventory.Previous) != 3 {
t.Fatalf("not getting the corrent inventory %v", inventory)
}
items := inventory.Prune()
if len(items) != 0 {
t.Fatalf("not getting the corrent items %v", items)
}
if len(inventory.Previous) != 0 {
t.Fatalf("not getting the corrent inventory %v", inventory)
}
inventory.UpdateCurrent(newref)
items = inventory.Prune()
if len(items) != 1 {
t.Fatalf("not getting the corrent items %v", items)
}
if len(inventory.Previous) != 0 {
t.Fatalf("not getting the corrent inventory %v", inventory.Previous)
}
}

View File

@@ -75,8 +75,11 @@ func (kf *KunstructuredFactoryImpl) FromMap(
// MakeConfigMap returns an instance of Kunstructured for ConfigMap
func (kf *KunstructuredFactoryImpl) MakeConfigMap(
kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
o, err := configmapandsecret.NewFactory(kvLdr).MakeConfigMap(args)
kvLdr ifc.KvLoader,
options *types.GeneratorOptions,
args *types.ConfigMapArgs) (ifc.Kunstructured, error) {
o, err := configmapandsecret.NewFactory(
kvLdr, options).MakeConfigMap(args)
if err != nil {
return nil, err
}
@@ -85,8 +88,11 @@ func (kf *KunstructuredFactoryImpl) MakeConfigMap(
// MakeSecret returns an instance of Kunstructured for Secret
func (kf *KunstructuredFactoryImpl) MakeSecret(
kvLdr ifc.KvLoader, args *types.SecretArgs) (ifc.Kunstructured, error) {
o, err := configmapandsecret.NewFactory(kvLdr).MakeSecret(args)
kvLdr ifc.KvLoader,
options *types.GeneratorOptions,
args *types.SecretArgs) (ifc.Kunstructured, error) {
o, err := configmapandsecret.NewFactory(
kvLdr, options).MakeSecret(args)
if err != nil {
return nil, err
}

View File

@@ -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)
}

View File

@@ -345,24 +345,5 @@ 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
`
)

View File

@@ -35,46 +35,37 @@ 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(b types.BuiltinPluginLoadingOptions) (*types.PluginConfig, error) {
func EnabledPluginConfig() (*types.PluginConfig, error) {
dir, err := DefaultAbsPluginHome(filesys.MakeFsOnDisk())
if err != nil {
return nil, err
}
return MakePluginConfig(types.PluginRestrictionsNone, b, dir), nil
return MakePluginConfig(types.PluginRestrictionsNone, dir), nil
}
func DisabledPluginConfig() *types.PluginConfig {
return MakePluginConfig(
types.PluginRestrictionsBuiltinsOnly,
types.BploUseStaticallyLinked,
NoPluginHomeSentinal)
types.PluginRestrictionsBuiltinsOnly, NoPluginHomeSentinal)
}
func MakePluginConfig(
pr types.PluginRestrictions,
b types.BuiltinPluginLoadingOptions,
home string) *types.PluginConfig {
pr types.PluginRestrictions, 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{

View File

@@ -154,8 +154,9 @@ LEGUME=chickpea
`)
th.WriteF("/app/overlay/configmap/dummy.txt",
`Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
`)
th.WriteF("/app/overlay/deployment/deployment.yaml", `
apiVersion: apps/v1
@@ -292,8 +293,11 @@ metadata:
---
apiVersion: v1
data:
nonsense: "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit, sed do eiusmod
tempor\nincididunt ut labore et dolore magna aliqua. \n"
nonsense: |
Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
kind: ConfigMap
metadata:
annotations:
@@ -302,6 +306,6 @@ metadata:
app: mungebot
org: kubernetes
repo: test-infra
name: test-infra-app-config-f462h769f9
name: test-infra-app-config-hh272bg5d4
`)
}

View File

@@ -143,8 +143,7 @@ apiVersion: builtin
kind: ConfigMapGenerator
metadata:
name: my-config
options:
disableNameSuffixHash: true
disableNameSuffixHash: true
literals:
- MY_ENV=foo
`)

View File

@@ -12,12 +12,10 @@ import (
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func findSecret(m resmap.ResMap, prefix string) *resource.Resource {
func findSecret(m resmap.ResMap) *resource.Resource {
for _, r := range m.Resources() {
if r.OrgId().Kind == "Secret" {
if prefix == "" || strings.HasPrefix(r.GetName(), prefix) {
return r
}
return r
}
}
return nil
@@ -79,7 +77,7 @@ metadata:
m := th.Run("/whatever", th.MakeDefaultOptions())
secret := findSecret(m, "")
secret := findSecret(m)
if secret == nil {
t.Errorf("Expected to find a Secret")
}
@@ -92,7 +90,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")
}
@@ -100,47 +98,3 @@ 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())
}
}

View File

@@ -71,7 +71,11 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
return nil, err
}
var m resmap.ResMap
m, err = kt.MakeCustomizedResMap()
if b.options.DoPrune {
m, err = kt.MakePruneConfigMap()
} else {
m, err = kt.MakeCustomizedResMap()
}
if err != nil {
return nil, err
}

View File

@@ -1,110 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func makeCommonFileForMergeEnvFromTest(th kusttest_test.Harness) {
th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: image1
envFrom:
- configMapRef:
name: some-config
- configMapRef:
name: more-config
`)
}
// When patching, `envFrom` should merge the list instead of replacing it.
func TestMergeEnvFrom(t *testing.T) {
th := kusttest_test.MakeHarness(t)
makeCommonFileForMergeEnvFromTest(th)
th.WriteK("/app", `
resources:
- deployment.yaml
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
envFrom:
- configMapRef:
name: another-config
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- envFrom:
- configMapRef:
name: another-config
image: image1
name: nginx
`)
}
func TestMergeEnvFromViaJsonInline(t *testing.T) {
th := kusttest_test.MakeHarness(t)
makeCommonFileForMergeEnvFromTest(th)
th.WriteK("app", `
resources:
- deployment.yaml
patches:
- target:
kind: Deployment
name: nginx
patch: |-
- op: add
path: /spec/template/spec/containers/0/envFrom/-
value:
configMapRef:
name: another-config
`)
m := th.Run("app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- envFrom:
- configMapRef:
name: some-config
- configMapRef:
name: more-config
- configMapRef:
name: another-config
image: image1
name: nginx
`)
}

View File

@@ -0,0 +1,163 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestPruneConfigMap(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", `
resources:
- deployment.yaml
- service.yaml
- secret.yaml
inventory:
type: ConfigMap
configMap:
name: haha
namespace: default
namePrefix: my-
namespace: default
`)
th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
emptyDir: {}
`)
th.WriteF("/app/base/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: mmmysql
labels:
app: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
`)
th.WriteF("/app/base/secret.yaml", `
apiVersion: v1
kind: Secret
metadata:
name: pass
type: Opaque
data:
# Default password is "admin".
password: YWRtaW4=
username: jingfang
`)
m := th.Run("/app/base", th.MakeDefaultOptions())
//nolint
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1beta2
kind: Deployment
metadata:
labels:
app: mysql
name: my-mysql
namespace: default
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: my-pass
image: mysql:5.6
name: mysql
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- mountPath: /var/lib/mysql
name: mysql-persistent-storage
volumes:
- emptyDir: {}
name: mysql-persistent-storage
---
apiVersion: v1
kind: Service
metadata:
labels:
app: mysql
name: my-mmmysql
namespace: default
spec:
ports:
- port: 3306
selector:
app: mysql
---
apiVersion: v1
data:
password: YWRtaW4=
username: jingfang
kind: Secret
metadata:
name: my-pass
namespace: default
type: Opaque
---
apiVersion: v1
kind: ConfigMap
metadata:
annotations:
kustomize.config.k8s.io/Inventory: '{"current":{"apps_v1beta2_Deployment|default|my-mysql":null,"~G_v1_Secret|default|my-pass":[{"group":"apps","version":"v1beta2","kind":"Deployment","name":"my-mysql","namespace":"default"}],"~G_v1_Service|default|my-mmmysql":null}}'
kustomize.config.k8s.io/InventoryHash: kd67f7ht8t
name: haha
namespace: default
`)
}

View File

@@ -9,6 +9,7 @@ import (
"fmt"
"os"
"path"
"regexp"
"strings"
"unicode"
"unicode/utf8"
@@ -85,11 +86,17 @@ func (kvl *loader) keyValuesFromFileSources(sources []string) ([]types.Pair, err
if err != nil {
return nil, err
}
kvs = append(kvs, types.Pair{Key: k, Value: string(content)})
kvs = append(kvs, types.Pair{Key: k, Value: trimTrailingSpacesInLines(string(content))})
}
return kvs, nil
}
// trimTrailingSpacesInLines takes string with multiple lines and trims the trailing white spaces and tabs from each line.
func trimTrailingSpacesInLines(str string) string {
re := regexp.MustCompile(`[ \t]*\n`)
return re.ReplaceAllString(str, "\n")
}
func (kvl *loader) keyValuesFromEnvFiles(paths []string) ([]types.Pair, error) {
var kvs []types.Pair
for _, p := range paths {

View File

@@ -95,3 +95,12 @@ func TestKeyValuesFromFileSources(t *testing.T) {
}
}
}
func TestTrimTrailingSpacesInLines(t *testing.T) {
input := "\"fooKey\": \"fooValue\" \t\n \t\t \n\t\"barKey\": \"barValue\""
expected := "\"fooKey\": \"fooValue\"\n\n\t\"barKey\": \"barValue\""
res := trimTrailingSpacesInLines(input)
if !reflect.DeepEqual(res, expected) {
t.Errorf("Trim trailing spaces in lines should succeed, got: %s exptected: %s", res, expected)
}
}

View File

@@ -66,10 +66,12 @@ 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, argList []types.ConfigMapArgs) (ResMap, error) {
kvLdr ifc.KvLoader,
options *types.GeneratorOptions,
argList []types.ConfigMapArgs) (ResMap, error) {
var resources []*resource.Resource
for _, args := range argList {
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
res, err := rmF.resF.MakeConfigMap(kvLdr, options, &args)
if err != nil {
return nil, errors.Wrap(err, "NewResMapFromConfigMapArgs")
}
@@ -79,8 +81,10 @@ func (rmF *Factory) NewResMapFromConfigMapArgs(
}
func (rmF *Factory) FromConfigMapArgs(
kvLdr ifc.KvLoader, args types.ConfigMapArgs) (ResMap, error) {
res, err := rmF.resF.MakeConfigMap(kvLdr, &args)
kvLdr ifc.KvLoader,
options *types.GeneratorOptions,
args types.ConfigMapArgs) (ResMap, error) {
res, err := rmF.resF.MakeConfigMap(kvLdr, options, &args)
if err != nil {
return nil, err
}
@@ -90,10 +94,12 @@ 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, argsList []types.SecretArgs) (ResMap, error) {
kvLdr ifc.KvLoader,
options *types.GeneratorOptions,
argsList []types.SecretArgs) (ResMap, error) {
var resources []*resource.Resource
for _, args := range argsList {
res, err := rmF.resF.MakeSecret(kvLdr, &args)
res, err := rmF.resF.MakeSecret(kvLdr, options, &args)
if err != nil {
return nil, errors.Wrap(err, "NewResMapFromSecretArgs")
}
@@ -103,8 +109,10 @@ func (rmF *Factory) NewResMapFromSecretArgs(
}
func (rmF *Factory) FromSecretArgs(
kvLdr ifc.KvLoader, args types.SecretArgs) (ResMap, error) {
res, err := rmF.resF.MakeSecret(kvLdr, &args)
kvLdr ifc.KvLoader,
options *types.GeneratorOptions,
args types.SecretArgs) (ResMap, error) {
res, err := rmF.resF.MakeSecret(kvLdr, options, &args)
if err != nil {
return nil, err
}

View File

@@ -227,7 +227,7 @@ BAR=baz
t.Fatalf("error adding file '%s': %v\n", tc.filepath, fErr)
}
}
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, tc.input)
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, nil, 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()), secrets)
valtest_test.MakeFakeValidator()), nil, secrets)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

View File

@@ -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)

View File

@@ -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) *Resource {
return rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args))
m map[string]interface{}, args *types.GeneratorArgs, option *types.GeneratorOptions) *Resource {
return rf.makeOne(rf.kf.FromMap(m), types.NewGenArgs(args, option))
}
// 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)
o = types.NewGenArgs(nil, nil)
}
r := &Resource{
Kunstructured: u,
@@ -147,19 +147,33 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
}
// MakeConfigMap makes an instance of Resource for ConfigMap
func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs) (*Resource, error) {
u, err := rf.kf.MakeConfigMap(kvLdr, args)
func (rf *Factory) MakeConfigMap(
kvLdr ifc.KvLoader,
options *types.GeneratorOptions,
args *types.ConfigMapArgs) (*Resource, error) {
u, err := rf.kf.MakeConfigMap(kvLdr, options, args)
if err != nil {
return nil, err
}
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
return rf.makeOne(
u,
types.NewGenArgs(
&types.GeneratorArgs{Behavior: args.Behavior},
options)), nil
}
// MakeSecret makes an instance of Resource for Secret
func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Resource, error) {
u, err := rf.kf.MakeSecret(kvLdr, args)
func (rf *Factory) MakeSecret(
kvLdr ifc.KvLoader,
options *types.GeneratorOptions,
args *types.SecretArgs) (*Resource, error) {
u, err := rf.kf.MakeSecret(kvLdr, options, args)
if err != nil {
return nil, err
}
return rf.makeOne(u, types.NewGenArgs(&args.GeneratorArgs)), nil
return rf.makeOne(
u,
types.NewGenArgs(
&types.GeneratorArgs{Behavior: args.Behavior},
options)), nil
}

View File

@@ -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(types.BploLoadFromFileSys)
c, err := konfig.EnabledPluginConfig()
if err != nil {
if strings.Contains(err.Error(), "unable to find plugin root") {
th.t.Log(

View File

@@ -4,9 +4,6 @@
package kusttest_test
import (
"bytes"
"fmt"
"strconv"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
@@ -19,50 +16,36 @@ 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 creates Resources from byte streams.
rf *resmap.Factory
// A file loader using the Harness.fSys to read test data.
rf *resmap.Factory
ldr ifc.Loader
// A plugin loader that loads plugins from a (real) file system.
pl *pLdr.Loader
pl *pLdr.Loader
}
func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
pte := newPluginTestEnv(t).set()
pc, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
pc, err := konfig.EnabledPluginConfig()
if err != nil {
t.Fatal(err)
}
fSys := filesys.MakeFsInMemory()
rf := resmap.NewFactory(
resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()),
transformer.NewFactoryImpl())
result := &HarnessEnhanced{
Harness: MakeHarness(t),
Harness: Harness{t: t, fSys: fSys},
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
@@ -72,23 +55,21 @@ 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.prepareGoPlugin(g, v, k)
th.pte.buildGoPlugin(g, v, k)
return th
}
func (th *HarnessEnhanced) PrepExecPlugin(g, v, k string) *HarnessEnhanced {
th.pte.prepareExecPlugin(g, v, k)
th.pte.prepExecPlugin(g, v, k)
return th
}
func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced {
th.pte.buildGoPlugin(konfig.BuiltinPluginPackage, "", 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)
@@ -128,68 +109,12 @@ 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))

View File

@@ -4,7 +4,11 @@
package kusttest_test
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
@@ -12,14 +16,17 @@ import (
"sigs.k8s.io/kustomize/api/konfig"
)
// pluginTestEnv manages compiling plugins for tests.
// It manages a Go plugin compiler, and sets/resets shell env vars as needed.
// 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.
type pluginTestEnv struct {
t *testing.T
compiler *compiler.Compiler
pluginRoot string
oldXdg string
wasSet bool
t *testing.T
compiler *compiler.Compiler
srcRoot string
workDir string
oldXdg string
wasSet bool
}
// newPluginTestEnv returns a new instance of pluginTestEnv.
@@ -32,44 +39,76 @@ 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.pluginRoot, err = compiler.DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
x.srcRoot, err = compiler.DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
if err != nil {
x.t.Error(err)
}
x.compiler = compiler.NewCompiler(x.pluginRoot)
x.compiler = compiler.NewCompiler(x.srcRoot, x.workDir)
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()
}
// 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()
// 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)
if err != nil {
x.t.Errorf("compile failed: %v", 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.
// 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) setEnv() {
x.oldXdg, x.wasSet = os.LookupEnv(konfig.KustomizePluginHomeEnv)
os.Setenv(konfig.KustomizePluginHomeEnv, x.pluginRoot)
os.Setenv(konfig.KustomizePluginHomeEnv, x.workDir)
}
func (x *pluginTestEnv) resetEnv() {

View File

@@ -0,0 +1,64 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform
import (
"errors"
"fmt"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
)
// mapTransformer applies a string->string map to fieldSpecs.
type mapTransformer struct {
m map[string]string
fieldSpecs []types.FieldSpec
}
var _ resmap.Transformer = &mapTransformer{}
// NewMapTransformer construct a mapTransformer.
func NewMapTransformer(
pc []types.FieldSpec, m map[string]string) (resmap.Transformer, error) {
if m == nil {
return newNoOpTransformer(), nil
}
if pc == nil {
return nil, errors.New("fieldSpecs is not expected to be nil")
}
return &mapTransformer{fieldSpecs: pc, m: m}, nil
}
// Transform apply each <key, value> pair in the mapTransformer to the
// fields specified in mapTransformer.
func (o *mapTransformer) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() {
for _, path := range o.fieldSpecs {
if !r.OrgId().IsSelected(&path.Gvk) {
continue
}
err := MutateField(
r.Map(), path.PathSlice(),
path.CreateIfNotPresent, o.addMap)
if err != nil {
return err
}
}
}
return nil
}
func (o *mapTransformer) addMap(in interface{}) (interface{}, error) {
m, ok := in.(map[string]interface{})
if in == nil {
m = map[string]interface{}{}
} else if !ok {
return nil, fmt.Errorf("%#v is expected to be %T", in, m)
}
for k, v := range o.m {
m[k] = v
}
return m, nil
}

View File

@@ -0,0 +1,570 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform_test
import (
"testing"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/resource"
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
. "sigs.k8s.io/kustomize/api/transform"
)
var resourceFactory = resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
func TestLabelsRun(t *testing.T) {
m := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
},
}).
Add(map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"old-label": "old-value",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
},
"spec": map[string]interface{}{
"ports": []interface{}{
map[string]interface{}{
"name": "port1",
"port": "12345",
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": map[string]interface{}{
"name": "job1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": map[string]interface{}{
"name": "job2",
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"old-label": "old-value",
},
},
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1beta1",
"kind": "CronJob",
"metadata": map[string]interface{}{
"name": "cronjob1",
},
"spec": map[string]interface{}{
"schedule": "* 23 * * *",
"jobTemplate": map[string]interface{}{
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1beta1",
"kind": "CronJob",
"metadata": map[string]interface{}{
"name": "cronjob2",
},
"spec": map[string]interface{}{
"schedule": "* 23 * * *",
"jobTemplate": map[string]interface{}{
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"old-label": "old-value",
},
},
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
},
},
}).ResMap()
expected := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
}).
Add(map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"old-label": "old-value",
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"ports": []interface{}{
map[string]interface{}{
"name": "port1",
"port": "12345",
},
},
"selector": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": map[string]interface{}{
"name": "job1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": map[string]interface{}{
"name": "job2",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
"old-label": "old-value",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1beta1",
"kind": "CronJob",
"metadata": map[string]interface{}{
"name": "cronjob1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"schedule": "* 23 * * *",
"jobTemplate": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1beta1",
"kind": "CronJob",
"metadata": map[string]interface{}{
"name": "cronjob2",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"schedule": "* 23 * * *",
"jobTemplate": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"old-label": "old-value",
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
},
},
}).ResMap()
lt, err := NewMapTransformer(
builtinconfig.MakeDefaultConfig().CommonLabels,
map[string]string{"label-key1": "label-value1", "label-key2": "label-value2"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = lt.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}
func TestAnnotationsRun(t *testing.T) {
m := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
},
}).
Add(map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"old-label": "old-value",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
},
"spec": map[string]interface{}{
"ports": []interface{}{
map[string]interface{}{
"name": "port1",
"port": "12345",
},
},
},
}).ResMap()
expected := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
},
}).
Add(map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
"labels": map[string]interface{}{
"old-label": "old-value",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
},
"spec": map[string]interface{}{
"ports": []interface{}{
map[string]interface{}{
"name": "port1",
"port": "12345",
},
},
},
}).ResMap()
at, err := NewMapTransformer(
builtinconfig.MakeDefaultConfig().CommonAnnotations,
map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = at.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}
func TestAnnotationsRunWithNullValue(t *testing.T) {
m := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
"annotations": nil,
},
}).ResMap()
expected := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
},
}).ResMap()
at, err := NewMapTransformer(
builtinconfig.MakeDefaultConfig().CommonAnnotations,
map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = at.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}

View File

@@ -0,0 +1,21 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform
import "sigs.k8s.io/kustomize/api/resmap"
// noOpTransformer contains a no-op transformer.
type noOpTransformer struct{}
var _ resmap.Transformer = &noOpTransformer{}
// newNoOpTransformer constructs a noOpTransformer.
func newNoOpTransformer() resmap.Transformer {
return &noOpTransformer{}
}
// Transform does nothing.
func (o *noOpTransformer) Transform(_ resmap.ResMap) error {
return nil
}

View File

@@ -0,0 +1,12 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
//go:generate stringer -type=GarbagePolicy
type GarbagePolicy int
const (
GarbageIgnore GarbagePolicy = iota + 1
GarbageCollect
)

View File

@@ -0,0 +1,25 @@
// Code generated by "stringer -type=GarbagePolicy"; DO NOT EDIT.
package types
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[GarbageIgnore-1]
_ = x[GarbageCollect-2]
}
const _GarbagePolicy_name = "GarbageIgnoreGarbageCollect"
var _GarbagePolicy_index = [...]uint8{0, 13, 27}
func (i GarbagePolicy) String() string {
i -= 1
if i < 0 || i >= GarbagePolicy(len(_GarbagePolicy_index)-1) {
return "GarbagePolicy(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _GarbagePolicy_name[_GarbagePolicy_index[i]:_GarbagePolicy_index[i+1]]
}

View File

@@ -8,14 +8,18 @@ import (
"strings"
)
// GenArgs is a facade over GeneratorArgs, exposing a few readonly properties.
// GenArgs contains both GeneratorArgs and GeneratorOptions.
type GenArgs struct {
args *GeneratorArgs
opts *GeneratorOptions
}
// NewGenArgs returns a new instance of GenArgs.
func NewGenArgs(args *GeneratorArgs) *GenArgs {
return &GenArgs{args: args}
// NewGenArgs returns a new object of GenArgs
func NewGenArgs(args *GeneratorArgs, opts *GeneratorOptions) *GenArgs {
return &GenArgs{
args: args,
opts: opts,
}
}
func (g *GenArgs) String() string {
@@ -34,7 +38,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.args.Options == nil || !g.args.Options.DisableNameSuffixHash)
(g.opts == nil || !g.opts.DisableNameSuffixHash)
}
// Behavior returns Behavior field of GeneratorArgs

View File

@@ -24,10 +24,8 @@ func TestGenArgs_String(t *testing.T) {
},
{
ga: NewGenArgs(
&GeneratorArgs{
Behavior: "merge",
Options: &GeneratorOptions{DisableNameSuffixHash: false},
}),
&GeneratorArgs{Behavior: "merge"},
&GeneratorOptions{DisableNameSuffixHash: false}),
expected: "{nsfx:true,beh:merge}",
},
}

View File

@@ -22,6 +22,6 @@ type GeneratorArgs struct {
// KvPairSources for the generator.
KvPairSources `json:",inline,omitempty" yaml:",inline,omitempty"`
// Local overrides to global generatorOptions field.
Options *GeneratorOptions `json:"options,omitempty" yaml:"options,omitempty"`
// GeneratorOptions modify this generator
GeneratorOptions *GeneratorOptions `json:"generatorOptions,omitempty" yaml:"generatorOptions,omitempty"`
}

View File

@@ -16,55 +16,3 @@ 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
}

View File

@@ -1,125 +0,0 @@
// 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)
}
}
}

View File

@@ -10,23 +10,22 @@ type PluginConfig struct {
// containing the fields 'apiVersion' and 'kind', e.g.
// apiVersion: apps/v1
// kind: Deployment
// 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
// 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
// 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.
// The value of AbsPluginHome can be any absolute path, but might
// default to $XDG_CONFIG_HOME/kustomize/plugin.
AbsPluginHome string
// PluginRestrictions distinguishes plugin restrictions.
// PluginRestrictions defines the plugin restriction state.
// See type for more information.
PluginRestrictions PluginRestrictions
// BpLoadingOptions distinguishes builtin plugin behaviors.
BpLoadingOptions BuiltinPluginLoadingOptions
}

View File

@@ -26,18 +26,3 @@ 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
)

View File

@@ -19,7 +19,7 @@ linters:
# - gochecknoinits
# - goconst
# - gocritic
# - gocyclo
- gocyclo
- gofmt
- goimports
# - golint

View File

@@ -32,7 +32,7 @@ lint:
$(GOBIN)/golangci-lint run ./...
test:
go test -v -timeout 45m -cover ./...
go test -cover ./...
vet:
go vet ./...

View File

@@ -77,7 +77,8 @@ An example using `v1/List` as input:
apiVersion: v1
kind: List
items:
- apiVersion: foo-corp.com/v1
spec:
- apiVersion: foo-corp.com/v1
kind: FulfillmentCenter
metadata:
name: staging

View File

@@ -4,14 +4,11 @@ go 1.13
require (
github.com/go-errors/errors v1.0.1
github.com/go-openapi/spec v0.19.5
github.com/olekukonko/tablewriter v0.0.4
github.com/posener/complete/v2 v2.0.1-alpha.12
github.com/spf13/cobra v1.0.0
github.com/spf13/cobra v0.0.5
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.7
sigs.k8s.io/kustomize/kyaml v0.1.4
)
replace sigs.k8s.io/kustomize/kyaml v0.1.7 => ../../kyaml

View File

@@ -1,8 +1,6 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/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=
@@ -10,29 +8,19 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/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=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
@@ -40,12 +28,8 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
@@ -61,31 +45,18 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
@@ -95,15 +66,10 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -116,7 +82,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -126,9 +91,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
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/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -138,7 +101,6 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -147,86 +109,50 @@ github.com/posener/complete/v2 v2.0.1-alpha.12 h1:0wvkuDfHb5vSZlNBYgpEH4XQHpF46M
github.com/posener/complete/v2 v2.0.1-alpha.12/go.mod h1://JlL91cS2JV7rOl6LVHrRqBXoBUecJu3ILQPgbJiMQ=
github.com/posener/script v1.0.4 h1:nSuXW5ZdmFnQIueLB2s0qvs4oNsUloM1Zydzh75v42w=
github.com/posener/script v1.0.4/go.mod h1:Rg3ijooqulo05aGLyGsHoLmIOUzHUVK19WVgrYBPU/E=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
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 h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
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 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=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -234,18 +160,9 @@ golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fq
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
@@ -253,9 +170,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
@@ -264,12 +179,13 @@ gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/apimachinery v0.17.0 h1:xRBnuie9rXcPxUkDizUsGvPf1cnlZCFu210op7J7LJo=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
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.4 h1:cDG2u7v6CTAZmWKzCjk0hKG7AIN+2mCHx2ifwPbvKrs=
sigs.k8s.io/kustomize/kyaml v0.1.4/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=

View File

@@ -4,15 +4,11 @@
package commands
import (
"github.com/go-openapi/spec"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters"
"sigs.k8s.io/kustomize/kyaml/setters2"
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
)
@@ -90,30 +86,12 @@ func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
if setterVersion == "v2" {
var err error
r.OpenAPIFile, err = ext.GetOpenAPIFile(args)
if err != nil {
return err
}
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
return err
}
// check if substitution with same name exists and throw error
ref, err := spec.NewRef(setters2.DefinitionsPrefix + setters2.SubstitutionDefinitionPrefix + r.CreateSetter.Name)
if err != nil {
return err
}
subst, _ := openapi.Resolve(&ref)
// if substitution already exists with the input setter name, throw error
if subst != nil {
return errors.Errorf("substitution with name %s already exists, "+
"substitution and setter can't have same name", r.CreateSetter.Name)
}
r.CreateSetter.Description = r.Set.SetPartialField.Description
r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy
r.CreateSetter.Type = r.Set.SetPartialField.Type
if err != nil {
return err
}
}
return nil
}

View File

@@ -22,10 +22,8 @@ func TestCreateSetterCommand(t *testing.T) {
input string
args []string
out string
inputOpenAPI string
expectedOpenAPI string
expectedResources string
err string
}{
{
name: "add replicas",
@@ -38,10 +36,6 @@ metadata:
spec:
replicas: 3
`,
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
`,
expectedOpenAPI: `
apiVersion: v1alpha1
kind: Example
@@ -64,27 +58,6 @@ spec:
replicas: 3 # {"$ref":"#/definitions/io.k8s.cli.setters.replicas"}
`,
},
{
name: "error if substitution with same name exists",
args: []string{"my-image", "3", "--description", "hello world", "--set-by", "me"},
inputOpenAPI: `
apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.substitutions.my-image:
x-k8s-cli:
substitution:
name: my-image
pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing
values:
- marker: ${my-image-setter}
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
- marker: ${my-tag-setter}
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
`,
err: "substitution with name my-image already exists, substitution and setter can't have same name",
},
}
for i := range tests {
test := tests[i]
@@ -98,7 +71,10 @@ openAPI:
t.FailNow()
}
defer os.Remove(f.Name())
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
err = ioutil.WriteFile(f.Name(), []byte(`
apiVersion: v1alpha1
kind: Example
`), 0600)
if !assert.NoError(t, err) {
t.FailNow()
}
@@ -123,14 +99,6 @@ openAPI:
runner.Command.SetOut(out)
runner.Command.SetArgs(append([]string{r.Name()}, test.args...))
err = runner.Command.Execute()
if test.err != "" {
if !assert.NotNil(t, err) {
t.FailNow()
} else {
assert.Equal(t, err.Error(), test.err)
return
}
}
if !assert.NoError(t, err) {
t.FailNow()
}

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