Compare commits

...

106 Commits

Author SHA1 Message Date
Kubernetes Prow Robot
3c321ef79c Merge pull request #4138 from KnVerey/pinToCmdConfig
Pin to cmd/config v0.10.0
2021-08-24 11:38:58 -07:00
Katrina Verey
7938fdb596 Pin to cmd/config v0.10.0 2021-08-24 11:27:56 -07:00
Kubernetes Prow Robot
a2111869e6 Merge pull request #4137 from KnVerey/pinToKyaml
Pin to kyaml v0.11.1
2021-08-24 11:10:59 -07:00
Katrina Verey
f8288e2f02 Pin to kyaml v0.11.1 2021-08-24 10:59:16 -07:00
Jeff Regan
f2f90d1185 Merge pull request #4114 from monopole/anchorsAweigh
Add YAML anchor/alias expansion.
2021-08-24 09:51:49 -07:00
Kubernetes Prow Robot
459e800ecf Merge pull request #4134 from phanimarupaka/FieldOrderingForNullFields
Preserve field order: Add test for null value
2021-08-23 16:44:14 -07:00
Phani Teja Marupaka
8f00d3fd53 Add test for null value 2021-08-21 15:58:49 -07:00
Kubernetes Prow Robot
cd94cb13c6 Merge pull request #4132 from natasha41575/moveWorkDirCheck
move check for working dir for exec functions
2021-08-20 11:11:31 -07:00
Natasha Sarkar
e100be620e move check for working dir for exec functions 2021-08-20 10:59:29 -07:00
Natasha Sarkar
1e1b9b484a exec function working dir is the kustomization that referenced it (#4125)
* exec function working dir is the kustomization that referenced it

* suggested changes

* more code review

* use a field instead of an annotation

* more code review
2021-08-19 20:15:24 -07:00
monopole
360585dfaf Add YAML anchor/alias expansion. 2021-08-19 11:55:09 -07:00
Kubernetes Prow Robot
f604619dd5 Merge pull request #4086 from campoy/pkgio-writer-filesys
Add FileSystem interface to LocalPackageWriter
2021-08-19 11:51:25 -07:00
Francesc Campoy
0fa056327a Skip in-memory tests for Windows and generate temporary directories correctly for Windows tests. 2021-08-19 10:27:39 -07:00
Francesc Campoy
6db2bf69f3 Add FileSystem interface to LocalPackageReadWriter and hook to LocalPackageWriter 2021-08-19 10:27:38 -07:00
Francesc Campoy
20fb9578c0 Adapt tests to new FileSystem field 2021-08-19 10:27:38 -07:00
Francesc Campoy
4eb8232495 Added FileSystem field to kio.LocalPackageWriter 2021-08-19 10:27:38 -07:00
Jeff Regan
6c4e8019f8 Merge pull request #4094 from brianpursley/merge3-test
Add unit tests to cover existing behavior of three way merge and strategic merge patch
2021-08-19 07:41:18 -07:00
Jeff Regan
28707bf5df Merge pull request #4065 from natasha41575/originDataAsAnnotation
option for origin data as annotation
2021-08-18 19:08:54 -07:00
Mike Borozdin
023a580f00 support for darwin and linux (#4122)
* support for darwin and linux

* updated to use go for env variables
2021-08-17 15:58:08 -07:00
Kubernetes Prow Robot
a2eaae5555 Merge pull request #4113 from KnVerey/kyaml_container_patch_kinds
Enable fn framework container patches to work with more kinds
2021-08-17 11:31:13 -07:00
Katrina Verey
75df1a5422 kyaml presubmit formatting 2021-08-17 11:13:54 -07:00
Katrina Verey
f0b4cc4581 ContainerPatch supports all common workload paths 2021-08-17 11:13:52 -07:00
Katrina Verey
7a41e479c9 Filter helper for fallback field lookup 2021-08-17 11:13:10 -07:00
Natasha Sarkar
3350c7213c option for origin data as annotation 2021-08-12 20:09:41 -07:00
Adrian Berger
7b5e43d343 Feature: Add edit set annotation (#4073)
* Add edit set annotation feature

* Apply suggested code improvements

* Apply suggested changes

* Fix regex, add more tests

* Add constant for common error message

* Fix too many characters per line error

* Use string concatenation instead, add FailNow call
2021-08-10 16:00:40 -07:00
Kubernetes Prow Robot
06661ea310 Merge pull request #4112 from natasha41575/updateOpenAPI
update openapi to v1.21.2
2021-08-10 10:44:40 -07:00
Natasha Sarkar
38b2b33503 update openapi to v1.21.2 2021-08-10 10:30:12 -07:00
Natasha Sarkar
f735d6fb3a Merge pull request #4110 from KnVerey/update-openapi-scripts
Update openapi updater scripts
2021-08-10 09:38:11 -07:00
Katrina Verey
5cb5e07ac0 Update openapi updater scripts 2021-08-09 11:44:27 -07:00
Kubernetes Prow Robot
54778504ed Merge pull request #4087 from campoy/fsnode-alias-fix
Avoid aliasing in fsnode by forcing copies for file contents
2021-08-04 20:32:56 -07:00
Kubernetes Prow Robot
1bfe0d08dc Merge pull request #4102 from monopole/anchorsNotHonored
Kyaml anchor failure test.
2021-08-04 19:40:55 -07:00
monopole
56da9a58fc Kyaml anchor failure test. 2021-08-04 17:10:04 -07:00
Kubernetes Prow Robot
54383bca25 Merge pull request #4085 from campoy/pkgio-reader-filesys
Add a filesys.FileSystem interface to kio.LocalPackageReader
2021-08-03 15:33:04 -07:00
Kubernetes Prow Robot
88461b4fed Merge pull request #4088 from natasha41575/noMoreGenArgs
replace genargs with two separate annotations
2021-08-03 14:07:03 -07:00
brianpursley
aabbea3e78 Add unit tests for current behavior of strategic merge patch 2021-08-02 20:36:55 -04:00
brianpursley
adedca09f2 Add unit tests for current behavior of three way merge 2021-08-02 20:36:28 -04:00
Natasha Sarkar
9a27a9f19f replace genargs with two separate annotations 2021-07-29 15:46:09 -07:00
Kubernetes Prow Robot
3ebdb3fcef Merge pull request #4091 from natasha41575/RemovePathAnno
remove annotations added by the kyaml reader
2021-07-28 11:28:48 -07:00
Natasha Sarkar
97e7cb1512 remove annotations added by the kyaml reader 2021-07-27 17:56:58 -07:00
Francesc Campoy
262a2d9288 Avoid testing in memory for Windows tests 2021-07-26 16:14:46 -07:00
Kubernetes Prow Robot
91b862b556 Merge pull request #4084 from kubernetes-sigs/kustomize-in-kubectl-1.22
Update kustomize-in-kubectl chart for 1.22
2021-07-26 13:42:34 -07:00
Katrina Verey
b8ffc725c7 Update kustomize-in-kubectl chart for 1.22 2021-07-26 13:23:45 -07:00
Jeff Regan
76f1411922 Merge pull request #4082 from monopole/autoChanges
Automated go.sum and fmt changes under go 1.16.6
2021-07-24 09:42:29 -07:00
monopole
d1003d6f8f Automated changes under go 1.16.6 2021-07-24 09:18:58 -07:00
Natasha Sarkar
91f74e8d16 replace Resource.options with annotations (#4061) 2021-07-23 18:19:05 -07:00
Kubernetes Prow Robot
94c5096a95 Merge pull request #4048 from natasha41575/DeprecateCfgCmds
Deprecate some cfg commands
2021-07-21 18:15:51 -07:00
Kubernetes Prow Robot
f35aeb6a8e Merge pull request #4077 from abutcher/cdpath
Unset CDPATH in hack/install_kustomize.sh
2021-07-21 10:14:07 -07:00
Kubernetes Prow Robot
d6ce846047 Merge pull request #4076 from KnVerey/kyaml_fixture_fix
Make UpdateExpectedFromActual work with hierarchical testdata directories
2021-07-21 08:52:08 -07:00
Andrew Butcher
ec069e4f19 Unset CDPATH to restore default cd behavior. 2021-07-21 11:38:44 -04:00
Katrina Verey
c5adafd9ce Make UpdateExpectedFromActual work with hierarchical testdata directories 2021-07-20 18:13:04 -07:00
Natasha Sarkar
16dcc98cff deprecate some cfg commands 2021-07-19 14:31:20 -07:00
Kubernetes Prow Robot
59c410a70a Merge pull request #4063 from dosmanak/mdrip_MYGOBIN
fix: Allow custom MYGOBIN in mdrip invocations
2021-07-19 10:30:52 -07:00
Francesc Campoy
9b586162d0 Add FileSystem interface to LocalPackageReadWriter and hook to LocalPackageReader 2021-07-16 14:48:22 -07:00
Francesc Campoy
803885049b Avoid aliasing in fsnode by forcing copies for file contents and add a test 2021-07-16 13:56:08 -07:00
Francesc Campoy
be4fe7540e Simplified tests with helper func 2021-07-16 11:06:32 -07:00
Francesc Campoy
927568eea2 Replace os.Stat call with FileSystem.Exists 2021-07-16 09:27:20 -07:00
Francesc Campoy
436d5e717c All LocalPackageReader tests adapted, one fails 2021-07-16 09:12:42 -07:00
Francesc Campoy
d37fa66ebc Adapt more LocalPackageReader tests 2021-07-15 18:21:42 -07:00
Francesc Campoy
e8a4bf6edc Add FileSystem field to LocalPackageReader and one of its tests 2021-07-15 18:06:30 -07:00
Kubernetes Prow Robot
35d1c3f9b4 Merge pull request #4072 from natasha41575/Revert
revert 'fix kyaml issue with multiline scalars'
2021-07-15 14:37:02 -07:00
Natasha Sarkar
e17785af21 revert 'fix kyaml issue with multiline scalars' 2021-07-15 14:24:39 -07:00
Natasha Sarkar
0537b59f27 support yaml formatted openapi schema (#4017)
* support yaml formatted openapi schema

* suggested changes
2021-07-15 14:11:02 -07:00
Kubernetes Prow Robot
339e33d2f3 Merge pull request #4071 from KnVerey/update-yaml-script
Update fork updater script
2021-07-15 10:06:45 -07:00
Natasha Sarkar
f082ac02cf fix multiline scalar value issue 2021-07-15 08:41:03 -07:00
Katrina Verey
9538ae1258 Update fork updater script 2021-07-15 08:31:32 -07:00
Kubernetes Prow Robot
34981b664f Merge pull request #4069 from natasha41575/LineBreakIssue
fix kyaml issue with multiline scalars
2021-07-14 16:20:47 -07:00
Natasha Sarkar
477d8930e0 fix kyaml issue with multiline scalars 2021-07-14 15:19:21 -07:00
Kubernetes Prow Robot
b5091a566a Merge pull request #4067 from natasha41575/demonstrateLineBreakIssue
demonstrate line break preservation issue in kyaml
2021-07-14 11:52:18 -07:00
Natasha Sarkar
9981c45554 demonstrate line break preservation issue in kyaml 2021-07-14 11:39:14 -07:00
phani
0f736ec7fd Handle comments for seq indent derivation (#4064)
* Handle comments for seq indent derivation

* Suggested changes
2021-07-13 17:46:26 -07:00
Kubernetes Prow Robot
7826ad1e06 Merge pull request #4031 from rjferguson21/prefix-overlay-fail
Add failing test for replacements when using an overlay with a namePrefix
2021-07-13 14:42:01 -07:00
Frank Farzan
f4e6816338 Expand documentation of annotations used in manifests and KRM functions API (#3995)
* Expand documentation of annotations used in manifests and KRM function wire format.

- Reserve `internal.config.kubernetes.io` for control annotations
- Document `local-config` annotation in a seperate document (It's
  orthogonal to KRM functions).
- There is a internal annotation that uses `config.k8s.io` instead of
  `config.kubernetes.io` used by other annotations. See [1] and [2]. We
  should avoid using two seperate annotation prefixes and audit the
  codebase for any other annotation. Given the `id` control annotation is used
  for comment preservation (no existing function should be modifying
  it), I suggest moving this over to use
  `fn-ctrl.config.kubernets.io/id`.

[1]: 7e8ba62e9f/kyaml/fn/runtime/runtimeutil/runtimeutil.go (L195)
[2]: https://github.com/kubernetes-sigs/kustomize/pull/2465

* Move path/index annotation to use internal prefix

* Clarify MUST NOT vs SHOULD NOT for internal annotations

* Update cmd/config/docs/api-conventions/functions-spec.md

Co-authored-by: Katrina Verey <kn.verey@gmail.com>

* Update cmd/config/docs/api-conventions/functions-spec.md

Co-authored-by: Katrina Verey <kn.verey@gmail.com>

* Update cmd/config/docs/api-conventions/manifest-annotations.md

Co-authored-by: Katrina Verey <kn.verey@gmail.com>

* Remove kusotmization as example

Co-authored-by: Katrina Verey <kn.verey@gmail.com>
2021-07-13 11:58:00 -07:00
Kubernetes Prow Robot
4a13725678 Merge pull request #4043 from phanimarupaka/PreserveIndentation
Make seq indent configurable and add retain seq indent functionality
2021-07-13 10:24:30 -07:00
Petr Studeny
ab9b010856 fix: Allow custom MYGOBIN in mdrip invokations 2021-07-13 14:34:50 +02:00
Phani Teja Marupaka
29be7fabe4 Suggested changes 2021-07-12 23:34:03 -07:00
Phani Teja Marupaka
74e867833a First sequence indent wins 2021-07-09 14:10:30 -07:00
Rob Ferguson
91dc6d2a0f add additional replacement transformer test with todos for failures 2021-07-09 12:45:47 -05:00
Rob Ferguson
3c1fd0e9cf add replacement test failures 2021-07-09 10:39:38 -05:00
Rob Ferguson
4deeb7d59b make replacement transformer test pass and add todo 2021-07-09 09:49:08 -05:00
Phani Teja Marupaka
89b12cfc62 Change annotation name, error if conflicting options 2021-07-09 01:32:48 -07:00
Phani Teja Marupaka
c07ffa5c1e Update comments, tests, not expose indent option 2021-07-09 00:17:09 -07:00
Kubernetes Prow Robot
259fcfcef8 Merge pull request #4002 from natasha41575/resref
replace Resource.refBy with annotations
2021-07-08 17:26:54 -07:00
Phani Teja Marupaka
f81201b74d Add options, keep seqindent annotation equivalent to index annotation 2021-07-08 14:54:23 -07:00
Phani Teja Marupaka
6dbc74b32e Suggested changes 2021-07-07 23:22:15 -07:00
Phani Teja Marupaka
ed38b5fe2b Make seq indent configurable and add retain seq indent functionality 2021-07-07 16:53:40 -07:00
Natasha Sarkar
a84badb834 replace Resource.refBy with annotations 2021-07-07 15:22:36 -07:00
phani
e1804cbc76 Retain field order after running any arbitrary functions on resources (#4021)
* Reorder resource fields

* Fix comment conflict

* Update e2e test ordering

* Suggested changes
2021-07-07 10:12:44 -07:00
Kubernetes Prow Robot
d13eef7951 Merge pull request #4040 from zhouhaibing089/fix-iampolicy-mod
iampolicygenerator: update module name
2021-07-07 09:46:44 -07:00
Kubernetes Prow Robot
0b4c6baf44 Merge pull request #4039 from zhouhaibing089/handle-dot-git-suffix
api/internal/git: handle .git suffix in repospec
2021-07-07 09:24:43 -07:00
Haibing Zhou
b3af54340c iampolicygenerator: update module name
The previous module name is incorrect and seems to be copied from
another builtin plugin. This change fixes the name.
2021-07-05 14:11:32 -07:00
Haibing Zhou
8c14b9d1af api/internal/git: handle .git suffix in repospec
This change adds a new test case for parsing url with `.git` suffix. In
that case, we should have the full url as clone spec with an empty
abspath.
2021-07-05 13:10:35 -07:00
Natasha Sarkar
d818ccae92 Merge pull request #4037 from natasha41575/pin
Unpin modules
2021-07-02 14:25:03 -07:00
Natasha Sarkar
4cea8b9785 Unpin modules 2021-07-02 14:10:05 -07:00
Kubernetes Prow Robot
84a36801e0 Merge pull request #4036 from joebowbeer/patch-1
Fix broken KEP link in README
2021-07-02 13:58:10 -07:00
Joe Bowbeer
6eb7b3508d Fix broken KEP link in README 2021-07-02 13:12:22 -07:00
Rob Ferguson
2a5f4ac7d7 add failing test for replacements 2021-07-02 08:54:52 -05:00
Kubernetes Prow Robot
518a16d3ac Merge pull request #4014 from brianpursley/doc-separator
Updated ByteReader to allow white space and comments content after --- when splitting YAML documents
2021-07-01 14:25:51 -07:00
Jeff Regan
d53a2ad45d Merge pull request #4027 from monopole/deleteWindowsBuild
Temporarily remove windows build.
2021-06-30 15:37:56 -07:00
monopole
bb02a7645b Temporarily remove windows build. 2021-06-30 15:36:57 -07:00
Jeff Regan
5a9d90c872 Merge pull request #4026 from monopole/upgradeGoReleaser
release process: upgrade to goreleaser v0.172.1 calling go1.16.5
2021-06-30 14:04:18 -07:00
monopole
4fd7269643 release process: upgrade to goreleaser v0.172.1 calling go go1.16.5 2021-06-30 14:02:55 -07:00
Kubernetes Prow Robot
1eb3c1a075 Merge pull request #4025 from KnVerey/pinToApi
Pin to api v0.8.11
2021-06-30 12:53:03 -07:00
Katrina Verey
a1746f2f8c Pin to api v0.8.11 2021-06-30 12:42:04 -07:00
Kubernetes Prow Robot
b727febd08 Merge pull request #4023 from mengqiy/jsontag
add missing json tags
2021-06-30 12:37:03 -07:00
Mengqi Yu
02d14d724a add missing json tags 2021-06-30 12:15:50 -07:00
brianpursley
78737f5a38 Updated ByteReader to allow white space and comments on the same line after --- and throw an error if anything else is detected 2021-06-24 21:32:39 -04:00
145 changed files with 11135 additions and 2417 deletions

View File

@@ -4,6 +4,8 @@
# Makefile for kustomize CLI and API.
SHELL := /usr/bin/env bash
GOOS = $(shell go env GOOS)
GOARCH = $(shell go env GOARCH)
MYGOBIN = $(shell go env GOBIN)
ifeq ($(MYGOBIN),)
MYGOBIN = $(shell go env GOPATH)/bin
@@ -289,8 +291,8 @@ $(MYGOBIN)/kubeval:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz; \
tar xf kubeval-linux-amd64.tar.gz; \
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-$(GOOS)-$(GOARCH).tar.gz; \
tar xf kubeval-$(GOOS)-$(GOARCH).tar.gz; \
mv kubeval $(MYGOBIN); \
rm -rf $$d; \
)
@@ -304,10 +306,10 @@ $(MYGOBIN)/helmV2:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
tgzFile=helm-v2.13.1-linux-amd64.tar.gz; \
tgzFile=helm-v2.13.1-$(GOOS)-$(GOARCH).tar.gz; \
wget https://storage.googleapis.com/kubernetes-helm/$$tgzFile; \
tar -xvzf $$tgzFile; \
mv linux-amd64/helm $(MYGOBIN)/helmV2; \
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV2; \
rm -rf $$d \
)
@@ -317,10 +319,10 @@ $(MYGOBIN)/helmV3:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
tgzFile=helm-v3.5.3-linux-amd64.tar.gz; \
tgzFile=helm-v3.5.3-$(GOOS)-$(GOARCH).tar.gz; \
wget https://get.helm.sh/$$tgzFile; \
tar -xvzf $$tgzFile; \
mv linux-amd64/helm $(MYGOBIN)/helmV3; \
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \
rm -rf $$d \
)
@@ -328,7 +330,7 @@ $(MYGOBIN)/kind:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
wget -O ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(shell uname)-amd64; \
wget -O ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(GOOS)-$(GOARCH); \
chmod +x ./kind; \
mv ./kind $(MYGOBIN); \
rm -rf $$d; \
@@ -339,10 +341,10 @@ $(MYGOBIN)/gh:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
tgzFile=gh_1.0.0_linux_amd64.tar.gz; \
tgzFile=gh_1.0.0_$(GOOS)_$(GOARCH).tar.gz; \
wget https://github.com/cli/cli/releases/download/v1.0.0/$$tgzFile; \
tar -xvzf $$tgzFile; \
mv gh_1.0.0_linux_amd64/bin/gh $(MYGOBIN)/gh; \
mv gh_1.0.0_$(GOOS)_$(GOARCH)/bin/gh $(MYGOBIN)/gh; \
rm -rf $$d \
)

View File

@@ -32,6 +32,7 @@ will be reflected in the Kubernetes release notes.
| < v1.14 | n/a |
| v1.14-v1.20 | v2.0.3 |
| v1.21 | v4.0.5 |
| v1.22 | v4.2.0 |
[v2.0.3]: /../../tree/v2.0.3
[#2506]: https://github.com/kubernetes-sigs/kustomize/issues/2506
@@ -151,7 +152,7 @@ is governed by the [Kubernetes Code of Conduct].
[`make`]: https://www.gnu.org/software/make
[`sed`]: https://www.gnu.org/software/sed
[DAM]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#declarative-application-management
[KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/0008-kustomize.md
[KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/2377-Kustomize/README.md
[Kubernetes Code of Conduct]: code-of-conduct.md
[applied]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#apply
[base]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#base

View File

@@ -1514,9 +1514,9 @@ kind: Deployment
metadata:
name: pre-deploy
annotations:
config.kubernetes.io/previousNames: deploy,deploy
config.kubernetes.io/previousKinds: CronJob,Deployment
config.kubernetes.io/previousNamespaces: default,default
internal.config.kubernetes.io/previousNames: deploy,deploy
internal.config.kubernetes.io/previousKinds: CronJob,Deployment
internal.config.kubernetes.io/previousNamespaces: default,default
spec:
template:
spec:
@@ -1543,9 +1543,9 @@ kind: Deployment
metadata:
name: pre-deploy
annotations:
config.kubernetes.io/previousNames: deploy,deploy
config.kubernetes.io/previousKinds: CronJob,Deployment
config.kubernetes.io/previousNamespaces: default,default
internal.config.kubernetes.io/previousNames: deploy,deploy
internal.config.kubernetes.io/previousKinds: CronJob,Deployment
internal.config.kubernetes.io/previousNamespaces: default,default
spec:
template:
spec:

View File

@@ -11,6 +11,6 @@ require (
github.com/stretchr/testify v1.5.1
gopkg.in/yaml.v2 v2.4.0
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
sigs.k8s.io/kustomize/kyaml v0.11.0
sigs.k8s.io/kustomize/kyaml v0.11.1
sigs.k8s.io/yaml v1.2.0
)

View File

@@ -223,8 +223,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -897,7 +897,9 @@ func TestNameReferenceClusterWide(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
expected.RemoveBuildAnnotations()
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf(notEqualErrFmt, err)
}

View File

@@ -362,10 +362,10 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
"metadata": map[string]interface{}{
"name": "sub-backendOne",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "Service",
"config.kubernetes.io/previousNames": "backendOne",
"config.kubernetes.io/previousNamespaces": "default",
"config.kubernetes.io/prefixes": "sub-",
"internal.config.kubernetes.io/previousKinds": "Service",
"internal.config.kubernetes.io/previousNames": "backendOne",
"internal.config.kubernetes.io/previousNamespaces": "default",
"internal.config.kubernetes.io/prefixes": "sub-",
},
}}).ResMap()

View File

@@ -125,7 +125,7 @@ func parseGitUrl(n string) (
index := strings.Index(n, gitSuffix)
orgRepo = n[0:index]
n = n[index+len(gitSuffix):]
if n[0] == '/' {
if len(n) > 0 && n[0] == '/' {
n = n[1:]
}
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)

View File

@@ -182,6 +182,12 @@ func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) {
absPath: notCloned.String(),
ref: "",
},
"t12": {
input: "https://bitbucket.example.com/scm/project/repository.git",
cloneSpec: "https://bitbucket.example.com/scm/project/repository.git",
absPath: notCloned.String(),
ref: "",
},
}
for tn, tc := range testcases {
t.Run(tn, func(t *testing.T) {

View File

@@ -79,6 +79,7 @@ func NewFnPlugin(o *types.FnPluginLoadingOptions) *FnPlugin {
StorageMounts: toStorageMounts(o.Mounts),
Env: o.Env,
AsCurrentUser: o.AsCurrentUser,
WorkingDir: o.WorkingDir,
},
}
}

View File

@@ -47,6 +47,11 @@ func (l *Loader) Config() *types.PluginConfig {
return l.pc
}
// SetWorkDir sets the working directory for this loader's plugins
func (l *Loader) SetWorkDir(wd string) {
l.pc.FnpLoadingOptions.WorkingDir = wd
}
func (l *Loader) LoadGenerators(
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
var result []resmap.Generator

View File

@@ -231,10 +231,10 @@ func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {
if err := r.SetAnnotations(annotations); err != nil {
return nil, err
}
r.SetOptions(types.NewGenArgs(
&types.GeneratorArgs{
Behavior: behavior,
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}}))
if needsHash {
r.EnableHashSuffix()
}
r.SetBehavior(types.NewGenerationBehavior(behavior))
}
return rm, nil
}

View File

@@ -16,8 +16,10 @@ import (
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/yaml"
@@ -38,11 +40,13 @@ func NewKustTarget(
validator ifc.Validator,
rFactory *resmap.Factory,
pLdr *loader.Loader) *KustTarget {
pLdrCopy := *pLdr
pLdrCopy.SetWorkDir(ldr.Root())
return &KustTarget{
ldr: ldr,
validator: validator,
rFactory: rFactory,
pLdr: pLdr,
pLdr: &pLdrCopy,
}
}
@@ -108,7 +112,7 @@ func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
}
func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) {
ra, err := kt.AccumulateTarget()
ra, err := kt.AccumulateTarget(&resource.Origin{})
if err != nil {
return nil, err
}
@@ -151,20 +155,29 @@ func (kt *KustTarget) addHashesToNames(
// holding customized resources and the data/rules used
// to do so. The name back references and vars are
// not yet fixed.
func (kt *KustTarget) AccumulateTarget() (
// The origin parameter is used through the recursive calls
// to annotate each resource with information about where
// the resource came from, e.g. the file and/or the repository
// it originated from.
// As an entrypoint, one can pass an empty resource.Origin object to
// AccumulateTarget. As AccumulateTarget moves recursively
// through kustomization directories, it updates `origin.path`
// accordingly. When a remote base is found, it updates `origin.repo`
// and `origin.ref` accordingly.
func (kt *KustTarget) AccumulateTarget(origin *resource.Origin) (
ra *accumulator.ResAccumulator, err error) {
return kt.accumulateTarget(accumulator.MakeEmptyAccumulator())
return kt.accumulateTarget(accumulator.MakeEmptyAccumulator(), origin)
}
// ra should be empty when this KustTarget is a Kustomization, or the ra of the parent if this KustTarget is a Component
// (or empty if the Component does not have a parent).
func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator, origin *resource.Origin) (
resRa *accumulator.ResAccumulator, err error) {
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources)
ra, err = kt.accumulateResources(ra, kt.kustomization.Resources, origin)
if err != nil {
return nil, errors.Wrap(err, "accumulating resources")
}
ra, err = kt.accumulateComponents(ra, kt.kustomization.Components)
ra, err = kt.accumulateComponents(ra, kt.kustomization.Components, origin)
if err != nil {
return nil, errors.Wrap(err, "accumulating components")
}
@@ -247,7 +260,7 @@ func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error)
}
ra.AppendAll(rm)
}
ra, err := kt.accumulateResources(ra, generatorPaths)
ra, err := kt.accumulateResources(ra, generatorPaths, &resource.Origin{})
if err != nil {
return nil, err
}
@@ -283,8 +296,7 @@ func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]re
}
ra.AppendAll(rm)
}
ra, err := kt.accumulateResources(ra, transformerPaths)
ra, err := kt.accumulateResources(ra, transformerPaths, &resource.Origin{})
if err != nil {
return nil, err
}
@@ -332,16 +344,16 @@ func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) error {
// accumulateResources fills the given resourceAccumulator
// with resources read from the given list of paths.
func (kt *KustTarget) accumulateResources(
ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) {
ra *accumulator.ResAccumulator, paths []string, origin *resource.Origin) (*accumulator.ResAccumulator, error) {
for _, path := range paths {
// try loading resource as file then as base (directory or git repository)
if errF := kt.accumulateFile(ra, path); errF != nil {
if errF := kt.accumulateFile(ra, path, origin); errF != nil {
ldr, err := kt.ldr.New(path)
if err != nil {
return nil, errors.Wrapf(
err, "accumulation err='%s'", errF.Error())
}
ra, err = kt.accumulateDirectory(ra, ldr, false)
ra, err = kt.accumulateDirectory(ra, ldr, origin.Append(path), false)
if err != nil {
return nil, errors.Wrapf(
err, "accumulation err='%s'", errF.Error())
@@ -354,7 +366,7 @@ func (kt *KustTarget) accumulateResources(
// accumulateResources fills the given resourceAccumulator
// with resources read from the given list of paths.
func (kt *KustTarget) accumulateComponents(
ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) {
ra *accumulator.ResAccumulator, paths []string, origin *resource.Origin) (*accumulator.ResAccumulator, error) {
for _, path := range paths {
// Components always refer to directories
ldr, errL := kt.ldr.New(path)
@@ -362,7 +374,8 @@ func (kt *KustTarget) accumulateComponents(
return nil, fmt.Errorf("loader.New %q", errL)
}
var errD error
ra, errD = kt.accumulateDirectory(ra, ldr, true)
origin.Path = filepath.Join(origin.Path, path)
ra, errD = kt.accumulateDirectory(ra, ldr, origin, true)
if errD != nil {
return nil, fmt.Errorf("accumulateDirectory: %q", errD)
}
@@ -371,7 +384,7 @@ func (kt *KustTarget) accumulateComponents(
}
func (kt *KustTarget) accumulateDirectory(
ra *accumulator.ResAccumulator, ldr ifc.Loader, isComponent bool) (*accumulator.ResAccumulator, error) {
ra *accumulator.ResAccumulator, ldr ifc.Loader, origin *resource.Origin, isComponent bool) (*accumulator.ResAccumulator, error) {
defer ldr.Cleanup()
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
err := subKt.Load()
@@ -379,6 +392,7 @@ func (kt *KustTarget) accumulateDirectory(
return nil, errors.Wrapf(
err, "couldn't make target for path '%s'", ldr.Root())
}
subKt.kustomization.BuildMetadata = kt.kustomization.BuildMetadata
var bytes []byte
path := ldr.Root()
if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists {
@@ -402,12 +416,12 @@ func (kt *KustTarget) accumulateDirectory(
var subRa *accumulator.ResAccumulator
if isComponent {
// Components don't create a new accumulator: the kustomization directives are added to the current accumulator
subRa, err = subKt.accumulateTarget(ra)
subRa, err = subKt.accumulateTarget(ra, origin)
ra = accumulator.MakeEmptyAccumulator()
} else {
// Child Kustomizations create a new accumulator which resolves their kustomization directives, which will later
// be merged into the current accumulator.
subRa, err = subKt.AccumulateTarget()
subRa, err = subKt.AccumulateTarget(origin)
}
if err != nil {
return nil, errors.Wrapf(
@@ -422,11 +436,18 @@ func (kt *KustTarget) accumulateDirectory(
}
func (kt *KustTarget) accumulateFile(
ra *accumulator.ResAccumulator, path string) error {
ra *accumulator.ResAccumulator, path string, origin *resource.Origin) error {
resources, err := kt.rFactory.FromFile(kt.ldr, path)
if err != nil {
return errors.Wrapf(err, "accumulating resources from '%s'", path)
}
if utils.StringSliceContains(kt.kustomization.BuildMetadata, "originAnnotations") {
origin = origin.Append(path)
err = resources.AnnotateAll(utils.OriginAnnotation, origin.String())
if err != nil {
return errors.Wrapf(err, "cannot add path annotation for '%s'", path)
}
}
err = ra.AppendAll(resources)
if err != nil {
return errors.Wrapf(err, "merging resources from '%s'", path)

View File

@@ -255,5 +255,5 @@ metadata:
actual.RemoveBuildAnnotations()
actYaml, err := actual.AsYaml()
assert.NoError(t, err)
assert.Equal(t, expYaml, actYaml)
assert.Equal(t, string(expYaml), string(actYaml))
}

View File

@@ -8,6 +8,7 @@ import (
"strings"
"testing"
"sigs.k8s.io/kustomize/api/resource"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/resid"
@@ -65,7 +66,7 @@ vars:
apiVersion: v300
`)
ra, err := makeAndLoadKustTarget(
t, th.GetFSys(), "/app").AccumulateTarget()
t, th.GetFSys(), "/app").AccumulateTarget(&resource.Origin{})
if err != nil {
t.Fatalf("Err: %v", err)
}
@@ -120,7 +121,7 @@ resources:
`)
ra, err := makeAndLoadKustTarget(
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget()
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget(&resource.Origin{})
if err != nil {
t.Fatalf("Err: %v", err)
}
@@ -177,7 +178,7 @@ resources:
- ../o1
`)
_, err := makeAndLoadKustTarget(
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget()
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget(&resource.Origin{})
if err == nil {
t.Fatalf("expected var collision")
}

View File

@@ -0,0 +1,23 @@
package utils
import "sigs.k8s.io/kustomize/api/konfig"
const (
BuildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds"
BuildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames"
BuildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
BuildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
BuildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces"
BuildAnnotationsRefBy = konfig.ConfigAnnoDomain + "/refBy"
BuildAnnotationsGenBehavior = konfig.ConfigAnnoDomain + "/generatorBehavior"
BuildAnnotationsGenAddHashSuffix = konfig.ConfigAnnoDomain + "/needsHashSuffix"
// the following are only for patches, to specify whether they can change names
// and kinds of their targets
BuildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange"
BuildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange"
OriginAnnotation = "config.kubernetes.io/origin"
Enabled = "enabled"
)

View File

@@ -4,25 +4,10 @@ import (
"fmt"
"strings"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
const (
BuildAnnotationPreviousKinds = konfig.ConfigAnnoDomain + "/previousKinds"
BuildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames"
BuildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
BuildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
BuildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces"
// the following are only for patches, to specify whether they can change names
// and kinds of their targets
BuildAnnotationAllowNameChange = konfig.ConfigAnnoDomain + "/allowNameChange"
BuildAnnotationAllowKindChange = konfig.ConfigAnnoDomain + "/allowKindChange"
Allowed = "allowed"
)
// MakeResIds returns all of an RNode's current and previous Ids
func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) {
var result []resid.ResId

View File

@@ -31,11 +31,12 @@ const (
// A program name, for use in help, finding the XDG_CONFIG_DIR, etc.
ProgramName = "kustomize"
// ConfigAnnoDomain is configuration-related annotation namespace.
ConfigAnnoDomain = "config.kubernetes.io"
// ConfigAnnoDomain is internal configuration-related annotation namespace.
// See https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md.
ConfigAnnoDomain = "internal.config.kubernetes.io"
// If a resource has this annotation, kustomize will drop it.
IgnoredByKustomizeAnnotation = ConfigAnnoDomain + "/local-config"
IgnoredByKustomizeAnnotation = "config.kubernetes.io/local-config"
// Label key that indicates the resources are built from Kustomize
ManagedbyLabelKey = "app.kubernetes.io/managed-by"

View File

@@ -0,0 +1,265 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
_ "sigs.k8s.io/kustomize/api/krusty"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestAnnoOriginLocalFiles(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: myService
spec:
ports:
- port: 7002
`)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- service.yaml
buildMetadata: [originAnnotations]
`)
options := th.MakeDefaultOptions()
m := th.Run(".", options)
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Service
metadata:
annotations:
config.kubernetes.io/origin: |
path: service.yaml
name: myService
spec:
ports:
- port: 7002
`)
}
func TestAnnoOriginLocalFilesWithOverlay(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
namePrefix: b-
resources:
- namespace.yaml
- role.yaml
- service.yaml
- deployment.yaml
`)
th.WriteF("base/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: myService
`)
th.WriteF("base/namespace.yaml", `
apiVersion: v1
kind: Namespace
metadata:
name: myNs
`)
th.WriteF("base/role.yaml", `
apiVersion: v1
kind: Role
metadata:
name: myRole
`)
th.WriteF("base/deployment.yaml", `
apiVersion: v1
kind: Deployment
metadata:
name: myDep
`)
th.WriteK("prod", `
namePrefix: p-
resources:
- ../base
- service.yaml
- namespace.yaml
buildMetadata: [originAnnotations]
`)
th.WriteF("prod/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: myService2
`)
th.WriteF("prod/namespace.yaml", `
apiVersion: v1
kind: Namespace
metadata:
name: myNs2
`)
m := th.Run("prod", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Namespace
metadata:
annotations:
config.kubernetes.io/origin: |
path: ../base/namespace.yaml
name: myNs
---
apiVersion: v1
kind: Role
metadata:
annotations:
config.kubernetes.io/origin: |
path: ../base/role.yaml
name: p-b-myRole
---
apiVersion: v1
kind: Service
metadata:
annotations:
config.kubernetes.io/origin: |
path: ../base/service.yaml
name: p-b-myService
---
apiVersion: v1
kind: Deployment
metadata:
annotations:
config.kubernetes.io/origin: |
path: ../base/deployment.yaml
name: p-b-myDep
---
apiVersion: v1
kind: Service
metadata:
annotations:
config.kubernetes.io/origin: |
path: service.yaml
name: p-myService2
---
apiVersion: v1
kind: Namespace
metadata:
annotations:
config.kubernetes.io/origin: |
path: namespace.yaml
name: myNs2
`)
}
// This is a copy of TestGeneratorBasics in configmaps_test.go,
// except that we've enabled the addAnnoOrigin option
// (which doesn't do anything yet).
// TODO: Generated resources should receive the annotation
// config.kubernetes.io/origin: |
// generated-by: path/to/kustomization.yaml
func TestGeneratorWithAnnoOrigin(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
namePrefix: blah-
configMapGenerator:
- name: bob
literals:
- fruit=apple
- vegetable=broccoli
envs:
- foo.env
env: bar.env
files:
- passphrase=phrase.dat
- forces.txt
- name: json
literals:
- 'v2=[{"path": "var/druid/segment-cache"}]'
- >-
druid_segmentCache_locations=[{"path":
"var/druid/segment-cache",
"maxSize": 32000000000,
"freeSpacePercent": 1.0}]
secretGenerator:
- name: bob
literals:
- fruit=apple
- vegetable=broccoli
envs:
- foo.env
files:
- passphrase=phrase.dat
- forces.txt
env: bar.env
`)
th.WriteF("foo.env", `
MOUNTAIN=everest
OCEAN=pacific
`)
th.WriteF("bar.env", `
BIRD=falcon
`)
th.WriteF("phrase.dat", `
Life is short.
But the years are long.
Not while the evil days come not.
`)
th.WriteF("forces.txt", `
gravitational
electromagnetic
strong nuclear
weak nuclear
`)
opts := th.MakeDefaultOptions()
m := th.Run(".", opts)
th.AssertActualEqualsExpected(
m, `
apiVersion: v1
data:
BIRD: falcon
MOUNTAIN: everest
OCEAN: pacific
forces.txt: |2
gravitational
electromagnetic
strong nuclear
weak nuclear
fruit: apple
passphrase: |2
Life is short.
But the years are long.
Not while the evil days come not.
vegetable: broccoli
kind: ConfigMap
metadata:
name: blah-bob-g9df72cd5b
---
apiVersion: v1
data:
druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize":
32000000000, "freeSpacePercent": 1.0}]'
v2: '[{"path": "var/druid/segment-cache"}]'
kind: ConfigMap
metadata:
name: blah-json-5298bc8g99
---
apiVersion: v1
data:
BIRD: ZmFsY29u
MOUNTAIN: ZXZlcmVzdA==
OCEAN: cGFjaWZpYw==
forces.txt: |
CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbn
VjbGVhcgo=
fruit: YXBwbGU=
passphrase: |
CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aG
UgZXZpbCBkYXlzIGNvbWUgbm90Lgo=
vegetable: YnJvY2NvbGk=
kind: Secret
metadata:
name: blah-bob-58g62h555c
type: Opaque
`)
}

View File

@@ -1,17 +1,52 @@
package krusty_test
import (
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
func TestFnExecGenerator(t *testing.T) {
// Function plugins should not need the env setup done by MakeEnhancedHarness
th := kusttest_test.MakeHarness(t)
const generateDeploymentDotSh = `#!/bin/sh
th.WriteK(".", `
cat <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
annotations:
tshirt-size: small # this injects the resource reservations
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
EOF
`
func TestFnExecGenerator(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
th.WriteK(tmpDir.String(), `
resources:
- short_secret.yaml
generators:
@@ -19,7 +54,8 @@ generators:
`)
// Create some additional resource just to make sure everything is added
th.WriteF("short_secret.yaml", `
th.WriteF(filepath.Join(tmpDir.String(), "short_secret.yaml"),
`
apiVersion: v1
kind: Secret
metadata:
@@ -32,22 +68,25 @@ stringData:
bootcmd:
- mkdir /mnt/vda
`)
th.WriteF(filepath.Join(tmpDir.String(), "generateDeployment.sh"), generateDeploymentDotSh)
th.WriteF("gener.yaml", `
assert.NoError(t, os.Chmod(filepath.Join(tmpDir.String(), "generateDeployment.sh"), 0777))
th.WriteF(filepath.Join(tmpDir.String(), "gener.yaml"), `
kind: executable
metadata:
name: demo
annotations:
config.kubernetes.io/function: |
exec:
path: ./fnplugin_test/fnexectest.sh
path: ./generateDeployment.sh
spec:
`)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true
m := th.Run(".", o)
th.AssertActualEqualsExpected(m, `
apiVersion: v1
m := th.Run(tmpDir.String(), o)
assert.NoError(t, err)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Secret
metadata:
labels:
@@ -63,7 +102,6 @@ apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
config.kubernetes.io/path: deployment_nginx.yaml
tshirt-size: small
labels:
app: nginx
@@ -80,7 +118,99 @@ spec:
containers:
- image: nginx
name: nginx
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func TestFnExecGeneratorWithOverlay(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
base := filepath.Join(tmpDir.String(), "base")
prod := filepath.Join(tmpDir.String(), "prod")
assert.NoError(t, fSys.Mkdir(base))
assert.NoError(t, fSys.Mkdir(prod))
th.WriteK(base, `
resources:
- short_secret.yaml
generators:
- gener.yaml
`)
th.WriteK(prod, `
resources:
- ../base
`)
th.WriteF(filepath.Join(base, "short_secret.yaml"),
`
apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-user-data: "true"
name: node1-bmc-secret
type: Opaque
stringData:
userData: |
bootcmd:
- mkdir /mnt/vda
`)
th.WriteF(filepath.Join(base, "generateDeployment.sh"), generateDeploymentDotSh)
assert.NoError(t, os.Chmod(filepath.Join(base, "generateDeployment.sh"), 0777))
th.WriteF(filepath.Join(base, "gener.yaml"), `
kind: executable
metadata:
name: demo
annotations:
config.kubernetes.io/function: |
exec:
path: ./generateDeployment.sh
spec:
`)
m := th.Run(prod, o)
assert.NoError(t, err)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Secret
metadata:
labels:
airshipit.org/ephemeral-user-data: "true"
name: node1-bmc-secret
stringData:
userData: |
bootcmd:
- mkdir /mnt/vda
type: Opaque
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
tshirt-size: small
labels:
app: nginx
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func skipIfNoDocker(t *testing.T) {
@@ -146,8 +276,6 @@ type: Opaque
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
annotations:
config.kubernetes.io/path: config/demo-budget_poddisruptionbudget.yaml
labels:
app: cockroachdb
name: demo
@@ -162,8 +290,6 @@ spec:
apiVersion: v1
kind: Service
metadata:
annotations:
config.kubernetes.io/path: config/demo-public_service.yaml
labels:
app: cockroachdb
name: demo
@@ -184,7 +310,6 @@ apiVersion: v1
kind: Service
metadata:
annotations:
config.kubernetes.io/path: config/demo_service.yaml
prometheus.io/path: _status/vars
prometheus.io/port: "8080"
prometheus.io/scrape: "true"
@@ -209,8 +334,6 @@ spec:
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
config.kubernetes.io/path: config/demo_statefulset.yaml
labels:
app: cockroachdb
name: demo
@@ -383,7 +506,6 @@ apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
config.kubernetes.io/path: deployment_nginx.yaml
tshirt-size: small
labels:
app: nginx
@@ -450,8 +572,6 @@ data:
apiVersion: v1
kind: Namespace
metadata:
annotations:
config.kubernetes.io/path: namespace_my-namespace.yaml
labels:
my-ns-name: function-test
name: my-namespace
@@ -459,8 +579,6 @@ metadata:
apiVersion: v1
kind: Namespace
metadata:
annotations:
config.kubernetes.io/path: namespace_another-namespace.yaml
labels:
my-ns-name: function-test
name: another-namespace
@@ -508,8 +626,6 @@ data:
value: value
kind: ConfigMap
metadata:
annotations:
config.kubernetes.io/path: configmap_env.yaml
name: env
`)
}

View File

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

View File

@@ -230,3 +230,92 @@ spec:
name: configmap-in-base
`)
}
func TestPathWithCronJobV1(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- resources.yaml
patches:
- patch: |
apiVersion: batch/v1
kind: CronJob
metadata:
name: test
spec:
jobTemplate:
spec:
template:
spec:
containers:
- name: test
env:
- name: ENV_NEW
value: val_new
target:
kind: CronJob
name: test
`)
th.WriteF("resources.yaml", `
apiVersion: batch/v1
kind: CronJob
metadata:
name: test
spec:
schedule: "5 10 * * 1"
concurrencyPolicy: Forbid
jobTemplate:
spec:
backoffLimit: 3
template:
spec:
restartPolicy: Never
containers:
- name: test
image: bash
command:
- /bin/sh
- -c
- echo "test"
env:
- name: ENV1
value: val1
- name: ENV2
value: val2
- name: ENV3
value: val3`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: batch/v1
kind: CronJob
metadata:
name: test
spec:
concurrencyPolicy: Forbid
jobTemplate:
spec:
backoffLimit: 3
template:
spec:
containers:
- command:
- /bin/sh
- -c
- echo "test"
env:
- name: ENV_NEW
value: val_new
- name: ENV1
value: val1
- name: ENV2
value: val2
- name: ENV3
value: val3
image: bash
name: test
restartPolicy: Never
schedule: 5 10 * * 1
`)
}

View File

@@ -17,6 +17,11 @@ func writeTestSchema(th kusttest_test.Harness, filepath string) {
th.WriteF(filepath+"mycrd_schema.json", string(bytes))
}
func writeTestSchemaYaml(th kusttest_test.Harness, filepath string) {
bytes, _ := ioutil.ReadFile("testdata/customschema.yaml")
th.WriteF(filepath+"mycrd_schema.yaml", string(bytes))
}
func writeCustomResource(th kusttest_test.Harness, filepath string) {
th.WriteF(filepath, `
apiVersion: example.com/v1alpha1
@@ -103,6 +108,21 @@ openapi:
th.AssertActualEqualsExpected(m, patchedCustomResource)
}
func TestCustomOpenApiFieldYaml(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- mycrd.yaml
openapi:
path: mycrd_schema.yaml
`+customSchemaPatch)
writeCustomResource(th, "mycrd.yaml")
writeTestSchemaYaml(th, "./")
openapi.ResetOpenAPI()
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, patchedCustomResource)
}
// Error if user tries to specify both builtin version
// and custom schema
func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
@@ -111,7 +131,7 @@ func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
resources:
- mycrd.yaml
openapi:
version: v1.20.4
version: v1.21.2
path: mycrd_schema.json
`+customSchemaPatch)
writeCustomResource(th, "mycrd.yaml")
@@ -197,7 +217,7 @@ openapi:
resources:
- ../base
openapi:
version: v1.20.4
version: v1.21.2
`+customSchemaPatch)
writeCustomResource(th, "base/mycrd.yaml")
writeTestSchema(th, "base/")
@@ -215,7 +235,7 @@ spec:
- image: nginx
name: server
`)
assert.Equal(t, "v1204", openapi.GetSchemaVersion())
assert.Equal(t, "v1212", openapi.GetSchemaVersion())
}
func TestCustomOpenAPIFieldFromComponent(t *testing.T) {

View File

@@ -16,7 +16,7 @@ func TestOpenApiFieldBasicUsage(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
openapi:
version: v1.20.4
version: v1.21.2
resources:
- deployment.yaml
`)
@@ -44,7 +44,8 @@ spec:
containers:
- image: whatever
`)
assert.Equal(t, "v1204", openapi.GetSchemaVersion())
assert.Equal(t, "v1212", openapi.GetSchemaVersion())
openapi.ResetOpenAPI()
}
func TestOpenApiFieldNotBuiltin(t *testing.T) {
@@ -71,6 +72,7 @@ spec:
if err == nil {
t.Fatalf("expected an error")
}
openapi.ResetOpenAPI()
}
func TestOpenApiFieldDefaultVersion(t *testing.T) {
@@ -104,4 +106,5 @@ spec:
- image: whatever
`)
assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion())
openapi.ResetOpenAPI()
}

View File

@@ -4,6 +4,7 @@
package krusty_test
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
@@ -39,3 +40,124 @@ spec:
name: nginx
`, string(yml))
}
func TestRemoteResource(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
assert.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(`
resources:
- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6
`)))
m, err := b.Run(
fSys,
tmpDir.String())
if utils.IsErrTimeout(err) {
// Don't fail on timeouts.
t.SkipNow()
}
assert.NoError(t, err)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Pod
metadata:
labels:
app: myapp
name: dev-myapp-pod
spec:
containers:
- image: nginx:1.7.9
name: nginx
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func TestRemoteResourceAnnoOrigin(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
assert.NoError(t, fSys.WriteFile(filepath.Join(tmpDir.String(), "kustomization.yaml"), []byte(`
resources:
- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6
buildMetadata: [originAnnotations]
`)))
m, err := b.Run(
fSys,
tmpDir.String())
if utils.IsErrTimeout(err) {
// Don't fail on timeouts.
t.SkipNow()
}
assert.NoError(t, err)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Pod
metadata:
annotations:
config.kubernetes.io/origin: |
path: examples/multibases/base/pod.yaml
repo: https://github.com/kubernetes-sigs/kustomize
ref: v1.0.6
labels:
app: myapp
name: dev-myapp-pod
spec:
containers:
- image: nginx:1.7.9
name: nginx
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}
func TestRemoteResourceAsBaseWithAnnoOrigin(t *testing.T) {
fSys := filesys.MakeFsOnDisk()
b := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
tmpDir, err := filesys.NewTmpConfirmedDir()
assert.NoError(t, err)
base := filepath.Join(tmpDir.String(), "base")
prod := filepath.Join(tmpDir.String(), "prod")
assert.NoError(t, fSys.Mkdir(base))
assert.NoError(t, fSys.Mkdir(prod))
assert.NoError(t, fSys.WriteFile(filepath.Join(base, "kustomization.yaml"), []byte(`
resources:
- github.com/kubernetes-sigs/kustomize/examples/multibases/dev/?ref=v1.0.6
`)))
assert.NoError(t, fSys.WriteFile(filepath.Join(prod, "kustomization.yaml"), []byte(`
resources:
- ../base
namePrefix: prefix-
buildMetadata: [originAnnotations]
`)))
m, err := b.Run(
fSys,
prod)
if utils.IsErrTimeout(err) {
// Don't fail on timeouts.
t.SkipNow()
}
assert.NoError(t, err)
yml, err := m.AsYaml()
assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Pod
metadata:
annotations:
config.kubernetes.io/origin: |
path: examples/multibases/base/pod.yaml
repo: https://github.com/kubernetes-sigs/kustomize
ref: v1.0.6
labels:
app: myapp
name: prefix-dev-myapp-pod
spec:
containers:
- image: nginx:1.7.9
name: nginx
`, string(yml))
assert.NoError(t, fSys.RemoveAll(tmpDir.String()))
}

View File

@@ -342,3 +342,143 @@ spec:
name: nginx
`)
}
// TODO: Address namePrefix in overlay not applying to replacement targets
// The property `data.blue-name` should end up being `overlay-blue` instead of `blue`
// https://github.com/kubernetes-sigs/kustomize/issues/4034
func TestReplacementTransformerWithNamePrefixOverlay(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK("base", `
generatorOptions:
disableNameSuffixHash: true
configMapGenerator:
- name: blue
- name: red
replacements:
- source:
kind: ConfigMap
name: blue
fieldPath: metadata.name
targets:
- select:
name: red
fieldPaths:
- data.blue-name
options:
create: true
`)
th.WriteK(".", `
namePrefix: overlay-
resources:
- base
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: ConfigMap
metadata:
name: overlay-blue
---
apiVersion: v1
data:
blue-name: blue
kind: ConfigMap
metadata:
name: overlay-red
`)
}
// TODO: Address namespace in overlay not applying to replacement targets
// The property `data.blue-namespace` should end up being `overlay-namespace` instead of `base-namespace`
// https://github.com/kubernetes-sigs/kustomize/issues/4034
func TestReplacementTransformerWithNamespaceOverlay(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK("base", `
namespace: base-namespace
generatorOptions:
disableNameSuffixHash: true
configMapGenerator:
- name: blue
- name: red
replacements:
- source:
kind: ConfigMap
name: blue
fieldPath: metadata.namespace
targets:
- select:
name: red
fieldPaths:
- data.blue-namespace
options:
create: true
`)
th.WriteK(".", `
namespace: overlay-namespace
resources:
- base
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: ConfigMap
metadata:
name: blue
namespace: overlay-namespace
---
apiVersion: v1
data:
blue-namespace: base-namespace
kind: ConfigMap
metadata:
name: red
namespace: overlay-namespace
`)
}
// TODO: Address configMapGenerator suffix not applying to replacement targets
// The property `data.blue-name` should end up being `blue-6ct58987ht` instead of `blue`
// https://github.com/kubernetes-sigs/kustomize/issues/4034
func TestReplacementTransformerWithConfigMapGenerator(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteK(".", `
configMapGenerator:
- name: blue
- name: red
replacements:
- source:
kind: ConfigMap
name: blue
fieldPath: metadata.name
targets:
- select:
name: red
fieldPaths:
- data.blue-name
options:
create: true
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: ConfigMap
metadata:
name: blue-6ct58987ht
---
apiVersion: v1
data:
blue-name: blue
kind: ConfigMap
metadata:
name: red-dc6gc5btkc
`)
}

75
api/krusty/testdata/customschema.yaml vendored Normal file
View File

@@ -0,0 +1,75 @@
definitions:
v1alpha1.MyCRD:
properties:
apiVersion:
type: string
kind:
type: string
metadata:
type: object
spec:
properties:
template:
"$ref": "#/definitions/io.k8s.api.core.v1.PodTemplateSpec"
type: object
status:
properties:
success:
type: boolean
type: object
type: object
x-kubernetes-group-version-kind:
- group: example.com
kind: MyCRD
version: v1alpha1
io.k8s.api.core.v1.PodTemplateSpec:
properties:
metadata:
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"
spec:
"$ref": "#/definitions/io.k8s.api.core.v1.PodSpec"
type: object
io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta:
properties:
name:
type: string
type: object
io.k8s.api.core.v1.PodSpec:
properties:
containers:
items:
"$ref": "#/definitions/io.k8s.api.core.v1.Container"
type: array
x-kubernetes-patch-merge-key: name
x-kubernetes-patch-strategy: merge
type: object
io.k8s.api.core.v1.Container:
properties:
command:
items:
type: string
type: array
image:
type: string
name:
type: string
ports:
items:
"$ref": "#/definitions/io.k8s.api.core.v1.ContainerPort"
type: array
x-kubernetes-list-map-keys:
- containerPort
- protocol
x-kubernetes-list-type: map
x-kubernetes-patch-merge-key: containerPort
x-kubernetes-patch-strategy: merge
type: object
io.k8s.api.core.v1.ContainerPort:
properties:
containerPort:
type: integer
name:
type: string
protocol:
type: string
type: object

View File

@@ -219,8 +219,10 @@ BAR=baz
}
r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, tc.input)
assert.NoError(t, err, tc.description)
r.RemoveBuildAnnotations()
rYaml, err := r.AsYaml()
assert.NoError(t, err, tc.description)
tc.expected.RemoveBuildAnnotations()
expYaml, err := tc.expected.AsYaml()
assert.NoError(t, err, tc.description)
assert.Equal(t, expYaml, rYaml)
@@ -252,6 +254,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
actual.RemoveBuildAnnotations()
actYaml, err := actual.AsYaml()
assert.NoError(t, err)

View File

@@ -136,6 +136,10 @@ type ResMap interface {
// self, then its behavior _cannot_ be merge or replace.
AbsorbAll(ResMap) error
// AnnotateAll annotates all resources in the ResMap with
// the provided key value pair.
AnnotateAll(key string, value string) error
// AsYaml returns the yaml form of resources.
AsYaml() ([]byte, error)
@@ -210,6 +214,35 @@ type ResMap interface {
// namespaces. Cluster wide objects are never excluded.
SubsetThatCouldBeReferencedByResource(*resource.Resource) ResMap
// DeAnchor replaces YAML aliases with structured data copied from anchors.
// This cannot be undone; if desired, call DeepCopy first.
// Subsequent marshalling to YAML will no longer have anchor
// definitions ('&') or aliases ('*').
//
// Anchors are not expected to work across YAML 'documents'.
// If three resources are loaded from one file containing three YAML docs:
//
// {resourceA}
// ---
// {resourceB}
// ---
// {resourceC}
//
// then anchors defined in A cannot be seen from B and C and vice versa.
// OTOH, cross-resource links (a field in B referencing fields in A) will
// work if the resources are gathered in a ResourceList:
//
// apiVersion: config.kubernetes.io/v1
// kind: ResourceList
// metadata:
// name: someList
// items:
// - {resourceA}
// - {resourceB}
// - {resourceC}
//
DeAnchor() error
// DeepCopy copies the ResMap and underlying resources.
DeepCopy() ResMap

View File

@@ -9,6 +9,7 @@ import (
"reflect"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/annotations"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
@@ -531,6 +532,19 @@ func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
}
}
// AnnotateAll implements ResMap
func (m *resWrangler) AnnotateAll(key string, value string) error {
return m.ApplyFilter(annotations.Filter{
Annotations: map[string]string{
key: value,
},
FsSlice: []types.FieldSpec{{
Path: "metadata/annotations",
CreateIfNotPresent: true,
}},
})
}
// Select returns a list of resources that
// are selected by a Selector
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
@@ -593,6 +607,16 @@ func (m *resWrangler) ToRNodeSlice() []*kyaml.RNode {
return result
}
// DeAnchor implements ResMap.
func (m *resWrangler) DeAnchor() (err error) {
for i := range m.rList {
if err = m.rList[i].DeAnchor(); err != nil {
return err
}
}
return nil
}
// ApplySmPatch applies the patch, and errors on Id collisions.
func (m *resWrangler) ApplySmPatch(
selectedSet *resource.IdSet, patch *resource.Resource) error {

View File

@@ -343,9 +343,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
"metadata": map[string]interface{}{
"name": "new-alice",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "ConfigMap",
"config.kubernetes.io/previousNames": "alice",
"config.kubernetes.io/previousNamespaces": "default",
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
"internal.config.kubernetes.io/previousNames": "alice",
"internal.config.kubernetes.io/previousNamespaces": "default",
},
},
})
@@ -356,9 +356,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
"metadata": map[string]interface{}{
"name": "new-bob",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap",
"config.kubernetes.io/previousNames": "bob,bob2",
"config.kubernetes.io/previousNamespaces": "default,default",
"internal.config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap",
"internal.config.kubernetes.io/previousNames": "bob,bob2",
"internal.config.kubernetes.io/previousNamespaces": "default,default",
},
},
})
@@ -370,9 +370,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
"name": "new-bob",
"namespace": "new-happy",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "ConfigMap",
"config.kubernetes.io/previousNames": "bob",
"config.kubernetes.io/previousNamespaces": "happy",
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
"internal.config.kubernetes.io/previousNames": "bob",
"internal.config.kubernetes.io/previousNamespaces": "happy",
},
},
})
@@ -384,9 +384,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
"name": "charlie",
"namespace": "happy",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "ConfigMap",
"config.kubernetes.io/previousNames": "charlie",
"config.kubernetes.io/previousNamespaces": "default",
"internal.config.kubernetes.io/previousKinds": "ConfigMap",
"internal.config.kubernetes.io/previousNames": "charlie",
"internal.config.kubernetes.io/previousNamespaces": "default",
},
},
})
@@ -845,6 +845,8 @@ func TestAbsorbAll(t *testing.T) {
}))
w := makeMap1()
assert.NoError(t, w.AbsorbAll(makeMap2(types.BehaviorMerge)))
expected.RemoveBuildAnnotations()
w.RemoveBuildAnnotations()
assert.NoError(t, expected.ErrorIfNotEqualLists(w))
w = makeMap1()
assert.NoError(t, w.AbsorbAll(nil))
@@ -853,6 +855,7 @@ func TestAbsorbAll(t *testing.T) {
w = makeMap1()
w2 := makeMap2(types.BehaviorReplace)
assert.NoError(t, w.AbsorbAll(w2))
w2.RemoveBuildAnnotations()
assert.NoError(t, w2.ErrorIfNotEqualLists(w))
w = makeMap1()
w2 = makeMap2(types.BehaviorUnspecified)
@@ -899,6 +902,100 @@ rules:
}
}
func TestDeAnchorSingleDoc(t *testing.T) {
input := `apiVersion: v1
kind: ConfigMap
metadata:
name: wildcard
data:
color: &color-used blue
feeling: *color-used
`
rm, err := rmF.NewResMapFromBytes([]byte(input))
assert.NoError(t, err)
assert.NoError(t, rm.DeAnchor())
yaml, err := rm.AsYaml()
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(`
apiVersion: v1
data:
color: blue
feeling: blue
kind: ConfigMap
metadata:
name: wildcard
`), strings.TrimSpace(string(yaml)))
}
// Anchor references don't cross YAML document boundaries.
func TestDeAnchorMultiDoc(t *testing.T) {
input := `apiVersion: v1
kind: ConfigMap
metadata:
name: betty
data:
color: &color-used blue
feeling: *color-used
---
apiVersion: v1
kind: ConfigMap
metadata:
name: bob
data:
color: red
feeling: *color-used
`
_, err := rmF.NewResMapFromBytes([]byte(input))
assert.Error(t, err)
assert.Contains(t, err.Error(), "unknown anchor 'color-used' referenced")
}
// Anchor references cross list elements in a ResourceList.
func TestDeAnchorResourceList(t *testing.T) {
input := `apiVersion: config.kubernetes.io/v1
kind: ResourceList
metadata:
name: aShortList
items:
- apiVersion: v1
kind: ConfigMap
metadata:
name: betty
data:
color: &color-used blue
feeling: *color-used
- apiVersion: v1
kind: ConfigMap
metadata:
name: bob
data:
color: red
feeling: *color-used
`
rm, err := rmF.NewResMapFromBytes([]byte(input))
assert.NoError(t, err)
assert.NoError(t, rm.DeAnchor())
yaml, err := rm.AsYaml()
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(`
apiVersion: v1
data:
color: blue
feeling: blue
kind: ConfigMap
metadata:
name: betty
---
apiVersion: v1
data:
color: red
feeling: blue
kind: ConfigMap
metadata:
name: bob
`), strings.TrimSpace(string(yaml)))
}
func TestApplySmPatch_General(t *testing.T) {
const (
myDeployment = "Deployment"

View File

@@ -64,18 +64,23 @@ func (rf *Factory) FromMapAndOption(
// TODO: return err instead of log.
log.Fatal(err)
}
return rf.makeOne(n, types.NewGenArgs(args))
return rf.makeOne(n, args)
}
// makeOne returns a new instance of Resource.
func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GenArgs) *Resource {
func (rf *Factory) makeOne(rn *yaml.RNode, o *types.GeneratorArgs) *Resource {
if rn == nil {
log.Fatal("RNode must not be null")
}
if o == nil {
o = types.NewGenArgs(nil)
resource := &Resource{RNode: *rn}
if o != nil {
if o.Options == nil || !o.Options.DisableNameSuffixHash {
resource.EnableHashSuffix()
}
resource.SetBehavior(types.NewGenerationBehavior(o.Behavior))
}
return &Resource{RNode: *rn, options: o}
return resource
}
// SliceFromPatches returns a slice of resources given a patch path
@@ -265,7 +270,7 @@ func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs)
if err != nil {
return nil, err
}
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
return rf.makeOne(rn, &args.GeneratorArgs), nil
}
// MakeSecret makes an instance of Resource for Secret
@@ -274,5 +279,5 @@ func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Reso
if err != nil {
return nil, err
}
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil
return rf.makeOne(rn, &args.GeneratorArgs), nil
}

