Compare commits

...

109 Commits

Author SHA1 Message Date
Jeff Regan
9d992aae68 Merge pull request #3015 from monopole/pinToCmdConfigV0.8.1
Pin to cmd/config/v0.8.1
2020-09-19 07:42:05 -07:00
Jeffrey Regan
8c906b804f Pin to cmd/config/v0.8.1 2020-09-19 07:12:13 -07:00
Jeff Regan
4ff4940fa7 Update README.md 2020-09-18 18:28:42 -07:00
Jeff Regan
09aec5694a Merge pull request #3013 from monopole/generated
Generated pin to cli-utils v0.20.2 hash
2020-09-18 18:26:25 -07:00
Jeffrey Regan
1f917c0499 Generated pin to cli-utils v0.20.2 hash 2020-09-18 17:52:33 -07:00
Kubernetes Prow Robot
225ffc7cd8 Merge pull request #3012 from Shell32-Natsu/remove-network-name
remove --network-name flag from kpt fn run
2020-09-18 16:48:27 -07:00
Kubernetes Prow Robot
eb638cc312 Merge pull request #3009 from Shell32-Natsu/endpoint-service
put endpoints before service
2020-09-18 14:20:28 -07:00
Jeff Regan
7dfb96425e Merge pull request #3010 from monopole/pinToCliUtils_v0.20.2
Pin to cli-utils/v0.20.2
2020-09-18 13:35:02 -07:00
Jeff Regan
6d4c6127c8 Update README.md 2020-09-18 13:18:54 -07:00
Jeff Regan
6aa72b66ef Update README.md 2020-09-18 13:15:52 -07:00
Jeff Regan
f03fdc09cb Update README.md 2020-09-18 13:10:19 -07:00
Jeffrey Regan
30b6eeb460 Pin to cli-utils/v0.20.2 2020-09-18 13:08:53 -07:00
Jeff Regan
bf67fcb6d6 Update README.md 2020-09-18 12:55:59 -07:00
Jeff Regan
4ae420cce1 Merge pull request #3008 from monopole/pinToKyamlv_0_8_1
Pin to kyaml v0.8.1
2020-09-18 12:40:02 -07:00
Donny Xia
87d2187436 remove --network-name flag from kpt fn run 2020-09-18 12:39:49 -07:00
Donny Xia
f1dabbd4fc put endpoints before service 2020-09-18 12:21:03 -07:00
Jeffrey Regan
747e05f2a4 Pin to kyaml v0.8.1 2020-09-18 12:11:03 -07:00
Jeff Regan
3514317b3d Merge pull request #3007 from monopole/unpin
Unpin the dependencies post release.
2020-09-18 11:19:42 -07:00
Jeff Regan
9299821571 Merge pull request #2998 from Shell32-Natsu/network
Change network to a boolean in kpt fn run
2020-09-18 11:12:26 -07:00
Jeffrey Regan
d91f313137 Unpin the dependencies post release. 2020-09-18 09:51:05 -07:00
Jeff Regan
161af9d99c Merge pull request #3002 from monopole/pinToApiV0_6_1
Pin to api/v0.6.1
2020-09-17 17:18:11 -07:00
jregan
b115c95ea1 Pin to api/v0.6.1 2020-09-17 16:27:08 -07:00
Jeff Regan
20cd4bfef9 Merge pull request #2997 from kubernetes-sigs/removeReplaceDir
Update go.mod
2020-09-17 11:02:08 -07:00
Donny Xia
f6c06b58ef Change network to a boolean 2020-09-16 16:20:50 -07:00
Jeff Regan
c45e05b7bd Update go.mod 2020-09-16 12:53:39 -07:00
Kubernetes Prow Robot
76bae738a0 Merge pull request #2932 from mstrYoda/master
add DisableNameSuffixHash parameter to edit Secret & ConfigMap
2020-09-16 10:29:20 -07:00
Jeff Regan
0770661b2a Merge pull request #2991 from monopole/goSumUpdates
Go sum updates
2020-09-15 17:34:23 -07:00
jregan
67d5871e87 Go sum updates 2020-09-15 17:02:40 -07:00
Jeff Regan
f98c683915 Merge pull request #2983 from Shell32-Natsu/allow-null
ignore null value in fieldspec
2020-09-15 17:02:20 -07:00
Jeff Regan
ffe9c9d947 Merge pull request #2990 from monopole/pinToKyamlv0_8_0
Pin to kyaml v0.8.0
2020-09-15 15:51:08 -07:00
jregan
4d42ffc7f8 Pin to kyaml v0.8.0 2020-09-15 15:26:29 -07:00
Jeff Regan
d7dc7d911e Merge pull request #2989 from monopole/minorFix
Generated changes.
2020-09-15 14:56:16 -07:00
jregan
04404ff61b Generated changes. 2020-09-15 14:55:08 -07:00
Jeff Regan
f864e15c68 Merge pull request #2974 from Shell32-Natsu/function-definition
remove not used args & use const string instead of literal
2020-09-15 14:34:25 -07:00
Jeff Regan
29a444fffc Merge pull request #2988 from Shell32-Natsu/env-flag
Add --env/-e flag to fn run
2020-09-15 14:31:40 -07:00
Donny Xia
327035a43a Add --env/-e flag 2020-09-15 11:49:26 -07:00
Kubernetes Prow Robot
ad7fed061e Merge pull request #2984 from phanimarupaka/FixCmdCfgIssues
Do not print package info in grep
2020-09-15 09:40:07 -07:00
Phani Teja Marupaka
cea2986574 Don not pring package info in grep 2020-09-15 00:15:52 -07:00
Kubernetes Prow Robot
00f0fd7109 Merge pull request #2978 from mortent/NamespaceabilityFromSchema
Determine namespaceability of resources from openapi schema
2020-09-14 21:10:06 -07:00
Kubernetes Prow Robot
1c6481d011 Merge pull request #2982 from mortent/UpdateCliUtils
Update cmd/config to use latest version of cli-utils
2020-09-14 13:27:24 -07:00
Donny Xia
f0bc926640 ignore null value in fieldspec 2020-09-14 13:20:20 -07:00
Morten Torkildsen
11d9ff5690 Update cmd/config to use latest version of cli-utils 2020-09-14 12:08:48 -07:00
Jeff Regan
6a0a909e73 Merge pull request #2976 from monopole/deferLive
Remove live; done testing.  Bring to sig-cli for discussion.
2020-09-14 11:00:48 -07:00
Kubernetes Prow Robot
bd8f0c88e5 Merge pull request #2967 from phanimarupaka/CfgTreeWithSubpackages
Print Krmfile data for cfg tree
2020-09-14 10:42:59 -07:00
Donny Xia
e5809e49cb remove not used args 2020-09-14 10:10:20 -07:00
Phani Teja Marupaka
880009b648 Print Krmfile data for cfg tree 2020-09-14 09:57:18 -07:00
Morten Torkildsen
d083c7f1d0 Determine namespaceability of resources from openapi schema 2020-09-12 15:01:42 -07:00
Jeffrey Regan
684ce141de Defer live testing. 2020-09-11 17:25:25 -07:00
Kubernetes Prow Robot
5c8c7a043a Merge pull request #2973 from phanimarupaka/CfgCommandsStdoutFixes
Format the output of cfg commands
2020-09-11 16:44:57 -07:00
Kubernetes Prow Robot
0fe7f65ef2 Merge pull request #2975 from monopole/addTest
Add test representing 2960
2020-09-11 16:10:57 -07:00
Jeffrey Regan
950c1de46d Add test representing 2960 2020-09-11 15:22:41 -07:00
Phani Teja Marupaka
fc690f14a8 Format the output of cfg commands 2020-09-11 14:49:18 -07:00
Jeff Regan
a6e03e4d11 Merge pull request #2920 from wyyxd2017/hato4
Technical details structure picture
2020-09-11 14:32:19 -07:00
Jeff Regan
60428be5fb Merge pull request #2944 from justinsb/alas_poor_grepfilter
Remove some obsolete references to GrepFilter
2020-09-11 14:16:30 -07:00
Jeff Regan
4d402d4875 Merge pull request #2969 from justinsb/fix_split_docs
Fix comment on SplitIndexNameValue
2020-09-11 14:13:43 -07:00
Jeff Regan
fbddd264be Merge pull request #2971 from etefera/fix-broken-patches-change
Fix unintended patches change in all Kustomization writes.
2020-09-11 13:33:09 -07:00
Eyob Tefera
d3c46d3f7c Move offending Write() tests to tests for fix. 2020-09-11 19:19:54 +00:00
Eyob Tefera
dda3984a8f Move kustomization fixing to before Write step. 2020-09-11 18:57:14 +00:00
Eyob Tefera
f889ca8885 Add set image test with patchesJson6902. 2020-09-11 18:48:48 +00:00
Justin SB
341bacb9a2 Fix comment on SplitIndexNameValue
It was incorrect and suggested some behaviour which isn't present.
Added test to verify the documented behaviour.
2020-09-11 11:39:49 -04:00
Kubernetes Prow Robot
badc1177d9 Merge pull request #2939 from ZhuGongpu/master
Add --log-steps flag
2020-09-09 19:05:44 -07:00
Kubernetes Prow Robot
1680cc72c0 Merge pull request #2953 from easimon/add_namespace_to_namespace_transformer
Add namespace name to namespace transformer defaults
2020-09-09 14:57:44 -07:00
Jeff Regan
2f89de86f8 Merge pull request #2956 from justinsb/fix_language
Replace language with more inclusive & accurate "hard-coded"
2020-09-09 11:33:53 -07:00
Jeff Regan
ab4e9c718b Merge pull request #2943 from Shell32-Natsu/clean-up-temp-dir
remove all temp dir
2020-09-09 11:20:17 -07:00
Kubernetes Prow Robot
288c03ddca Merge pull request #2959 from phanimarupaka/CatWithSubpkgs
Cat with subpackages
2020-09-09 10:49:08 -07:00
Justin SB
5c60285f25 Replace language with more inclusive & accurate "hard-coded"
hard-coded is probably more helpful to understanding the limitations
of the current approach.
2020-09-09 08:41:33 -04:00
Kubernetes Prow Robot
6df0a45368 Merge pull request #2958 from phanimarupaka/GrepWithSubpkgs
Grep with subpackages
2020-09-08 21:09:52 -07:00
Kubernetes Prow Robot
8206987580 Merge pull request #2957 from phanimarupaka/CountWithSubpkgs
Count with Subpackages
2020-09-08 20:53:52 -07:00
Kubernetes Prow Robot
6189ca9798 Merge pull request #2955 from phanimarupaka/FmtWithSubpkgs
Fmt with subpackages
2020-09-08 20:33:52 -07:00
Phani Teja Marupaka
b8c1601a93 Cat with subpackages 2020-09-08 18:42:31 -07:00
Phani Teja Marupaka
34d610a38d Fmt with subpackages 2020-09-08 17:07:55 -07:00
Phani Teja Marupaka
8e4c8464e7 Grep with subpackages 2020-09-08 16:59:23 -07:00
Phani Teja Marupaka
43ab7a8e71 Count with Subpackages 2020-09-08 16:33:00 -07:00
Kubernetes Prow Robot
d2f23a4b8b Merge pull request #2938 from phanimarupaka/OtherCfgCommandsWithSubPkgs
annotate, delete-setter, delete-subst With Subpackages
2020-09-08 16:25:52 -07:00
Phani Teja Marupaka
0dc36a4f7c Annotate With Subpackages
Delete Setters And Subst With Subpkgs
2020-09-08 15:52:57 -07:00
Kubernetes Prow Robot
678ae12115 Merge pull request #2946 from phanimarupaka/RefactorSubPkgsIteration
Refactor subpackages logic
2020-09-08 11:47:52 -07:00
Phani Teja Marupaka
c4d937322f Refactor subpackages logic 2020-09-08 11:23:54 -07:00
Kubernetes Prow Robot
a2adb835b6 Merge pull request #2941 from mortent/FixIgnoreFilesMatcher
Fix issue where ignoreFilesMatcher doesn't work correctly
2020-09-08 10:33:44 -07:00
Markus Dobel
01b5c4e9da Add namespace name to namespace transformer defaults 2020-09-08 11:24:58 +02:00
Justin SB
eb4c5dc035 Remove some obsolete references to GrepFilter
Looks like GrepFilter was generalized to Filter.
2020-09-04 13:41:16 -04:00
Morten Torkildsen
e976386931 Fix issue where ignoreFilesMatcher doesn't work correctly 2020-09-03 20:14:07 -07:00
Donny Xia
bae9986422 remove all temp dir 2020-09-03 12:03:12 -07:00
Jeff Regan
e7970d82a8 Merge pull request #2942 from Shell32-Natsu/addDonny
add Donny to maintainer
2020-09-03 11:52:14 -07:00
Donny Xia
9bdd489c96 add Donny to maintainer 2020-09-03 10:57:50 -07:00
Kubernetes Prow Robot
0f49fef5ed Merge pull request #2940 from phanimarupaka/FixSettersSubPkgsFriction
Fix setters subpkgs friction
2020-09-03 08:59:41 -07:00
Phani Teja Marupaka
8d74b8c3b5 Fix setters subpkgs friction 2020-09-02 23:06:26 -07:00
Gongpu Zhu
39a8798a87 Add --log-steps flag 2020-09-02 20:47:21 -07:00
Jeff Regan
980f407552 Merge pull request #2931 from Shell32-Natsu/image-tag-legacy
add legacy filter to image tag transformer
2020-09-02 13:02:53 -07:00
Donny Xia
9ca8f4602d add legacy filter to image tag transformer 2020-09-01 13:06:14 -07:00
Kubernetes Prow Robot
ba0f583ee5 Merge pull request #2911 from phanimarupaka/SetWithSubPackages
Setters/substitutions with subpackages
2020-09-01 11:23:51 -07:00
Phani Teja Marupaka
f432f4d75e Setters with subpackages 2020-09-01 10:54:08 -07:00
Jeff Regan
17793abacd Merge pull request #2930 from daniel-hutao/patch-1
fix “chart s” to “charts ”
2020-09-01 10:12:58 -07:00
Jeff Regan
64cd4ec1d5 Merge pull request #2929 from monopole/testExamplesAgainst_v3.8.2
Test examples against v3.8.2
2020-09-01 10:12:18 -07:00
Daniel (ht)
fb822984e3 fix “chart s” to “charts ”
fix “chart s” to “charts ”
2020-09-01 16:24:49 +08:00
jregan
6d2a737c29 Test examples against v3.8.2 2020-08-31 16:48:19 -07:00
Jeff Regan
6e7713281e Merge pull request #2928 from monopole/addGorepomod
Add more release instructions
2020-08-31 13:50:48 -07:00
jregan
6a7bb9e33e Add gorepomod tool to install list 2020-08-31 13:28:19 -07:00
Jeff Regan
0e9428c8b0 Merge pull request #2927 from monopole/unpinning
Unpinning after release releases
2020-08-31 12:47:28 -07:00
jregan
c838962432 fix module hashes 2020-08-31 12:08:16 -07:00
jregan
548d10ef08 unpin kyaml 2020-08-31 12:08:16 -07:00
jregan
2db8487f02 unpin cmd/config 2020-08-31 12:08:16 -07:00
jregan
b42f71a20f unpin api 2020-08-31 12:08:16 -07:00
Jeff Regan
e9824aa749 Merge pull request #2926 from monopole/fixAGoMod
Repair a go module spec.
2020-08-31 12:08:06 -07:00
jregan
92cc9fc5e1 Repair a go module spec. 2020-08-31 11:35:32 -07:00
wangyeyu
e53b4c9884 Technical details structure picture 2020-08-31 10:01:19 +08:00
Jeff Regan
e2973f6ecc Merge pull request #2915 from monopole/pinToKustomizeApiv_0_6_0
Pin to kustomize api v0.6.0
2020-08-29 09:42:26 -07:00
jregan
2bf9fc816d Pin to kustomize api v0.6.0 2020-08-29 08:55:29 -07:00
mstrYoda
257707d839 Merge branch 'master' of https://github.com/kubernetes-sigs/kustomize 2020-08-26 13:24:04 +03:00
mstrYoda
c1cd872df6 add DisableNameSuffixHash for secret and configmap 2020-08-26 13:22:26 +03:00
161 changed files with 71606 additions and 5764 deletions

View File

@@ -15,7 +15,8 @@ verify-kustomize: \
lint-kustomize \ lint-kustomize \
test-unit-kustomize-all \ test-unit-kustomize-all \
test-examples-kustomize-against-HEAD \ test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-3.8.0 test-examples-kustomize-against-3.8.0 \
test-examples-kustomize-against-3.8.2
# The following target referenced by a file in # The following target referenced by a file in
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize # https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
@@ -26,7 +27,8 @@ prow-presubmit-check: \
test-unit-cmd-all \ test-unit-cmd-all \
test-go-mod \ test-go-mod \
test-examples-kustomize-against-HEAD \ test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-3.8.0 test-examples-kustomize-against-3.8.0 \
test-examples-kustomize-against-3.8.2
.PHONY: verify-kustomize-e2e .PHONY: verify-kustomize-e2e
verify-kustomize-e2e: test-examples-e2e-kustomize verify-kustomize-e2e: test-examples-e2e-kustomize
@@ -45,17 +47,18 @@ $(MYGOBIN)/golangci-lint-kustomize:
GO111MODULE=on go build -tags=tools -o $(MYGOBIN)/golangci-lint-kustomize github.com/golangci/golangci-lint/cmd/golangci-lint; \ GO111MODULE=on go build -tags=tools -o $(MYGOBIN)/golangci-lint-kustomize github.com/golangci/golangci-lint/cmd/golangci-lint; \
) )
# Version pinned by api/go.mod $(MYGOBIN)/gorepomod:
cd api; \
go install github.com/monopole/gorepomod
$(MYGOBIN)/mdrip: $(MYGOBIN)/mdrip:
cd api; \ cd api; \
go install github.com/monopole/mdrip go install github.com/monopole/mdrip
# Version pinned by api/go.mod
$(MYGOBIN)/stringer: $(MYGOBIN)/stringer:
cd api; \ cd api; \
go install golang.org/x/tools/cmd/stringer go install golang.org/x/tools/cmd/stringer
# Version pinned by api/go.mod
$(MYGOBIN)/goimports: $(MYGOBIN)/goimports:
cd api; \ cd api; \
go install golang.org/x/tools/cmd/goimports go install golang.org/x/tools/cmd/goimports
@@ -81,6 +84,7 @@ $(MYGOBIN)/kustomize:
install-tools: \ install-tools: \
$(MYGOBIN)/goimports \ $(MYGOBIN)/goimports \
$(MYGOBIN)/golangci-lint-kustomize \ $(MYGOBIN)/golangci-lint-kustomize \
$(MYGOBIN)/gorepomod \
$(MYGOBIN)/mdrip \ $(MYGOBIN)/mdrip \
$(MYGOBIN)/pluginator \ $(MYGOBIN)/pluginator \
$(MYGOBIN)/stringer $(MYGOBIN)/stringer
@@ -245,6 +249,19 @@ test-examples-kustomize-against-3.8.0: $(MYGOBIN)/mdrip
cd kustomize; go install .; \ cd kustomize; go install .; \
) )
.PHONY:
test-examples-kustomize-against-3.8.2: $(MYGOBIN)/mdrip
( \
set -e; \
tag=v3.8.2; \
/bin/rm -f $(MYGOBIN)/kustomize; \
echo "Installing kustomize $$tag."; \
GO111MODULE=on go get sigs.k8s.io/kustomize/kustomize/v3@$${tag}; \
./hack/testExamplesAgainstKustomize.sh $$tag; \
echo "Reinstalling kustomize from HEAD."; \
cd kustomize; go install .; \
)
# linux only. # linux only.
# This is for testing an example plugin that # This is for testing an example plugin that
# uses kubeval for validation. # uses kubeval for validation.

View File

@@ -11,3 +11,4 @@ aliases:
- pwittrock - pwittrock
- mortent - mortent
- phanimarupaka - phanimarupaka
- Shell32-Natsu

View File