60
api/resource/origin.go Normal file
View File

@@ -0,0 +1,60 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resource
import (
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/api/internal/git"
)
// Origin retains information about where resources in the output
// of `kustomize build` originated from
type Origin struct {
// Path is the path to the resource, rooted from the directory upon
// which `kustomize build` was invoked
Path string
// Repo is the remote repository that the resource originated from if it is
// not from a local file
Repo string
// Ref is the ref of the remote repository that the resource originated from
// if it is not from a local file
Ref string
}
// Copy returns a copy of origin
func (origin *Origin) Copy() Origin {
return *origin
}
// Append returns a copy of origin with a path appended to it
func (origin *Origin) Append(path string) *Origin {
originCopy := origin.Copy()
repoSpec, err := git.NewRepoSpecFromUrl(path)
if err == nil {
originCopy.Repo = repoSpec.Host + repoSpec.OrgRepo
absPath := repoSpec.AbsPath()
path = absPath[strings.Index(absPath[1:], "/")+1:][1:]
originCopy.Path = ""
originCopy.Ref = repoSpec.Ref
}
originCopy.Path = filepath.Join(originCopy.Path, path)
return &originCopy
}
// String returns a string version of origin
func (origin *Origin) String() string {
var anno string
anno = anno + "path: " + origin.Path + "\n"
if origin.Repo != "" {
anno = anno + "repo: " + origin.Repo + "\n"
}
if origin.Ref != "" {
anno = anno + "ref: " + origin.Ref + "\n"
}
return anno
}

View File

@@ -0,0 +1,83 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package resource_test
import (
"testing"
. "sigs.k8s.io/kustomize/api/resource"
)
func TestOriginAppend(t *testing.T) {
tests := []struct {
in *Origin
path string
expected string
}{
{
in: &Origin{
Path: "prod",
},
path: "service.yaml",
expected: `path: prod/service.yaml
`,
},
{
in: &Origin{
Path: "overlay/prod",
},
path: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/",
expected: `path: examples/multibases/dev
repo: https://github.com/kubernetes-sigs/kustomize
`,
},
}
for _, test := range tests {
actual := test.in.Append(test.path).String()
if actual != test.expected {
t.Fatalf("Expected %v, but got %v\n", test.expected, actual)
}
}
}
func TestOriginString(t *testing.T) {
tests := []struct {
in *Origin
expected string
}{
{
in: &Origin{
Path: "prod/service.yaml",
Repo: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/",
Ref: "v1.0.6",
},
expected: `path: prod/service.yaml
repo: github.com/kubernetes-sigs/kustomize/examples/multibases/dev/
ref: v1.0.6
`,
},
{
in: &Origin{
Path: "prod/service.yaml",
Repo: "github.com/kubernetes-sigs/kustomize/examples/multibases/dev/",
},
expected: `path: prod/service.yaml
repo: github.com/kubernetes-sigs/kustomize/examples/multibases/dev/
`,
},
{
in: &Origin{
Path: "prod/service.yaml",
},
expected: `path: prod/service.yaml
`,
},
}
for _, test := range tests {
if test.in.String() != test.expected {
t.Fatalf("Expected %v, but got %v\n", test.expected, test.in.String())
}
}
}