@@ -31,14 +31,15 @@ func (p *ImageTagTransformerPlugin) Config(
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error { func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() { for _, r := range m.Resources() {
// If you're here because someone expected any field containing // traverse all fields at first
// the string "containers" or "initContainers" to get an image err := filtersutil.ApplyToJSON(imagetag.LegacyFilter{
// update (not just spec/template/spec/containers[], etc.) then ImageTag: p.ImageTag,
// a code change is needed. See api/filters/imagetag/legacy }, r)
// for the start of an implementation that won't use an if err != nil {
// allowlist like FsSlice, and instead walks the object looking return err
// for fields named containers or initContainers. }
err := filtersutil.ApplyToJSON(imagetag.Filter{ // then use user specified field specs
err = filtersutil.ApplyToJSON(imagetag.Filter{
ImageTag: p.ImageTag, ImageTag: p.ImageTag,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,
}, r) }, r)

View File

@@ -51,6 +51,9 @@ func (fltr Filter) filter(obj *yaml.RNode) error {
// found the field -- set its value // found the field -- set its value
return fltr.SetValue(obj) return fltr.SetValue(obj)
} }
if obj.IsTaggedNull() {
return nil
}
switch obj.YNode().Kind { switch obj.YNode().Kind {
case yaml.SequenceNode: case yaml.SequenceNode:
return fltr.seq(obj) return fltr.seq(obj)
@@ -67,7 +70,7 @@ func (fltr Filter) field(obj *yaml.RNode) error {
// lookup the field matching the next path element // lookup the field matching the next path element
var lookupField yaml.Filter var lookupField yaml.Filter
var kind yaml.Kind var kind yaml.Kind
tag := "" // TODO: change to yaml.NodeTagEmpty tag := yaml.NodeTagEmpty
switch { switch {
case !fltr.FieldSpec.CreateIfNotPresent || fltr.CreateKind == 0 || isSeq: case !fltr.FieldSpec.CreateIfNotPresent || fltr.CreateKind == 0 || isSeq:
// dont' create the field if we don't find it // dont' create the field if we don't find it
@@ -95,9 +98,10 @@ func (fltr Filter) field(obj *yaml.RNode) error {
return errors.WrapPrefixf(err, "fieldName: %s", fieldName) return errors.WrapPrefixf(err, "fieldName: %s", fieldName)
} }
// if the value exists, but is null, then change it to the creation type // if the value exists, but is null and kind is set,
// then change it to the creation type
// TODO: update yaml.LookupCreate to support this // TODO: update yaml.LookupCreate to support this
if field.YNode().Tag == yaml.NodeTagNull { if field.YNode().Tag == yaml.NodeTagNull && yaml.IsCreate(kind) {
field.YNode().Kind = kind field.YNode().Kind = kind
field.YNode().Tag = tag field.YNode().Tag = tag
} }

View File

@@ -433,7 +433,6 @@ spec:
SetValue: filtersutil.SetScalar("bar"), SetValue: filtersutil.SetScalar("bar"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
}, },
error: "obj '' at path 'spec/containers/image': expected sequence or mapping node",
}, },
{ {
name: "filedname with slash '/'", name: "filedname with slash '/'",

View File

@@ -168,6 +168,44 @@ metadata:
map: map:
name: newName name: newName
namespace: oldNs namespace: oldNs
`,
filter: Filter{
FieldSpec: types.FieldSpec{Path: "map"},
Target: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
},
},
},
"null value": {
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep
map:
name: null
`,
candidates: `
apiVersion: apps/v1
kind: Secret
metadata:
name: newName
---
apiVersion: apps/v1
kind: NotSecret
metadata:
name: newName2
`,
originalNames: []string{"oldName", ""},
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep
map:
name: null
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "map"}, FieldSpec: types.FieldSpec{Path: "map"},
@@ -217,27 +255,6 @@ func TestNamerefFilterUnhappy(t *testing.T) {
filter Filter filter Filter
originalNames []string originalNames []string
}{ }{
"invalid node type": {
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep
ref:
name: null
`,
candidates: "",
originalNames: []string{},
expected: "obj '' at path 'ref/name': node is expected to be either a string or a slice of string or a map of string",
filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{
Group: "apps",
Version: "v1",
Kind: "Secret",
},
},
},
"multiple match": { "multiple match": {
input: ` input: `
apiVersion: apps/v1 apiVersion: apps/v1

View File

@@ -69,7 +69,7 @@ func (ns Filter) hacks(obj *yaml.RNode) error {
// metaNamespaceHack is a hack for implementing the namespace transform // metaNamespaceHack is a hack for implementing the namespace transform
// for the metadata.namespace field on namespace scoped resources. // for the metadata.namespace field on namespace scoped resources.
// namespace scoped resources are determined by NOT being present // namespace scoped resources are determined by NOT being present
// in a blacklist of cluster-scoped resource types (by apiVersion and kind). // in a hard-coded list of cluster-scoped resource types (by apiVersion and kind).
// //
// This hack should be updated to allow individual resources to specify // This hack should be updated to allow individual resources to specify
// if they are cluster scoped through either an annotation on the resources, // if they are cluster scoped through either an annotation on the resources,

View File

@@ -188,6 +188,26 @@ data:
FieldSpec: types.FieldSpec{Path: "data/slice2"}, FieldSpec: types.FieldSpec{Path: "data/slice2"},
}, },
}, },
"null value": {
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep
data:
FOO: null`,
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep
data:
FOO: null`,
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{}),
FieldSpec: types.FieldSpec{Path: "data/FOO"},
},
},
} }
for tn, tc := range testCases { for tn, tc := range testCases {
@@ -260,20 +280,6 @@ data:
FieldSpec: types.FieldSpec{Path: "data"}, FieldSpec: types.FieldSpec{Path: "data"},
}, },
}, },
"null input": {
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep
data:
FOO: null`,
expectedError: "obj '' at path 'data/FOO': invalid type encountered 0",
filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{}),
FieldSpec: types.FieldSpec{Path: "data/FOO"},
},
},
} }
for tn, tc := range testCases { for tn, tc := range testCases {

View File

@@ -10,13 +10,13 @@ require (
github.com/pkg/errors v0.8.1 github.com/pkg/errors v0.8.1
github.com/stretchr/testify v1.4.0 github.com/stretchr/testify v1.4.0
github.com/yujunz/go-getter v1.4.1-lite github.com/yujunz/go-getter v1.4.1-lite
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e
gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71 gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71
k8s.io/api v0.17.0 k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0 k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0 k8s.io/client-go v0.17.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
sigs.k8s.io/kustomize/kyaml v0.7.1 sigs.k8s.io/kustomize/kyaml v0.8.1
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@@ -307,6 +307,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -526,6 +528,8 @@ golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
@@ -584,8 +588,8 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphD
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/kyaml v0.7.1 h1:Ih6SJPvfKYfZaIFWUa2YAyg/0ZSTpA3LFjR/hv7+8ao= sigs.k8s.io/kustomize/kyaml v0.8.1 h1:5GRanVGU6+iq3ERTiQD9VIfyGByFVB4z4GthP8NkRYE=
sigs.k8s.io/kustomize/kyaml v0.7.1/go.mod h1:ne3F9JPhW2wrVaLslxBsEe6MQJQ9YK5rUutrdhBWXwI= sigs.k8s.io/kustomize/kyaml v0.8.1/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=

View File

@@ -132,7 +132,7 @@ func TestRefVarTransformer(t *testing.T) {
' at path 'data/slice': invalid value type expect a string`, ' at path 'data/slice': invalid value type expect a string`,
}, },
{ {
description: "var replacement panic in nil", description: "var replacement in nil",
given: given{ given: given{
varMap: map[string]interface{}{}, varMap: map[string]interface{}{},
fs: []types.FieldSpec{ fs: []types.FieldSpec{
@@ -150,7 +150,19 @@ func TestRefVarTransformer(t *testing.T) {
"nil": nil, // noticeably *not* a []string "nil": nil, // noticeably *not* a []string
}}).ResMap(), }}).ResMap(),
}, },
errMessage: `obj '' at path 'data/nil': invalid type encountered 0`, expected: expected{
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{}{
"nil": nil, // noticeably *not* a []string
}}).ResMap(),
},
}, },
} }

View File

@@ -217,7 +217,7 @@ overview of each component with the following sections going into more details.
The overall structure is outlined in the following figure: The overall structure is outlined in the following figure:
![overview]( ![overview](
https://sigs.k8s.io/kustomize/internal/tools/pictures/sys_arch.png) https://github.com/kubernetes-sigs/kustomize/blob/master/api/internal/crawl/pictures/token_config.png)
#### Crawler #### Crawler
The leftmost component consists of a crawler with an http cache of GitHub The leftmost component consists of a crawler with an http cache of GitHub

View File

@@ -271,6 +271,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -470,6 +472,7 @@ golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDq
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
@@ -524,8 +527,8 @@ k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/kyaml v0.7.1 h1:Ih6SJPvfKYfZaIFWUa2YAyg/0ZSTpA3LFjR/hv7+8ao= sigs.k8s.io/kustomize/kyaml v0.8.0 h1:/MqPML99XAm2pbrD/eTpePh5rnU5bpnuTPqb29LpSz4=
sigs.k8s.io/kustomize/kyaml v0.7.1/go.mod h1:ne3F9JPhW2wrVaLslxBsEe6MQJQ9YK5rUutrdhBWXwI= sigs.k8s.io/kustomize/kyaml v0.8.0/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -65,8 +65,8 @@ func TestLoader(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
for _, behavior := range []types.BuiltinPluginLoadingOptions{ for _, behavior := range []types.BuiltinPluginLoadingOptions{
types.BploUseStaticallyLinked, /* types.BploUseStaticallyLinked,
types.BploLoadFromFileSys} { types.BploLoadFromFileSys */} {
c, err := konfig.EnabledPluginConfig(behavior) c, err := konfig.EnabledPluginConfig(behavior)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@@ -8,6 +8,9 @@ const (
namespace: namespace:
- path: metadata/namespace - path: metadata/namespace
create: true create: true
- path: metadata/name
kind: Namespace
create: true
- path: subjects - path: subjects
kind: RoleBinding kind: RoleBinding
- path: subjects - path: subjects

View File

@@ -9,7 +9,7 @@ import (
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
) )
func TestNullValues(t *testing.T) { func TestNullValues1(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
th.WriteF("/app/deployment.yaml", ` th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1
@@ -62,3 +62,36 @@ spec:
name: example name: example
`) `)
} }
func TestNullValues2(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("deploy.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
spec:
template:
spec:
containers:
- name: test
volumes: null
`)
th.WriteK(".", `
resources:
- deploy.yaml
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
spec:
template:
spec:
containers:
- name: test
volumes: null
`)
}

View File

@@ -215,25 +215,25 @@ spec2:
template: template:
spec: spec:
containers: containers:
- image: nginx:v1 - image: nginx:v2
name: nginx3 name: nginx3
- image: my-nginx:latest - image: my-nginx:previous
name: nginx4 name: nginx4
spec3: spec3:
template: template:
spec: spec:
initContainers: initContainers:
- image: postgres:alpine-9 - image: my-postgres:v3
name: postgresdb name: postgresdb
- image: docker:17-git - image: my-docker@sha256:25a0d4b4
name: init-docker name: init-docker
- image: myprivaterepohostname:1234/my/image:latest - image: myprivaterepohostname:1234/my/image:v1.0.1
name: myImage name: myImage
- image: myprivaterepohostname:1234/my/image - image: myprivaterepohostname:1234/my/image:v1.0.1
name: myImage2 name: myImage2
- image: my-app-image:v1 - image: my-app-image:v1
name: my-app name: my-app
- image: gcr.io:8080/my-project/my-cool-app:latest - image: my-cool-app:latest
name: my-cool-app name: my-cool-app
`) `)
} }

View File

@@ -20,6 +20,9 @@ type remoteTargetSpec struct {
// Dir is where the resource is saved // Dir is where the resource is saved
Dir filesys.ConfirmedDir Dir filesys.ConfirmedDir
// TempDir is the directory created to hold all resources, including Dir
TempDir filesys.ConfirmedDir
} }
// Getter is a function that can gets resource // Getter is a function that can gets resource
@@ -31,7 +34,7 @@ func newLoaderAtGetter(raw string, fSys filesys.FileSystem, referrer *fileLoader
} }
cleaner := func() error { cleaner := func() error {
return fSys.RemoveAll(rs.Dir.String()) return fSys.RemoveAll(rs.TempDir.String())
} }
if err := getter(rs); err != nil { if err := getter(rs); err != nil {
@@ -55,12 +58,12 @@ func newLoaderAtGetter(raw string, fSys filesys.FileSystem, referrer *fileLoader
func getRemoteTarget(rs *remoteTargetSpec) error { func getRemoteTarget(rs *remoteTargetSpec) error {
var err error var err error
rs.Dir, err = filesys.NewTmpConfirmedDir() rs.TempDir, err = filesys.NewTmpConfirmedDir()
if err != nil { if err != nil {
return err return err
} }
rs.Dir = filesys.ConfirmedDir(rs.Dir.Join("repo")) rs.Dir = filesys.ConfirmedDir(rs.TempDir.Join("repo"))
// Get the pwd // Get the pwd
pwd, err := os.Getwd() pwd, err := os.Getwd()

View File

@@ -99,6 +99,7 @@ var orderFirst = []string{
"ClusterRoleBinding", "ClusterRoleBinding",
"ConfigMap", "ConfigMap",
"Secret", "Secret",
"Endpoints",
"Service", "Service",
"LimitRange", "LimitRange",
"PriorityClass", "PriorityClass",

View File

@@ -78,6 +78,8 @@ var lessThanTests = []struct {
Gvk{Group: "a", Version: "b", Kind: "ValidatingWebhookConfiguration"}}, Gvk{Group: "a", Version: "b", Kind: "ValidatingWebhookConfiguration"}},
{Gvk{Group: "a", Version: "b", Kind: "Service"}, {Gvk{Group: "a", Version: "b", Kind: "Service"},
Gvk{Group: "a", Version: "b", Kind: "APIService"}}, Gvk{Group: "a", Version: "b", Kind: "APIService"}},
{Gvk{Group: "a", Version: "b", Kind: "Endpoints"},
Gvk{Group: "a", Version: "b", Kind: "Service"}},
} }
func TestIsLessThan1(t *testing.T) { func TestIsLessThan1(t *testing.T) {

View File

@@ -39,7 +39,7 @@ var (
ExitOnError = &commands.ExitOnError ExitOnError = &commands.ExitOnError
) )
// AddCommands adds the cfg, fn and live commands to kustomize. // AddCommands adds the cfg and fn commands to kustomize.
func AddCommands(root *cobra.Command, name string) *cobra.Command { func AddCommands(root *cobra.Command, name string) *cobra.Command {
commands.ExitOnError = true commands.ExitOnError = true
@@ -48,7 +48,6 @@ func AddCommands(root *cobra.Command, name string) *cobra.Command {
root.AddCommand(GetCfg(name)) root.AddCommand(GetCfg(name))
root.AddCommand(GetFn(name)) root.AddCommand(GetFn(name))
root.AddCommand(GetLive(name))
root.AddCommand(&cobra.Command{ root.AddCommand(&cobra.Command{
Use: "docs-merge", Use: "docs-merge",

View File

@@ -53,7 +53,7 @@ func GetLive(name string) *cobra.Command {
cmd.AddCommand( cmd.AddCommand(
applyCmd, applyCmd,
initcmd.NewCmdInit(ioStreams), initcmd.NewCmdInit(f, ioStreams),
preview.GetPreviewRunner(f, ioStreams).Command, preview.GetPreviewRunner(f, ioStreams).Command,
diff.NewCmdDiff(f, ioStreams), diff.NewCmdDiff(f, ioStreams),
destroy.GetDestroyRunner(f, ioStreams).Command) destroy.GetDestroyRunner(f, ioStreams).Command)

View File

@@ -14,6 +14,6 @@ require (
k8s.io/cli-runtime v0.17.3 k8s.io/cli-runtime v0.17.3
k8s.io/client-go v0.17.3 k8s.io/client-go v0.17.3
k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd k8s.io/kubectl v0.0.0-20191219154910-1528d4eea6dd
sigs.k8s.io/cli-utils v0.19.2 sigs.k8s.io/cli-utils v0.20.2
sigs.k8s.io/kustomize/kyaml v0.7.1 sigs.k8s.io/kustomize/kyaml v0.8.1
) )

View File

@@ -283,6 +283,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -615,14 +617,14 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
sigs.k8s.io/cli-utils v0.19.2 h1:BwidWPZ3Qop4RBOl27MU8uN/BSEZPQ8rw1mwsNofXfw= sigs.k8s.io/cli-utils v0.20.2 h1:jNMu4ExtvFXlmKqZMDJqySqK55vGtVRqoLht6eIMffw=
sigs.k8s.io/cli-utils v0.19.2/go.mod h1:ulIQPERYwkYksNriRknJRbGECDR/ZZROpkiThlXBtB4= sigs.k8s.io/cli-utils v0.20.2/go.mod h1:uT5cVxMrOoRplL8umtrJx5R51ZWsIKD7lQfPtot80uA=
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg= sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/kustomize/kyaml v0.7.1 h1:Ih6SJPvfKYfZaIFWUa2YAyg/0ZSTpA3LFjR/hv7+8ao= sigs.k8s.io/kustomize/kyaml v0.8.1 h1:5GRanVGU6+iq3ERTiQD9VIfyGByFVB4z4GthP8NkRYE=
sigs.k8s.io/kustomize/kyaml v0.7.1/go.mod h1:ne3F9JPhW2wrVaLslxBsEe6MQJQ9YK5rUutrdhBWXwI= sigs.k8s.io/kustomize/kyaml v0.8.1/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=

View File

@@ -4,9 +4,12 @@
package commands package commands
import ( import (
"fmt"
"io"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
@@ -31,6 +34,8 @@ func NewAnnotateRunner(parent string) *AnnotateRunner {
c.Flags().StringVar(&r.Name, "name", "", "Resource name to annotate") c.Flags().StringVar(&r.Name, "name", "", "Resource name to annotate")
c.Flags().StringVar(&r.Namespace, "namespace", "", "Resource namespace to annotate") c.Flags().StringVar(&r.Namespace, "namespace", "", "Resource namespace to annotate")
c.Flags().StringSliceVar(&r.Values, "kv", []string{}, "annotation as KEY=VALUE") c.Flags().StringSliceVar(&r.Values, "kv", []string{}, "annotation as KEY=VALUE")
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
"add annotations recursively in all the nested subpackages")
return r return r
} }
@@ -46,6 +51,7 @@ type AnnotateRunner struct {
ApiVersion string ApiVersion string
Namespace string Namespace string
Path string Path string
RecurseSubPackages bool
} }
func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error { func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error {
@@ -55,11 +61,7 @@ func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error {
rw := &kio.ByteReadWriter{Reader: c.InOrStdin(), Writer: c.OutOrStdout()} rw := &kio.ByteReadWriter{Reader: c.InOrStdin(), Writer: c.OutOrStdout()}
input = []kio.Reader{rw} input = []kio.Reader{rw}
output = []kio.Writer{rw} output = []kio.Writer{rw}
} else {
rw := &kio.LocalPackageReadWriter{PackagePath: args[0], NoDeleteFiles: true}
input = []kio.Reader{rw}
output = []kio.Writer{rw}
}
return handleError(c, kio.Pipeline{ return handleError(c, kio.Pipeline{
Inputs: input, Inputs: input,
Filters: []kio.Filter{r}, Filters: []kio.Filter{r},
@@ -67,6 +69,48 @@ func (r *AnnotateRunner) runE(c *cobra.Command, args []string) error {
}.Execute()) }.Execute())
} }
e := executeCmdOnPkgs{
writer: c.OutOrStdout(),
needOpenAPI: false,
recurseSubPackages: r.RecurseSubPackages,
cmdRunner: r,
rootPkgPath: args[0],
}
err := e.execute()
if err != nil {
return err
}
return nil
}
func (r *AnnotateRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
rw := &kio.LocalPackageReadWriter{PackagePath: pkgPath, NoDeleteFiles: true, PackageFileName: openAPIFileName}
input := []kio.Reader{rw}
output := []kio.Writer{rw}
err = kio.Pipeline{
Inputs: input,
Filters: []kio.Filter{r},
Outputs: output,
}.Execute()
if err != nil {
// return err if there is only package
if !r.RecurseSubPackages {
return err
} else {
// print error message and continue if there are multiple packages to annotate
fmt.Fprintf(w, "%s\n", err.Error())
}
} else {
fmt.Fprint(w, "added annotations in the package\n")
}
return nil
}
func (r *AnnotateRunner) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (r *AnnotateRunner) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
for i := range nodes { for i := range nodes {
n := nodes[i] n := nodes[i]

View File

@@ -12,7 +12,9 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/openapi"
) )
func TestAnnotateCommand(t *testing.T) { func TestAnnotateCommand(t *testing.T) {
@@ -486,3 +488,77 @@ spec:
replicas: 3 replicas: 3
` `
) )
func TestAnnotateSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "annotate-recurse-subpackages",
dataset: "dataset-without-setters",
args: []string{"--kv", "foo=bar", "-R"},
expected: `${baseDir}/mysql/
added annotations in the package
${baseDir}/mysql/storage/
added annotations in the package
`,
},
{
name: "annotate-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql",
args: []string{"--kv", "foo=bar"},
expected: `${baseDir}/mysql/
added annotations in the package
`,
},
{
name: "annotate-nested-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql/storage",
args: []string{"--kv", "foo=bar"},
expected: `${baseDir}/mysql/storage/
added annotations in the package
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := NewAnnotateRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
t.FailNow()
}
})
}
}

View File

@@ -5,9 +5,11 @@ package commands
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
@@ -19,15 +21,14 @@ import (
func GetCatRunner(name string) *CatRunner { func GetCatRunner(name string) *CatRunner {
r := &CatRunner{} r := &CatRunner{}
c := &cobra.Command{ c := &cobra.Command{
Use: "cat DIR...", Use: "cat DIR",
Short: commands.CatShort, Short: commands.CatShort,
Long: commands.CatLong, Long: commands.CatLong,
Example: commands.CatExamples, Example: commands.CatExamples,
RunE: r.runE, RunE: r.runE,
Args: cobra.MaximumNArgs(1),
} }
fixDocs(name, c) fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
c.Flags().BoolVar(&r.Format, "format", true, c.Flags().BoolVar(&r.Format, "format", true,
"format resource config yaml before printing.") "format resource config yaml before printing.")
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", false, c.Flags().BoolVar(&r.KeepAnnotations, "annotate", false,
@@ -49,6 +50,8 @@ func GetCatRunner(name string) *CatRunner {
"if true, exclude non-local-config in the output.") "if true, exclude non-local-config in the output.")
c.Flags().StringVar(&r.OutputDest, "dest", "", c.Flags().StringVar(&r.OutputDest, "dest", "",
"if specified, write output to a file rather than stdout") "if specified, write output to a file rather than stdout")
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
"print resources recursively in all the nested subpackages")
r.Command = c r.Command = c
return r return r
} }
@@ -59,7 +62,6 @@ func CatCommand(name string) *cobra.Command {
// CatRunner contains the run function // CatRunner contains the run function
type CatRunner struct { type CatRunner struct {
IncludeSubpackages bool
Format bool Format bool
KeepAnnotations bool KeepAnnotations bool
WrapKind string WrapKind string
@@ -71,56 +73,102 @@ type CatRunner struct {
IncludeLocal bool IncludeLocal bool
ExcludeNonLocal bool ExcludeNonLocal bool
Command *cobra.Command Command *cobra.Command
RecurseSubPackages bool
} }
func (r *CatRunner) runE(c *cobra.Command, args []string) error { func (r *CatRunner) runE(c *cobra.Command, args []string) error {
var writer = c.OutOrStdout()
if r.OutputDest != "" {
o, err := os.Create(r.OutputDest)
if err != nil {
return errors.Wrap(err)
}
defer o.Close()
writer = o
}
if len(args) == 0 {
input := &kio.ByteReader{Reader: c.InOrStdin()}
// if there is a function-config specified, emit it // if there is a function-config specified, emit it
outputs, err := r.out(writer)
if err != nil {
return err
}
return handleError(c, kio.Pipeline{Inputs: []kio.Reader{input}, Filters: r.catFilters(), Outputs: outputs}.Execute())
}
e := executeCmdOnPkgs{
writer: writer,
needOpenAPI: false,
recurseSubPackages: r.RecurseSubPackages,
cmdRunner: r,
rootPkgPath: args[0],
skipPkgPathPrint: true,
}
return e.execute()
}
func (r *CatRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
input := kio.LocalPackageReader{PackagePath: pkgPath, PackageFileName: openAPIFileName}
outputs, err := r.out(w)
if err != nil {
return err
}
err = kio.Pipeline{
Inputs: []kio.Reader{input},
Filters: r.catFilters(),
Outputs: outputs,
}.Execute()
if err != nil {
// return err if there is only package
if !r.RecurseSubPackages {
return err
} else {
// print error message and continue if there are multiple packages to annotate
fmt.Fprintf(w, "%s in package %q\n", err.Error(), pkgPath)
}
}
fmt.Fprintf(w, "---")
return nil
}
func (r *CatRunner) catFilters() []kio.Filter {
var fltrs []kio.Filter
// don't include reconcilers
fltrs = append(fltrs, &filters.IsLocalConfig{
IncludeLocalConfig: r.IncludeLocal,
ExcludeNonLocalConfig: r.ExcludeNonLocal,
})
if r.Format {
fltrs = append(fltrs, filters.FormatFilter{})
}
if r.StripComments {
fltrs = append(fltrs, filters.StripCommentsFilter{})
}
return fltrs
}
func (r *CatRunner) out(w io.Writer) ([]kio.Writer, error) {
var outputs []kio.Writer
var functionConfig *yaml.RNode var functionConfig *yaml.RNode
if r.FunctionConfig != "" { if r.FunctionConfig != "" {
configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig, configs, err := kio.LocalPackageReader{PackagePath: r.FunctionConfig,
OmitReaderAnnotations: !r.KeepAnnotations}.Read() OmitReaderAnnotations: !r.KeepAnnotations}.Read()
if err != nil { if err != nil {
return err return outputs, err
} }
if len(configs) != 1 { if len(configs) != 1 {
return fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs)) return outputs, fmt.Errorf("expected exactly 1 functionConfig, found %d", len(configs))
} }
functionConfig = configs[0] functionConfig = configs[0]
} }
var inputs []kio.Reader
for _, a := range args {
inputs = append(inputs, kio.LocalPackageReader{
PackagePath: a,
IncludeSubpackages: r.IncludeSubpackages,
})
}
if len(inputs) == 0 {
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
}
var fltr []kio.Filter
// don't include reconcilers
fltr = append(fltr, &filters.IsLocalConfig{
IncludeLocalConfig: r.IncludeLocal,
ExcludeNonLocalConfig: r.ExcludeNonLocal,
})
if r.Format {
fltr = append(fltr, filters.FormatFilter{})
}
if r.StripComments {
fltr = append(fltr, filters.StripCommentsFilter{})
}
var out = c.OutOrStdout()
if r.OutputDest != "" {
o, err := os.Create(r.OutputDest)
if err != nil {
return handleError(c, errors.Wrap(err))
}
defer o.Close()
out = o
}
// remove this annotation explicitly, the ByteWriter won't clear it by // remove this annotation explicitly, the ByteWriter won't clear it by
// default because it doesn't set it // default because it doesn't set it
clear := []string{"config.kubernetes.io/path"} clear := []string{"config.kubernetes.io/path"}
@@ -128,9 +176,8 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
clear = nil clear = nil
} }
var outputs []kio.Writer
outputs = append(outputs, kio.ByteWriter{ outputs = append(outputs, kio.ByteWriter{
Writer: out, Writer: w,
KeepReaderAnnotations: r.KeepAnnotations, KeepReaderAnnotations: r.KeepAnnotations,
WrappingKind: r.WrapKind, WrappingKind: r.WrapKind,
WrappingAPIVersion: r.WrapApiVersion, WrappingAPIVersion: r.WrapApiVersion,
@@ -139,5 +186,5 @@ func (r *CatRunner) runE(c *cobra.Command, args []string) error {
ClearAnnotations: clear, ClearAnnotations: clear,
}) })
return handleError(c, kio.Pipeline{Inputs: inputs, Filters: fltr, Outputs: outputs}.Execute()) return outputs, nil
} }

View File

@@ -8,10 +8,13 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/openapi"
) )
// TODO(pwittrock): write tests for reading / writing ResourceLists // TODO(pwittrock): write tests for reading / writing ResourceLists
@@ -112,7 +115,7 @@ metadata:
app: nginx app: nginx
spec: spec:
replicas: 3 replicas: 3
`, b.String()) { ---`, b.String()) {
return return
} }
} }
@@ -168,8 +171,7 @@ metadata:
annotations: annotations:
app: nginx app: nginx
spec: spec:
replicas: 3 replicas: 3`), 0600)
`), 0600)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
return return
} }
@@ -223,7 +225,7 @@ metadata:
app: nginx app: nginx
spec: spec:
replicas: 3 replicas: 3
`, b.String()) { ---`, b.String()) {
return return
} }
} }
@@ -305,7 +307,7 @@ metadata:
image: gcr.io/example/reconciler:v1 image: gcr.io/example/reconciler:v1
spec: spec:
replicas: 3 replicas: 3
`, b.String()) { ---`, b.String()) {
return return
} }
} }
@@ -362,8 +364,7 @@ metadata:
annotations: annotations:
app: nginx app: nginx
spec: spec:
replicas: 3 replicas: 3`), 0600)
`), 0600)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
return return
} }
@@ -420,7 +421,7 @@ metadata:
app: nginx app: nginx
spec: spec:
replicas: 3 replicas: 3
`, string(actual)) { ---`, string(actual)) {
return return
} }
} }
@@ -477,8 +478,7 @@ metadata:
annotations: annotations:
app: nginx app: nginx
spec: spec:
replicas: 3 replicas: 3`), 0600)
`), 0600)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
return return
} }
@@ -536,7 +536,132 @@ metadata:
app: nginx app: nginx
spec: spec:
replicas: 3 replicas: 3
`, string(actual)) { ---`, string(actual)) {
return return
} }
} }
func TestCatSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "cat-recurse-subpackages",
dataset: "dataset-without-setters",
expected: `# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-deployment
namespace: myspace
spec:
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:1.7.9
---
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: storage-deployment
namespace: myspace
spec:
replicas: 4
template:
spec:
containers:
- name: storage
image: storage:1.7.7
---`,
},
{
name: "cat-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
args: []string{"-R=false"},
packagePath: "mysql",
expected: `# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-deployment
namespace: myspace
spec:
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:1.7.9
---`,
},
{
name: "cat-nested-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql/storage",
args: []string{"-R=false"},
expected: `# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: storage-deployment
namespace: myspace
spec:
replicas: 4
template:
spec:
containers:
- name: storage
image: storage:1.7.7
---`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := commands.GetCatRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
t.FailNow()
}
})
}
}

View File

@@ -5,7 +5,10 @@ package commands
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io"
"io/ioutil" "io/ioutil"
"path/filepath"
"strings" "strings"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
@@ -13,9 +16,7 @@ import (
"sigs.k8s.io/kustomize/cmd/config/ext" "sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters" "sigs.k8s.io/kustomize/kyaml/setters"
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil" "sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
) )
@@ -59,6 +60,8 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
set.Flags().StringVar(&r.SchemaPath, "schema-path", "", set.Flags().StringVar(&r.SchemaPath, "schema-path", "",
`openAPI schema file path for setter constraints -- file content `+ `openAPI schema file path for setter constraints -- file content `+
`e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`) `e.g. {"type": "string", "maxLength": 15, "enum": ["allowedValue1", "allowedValue2"]}`)
set.Flags().BoolVarP(&r.CreateSetter.RecurseSubPackages, "recurse-subpackages", "R", false,
"creates setter recursively in all the nested subpackages")
set.Flags().MarkHidden("version") set.Flags().MarkHidden("version")
fixDocs(parent, set) fixDocs(parent, set)
r.Command = set r.Command = set
@@ -78,7 +81,7 @@ type CreateSetterRunner struct {
} }
func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error { func (r *CreateSetterRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, r.set(c, args)) return handleError(c, r.createSetter(c, args))
} }
func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error { func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
@@ -111,42 +114,6 @@ func (r *CreateSetterRunner) preRunE(c *cobra.Command, args []string) error {
} }
if setterVersion == "v2" { 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(fieldmeta.DefinitionsPrefix + fieldmeta.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)
}
// check if setter with same name exists and throw error
ref, err = spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + r.CreateSetter.Name)
if err != nil {
return err
}
setter, _ := openapi.Resolve(&ref)
// if setter already exists with the input setter name, throw error
if setter != nil {
return errors.Errorf("setter with name %s already exists, "+
"if you want to modify it, please delete the existing setter and recreate it", r.CreateSetter.Name)
}
r.CreateSetter.Description = r.Set.SetPartialField.Description r.CreateSetter.Description = r.Set.SetPartialField.Description
r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy r.CreateSetter.SetBy = r.Set.SetPartialField.SetBy
r.CreateSetter.Type = r.Set.SetPartialField.Type r.CreateSetter.Type = r.Set.SetPartialField.Type
@@ -210,9 +177,20 @@ func (r *CreateSetterRunner) processSchema() error {
return nil return nil
} }
func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error { func (r *CreateSetterRunner) createSetter(c *cobra.Command, args []string) error {
if setterVersion == "v2" { if setterVersion == "v2" {
return r.CreateSetter.Create(r.OpenAPIFile, args[0]) e := executeCmdOnPkgs{
needOpenAPI: true,
writer: c.OutOrStdout(),
rootPkgPath: args[0],
recurseSubPackages: r.CreateSetter.RecurseSubPackages,
cmdRunner: r,
}
err := e.execute()
if err != nil {
return handleError(c, err)
}
return nil
} }
rw := &kio.LocalPackageReadWriter{PackagePath: args[0]} rw := &kio.LocalPackageReadWriter{PackagePath: args[0]}
@@ -226,6 +204,41 @@ func (r *CreateSetterRunner) set(c *cobra.Command, args []string) error {
return nil return nil
} }
func (r *CreateSetterRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
r.CreateSetter = settersutil.SetterCreator{
Name: r.CreateSetter.Name,
SetBy: r.CreateSetter.SetBy,
Description: r.CreateSetter.Description,
Type: r.CreateSetter.Type,
Schema: r.CreateSetter.Schema,
FieldName: r.CreateSetter.FieldName,
FieldValue: r.CreateSetter.FieldValue,
Required: r.CreateSetter.Required,
RecurseSubPackages: r.CreateSetter.RecurseSubPackages,
OpenAPIFileName: openAPIFileName,
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
ResourcesPath: pkgPath,
}
err = r.CreateSetter.Create()
if err != nil {
// return err if RecurseSubPackages is false
if !r.CreateSetter.RecurseSubPackages {
return err
} else {
// print error message and continue if RecurseSubPackages is true
fmt.Fprintf(w, "%s\n", err.Error())
}
} else {
fmt.Fprintf(w, "created setter %q\n", r.CreateSetter.Name)
}
return nil
}
// schemaFromFile reads the contents from schemaPath and returns schema // schemaFromFile reads the contents from schemaPath and returns schema
func schemaFromFile(schemaPath string) (*spec.Schema, error) { func schemaFromFile(schemaPath string) (*spec.Schema, error) {
sc := &spec.Schema{} sc := &spec.Schema{}

View File

@@ -7,12 +7,13 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/openapi"
) )
@@ -43,6 +44,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter "replicas"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -81,6 +83,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter "replicas"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -122,7 +125,7 @@ openAPI:
- marker: ${my-tag-setter} - marker: ${my-tag-setter}
ref: '#/definitions/io.k8s.cli.setters.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", err: `substitution with name "my-image" already exists, substitution and setter can't have same name`,
}, },
{ {
@@ -140,7 +143,7 @@ openAPI:
name: my-image name: my-image
value: "nginx" value: "nginx"
`, `,
err: "setter with name my-image already exists, if you want to modify it, please delete the existing setter and recreate it", err: `setter with name "my-image" already exists, if you want to modify it, please delete the existing setter and recreate it`,
}, },
{ {
@@ -159,6 +162,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter "replicas"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -200,6 +204,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter replicas`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -254,6 +259,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter "list"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -354,6 +360,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter replicas`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -400,6 +407,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter "replicas"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -437,6 +445,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter "foo.bar"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -587,6 +596,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter "replicas"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -712,6 +722,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created setter "replicas"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -742,13 +753,14 @@ spec:
openapi.ResetOpenAPI() openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI() defer openapi.ResetOpenAPI()
f, err := ioutil.TempFile("", "k8s-cli-") baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
defer os.Remove(f.Name()) defer os.RemoveAll(baseDir)
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600) f := filepath.Join(baseDir, "Krmfile")
err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -768,13 +780,7 @@ spec:
test.args = append(test.args, "--schema-path", sch.Name()) test.args = append(test.args, "--schema-path", sch.Name())
} }
old := ext.GetOpenAPIFile r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -787,7 +793,7 @@ spec:
runner := commands.NewCreateSetterRunner("") runner := commands.NewCreateSetterRunner("")
out := &bytes.Buffer{} out := &bytes.Buffer{}
runner.Command.SetOut(out) runner.Command.SetOut(out)
runner.Command.SetArgs(append([]string{r.Name()}, test.args...)) runner.Command.SetArgs(append([]string{baseDir}, test.args...))
err = runner.Command.Execute() err = runner.Command.Execute()
if test.err != "" { if test.err != "" {
if !assert.NotNil(t, err) { if !assert.NotNil(t, err) {
@@ -801,7 +807,14 @@ spec:
t.FailNow() t.FailNow()
} }
if !assert.Equal(t, test.out, out.String()) { expectedOut := strings.Replace(test.out, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expectedOut, "\\", "/", -1)
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(out.String(), "\\", "/", -1),
"//", "/", -1)
if !assert.Contains(t, actualNormalized, expectedNormalized) {
t.FailNow() t.FailNow()
} }
@@ -815,7 +828,7 @@ spec:
t.FailNow() t.FailNow()
} }
actualOpenAPI, err := ioutil.ReadFile(f.Name()) actualOpenAPI, err := ioutil.ReadFile(f)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -827,3 +840,92 @@ spec:
}) })
} }
} }
func TestCreateSetterSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "create-setter-recurse-subpackages",
dataset: "dataset-without-setters",
args: []string{"namespace", "myspace", "-R"},
expected: `${baseDir}/mysql/
created setter "namespace"
${baseDir}/mysql/storage/
created setter "namespace"
`,
},
{
name: "create-setter-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql",
args: []string{"namespace", "myspace"},
expected: `${baseDir}/mysql/
created setter "namespace"
`,
},
{
name: "create-setter-nested-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql/storage",
args: []string{"namespace", "myspace"},
expected: `${baseDir}/mysql/storage/
created setter "namespace"
`,
},
{
name: "create-setter-already-exists",
dataset: "dataset-with-setters",
packagePath: "mysql",
args: []string{"namespace", "myspace", "-R"},
expected: `${baseDir}/mysql/
setter with name "namespace" already exists, if you want to modify it, please delete the existing setter and recreate it
${baseDir}/mysql/nosetters/
created setter "namespace"
${baseDir}/mysql/storage/
setter with name "namespace" already exists, if you want to modify it, please delete the existing setter and recreate it
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := commands.NewCreateSetterRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
t.FailNow()
}
})
}
}

View File

@@ -5,13 +5,11 @@ package commands
import ( import (
"fmt" "fmt"
"io"
"path/filepath"
"github.com/go-openapi/spec"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext" "sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil" "sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
) )
@@ -21,7 +19,7 @@ func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
cs := &cobra.Command{ cs := &cobra.Command{
Use: "create-subst DIR NAME", Use: "create-subst DIR NAME",
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
PreRunE: r.preRunE, PreRun: r.preRun,
RunE: r.runE, RunE: r.runE,
} }
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "", cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
@@ -30,6 +28,8 @@ func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
"value of the field to create substitution for -- e.g. --field-value nginx:0.1.0") "value of the field to create substitution for -- e.g. --field-value nginx:0.1.0")
cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "", cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "",
`substitution pattern -- e.g. --pattern \${my-image-setter}:\${my-tag-setter}`) `substitution pattern -- e.g. --pattern \${my-image-setter}:\${my-tag-setter}`)
cs.Flags().BoolVarP(&r.CreateSubstitution.RecurseSubPackages, "recurse-subpackages", "R", false,
"creates substitution recursively in all the nested subpackages")
_ = cs.MarkFlagRequired("pattern") _ = cs.MarkFlagRequired("pattern")
_ = cs.MarkFlagRequired("field-value") _ = cs.MarkFlagRequired("field-value")
fixDocs(parent, cs) fixDocs(parent, cs)
@@ -49,49 +49,52 @@ type CreateSubstitutionRunner struct {
} }
func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error { func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, r.CreateSubstitution.Create(r.OpenAPIFile, args[0])) e := executeCmdOnPkgs{
needOpenAPI: true,
writer: c.OutOrStdout(),
rootPkgPath: args[0],
recurseSubPackages: r.CreateSubstitution.RecurseSubPackages,
cmdRunner: r,
} }
err := e.execute()
func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
var err error
r.CreateSubstitution.Name = args[1]
if err != nil { if err != nil {
return err return handleError(c, err)
}
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(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + r.CreateSubstitution.Name)
if err != nil {
return err
}
subst, _ := openapi.Resolve(&ref)
// if substitution already exists with the input substitution name, throw error
if subst != nil {
return errors.Errorf("substitution with name %s already exists", r.CreateSubstitution.Name)
}
// check if setter with same name exists and throw error
ref, err = spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + r.CreateSubstitution.Name)
if err != nil {
return err
}
setter, _ := openapi.Resolve(&ref)
// if setter already exists with input substitution name, throw error
if setter != nil {
return errors.Errorf(fmt.Sprintf("setter with name %s already exists, "+
"substitution and setter can't have same name", r.CreateSubstitution.Name))
} }
return nil return nil
} }
func (r *CreateSubstitutionRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
r.CreateSubstitution = settersutil.SubstitutionCreator{
Name: r.CreateSubstitution.Name,
FieldName: r.CreateSubstitution.FieldName,
FieldValue: r.CreateSubstitution.FieldValue,
RecurseSubPackages: r.CreateSubstitution.RecurseSubPackages,
Pattern: r.CreateSubstitution.Pattern,
OpenAPIFileName: openAPIFileName,
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
ResourcesPath: pkgPath,
}
err = r.CreateSubstitution.Create()
if err != nil {
// return err if RecurseSubPackages is false
if !r.CreateSubstitution.RecurseSubPackages {
return err
} else {
// print error message and continue if RecurseSubPackages is true
fmt.Fprintf(w, "%s\n", err.Error())
}
} else {
fmt.Fprintf(w, "created substitution %q\n", r.CreateSubstitution.Name)
}
return nil
}
func (r *CreateSubstitutionRunner) preRun(c *cobra.Command, args []string) {
r.CreateSubstitution.Name = args[1]
}

View File

@@ -7,12 +7,13 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/openapi"
) )
@@ -62,6 +63,7 @@ openAPI:
name: my-tag-setter name: my-tag-setter
value: "1.7.9" value: "1.7.9"
`, `,
out: `created substitution "my-image-subst"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -123,7 +125,7 @@ openAPI:
- marker: ${my-tag-setter} - marker: ${my-tag-setter}
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
`, `,
err: "substitution with name my-image already exists", err: `substitution with name "my-image" already exists`,
}, },
{ {
name: "error if setter with same name exists", name: "error if setter with same name exists",
@@ -140,7 +142,7 @@ openAPI:
name: my-image name: my-image
value: "nginx" value: "nginx"
`, `,
err: "setter with name my-image already exists, substitution and setter can't have same name", err: `setter with name "my-image" already exists, substitution and setter can't have same name`,
}, },
{ {
name: "substitution and create setters 1", name: "substitution and create setters 1",
@@ -165,6 +167,7 @@ spec:
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
`, `,
out: `created substitution "my-image-subst"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -253,6 +256,7 @@ openAPI:
- marker: ${my-tag-setter} - marker: ${my-tag-setter}
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter' ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
`, `,
out: `created substitution "my-nested-subst"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -341,7 +345,7 @@ spec:
substVal: prefix-1234 substVal: prefix-1234
`, `,
err: "setters must have different name than the substitution: foo", err: `setters must have different name than the substitution: foo`,
}, },
} }
for i := range tests { for i := range tests {
@@ -351,22 +355,18 @@ spec:
openapi.ResetOpenAPI() openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI() defer openapi.ResetOpenAPI()
f, err := ioutil.TempFile("", "k8s-cli-") baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
defer os.Remove(f.Name()) defer os.RemoveAll(baseDir)
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600) f := filepath.Join(baseDir, "Krmfile")
err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
old := ext.GetOpenAPIFile
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}
r, err := ioutil.TempFile("", "k8s-cli-*.yaml") r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -379,7 +379,7 @@ spec:
runner := commands.NewCreateSubstitutionRunner("") runner := commands.NewCreateSubstitutionRunner("")
out := &bytes.Buffer{} out := &bytes.Buffer{}
runner.Command.SetOut(out) runner.Command.SetOut(out)
runner.Command.SetArgs(append([]string{r.Name()}, test.args...)) runner.Command.SetArgs(append([]string{baseDir}, test.args...))
err = runner.Command.Execute() err = runner.Command.Execute()
if test.err != "" { if test.err != "" {
@@ -393,7 +393,14 @@ spec:
t.FailNow() t.FailNow()
} }
if !assert.Equal(t, test.out, out.String()) { expectedOut := strings.Replace(test.out, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expectedOut, "\\", "/", -1)
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(out.String(), "\\", "/", -1),
"//", "/", -1)
if !assert.Contains(t, actualNormalized, expectedNormalized) {
t.FailNow() t.FailNow()
} }
@@ -407,7 +414,7 @@ spec:
t.FailNow() t.FailNow()
} }
actualOpenAPI, err := ioutil.ReadFile(f.Name()) actualOpenAPI, err := ioutil.ReadFile(f)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -419,3 +426,89 @@ spec:
}) })
} }
} }
func TestCreateSubstSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "create-subst-recurse-subpackages",
dataset: "dataset-without-setters",
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}", "-R"},
expected: `${baseDir}/mysql/
created substitution "image-tag"
${baseDir}/mysql/storage/
created substitution "image-tag"
`,
},
{
name: "create-subst-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql",
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}"},
expected: `${baseDir}/mysql/
created substitution "image-tag"`,
},
{
name: "create-subst-nested-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql/storage",
args: []string{"image-tag", "--field-value", "storage:1.7.9", "--pattern", "${image}:${tag}"},
expected: `${baseDir}/mysql/storage/
created substitution "image-tag"`,
},
{
name: "create-subst-already-exists",
dataset: "dataset-with-setters",
packagePath: "mysql",
args: []string{"image-tag", "--field-value", "mysql:1.7.9", "--pattern", "${image}:${tag}", "-R"},
expected: `${baseDir}/mysql/
substitution with name "image-tag" already exists
${baseDir}/mysql/nosetters/
created substitution "image-tag"
${baseDir}/mysql/storage/
created substitution "image-tag"`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := commands.NewCreateSubstitutionRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
t.FailNow()
}
})
}
}

View File

@@ -4,11 +4,14 @@
package commands package commands
import ( import (
"fmt"
"io"
"path/filepath"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext" "sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/fieldmeta" "sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil" "sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
) )
@@ -24,6 +27,8 @@ func NewDeleteSetterRunner(parent string) *DeleteSetterRunner {
PreRunE: r.preRunE, PreRunE: r.preRunE,
RunE: r.runE, RunE: r.runE,
} }
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
"deletes setter recursively in all the nested subpackages")
fixDocs(parent, c) fixDocs(parent, c)
r.Command = c r.Command = c
@@ -38,6 +43,7 @@ type DeleteSetterRunner struct {
Command *cobra.Command Command *cobra.Command
DeleteSetter settersutil.DeleterCreator DeleteSetter settersutil.DeleterCreator
OpenAPIFile string OpenAPIFile string
RecurseSubPackages bool
} }
func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error { func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
@@ -50,17 +56,49 @@ func (r *DeleteSetterRunner) preRunE(c *cobra.Command, args []string) error {
return err return err
} }
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
return err
}
return nil return nil
} }
func (r *DeleteSetterRunner) runE(c *cobra.Command, args []string) error { func (r *DeleteSetterRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, r.delete(c, args)) e := executeCmdOnPkgs{
needOpenAPI: true,
writer: c.OutOrStdout(),
rootPkgPath: args[0],
recurseSubPackages: r.RecurseSubPackages,
cmdRunner: r,
}
err := e.execute()
if err != nil {
return handleError(c, err)
}
return nil
} }
func (r *DeleteSetterRunner) delete(c *cobra.Command, args []string) error { func (r *DeleteSetterRunner) executeCmd(w io.Writer, pkgPath string) error {
return r.DeleteSetter.Delete(r.OpenAPIFile, args[0]) openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
r.DeleteSetter = settersutil.DeleterCreator{
Name: r.DeleteSetter.Name,
DefinitionPrefix: fieldmeta.SetterDefinitionPrefix,
RecurseSubPackages: r.RecurseSubPackages,
OpenAPIFileName: openAPIFileName,
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
ResourcesPath: pkgPath,
}
err = r.DeleteSetter.Delete()
if err != nil {
// return err if RecurseSubPackages is false
if !r.DeleteSetter.RecurseSubPackages {
return err
} else {
// print error message and continue if RecurseSubPackages is true
fmt.Fprintf(w, "%s\n", err.Error())
}
} else {
fmt.Fprintf(w, "deleted setter %q\n", r.DeleteSetter.Name)
}
return nil
} }

View File