View File

@@ -13,6 +13,7 @@ import (
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/resid"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml"
@@ -22,8 +23,6 @@ import (
// paired with metadata used by kustomize.
type Resource struct {
kyaml.RNode
options *types.GenArgs
refBy []resid.ResId
refVarNames []string
}
@@ -35,6 +34,13 @@ var BuildAnnotations = []string{
utils.BuildAnnotationPreviousNamespaces,
utils.BuildAnnotationAllowNameChange,
utils.BuildAnnotationAllowKindChange,
utils.BuildAnnotationsRefBy,
utils.BuildAnnotationsGenBehavior,
utils.BuildAnnotationsGenAddHashSuffix,
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
kioutil.SeqIndentAnnotation,
}
func (r *Resource) ResetRNode(incoming *Resource) {
@@ -80,6 +86,8 @@ func (r *Resource) DeepCopy() *Resource {
// CopyMergeMetaDataFieldsFrom copies everything but the non-metadata in
// the resource.
// TODO: move to RNode, use GetMeta to improve performance.
// TODO: make a version of mergeStringMaps that is build-annotation aware
// to avoid repeatedly setting refby and genargs annotations
// Must remove the kustomize bit at the end.
func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
if err := r.SetLabels(
@@ -87,7 +95,7 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
return fmt.Errorf("copyMerge cannot set labels - %w", err)
}
if err := r.SetAnnotations(
mergeStringMaps(other.GetAnnotations(), r.GetAnnotations())); err != nil {
mergeStringMapsWithBuildAnnotations(other.GetAnnotations(), r.GetAnnotations())); err != nil {
return fmt.Errorf("copyMerge cannot set annotations - %w", err)
}
if err := r.SetName(other.GetName()); err != nil {
@@ -101,8 +109,6 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
}
func (r *Resource) copyKustomizeSpecificFields(other *Resource) {
r.options = other.options
r.refBy = other.copyRefBy()
r.refVarNames = copyStringSlice(other.refVarNames)
}
@@ -144,10 +150,10 @@ func (r *Resource) ErrIfNotEquals(o *Resource) error {
func (r *Resource) ReferencesEqual(other *Resource) bool {
setSelf := make(map[resid.ResId]bool)
setOther := make(map[resid.ResId]bool)
for _, ref := range other.refBy {
for _, ref := range other.GetRefBy() {
setOther[ref] = true
}
for _, ref := range r.refBy {
for _, ref := range r.GetRefBy() {
if _, ok := setOther[ref]; !ok {
return false
}
@@ -156,15 +162,6 @@ func (r *Resource) ReferencesEqual(other *Resource) bool {
return len(setSelf) == len(setOther)
}
func (r *Resource) copyRefBy() []resid.ResId {
if r.refBy == nil {
return nil
}
s := make([]resid.ResId, len(r.refBy))
copy(s, r.refBy)
return s
}
func copyStringSlice(s []string) []string {
if s == nil {
return nil
@@ -250,41 +247,45 @@ func (r *Resource) setPreviousId(ns string, n string, k string) *Resource {
// AllowNameChange allows name changes to the resource.
func (r *Resource) AllowNameChange() {
annotations := r.GetAnnotations()
annotations[utils.BuildAnnotationAllowNameChange] = utils.Allowed
if err := r.SetAnnotations(annotations); err != nil {
panic(err)
}
r.enable(utils.BuildAnnotationAllowNameChange)
}
// NameChangeAllowed checks if a patch resource is allowed to change another resource's name.
func (r *Resource) NameChangeAllowed() bool {
annotations := r.GetAnnotations()
v, ok := annotations[utils.BuildAnnotationAllowNameChange]
return ok && v == utils.Allowed
return r.isEnabled(utils.BuildAnnotationAllowNameChange)
}
// AllowKindChange allows kind changes to the resource.
func (r *Resource) AllowKindChange() {
r.enable(utils.BuildAnnotationAllowKindChange)
}
// KindChangeAllowed checks if a patch resource is allowed to change another resource's kind.
func (r *Resource) KindChangeAllowed() bool {
return r.isEnabled(utils.BuildAnnotationAllowKindChange)
}
func (r *Resource) isEnabled(annoKey string) bool {
annotations := r.GetAnnotations()
annotations[utils.BuildAnnotationAllowKindChange] = utils.Allowed
v, ok := annotations[annoKey]
return ok && v == utils.Enabled
}
func (r *Resource) enable(annoKey string) {
annotations := r.GetAnnotations()
annotations[annoKey] = utils.Enabled
if err := r.SetAnnotations(annotations); err != nil {
panic(err)
}
}
func (r *Resource) KindChangeAllowed() bool {
annotations := r.GetAnnotations()
v, ok := annotations[utils.BuildAnnotationAllowKindChange]
return ok && v == utils.Allowed
}
// String returns resource as JSON.
func (r *Resource) String() string {
bs, err := r.MarshalJSON()
if err != nil {
return "<" + err.Error() + ">"
}
return strings.TrimSpace(string(bs)) + r.options.String()
return strings.TrimSpace(string(bs))
}
// AsYAML returns the resource in Yaml form.
@@ -306,20 +307,34 @@ func (r *Resource) MustYaml() string {
return string(yml)
}
// SetOptions updates the generator options for the resource.
func (r *Resource) SetOptions(o *types.GenArgs) {
r.options = o
}
// Behavior returns the behavior for the resource.
func (r *Resource) Behavior() types.GenerationBehavior {
return r.options.Behavior()
annotations := r.GetAnnotations()
if v, ok := annotations[utils.BuildAnnotationsGenBehavior]; ok {
return types.NewGenerationBehavior(v)
}
return types.NewGenerationBehavior("")
}
// SetBehavior sets the behavior for the resource.
func (r *Resource) SetBehavior(behavior types.GenerationBehavior) {
annotations := r.GetAnnotations()
annotations[utils.BuildAnnotationsGenBehavior] = behavior.String()
if err := r.SetAnnotations(annotations); err != nil {
panic(err)
}
}
// NeedHashSuffix returns true if a resource content
// hash should be appended to the name of the resource.
func (r *Resource) NeedHashSuffix() bool {
return r.options != nil && r.options.ShouldAddHashSuffixToName()
return r.isEnabled(utils.BuildAnnotationsGenAddHashSuffix)
}
// EnableHashSuffix marks the resource as needing a content
// hash to be appended to the name of the resource.
func (r *Resource) EnableHashSuffix() {
r.enable(utils.BuildAnnotationsGenAddHashSuffix)
}
// OrgId returns the original, immutable ResId for the resource.
@@ -363,12 +378,18 @@ func (r *Resource) CurId() resid.ResId {
// GetRefBy returns the ResIds that referred to current resource
func (r *Resource) GetRefBy() []resid.ResId {
return r.refBy
var resIds []resid.ResId
asStrings := r.getCsvAnnotation(utils.BuildAnnotationsRefBy)
for _, s := range asStrings {
resIds = append(resIds, resid.FromString(s))
}
return resIds
}
// AppendRefBy appends a ResId into the refBy list
func (r *Resource) AppendRefBy(id resid.ResId) {
r.refBy = append(r.refBy, id)
// Using any type except fmt.Stringer here results in a compilation error
func (r *Resource) AppendRefBy(id fmt.Stringer) {
r.appendCsvAnnotation(utils.BuildAnnotationsRefBy, id.String())
}
// GetRefVarNames returns vars that refer to current resource
@@ -424,3 +445,17 @@ func mergeStringMaps(maps ...map[string]string) map[string]string {
}
return result
}
func mergeStringMapsWithBuildAnnotations(maps ...map[string]string) map[string]string {
result := mergeStringMaps(maps...)
for i := range BuildAnnotations {
if len(maps) > 0 {
if v, ok := maps[0][BuildAnnotations[i]]; ok {
result[BuildAnnotations[i]] = v
continue
}
}
delete(result, BuildAnnotations[i])
}
return result
}

View File

@@ -28,8 +28,6 @@ var testConfigMap = factory.FromMap(
},
})
const genArgOptions = "{nsfx:false,beh:unspecified}"
//nolint:gosec
const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
@@ -66,17 +64,15 @@ func TestResourceString(t *testing.T) {
}{
{
in: testConfigMap,
s: configMapAsString + genArgOptions,
s: configMapAsString,
},
{
in: testDeployment,
s: deploymentAsString + genArgOptions,
s: deploymentAsString,
},
}
for _, test := range tests {
if test.in.String() != test.s {
t.Fatalf("Expected %s == %s", test.in.String(), test.s)
}
assert.Equal(t, test.in.String(), test.s)
}
}
@@ -284,6 +280,266 @@ spec:
`, string(bytes))
}
func TestApplySmPatchShouldOutputListItemsInCorrectOrder(t *testing.T) {
cases := []struct {
name string
skip bool
patch string
expectedOutput string
}{
{
name: "Order should not change when patch has foo only",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: foo
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: foo
- name: bar
`,
},
{
name: "Order changes when patch has bar only",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: bar
`,
// This test records current behavior, but this behavior might be undesirable.
// If so, feel free to change the test to pass with some improved algorithm.
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: bar
- name: foo
`,
},
{
name: "Order should not change and should include a new item at the beginning when patch has a new list item",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: baz
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: baz
- name: foo
- name: bar
`,
},
{
name: "Order should not change when patch has foo and bar in same order",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: foo
- name: bar
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: foo
- name: bar
`,
},
{
name: "Order should change when patch has foo and bar in different order",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: bar
- name: foo
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: bar
- name: foo
`,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if tc.skip {
t.Skip()
}
resource, err := factory.FromBytes([]byte(`
apiVersion: v1
kind: Pod
metadata:
name: test
spec:
initContainers:
- name: foo
- name: bar
`))
assert.NoError(t, err)
patch, err := factory.FromBytes([]byte(tc.patch))
assert.NoError(t, err)
assert.NoError(t, resource.ApplySmPatch(patch))
bytes, err := resource.AsYAML()
assert.NoError(t, err)
assert.Equal(t, tc.expectedOutput, string(bytes))
})
}
}
func TestApplySmPatchShouldOutputPrimitiveListItemsInCorrectOrder(t *testing.T) {
cases := []struct {
name string
skip bool
patch string
expectedOutput string
}{
{
name: "Order should not change when patch has foo only",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
finalizers: ["foo"]
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
finalizers:
- foo
- bar
name: test
`,
},
{
name: "Order should not change when patch has bar only",
skip: true, // TODO: This test should pass but fails currently. Fix the problem and unskip this test
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
finalizers: ["bar"]
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
finalizers:
- foo
- bar
name: test
`,
},
{
name: "Order should not change and should include a new item at the beginning when patch has a new list item",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
finalizers: ["baz"]
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
finalizers:
- baz
- foo
- bar
name: test
`,
},
{
name: "Order should not change when patch has foo and bar in same order",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
finalizers: ["foo", "bar"]
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
finalizers:
- foo
- bar
name: test
`,
},
{
name: "Order should change when patch has foo and bar in different order",
patch: `apiVersion: v1
kind: Pod
metadata:
name: test
finalizers: ["bar", "foo"]
`,
expectedOutput: `apiVersion: v1
kind: Pod
metadata:
finalizers:
- bar
- foo
name: test
`,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if tc.skip {
t.Skip()
}
resource, err := factory.FromBytes([]byte(`
kind: Pod
metadata:
name: test
finalizers: ["foo", "bar"]
`))
assert.NoError(t, err)
patch, err := factory.FromBytes([]byte(tc.patch))
assert.NoError(t, err)
assert.NoError(t, resource.ApplySmPatch(patch))
bytes, err := resource.AsYAML()
assert.NoError(t, err)
assert.Equal(t, tc.expectedOutput, string(bytes))
})
}
}
func TestMergeDataMapFrom(t *testing.T) {
resource, err := factory.FromBytes([]byte(`
apiVersion: v1
@@ -717,9 +973,9 @@ metadata:
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousKinds: Secret
config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default
internal.config.kubernetes.io/previousKinds: Secret
internal.config.kubernetes.io/previousNames: oldName
internal.config.kubernetes.io/previousNamespaces: default
name: newName
`,
},
@@ -729,9 +985,9 @@ metadata:
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousKinds: Secret
config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default
internal.config.kubernetes.io/previousKinds: Secret
internal.config.kubernetes.io/previousNames: oldName
internal.config.kubernetes.io/previousNamespaces: default
name: oldName2
`,
newName: "newName",
@@ -740,9 +996,9 @@ metadata:
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousKinds: Secret,Secret
config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,default
internal.config.kubernetes.io/previousKinds: Secret,Secret
internal.config.kubernetes.io/previousNames: oldName,oldName2
internal.config.kubernetes.io/previousNamespaces: default,default
name: newName
`,
},
@@ -752,9 +1008,9 @@ metadata:
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousKinds: Secret
config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default
internal.config.kubernetes.io/previousKinds: Secret
internal.config.kubernetes.io/previousNames: oldName
internal.config.kubernetes.io/previousNamespaces: default
name: oldName2
namespace: oldNamespace
`,
@@ -764,9 +1020,9 @@ metadata:
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousKinds: Secret,Secret
config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,oldNamespace
internal.config.kubernetes.io/previousKinds: Secret,Secret
internal.config.kubernetes.io/previousNames: oldName,oldName2
internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
name: newName
namespace: newNamespace
`,
@@ -814,9 +1070,9 @@ metadata:
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousKinds: Secret
config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default
internal.config.kubernetes.io/previousKinds: Secret
internal.config.kubernetes.io/previousNames: oldName
internal.config.kubernetes.io/previousNamespaces: default
name: newName
`,
expected: []resid.ResId{
@@ -833,9 +1089,9 @@ metadata:
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousKinds: Secret,Secret
config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,oldNamespace
internal.config.kubernetes.io/previousKinds: Secret,Secret
internal.config.kubernetes.io/previousNames: oldName,oldName2
internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
name: newName
namespace: newNamespace
`,
@@ -1133,3 +1389,41 @@ spec:
t.Fatalf("expected '%s', got '%s'", expected, actual)
}
}
func TestRefBy(t *testing.T) {
r, err := factory.FromBytes([]byte(`
apiVersion: v1
kind: Deployment
metadata:
name: clown
spec:
numReplicas: 1
`))
assert.NoError(t, err)
r.AppendRefBy(resid.FromString("gr1_ver1_knd1|ns1|name1"))
assert.Equal(t, r.RNode.MustString(), `apiVersion: v1
kind: Deployment
metadata:
name: clown
annotations:
internal.config.kubernetes.io/refBy: gr1_ver1_knd1|ns1|name1
spec:
numReplicas: 1
`)
assert.Equal(t, r.GetRefBy(), []resid.ResId{resid.FromString("gr1_ver1_knd1|ns1|name1")})
r.AppendRefBy(resid.FromString("gr2_ver2_knd2|ns2|name2"))
assert.Equal(t, r.RNode.MustString(), `apiVersion: v1
kind: Deployment
metadata:
name: clown
annotations:
internal.config.kubernetes.io/refBy: gr1_ver1_knd1|ns1|name1,gr2_ver2_knd2|ns2|name2
spec:
numReplicas: 1
`)
assert.Equal(t, r.GetRefBy(), []resid.ResId{
resid.FromString("gr1_ver1_knd1|ns1|name1"),
resid.FromString("gr2_ver2_knd2|ns2|name2"),
})
}

View File

@@ -148,6 +148,13 @@ func (th *HarnessEnhanced) ResetLoaderRoot(root string) {
}
func (th *HarnessEnhanced) LoadAndRunGenerator(
config string) resmap.ResMap {
rm := th.LoadAndRunGeneratorWithBuildAnnotations(config)
rm.RemoveBuildAnnotations()
return rm
}
func (th *HarnessEnhanced) LoadAndRunGeneratorWithBuildAnnotations(
config string) resmap.ResMap {
res, err := th.rf.RF().FromBytes([]byte(config))
if err != nil {
@@ -162,7 +169,6 @@ func (th *HarnessEnhanced) LoadAndRunGenerator(
if err != nil {
th.t.Fatalf("generate err: %v", err)
}
rm.RemoveBuildAnnotations()
return rm
}

View File

@@ -1,46 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
import (
"strconv"
"strings"
)
// GenArgs is a facade over GeneratorArgs, exposing a few readonly properties.
type GenArgs struct {
args *GeneratorArgs
}
// NewGenArgs returns a new instance of GenArgs.
func NewGenArgs(args *GeneratorArgs) *GenArgs {
return &GenArgs{args: args}
}
func (g *GenArgs) String() string {
if g == nil {
return "{nilGenArgs}"
}
return "{" +
strings.Join([]string{
"nsfx:" + strconv.FormatBool(g.ShouldAddHashSuffixToName()),
"beh:" + g.Behavior().String()},
",") +
"}"
}
// ShouldAddHashSuffixToName returns true if a resource
// content hash should be appended to the name of the resource.
func (g *GenArgs) ShouldAddHashSuffixToName() bool {
return g.args != nil &&
(g.args.Options == nil || !g.args.Options.DisableNameSuffixHash)
}
// Behavior returns Behavior field of GeneratorArgs
func (g *GenArgs) Behavior() GenerationBehavior {
if g == nil || g.args == nil {
return BehaviorUnspecified
}
return NewGenerationBehavior(g.args.Behavior)
}

View File

@@ -1,39 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types_test
import (
"testing"
. "sigs.k8s.io/kustomize/api/types"
)
func TestGenArgs_String(t *testing.T) {
tests := []struct {
ga *GenArgs
expected string
}{
{
ga: nil,
expected: "{nilGenArgs}",
},
{
ga: &GenArgs{},
expected: "{nsfx:false,beh:unspecified}",
},
{
ga: NewGenArgs(
&GeneratorArgs{
Behavior: "merge",
Options: &GeneratorOptions{DisableNameSuffixHash: false},
}),
expected: "{nsfx:true,beh:merge}",
},
}
for _, test := range tests {
if test.ga.String() != test.expected {
t.Fatalf("Expected '%s', got '%s'", test.expected, test.ga.String())
}
}
}

View File

@@ -161,6 +161,9 @@ type Kustomization struct {
// Inventory appends an object that contains the record
// of all other objects, which can be used in apply, prune and delete
Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"`
// BuildMetadata is a list of strings used to toggle different build options
BuildMetadata []string `json:"buildMetadata,omitempty" yaml:"buildMetadata,omitempty"`
}
// FixKustomizationPostUnmarshalling fixes things

View File

@@ -57,4 +57,6 @@ type FnPluginLoadingOptions struct {
Env []string
// Run as uid and gid of the command executor
AsCurrentUser bool
// Run in this working directory
WorkingDir string
}

View File

@@ -327,49 +327,51 @@ A function SHOULD preserve comments when input serialization format is YAML.
This allows for human authoring of configuration to coexist with changes made by
functions.
### Annotations
### Internal Annotations
The orchestrator annotates resources in the wire format with annotation prefix
`config.kubernetes.io`. These annotations are not persisted when the
orchestrator writes the resources to the filesystem. The orchestrator sets this
annotation when reading files from the local filesystem and removes the
annotation when writing the output of functions back to the filesystem.
For orchestration purposes, the orchestrator will use a set of annotations,
referred to as _internal annotations_, on resources in `Resources.items`. These
annotations are not persisted to resource manifests on the filesystem: The
orchestrator sets this annotation when reading files from the local filesystem
and removes the annotation when writing the output of functions back to the
filesystem.
In general, a function MUST NOT modify these annotations except the ones
explicitly listed below.
Annotation prefix `internal.config.kubernetes.io` is reserved for use for
internal annotations. In general, a function MUST NOT modify these annotations with
the exception of the specific annotations listed below. This enables orchestrators to add additional internal annotations, without requiring changes to existing functions.
#### `config.kubernetes.io/path`
#### `internal.config.kubernetes.io/path`
Records the slash-delimited, OS-agnostic, relative file path to a resource. The
path is relative to a fix location on the filesystem. Different orchestrator
implementations can choose different fixed points.
A function SHOULD NOT modify this annotation.
A function SHOULD NOT modify these annotations.
Example:
```yaml
metadata:
annotations:
config.kubernetes.io/path: "relative/file/path.yaml"
internal.config.kubernetes.io/path: "relative/file/path.yaml"
```
#### `config.kubernetes.io/index`
#### `internal.config.kubernetes.io/index`
Records the index of a Resource in file. In a multi-object YAML file, resources
are separated by three dashes (`---`), and the index represents the position of
the Resource starting from zero. When this annotation is not specified, it
implies a value of `0`.
A function SHOULD NOT modify this annotation.
A function SHOULD NOT modify these annotations.
Example:
```yaml
metadata:
annotations:
config.kubernetes.io/path: "relative/file/path.yaml"
config.kubernetes.io/index: 2
internal.config.kubernetes.io/path: "relative/file/path.yaml"
internal.config.kubernetes.io/index: 2
```
This represents the third resource in the file.

View File

@@ -0,0 +1,11 @@
# Manifest Annotations
This document lists the annotations that can be declared in resource manifests.
### `config.kubernetes.io/local-config`
A value of `"true"` for this annotation declares that the resource is only consumed by
client-side tooling and should not be applied to the API server.
A value of `"false"` can be used to declare that a resource should be applied to
the API server even when it is assumed to be local.

View File

@@ -16,5 +16,5 @@ require (
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/inf.v0 v0.9.1
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
sigs.k8s.io/kustomize/kyaml v0.11.0
sigs.k8s.io/kustomize/kyaml v0.11.1
)

View File

@@ -245,7 +245,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -21,12 +21,13 @@ import (
func NewAnnotateRunner(parent string) *AnnotateRunner {
r := &AnnotateRunner{}
c := &cobra.Command{
Use: "annotate [DIR]",
Args: cobra.MaximumNArgs(1),
Short: commands.AnnotateShort,
Long: commands.AnnotateLong,
Example: commands.AnnotateExamples,
RunE: r.runE,
Use: "annotate [DIR]",
Args: cobra.MaximumNArgs(1),
Short: commands.AnnotateShort,
Long: commands.AnnotateLong,
Example: commands.AnnotateExamples,
RunE: r.runE,
Deprecated: "use the `commonAnnotations` field in your kustomization file.",
}
runner.FixDocs(parent, c)
r.Command = c

View File

@@ -559,7 +559,7 @@ added annotations in the package
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
if !assert.Contains(t, actualNormalized, expectedNormalized) {
t.FailNow()
}
})

View File

@@ -32,6 +32,8 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
Example: commands.CreateSetterExamples,
PreRunE: r.preRunE,
RunE: r.runE,
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
}
set.Flags().StringVar(&r.FieldValue, "value", "",
"optional flag, alternative to specifying the value as an argument. e.g. used to specify values that start with '-'")

View File

@@ -869,7 +869,7 @@ setter with name "namespace" already exists, if you want to modify it, please de
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) {
if !assert.Contains(t, actualNormalized, expectedNormalized) {
t.FailNow()
}
})

View File

@@ -23,6 +23,8 @@ func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
Args: cobra.ExactArgs(2),
PreRun: r.preRun,
RunE: r.runE,
Deprecated: "imperative substitutions will no longer be available in kustomize v5.\n" +
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
}
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
"name of the field to set -- e.g. --field image")

View File

@@ -506,7 +506,7 @@ created substitution "image-tag"`,
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
t.FailNow()
}
})

View File

@@ -26,6 +26,8 @@ func GetInitRunner(name string) *InitRunner {
Long: commands.InitLong,
Example: commands.InitExamples,
RunE: r.runE,
Deprecated: "setter commands and substitutions will no longer be available in kustomize v5.\n" +
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
}
runner.FixDocs(name, c)
r.Command = c

View File

@@ -31,6 +31,8 @@ func NewListSettersRunner(parent string) *ListSettersRunner {
Example: commands.ListSettersExamples,
PreRunE: r.preRunE,
RunE: r.runE,
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
}
c.Flags().BoolVar(&r.Markdown, "markdown", false,
"output as github markdown")

View File

@@ -525,7 +525,7 @@ test/testdata/dataset-with-setters/mysql/
// normalize path format for windows
actualNormalized := strings.Replace(actual.String(), "\\", "/", -1)
if !assert.Equal(t, strings.TrimSpace(test.expected), strings.TrimSpace(actualNormalized)) {
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(test.expected)) {
t.FailNow()
}
})

View File

@@ -27,6 +27,8 @@ func NewSetRunner(parent string) *SetRunner {
Example: commands.SetExamples,
PreRunE: r.preRunE,
RunE: r.runE,
Deprecated: "setter commands will no longer be available in kustomize v5.\n" +
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
}
runner.FixDocs(parent, c)
r.Command = c

View File

@@ -1137,7 +1137,7 @@ set 1 field(s) of setter "namespace" to value "otherspace"
expectedNormalized := strings.Replace(
strings.Replace(expected, "\\", "/", -1),
"//", "/", -1)
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
t.FailNow()
}
})

View File

@@ -542,13 +542,13 @@ kind: Input
metadata:
name: foo
annotations:
a-bool-value: true
a-int-value: 2
a-string-value: a
config.kubernetes.io/function: |
starlark:
path: script.star
name: fn
a-bool-value: true
a-int-value: 2
a-string-value: a
data:
boolValue: true
intValue: 2

View File

@@ -25,6 +25,8 @@ func GetFmtRunner(name string) *FmtRunner {
Example: commands.FmtExamples,
RunE: r.runE,
PreRunE: r.preRunE,
Deprecated: "imperative formatting will no longer be available in kustomize v5.\n" +
"Declare a formatting transformer in your kustomization instead.",
}
runner.FixDocs(name, c)
c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern,

View File

@@ -78,7 +78,7 @@ func TestFmtCommand_stdin(t *testing.T) {
assert.NoError(t, err)
// verify the output
assert.Equal(t, string(testyaml.FormattedYaml1), out.String())
assert.Contains(t, out.String(), string(testyaml.FormattedYaml1))
}
// TestCmd_filesAndstdin verifies that if both files and stdin input are provided, only
@@ -238,7 +238,7 @@ formatted resource files in the package
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, strings.TrimSpace(expectedNormalized), strings.TrimSpace(actualNormalized)) {
if !assert.Contains(t, strings.TrimSpace(actualNormalized), strings.TrimSpace(expectedNormalized)) {
t.FailNow()
}
})

View File

@@ -43,7 +43,9 @@ kind: Krmfile
t.FailNow()
}
if !assert.Equal(t, "", b.String()) {
if !assert.Equal(t, `Command "init" is deprecated, setter commands and substitutions will no longer be available in kustomize v5.
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.
`, b.String()) {
t.FailNow()
}
}
@@ -78,7 +80,9 @@ kind: Krmfile
t.FailNow()
}
if !assert.Equal(t, "", b.String()) {
if !assert.Equal(t, `Command "init" is deprecated, setter commands and substitutions will no longer be available in kustomize v5.
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.
`, b.String()) {
t.FailNow()
}
}

View File

@@ -19,6 +19,8 @@ func GetMergeRunner(name string) *MergeRunner {
Long: commands.MergeLong,
Example: commands.MergeExamples,
RunE: r.runE,
Deprecated: "this will no longer be available in kustomize v5.\n" +
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
}
runner.FixDocs(name, c)
r.Command = c

View File

@@ -18,6 +18,8 @@ func GetMerge3Runner(name string) *Merge3Runner {
Long: commands.Merge3Long,
Example: commands.Merge3Examples,
RunE: r.runE,
Deprecated: "this will no longer be available in kustomize v5.\n" +
"See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.",
}
runner.FixDocs(name, c)
c.Flags().StringVar(&r.ancestor, "ancestor", "",

View File

@@ -6,6 +6,7 @@ package commands
import (
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
@@ -73,6 +74,7 @@ func GetRunFnRunner(name string) *RunFnRunner {
"a list of environment variables to be used by functions")
r.Command.Flags().BoolVar(
&r.AsCurrentUser, "as-current-user", false, "use the uid and gid of the command executor to run the function in the container")
return r
}
@@ -302,6 +304,11 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
// parse mounts to set storageMounts
storageMounts := toStorageMounts(r.Mounts)
wd, err := os.Getwd()
if err != nil {
return err
}
r.RunFns = runfn.RunFns{
FunctionPaths: r.FnPaths,
GlobalScope: r.GlobalScope,
@@ -317,6 +324,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
LogSteps: r.LogSteps,
Env: r.Env,
AsCurrentUser: r.AsCurrentUser,
WorkingDir: wd,
}
// don't consider args for the function

View File

@@ -11,13 +11,14 @@ import (
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/runfn"
)
// TestRunFnCommand_preRunE verifies that preRunE correctly parses the commandline
// flags and arguments into the RunFns structure to be executed.
func TestRunFnCommand_preRunE(t *testing.T) {
wd, err := os.Getwd()
assert.NoError(t, err)
tests := []struct {
name string
args []string
@@ -201,6 +202,7 @@ apiVersion: v1
Path: "dir",
EnableStarlark: true,
Env: []string{},
WorkingDir: wd,
},
},
{
@@ -254,6 +256,7 @@ apiVersion: v1
Path: "dir",
ResultsDir: "foo/",
Env: []string{},
WorkingDir: wd,
},
expected: `
metadata:
@@ -286,9 +289,10 @@ apiVersion: v1
args: []string{"run", "dir", "--log-steps"},
path: "dir",
expectedStruct: &runfn.RunFns{
Path: "dir",
LogSteps: true,
Env: []string{},
Path: "dir",
LogSteps: true,
Env: []string{},
WorkingDir: wd,
},
},
{
@@ -296,8 +300,9 @@ apiVersion: v1
args: []string{"run", "dir", "--env", "FOO=BAR", "-e", "BAR"},
path: "dir",
expectedStruct: &runfn.RunFns{
Path: "dir",
Env: []string{"FOO=BAR", "BAR"},
Path: "dir",
Env: []string{"FOO=BAR", "BAR"},
WorkingDir: wd,
},
},
{
@@ -308,6 +313,7 @@ apiVersion: v1
Path: "dir",
AsCurrentUser: true,
Env: []string{},
WorkingDir: wd,
},
},
}

View File

@@ -6,8 +6,8 @@ require (
github.com/rakyll/statik v0.1.7
github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.5.1
sigs.k8s.io/kustomize/api v0.8.10
sigs.k8s.io/kustomize/kyaml v0.11.0
sigs.k8s.io/kustomize/api v0.8.11
sigs.k8s.io/kustomize/kyaml v0.11.1
)
replace sigs.k8s.io/kustomize/api => ../../api

View File

@@ -228,8 +228,8 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -32,7 +32,7 @@ chart repository.
This example defines the `helm` command as
<!-- @defineHelmCommand @testHelm -->
```
helmCommand=~/go/bin/helmV3
helmCommand=${MYGOBIN:-~/go/bin}/helmV3
```
This value is needed for testing this example in CI/CD.

View File

@@ -19,6 +19,10 @@
set -e
# Unset CDPATH to restore default cd behavior. An exported CDPATH can
# cause cd to output the current directory to STDOUT.
unset CDPATH
where=$PWD
release_url=https://api.github.com/repos/kubernetes-sigs/kustomize/releases

View File

@@ -29,6 +29,7 @@ fi
# We test against the latest release, and HEAD, and presumably
# any branch using this label, so it should probably get
# a new value.
export MYGOBIN
mdrip --mode test --blockTimeOut 15m \
--label testAgainstLatestRelease examples

View File

@@ -32,6 +32,7 @@ func NewCmdSet(fSys filesys.FileSystem, ldr ifc.KvLoader, v ifc.Validator) *cobr
newCmdSetImage(fSys),
newCmdSetReplicas(fSys),
newCmdSetLabel(fSys, ldr.Validator().MakeLabelValidator()),
newCmdSetAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()),
)
return c
}