@@ -7,12 +7,13 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/openapi"
) )
@@ -52,6 +53,7 @@ openAPI:
value: "3" value: "3"
setBy: me setBy: me
`, `,
out: `deleted setter "replicas-setter"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -95,6 +97,7 @@ openAPI:
name: image name: image
value: nginx value: nginx
`, `,
out: `deleted setter "replicas-setter"`,
expectedOpenAPI: ` expectedOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -158,6 +161,7 @@ spec:
- "b" - "b"
- "c" - "c"
`, `,
out: `deleted setter "list"`,
expectedResources: ` expectedResources: `
apiVersion: example.com/v1beta1 apiVersion: example.com/v1beta1
kind: Example1 kind: Example1
@@ -226,7 +230,7 @@ metadata:
spec: spec:
replicas: 3 # {"$openapi" : "replicas-setter"} replicas: 3 # {"$openapi" : "replicas-setter"}
`, `,
err: `setter with name image does not exist`, err: `setter "image" does not exist`,
}, },
{ {
name: "delete setter used in substitution error", name: "delete setter used in substitution error",
@@ -287,7 +291,7 @@ openAPI:
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
`, `,
err: `setter is used in substitution image, please delete the parent substitution first`, err: `setter "image-name" is used in substitution "image", please delete the parent substitution first`,
}, },
} }
for i := range tests { for i := range tests {
@@ -297,24 +301,18 @@ kind: Deployment
openapi.ResetOpenAPI() openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI() defer openapi.ResetOpenAPI()
f, err := ioutil.TempFile("", "k8s-cli-") baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
defer os.Remove(f.Name()) defer os.RemoveAll(baseDir)
f := filepath.Join(baseDir, "Krmfile")
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600) err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
old := ext.GetOpenAPIFile r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -327,21 +325,29 @@ kind: Deployment
runner := commands.NewDeleteSetterRunner("") runner := commands.NewDeleteSetterRunner("")
out := &bytes.Buffer{} out := &bytes.Buffer{}
runner.Command.SetOut(out) runner.Command.SetOut(out)
runner.Command.SetArgs(append([]string{r.Name()}, test.args...)) runner.Command.SetArgs(append([]string{baseDir}, test.args...))
err = runner.Command.Execute() err = runner.Command.Execute()
if test.err != "" { if test.err != "" {
if !assert.NotNil(t, err) { if !assert.NotNil(t, err) {
t.FailNow() t.FailNow()
} else {
assert.Equal(t, err.Error(), test.err)
return
} }
assert.Equal(t, test.err, err.Error())
return
} }
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
if !assert.Equal(t, test.out, out.String()) { // normalize path format for windows
actualNorm := strings.Replace(
strings.Replace(out.String(), "\\", "/", -1),
"//", "/", -1)
expectedOut := strings.Replace(test.out, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expectedOut, "\\", "/", -1)
if !assert.Contains(t, strings.TrimSpace(actualNorm), expectedNormalized) {
t.FailNow() t.FailNow()
} }
@@ -355,7 +361,7 @@ kind: Deployment
t.FailNow() t.FailNow()
} }
actualOpenAPI, err := ioutil.ReadFile(f.Name()) actualOpenAPI, err := ioutil.ReadFile(f)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -367,3 +373,80 @@ kind: Deployment
}) })
} }
} }
func TestDeleteSetterSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "delete-setter-recurse-subpackages",
dataset: "dataset-with-setters",
args: []string{"namespace", "-R"},
expected: `${baseDir}/mysql/
deleted setter "namespace"
${baseDir}/mysql/nosetters/
setter "namespace" does not exist
${baseDir}/mysql/storage/
deleted setter "namespace"
`,
},
{
name: "delete-setter-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-with-setters",
packagePath: "mysql",
args: []string{"namespace"},
expected: `${baseDir}/mysql/
deleted setter "namespace"
`,
},
{
name: "delete-setter-nested-pkg-no-recurse-subpackages",
dataset: "dataset-with-setters",
packagePath: "mysql/storage",
args: []string{"namespace"},
expected: `${baseDir}/mysql/storage/
deleted setter "namespace"
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
//defer os.RemoveAll(baseDir)
runner := commands.NewDeleteSetterRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
t.FailNow()
}
})
}
}

View File

@@ -4,10 +4,13 @@
package commands package commands
import ( import (
"fmt"
"io"
"path/filepath"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext" "sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/kyaml/fieldmeta" "sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/setters2/settersutil" "sigs.k8s.io/kustomize/kyaml/setters2/settersutil"
) )
@@ -20,6 +23,8 @@ func NewDeleteSubstitutionRunner(parent string) *DeleteSubstitutionRunner {
PreRunE: r.preRunE, PreRunE: r.preRunE,
RunE: r.runE, RunE: r.runE,
} }
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
"deletes substitution recursively in all the nested subpackages")
fixDocs(parent, c) fixDocs(parent, c)
r.Command = c r.Command = c
@@ -34,6 +39,7 @@ type DeleteSubstitutionRunner struct {
Command *cobra.Command Command *cobra.Command
DeleteSubstitution settersutil.DeleterCreator DeleteSubstitution settersutil.DeleterCreator
OpenAPIFile string OpenAPIFile string
RecurseSubPackages bool
} }
func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) error { func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
@@ -46,17 +52,49 @@ func (r *DeleteSubstitutionRunner) preRunE(c *cobra.Command, args []string) erro
return err return err
} }
if err := openapi.AddSchemaFromFile(r.OpenAPIFile); err != nil {
return err
}
return nil return nil
} }
func (r *DeleteSubstitutionRunner) runE(c *cobra.Command, args []string) error { func (r *DeleteSubstitutionRunner) runE(c *cobra.Command, args []string) error {
return handleError(c, r.delete(c, args)) e := executeCmdOnPkgs{
needOpenAPI: true,
writer: c.OutOrStdout(),
rootPkgPath: args[0],
recurseSubPackages: r.RecurseSubPackages,
cmdRunner: r,
}
err := e.execute()
if err != nil {
return handleError(c, err)
}
return nil
} }
func (r *DeleteSubstitutionRunner) delete(c *cobra.Command, args []string) error { func (r *DeleteSubstitutionRunner) executeCmd(w io.Writer, pkgPath string) error {
return r.DeleteSubstitution.Delete(r.OpenAPIFile, args[0]) openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
r.DeleteSubstitution = settersutil.DeleterCreator{
Name: r.DeleteSubstitution.Name,
DefinitionPrefix: fieldmeta.SubstitutionDefinitionPrefix,
RecurseSubPackages: r.RecurseSubPackages,
OpenAPIFileName: openAPIFileName,
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
ResourcesPath: pkgPath,
}
err = r.DeleteSubstitution.Delete()
if err != nil {
// return err if RecurseSubPackages is false
if !r.DeleteSubstitution.RecurseSubPackages {
return err
} else {
// print error message and continue if RecurseSubPackages is true
fmt.Fprintf(w, "%s\n", err.Error())
}
} else {
fmt.Fprintf(w, "deleted substitution %q\n", r.DeleteSubstitution.Name)
}
return nil
} }

View File

@@ -7,12 +7,13 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/openapi"
) )
@@ -79,6 +80,7 @@ spec:
- name: sidecar - name: sidecar
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"} image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my.image"}
`, `,
out: `deleted substitution "my.image"`,
expectedResources: ` expectedResources: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
@@ -169,6 +171,7 @@ spec:
- name: sidecar - name: sidecar
image: nginx:1.7.9 # {"$openapi":"my-image-sub"} image: nginx:1.7.9 # {"$openapi":"my-image-sub"}
`, `,
out: `deleted substitution "my-image-sub"`,
expectedResources: ` expectedResources: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
@@ -297,7 +300,7 @@ openAPI:
value: "3" value: "3"
setBy: me setBy: me
`, `,
err: "substitution with name my-image-sub-not-present does not exist", err: `substitution "my-image-sub-not-present" does not exist`,
}, },
{ {
@@ -415,7 +418,7 @@ spec:
- name: sidecar - name: sidecar
image: nginx::1.7.9 # {"$openapi":"my-image-subst"} image: nginx::1.7.9 # {"$openapi":"my-image-subst"}
`, `,
err: "substitution is used in substitution my-nested-subst, please delete the parent substitution first", err: `substitution "my-image-subst" is used in substitution "my-nested-subst", please delete the parent substitution first`,
}, },
} }
for i := range tests { for i := range tests {
@@ -425,24 +428,18 @@ spec:
openapi.ResetOpenAPI() openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI() defer openapi.ResetOpenAPI()
f, err := ioutil.TempFile("", "k8s-cli-") baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
defer os.Remove(f.Name()) defer os.RemoveAll(baseDir)
f := filepath.Join(baseDir, "Krmfile")
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600) err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
old := ext.GetOpenAPIFile r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}
r, err := ioutil.TempFile("", "k8s-cli-*.yaml")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -455,21 +452,38 @@ spec:
runner := commands.NewDeleteSubstitutionRunner("") runner := commands.NewDeleteSubstitutionRunner("")
out := &bytes.Buffer{} out := &bytes.Buffer{}
runner.Command.SetOut(out) runner.Command.SetOut(out)
runner.Command.SetArgs(append([]string{r.Name()}, test.args...)) runner.Command.SetArgs(append([]string{baseDir}, test.args...))
err = runner.Command.Execute() err = runner.Command.Execute()
if test.err != "" { if test.err != "" {
if !assert.NotNil(t, err) { if !assert.NotNil(t, err) {
t.FailNow() t.FailNow()
} else {
assert.Equal(t, err.Error(), test.err)
return
} }
assert.Equal(t, test.err, err.Error())
return
} }
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
if !assert.Equal(t, test.out, out.String()) { // normalize path format for windows
actualNorm := strings.Replace(
strings.Replace(out.String(), "\\", "/", -1),
"//", "/", -1)
expectedOut := strings.Replace(test.out, "${baseDir}", baseDir, -1)
expectedNorm := strings.Replace(expectedOut, "\\", "/", -1)
if !assert.Contains(t, strings.TrimSpace(actualNorm), expectedNorm) {
t.FailNow()
}
actualOpenAPI, err := ioutil.ReadFile(f)
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t,
strings.TrimSpace(test.expectedOpenAPI),
strings.TrimSpace(string(actualOpenAPI))) {
t.FailNow() t.FailNow()
} }
@@ -480,16 +494,74 @@ spec:
if !assert.Equal(t, if !assert.Equal(t,
strings.TrimSpace(test.expectedResources), strings.TrimSpace(test.expectedResources),
strings.TrimSpace(string(actualResources))) { strings.TrimSpace(string(actualResources))) {
return t.FailNow()
} }
})
actualOpenAPI, err := ioutil.ReadFile(f.Name()) }
if !assert.NoError(t, err) { }
t.FailNow()
} func TestDeleteSubstitutionSubPackages(t *testing.T) {
if !assert.Equal(t, var tests = []struct {
strings.TrimSpace(test.expectedOpenAPI), name string
strings.TrimSpace(string(actualOpenAPI))) { dataset string
packagePath string
args []string
expected string
}{
{
name: "delete-substitution-recurse-subpackages",
dataset: "dataset-with-setters",
args: []string{"image-tag", "-R"},
expected: `${baseDir}/mysql/
deleted substitution "image-tag"
${baseDir}/mysql/nosetters/
substitution "image-tag" does not exist
${baseDir}/mysql/storage/
substitution "image-tag" does not exist
`,
},
{
name: "delete-setter-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-with-setters",
packagePath: "mysql",
args: []string{"image-tag"},
expected: `${baseDir}/mysql/
deleted substitution "image-tag"
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
//defer os.RemoveAll(baseDir)
runner := commands.NewDeleteSubstitutionRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
t.FailNow() t.FailNow()
} }
}) })

View File

@@ -7,15 +7,14 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext" "sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fieldmeta" "sigs.k8s.io/kustomize/kyaml/fieldmeta"
"sigs.k8s.io/kustomize/kyaml/pathutil"
"sigs.k8s.io/kustomize/kyaml/setters" "sigs.k8s.io/kustomize/kyaml/setters"
"sigs.k8s.io/kustomize/kyaml/setters2" "sigs.k8s.io/kustomize/kyaml/setters2"
) )
@@ -36,6 +35,8 @@ func NewListSettersRunner(parent string) *ListSettersRunner {
"output as github markdown") "output as github markdown")
c.Flags().BoolVar(&r.IncludeSubst, "include-subst", false, c.Flags().BoolVar(&r.IncludeSubst, "include-subst", false,
"include substitutions in the output") "include substitutions in the output")
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
"list setters recursively in all the nested subpackages")
fixDocs(parent, c) fixDocs(parent, c)
r.Command = c r.Command = c
return r return r
@@ -51,6 +52,7 @@ type ListSettersRunner struct {
List setters2.List List setters2.List
Markdown bool Markdown bool
IncludeSubst bool IncludeSubst bool
RecurseSubPackages bool
} }
func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error { func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
@@ -65,48 +67,50 @@ func (r *ListSettersRunner) preRunE(c *cobra.Command, args []string) error {
func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error { func (r *ListSettersRunner) runE(c *cobra.Command, args []string) error {
if setterVersion == "v2" { if setterVersion == "v2" {
e := executeCmdOnPkgs{
needOpenAPI: true,
writer: c.OutOrStdout(),
rootPkgPath: args[0],
recurseSubPackages: r.RecurseSubPackages,
cmdRunner: r,
}
err := e.execute()
if err != nil {
return handleError(c, err)
}
return nil
}
return handleError(c, lookup(r.Lookup, c, args))
}
func (r *ListSettersRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName() openAPIFileName, err := ext.OpenAPIFileName()
if err != nil { if err != nil {
return err return err
} }
openAPIPaths, err := pathutil.SubDirsWithFile(args[0], openAPIFileName)
if err != nil {
return err
}
if len(openAPIPaths) == 0 {
return errors.Errorf("unable to find %s in %s", openAPIFileName, args[0])
}
// list setters for all the subpackages with openAPI file paths
for _, openAPIPath := range openAPIPaths {
r.List = setters2.List{ r.List = setters2.List{
Name: r.List.Name, Name: r.List.Name,
OpenAPIFileName: openAPIFileName, OpenAPIFileName: openAPIFileName,
} }
resourcePath := strings.TrimSuffix(openAPIPath, openAPIFileName) openAPIPath := filepath.Join(pkgPath, openAPIFileName)
fmt.Fprintf(c.OutOrStdout(), "%s\n", resourcePath) if err := r.ListSetters(w, openAPIPath, pkgPath); err != nil {
if err := r.ListSetters(c, openAPIPath, resourcePath); err != nil {
return err return err
} }
if r.IncludeSubst { if r.IncludeSubst {
if err := r.ListSubstitutions(c, openAPIPath); err != nil { if err := r.ListSubstitutions(w, openAPIPath); err != nil {
return err return err
} }
} }
}
return nil return nil
} }
return handleError(c, lookup(r.Lookup, c, args)) func (r *ListSettersRunner) ListSetters(w io.Writer, openAPIPath, resourcePath string) error {
}
func (r *ListSettersRunner) ListSetters(c *cobra.Command, openAPIPath, resourcePath string) error {
// use setters v2 // use setters v2
if err := r.List.ListSetters(openAPIPath, resourcePath); err != nil { if err := r.List.ListSetters(openAPIPath, resourcePath); err != nil {
return err return err
} }
table := newTable(c.OutOrStdout(), r.Markdown) table := newTable(w, r.Markdown)
table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT", "REQUIRED"}) table.SetHeader([]string{"NAME", "VALUE", "SET BY", "DESCRIPTION", "COUNT", "REQUIRED"})
for i := range r.List.Setters { for i := range r.List.Setters {
s := r.List.Setters[i] s := r.List.Setters[i]
@@ -137,12 +141,12 @@ func (r *ListSettersRunner) ListSetters(c *cobra.Command, openAPIPath, resourceP
return nil return nil
} }
func (r *ListSettersRunner) ListSubstitutions(c *cobra.Command, openAPIPath string) error { func (r *ListSettersRunner) ListSubstitutions(w io.Writer, openAPIPath string) error {
// use setters v2 // use setters v2
if err := r.List.ListSubst(openAPIPath); err != nil { if err := r.List.ListSubst(openAPIPath); err != nil {
return err return err
} }
table := newTable(c.OutOrStdout(), r.Markdown) table := newTable(w, r.Markdown)
b := tablewriter.Border{Top: true} b := tablewriter.Border{Top: true}
table.SetBorders(b) table.SetBorders(b)

View File

@@ -471,9 +471,11 @@ func TestListSettersSubPackages(t *testing.T) {
}{ }{
{ {
name: "list-replicas", name: "list-replicas",
dataset: "dataset1", dataset: "dataset-with-setters",
args: []string{"--include-subst"}, args: []string{"--include-subst"},
expected: `test/testdata/dataset1/mysql/ expected: `
test/testdata/dataset-with-setters/mysql/
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
image mysql 1 No image mysql 1 No
namespace myspace 1 No namespace myspace 1 No
@@ -481,11 +483,26 @@ func TestListSettersSubPackages(t *testing.T) {
--------------- ----------------- -------------- --------------- ----------------- --------------
SUBSTITUTION PATTERN REFERENCES SUBSTITUTION PATTERN REFERENCES
image-tag ${image}:${tag} [image,tag] image-tag ${image}:${tag} [image,tag]
test/testdata/dataset1/mysql/nosetters/
test/testdata/dataset-with-setters/mysql/nosetters/
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
test/testdata/dataset1/mysql/storage/
test/testdata/dataset-with-setters/mysql/storage/
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
namespace myspace 1 No namespace myspace 1 No
`,
},
{
name: "list-replicas",
dataset: "dataset-with-setters/mysql",
args: []string{"--recurse-subpackages=false"},
expected: `
test/testdata/dataset-with-setters/mysql/
NAME VALUE SET BY DESCRIPTION COUNT REQUIRED
image mysql 1 No
namespace myspace 1 No
tag 1.7.9 1 No
`, `,
}, },
} }
@@ -509,7 +526,7 @@ test/testdata/dataset1/mysql/storage/
// normalize path format for windows // normalize path format for windows
actualNormalized := strings.Replace(actual.String(), "\\", "/", -1) actualNormalized := strings.Replace(actual.String(), "\\", "/", -1)
if !assert.Equal(t, test.expected, actualNormalized) { if !assert.Equal(t, strings.TrimSpace(test.expected), strings.TrimSpace(actualNormalized)) {
t.FailNow() t.FailNow()
} }
}) })

View File

@@ -5,7 +5,9 @@ package commands
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"path/filepath"
"github.com/olekukonko/tablewriter" "github.com/olekukonko/tablewriter"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -39,6 +41,8 @@ func NewSetRunner(parent string) *SetRunner {
"annotate the field with a description of its value") "annotate the field with a description of its value")
c.Flags().StringVar(&setterVersion, "version", "", c.Flags().StringVar(&setterVersion, "version", "",
"use this version of the setter format") "use this version of the setter format")
c.Flags().BoolVarP(&r.Set.RecurseSubPackages, "recurse-subpackages", "R", false,
"sets recursively in all the nested subpackages")
c.Flags().MarkHidden("version") c.Flags().MarkHidden("version")
return r return r
@@ -126,22 +130,62 @@ func (r *SetRunner) preRunE(c *cobra.Command, args []string) error {
return err return err
} }
} }
return nil return nil
} }
func (r *SetRunner) runE(c *cobra.Command, args []string) error { func (r *SetRunner) runE(c *cobra.Command, args []string) error {
if setterVersion == "v2" { if setterVersion == "v2" {
count, err := r.Set.Set(r.OpenAPIFile, args[0]) e := executeCmdOnPkgs{
fmt.Fprintf(c.OutOrStdout(), "set %d fields\n", count) needOpenAPI: true,
writer: c.OutOrStdout(),
rootPkgPath: args[0],
recurseSubPackages: r.Set.RecurseSubPackages,
cmdRunner: r,
}
err := e.execute()
if err != nil {
return handleError(c, err) return handleError(c, err)
} }
return nil
}
if len(args) > 2 || c.Flag("values").Changed { if len(args) > 2 || c.Flag("values").Changed {
return handleError(c, r.perform(c, args)) return handleError(c, r.perform(c, args))
} }
return handleError(c, lookup(r.Lookup, c, args)) return handleError(c, lookup(r.Lookup, c, args))
} }
func (r *SetRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
r.Set = settersutil.FieldSetter{
Name: r.Set.Name,
Value: r.Set.Value,
ListValues: r.Set.ListValues,
Description: r.Set.Description,
SetBy: r.Set.SetBy,
Count: 0,
OpenAPIPath: filepath.Join(pkgPath, openAPIFileName),
OpenAPIFileName: openAPIFileName,
ResourcesPath: pkgPath,
RecurseSubPackages: r.Set.RecurseSubPackages,
}
count, err := r.Set.Set()
if err != nil {
// return err if RecurseSubPackages is false
if !r.Set.RecurseSubPackages {
return err
} else {
// print error message and continue if RecurseSubPackages is true
fmt.Fprintf(w, "%s\n", err.Error())
}
} else {
fmt.Fprintf(w, "set %d field(s)\n", count)
}
return nil
}
func lookup(l setters.LookupSetters, c *cobra.Command, args []string) error { func lookup(l setters.LookupSetters, c *cobra.Command, args []string) error {
// lookup the setters // lookup the setters
err := kio.Pipeline{ err := kio.Pipeline{

View File

@@ -7,12 +7,13 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/openapi"
) )
@@ -30,7 +31,7 @@ func TestSetCommand(t *testing.T) {
{ {
name: "set replicas", name: "set replicas",
args: []string{"replicas", "4", "--description", "hi there", "--set-by", "pw"}, args: []string{"replicas", "4", "--description", "hi there", "--set-by", "pw"},
out: "set 1 fields\n", out: "set 1 field(s)\n",
inputOpenAPI: ` inputOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -126,7 +127,7 @@ spec:
{ {
name: "set replicas no description", name: "set replicas no description",
args: []string{"replicas", "4"}, args: []string{"replicas", "4"},
out: "set 1 fields\n", out: "set 1 field(s)\n",
inputOpenAPI: ` inputOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -174,7 +175,7 @@ spec:
{ {
name: "set image with value", name: "set image with value",
args: []string{"tag", "1.8.1"}, args: []string{"tag", "1.8.1"},
out: "set 1 fields\n", out: "set 1 field(s)\n",
inputOpenAPI: ` inputOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -509,7 +510,7 @@ list in body should have at most 2 items`,
{ {
name: "set replicas with value set by flag", name: "set replicas with value set by flag",
args: []string{"replicas", "--values", "4", "--description", "hi there"}, args: []string{"replicas", "--values", "4", "--description", "hi there"},
out: "set 1 fields\n", out: "set 1 field(s)\n",
inputOpenAPI: ` inputOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -605,7 +606,7 @@ spec:
{ {
name: "openAPI list values set by flag success", name: "openAPI list values set by flag success",
args: []string{"list", "--values", "10", "--values", "11"}, args: []string{"list", "--values", "10", "--values", "11"},
out: "set 1 fields\n", out: "set 1 field(s)\n",
inputOpenAPI: ` inputOpenAPI: `
kind: Kptfile kind: Kptfile
openAPI: openAPI:
@@ -709,7 +710,7 @@ list in body should have at most 2 items`,
{ {
name: "nested substitution", name: "nested substitution",
args: []string{"my-image-setter", "ubuntu"}, args: []string{"my-image-setter", "ubuntu"},
out: "set 2 fields\n", out: "set 2 field(s)\n",
inputOpenAPI: ` inputOpenAPI: `
apiVersion: v1alpha1 apiVersion: v1alpha1
kind: Example kind: Example
@@ -1013,22 +1014,19 @@ spec:
openapi.ResetOpenAPI() openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI() defer openapi.ResetOpenAPI()
f, err := ioutil.TempFile("", "k8s-cli-") baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
defer os.Remove(f.Name()) defer os.RemoveAll(baseDir)
err = ioutil.WriteFile(f.Name(), []byte(test.inputOpenAPI), 0600)
f := filepath.Join(baseDir, "Krmfile")
err = ioutil.WriteFile(f, []byte(test.inputOpenAPI), 0600)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
old := ext.GetOpenAPIFile
defer func() { ext.GetOpenAPIFile = old }()
ext.GetOpenAPIFile = func(args []string) (s string, err error) {
return f.Name(), nil
}
r, err := ioutil.TempFile("", "k8s-cli-*.yaml") r, err := ioutil.TempFile(baseDir, "k8s-cli-*.yaml")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -1041,7 +1039,7 @@ spec:
runner := commands.NewSetRunner("") runner := commands.NewSetRunner("")
out := &bytes.Buffer{} out := &bytes.Buffer{}
runner.Command.SetOut(out) runner.Command.SetOut(out)
runner.Command.SetArgs(append([]string{r.Name()}, test.args...)) runner.Command.SetArgs(append([]string{baseDir}, test.args...))
err = runner.Command.Execute() err = runner.Command.Execute()
if test.errMsg != "" { if test.errMsg != "" {
if !assert.NotNil(t, err) { if !assert.NotNil(t, err) {
@@ -1056,7 +1054,7 @@ spec:
t.FailNow() t.FailNow()
} }
if test.errMsg == "" && !assert.Equal(t, test.out, out.String()) { if test.errMsg == "" && !assert.Contains(t, out.String(), strings.TrimSpace(test.out)) {
t.FailNow() t.FailNow()
} }
@@ -1070,7 +1068,7 @@ spec:
t.FailNow() t.FailNow()
} }
actualOpenAPI, err := ioutil.ReadFile(f.Name()) actualOpenAPI, err := ioutil.ReadFile(f)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -1082,3 +1080,82 @@ spec:
}) })
} }
} }
func TestSetSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "set-recurse-subpackages",
dataset: "dataset-with-setters",
args: []string{"namespace", "otherspace", "-R"},
expected: `${baseDir}/mysql/
set 1 field(s)
${baseDir}/mysql/nosetters/
setter "namespace" is not found
${baseDir}/mysql/storage/
set 1 field(s)
`,
},
{
name: "set-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-with-setters",
packagePath: "mysql",
args: []string{"namespace", "otherspace"},
expected: `${baseDir}/mysql/
set 1 field(s)
`,
},
{
name: "set-nested-pkg-no-recurse-subpackages",
dataset: "dataset-with-setters",
packagePath: "mysql/storage",
args: []string{"namespace", "otherspace"},
expected: `${baseDir}/mysql/storage/
set 1 field(s)
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := commands.NewSetRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(
strings.Replace(expected, "\\", "/", -1),
"//", "/", -1)
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
t.FailNow()
}
})
}
}

View File

@@ -182,7 +182,7 @@ items:
kind: Deployment kind: Deployment
metadata: metadata:
name: mysql-deployment name: mysql-deployment
namespace: myspace # {"$openapi":"namespace"} namespace: myspace
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml' config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml'
@@ -192,7 +192,7 @@ items:
spec: spec:
containers: containers:
- name: mysql - name: mysql
image: mysql:1.7.9 # {"$openapi":"image-tag"} image: mysql:1.7.9
- apiVersion: apps/v1 - apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -212,7 +212,7 @@ items:
kind: Deployment kind: Deployment
metadata: metadata:
name: storage-deployment name: storage-deployment
namespace: myspace # {"$openapi":"namespace"} namespace: myspace
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml' config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml'

View File

@@ -5,9 +5,11 @@ package commands
import ( import (
"fmt" "fmt"
"io"
"sort" "sort"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/sets" "sigs.k8s.io/kustomize/kyaml/sets"
@@ -17,18 +19,18 @@ import (
func GetCountRunner(name string) *CountRunner { func GetCountRunner(name string) *CountRunner {
r := &CountRunner{} r := &CountRunner{}
c := &cobra.Command{ c := &cobra.Command{
Use: "count DIR...", Use: "count [DIR]",
Args: cobra.MaximumNArgs(1),
Short: commands.CountShort, Short: commands.CountShort,
Long: commands.CountLong, Long: commands.CountLong,
Example: commands.CountExamples, Example: commands.CountExamples,
RunE: r.runE, RunE: r.runE,
} }
fixDocs(name, c) fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
c.Flags().BoolVar(&r.Kind, "kind", true, c.Flags().BoolVar(&r.Kind, "kind", true,
"count resources by kind.") "count resources by kind.")
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
"prints count of resources recursively in all the nested subpackages")
r.Command = c r.Command = c
return r return r
} }
@@ -42,20 +44,56 @@ type CountRunner struct {
IncludeSubpackages bool IncludeSubpackages bool
Kind bool Kind bool
Command *cobra.Command Command *cobra.Command
RecurseSubPackages bool
} }
func (r *CountRunner) runE(c *cobra.Command, args []string) error { func (r *CountRunner) runE(c *cobra.Command, args []string) error {
var inputs []kio.Reader if len(args) == 0 {
for _, a := range args { input := &kio.ByteReader{Reader: c.InOrStdin()}
inputs = append(inputs, kio.LocalPackageReader{
PackagePath: a, return handleError(c, kio.Pipeline{
IncludeSubpackages: r.IncludeSubpackages, Inputs: []kio.Reader{input},
}) Outputs: r.out(c.OutOrStdout()),
} }.Execute())
if len(inputs) == 0 {
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
} }
e := executeCmdOnPkgs{
writer: c.OutOrStdout(),
needOpenAPI: false,
recurseSubPackages: r.RecurseSubPackages,
cmdRunner: r,
rootPkgPath: args[0],
}
return e.execute()
}
func (r *CountRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
input := kio.LocalPackageReader{PackagePath: pkgPath, PackageFileName: openAPIFileName}
err = kio.Pipeline{
Inputs: []kio.Reader{input},
Outputs: r.out(w),
}.Execute()
if err != nil {
// return err if there is only package
if !r.RecurseSubPackages {
return err
} else {
// print error message and continue if there are multiple packages to annotate
fmt.Fprintf(w, "%s\n", err.Error())
}
}
return nil
}
func (r *CountRunner) out(w io.Writer) []kio.Writer {
var out []kio.Writer var out []kio.Writer
if r.Kind { if r.Kind {
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error { out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
@@ -69,20 +107,15 @@ func (r *CountRunner) runE(c *cobra.Command, args []string) error {
order := k.List() order := k.List()
sort.Strings(order) sort.Strings(order)
for _, k := range order { for _, k := range order {
fmt.Fprintf(c.OutOrStdout(), "%s: %d\n", k, count[k]) fmt.Fprintf(w, "%s: %d\n", k, count[k])
} }
return nil return nil
})) }))
} else { } else {
out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error { out = append(out, kio.WriterFunc(func(nodes []*yaml.RNode) error {
fmt.Fprintf(c.OutOrStdout(), "%d\n", len(nodes)) fmt.Fprintf(w, "%d\n", len(nodes))
return nil return nil
})) }))
} }
return handleError(c, kio.Pipeline{ return out
Inputs: inputs,
Outputs: out,
}.Execute())
} }

View File

@@ -8,10 +8,13 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/openapi"
) )
func TestCountCommand_files(t *testing.T) { func TestCountCommand_files(t *testing.T) {
@@ -67,7 +70,80 @@ spec:
return return
} }
if !assert.Equal(t, "Deployment: 2\nService: 1\n", b.String()) { if !assert.Contains(t, b.String(), "Deployment: 2\nService: 1\n") {
return return
} }
} }
func TestCountSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "count-recurse-subpackages",
dataset: "dataset-without-setters",
expected: `${baseDir}/mysql/
Deployment: 1
${baseDir}/mysql/storage/
Deployment: 1
`,
},
{
name: "count-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
args: []string{"-R=false"},
packagePath: "mysql",
expected: `${baseDir}/mysql/
Deployment: 1
`,
},
{
name: "count-nested-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql/storage",
args: []string{"-R=false"},
expected: `${baseDir}/mysql/storage/
Deployment: 1
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := commands.GetCountRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
t.FailNow()
}
})
}
}

View File

@@ -4,7 +4,11 @@
package commands package commands
import ( import (
"fmt"
"io"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters" "sigs.k8s.io/kustomize/kyaml/kio/filters"
@@ -33,6 +37,8 @@ formatting substitution verbs {'%n': 'metadata.name', '%s': 'metadata.namespace'
`if true, override existing filepath annotations.`) `if true, override existing filepath annotations.`)
c.Flags().BoolVar(&r.UseSchema, "use-schema", false, c.Flags().BoolVar(&r.UseSchema, "use-schema", false,
`if true, uses openapi resource schema to format resources.`) `if true, uses openapi resource schema to format resources.`)
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", false,
"formats resource files recursively in all the nested subpackages")
r.Command = c r.Command = c
return r return r
} }
@@ -49,6 +55,7 @@ type FmtRunner struct {
KeepAnnotations bool KeepAnnotations bool
Override bool Override bool
UseSchema bool UseSchema bool
RecurseSubPackages bool
} }
func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error { func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error {
@@ -59,17 +66,6 @@ func (r *FmtRunner) preRunE(c *cobra.Command, args []string) error {
} }
func (r *FmtRunner) runE(c *cobra.Command, args []string) error { func (r *FmtRunner) runE(c *cobra.Command, args []string) error {
f := []kio.Filter{filters.FormatFilter{
UseSchema: r.UseSchema,
}}
// format with file names
if r.SetFilenames {
f = append(f, &filters.FileSetter{
FilenamePattern: r.FilenamePattern,
Override: r.Override,
})
}
// format stdin if there are no args // format stdin if there are no args
if len(args) == 0 { if len(args) == 0 {
@@ -79,20 +75,64 @@ func (r *FmtRunner) runE(c *cobra.Command, args []string) error {
KeepReaderAnnotations: r.KeepAnnotations, KeepReaderAnnotations: r.KeepAnnotations,
} }
return handleError(c, kio.Pipeline{ return handleError(c, kio.Pipeline{
Inputs: []kio.Reader{rw}, Filters: f, Outputs: []kio.Writer{rw}}.Execute()) Inputs: []kio.Reader{rw}, Filters: r.fmtFilters(), Outputs: []kio.Writer{rw}}.Execute())
} }
for i := range args { for _, rootPkgPath := range args {
path := args[i] e := executeCmdOnPkgs{
rw := &kio.LocalPackageReadWriter{ writer: c.OutOrStdout(),
NoDeleteFiles: true, needOpenAPI: false,
PackagePath: path, recurseSubPackages: r.RecurseSubPackages,
KeepReaderAnnotations: r.KeepAnnotations} cmdRunner: r,
err := kio.Pipeline{ rootPkgPath: rootPkgPath,
Inputs: []kio.Reader{rw}, Filters: f, Outputs: []kio.Writer{rw}}.Execute() }
err := e.execute()
if err != nil { if err != nil {
return handleError(c, err) return err
} }
} }
return nil return nil
} }
func (r *FmtRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
rw := &kio.LocalPackageReadWriter{
NoDeleteFiles: true,
PackagePath: pkgPath,
KeepReaderAnnotations: r.KeepAnnotations, PackageFileName: openAPIFileName}
err = kio.Pipeline{
Inputs: []kio.Reader{rw}, Filters: r.fmtFilters(), Outputs: []kio.Writer{rw}}.Execute()
if err != nil {
// return err if RecurseSubPackages is false
if !r.RecurseSubPackages {
return err
} else {
// print error message and continue if RecurseSubPackages is true
fmt.Fprintf(w, "%s\n", err.Error())
}
} else {
fmt.Fprint(w, "formatted resource files in the package\n")
}
return nil
}
func (r *FmtRunner) fmtFilters() []kio.Filter {
fmtFilters := []kio.Filter{filters.FormatFilter{
UseSchema: r.UseSchema,
}}
// format with file names
if r.SetFilenames {
fmtFilters = append(fmtFilters, &filters.FileSetter{
FilenamePattern: r.FilenamePattern,
Override: r.Override,
})
}
return fmtFilters
}

View File

@@ -7,12 +7,15 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
"sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml" "sigs.k8s.io/kustomize/kyaml/kio/filters/testyaml"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/testutil" "sigs.k8s.io/kustomize/kyaml/testutil"
) )
@@ -163,3 +166,78 @@ func TestCmd_failFileContents(t *testing.T) {
// expect an error // expect an error
assert.EqualError(t, err, "yaml: line 1: did not find expected node content") assert.EqualError(t, err, "yaml: line 1: did not find expected node content")
} }
func TestFmtSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "fmt-recurse-subpackages",
dataset: "dataset-with-setters",
args: []string{"-R"},
expected: `${baseDir}/mysql/
formatted resource files in the package
${baseDir}/mysql/nosetters/
formatted resource files in the package
${baseDir}/mysql/storage/
formatted resource files in the package
`,
},
{
name: "fmt-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql",
expected: `${baseDir}/mysql/
formatted resource files in the package
`,
},
{
name: "fmt-nested-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql/storage",
expected: `${baseDir}/mysql/storage/
formatted resource files in the package
`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
// reset the openAPI afterward
openapi.ResetOpenAPI()
defer openapi.ResetOpenAPI()
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := commands.GetFmtRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append([]string{filepath.Join(baseDir, test.packagePath)}, test.args...))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
t.FailNow()
}
})
}
}

View File

@@ -5,10 +5,12 @@ package commands
import ( import (
"fmt" "fmt"
"io"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters" "sigs.k8s.io/kustomize/kyaml/kio/filters"
@@ -18,22 +20,21 @@ import (
func GetGrepRunner(name string) *GrepRunner { func GetGrepRunner(name string) *GrepRunner {
r := &GrepRunner{} r := &GrepRunner{}
c := &cobra.Command{ c := &cobra.Command{
Use: "grep QUERY [DIR]...", Use: "grep QUERY [DIR]",
Short: commands.GrepShort, Short: commands.GrepShort,
Long: commands.GrepLong, Long: commands.GrepLong,
Example: commands.GrepExamples, Example: commands.GrepExamples,
PreRunE: r.preRunE, PreRunE: r.preRunE,
RunE: r.runE, RunE: r.runE,
Args: cobra.MinimumNArgs(1), Args: cobra.MaximumNArgs(2),
} }
fixDocs(name, c) fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
c.Flags().BoolVar(&r.KeepAnnotations, "annotate", true, c.Flags().BoolVar(&r.KeepAnnotations, "annotate", true,
"annotate resources with their file origins.") "annotate resources with their file origins.")
c.Flags().BoolVarP(&r.InvertMatch, "invert-match", "", false, c.Flags().BoolVarP(&r.InvertMatch, "invert-match", "", false,
"Selected Resources are those not matching any of the specified patterns..") "Selected Resources are those not matching any of the specified patterns..")
c.Flags().BoolVarP(&r.RecurseSubPackages, "recurse-subpackages", "R", true,
"also print resources recursively in all the nested subpackages")
r.Command = c r.Command = c
return r return r
} }
@@ -44,11 +45,11 @@ func GrepCommand(name string) *cobra.Command {
// GrepRunner contains the run function // GrepRunner contains the run function
type GrepRunner struct { type GrepRunner struct {
IncludeSubpackages bool
KeepAnnotations bool KeepAnnotations bool
Command *cobra.Command Command *cobra.Command
filters.GrepFilter filters.GrepFilter
Format bool Format bool
RecurseSubPackages bool
} }
func (r *GrepRunner) preRunE(c *cobra.Command, args []string) error { func (r *GrepRunner) preRunE(c *cobra.Command, args []string) error {
@@ -101,25 +102,57 @@ func (r *GrepRunner) preRunE(c *cobra.Command, args []string) error {
} }
func (r *GrepRunner) runE(c *cobra.Command, args []string) error { func (r *GrepRunner) runE(c *cobra.Command, args []string) error {
var filters = []kio.Filter{r.GrepFilter} if len(args) == 1 {
input := &kio.ByteReader{Reader: c.InOrStdin()}
var inputs []kio.Reader
for _, a := range args[1:] {
inputs = append(inputs, kio.LocalPackageReader{
PackagePath: a,
IncludeSubpackages: r.IncludeSubpackages,
})
}
if len(inputs) == 0 {
inputs = append(inputs, &kio.ByteReader{Reader: c.InOrStdin()})
}
return handleError(c, kio.Pipeline{ return handleError(c, kio.Pipeline{
Inputs: inputs, Inputs: []kio.Reader{input},
Filters: filters, Filters: []kio.Filter{r.GrepFilter},
Outputs: []kio.Writer{kio.ByteWriter{ Outputs: []kio.Writer{kio.ByteWriter{
Writer: c.OutOrStdout(), Writer: c.OutOrStdout(),
KeepReaderAnnotations: r.KeepAnnotations, KeepReaderAnnotations: r.KeepAnnotations,
}}, }},
}.Execute()) }.Execute())
} }
e := executeCmdOnPkgs{
writer: c.OutOrStdout(),
needOpenAPI: false,
recurseSubPackages: r.RecurseSubPackages,
cmdRunner: r,
rootPkgPath: args[1],
skipPkgPathPrint: true,
}
return e.execute()
}
func (r *GrepRunner) executeCmd(w io.Writer, pkgPath string) error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
input := kio.LocalPackageReader{PackagePath: pkgPath, PackageFileName: openAPIFileName}
err = kio.Pipeline{
Inputs: []kio.Reader{input},
Filters: []kio.Filter{r.GrepFilter},
Outputs: []kio.Writer{kio.ByteWriter{
Writer: w,
KeepReaderAnnotations: r.KeepAnnotations,
}},
}.Execute()
if err != nil {
// return err if there is only package
if !r.RecurseSubPackages {
return err
} else {
// print error message and continue if there are multiple packages to annotate
fmt.Fprintf(w, "%s\n", err.Error())
}
}
fmt.Fprintf(w, "---")
return nil
}

View File

@@ -8,10 +8,12 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/cmd/config/internal/commands" "sigs.k8s.io/kustomize/cmd/config/internal/commands"
"sigs.k8s.io/kustomize/kyaml/copyutil"
) )
// TestGrepCommand_files verifies grep reads the files and filters them // TestGrepCommand_files verifies grep reads the files and filters them
@@ -68,7 +70,7 @@ spec:
return return
} }
if !assert.Equal(t, `kind: Deployment if !assert.Contains(t, b.String(), `kind: Deployment
metadata: metadata:
labels: labels:
app: nginx2 app: nginx2
@@ -90,7 +92,7 @@ metadata:
spec: spec:
selector: selector:
app: nginx app: nginx
`, b.String()) { `) {
return return
} }
} }
@@ -136,7 +138,7 @@ spec:
return return
} }
if !assert.Equal(t, `kind: Deployment if !assert.Contains(t, b.String(), `kind: Deployment
metadata: metadata:
labels: labels:
app: nginx2 app: nginx2
@@ -156,7 +158,7 @@ metadata:
spec: spec:
selector: selector:
app: nginx app: nginx
`, b.String()) { `) {
return return
} }
} }
@@ -250,7 +252,7 @@ spec:
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
return return
} }
if !assert.Equal(t, `kind: Deployment if !assert.Contains(t, b.String(), `kind: Deployment
metadata: metadata:
labels: labels:
app: nginx1.7 app: nginx1.7
@@ -264,7 +266,142 @@ spec:
containers: containers:
- name: nginx - name: nginx
image: nginx:1.7.9 image: nginx:1.7.9
`, b.String()) { `) {
return return
} }
} }
func TestGrepSubPackages(t *testing.T) {
var tests = []struct {
name string
dataset string
packagePath string
args []string
expected string
}{
{
name: "grep-recurse-subpackages",
dataset: "dataset-without-setters",
args: []string{"kind=Deployment"},
expected: `# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: myspace
name: mysql-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'deployment.yaml'
spec:
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:1.7.9
---
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: myspace
name: storage-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'deployment.yaml'
spec:
replicas: 4
template:
spec:
containers:
- name: storage
image: storage:1.7.7
---`,
},
{
name: "grep-top-level-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
args: []string{"kind=Deployment", "-R=false"},
packagePath: "mysql",
expected: `# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: myspace
name: mysql-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'deployment.yaml'
spec:
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:1.7.9
---`,
},
{
name: "grep-nested-pkg-no-recurse-subpackages",
dataset: "dataset-without-setters",
packagePath: "mysql/storage",
args: []string{"kind=Deployment", "-R=false"},
expected: `# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: myspace
name: storage-deployment
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'deployment.yaml'
spec:
replicas: 4
template:
spec:
containers:
- name: storage
image: storage:1.7.7
---`,
},
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
sourceDir := filepath.Join("test", "testdata", test.dataset)
baseDir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
copyutil.CopyDir(sourceDir, baseDir)
defer os.RemoveAll(baseDir)
runner := commands.GetGrepRunner("")
actual := &bytes.Buffer{}
runner.Command.SetOut(actual)
runner.Command.SetArgs(append(test.args, filepath.Join(baseDir, test.packagePath)))
err = runner.Command.Execute()
if !assert.NoError(t, err) {
t.FailNow()
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
t.FailNow()
}
})
}
}

View File

@@ -9,11 +9,13 @@ import (
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil" "sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/runfn" "sigs.k8s.io/kustomize/kyaml/runfn"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
) )
// GetCatRunner returns a RunFnRunner. // GetCatRunner returns a RunFnRunner.
@@ -60,11 +62,14 @@ func GetRunFnRunner(name string) *RunFnRunner {
r.Command.Flags().BoolVar( r.Command.Flags().BoolVar(
&r.Network, "network", false, "enable network access for functions that declare it") &r.Network, "network", false, "enable network access for functions that declare it")
r.Command.Flags().StringVar(
&r.NetworkName, "network-name", "bridge", "the docker network to run the container in")
r.Command.Flags().StringArrayVar( r.Command.Flags().StringArrayVar(
&r.Mounts, "mount", []string{}, &r.Mounts, "mount", []string{},
"a list of storage options read from the filesystem") "a list of storage options read from the filesystem")
r.Command.Flags().BoolVar(
&r.LogSteps, "log-steps", false, "log steps to stderr")
r.Command.Flags().StringArrayVarP(
&r.Env, "env", "e", []string{},
"a list of environment variables to be used by functions")
return r return r
} }
@@ -89,8 +94,9 @@ type RunFnRunner struct {
RunFns runfn.RunFns RunFns runfn.RunFns
ResultsDir string ResultsDir string
Network bool Network bool
NetworkName string
Mounts []string Mounts []string
LogSteps bool
Env []string
} }
func (r *RunFnRunner) runE(c *cobra.Command, args []string) error { func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
@@ -99,7 +105,7 @@ func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
// getContainerFunctions parses the commandline flags and arguments into explicit // getContainerFunctions parses the commandline flags and arguments into explicit
// Functions to run. // Functions to run.
func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []string) ( func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, dataItems []string) (
[]*yaml.RNode, error) { []*yaml.RNode, error) {
if r.Image == "" && r.StarPath == "" && r.ExecPath == "" && r.StarURL == "" { if r.Image == "" && r.StarPath == "" && r.ExecPath == "" && r.StarURL == "" {
@@ -124,8 +130,8 @@ func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []
} }
if r.Network { if r.Network {
err = fn.PipeE( err = fn.PipeE(
yaml.LookupCreate(yaml.MappingNode, "container", "network"), yaml.Lookup("container"),
yaml.SetField("required", yaml.NewScalarRNode("true"))) yaml.SetField("network", yaml.NewScalarRNode("true")))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -193,7 +199,7 @@ data: {}
} }
err = rc.PipeE( err = rc.PipeE(
yaml.LookupCreate(yaml.MappingNode, "metadata", "annotations"), yaml.LookupCreate(yaml.MappingNode, "metadata", "annotations"),
yaml.SetField("config.kubernetes.io/function", yaml.NewScalarRNode(value))) yaml.SetField(runtimeutil.FunctionAnnotationKey, yaml.NewScalarRNode(value)))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -267,7 +273,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
return errors.Errorf("0 or 1 arguments supported, function arguments go after '--'") return errors.Errorf("0 or 1 arguments supported, function arguments go after '--'")
} }
fns, err := r.getContainerFunctions(c, args, dataItems) fns, err := r.getContainerFunctions(c, dataItems)
if err != nil { if err != nil {
return err return err
} }
@@ -300,11 +306,12 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
Input: input, Input: input,
Path: path, Path: path,
Network: r.Network, Network: r.Network,
NetworkName: r.NetworkName,
EnableStarlark: r.EnableStar, EnableStarlark: r.EnableStar,
EnableExec: r.EnableExec, EnableExec: r.EnableExec,
StorageMounts: storageMounts, StorageMounts: storageMounts,
ResultsDir: r.ResultsDir, ResultsDir: r.ResultsDir,
LogSteps: r.LogSteps,
Env: r.Env,
} }
// don't consider args for the function // don't consider args for the function

View File