View File

@@ -0,0 +1,99 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"errors"
"fmt"
"regexp"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/util"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
type setAnnotationOptions struct {
metadata map[string]string
mapValidator func(map[string]string) error
}
// IsValidKey checks key against regex. First part for prefix segment (DNS1123Label) of an annotation followed by a slash, second part for name segment of an annotation
// see https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
var IsValidKey = regexp.MustCompile(`^([a-zA-Z](([-a-zA-Z0-9.]{0,251})[a-zA-Z0-9])?\/)?[a-zA-Z0-9]([-a-zA-Z0-9_.]{0,61}[a-zA-Z0-9])?$`).MatchString
// newCmdSetAnnotation sets one or more commonAnnotations to the kustomization file.
func newCmdSetAnnotation(fSys filesys.FileSystem, v func(map[string]string) error) *cobra.Command {
var o setAnnotationOptions
o.mapValidator = v
cmd := &cobra.Command{
Use: "annotation",
Short: "Sets one or more commonAnnotations in " +
konfig.DefaultKustomizationFileName(),
Example: `
set annotation {annotationKey1:annotationValue1} {annotationKey2:annotationValue2}`,
RunE: func(cmd *cobra.Command, args []string) error {
return o.runE(args, fSys, o.setAnnotations)
},
}
return cmd
}
func (o *setAnnotationOptions) runE(
args []string, fSys filesys.FileSystem, setter func(*types.Kustomization) error) error {
err := o.validateAndParse(args)
if err != nil {
return err
}
kf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := kf.Read()
if err != nil {
return err
}
err = setter(m)
if err != nil {
return err
}
return kf.Write(m)
}
// validateAndParse validates `set` commands and parses them into o.metadata
func (o *setAnnotationOptions) validateAndParse(args []string) error {
if len(args) < 1 {
return fmt.Errorf("must specify annotation")
}
m, err := util.ConvertSliceToMap(args, "annotation")
if err != nil {
return err
}
if err = o.mapValidator(m); err != nil {
return err
}
for key := range m {
if !IsValidKey(key) {
return errors.New("invalid annotation key: see the syntax and character set rules at https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/")
}
}
o.metadata = m
return nil
}
func (o *setAnnotationOptions) setAnnotations(m *types.Kustomization) error {
if m.CommonAnnotations == nil {
m.CommonAnnotations = make(map[string]string)
}
return o.writeToMap(m.CommonAnnotations)
}
func (o *setAnnotationOptions) writeToMap(m map[string]string) error {
for k, v := range o.metadata {
m[k] = v
}
return nil
}