@@ -11,6 +11,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/runfn" "sigs.k8s.io/kustomize/kyaml/runfn"
) )
@@ -28,7 +29,6 @@ func TestRunFnCommand_preRunE(t *testing.T) {
output io.Writer output io.Writer
functionPaths []string functionPaths []string
network bool network bool
networkName string
mount []string mount []string
}{ }{
{ {
@@ -98,13 +98,12 @@ apiVersion: v1
args: []string{"run", "dir", "--image", "foo:bar", "--network"}, args: []string{"run", "dir", "--image", "foo:bar", "--network"},
path: "dir", path: "dir",
network: true, network: true,
networkName: "bridge",
expected: ` expected: `
metadata: metadata:
name: function-input name: function-input
annotations: annotations:
config.kubernetes.io/function: | config.kubernetes.io/function: |
container: {image: 'foo:bar', network: {required: true}} container: {image: 'foo:bar', network: true}
data: {} data: {}
kind: ConfigMap kind: ConfigMap
apiVersion: v1 apiVersion: v1
@@ -112,16 +111,15 @@ apiVersion: v1
}, },
{ {
name: "with network name", name: "with network name",
args: []string{"run", "dir", "--image", "foo:bar", "--network", "--network-name", "foo"}, args: []string{"run", "dir", "--image", "foo:bar", "--network"},
path: "dir", path: "dir",
network: true, network: true,
networkName: "foo",
expected: ` expected: `
metadata: metadata:
name: function-input name: function-input
annotations: annotations:
config.kubernetes.io/function: | config.kubernetes.io/function: |
container: {image: 'foo:bar', network: {required: true}} container: {image: 'foo:bar', network: true}
data: {} data: {}
kind: ConfigMap kind: ConfigMap
apiVersion: v1 apiVersion: v1
@@ -201,8 +199,8 @@ apiVersion: v1
path: "dir", path: "dir",
expectedStruct: &runfn.RunFns{ expectedStruct: &runfn.RunFns{
Path: "dir", Path: "dir",
NetworkName: "bridge",
EnableStarlark: true, EnableStarlark: true,
Env: []string{},
}, },
}, },
{ {
@@ -254,8 +252,8 @@ apiVersion: v1
path: "dir", path: "dir",
expectedStruct: &runfn.RunFns{ expectedStruct: &runfn.RunFns{
Path: "dir", Path: "dir",
NetworkName: "bridge",
ResultsDir: "foo/", ResultsDir: "foo/",
Env: []string{},
}, },
expected: ` expected: `
metadata: metadata:
@@ -283,6 +281,25 @@ apiVersion: v1
args: []string{"run", "dir", "--image", "foo:bar", "--", "a=b", "c", "e=f"}, args: []string{"run", "dir", "--image", "foo:bar", "--", "a=b", "c", "e=f"},
err: "must have keys and values separated by", err: "must have keys and values separated by",
}, },
{
name: "log steps",
args: []string{"run", "dir", "--log-steps"},
path: "dir",
expectedStruct: &runfn.RunFns{
Path: "dir",
LogSteps: true,
Env: []string{},
},
},
{
name: "envs",
args: []string{"run", "dir", "--env", "FOO=BAR", "-e", "BAR"},
path: "dir",
expectedStruct: &runfn.RunFns{
Path: "dir",
Env: []string{"FOO=BAR", "BAR"},
},
},
} }
for i := range tests { for i := range tests {
@@ -338,9 +355,6 @@ apiVersion: v1
if !assert.Equal(t, tt.network, r.RunFns.Network) { if !assert.Equal(t, tt.network, r.RunFns.Network) {
t.FailNow() t.FailNow()
} }
if !assert.Equal(t, tt.networkName, r.RunFns.NetworkName) {
t.FailNow()
}
} else { } else {
if !assert.Equal(t, false, r.RunFns.Network) { if !assert.Equal(t, false, r.RunFns.Network) {
t.FailNow() t.FailNow()
@@ -385,5 +399,4 @@ apiVersion: v1
}) })
} }
} }

View File

@@ -0,0 +1,6 @@
apiVersion: krm.dev/v1alpha1
kind: Krmfile
metadata:
name: mysql
packageMetadata:
shortDescription: sample description

View File

@@ -0,0 +1,15 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: myspace
name: mysql-deployment
spec:
replicas: 3
template:
spec:
containers:
- name: mysql
image: mysql:1.7.9

View File

@@ -0,0 +1,6 @@
apiVersion: krm.dev/v1alpha1
kind: Krmfile
metadata:
name: storage
packageMetadata:
shortDescription: sample description

View File

@@ -0,0 +1,15 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: myspace
name: storage-deployment
spec:
replicas: 4
template:
spec:
containers:
- name: storage
image: storage:1.7.7

View File

@@ -7,6 +7,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/kio/filters" "sigs.k8s.io/kustomize/kyaml/kio/filters"
@@ -26,8 +27,6 @@ func GetTreeRunner(name string) *TreeRunner {
Args: cobra.MaximumNArgs(1), Args: cobra.MaximumNArgs(1),
} }
fixDocs(name, c) fixDocs(name, c)
c.Flags().BoolVar(&r.IncludeSubpackages, "include-subpackages", true,
"also print resources from subpackages.")
// TODO(pwittrock): Figure out if these are the right things to expose, and consider making it // TODO(pwittrock): Figure out if these are the right things to expose, and consider making it
// a list of options instead of individual flags // a list of options instead of individual flags
@@ -59,7 +58,6 @@ func TreeCommand(name string) *cobra.Command {
// TreeRunner contains the run function // TreeRunner contains the run function
type TreeRunner struct { type TreeRunner struct {
IncludeSubpackages bool
Command *cobra.Command Command *cobra.Command
name bool name bool
resources bool resources bool
@@ -79,9 +77,14 @@ type TreeRunner struct {
func (r *TreeRunner) runE(c *cobra.Command, args []string) error { func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
var input kio.Reader var input kio.Reader
var root = "." var root = "."
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
matchFilesGlob := append([]string{openAPIFileName}, kio.DefaultMatch...)
if len(args) == 1 { if len(args) == 1 {
root = filepath.Clean(args[0]) root = filepath.Clean(args[0])
input = kio.LocalPackageReader{PackagePath: args[0]} input = kio.LocalPackageReader{PackagePath: args[0], MatchFilesGlob: matchFilesGlob}
} else { } else {
input = &kio.ByteReader{Reader: c.InOrStdin()} input = &kio.ByteReader{Reader: c.InOrStdin()}
} }
@@ -159,7 +162,9 @@ func (r *TreeRunner) runE(c *cobra.Command, args []string) error {
Root: root, Root: root,
Writer: c.OutOrStdout(), Writer: c.OutOrStdout(),
Fields: fields, Fields: fields,
Structure: kio.TreeStructure(r.structure)}}, Structure: kio.TreeStructure(r.structure),
OpenAPIFileName: openAPIFileName,
}},
}.Execute()) }.Execute())
} }

View File

@@ -90,6 +90,109 @@ spec:
} }
} }
func TestTreeCommand_subpkgs(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize-tree-test")
defer os.RemoveAll(d)
if !assert.NoError(t, err) {
t.FailNow()
}
err = os.MkdirAll(filepath.Join(d, "subpkg"), 0700)
if !assert.NoError(t, err) {
t.FailNow()
}
err = ioutil.WriteFile(filepath.Join(d, "f1.yaml"), []byte(`
apiVersion: v1
kind: Abstraction
metadata:
name: foo
configFn:
container:
image: gcr.io/example/reconciler:v1
annotations:
config.kubernetes.io/local-config: "true"
spec:
replicas: 1
---
kind: Deployment
metadata:
labels:
app: nginx2
name: foo
annotations:
app: nginx2
spec:
replicas: 1
---
kind: Service
metadata:
name: foo
annotations:
app: nginx
spec:
selector:
app: nginx
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "subpkg", "f2.yaml"), []byte(`kind: Deployment
metadata:
labels:
app: nginx
name: bar
annotations:
app: nginx
spec:
replicas: 3
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "Krmfile"), []byte(`apiVersion: kpt.dev/v1alpha1
kind: Krmfile
metadata:
name: mainpkg
openAPI:
definitions:
`), 0600)
if !assert.NoError(t, err) {
return
}
err = ioutil.WriteFile(filepath.Join(d, "subpkg", "Krmfile"), []byte(`apiVersion: kpt.dev/v1alpha1
kind: Krmfile
metadata:
name: subpkg
openAPI:
definitions:
`), 0600)
if !assert.NoError(t, err) {
return
}
// fmt the files
b := &bytes.Buffer{}
r := commands.GetTreeRunner("")
r.Command.SetArgs([]string{d})
r.Command.SetOut(b)
if !assert.NoError(t, r.Command.Execute()) {
return
}
if !assert.Equal(t, fmt.Sprintf(`%s
├── [Krmfile] Krmfile mainpkg
├── [f1.yaml] Deployment foo
├── [f1.yaml] Service foo
└── Pkg: subpkg
├── [Krmfile] Krmfile subpkg
└── [f2.yaml] Deployment bar
`, d), b.String()) {
return
}
}
func TestTreeCommand_stdin(t *testing.T) { func TestTreeCommand_stdin(t *testing.T) {
// fmt the files // fmt the files
b := &bytes.Buffer{} b := &bytes.Buffer{}

View File

@@ -5,13 +5,91 @@ package commands
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/pathutil"
) )
// cmdRunner interface holds executeCmd definition which executes respective command's
// implementation on single package
type cmdRunner interface {
executeCmd(w io.Writer, pkgPath string) error
}
// executeCmdOnPkgs struct holds the parameters necessary to
// execute the filter command on packages in rootPkgPath
type executeCmdOnPkgs struct {
rootPkgPath string
recurseSubPackages bool
needOpenAPI bool
cmdRunner cmdRunner
writer io.Writer
skipPkgPathPrint bool
}
// executeCmdOnPkgs takes the function definition for a command to be executed on single package, applies that definition
// recursively on all the subpackages present in rootPkgPath if recurseSubPackages is true, else applies the command on rootPkgPath only
func (e executeCmdOnPkgs) execute() error {
openAPIFileName, err := ext.OpenAPIFileName()
if err != nil {
return err
}
pkgsPaths, err := pathutil.DirsWithFile(e.rootPkgPath, openAPIFileName, e.recurseSubPackages)
if err != nil {
return err
}
if len(pkgsPaths) == 0 {
// at this point, there are no openAPI files in the rootPkgPath
if e.needOpenAPI {
// few executions need openAPI file to be present(ex: setters commands), if true throw an error
return errors.Errorf("unable to find %q in package %q", openAPIFileName, e.rootPkgPath)
}
// add root path for commands which doesn't need openAPI(ex: annotate, fmt)
pkgsPaths = []string{e.rootPkgPath}
}
for i := range pkgsPaths {
pkgPath := pkgsPaths[i]
// Add schema present in openAPI file for current package
if e.needOpenAPI {
if err := openapi.AddSchemaFromFile(filepath.Join(pkgPath, openAPIFileName)); err != nil {
return err
}
}
if !e.skipPkgPathPrint {
fmt.Fprintf(e.writer, "%s/\n", pkgPath)
}
err := e.cmdRunner.executeCmd(e.writer, pkgPath)
if err != nil {
return err
}
if i != len(pkgsPaths)-1 {
fmt.Fprint(e.writer, "\n")
}
// Delete schema present in openAPI file for current package
if e.needOpenAPI {
if err := openapi.DeleteSchemaInFile(filepath.Join(pkgPath, openAPIFileName)); err != nil {
return err
}
}
}
return nil
}
// parseFieldPath parse a flag value into a field path // parseFieldPath parse a flag value into a field path
func parseFieldPath(path string) ([]string, error) { func parseFieldPath(path string) ([]string, error) {
// fixup '\.' so we don't split on it // fixup '\.' so we don't split on it

View File

@@ -0,0 +1,184 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package commands
import (
"bytes"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/errors"
)
func TestExecuteCmdOnPkgs(t *testing.T) {
var tests = []struct {
name string
recurse bool
pkgPath string
needOpenAPI bool
errMsg string
expectedOut string
}{
{
name: "need_Krmfile_error",
recurse: true,
needOpenAPI: true,
pkgPath: "subpkg1/subdir1",
errMsg: `unable to find "Krmfile" in package`,
},
{
name: "Krmfile_not_needed_no_err",
recurse: true,
needOpenAPI: false,
pkgPath: "subpkg1/subdir1",
expectedOut: `${baseDir}/subpkg1/subdir1/
`,
},
{
name: "executeCmd_returns_error",
recurse: true,
needOpenAPI: false,
pkgPath: "subpkg4",
expectedOut: `${baseDir}/subpkg4/
`,
errMsg: `this command returns an error if package has error.txt file`,
},
{
name: "executeCmd_prints_pkgpaths",
recurse: true,
needOpenAPI: false,
pkgPath: "subpkg2",
expectedOut: `${baseDir}/subpkg2/
${baseDir}/subpkg2/subpkg3/
`,
},
}
dir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.RemoveAll(dir)
err = createTestDirStructure(dir)
if !assert.NoError(t, err) {
t.FailNow()
}
for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
actual := &bytes.Buffer{}
r := &TestRunner{}
e := executeCmdOnPkgs{
needOpenAPI: test.needOpenAPI,
writer: actual,
rootPkgPath: filepath.Join(dir, test.pkgPath),
recurseSubPackages: test.recurse,
cmdRunner: r,
}
err := e.execute()
if test.errMsg == "" {
if !assert.NoError(t, err) {
t.FailNow()
}
} else {
if !assert.Error(t, err) {
t.FailNow()
}
if !assert.Contains(t, err.Error(), test.errMsg) {
t.FailNow()
}
}
// normalize path format for windows
actualNormalized := strings.Replace(
strings.Replace(actual.String(), "\\", "/", -1),
"//", "/", -1)
expected := strings.Replace(test.expectedOut, "${baseDir}", dir+"/", -1)
expectedNormalized := strings.Replace(
strings.Replace(expected, "\\", "/", -1),
"//", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
t.FailNow()
}
})
}
}
func createTestDirStructure(dir string) error {
/*
Adds the folders to the input dir with following structure
dir
├── subpkg1
│   ├── Krmfile
│   └── subdir1
├── subpkg4
│   ├── Krmfile
│   └── error.txt
└── subpkg2
├── subpkg3
│ ├── Krmfile
│ └── subdir2
└── Krmfile
*/
err := os.MkdirAll(filepath.Join(dir, "subpkg1/subdir1"), 0777|os.ModeDir)
if err != nil {
return err
}
err = os.MkdirAll(filepath.Join(dir, "subpkg2/subpkg3/subdir2"), 0777|os.ModeDir)
if err != nil {
return err
}
err = os.MkdirAll(filepath.Join(dir, "subpkg4"), 0777|os.ModeDir)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dir, "subpkg1", "Krmfile"), []byte(""), 0777)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dir, "subpkg2", "Krmfile"), []byte(""), 0777)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dir, "subpkg2/subpkg3", "Krmfile"), []byte(""), 0777)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dir, "subpkg4", "error.txt"), []byte(""), 0777)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dir, "subpkg4", "Krmfile"), []byte(""), 0777)
if err != nil {
return err
}
err = ioutil.WriteFile(filepath.Join(dir, "Krmfile"), []byte(""), 0777)
if err != nil {
return err
}
return nil
}
type TestRunner struct{}
func (r *TestRunner) executeCmd(w io.Writer, pkgPath string) error {
children, err := ioutil.ReadDir(pkgPath)
if err != nil {
return err
}
for _, child := range children {
if child.Name() == "error.txt" {
return errors.Errorf("this command returns an error if package has error.txt file")
}
}
return nil
}

View File

@@ -8,19 +8,15 @@ require (
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
k8s.io/client-go v0.17.3 k8s.io/client-go v0.17.3
sigs.k8s.io/kustomize/api v0.5.1 sigs.k8s.io/kustomize/api v0.6.1
sigs.k8s.io/kustomize/cmd/config v0.5.0 sigs.k8s.io/kustomize/cmd/config v0.8.1
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
replace (
sigs.k8s.io/kustomize/api v0.5.1 => ../api
sigs.k8s.io/kustomize/cmd/config v0.5.0 => ../cmd/config
sigs.k8s.io/kustomize/kyaml => ../kyaml
)
exclude ( exclude (
github.com/russross/blackfriday v2.0.0+incompatible github.com/russross/blackfriday v2.0.0+incompatible
sigs.k8s.io/kustomize/api v0.2.0 sigs.k8s.io/kustomize/api v0.2.0
sigs.k8s.io/kustomize/cmd/config v0.2.0 sigs.k8s.io/kustomize/cmd/config v0.2.0
) )
replace sigs.k8s.io/kustomize/api v0.6.1 => ../api

View File

@@ -629,6 +629,7 @@ golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -744,12 +745,18 @@ modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/cli-utils v0.19.2 h1:BwidWPZ3Qop4RBOl27MU8uN/BSEZPQ8rw1mwsNofXfw= sigs.k8s.io/cli-utils v0.20.1 h1:S3IM4Rmely9oWNAeytx46PhngTwdRxsJ4ixtoGWPc3o=
sigs.k8s.io/cli-utils v0.19.2/go.mod h1:ulIQPERYwkYksNriRknJRbGECDR/ZZROpkiThlXBtB4= sigs.k8s.io/cli-utils v0.20.1/go.mod h1:fQkEMmLXduMNmUf1+dgQ5GRJa4OrrLMw3qzAN8YXAU8=
sigs.k8s.io/cli-utils v0.20.2 h1:jNMu4ExtvFXlmKqZMDJqySqK55vGtVRqoLht6eIMffw=
sigs.k8s.io/cli-utils v0.20.2/go.mod h1:uT5cVxMrOoRplL8umtrJx5R51ZWsIKD7lQfPtot80uA=
sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg= sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9NPsg=
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0= sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU= sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/kustomize/kyaml v0.8.0 h1:/MqPML99XAm2pbrD/eTpePh5rnU5bpnuTPqb29LpSz4=
sigs.k8s.io/kustomize/kyaml v0.8.0/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
sigs.k8s.io/kustomize/kyaml v0.8.1 h1:5GRanVGU6+iq3ERTiQD9VIfyGByFVB4z4GthP8NkRYE=
sigs.k8s.io/kustomize/kyaml v0.8.1/go.mod h1:UTm64bSWVdBUA8EQoYCxVOaBQxUdIOr5LKWxA4GNbkw=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18= sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=

View File

@@ -81,6 +81,11 @@ func newCmdAddConfigMap(
"from-env-file", "from-env-file",
"", "",
"Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).") "Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).")
cmd.Flags().BoolVar(
&flags.DisableNameSuffixHash,
"disableNameSuffixHash",
false,
"Disable the name suffix for the configmap")
return cmd return cmd
} }
@@ -126,4 +131,9 @@ func mergeFlagsIntoCmArgs(args *types.ConfigMapArgs, flags flagsAndArgs) {
args.EnvSources = append( args.EnvSources = append(
args.EnvSources, flags.EnvFileSource) args.EnvSources, flags.EnvFileSource)
} }
if flags.DisableNameSuffixHash {
args.Options = &types.GeneratorOptions{
DisableNameSuffixHash: true,
}
}
} }

View File

@@ -26,6 +26,8 @@ type flagsAndArgs struct {
Type string Type string
// Namespace of secret // Namespace of secret
Namespace string Namespace string
// Disable name suffix
DisableNameSuffixHash bool
} }
// Validate validates required fields are set to support structured generation. // Validate validates required fields are set to support structured generation.

View File

@@ -91,6 +91,11 @@ func newCmdAddSecret(
"namespace", "namespace",
"", "",
"Specify the namespace of the secret") "Specify the namespace of the secret")
cmd.Flags().BoolVar(
&flags.DisableNameSuffixHash,
"disableNameSuffixHash",
false,
"Disable the name suffix for the secret")
return cmd return cmd
} }
@@ -139,4 +144,9 @@ func mergeFlagsIntoGeneratorArgs(args *types.GeneratorArgs, flags flagsAndArgs)
args.EnvSources = append( args.EnvSources = append(
args.EnvSources, flags.EnvFileSource) args.EnvSources, flags.EnvFileSource)
} }
if flags.DisableNameSuffixHash {
args.Options = &types.GeneratorOptions{
DisableNameSuffixHash: true,
}
}
} }

View File

@@ -114,3 +114,14 @@ func TestMergeFlagsIntoSecretArgs_EnvSource(t *testing.T) {
t.Fatalf("expected env2") t.Fatalf("expected env2")
} }
} }
func TestMergeFlagsIntoSecretArgs_DisableNameSuffixHash(t *testing.T) {
k := &types.Kustomization{}
args := findOrMakeSecretArgs(k, "foo", "bar", "forbidden")
mergeFlagsIntoGeneratorArgs(
&args.GeneratorArgs,
flagsAndArgs{DisableNameSuffixHash: true})
if k.SecretGenerator[0].Options.DisableNameSuffixHash != true {
t.Fatalf("expected true")
}
}

View File

@@ -39,5 +39,6 @@ func RunFix(fSys filesys.FileSystem) error {
return err return err
} }
m.FixKustomizationPreMarshalling()
return mf.Write(m) return mf.Write(m)
} }

View File

@@ -7,6 +7,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/google/go-cmp/cmp"
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
testutils_test "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/testutils" testutils_test "sigs.k8s.io/kustomize/kustomize/v3/internal/commands/testutils"
) )
@@ -31,3 +32,104 @@ func TestFix(t *testing.T) {
t.Errorf("expected kind in kustomization") t.Errorf("expected kind in kustomization")
} }
} }
func TestFixOutdatedPatchesFieldTitle(t *testing.T) {
kustomizationContentWithOutdatedPatchesFieldTitle := []byte(`
patchesJson6902:
- path: patch1.yaml
target:
kind: Service
- path: patch2.yaml
target:
group: apps
kind: Deployment
version: v1
`)
expected := []byte(`
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: patch1.yaml
target:
kind: Service
- path: patch2.yaml
target:
group: apps
kind: Deployment
version: v1
`)
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, kustomizationContentWithOutdatedPatchesFieldTitle)
cmd := NewCmdFix(fSys)
err := cmd.RunE(cmd, nil)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := testutils_test.ReadTestKustomization(fSys)
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
if !strings.Contains(string(content), "apiVersion: ") {
t.Errorf("expected apiVersion in kustomization")
}
if !strings.Contains(string(content), "kind: Kustomization") {
t.Errorf("expected kind in kustomization")
}
if diff := cmp.Diff(expected, content); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
func TestRenameAndKeepOutdatedPatchesField(t *testing.T) {
kustomizationContentWithOutdatedPatchesFieldTitle := []byte(`
patchesJson6902:
- path: patch1.yaml
target:
kind: Deployment
patches:
- path: patch2.yaml
target:
kind: Deployment
- path: patch3.yaml
target:
kind: Service
`)
expected := []byte(`
patches:
- path: patch2.yaml
target:
kind: Deployment
- path: patch3.yaml
target:
kind: Service
- path: patch1.yaml
target:
kind: Deployment
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`)
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, kustomizationContentWithOutdatedPatchesFieldTitle)
cmd := NewCmdFix(fSys)
err := cmd.RunE(cmd, nil)
if err != nil {
t.Errorf("unexpected cmd error: %v", err)
}
content, err := testutils_test.ReadTestKustomization(fSys)
if err != nil {
t.Errorf("unexpected read error: %v", err)
}
if !strings.Contains(string(content), "apiVersion: ") {
t.Errorf("expected apiVersion in kustomization")
}
if !strings.Contains(string(content), "kind: Kustomization") {
t.Errorf("expected kind in kustomization")
}
if diff := cmp.Diff(expected, content); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}

View File

@@ -145,6 +145,47 @@ func TestSetImage(t *testing.T) {
" newTag: my-tag2", " newTag: my-tag2",
}}, }},
}, },
{
description: "override file with patch",
given: given{
args: []string{"image1=foo.bar.foo:8800/foo/image1:foo-bar"},
infileImages: []string{
"images:",
"- name: image1",
" newName: my-image1",
" newTag: my-tag",
"- name: image2",
" newName: my-image2",
" newTag: my-tag2",
"patchesJson6902:",
"- patch: |-",
" - op: remove",
" path: /spec/selector",
" target:",
" kind: Service",
" name: foo",
" version: v1",
},
},
expected: expected{
fileOutput: []string{
"images:",
"- name: image1",
" newName: foo.bar.foo:8800/foo/image1",
" newTag: foo-bar",
"- name: image2",
" newName: my-image2",
" newTag: my-tag2",
"patchesJson6902:",
"- patch: |-",
" - op: remove",
" path: /spec/selector",
" target:",
" kind: Service",
" name: foo",
" version: v1",
}},
},
{ {
description: "override new tag and new name with just a new tag", description: "override new tag and new name with just a new tag",
given: given{ given: given{

View File

@@ -170,7 +170,6 @@ func (mf *kustomizationFile) Write(kustomization *types.Kustomization) error {
if kustomization == nil { if kustomization == nil {
return errors.New("util: kustomization file arg is nil") return errors.New("util: kustomization file arg is nil")
} }
kustomization.FixKustomizationPreMarshalling()
data, err := mf.marshal(kustomization) data, err := mf.marshal(kustomization)
if err != nil { if err != nil {
return err return err

View File

@@ -338,103 +338,6 @@ kind: Kustomization
} }
} }
func TestRenameAndKeepOutdatedPatchesField(t *testing.T) {
kustomizationContentWithOutdatedPatchesFieldTitle := []byte(`
patchesJson6902:
- path: patch1.yaml
target:
kind: Deployment
patches:
- path: patch2.yaml
target:
kind: Deployment
- path: patch3.yaml
target:
kind: Service
`)
expected := []byte(`
patches:
- path: patch2.yaml
target:
kind: Deployment
- path: patch3.yaml
target:
kind: Service
- path: patch1.yaml
target:
kind: Deployment
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`)
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, kustomizationContentWithOutdatedPatchesFieldTitle)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if diff := cmp.Diff(expected, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
func TestFixOutdatedPatchesFieldTitle(t *testing.T) {
kustomizationContentWithOutdatedPatchesFieldTitle := []byte(`
patchesJson6902:
- path: patch1.yaml
target:
kind: Service
- path: patch2.yaml
target:
group: apps
kind: Deployment
version: v1
`)
expected := []byte(`
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: patch1.yaml
target:
kind: Service
- path: patch2.yaml
target:
group: apps
kind: Deployment
version: v1
`)
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, kustomizationContentWithOutdatedPatchesFieldTitle)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if diff := cmp.Diff(expected, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
func TestUnknownFieldInKustomization(t *testing.T) { func TestUnknownFieldInKustomization(t *testing.T) {
kContent := []byte(` kContent := []byte(`
foo: foo:

View File

@@ -154,13 +154,17 @@ func (c *Filter) setupExec() {
// getArgs returns the command + args to run to spawn the container // getArgs returns the command + args to run to spawn the container
func (c *Filter) getCommand() (string, []string) { func (c *Filter) getCommand() (string, []string) {
network := runtimeutil.NetworkNameNone
if c.ContainerSpec.Network {
network = runtimeutil.NetworkNameHost
}
// run the container using docker. this is simpler than using the docker // run the container using docker. this is simpler than using the docker
// libraries, and ensures things like auth work the same as if the container // libraries, and ensures things like auth work the same as if the container
// was run from the cli. // was run from the cli.
args := []string{"run", args := []string{"run",
"--rm", // delete the container afterward "--rm", // delete the container afterward
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR", // attach stdin, stdout, stderr "-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR", // attach stdin, stdout, stderr
"--network", string(c.ContainerSpec.Network.Name), "--network", string(network),
// added security options // added security options
"--user", c.User.String(), "--user", c.User.String(),
@@ -186,10 +190,5 @@ func NewContainer(spec runtimeutil.ContainerSpec) Filter {
f.ContainerSpec.User = runtimeutil.UserNobody f.ContainerSpec.User = runtimeutil.UserNobody
} }
// default network name is none
if f.ContainerSpec.Network.Name == "" {
f.ContainerSpec.Network.Name = runtimeutil.NetworkNameNone
}
return f return f
} }

View File

@@ -55,16 +55,14 @@ metadata:
"run", "run",
"--rm", "--rm",
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR", "-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
"--network", "test-1", "--network", "host",
"--user", "nobody", "--user", "nobody",
"--security-opt=no-new-privileges", "--security-opt=no-new-privileges",
}, },
instance: NewContainer( instance: NewContainer(
runtimeutil.ContainerSpec{ runtimeutil.ContainerSpec{
Image: "example.com:version", Image: "example.com:version",
Network: runtimeutil.ContainerNetwork{ Network: true,
Name: "test-1",
},
User: "nobody", User: "nobody",
}, },
), ),

View File

@@ -39,7 +39,7 @@ type ContainerNetworkName string
const ( const (
NetworkNameNone ContainerNetworkName = "none" NetworkNameNone ContainerNetworkName = "none"
NetworkNameEmpty ContainerNetworkName = "" NetworkNameHost ContainerNetworkName = "host"
) )
const defaultEnvValue string = "true" const defaultEnvValue string = "true"
@@ -166,7 +166,7 @@ type ContainerSpec struct {
Image string `json:"image,omitempty" yaml:"image,omitempty"` Image string `json:"image,omitempty" yaml:"image,omitempty"`
// Network defines network specific configuration // Network defines network specific configuration
Network ContainerNetwork `json:"network,omitempty" yaml:"network,omitempty"` Network bool `json:"network,omitempty" yaml:"network,omitempty"`
// Mounts are the storage or directories to mount into the container // Mounts are the storage or directories to mount into the container
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"` StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
@@ -178,15 +178,6 @@ type ContainerSpec struct {
Env []string `json:"envs,omitempty" yaml:"envs,omitempty"` Env []string `json:"envs,omitempty" yaml:"envs,omitempty"`
} }
// ContainerNetwork
type ContainerNetwork struct {
// Required specifies that function requires a network
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
// Name is the name of the network to use from a container
Name ContainerNetworkName `json:"name,omitempty" yaml:"name,omitempty"`
}
// StarlarkSpec defines how to run a function as a starlark program // StarlarkSpec defines how to run a function as a starlark program
type StarlarkSpec struct { type StarlarkSpec struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"`
@@ -237,7 +228,6 @@ func GetFunctionSpec(n *yaml.RNode) *FunctionSpec {
} }
if fn := getFunctionSpecFromAnnotation(n, meta); fn != nil { if fn := getFunctionSpecFromAnnotation(n, meta); fn != nil {
fn.Container.Network.Name = NetworkNameEmpty
fn.StorageMounts = []StorageMount{} fn.StorageMounts = []StorageMount{}
return fn return fn
} }

View File

@@ -1208,14 +1208,12 @@ metadata:
config.kubernetes.io/function: |- config.kubernetes.io/function: |-
container: container:
image: foo:v1.0.0 image: foo:v1.0.0
network: network: true
required: true
`, `,
expectedFn: ` expectedFn: `
container: container:
image: foo:v1.0.0 image: foo:v1.0.0
network: network: true
required: true
`, `,
}, },
@@ -1324,8 +1322,7 @@ metadata:
configFn: configFn:
container: container:
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0 image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
network: network: true
required: true
`, `,
required: true, required: true,
}, },
@@ -1337,8 +1334,7 @@ metadata:
configFn: configFn:
container: container:
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0 image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
network: network: false
required: false
`, `,
required: false, required: false,
}, },
@@ -1363,8 +1359,7 @@ metadata:
config.kubernetes.io/function: | config.kubernetes.io/function: |
container: container:
image: gcr.io/kustomize-functions/example-tshirt:v0.1.0 image: gcr.io/kustomize-functions/example-tshirt:v0.1.0
network: network: true
required: true
`, `,
required: true, required: true,
}, },
@@ -1376,7 +1371,7 @@ metadata:
return return
} }
fn := GetFunctionSpec(cfg) fn := GetFunctionSpec(cfg)
assert.Equal(t, tc.required, fn.Container.Network.Required) assert.Equal(t, tc.required, fn.Container.Network)
} }
} }

View File