View File

@@ -0,0 +1,229 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"testing"
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
const invalidAnnotationKey string = "invalid annotation key: see the syntax and character set rules at " +
"https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/"
func makeAnnotationKustomization(t *testing.T) *types.Kustomization {
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomization(fSys)
kf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
t.Errorf("unexpected new error %v", err)
}
m, err := kf.Read()
if err != nil {
t.Errorf("unexpected read error %v", err)
}
return m
}
func TestRunSetAnnotation(t *testing.T) {
var o setAnnotationOptions
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
m := makeAnnotationKustomization(t)
err := o.setAnnotations(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
// adding the same test input should work
err = o.setAnnotations(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
// adding new annotations should work
o.metadata = map[string]string{"new": "annotation", "owls": "not cute"}
err = o.setAnnotations(m)
if err != nil {
t.Errorf("unexpected error: could not write to kustomization file")
}
}
func TestSetAnnotationNoArgs(t *testing.T) {
fSys := filesys.MakeFsInMemory()
v := valtest_test.MakeHappyMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
err := cmd.Execute()
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
t.FailNow()
}
if err.Error() != "must specify annotation" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestSetAnnotationInvalidFormat(t *testing.T) {
fSys := filesys.MakeFsInMemory()
v := valtest_test.MakeSadMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
args := []string{"exclamation!:point"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
t.FailNow()
}
if err.Error() != valtest_test.SAD {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestSetAnnotationPrefixColonName(t *testing.T) {
var o setAnnotationOptions
o.metadata = map[string]string{"internal.config.kubernetes.io/options": "true"}
m := makeAnnotationKustomization(t)
err := o.setAnnotations(m)
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestSetAnnotation253Prefix63Name(t *testing.T) {
var o setAnnotationOptions
o.metadata = map[string]string{"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu" +
"vwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde" +
"fghijklmnopqrstuvwxyzabcdefghijklmnopqrs/abcdefghijklmnopqrstuvwxyzabcdefghijklmnop" +
"qrstuvwxyzabcdefghijk": "true"}
m := makeAnnotationKustomization(t)
err := o.setAnnotations(m)
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestSetAnnotation254Prefix62Name(t *testing.T) {
fSys := filesys.MakeFsInMemory()
v := valtest_test.MakeHappyMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
args := []string{"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi" +
"jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn" +
"opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrs" +
"tuvwxyzabcdefghijklmnopqrst/abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabc" +
"defghij:true"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
t.FailNow()
}
if err.Error() != invalidAnnotationKey {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestSetAnnotation252Prefix64Name(t *testing.T) {
fSys := filesys.MakeFsInMemory()
v := valtest_test.MakeHappyMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
args := []string{"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi" +
"jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmn" +
"opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrs" +
"tuvwxyzabcdefghijklmnopqr/abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde" +
"fghijkl:true"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
t.FailNow()
}
if err.Error() != invalidAnnotationKey {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestSetAnnotationNoKey(t *testing.T) {
fSys := filesys.MakeFsInMemory()
v := valtest_test.MakeHappyMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
args := []string{":nokey"}
err := cmd.RunE(cmd, args)
v.VerifyNoCall()
if err == nil {
t.Errorf("expected an error")
t.FailNow()
}
if err.Error() != "invalid annotation: ':nokey' (need k:v pair where v may be quoted)" {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestSetAnnotationTooManyColons(t *testing.T) {
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomization(fSys)
v := valtest_test.MakeHappyMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
args := []string{"key:v1:v2"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestSetAnnotationNoValue(t *testing.T) {
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomization(fSys)
v := valtest_test.MakeHappyMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
args := []string{"no,value:"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err == nil {
t.Errorf("expected an error")
t.FailNow()
}
if err.Error() != invalidAnnotationKey {
t.Errorf("incorrect error: %v", err.Error())
}
}
func TestSetAnnotationMultipleArgs(t *testing.T) {
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomization(fSys)
v := valtest_test.MakeHappyMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
args := []string{"this:input", "has:spaces"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}
func TestSetAnnotationExisting(t *testing.T) {
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomization(fSys)
v := valtest_test.MakeHappyMapValidator(t)
cmd := newCmdSetAnnotation(fSys, v.Validator)
args := []string{"key:foo"}
err := cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
v = valtest_test.MakeHappyMapValidator(t)
cmd = newCmdSetAnnotation(fSys, v.Validator)
err = cmd.RunE(cmd, args)
v.VerifyCall()
if err != nil {
t.Errorf("unexpected error: %v", err.Error())
}
}

View File

@@ -8,26 +8,38 @@ import (
"os/exec"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
)
var format string
// NewCmdFetch makes a new fetch command.
func NewCmdFetch(w io.Writer) *cobra.Command {
infoCmd := cobra.Command{
fetchCmd := cobra.Command{
Use: "fetch",
Short: `Fetches the OpenAPI specification from the current kubernetes cluster specified
in the user's kubeconfig`,
Example: `kustomize openapi fetch`,
Run: func(cmd *cobra.Command, args []string) {
printSchema(w)
RunE: func(cmd *cobra.Command, args []string) error {
return printSchema(w)
},
}
return &infoCmd
fetchCmd.Flags().StringVar(
&format,
"format",
"json",
"Specify format for fetched schema ('json' or 'yaml')")
return &fetchCmd
}
func printSchema(w io.Writer) {
func printSchema(w io.Writer) error {
if format != "json" && format != "yaml" {
return fmt.Errorf("format must be either 'json' or 'yaml'")
}
errMsg := `
Error fetching schema from cluster.
Please make sure kubectl is installed and its context is set correctly.
Please make sure kubectl is installed, its context is set correctly, and your cluster is up.
Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/install-kubectl/`
command := exec.Command("kubectl", []string{"get", "--raw", "/openapi/v2"}...)
@@ -36,9 +48,10 @@ Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/inst
command.Stdout = &stdout
command.Stderr = &stderr
err := command.Run()
if err != nil || stdout.String() == "" {
fmt.Fprintln(w, err, stderr.String()+errMsg)
return
if err != nil {
return fmt.Errorf("%w\n%s", err, stderr.String()+errMsg)
} else if stdout.String() == "" {
return fmt.Errorf(stderr.String() + errMsg)
}
// format and output
@@ -46,5 +59,14 @@ Installation and setup instructions: https://kubernetes.io/docs/tasks/tools/inst
output := stdout.Bytes()
json.Unmarshal(output, &jsonSchema)
output, _ = json.MarshalIndent(jsonSchema, "", " ")
if format == "yaml" {
output, err = yaml.JSONToYAML(output)
if err != nil {
return err
}
}
fmt.Fprintln(w, string(output))
return nil
}

View File

@@ -8,9 +8,9 @@ require (
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
sigs.k8s.io/kustomize/api v0.8.10
sigs.k8s.io/kustomize/cmd/config v0.9.13
sigs.k8s.io/kustomize/kyaml v0.11.0
sigs.k8s.io/kustomize/api v0.8.11
sigs.k8s.io/kustomize/cmd/config v0.10.0
sigs.k8s.io/kustomize/kyaml v0.11.1
sigs.k8s.io/yaml v1.2.0
)

View File

@@ -253,10 +253,10 @@ k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
sigs.k8s.io/kustomize/cmd/config v0.9.13 h1:lqOf0QcFhNvgZkgrPINNRs7TxEO7IGVtLMyUJId3oRE=
sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM=
sigs.k8s.io/kustomize/cmd/config v0.10.0 h1:6XPkaOu9eFdvrcqHpfmYAKoBvE01JdvsTVvtDuAFM+8=
sigs.k8s.io/kustomize/cmd/config v0.10.0/go.mod h1:XS4NFKucjk0/+nPKJwSMoZYCjey2PB0ipNoZabXUFPU=
sigs.k8s.io/kustomize/kyaml v0.11.1 h1:MWihd9syKG7VQnAzr/OpKb94FvH+cw96nEV1u3it7pI=
sigs.k8s.io/kustomize/kyaml v0.11.1/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -123,11 +123,11 @@ func (n *fsNode) addFile(name string, c []byte) (result *fsNode, err error) {
if result.offset != nil {
return nil, fmt.Errorf("cannot add already opened file '%s'", n.Path())
}
result.content = c
result.content = append(result.content[:0], c...)
return result, nil
}
result = &fsNode{
content: c,
content: append([]byte(nil), c...),
parent: parent,
}
parent.dir[fileName] = result

View File

@@ -157,9 +157,13 @@ func runBasicOperations(
if string(stuff) != both {
t.Fatalf("%s; unexpected content '%s', expected '%s'", c.what, stuff, both)
}
if err := fSys.WriteFile(c.path, []byte(shortContent)); err != nil {
content := []byte(shortContent)
if err := fSys.WriteFile(c.path, content); err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
// This ensures that modifying the original slice does not change the contents of the file.
content[0] = '@'
stuff, err = fSys.ReadFile(c.path)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)

View File

@@ -74,6 +74,8 @@ type CommandResultsChecker struct {
// Command provides the function to run.
Command func() *cobra.Command
*checkerCore
}
// Assert runs the command with the input provided in each valid test directory
@@ -85,39 +87,51 @@ func (rc *CommandResultsChecker) Assert(t *testing.T) bool {
if rc.InputFilenameGlob == "" {
rc.InputFilenameGlob = DefaultInputFilenameGlob
}
checker := newResultsChecker(
rc.TestDataDirectory, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename,
rc.OutputAssertionFunc, rc.ErrorAssertionFunc,
rc.UpdateExpectedFromActual,
)
checker.assert(t, func() (string, string) {
_, err := os.Stat(rc.ConfigInputFilename)
if os.IsNotExist(err) {
t.Errorf("Test case is missing FunctionConfig input file (default: %s)", DefaultConfigInputFilename)
}
require.NoError(t, err)
args := []string{rc.ConfigInputFilename}
if rc.InputFilenameGlob != "" {
inputs, err := filepath.Glob(rc.InputFilenameGlob)
require.NoError(t, err)
args = append(args, inputs...)
}
var stdOut, stdErr bytes.Buffer
cmd := rc.Command()
cmd.SetArgs(args)
cmd.SetOut(&stdOut)
cmd.SetErr(&stdErr)
err = cmd.Execute()
return stdOut.String(), stdErr.String()
})
rc.checkerCore = &checkerCore{
testDataDirectory: rc.TestDataDirectory,
expectedOutputFilename: rc.ExpectedOutputFilename,
expectedErrorFilename: rc.ExpectedErrorFilename,
updateExpectedFromActual: rc.UpdateExpectedFromActual,
outputAssertionFunc: rc.OutputAssertionFunc,
errorAssertionFunc: rc.ErrorAssertionFunc,
}
rc.checkerCore.setDefaults()
runAllTestCases(t, rc)
return true
}
func (rc *CommandResultsChecker) isTestDir(path string) bool {
return atLeastOneFileExists(
filepath.Join(path, rc.ConfigInputFilename),
filepath.Join(path, rc.checkerCore.expectedOutputFilename),
filepath.Join(path, rc.checkerCore.expectedErrorFilename),
)
}
func (rc *CommandResultsChecker) runInCurrentDir(t *testing.T) (string, string) {
_, err := os.Stat(rc.ConfigInputFilename)
if os.IsNotExist(err) {
t.Errorf("Test case is missing FunctionConfig input file (default: %s)", DefaultConfigInputFilename)
}
require.NoError(t, err)
args := []string{rc.ConfigInputFilename}
if rc.InputFilenameGlob != "" {
inputs, err := filepath.Glob(rc.InputFilenameGlob)
require.NoError(t, err)
args = append(args, inputs...)
}
var stdOut, stdErr bytes.Buffer
cmd := rc.Command()
cmd.SetArgs(args)
cmd.SetOut(&stdOut)
cmd.SetErr(&stdErr)
_ = cmd.Execute()
return stdOut.String(), stdErr.String()
}
// ProcessorResultsChecker tests a processor function by running it with predefined inputs
// and comparing the outputs to expected results.
type ProcessorResultsChecker struct {
@@ -159,6 +173,8 @@ type ProcessorResultsChecker struct {
// Processor returns a ResourceListProcessor to run.
Processor func() framework.ResourceListProcessor
*checkerCore
}
// Assert runs the processor with the input provided in each valid test directory
@@ -167,37 +183,50 @@ func (rc *ProcessorResultsChecker) Assert(t *testing.T) bool {
if rc.InputFilename == "" {
rc.InputFilename = DefaultInputFilename
}
checker := newResultsChecker(
rc.TestDataDirectory, rc.ExpectedOutputFilename, rc.ExpectedErrorFilename,
rc.OutputAssertionFunc, rc.ErrorAssertionFunc,
rc.UpdateExpectedFromActual,
)
checker.assert(t, func() (string, string) {
_, err := os.Stat(rc.InputFilename)
if os.IsNotExist(err) {
t.Error("Test case is missing input file")
}
require.NoError(t, err)
actualOutput := bytes.NewBuffer([]byte{})
rlBytes, err := ioutil.ReadFile(rc.InputFilename)
require.NoError(t, err)
rw := kio.ByteReadWriter{
Reader: bytes.NewBuffer(rlBytes),
Writer: actualOutput,
}
err = framework.Execute(rc.Processor(), &rw)
if err != nil {
require.NotEmptyf(t, err.Error(), "processor returned error with empty message")
return actualOutput.String(), err.Error()
}
return actualOutput.String(), ""
})
rc.checkerCore = &checkerCore{
testDataDirectory: rc.TestDataDirectory,
expectedOutputFilename: rc.ExpectedOutputFilename,
expectedErrorFilename: rc.ExpectedErrorFilename,
updateExpectedFromActual: rc.UpdateExpectedFromActual,
outputAssertionFunc: rc.OutputAssertionFunc,
errorAssertionFunc: rc.ErrorAssertionFunc,
}
rc.checkerCore.setDefaults()
runAllTestCases(t, rc)
return true
}
func (rc *ProcessorResultsChecker) isTestDir(path string) bool {
return atLeastOneFileExists(
filepath.Join(path, rc.InputFilename),
filepath.Join(path, rc.checkerCore.expectedOutputFilename),
filepath.Join(path, rc.checkerCore.expectedErrorFilename),
)
}
func (rc *ProcessorResultsChecker) runInCurrentDir(t *testing.T) (string, string) {
_, err := os.Stat(rc.InputFilename)
if os.IsNotExist(err) {
t.Errorf("Test case is missing input file (default: %s)", DefaultInputFilename)
}
require.NoError(t, err)
actualOutput := bytes.NewBuffer([]byte{})
rlBytes, err := ioutil.ReadFile(rc.InputFilename)
require.NoError(t, err)
rw := kio.ByteReadWriter{
Reader: bytes.NewBuffer(rlBytes),
Writer: actualOutput,
}
err = framework.Execute(rc.Processor(), &rw)
if err != nil {
require.NotEmptyf(t, err.Error(), "processor returned error with empty message")
return actualOutput.String(), err.Error()
}
return actualOutput.String(), ""
}
type AssertionFunc func(t *testing.T, expected string, actual string)
// RequireEachLineMatches is an AssertionFunc that treats each line of expected string
@@ -223,8 +252,35 @@ func standardizeSpacing(s string) string {
return strings.ReplaceAll(strings.TrimSpace(s), "\r\n", "\n")
}
// resultsChecker implements the core logic shared by all results checking types.
type resultsChecker struct {
// resultsChecker is implemented by ProcessorResultsChecker and CommandResultsChecker, partially via checkerCore
type resultsChecker interface {
// TestCasesRun returns a list of the test case directories that have been seen.
TestCasesRun() (paths []string)
// rootDir is the root of the tree of test directories to be searched for fixtures.
rootDir() string
// isTestDir takes the name of directory and returns whether or not it contains the files required to be a test case.
isTestDir(dir string) bool
// runInCurrentDir executes the code the checker is checking in the context of the current working directory.
runInCurrentDir(t *testing.T) (stdOut, stdErr string)
// resetTestCasesRun resets the list of test case directories seen.
resetTestCasesRun()
// recordTestCase adds to the list of test case directories seen.
recordTestCase(path string)
// readAssertionFiles returns the contents of the output and error fixtures
readAssertionFiles(t *testing.T) (expectedOutput string, expectedError string)
// shouldUpdateFixtures indicates whether or not this checker is currently being used to update fixtures instead of run them.
shouldUpdateFixtures() bool
// updateFixtures modifies the test fixture files to match the given content
updateFixtures(t *testing.T, actualOutput string, actualError string)
// assertOutputMatches compares the expected output to the output recieved.
assertOutputMatches(t *testing.T, expected string, actual string)
// assertErrorMatches compares teh expected error to the error received.
assertErrorMatches(t *testing.T, expected string, actual string)
}
// checkerCore implements the resultsChecker methods that are shared between the two checker types
type checkerCore struct {
testDataDirectory string
expectedOutputFilename string
expectedErrorFilename string
@@ -232,20 +288,10 @@ type resultsChecker struct {
outputAssertionFunc AssertionFunc
errorAssertionFunc AssertionFunc
testsCasesRun int
testsCasesRun []string
}
func newResultsChecker(testDataDir string, outputFilename string, errorFilename string,
outputAsserter AssertionFunc, errorAsserter AssertionFunc,
updateFixtures bool) *resultsChecker {
rc := resultsChecker{
testDataDirectory: testDataDir,
expectedOutputFilename: outputFilename,
expectedErrorFilename: errorFilename,
updateExpectedFromActual: updateFixtures,
outputAssertionFunc: outputAsserter,
errorAssertionFunc: errorAsserter,
}
func (rc *checkerCore) setDefaults() {
if rc.testDataDirectory == "" {
rc.testDataDirectory = DefaultTestDataDirectory
}
@@ -261,73 +307,47 @@ func newResultsChecker(testDataDir string, outputFilename string, errorFilename
if rc.errorAssertionFunc == nil {
rc.errorAssertionFunc = RequireEachLineMatches
}
return &rc
}
// assert traverses TestDataDirectory to find test cases, calls getResult to invoke the function
// under test in each directory, and then runs assertions on the returned output and error results.
// It triggers a test failure if no valid test directories were found.
func (rc *resultsChecker) assert(t *testing.T, getResult func() (string, string)) {
err := filepath.Walk(rc.testDataDirectory,
func(path string, info os.FileInfo, err error) error {
require.NoError(t, err)
if !info.IsDir() {
// skip non-directories
return nil
}
rc.runDirectoryTestCase(t, path, getResult)
return nil
})
require.NoError(t, err)
require.NotZero(t, rc.testsCasesRun, "No complete test cases found in %s", rc.testDataDirectory)
func (rc *checkerCore) rootDir() string {
return rc.testDataDirectory
}
func (rc *resultsChecker) runDirectoryTestCase(t *testing.T, path string, getResult func() (string, string)) {
// cd into the directory so we can test functions that refer
// local files by relative paths
d, err := os.Getwd()
require.NoError(t, err)
func (rc *checkerCore) TestCasesRun() []string {
return rc.testsCasesRun
}
defer func() { require.NoError(t, os.Chdir(d)) }()
require.NoError(t, os.Chdir(path))
func (rc *checkerCore) resetTestCasesRun() {
rc.testsCasesRun = []string{}
}
expectedOutput, expectedError := rc.readAssertionFiles(t)
if expectedError == "" && expectedOutput == "" && !rc.updateExpectedFromActual {
// not a test directory: missing expectations and updateExpectedFromActual == false
return
func (rc *checkerCore) recordTestCase(s string) {
rc.testsCasesRun = append(rc.testsCasesRun, s)
}
func (rc *checkerCore) shouldUpdateFixtures() bool {
return rc.updateExpectedFromActual
}
func (rc *checkerCore) updateFixtures(t *testing.T, actualOutput string, actualError string) {
if actualError != "" {
require.NoError(t, ioutil.WriteFile(rc.expectedErrorFilename, []byte(actualError), 0600))
}
// run the test
t.Run(path, func(t *testing.T) {
rc.testsCasesRun += 1
actualOutput, actualError := getResult()
// Configured to update the assertion files instead of comparing them
if rc.updateExpectedFromActual {
if actualError != "" {
require.NoError(t, ioutil.WriteFile(rc.expectedErrorFilename, []byte(actualError), 0600))
}
if len(actualOutput) > 0 {
require.NoError(t, ioutil.WriteFile(rc.expectedOutputFilename, []byte(actualOutput), 0600))
}
t.Skip("Updated fixtures for test case")
}
// Compare the results to the assertion files
if expectedError != "" {
// We expected an error, so make sure there was one
require.NotEmptyf(t, actualError, "test expected an error but message was empty, and output was:\n%s", actualOutput)
rc.errorAssertionFunc(t, expectedError, actualError)
} else {
// We didn't expect an error, and the output should match
require.Emptyf(t, actualError, "test expected no error but got an error message, and output was:\n%s", actualOutput)
rc.outputAssertionFunc(t, expectedOutput, actualOutput)
}
})
if len(actualOutput) > 0 {
require.NoError(t, ioutil.WriteFile(rc.expectedOutputFilename, []byte(actualOutput), 0600))
}
t.Skip("Updated fixtures for test case")
}
// readAssertionFiles reads the expected results and error files
func (rc *resultsChecker) readAssertionFiles(t *testing.T) (string, string) {
func (rc *checkerCore) assertOutputMatches(t *testing.T, expected string, actual string) {
rc.outputAssertionFunc(t, expected, actual)
}
func (rc *checkerCore) assertErrorMatches(t *testing.T, expected string, actual string) {
rc.errorAssertionFunc(t, expected, actual)
}
func (rc *checkerCore) readAssertionFiles(t *testing.T) (string, string) {
// read the expected results
var expectedOutput, expectedError string
if rc.expectedOutputFilename != "" {
@@ -360,3 +380,65 @@ func (rc *resultsChecker) readAssertionFiles(t *testing.T) (string, string) {
}
return expectedOutput, expectedError
}
// runAllTestCases traverses rootDir to find test cases, calls getResult to invoke the function
// under test in each directory, and then runs assertions on the returned output and error results.
// It triggers a test failure if no valid test directories were found.
func runAllTestCases(t *testing.T, checker resultsChecker) {
checker.resetTestCasesRun()
err := filepath.Walk(checker.rootDir(),
func(path string, info os.FileInfo, err error) error {
require.NoError(t, err)
if info.IsDir() && checker.isTestDir(path) {
runDirectoryTestCase(t, path, checker)
}
return nil
})
require.NoError(t, err)
require.NotZero(t, len(checker.TestCasesRun()), "No complete test cases found in %s", checker.rootDir())
}
func runDirectoryTestCase(t *testing.T, path string, checker resultsChecker) {
// cd into the directory so we can test functions that refer to local files by relative paths
d, err := os.Getwd()
require.NoError(t, err)
defer func() { require.NoError(t, os.Chdir(d)) }()
require.NoError(t, os.Chdir(path))
expectedOutput, expectedError := checker.readAssertionFiles(t)
if expectedError == "" && expectedOutput == "" && !checker.shouldUpdateFixtures() {
t.Fatalf("test directory %s must include either expected output or expected error fixture", path)
}
// run the test
t.Run(path, func(t *testing.T) {
checker.recordTestCase(path)
actualOutput, actualError := checker.runInCurrentDir(t)
// Configured to update the assertion files instead of comparing them
if checker.shouldUpdateFixtures() {
checker.updateFixtures(t, actualOutput, actualError)
}
// Compare the results to the assertion files
if expectedError != "" {
// We expected an error, so make sure there was one
require.NotEmptyf(t, actualError, "test expected an error but message was empty, and output was:\n%s", actualOutput)
checker.assertErrorMatches(t, expectedError, actualError)
} else {
// We didn't expect an error, and the output should match
require.Emptyf(t, actualError, "test expected no error but got an error message, and output was:\n%s", actualOutput)
checker.assertOutputMatches(t, expectedOutput, actualOutput)
}
})
}
func atLeastOneFileExists(files ...string) bool {
for _, file := range files {
if _, err := os.Stat(file); err == nil {
return true
}
}
return false
}

View File

@@ -0,0 +1,68 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package frameworktestutil
import (
"path/filepath"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/fn/framework/command"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestProcessorResultsChecker_UpdateExpectedFromActual(t *testing.T) {
dir := filepath.FromSlash("testdata/update_expectations/processor")
checker := ProcessorResultsChecker{
TestDataDirectory: dir,
UpdateExpectedFromActual: true,
Processor: testProcessor,
}
// This should result in the test being skipped. If no tests are found, it will instead fail.
checker.Assert(t)
require.Contains(t, checker.TestCasesRun(), filepath.Join(dir, "important_subdir"))
checker.UpdateExpectedFromActual = false
// This time should inherently pass
checker.Assert(t)
require.Contains(t, checker.TestCasesRun(), filepath.Join(dir, "important_subdir"))
}
func TestCommandResultsChecker_UpdateExpectedFromActual(t *testing.T) {
dir := filepath.FromSlash("testdata/update_expectations/command")
checker := CommandResultsChecker{
TestDataDirectory: dir,
UpdateExpectedFromActual: true,
Command: testCommand,
}
// This should result in the test being skipped. If no tests are found, it will instead fail.
checker.Assert(t)
require.Contains(t, checker.TestCasesRun(), filepath.Join(dir, "important_subdir"))
checker.UpdateExpectedFromActual = false
// This time should inherently pass
checker.Assert(t)
require.Contains(t, checker.TestCasesRun(), filepath.Join(dir, "important_subdir"))
}
func testCommand() *cobra.Command {
return command.Build(testProcessor(), command.StandaloneEnabled, false)
}
func testProcessor() framework.ResourceListProcessor {
return framework.SimpleProcessor{
Filter: kio.FilterFunc(func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
for _, node := range nodes {
err := node.SetAnnotations(map[string]string{"updated": "true"})
if err != nil {
return nil, err
}
}
return nodes, nil
}),
}
}

View File

@@ -0,0 +1,7 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: example.com/v1alpha1
kind: Demo
spec:
value: a

View File

@@ -0,0 +1,16 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-1
annotations:
updated: "true"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-2
annotations:
updated: "true"

View File

@@ -0,0 +1,16 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-1
annotations:
baz: foo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-2
annotations:
foo: bar

View File

@@ -0,0 +1,20 @@
apiVersion:
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: test-1
annotations:
updated: "true"
- apiVersion: apps/v1
kind: Deployment
metadata:
name: test-2
annotations:
updated: "true"
functionConfig:
apiVersion: example.com/v1alpha1
kind: Demo
spec:
value: a

View File

@@ -0,0 +1,22 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
kind: ResourceList
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: test-1
annotations:
baz: foo
- apiVersion: apps/v1
kind: Deployment
metadata:
name: test-2
annotations:
foo: bar
functionConfig:
apiVersion: example.com/v1alpha1
kind: Demo
spec:
value: a

View File

@@ -168,8 +168,7 @@ func (cpt ContainerPatchTemplate) apply(matches []*yaml.RNode) error {
}
for i := range matches {
// TODO(knverey): Make this work for more Kinds and expose the helper for doing so.
containers, err := matches[i].Pipe(yaml.Lookup("spec", "template", "spec", "containers"))
containers, err := matches[i].Pipe(yaml.LookupFirstMatch(yaml.ConventionalContainerPaths))
if err != nil {
return errors.Wrap(err)
}

View File

@@ -5,6 +5,7 @@ package framework_test
import (
"bytes"
"regexp"
"strings"
"testing"
@@ -276,6 +277,57 @@ functionConfig:
`), strings.TrimSpace(out.String()))
}
func TestTemplateProcessor_ContainerPatchTemplates_MultipleWorkloadKinds(t *testing.T) {
type API struct {
Spec struct {
Key string `json:"key" yaml:"key"`
Value string `json:"value" yaml:"value"`
A string `json:"a" yaml:"a"`
}
}
config := &API{}
p := framework.TemplateProcessor{
TemplateData: config,
ResourceTemplates: []framework.ResourceTemplate{{
Templates: parser.TemplateFiles("testdata/template-processor/templates/container-sources"),
}},
PatchTemplates: []framework.PatchTemplate{
&framework.ContainerPatchTemplate{
Templates: parser.TemplateFiles("testdata/template-processor/container-patches"),
},
},
}
out := new(bytes.Buffer)
rw := &kio.ByteReadWriter{Writer: out, Reader: bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
items: []
functionConfig:
spec:
key: Hello
value: World
a: bar
`)}
require.NoError(t, framework.Execute(p, rw))
resources, err := (&kio.ByteReader{Reader: out}).Read()
require.NoError(t, err)
envRegex := regexp.MustCompile(strings.TrimSpace(`
\s+ env:
\s+ - name: EXISTING
\s+ value: variable
\s+ - name: Hello
\s+ value: World
`))
require.Equal(t, 9, len(resources))
for i, r := range resources {
t.Run(r.GetKind(), func(t *testing.T) {
assert.Regexp(t, envRegex, resources[i].MustString())
})
}
}
func TestSimpleProcessor_Process_loads_config(t *testing.T) {
cfg := new(struct {
Value string `yaml:"value"`

View File

@@ -0,0 +1,19 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,23 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: hello
spec:
selector:
matchLabels:
name: hello
template:
metadata:
labels:
name: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,22 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,16 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,14 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: v1
kind: Pod
metadata:
name: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,17 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: v1
kind: PodTemplate
metadata:
name: hello
labels:
tier: hello
template:
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,22 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: hello
spec:
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,22 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: v1
kind: ReplicationController
metadata:
name: hello
spec:
selector:
app: hello
template:
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -0,0 +1,24 @@
# Copyright 2021 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: hello
spec:
serviceName: "nginx"
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginx
env:
- name: EXISTING
value: variable

View File

@@ -5,6 +5,7 @@ package container
import (
"fmt"
"os"
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
@@ -139,19 +140,27 @@ func (c Filter) GetExit() error {
}
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
c.setupExec()
if err := c.setupExec(); err != nil {
return nil, err
}
return c.Exec.Filter(nodes)
}
func (c *Filter) setupExec() {
func (c *Filter) setupExec() error {
// don't init 2x
if c.Exec.Path != "" {
return
return nil
}
wd, err := os.Getwd()
if err != nil {
return err
}
c.Exec.WorkingDir = wd
path, args := c.getCommand()
c.Exec.Path = path
c.Exec.Args = args
return nil
}
// getArgs returns the command + args to run to spawn the container

View File

@@ -6,6 +6,8 @@ package container
import (
"bytes"
"fmt"
"github.com/stretchr/testify/require"
"os"
"testing"
"github.com/stretchr/testify/assert"
@@ -128,7 +130,7 @@ metadata:
instance := NewContainer(tt.containerSpec, tt.UIDGID)
instance.Exec.FunctionConfig = cfg
instance.Env = append(instance.Env, "KYAML_TEST=FOO")
instance.setupExec()
assert.NoError(t, instance.setupExec())
tt.expectedArgs = append(tt.expectedArgs,
runtimeutil.NewContainerEnvFromStringSlice(instance.Env).GetDockerFlags()...)
@@ -173,6 +175,8 @@ metadata:
instance.Exec.FunctionConfig = cfg
instance.Exec.Path = "sed"
instance.Exec.Args = []string{"s/Deployment/StatefulSet/g"}
instance.Exec.WorkingDir = getWorkingDir(t)
output, err := instance.Filter(input)
if !assert.NoError(t, err) {
t.FailNow()
@@ -219,6 +223,7 @@ func TestFilter_ExitCode(t *testing.T) {
instance := Filter{}
instance.Exec.Path = "/not/real/command"
instance.Exec.DeferFailure = true
instance.Exec.WorkingDir = getWorkingDir(t)
_, err := instance.Filter(nil)
if !assert.NoError(t, err) {
t.FailNow()
@@ -231,3 +236,9 @@ func TestFilter_ExitCode(t *testing.T) {
t.FailNow()
}
}
func getWorkingDir(t *testing.T) string {
wd, err := os.Getwd()
require.NoError(t, err)
return wd
}

View File

@@ -7,7 +7,9 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -19,6 +21,10 @@ type Filter struct {
// Args are the arguments to the executable
Args []string `yaml:"args,omitempty"`
// WorkingDir is the working directory that the executable
// should run in
WorkingDir string
runtimeutil.FunctionFilter
}
@@ -32,5 +38,17 @@ func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
cmd.Stdin = reader
cmd.Stdout = writer
cmd.Stderr = os.Stderr
if c.WorkingDir == "" {
return errors.Errorf("no working directory set for exec function")
}
if !filepath.IsAbs(c.WorkingDir) {
return errors.Errorf(
"relative working directory %s not allowed", c.WorkingDir)
}
if c.WorkingDir == "/" {
return errors.Errorf(
"root working directory '/' not allowed")
}
cmd.Dir = c.WorkingDir
return cmd.Run()
}

View File

@@ -4,15 +4,19 @@
package exec_test
import (
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestFunctionFilter_Filter(t *testing.T) {
wd, err := os.Getwd()
require.NoError(t, err)
var tests = []struct {
name string
input []string
@@ -51,8 +55,9 @@ metadata:
},
expectedError: "",
instance: exec.Filter{
Path: "sed",
Args: []string{"s/Deployment/StatefulSet/g"},
Path: "sed",
Args: []string{"s/Deployment/StatefulSet/g"},
WorkingDir: wd,
},
},
}

View File

@@ -15,6 +15,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/order"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -169,8 +170,8 @@ func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return nil, err
}
// copy the comments from the inputs to the outputs
if err := c.setComments(output); err != nil {
// copy the comments and sync the order of fields from the inputs to the outputs
if err := c.copyCommentsAndSyncOrder(output); err != nil {
return nil, err
}
@@ -210,7 +211,7 @@ func (c *FunctionFilter) setIds(nodes []*yaml.RNode) error {
return nil
}
func (c *FunctionFilter) setComments(nodes []*yaml.RNode) error {
func (c *FunctionFilter) copyCommentsAndSyncOrder(nodes []*yaml.RNode) error {
for i := range nodes {
node := nodes[i]
anID, err := node.Pipe(yaml.GetAnnotation(idAnnotation))
@@ -229,6 +230,9 @@ func (c *FunctionFilter) setComments(nodes []*yaml.RNode) error {
if err := comments.CopyComments(in, node); err != nil {
return errors.Wrap(err)
}
if err := order.SyncOrder(in, node); err != nil {
return errors.Wrap(err)
}
if err := node.PipeE(yaml.ClearAnnotation(idAnnotation)); err != nil {
return errors.Wrap(err)
}

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