@@ -38,6 +38,7 @@ type ignoreFilesMatcher struct {
// if it is, reads it in and turns it into a matcher. If we can't find a file, // if it is, reads it in and turns it into a matcher. If we can't find a file,
// we just add a matcher that match nothing. // we just add a matcher that match nothing.
func (i *ignoreFilesMatcher) readIgnoreFile(path string) error { func (i *ignoreFilesMatcher) readIgnoreFile(path string) error {
i.verifyPath(path)
m, err := gitignore.NewGitIgnore(filepath.Join(path, ext.GetIgnoreFileName())) m, err := gitignore.NewGitIgnore(filepath.Join(path, ext.GetIgnoreFileName()))
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
@@ -63,8 +64,8 @@ func (i *ignoreFilesMatcher) readIgnoreFile(path string) error {
func (i *ignoreFilesMatcher) verifyPath(path string) { func (i *ignoreFilesMatcher) verifyPath(path string) {
for j := len(i.matchers) - 1; j >= 0; j-- { for j := len(i.matchers) - 1; j >= 0; j-- {
matcher := i.matchers[j] matcher := i.matchers[j]
if !strings.HasPrefix(path, matcher.basePath) { if strings.HasPrefix(path, matcher.basePath) || path == matcher.basePath {
i.matchers = i.matchers[:j] i.matchers = i.matchers[:j+1]
return return
} }
} }
@@ -76,7 +77,7 @@ func (i *ignoreFilesMatcher) matchFile(path string) bool {
if len(i.matchers) == 0 { if len(i.matchers) == 0 {
return false return false
} }
i.verifyPath(path) i.verifyPath(filepath.Dir(path))
return i.matchers[len(i.matchers)-1].matcher.Match(path, false) return i.matchers[len(i.matchers)-1].matcher.Match(path, false)
} }

View File

@@ -165,6 +165,52 @@ a_test.yaml
`b: b`, `b: b`,
}, },
}, },
{
name: "handles a combination of packages and directories",
directories: []string{
filepath.Join("a"),
filepath.Join("d", "e"),
filepath.Join("f"),
},
files: map[string][]byte{
filepath.Join("pkgFile"): {},
filepath.Join("d", "pkgFile"): {},
filepath.Join("d", "e", "pkgFile"): {},
filepath.Join("f", "pkgFile"): {},
filepath.Join("manifest.yaml"): []byte(`root: root`),
filepath.Join("a", "manifest.yaml"): []byte(`a: a`),
filepath.Join("d", "manifest.yaml"): []byte(`d: d`),
filepath.Join("d", "e", "manifest.yaml"): []byte(`e: e`),
filepath.Join("f", "manifest.yaml"): []byte(`f: f`),
filepath.Join("d", ".krmignore"): []byte(`
manifest.yaml
`),
},
expected: []string{
`a: a`,
`e: e`,
`f: f`,
`root: root`,
},
},
{
name: "ignore file can exclude subpackages",
directories: []string{
filepath.Join("a"),
},
files: map[string][]byte{
filepath.Join("pkgFile"): {},
filepath.Join("a", "pkgFile"): {},
filepath.Join("manifest.yaml"): []byte(`root: root`),
filepath.Join("a", "manifest.yaml"): []byte(`a: a`),
filepath.Join(".krmignore"): []byte(`
a
`),
},
expected: []string{
`root: root`,
},
},
} }
for i := range testCases { for i := range testCases {

View File

@@ -80,6 +80,7 @@ func (r *LocalPackageReadWriter) Read() ([]*yaml.RNode, error) {
IncludeSubpackages: r.IncludeSubpackages, IncludeSubpackages: r.IncludeSubpackages,
ErrorIfNonResources: r.ErrorIfNonResources, ErrorIfNonResources: r.ErrorIfNonResources,
SetAnnotations: r.SetAnnotations, SetAnnotations: r.SetAnnotations,
PackageFileName: r.PackageFileName,
}.Read() }.Read()
if err != nil { if err != nil {
return nil, errors.Wrap(err) return nil, errors.Wrap(err)
@@ -212,9 +213,9 @@ func (r LocalPackageReader) Read() ([]*yaml.RNode, error) {
// check if we should skip the directory or file // check if we should skip the directory or file
if info.IsDir() { if info.IsDir() {
return r.ShouldSkipDir(path, ignoreFilesMatcher) return r.shouldSkipDir(path, ignoreFilesMatcher)
} }
if match, err := r.ShouldSkipFile(path, ignoreFilesMatcher); err != nil { if match, err := r.shouldSkipFile(path, ignoreFilesMatcher); err != nil {
return err return err
} else if !match { } else if !match {
// skip this file // skip this file
@@ -256,8 +257,8 @@ func (r *LocalPackageReader) readFile(path string, _ os.FileInfo) ([]*yaml.RNode
return rr.Read() return rr.Read()
} }
// ShouldSkipFile returns true if the file should be skipped // shouldSkipFile returns true if the file should be skipped
func (r *LocalPackageReader) ShouldSkipFile(path string, matcher *ignoreFilesMatcher) (bool, error) { func (r *LocalPackageReader) shouldSkipFile(path string, matcher *ignoreFilesMatcher) (bool, error) {
// check if the file is covered by a .krmignore file. // check if the file is covered by a .krmignore file.
if matcher.matchFile(path) { if matcher.matchFile(path) {
return false, nil return false, nil
@@ -284,23 +285,18 @@ func (r *LocalPackageReader) initReaderAnnotations(path string, _ os.FileInfo) {
} }
} }
// ShouldSkipDir returns a filepath.SkipDir if the directory should be skipped // shouldSkipDir returns a filepath.SkipDir if the directory should be skipped
func (r *LocalPackageReader) ShouldSkipDir(path string, matcher *ignoreFilesMatcher) error { func (r *LocalPackageReader) shouldSkipDir(path string, matcher *ignoreFilesMatcher) error {
if r.PackageFileName == "" {
// If the folder is not a package, but covered by the .krmignore file,
// we skip it.
if matcher.matchDir(path) { if matcher.matchDir(path) {
return filepath.SkipDir return filepath.SkipDir
} }
if r.PackageFileName == "" {
return nil return nil
} }
// check if this is a subpackage // check if this is a subpackage
_, err := os.Stat(filepath.Join(path, r.PackageFileName)) _, err := os.Stat(filepath.Join(path, r.PackageFileName))
if os.IsNotExist(err) { if os.IsNotExist(err) {
// Skip the folder if it is covered by the .krmignore file.
if matcher.matchDir(path) {
return filepath.SkipDir
}
return nil return nil
} else if err != nil { } else if err != nil {
return errors.Wrap(err) return errors.Wrap(err)
@@ -308,9 +304,5 @@ func (r *LocalPackageReader) ShouldSkipDir(path string, matcher *ignoreFilesMatc
if !r.IncludeSubpackages { if !r.IncludeSubpackages {
return filepath.SkipDir return filepath.SkipDir
} }
// We don't allow the .krmignore file in a package cause us to skip
// a subpackage. So if we have found a package file in the folder and
// we should include subpackages, we don't check the .krmignore file. We
// do however check whether the package contains a .krmignore file.
return matcher.readIgnoreFile(path) return matcher.readIgnoreFile(path)
} }

View File

@@ -6,6 +6,7 @@ package kio
import ( import (
"fmt" "fmt"
"io" "io"
"os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
@@ -37,6 +38,7 @@ type TreeWriter struct {
Root string Root string
Fields []TreeWriterField Fields []TreeWriterField
Structure TreeStructure Structure TreeStructure
OpenAPIFileName string
} }
// TreeWriterField configures a Resource field to be included in the tree // TreeWriterField configures a Resource field to be included in the tree
@@ -72,7 +74,7 @@ func (p TreeWriter) packageStructure(nodes []*yaml.RNode) error {
// create a new branch for the package // create a new branch for the package
createOk := pkg != "." // special edge case logic for tree on current working dir createOk := pkg != "." // special edge case logic for tree on current working dir
if createOk { if createOk {
branch = branch.AddBranch(pkg) branch = branch.AddBranch(branchName(p.Root, pkg, p.OpenAPIFileName))
} }
// cache the branch for this package // cache the branch for this package
@@ -91,6 +93,19 @@ func (p TreeWriter) packageStructure(nodes []*yaml.RNode) error {
return err return err
} }
// branchName takes the root directory and relative path to the directory
// and returns the branch name
func branchName(root, dirRelPath, openAPIFileName string) string {
name := filepath.Base(dirRelPath)
_, err := os.Stat(filepath.Join(root, dirRelPath, openAPIFileName))
if !os.IsNotExist(err) {
// add Pkg: prefix indicating that it is a separate package as it has
// openAPIFile
return fmt.Sprintf("Pkg: %s", name)
}
return name
}
// Write writes the ascii tree to p.Writer // Write writes the ascii tree to p.Writer
func (p TreeWriter) Write(nodes []*yaml.RNode) error { func (p TreeWriter) Write(nodes []*yaml.RNode) error {
switch p.Structure { switch p.Structure {

View File

@@ -6,7 +6,6 @@ package kio_test
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -76,9 +75,9 @@ spec:
└── foo-package └── foo-package
├── [f1.yaml] Deployment default/foo ├── [f1.yaml] Deployment default/foo
├── [f1.yaml] Service default/foo ├── [f1.yaml] Service default/foo
└── foo-package%s3 └── 3
└── [f3.yaml] Deployment default/foo └── [f3.yaml] Deployment default/foo
`, string(filepath.Separator)), out.String()) { `), out.String()) {
t.FailNow() t.FailNow()
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -95,7 +95,7 @@ func openapiKustomizationapiSwaggerJson() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "openapi/kustomizationapi/swagger.json", size: 3127, mode: os.FileMode(420), modTime: time.Unix(1586844916, 0)} info := bindataFileInfo{name: "openapi/kustomizationapi/swagger.json", size: 3127, mode: os.FileMode(420), modTime: time.Unix(1586909905, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }

View File

@@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"reflect" "reflect"
"strings"
"sync" "sync"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
@@ -26,6 +27,7 @@ type openapiData struct {
setup sync.Once setup sync.Once
schema spec.Schema schema spec.Schema
schemaByResourceType map[yaml.TypeMeta]*spec.Schema schemaByResourceType map[yaml.TypeMeta]*spec.Schema
namespaceabilityByResourceType map[yaml.TypeMeta]bool
noUseBuiltInSchema bool noUseBuiltInSchema bool
} }
@@ -66,6 +68,41 @@ func AddSchemaFromFile(path string) error {
return AddSchemaFromFileUsingField(path, SupplementaryOpenAPIFieldName) return AddSchemaFromFileUsingField(path, SupplementaryOpenAPIFieldName)
} }
// DeleteSchemaInFile reads the file at path and removes all the openAPI definitions
// present in file from global schema
func DeleteSchemaInFile(path string) error {
b, err := ioutil.ReadFile(path)
if err != nil {
return err
}
object, err := yaml.Parse(string(b))
if err != nil {
return err
}
definitions, err := object.Pipe(yaml.Lookup(SupplementaryOpenAPIFieldName, "definitions"))
if definitions == nil {
return nil
}
if err != nil {
return err
}
fields, err := definitions.Fields()
if err != nil {
return err
}
for _, field := range fields {
_, ok := globalSchema.schema.Definitions[field]
if ok {
delete(globalSchema.schema.Definitions, field)
}
}
return nil
}
// AddSchemaFromFileUsingField reads the file at path and parses the OpenAPI definitions // AddSchemaFromFileUsingField reads the file at path and parses the OpenAPI definitions
// from the specified field. If field is the empty string, use the whole document as // from the specified field. If field is the empty string, use the whole document as
// OpenAPI. // OpenAPI.
@@ -109,7 +146,7 @@ func AddSchemaFromFileUsingField(path, field string) error {
} }
// add the json schema to the global schema // add the json schema to the global schema
_, err = AddSchema(j) err = AddSchema(j)
if err != nil { if err != nil {
return err return err
} }
@@ -117,7 +154,7 @@ func AddSchemaFromFileUsingField(path, field string) error {
} }
// AddSchema parses s, and adds definitions from s to the global schema. // AddSchema parses s, and adds definitions from s to the global schema.
func AddSchema(s []byte) (*spec.Schema, error) { func AddSchema(s []byte) error {
return parse(s) return parse(s)
} }
@@ -153,19 +190,27 @@ func AddDefinitions(definitions spec.Definitions) {
if !ok || len(exts) != 1 { if !ok || len(exts) != 1 {
continue continue
} }
m, ok := exts[0].(map[string]interface{})
typeMeta, ok := toTypeMeta(exts[0])
if !ok { if !ok {
continue continue
} }
globalSchema.schemaByResourceType[typeMeta] = &d
}
}
func toTypeMeta(ext interface{}) (yaml.TypeMeta, bool) {
m, ok := ext.(map[string]interface{})
if !ok {
return yaml.TypeMeta{}, false
}
// build the index key and save it
g := m[groupKey].(string) g := m[groupKey].(string)
apiVersion := m[versionKey].(string) apiVersion := m[versionKey].(string)
if g != "" { if g != "" {
apiVersion = g + "/" + apiVersion apiVersion = g + "/" + apiVersion
} }
globalSchema.schemaByResourceType[yaml.TypeMeta{Kind: m[kindKey].(string), APIVersion: apiVersion}] = &d return yaml.TypeMeta{Kind: m[kindKey].(string), APIVersion: apiVersion}, true
}
} }
// Resolve resolves the reference against the global schema // Resolve resolves the reference against the global schema
@@ -196,6 +241,19 @@ func GetSchema(s string) (*ResourceSchema, error) {
return &ResourceSchema{Schema: &sc}, nil return &ResourceSchema{Schema: &sc}, nil
} }
// IsNamespaceScoped determines whether a resource is namespace or
// cluster-scoped by looking at the information in the openapi schema.
// The second return value tells whether the provided type could be found
// in the openapi schema. If the value is false here, the scope of the
// resource is not known. If the type if found, the first return value will
// be true if the resource is namespace-scoped, and false if the type is
// cluster-scoped.
func IsNamespaceScoped(typeMeta yaml.TypeMeta) (bool, bool) {
initSchema()
isNamespaceScoped, found := globalSchema.namespaceabilityByResourceType[typeMeta]
return isNamespaceScoped, found
}
// SuppressBuiltInSchemaUse can be called to prevent using the built-in Kubernetes // SuppressBuiltInSchemaUse can be called to prevent using the built-in Kubernetes
// schema as part of the global schema. // schema as part of the global schema.
// Must be called before the schema is used. // Must be called before the schema is used.
@@ -333,12 +391,12 @@ func initSchema() {
} }
// parse the swagger, this should never fail // parse the swagger, this should never fail
if _, err := parse(kubernetesapi.MustAsset(kubernetesAPIAssetName)); err != nil { if err := parse(kubernetesapi.MustAsset(kubernetesAPIAssetName)); err != nil {
// this should never happen // this should never happen
panic(err) panic(err)
} }
if _, err := parse(kustomizationapi.MustAsset(kustomizationAPIAssetName)); err != nil { if err := parse(kustomizationapi.MustAsset(kustomizationAPIAssetName)); err != nil {
// this should never happen // this should never happen
panic(err) panic(err)
} }
@@ -346,14 +404,55 @@ func initSchema() {
} }
// parse parses and indexes a single json schema // parse parses and indexes a single json schema
func parse(b []byte) (*spec.Schema, error) { func parse(b []byte) error {
var sc spec.Schema var swagger spec.Swagger
if err := sc.UnmarshalJSON(b); err != nil { if err := swagger.UnmarshalJSON(b); err != nil {
return nil, errors.Wrap(err) return errors.Wrap(err)
}
AddDefinitions(swagger.Definitions)
findNamespaceability(swagger.Paths)
return nil
}
// findNamespaceability looks at the api paths for the resource to determine
// if it is cluster-scoped or namespace-scoped. The gvk of the resource
// for each path is found by looking at the x-kubernetes-group-version-kind
// extension. If a path exists for the resource that contains a namespace path
// parameter, the resource is namespace-scoped.
func findNamespaceability(paths *spec.Paths) {
if globalSchema.namespaceabilityByResourceType == nil {
globalSchema.namespaceabilityByResourceType = make(map[yaml.TypeMeta]bool)
}
if paths == nil {
return
}
for path, pathInfo := range paths.Paths {
if pathInfo.Get == nil {
continue
}
gvk, found := pathInfo.Get.VendorExtensible.Extensions[kubernetesGVKExtensionKey]
if !found {
continue
}
typeMeta, found := toTypeMeta(gvk)
if !found {
continue
}
if strings.Contains(path, "namespaces/{namespace}") {
// if we find a namespace path parameter, we just update the map
// directly
globalSchema.namespaceabilityByResourceType[typeMeta] = true
} else if _, found := globalSchema.namespaceabilityByResourceType[typeMeta]; !found {
// if the resource doesn't have the namespace path parameter, we
// only add it to the map if it doesn't already exist.
globalSchema.namespaceabilityByResourceType[typeMeta] = false
}
} }
AddDefinitions(sc.Definitions)
return &sc, nil
} }
func resolve(root interface{}, ref *spec.Ref) (*spec.Schema, error) { func resolve(root interface{}, ref *spec.Ref) (*spec.Schema, error) {

View File

@@ -16,7 +16,7 @@ func TestAddSchema(t *testing.T) {
// reset package vars // reset package vars
globalSchema = openapiData{} globalSchema = openapiData{}
_, err := AddSchema(additionalSchema) err := AddSchema(additionalSchema)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -36,7 +36,7 @@ func TestNoUseBuiltInSchema_AddSchema(t *testing.T) {
globalSchema = openapiData{} globalSchema = openapiData{}
SuppressBuiltInSchemaUse() SuppressBuiltInSchemaUse()
_, err := AddSchema(additionalSchema) err := AddSchema(additionalSchema)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -160,6 +160,82 @@ openAPI:
fmt.Sprintf("%v", s.Schema.Extensions)) fmt.Sprintf("%v", s.Schema.Extensions))
} }
func TestDeleteSchemaInFile(t *testing.T) {
ResetOpenAPI()
inputyaml := `
openAPI:
definitions:
io.k8s.cli.setters.image-name:
x-k8s-cli:
setter:
name: image-name
value: "nginx"
`
f, err := ioutil.TempFile("", "openapi-")
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) {
t.FailNow()
}
err = AddSchemaFromFile(f.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
s, err := GetSchema(`{"$ref": "#/definitions/io.k8s.cli.setters.image-name"}`)
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Greater(t, len(globalSchema.schema.Definitions), 200) {
t.FailNow()
}
assert.Equal(t, `map[x-k8s-cli:map[setter:map[name:image-name value:nginx]]]`,
fmt.Sprintf("%v", s.Schema.Extensions))
err = DeleteSchemaInFile(f.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
_, err = GetSchema(`{"$ref": "#/definitions/io.k8s.cli.setters.image-name"}`)
if !assert.Error(t, err) {
t.FailNow()
}
if !assert.Equal(t, `object has no key "io.k8s.cli.setters.image-name"`, err.Error()) {
t.FailNow()
}
}
func TestDeleteSchemaInFileNoDefs(t *testing.T) {
ResetOpenAPI()
inputyaml := `
openAPI:
definitions:
`
f, err := ioutil.TempFile("", "openapi-")
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.NoError(t, ioutil.WriteFile(f.Name(), []byte(inputyaml), 0600)) {
t.FailNow()
}
err = AddSchemaFromFile(f.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
err = DeleteSchemaInFile(f.Name())
if !assert.NoError(t, err) {
t.FailNow()
}
}
func TestPopulateDefsInOpenAPI_Substitution(t *testing.T) { func TestPopulateDefsInOpenAPI_Substitution(t *testing.T) {
ResetOpenAPI() ResetOpenAPI()
inputyaml := ` inputyaml := `
@@ -237,3 +313,100 @@ kind: Example
t.FailNow() t.FailNow()
} }
} }
func TestIsNamespaceScoped_builtin(t *testing.T) {
testCases := []struct {
name string
typeMeta yaml.TypeMeta
expectIsFound bool
expectIsNamespaced bool
}{
{
name: "namespacescoped resource",
typeMeta: yaml.TypeMeta{
APIVersion: "apps/v1",
Kind: "Deployment",
},
expectIsFound: true,
expectIsNamespaced: true,
},
{
name: "clusterscoped resource",
typeMeta: yaml.TypeMeta{
APIVersion: "v1",
Kind: "Namespace",
},
expectIsFound: true,
expectIsNamespaced: false,
},
{
name: "unknown resource",
typeMeta: yaml.TypeMeta{
APIVersion: "custom.io/v1",
Kind: "Custom",
},
expectIsFound: false,
},
}
for i := range testCases {
test := testCases[i]
t.Run(test.name, func(t *testing.T) {
ResetOpenAPI()
isNamespaceable, isFound := IsNamespaceScoped(test.typeMeta)
if !test.expectIsFound {
assert.False(t, isFound)
return
}
assert.True(t, isFound)
assert.Equal(t, test.expectIsNamespaced, isNamespaceable)
})
}
}
func TestIsNamespaceScoped_custom(t *testing.T) {
SuppressBuiltInSchemaUse()
err := AddSchema([]byte(`
{
"definitions": {},
"paths": {
"/apis/custom.io/v1/namespaces/{namespace}/customs/{name}": {
"get": {
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "custom.io",
"kind": "Custom",
"version": "v1"
}
}
},
"/apis/custom.io/v1/clustercustoms": {
"get": {
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "custom.io",
"kind": "ClusterCustom",
"version": "v1"
}
}
}
}
}
`))
assert.NoError(t, err)
isNamespaceable, isFound := IsNamespaceScoped(yaml.TypeMeta{
APIVersion: "custom.io/v1",
Kind: "ClusterCustom",
})
assert.True(t, isFound)
assert.False(t, isNamespaceable)
isNamespaceable, isFound = IsNamespaceScoped(yaml.TypeMeta{
APIVersion: "custom.io/v1",
Kind: "Custom",
})
assert.True(t, isFound)
assert.True(t, isNamespaceable)
}

View File

@@ -6,23 +6,29 @@ package pathutil
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
// SubDirsWithFile takes the root directory path and returns all the paths of // DirsWithFile takes the root directory path and returns all the paths of
// sub-directories which contain file with input fileName including itself // sub-directories(including itself) which contain file with input fileName
func SubDirsWithFile(root, fileName string) ([]string, error) { // at top level if recurse is true
func DirsWithFile(root, fileName string, recurse bool) ([]string, error) {
var res []string var res []string
if !recurse {
// check if the file with fileName is present in root and return it
// else return empty list
_, err := os.Stat(filepath.Join(root, fileName))
if !os.IsNotExist(err) {
res = append(res, filepath.Clean(root))
}
return res, nil
}
err := filepath.Walk(root, err := filepath.Walk(root,
func(path string, info os.FileInfo, err error) error { func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
if strings.HasSuffix(path, fileName) { if filepath.Base(path) == fileName {
if root == "." { res = append(res, filepath.Dir(path))
path = root + "/" + path
}
res = append(res, path)
} }
return nil return nil
}) })

View File

@@ -13,6 +13,32 @@ import (
) )
func TestSubDirsWithFile(t *testing.T) { func TestSubDirsWithFile(t *testing.T) {
var tests = []struct {
name string
fileName string
recurse bool
outFilesCount int
}{
{
name: "dirs-with-file-recurse",
fileName: "Krmfile",
outFilesCount: 3,
recurse: true,
},
{
name: "dirs-with-non-existent-file-recurse",
fileName: "non-existent-file.txt",
outFilesCount: 0,
recurse: true,
},
{
name: "dir-with-file-no-recurse",
fileName: "Krmfile",
outFilesCount: 1,
recurse: false,
},
}
dir, err := ioutil.TempDir("", "") dir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
@@ -23,28 +49,17 @@ func TestSubDirsWithFile(t *testing.T) {
t.FailNow() t.FailNow()
} }
res, err := SubDirsWithFile(dir, "Krmfile") for i := range tests {
test := tests[i]
t.Run(test.name, func(t *testing.T) {
res, err := DirsWithFile(dir, test.fileName, test.recurse)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
if !assert.Equal(t, 3, len(res)) { if !assert.Equal(t, test.outFilesCount, len(res)) {
t.FailNow() t.FailNow()
} }
} })
func TestSubDirsWithFileNoMatch(t *testing.T) {
dir, err := ioutil.TempDir("", "")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.RemoveAll(dir)
res, err := SubDirsWithFile(dir, "non-existent-file.txt")
if !assert.NoError(t, err) {
t.FailNow()
}
var expected []string
if !assert.Equal(t, expected, res) {
t.FailNow()
} }
} }

View File

@@ -52,9 +52,6 @@ type RunFns struct {
// Network enables network access for functions that declare it // Network enables network access for functions that declare it
Network bool Network bool
// NetworkName is the name of the docker network to use for the container
NetworkName string
// Output can be set to write the result to Output rather than back to the directory // Output can be set to write the result to Output rather than back to the directory
Output io.Writer Output io.Writer
@@ -294,13 +291,14 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
for i := range fns { for i := range fns {
api := fns[i] api := fns[i]
spec := runtimeutil.GetFunctionSpec(api) spec := runtimeutil.GetFunctionSpec(api)
if spec.Container.Network.Required { if spec == nil {
if !r.Network { // resource doesn't have function spec
continue
}
if spec.Container.Network && !r.Network {
// TODO(eddiezane): Provide error info about which function needs the network // TODO(eddiezane): Provide error info about which function needs the network
return fltrs, errors.Errorf("network required but not enabled with --network") return fltrs, errors.Errorf("network required but not enabled with --network")
} }
spec.Container.Network.Name = runtimeutil.ContainerNetworkName(r.NetworkName)
}
// command line username and envs has higher priority // command line username and envs has higher priority
if !r.User.IsEmpty() { if !r.User.IsEmpty() {
spec.Container.User = r.User spec.Container.User = r.User

View File

@@ -244,6 +244,17 @@ metadata:
out: []string{"gcr.io/example.com/image:v1.0.0"}, out: []string{"gcr.io/example.com/image:v1.0.0"},
}, },
{name: "no function spec",
in: []f{
{
explicitFunction: true,
value: `
foo: bar
`,
},
},
},
// Test // Test
// //
// //
@@ -685,6 +696,98 @@ metadata:
} }
} }
func TestRunFns_network(t *testing.T) {
tests := []struct {
name string
input string
network bool
expectNetwork bool
error string
}{
{
name: "imperative false, declarative false",
input: `
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: a
network: false
`,
network: false,
expectNetwork: false,
},
{
name: "imperative true, declarative false",
input: `
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: a
network: false
`,
network: true,
expectNetwork: false,
},
{
name: "imperative true, declarative true",
input: `
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: a
network: true
`,
network: true,
expectNetwork: true,
},
{
name: "imperative false, declarative true",
input: `
metadata:
annotations:
config.kubernetes.io/function: |
container:
image: a
network: true
`,
network: false,
error: "network required but not enabled with --network",
},
}
for i := range tests {
tt := tests[i]
fn := yaml.MustParse(tt.input)
t.Run(tt.name, func(t *testing.T) {
// init the instance
r := &RunFns{
Functions: []*yaml.RNode{fn},
Network: tt.network,
}
r.init()
_, fltrs, _, err := r.getNodesAndFilters()
if tt.error != "" {
if !assert.EqualError(t, err, tt.error) {
t.FailNow()
}
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
fltr := fltrs[0].(*container.Filter)
if !assert.Equal(t, tt.expectNetwork, fltr.Network) {
t.FailNow()
}
})
}
}
func TestCmd_Execute(t *testing.T) { func TestCmd_Execute(t *testing.T) {
dir := setupTest(t) dir := setupTest(t)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
@@ -987,7 +1090,7 @@ func getFilterProvider(t *testing.T) func(runtimeutil.FunctionSpec, *yaml.RNode)
} }
} }
func TestRunfns_mergeContainerEnv(t *testing.T) { func TestRunFns_mergeContainerEnv(t *testing.T) {
testcases := []struct { testcases := []struct {
name string name string
instance RunFns instance RunFns

View File

@@ -135,18 +135,18 @@ func (dd DeleterDefinition) Filter(object *yaml.RNode) (*yaml.RNode, error) {
} }
definitions, err := object.Pipe(yaml.Lookup(openapi.SupplementaryOpenAPIFieldName, "definitions")) definitions, err := object.Pipe(yaml.Lookup(openapi.SupplementaryOpenAPIFieldName, "definitions"))
if err != nil || definitions == nil { if err != nil {
return nil, err return nil, err
} }
// return error if the setter to be deleted doesn't exist // return error if the setter to be deleted doesn't exist
if definitions.Field(key) == nil { if definitions == nil || definitions.Field(key) == nil {
return nil, errors.Errorf("%s with name %s does not exist", defType, dd.Name) return nil, errors.Errorf("%s %q does not exist", defType, dd.Name)
} }
subst := SubstReferringDefinition(definitions, key) subst := SubstReferringDefinition(definitions, key)
if subst != "" { if subst != "" {
return nil, errors.Errorf("%s is used in substitution %s, please delete the parent substitution first", defType, subst) return nil, errors.Errorf("%s %q is used in substitution %q, please delete the parent substitution first", defType, dd.Name, subst)
} }
_, err = definitions.Pipe(yaml.FieldClearer{Name: key}) _, err = definitions.Pipe(yaml.FieldClearer{Name: key})

View File

@@ -38,7 +38,7 @@ metadata:
spec: spec:
replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"} replicas: 3 # {"$ref": "#/definitions/io.k8s.cli.setters.replicas"}
` `
_, err := openapi.AddSchema([]byte(schema)) // add the schema definitions err := openapi.AddSchema([]byte(schema)) // add the schema definitions
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -103,7 +103,7 @@ spec:
image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"} image: nginx:1.7.9 # {"$ref": "#/definitions/io.k8s.cli.substitutions.image"}
` `
_, err := openapi.AddSchema([]byte(schema)) // add the schema definitions err := openapi.AddSchema([]byte(schema)) // add the schema definitions
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@@ -367,14 +367,14 @@ func (s SetOpenAPI) Filter(object *yaml.RNode) (*yaml.RNode, error) {
return nil, err return nil, err
} }
if oa == nil { if oa == nil {
return nil, errors.Errorf("no setter %s found", s.Name) return nil, errors.Errorf("setter %q is not found", s.Name)
} }
def, err := oa.Pipe(yaml.Lookup("x-k8s-cli", "setter")) def, err := oa.Pipe(yaml.Lookup("x-k8s-cli", "setter"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if def == nil { if def == nil {
return nil, errors.Errorf("no setter %s found", s.Name) return nil, errors.Errorf("setter %q is not found", s.Name)
} }
// record the OpenAPI type for the setter // record the OpenAPI type for the setter

View File

@@ -954,7 +954,7 @@ func initSchema(t *testing.T, s string) {
openapi.ResetOpenAPI() openapi.ResetOpenAPI()
// add the json schema to the global schema // add the json schema to the global schema
_, err = openapi.AddSchema(j) err = openapi.AddSchema(j)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
@@ -1271,7 +1271,7 @@ openAPI:
{ {
name: "error", name: "error",
setter: "replicas", setter: "replicas",
err: "no setter replicas found", err: `setter "replicas" is not found`,
input: ` input: `
openAPI: openAPI:
definitions: definitions:

View File

@@ -75,8 +75,10 @@ func TestDeleterCreator_Delete(t *testing.T) {
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
dc.OpenAPIPath = openAPI.Name()
dc.ResourcesPath = resource.Name()
err = dc.Delete(openAPI.Name(), resource.Name()) err = dc.Delete()
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }

View File

@@ -17,24 +17,34 @@ type DeleterCreator struct {
// DefinitionPrefix is the prefix of the OpenAPI definition type // DefinitionPrefix is the prefix of the OpenAPI definition type
DefinitionPrefix string DefinitionPrefix string
RecurseSubPackages bool
OpenAPIFileName string
// Path to openAPI file
OpenAPIPath string
// Path to resources folder
ResourcesPath string
} }
func (d DeleterCreator) Delete(openAPIPath, resourcesPath string) error { func (d DeleterCreator) Delete() error {
dd := setters2.DeleterDefinition{ dd := setters2.DeleterDefinition{
Name: d.Name, Name: d.Name,
DefinitionPrefix: d.DefinitionPrefix, DefinitionPrefix: d.DefinitionPrefix,
} }
if err := dd.DeleteFromFile(openAPIPath); err != nil { if err := dd.DeleteFromFile(d.OpenAPIPath); err != nil {
return err return err
} }
// Load the updated definitions // Load the updated definitions
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil { if err := openapi.AddSchemaFromFile(d.OpenAPIPath); err != nil {
return err return err
} }
// Update the resources with the deleter reference // Update the resources with the deleter reference
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath} inout := &kio.LocalPackageReadWriter{PackagePath: d.ResourcesPath, PackageFileName: d.OpenAPIFileName}
return kio.Pipeline{ return kio.Pipeline{
Inputs: []kio.Reader{inout}, Inputs: []kio.Reader{inout},
Filters: []kio.Filter{kio.FilterAll( Filters: []kio.Filter{kio.FilterAll(

View File

@@ -32,16 +32,20 @@ type FieldSetter struct {
OpenAPIPath string OpenAPIPath string
OpenAPIFileName string
ResourcesPath string ResourcesPath string
RecurseSubPackages bool
} }
func (fs *FieldSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) { func (fs *FieldSetter) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
fs.Count, _ = fs.Set(fs.OpenAPIPath, fs.ResourcesPath) fs.Count, _ = fs.Set()
return nil, nil return nil, nil
} }
// Set updates the OpenAPI definitions and resources with the new setter value // Set updates the OpenAPI definitions and resources with the new setter value
func (fs FieldSetter) Set(openAPIPath, resourcesPath string) (int, error) { func (fs FieldSetter) Set() (int, error) {
// Update the OpenAPI definitions // Update the OpenAPI definitions
soa := setters2.SetOpenAPI{ soa := setters2.SetOpenAPI{
Name: fs.Name, Name: fs.Name,
@@ -55,30 +59,30 @@ func (fs FieldSetter) Set(openAPIPath, resourcesPath string) (int, error) {
// at to get the value and set it to resource files, but if there is error // at to get the value and set it to resource files, but if there is error
// after updating openAPI file and while updating resources, the openAPI // after updating openAPI file and while updating resources, the openAPI
// file should be reverted, as set operation failed // file should be reverted, as set operation failed
stat, err := os.Stat(openAPIPath) stat, err := os.Stat(fs.OpenAPIPath)
if err != nil { if err != nil {
return 0, err return 0, err
} }
curOpenAPI, err := ioutil.ReadFile(openAPIPath) curOpenAPI, err := ioutil.ReadFile(fs.OpenAPIPath)
if err != nil { if err != nil {
return 0, err return 0, err
} }
// write the new input value to openAPI file // write the new input value to openAPI file
if err := soa.UpdateFile(openAPIPath); err != nil { if err := soa.UpdateFile(fs.OpenAPIPath); err != nil {
return 0, err return 0, err
} }
// Load the updated definitions // Load the updated definitions
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil { if err := openapi.AddSchemaFromFile(fs.OpenAPIPath); err != nil {
return 0, err return 0, err
} }
// Update the resources with the new value // Update the resources with the new value
// Set NoDeleteFiles to true as SetAll will return only the nodes of files which should be updated and // Set NoDeleteFiles to true as SetAll will return only the nodes of files which should be updated and
// hence, rest of the files should not be deleted // hence, rest of the files should not be deleted
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath, NoDeleteFiles: true} inout := &kio.LocalPackageReadWriter{PackagePath: fs.ResourcesPath, NoDeleteFiles: true, PackageFileName: fs.OpenAPIFileName}
s := &setters2.Set{Name: fs.Name} s := &setters2.Set{Name: fs.Name}
err = kio.Pipeline{ err = kio.Pipeline{
Inputs: []kio.Reader{inout}, Inputs: []kio.Reader{inout},
@@ -88,7 +92,7 @@ func (fs FieldSetter) Set(openAPIPath, resourcesPath string) (int, error) {
// revert openAPI file if set operation fails // revert openAPI file if set operation fails
if err != nil { if err != nil {
if writeErr := ioutil.WriteFile(openAPIPath, curOpenAPI, stat.Mode().Perm()); writeErr != nil { if writeErr := ioutil.WriteFile(fs.OpenAPIPath, curOpenAPI, stat.Mode().Perm()); writeErr != nil {
return 0, writeErr return 0, writeErr
} }
} }

View File

@@ -47,6 +47,10 @@ type SetterCreator struct {
// the package publisher's intent to make the setter required to be set. // the package publisher's intent to make the setter required to be set.
Required bool Required bool
RecurseSubPackages bool
OpenAPIFileName string
// Path to openAPI file // Path to openAPI file
OpenAPIPath string OpenAPIPath string
@@ -55,17 +59,21 @@ type SetterCreator struct {
} }
func (c *SetterCreator) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) { func (c *SetterCreator) Filter(input []*yaml.RNode) ([]*yaml.RNode, error) {
return nil, c.Create(c.OpenAPIPath, c.ResourcesPath) return nil, c.Create()
} }
func (c SetterCreator) Create(openAPIPath, resourcesPath string) error { func (c SetterCreator) Create() error {
err := validateSchema(c.Schema) err := c.validateSetterInfo()
if err != nil {
return err
}
err = validateSchema(c.Schema)
if err != nil { if err != nil {
return errors.Errorf("invalid schema: %v", err) return errors.Errorf("invalid schema: %v", err)
} }
// Update the resources with the setter reference // Update the resources with the setter reference
inout := &kio.LocalPackageReadWriter{PackagePath: resourcesPath} inout := &kio.LocalPackageReadWriter{PackagePath: c.ResourcesPath}
a := &setters2.Add{ a := &setters2.Add{
FieldName: c.FieldName, FieldName: c.FieldName,
FieldValue: c.FieldValue, FieldValue: c.FieldValue,
@@ -78,7 +86,7 @@ func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
Outputs: []kio.Writer{inout}, Outputs: []kio.Writer{inout},
}.Execute() }.Execute()
if a.Count == 0 { if a.Count == 0 {
fmt.Printf("setter %s doesn't match any field in resource configs, "+ fmt.Printf("setter %q doesn't match any field in resource configs, "+
"but creating setter definition\n", c.Name) "but creating setter definition\n", c.Name)
} }
if err != nil { if err != nil {
@@ -90,12 +98,12 @@ func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
Name: c.Name, Value: c.FieldValue, SetBy: c.SetBy, Description: c.Description, Name: c.Name, Value: c.FieldValue, SetBy: c.SetBy, Description: c.Description,
Type: c.Type, Schema: c.Schema, Required: c.Required, Type: c.Type, Schema: c.Schema, Required: c.Required,
} }
if err := sd.AddToFile(openAPIPath); err != nil { if err := sd.AddToFile(c.OpenAPIPath); err != nil {
return err return err
} }
// Load the updated definitions // Load the updated definitions
if err := openapi.AddSchemaFromFile(openAPIPath); err != nil { if err := openapi.AddSchemaFromFile(c.OpenAPIPath); err != nil {
return err return err
} }
// if the setter is of array type write the derived list values back to // if the setter is of array type write the derived list values back to
@@ -103,7 +111,7 @@ func (c SetterCreator) Create(openAPIPath, resourcesPath string) error {
if len(a.ListValues) > 0 { if len(a.ListValues) > 0 {
sd.ListValues = a.ListValues sd.ListValues = a.ListValues
sd.Value = "" sd.Value = ""
if err := sd.AddToFile(openAPIPath); err != nil { if err := sd.AddToFile(c.OpenAPIPath); err != nil {
return err return err
} }
} }
@@ -165,3 +173,32 @@ func validateSchemaTypes(sc *spec.Schema) error {
} }
return nil return nil
} }
func (c SetterCreator) validateSetterInfo() error {
// check if substitution with same name exists and throw error
ref, err := spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SubstitutionDefinitionPrefix + c.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 %q already exists, "+
"substitution and setter can't have same name", c.Name)
}
// check if setter with same name exists and throw error
ref, err = spec.NewRef(fieldmeta.DefinitionsPrefix + fieldmeta.SetterDefinitionPrefix + c.Name)
if err != nil {
return err
}
setter, _ := openapi.Resolve(&ref)
// if setter already exists with the input setter name, throw error
if setter != nil {
return errors.Errorf("setter with name %q already exists, "+
"if you want to modify it, please delete the existing setter and recreate it", c.Name)
}
return nil
}

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