Compare commits

...

163 Commits

Author SHA1 Message Date
Katrina Verey
63ec6bdb3d Merge pull request #4199 from KnVerey/pinToApi
Pin to api v0.10.0
2021-09-27 09:13:36 -07:00
Kubernetes Prow Robot
25bfe6f306 Merge pull request #4204 from KnVerey/release_updates
Use keyring from kustomize GCP project with new token
2021-09-27 09:01:35 -07:00
Katrina Verey
febfaf16dc Use keyring from kustomize GCP project with new token 2021-09-24 20:24:10 -04:00
Katrina Verey
8268b17700 Pin to api v0.10.0 2021-09-24 16:58:20 -07:00
Kubernetes Prow Robot
0889995a61 Merge pull request #4198 from KnVerey/pinToCmdConfig
Pin to cmd/config v0.10.1
2021-09-24 16:34:21 -07:00
Katrina Verey
4e476ae574 Pin to cmd/config v0.10.1 2021-09-24 16:07:21 -07:00
Kubernetes Prow Robot
7efd7d23fe Merge pull request #4197 from KnVerey/pinToKyaml
Pin to kyaml v0.12.0
2021-09-24 14:07:50 -07:00
Katrina Verey
6fb944815b Pin to kyaml v0.12.0 2021-09-24 12:23:09 -07:00
Kubernetes Prow Robot
5e3432fbbe Merge pull request #4196 from natasha41575/test
fix tests for reader annotations
2021-09-24 08:31:24 -07:00
natasha41575
f0c6bd7773 fix tests for reader annotations 2021-09-24 08:20:44 -07:00
Jeff Regan
dd579c905d Merge pull request #4180 from yuwenma/fix-4124
[Fix 4124]  Skip local resource until all transformations have completed.
2021-09-24 08:05:19 -07:00
Jeff Regan
22b735885a Merge pull request #4190 from natasha41575/MigrateIndexPathIdAnnotations
Migrate index path id annotations
2021-09-24 08:00:15 -07:00
Kubernetes Prow Robot
b7c5058e37 Merge pull request #4177 from sylr/helm-v3.6
Upgrade Helm v3 to v3.6.3
2021-09-24 07:45:24 -07:00
Kubernetes Prow Robot
baff5f4359 Merge pull request #4187 from monopole/deanchorCall
Do YAML anchor expansion shortly after reading YAML.
2021-09-22 13:14:33 -07:00
monopole
dce4ea5846 Add AnchorsAweigh option to ByteReader to toggle YAML alias/anchor expansion 2021-09-22 12:44:08 -07:00
Kubernetes Prow Robot
c47fc48607 Merge pull request #4193 from phanimarupaka/AddressBareSeqNodeComments
Clean up bare sequence node wrapping
2021-09-20 15:46:23 -07:00
Phani Teja Marupaka
1d9b6cbe57 Clean up bare sequence node wrapping 2021-09-20 15:35:40 -07:00
Kubernetes Prow Robot
1cb93123fc Merge pull request #4189 from phanimarupaka/SkipNonKRMResources
Handle parsing of bare sequence yaml nodes
2021-09-20 11:40:24 -07:00
Kubernetes Prow Robot
c6cb42ec27 Merge pull request #4185 from KnVerey/standardize_owners_files
Use standard Kubernetes project roles for ownership
2021-09-20 10:44:10 -07:00
Natasha Sarkar
67a5f6d68f support krm spec v1 and legacy path, index, and id annotations 2021-09-17 17:10:10 -07:00
Phani Teja Marupaka
e997cc5486 Handle parsing of bare sequence yaml nodes 2021-09-17 14:19:27 -07:00
Kubernetes Prow Robot
53577a5190 Merge pull request #4176 from m-Bilal/fix-4123
Fixes 4123; Length check on originalFields of kustomizationFile to prevent panic
2021-09-16 13:15:27 -07:00
Kubernetes Prow Robot
c1ae234a64 Merge pull request #4163 from natasha41575/multipleGvksInOpenApi
support multiple gvks in custom openapi schema
2021-09-16 12:59:26 -07:00
Natasha Sarkar
02cb395ec2 support multiple gvks in custom openapi schema 2021-09-16 12:43:18 -07:00
Kubernetes Prow Robot
65e7529ca0 Merge pull request #4097 from natasha41575/deprecateFn
deprecate fn wrap, xargs, sink, source commands
2021-09-16 12:25:27 -07:00
Natasha Sarkar
f70743b267 deprecate some fn commands 2021-09-16 11:48:02 -07:00
Yuwen Ma
f4382738ab [fix 4124] Skip local resource until all transformations have completed.
Resources annotated as "local-config" are expected to be ignored. This skip local resource happens in "accumulateResources" which happens before any transformation operations.
However, the local resource may be needed in transformations.
Thus, this change removes the "drop local resource" logic from accumulateResources and removes these local resource after all transformation operations and var operations are done.

Note:
None of the existing ResMap functions can drop the resource slice easily: "Clear" will ruin the resource order, "AppendAll" only adds non-existing resource, "AbsorbAll" only add or modify but not delete.
Thus, we introduce a new func "Intersection" for resourceAccumulator that specificaly removes the resource by ID and keep the original order.
2021-09-16 11:15:05 -07:00
Yuwen Ma
a100dca303 [Fix 4124] Add unittest with the given example. 2021-09-16 11:14:07 -07:00
Jeff Regan
50414208d1 Merge pull request #4186 from monopole/cleanupTest
Clean up factor_test before adding DeAnchor call
2021-09-15 17:47:19 -07:00
monopole
e17a007719 Clean up factor_test before adding DeAnchor call 2021-09-15 17:26:50 -07:00
Katrina Verey
dd3c5f5c0a Use standard Kubernetes project roles for ownership 2021-09-15 16:04:58 -07:00
Sylvain Rabot
fb3f560e0c Upgrade Helm v3 to v3.6.3
Helm has started to build darwin/arm64 from v3.6.0.

Signed-off-by: Sylvain Rabot <sylvain@abstraction.fr>
2021-09-12 18:18:41 +02:00
m-Bilal
12c177a365 fixes 4123; added length check on originalFields of kustomizationFile to prevent panic when kustomization file began with a comment(or a blank line) followed by a document separator 2021-09-12 17:35:23 +05:30
John Howard
402f6ca72b Precompute IsNamespaceScoped to avoid expensive schema reads (#4152)
* Precompute IsNamespaceScoped to avoid expensive schema reads

See https://github.com/GoogleContainerTools/kpt/issues/2469

For the `gcr.io/kpt-fn/set-namespace:v0.1` function, over 50% of CPU
time is spent on IsNamespaceScoped. Instead of unmarshalling 100k lines
of JSON to determine this, instead just precompute it. We can ensure
this never is inaccurate as the test verifies the precomputed result is
up to date.

In real world kpt pipelines this cuts execution of set-namespace (and
similar functions, just an example of a trivial function) from 2.0s to
1.0s. Because these functions are run in long pipelines over many
resources, this adds up a lot.

* Add documentation
2021-09-09 10:08:11 -07:00
Kubernetes Prow Robot
2b8a39373e Merge pull request #4172 from phanimarupaka/CopyReferenceNodesBeforeComparing
Copy reference nodes before copying comments and syncing order
2021-09-08 11:29:56 -07:00
Phani Teja Marupaka
17f18604e4 Copy reference nodes before copying comments and syncing order 2021-09-07 16:58:06 -07:00
Kubernetes Prow Robot
99e404cb61 Merge pull request #4169 from invidian/fix-typo
api/krusty: fix typo fileystem -> filesystem
2021-09-07 11:41:17 -07:00
Kubernetes Prow Robot
d4e3b4f832 Merge pull request #4171 from justinsb/cache_orgid
Cache the OrgId for nameReferenceTransformer
2021-09-07 11:17:16 -07:00
Justin SB
6552b90657 Cache the OrgId for nameReferenceTransformer
Because this is in an inner loop and is fairly memory-allocation
expensive even on a single allocation, it comes up top-of-the-list in
memory allocation pprof profiles, for example with the coredns
ClusterAddon.

Add simple caching.
2021-09-07 14:06:38 -04:00
Mateusz Gozdek
bf57d698b1 api/krusty: fix typo fileystem -> filesystem
Part of https://github.com/kubernetes/kubernetes/pull/104747.

Signed-off-by: Mateusz Gozdek <mgozdekof@gmail.com>
2021-09-05 11:33:10 +02:00
Kubernetes Prow Robot
4d002af735 Merge pull request #4165 from natasha41575/nameRefAfterKindChange
update name references after kind change
2021-09-03 12:56:52 -07:00
Natasha Sarkar
2bfc7cc1b0 throw error instead of panic when replacements source.fieldPath doesn't exist (#4166)
* test for missing source.fieldPath in replacements

* throw error instead of panic when replacements source.fieldPath doesn't exist
2021-09-03 11:10:53 -07:00
Natasha Sarkar
0244f0919e update name references after kind change 2021-09-02 12:10:01 -07:00
Natasha Sarkar
f122fb12f3 Merge pull request #4158 from kubernetes-sigs/revert-4157-fix/github-rate-limiter-output
Revert "Return a meaningful message if we hit the rate-limiter of GitHub"
2021-08-31 13:29:11 -07:00
Natasha Sarkar
7d0b7e2113 Revert "Return a meaningful message if we hit the rate-limiter of GitHub" 2021-08-31 13:13:36 -07:00
Kubernetes Prow Robot
c3a67cfdca Merge pull request #4157 from osherdp/fix/github-rate-limiter-output
Return a meaningful message if we hit the rate-limiter of GitHub
2021-08-31 12:42:22 -07:00
Osher De Paz
4315e982be return a meaningful message if we hit the rate-limiter of GitHub 2021-08-31 16:19:39 +03:00
Kubernetes Prow Robot
634464353f Merge pull request #4128 from KnVerey/mini_proposal_process
Introduce in-repo proposal process
2021-08-30 17:22:28 -07:00
Kubernetes Prow Robot
9c36004493 Merge pull request #4143 from KnVerey/release_updates
Release updates
2021-08-30 16:08:29 -07:00
Katrina Verey
1b1034442c Enable real release after dry run without manual cleanup 2021-08-30 15:09:40 -07:00
Katrina Verey
a89863c84c Update release instructions 2021-08-30 15:09:40 -07:00
Katrina Verey
f7340e0615 Proposal template feedback 2021-08-30 12:10:02 -07:00
Kubernetes Prow Robot
bf6b207cc9 Merge pull request #4141 from KnVerey/unpinEverything
Back to development mode; unpin the modules
2021-08-24 12:46:58 -07:00
Katrina Verey
f93b4877f7 Back to development mode; unpin the modules 2021-08-24 12:35:26 -07:00
Kubernetes Prow Robot
cd17338759 Merge pull request #4139 from KnVerey/pinToApi
Pin to api v0.9.0
2021-08-24 12:04:58 -07:00
Katrina Verey
c46867c3a7 Pin to api v0.9.0 2021-08-24 11:54:43 -07:00
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
Katrina Verey
3e7246690f Introduce in-repo proposal process 2021-08-19 17:00:20 -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
192 changed files with 13829 additions and 2779 deletions

View File

@@ -1,6 +1,25 @@
[SIG-CLI]: https://github.com/kubernetes/community/tree/master/sig-cli
[Slack channel]: https://kubernetes.slack.com/messages/kustomize
[Mailing list]: https://groups.google.com/forum/#!forum/kubernetes-sig-cli
[OWNERS file spec]: https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md
[Kustomize OWNERS_ALIASES]: https://github.com/kubernetes-sigs/kustomize/blob/8049f7b1af52e8a7ec26faf6cf714f560d0043c5/OWNERS_ALIASES
[SIG-CLI Teams]: https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml
[Github permissions]: https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level
[Contributor License Agreement]: https://git.k8s.io/community/CLA.md
[Kubernetes Contributor Guide]: http://git.k8s.io/community/contributors/guide
[Contributor Cheat Sheet]: https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md
[CNCF Code of Conduct]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md
[Kubernetes Community Membership]: https://github.com/kubernetes/community/blob/master/community-membership.md
[Contribution Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/
[MacOS Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/mac/
[Windows Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/windows/
# Contributing Guidelines # Contributing Guidelines
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the [CNCF Code of Conduct]. Here is an excerpt:
_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ _As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._
@@ -8,13 +27,22 @@ _As contributors and maintainers of this project, and in the interest of fosteri
Dev guides: Dev guides:
- [Mac](docs/macDevGuide.md) - [Contribution Guide]
- [MacOS Dev Guide]
- [Windows Dev Guide]
We have full documentation on how to get started contributing here: General resources for contributors:
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests - [Contributor License Agreement] - Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests.
- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing) - [Kubernetes Contributor Guide] - Main contributor documentation.
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md) - Common resources for existing developers - [Contributor Cheat Sheet] - Common resources for existing developers.
Here are some additional ideas to help you get started with Kustomize:
- Attend a Kustomize Bug Scrub. Check the [SIG-CLI] meetings list to find the next one.
- Help triage issues by confirming validity and applying the appropriate `kind` label (e.g. comment `/kind bug`).
- Pick up an issue to fix. Issues with the `help-wanted` label are a good place to start, but you can also look for any issue with the `triage/accepted` label and no assignee. Remember to `/assign` yourself to let others know you're working on it.
- Help confirm new issues labelled `kind/bug` by reproducing them with the latest release.
- Support Kustomize users by responding to questions on issues labelled `kind/support` or in the [Slack channel].
## Mentorship ## Mentorship
@@ -22,19 +50,22 @@ We have full documentation on how to get started contributing here:
## Contributor Ladder ## Contributor Ladder
Kustomize generally follows the [Kubernetes Community Membership](https://github.com/kubernetes/community/blob/master/community-membership.md) contributor ladder. Roles are as follows: Kustomize follows the [Kubernetes Community Membership] contributor ladder. Roles are as follows:
1. Contributor: Anyone who actively contributes code, issues or reviews to the project. There are no Kustomize-specific requirements for this status. All contributors must [sign the CLA](https://github.com/kubernetes/community/tree/master/contributors/guide#prerequisites). 1. Contributor: Anyone who actively contributes code, issues or reviews to the project. All contributors must sign the [Contributor License Agreement].
1. Member/Reviewer: All Kubernetes-SIGs org members have LGTM rights on the Kustomize repo. There are no Kustomize-specific requirements. Kustomize does not currently have any formal reviewers, but the role will be created if there is interest. 1. Reviewer: Contributors with a history of review and authorship on Kustomize. Has LGTM rights on the Kustomize repo (as do all kubernetes-sigs org members). Active contributors are encouraged to join the reviewers list to be automatically pinged on PRs.
1. Maintainer/Approver: Highly experienced active reviewer and contributor to Kustomize. Has both LTGM and approval rights on the Kustomize repo, as well as [Github "maintain" rights](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level). 1. Approver: Highly experienced active reviewer and contributor to Kustomize. Has both LTGM and approval rights on the Kustomize repo, as well as "maintain" [Github permissions].
1. Admin/Owner: Maintainer who sets technical direction and makes or approves design decisions for the project. Has LGTM and approval rights on the Kustomize repo as well as [Github "admin" rights](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level). 1. Owner: Approver who sets technical direction and makes or approves design decisions for the project. Has LGTM and approval rights on the Kustomize repo as well as "admin" [Github permissions].
The kyaml module within the Kustomize repo has additional owners following the same ladder.
Administrative notes: Administrative notes:
- Maintainers and admins must be added to the appropriate list both [in the Kustomize repo](https://github.com/kubernetes-sigs/kustomize/blob/8049f7b1af52e8a7ec26faf6cf714f560d0043c5/OWNERS_ALIASES) and [in the community repo](https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml). If this isn't done, the individual in question will lack either PR approval rights (Kustomize list) or the appropriate Github repository permissions (community list).
- The spec for the OWNERS file is [in the community repo](https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md). - The [OWNERS file spec] is a useful resources in making changes.
- Maintainers and admins must be added to the appropriate lists in both [Kustomize OWNERS_ALIASES] and [SIG-CLI Teams]. If this isn't done, the individual in question will lack either PR approval rights (Kustomize list) or the appropriate Github repository permissions (community list).
## Contact Information ## Contact Information
- [Slack channel](https://kubernetes.slack.com/messages/sig-cli) - [Slack channel]
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-cli) - [Mailing list]

View File

@@ -4,12 +4,15 @@
# Makefile for kustomize CLI and API. # Makefile for kustomize CLI and API.
SHELL := /usr/bin/env bash SHELL := /usr/bin/env bash
GOOS = $(shell go env GOOS)
GOARCH = $(shell go env GOARCH)
MYGOBIN = $(shell go env GOBIN) MYGOBIN = $(shell go env GOBIN)
ifeq ($(MYGOBIN),) ifeq ($(MYGOBIN),)
MYGOBIN = $(shell go env GOPATH)/bin MYGOBIN = $(shell go env GOPATH)/bin
endif endif
export PATH := $(MYGOBIN):$(PATH) export PATH := $(MYGOBIN):$(PATH)
MODULES := '"cmd/config" "api/" "kustomize/" "kyaml/"' MODULES := '"cmd/config" "api/" "kustomize/" "kyaml/"'
LATEST_V4_RELEASE=v4.3.0
# Provide defaults for REPO_OWNER and REPO_NAME if not present. # Provide defaults for REPO_OWNER and REPO_NAME if not present.
# Typically these values would be provided by Prow. # Typically these values would be provided by Prow.
@@ -29,7 +32,7 @@ verify-kustomize: \
lint-kustomize \ lint-kustomize \
test-unit-kustomize-all \ test-unit-kustomize-all \
test-examples-kustomize-against-HEAD \ test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-4.1 test-examples-kustomize-against-v4-release
# The following target referenced by a file in # The following target referenced by a file in
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize # https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
@@ -42,7 +45,7 @@ prow-presubmit-check: \
test-unit-cmd-all \ test-unit-cmd-all \
test-go-mod \ test-go-mod \
test-examples-kustomize-against-HEAD \ test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-4.1 test-examples-kustomize-against-v4-release
.PHONY: verify-kustomize-e2e .PHONY: verify-kustomize-e2e
verify-kustomize-e2e: test-examples-e2e-kustomize verify-kustomize-e2e: test-examples-e2e-kustomize
@@ -276,8 +279,8 @@ test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD ./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY: .PHONY:
test-examples-kustomize-against-4.1: $(MYGOBIN)/mdrip test-examples-kustomize-against-v4-release: $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh v4@v4.1.2 ./hack/testExamplesAgainstKustomize.sh v4@$(LATEST_V4_RELEASE)
# linux only. # linux only.
# This is for testing an example plugin that # This is for testing an example plugin that
@@ -289,8 +292,8 @@ $(MYGOBIN)/kubeval:
( \ ( \
set -e; \ set -e; \
d=$(shell mktemp -d); cd $$d; \ d=$(shell mktemp -d); cd $$d; \
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz; \ wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-$(GOOS)-$(GOARCH).tar.gz; \
tar xf kubeval-linux-amd64.tar.gz; \ tar xf kubeval-$(GOOS)-$(GOARCH).tar.gz; \
mv kubeval $(MYGOBIN); \ mv kubeval $(MYGOBIN); \
rm -rf $$d; \ rm -rf $$d; \
) )
@@ -304,10 +307,10 @@ $(MYGOBIN)/helmV2:
( \ ( \
set -e; \ set -e; \
d=$(shell mktemp -d); cd $$d; \ 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; \ wget https://storage.googleapis.com/kubernetes-helm/$$tgzFile; \
tar -xvzf $$tgzFile; \ tar -xvzf $$tgzFile; \
mv linux-amd64/helm $(MYGOBIN)/helmV2; \ mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV2; \
rm -rf $$d \ rm -rf $$d \
) )
@@ -317,10 +320,10 @@ $(MYGOBIN)/helmV3:
( \ ( \
set -e; \ set -e; \
d=$(shell mktemp -d); cd $$d; \ d=$(shell mktemp -d); cd $$d; \
tgzFile=helm-v3.5.3-linux-amd64.tar.gz; \ tgzFile=helm-v3.6.3-$(GOOS)-$(GOARCH).tar.gz; \
wget https://get.helm.sh/$$tgzFile; \ wget https://get.helm.sh/$$tgzFile; \
tar -xvzf $$tgzFile; \ tar -xvzf $$tgzFile; \
mv linux-amd64/helm $(MYGOBIN)/helmV3; \ mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \
rm -rf $$d \ rm -rf $$d \
) )
@@ -328,7 +331,7 @@ $(MYGOBIN)/kind:
( \ ( \
set -e; \ set -e; \
d=$(shell mktemp -d); cd $$d; \ 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; \ chmod +x ./kind; \
mv ./kind $(MYGOBIN); \ mv ./kind $(MYGOBIN); \
rm -rf $$d; \ rm -rf $$d; \
@@ -339,10 +342,10 @@ $(MYGOBIN)/gh:
( \ ( \
set -e; \ set -e; \
d=$(shell mktemp -d); cd $$d; \ 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; \ wget https://github.com/cli/cli/releases/download/v1.0.0/$$tgzFile; \
tar -xvzf $$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 \ rm -rf $$d \
) )

6
OWNERS
View File

@@ -1,4 +1,6 @@
# See https://github.com/kubernetes/community/blob/master/community-membership.md # See https://github.com/kubernetes/community/blob/master/community-membership.md
approvers: approvers:
- kustomize-admins - kustomize-approvers
- kustomize-maintainers
reviewers:
- kustomize-reviewers

View File

@@ -1,16 +1,30 @@
# Keep *-admins and *-maintainers list in sync with corresponding lists in # Keep *-owners and *-approvers lists in sync with *-admins and *-maintainers in
# https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml # https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml
aliases: aliases:
kustomize-admins: kustomize-owners:
- knverey - knverey
- monopole - monopole
- pwittrock - pwittrock
kustomize-maintainers: kustomize-approvers:
- justinsb - justinsb
- mortent - knverey
- monopole
- natasha41575 - natasha41575
- phanimarupaka - pwittrock
- Shell32-Natsu kustomize-reviewers:
emeritus-maintainers: - knverey
- liujingfang1 - monopole
- natasha41575
kyaml-approvers:
- mengqiy - mengqiy
- mortent
- phanimarupaka
kyaml-reviewers:
- mengqiy
- mortent
- phanimarupaka
emeritus-approvers:
- liujingfang1
- Shell32-Natsu

View File

@@ -32,6 +32,7 @@ will be reflected in the Kubernetes release notes.
| < v1.14 | n/a | | < v1.14 | n/a |
| v1.14-v1.20 | v2.0.3 | | v1.14-v1.20 | v2.0.3 |
| v1.21 | v4.0.5 | | v1.21 | v4.0.5 |
| v1.22 | v4.2.0 |
[v2.0.3]: /../../tree/v2.0.3 [v2.0.3]: /../../tree/v2.0.3
[#2506]: https://github.com/kubernetes-sigs/kustomize/issues/2506 [#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 [`make`]: https://www.gnu.org/software/make
[`sed`]: https://www.gnu.org/software/sed [`sed`]: https://www.gnu.org/software/sed
[DAM]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#declarative-application-management [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 [Kubernetes Code of Conduct]: code-of-conduct.md
[applied]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#apply [applied]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#apply
[base]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#base [base]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#base

View File

@@ -56,6 +56,7 @@ func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
// sanity check. // sanity check.
return nil, err return nil, err
} }
f.NameFieldToUpdate.Gvk = f.Referrer.GetGvk()
if err := node.PipeE(fieldspec.Filter{ if err := node.PipeE(fieldspec.Filter{
FieldSpec: f.NameFieldToUpdate, FieldSpec: f.NameFieldToUpdate,
SetValue: f.set, SetValue: f.set,

View File

@@ -250,6 +250,7 @@ metadata:
name: dep name: dep
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
internal.config.kubernetes.io/index: '0'
data: data:
slice: slice:
- false - false
@@ -276,6 +277,7 @@ metadata:
name: dep name: dep
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
internal.config.kubernetes.io/index: '0'
data: data:
1: str 1: str
: invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`, : invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`,

View File

@@ -131,10 +131,11 @@ func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, err
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !rn.IsNilOrEmpty() { if rn.IsNilOrEmpty() {
return getRefinedValue(r.Source.Options, rn) return nil, fmt.Errorf("fieldPath `%s` is missing for replacement source %s", r.Source.FieldPath, r.Source)
} }
return rn, nil
return getRefinedValue(r.Source.Options, rn)
} }
func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode, error) { func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode, error) {

View File

@@ -1514,9 +1514,9 @@ kind: Deployment
metadata: metadata:
name: pre-deploy name: pre-deploy
annotations: annotations:
config.kubernetes.io/previousNames: deploy,deploy internal.config.kubernetes.io/previousNames: deploy,deploy
config.kubernetes.io/previousKinds: CronJob,Deployment internal.config.kubernetes.io/previousKinds: CronJob,Deployment
config.kubernetes.io/previousNamespaces: default,default internal.config.kubernetes.io/previousNamespaces: default,default
spec: spec:
template: template:
spec: spec:
@@ -1543,9 +1543,9 @@ kind: Deployment
metadata: metadata:
name: pre-deploy name: pre-deploy
annotations: annotations:
config.kubernetes.io/previousNames: deploy,deploy internal.config.kubernetes.io/previousNames: deploy,deploy
config.kubernetes.io/previousKinds: CronJob,Deployment internal.config.kubernetes.io/previousKinds: CronJob,Deployment
config.kubernetes.io/previousNamespaces: default,default internal.config.kubernetes.io/previousNamespaces: default,default
spec: spec:
template: template:
spec: spec:
@@ -1556,6 +1556,38 @@ spec:
name: postgresdb name: postgresdb
`, `,
}, },
"replacement source.fieldPath does not exist": {
input: `apiVersion: v1
kind: ConfigMap
metadata:
name: ports-from
data:
grpcPort: 8080
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ports-to
data:
grpcPort: 8081
`,
replacements: `replacements:
- source:
kind: ConfigMap
name: ports-from
fieldPath: data.httpPort
targets:
- select:
kind: ConfigMap
name: ports-to
fieldPaths:
- data.grpcPort
options:
create: true
`,
expectedErr: "fieldPath `data.httpPort` is missing for replacement source ~G_~V_ConfigMap|~X|ports-from:data.httpPort",
},
} }
for tn, tc := range testCases { for tn, tc := range testCases {

View File

@@ -11,6 +11,6 @@ require (
github.com/stretchr/testify v1.5.1 github.com/stretchr/testify v1.5.1
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
sigs.k8s.io/kustomize/kyaml v0.11.0 sigs.k8s.io/kustomize/kyaml v0.12.0
sigs.k8s.io/yaml v1.2.0 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/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 h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= 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.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 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 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -11,6 +11,7 @@ import (
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/kyaml/resid"
) )
type nameReferenceTransformer struct { type nameReferenceTransformer struct {
@@ -109,11 +110,18 @@ func debug(fMap filterMap) {
// 'spec/scaleTargetRef/name' field. Return a filter that can do that. // 'spec/scaleTargetRef/name' field. Return a filter that can do that.
func (t *nameReferenceTransformer) determineFilters( func (t *nameReferenceTransformer) determineFilters(
resources []*resource.Resource) (fMap filterMap) { resources []*resource.Resource) (fMap filterMap) {
// We cache the resource OrgId values because they don't change and otherwise are very visible in a memory pprof
resourceOrgIds := make([]resid.ResId, len(resources))
for i, resource := range resources {
resourceOrgIds[i] = resource.OrgId()
}
fMap = make(filterMap) fMap = make(filterMap)
for _, backReference := range t.backRefs { for _, backReference := range t.backRefs {
for _, referrerSpec := range backReference.Referrers { for _, referrerSpec := range backReference.Referrers {
for _, res := range resources { for i, res := range resources {
if res.OrgId().IsSelected(&referrerSpec.Gvk) { if resourceOrgIds[i].IsSelected(&referrerSpec.Gvk) {
// If this is true, the res might be a referrer, and if // If this is true, the res might be a referrer, and if
// so, the name reference it holds might need an update. // so, the name reference it holds might need an update.
if resHasField(res, referrerSpec.Path) { if resHasField(res, referrerSpec.Path) {

View File

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

View File

@@ -168,3 +168,23 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
return ra.Transform( return ra.Transform(
newNameReferenceTransformer(ra.tConfig.NameReference)) newNameReferenceTransformer(ra.tConfig.NameReference))
} }
// Intersection drops the resources which "other" does not have.
func (ra *ResAccumulator) Intersection(other resmap.ResMap) error {
for _, curId := range ra.resMap.AllIds() {
toDelete := true
for _, otherId := range other.AllIds() {
if otherId == curId {
toDelete = false
break
}
}
if toDelete {
err := ra.resMap.Remove(curId)
if err != nil {
return err
}
}
}
return nil
}

View File

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

View File

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

View File

@@ -182,6 +182,12 @@ func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) {
absPath: notCloned.String(), absPath: notCloned.String(),
ref: "", 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 { for tn, tc := range testcases {
t.Run(tn, func(t *testing.T) { t.Run(tn, func(t *testing.T) {

View File

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

View File

@@ -47,6 +47,11 @@ func (l *Loader) Config() *types.PluginConfig {
return l.pc 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( func (l *Loader) LoadGenerators(
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) { ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
var result []resmap.Generator 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 { if err := r.SetAnnotations(annotations); err != nil {
return nil, err return nil, err
} }
r.SetOptions(types.NewGenArgs( if needsHash {
&types.GeneratorArgs{ r.EnableHashSuffix()
Behavior: behavior, }
Options: &types.GeneratorOptions{DisableNameSuffixHash: !needsHash}})) r.SetBehavior(types.NewGenerationBehavior(behavior))
} }
return rm, nil return rm, nil
} }

View File

@@ -10,14 +10,17 @@ import (
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/builtins" "sigs.k8s.io/kustomize/api/builtins"
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/accumulator" "sigs.k8s.io/kustomize/api/internal/accumulator"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers" "sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/internal/plugins/loader" "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/konfig"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/openapi" "sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
@@ -38,11 +41,13 @@ func NewKustTarget(
validator ifc.Validator, validator ifc.Validator,
rFactory *resmap.Factory, rFactory *resmap.Factory,
pLdr *loader.Loader) *KustTarget { pLdr *loader.Loader) *KustTarget {
pLdrCopy := *pLdr
pLdrCopy.SetWorkDir(ldr.Root())
return &KustTarget{ return &KustTarget{
ldr: ldr, ldr: ldr,
validator: validator, validator: validator,
rFactory: rFactory, rFactory: rFactory,
pLdr: pLdr, pLdr: &pLdrCopy,
} }
} }
@@ -108,7 +113,7 @@ func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
} }
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 { if err != nil {
return nil, err return nil, err
} }
@@ -151,20 +156,29 @@ func (kt *KustTarget) addHashesToNames(
// holding customized resources and the data/rules used // holding customized resources and the data/rules used
// to do so. The name back references and vars are // to do so. The name back references and vars are
// not yet fixed. // 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) { 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 // 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). // (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) { 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 { if err != nil {
return nil, errors.Wrap(err, "accumulating resources") 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 { if err != nil {
return nil, errors.Wrap(err, "accumulating components") return nil, errors.Wrap(err, "accumulating components")
} }
@@ -205,9 +219,26 @@ func (kt *KustTarget) accumulateTarget(ra *accumulator.ResAccumulator) (
return nil, errors.Wrapf( return nil, errors.Wrapf(
err, "merging vars %v", kt.kustomization.Vars) err, "merging vars %v", kt.kustomization.Vars)
} }
err = kt.IgnoreLocal(ra)
if err != nil {
return nil, err
}
return ra, nil return ra, nil
} }
// IgnoreLocal drops the local resource by checking the annotation "config.kubernetes.io/local-config".
func (kt *KustTarget) IgnoreLocal(ra *accumulator.ResAccumulator) error {
rf := kt.rFactory.RF()
if rf.IncludeLocalConfigs {
return nil
}
remainRes, err := rf.DropLocalNodes(ra.ResMap().ToRNodeSlice())
if err != nil {
return err
}
return ra.Intersection(kt.rFactory.FromResourceSlice(remainRes))
}
func (kt *KustTarget) runGenerators( func (kt *KustTarget) runGenerators(
ra *accumulator.ResAccumulator) error { ra *accumulator.ResAccumulator) error {
var generators []resmap.Generator var generators []resmap.Generator
@@ -247,7 +278,7 @@ func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error)
} }
ra.AppendAll(rm) ra.AppendAll(rm)
} }
ra, err := kt.accumulateResources(ra, generatorPaths) ra, err := kt.accumulateResources(ra, generatorPaths, &resource.Origin{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -283,8 +314,7 @@ func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]re
} }
ra.AppendAll(rm) ra.AppendAll(rm)
} }
ra, err := kt.accumulateResources(ra, transformerPaths) ra, err := kt.accumulateResources(ra, transformerPaths, &resource.Origin{})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -332,16 +362,16 @@ func (kt *KustTarget) removeValidatedByLabel(rm resmap.ResMap) error {
// accumulateResources fills the given resourceAccumulator // accumulateResources fills the given resourceAccumulator
// with resources read from the given list of paths. // with resources read from the given list of paths.
func (kt *KustTarget) accumulateResources( func (kt *KustTarget) accumulateResources(
ra *accumulator.ResAccumulator, paths []string) (*accumulator.ResAccumulator, error) { ra *accumulator.ResAccumulator, paths []string, origin *resource.Origin) (*accumulator.ResAccumulator, error) {
for _, path := range paths { for _, path := range paths {
// try loading resource as file then as base (directory or git repository) // 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) ldr, err := kt.ldr.New(path)
if err != nil { if err != nil {
return nil, errors.Wrapf( return nil, errors.Wrapf(
err, "accumulation err='%s'", errF.Error()) 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 { if err != nil {
return nil, errors.Wrapf( return nil, errors.Wrapf(
err, "accumulation err='%s'", errF.Error()) err, "accumulation err='%s'", errF.Error())
@@ -354,7 +384,7 @@ func (kt *KustTarget) accumulateResources(
// accumulateResources fills the given resourceAccumulator // accumulateResources fills the given resourceAccumulator
// with resources read from the given list of paths. // with resources read from the given list of paths.
func (kt *KustTarget) accumulateComponents( 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 { for _, path := range paths {
// Components always refer to directories // Components always refer to directories
ldr, errL := kt.ldr.New(path) ldr, errL := kt.ldr.New(path)
@@ -362,7 +392,8 @@ func (kt *KustTarget) accumulateComponents(
return nil, fmt.Errorf("loader.New %q", errL) return nil, fmt.Errorf("loader.New %q", errL)
} }
var errD error 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 { if errD != nil {
return nil, fmt.Errorf("accumulateDirectory: %q", errD) return nil, fmt.Errorf("accumulateDirectory: %q", errD)
} }
@@ -371,7 +402,7 @@ func (kt *KustTarget) accumulateComponents(
} }
func (kt *KustTarget) accumulateDirectory( 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() defer ldr.Cleanup()
subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr) subKt := NewKustTarget(ldr, kt.validator, kt.rFactory, kt.pLdr)
err := subKt.Load() err := subKt.Load()
@@ -379,6 +410,7 @@ func (kt *KustTarget) accumulateDirectory(
return nil, errors.Wrapf( return nil, errors.Wrapf(
err, "couldn't make target for path '%s'", ldr.Root()) err, "couldn't make target for path '%s'", ldr.Root())
} }
subKt.kustomization.BuildMetadata = kt.kustomization.BuildMetadata
var bytes []byte var bytes []byte
path := ldr.Root() path := ldr.Root()
if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists { if openApiPath, exists := subKt.Kustomization().OpenAPI["path"]; exists {
@@ -402,12 +434,12 @@ func (kt *KustTarget) accumulateDirectory(
var subRa *accumulator.ResAccumulator var subRa *accumulator.ResAccumulator
if isComponent { if isComponent {
// Components don't create a new accumulator: the kustomization directives are added to the current accumulator // 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() ra = accumulator.MakeEmptyAccumulator()
} else { } else {
// Child Kustomizations create a new accumulator which resolves their kustomization directives, which will later // Child Kustomizations create a new accumulator which resolves their kustomization directives, which will later
// be merged into the current accumulator. // be merged into the current accumulator.
subRa, err = subKt.AccumulateTarget() subRa, err = subKt.AccumulateTarget(origin)
} }
if err != nil { if err != nil {
return nil, errors.Wrapf( return nil, errors.Wrapf(
@@ -422,11 +454,18 @@ func (kt *KustTarget) accumulateDirectory(
} }
func (kt *KustTarget) accumulateFile( 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) resources, err := kt.rFactory.FromFile(kt.ldr, path)
if err != nil { if err != nil {
return errors.Wrapf(err, "accumulating resources from '%s'", path) 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) err = ra.AppendAll(resources)
if err != nil { if err != nil {
return errors.Wrapf(err, "merging resources from '%s'", path) return errors.Wrapf(err, "merging resources from '%s'", path)

View File

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

View File

@@ -8,6 +8,7 @@ import (
"strings" "strings"
"testing" "testing"
"sigs.k8s.io/kustomize/api/resource"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/resid" "sigs.k8s.io/kustomize/kyaml/resid"
@@ -65,7 +66,7 @@ vars:
apiVersion: v300 apiVersion: v300
`) `)
ra, err := makeAndLoadKustTarget( ra, err := makeAndLoadKustTarget(
t, th.GetFSys(), "/app").AccumulateTarget() t, th.GetFSys(), "/app").AccumulateTarget(&resource.Origin{})
if err != nil { if err != nil {
t.Fatalf("Err: %v", err) t.Fatalf("Err: %v", err)
} }
@@ -120,7 +121,7 @@ resources:
`) `)
ra, err := makeAndLoadKustTarget( ra, err := makeAndLoadKustTarget(
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget() t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget(&resource.Origin{})
if err != nil { if err != nil {
t.Fatalf("Err: %v", err) t.Fatalf("Err: %v", err)
} }
@@ -177,7 +178,7 @@ resources:
- ../o1 - ../o1
`) `)
_, err := makeAndLoadKustTarget( _, err := makeAndLoadKustTarget(
t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget() t, th.GetFSys(), "/app/overlays/o2").AccumulateTarget(&resource.Origin{})
if err == nil { if err == nil {
t.Fatalf("expected var collision") 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" "fmt"
"strings" "strings"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/kyaml/resid" "sigs.k8s.io/kustomize/kyaml/resid"
"sigs.k8s.io/kustomize/kyaml/yaml" "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 // MakeResIds returns all of an RNode's current and previous Ids
func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) { func MakeResIds(n *yaml.RNode) ([]resid.ResId, error) {
var result []resid.ResId var result []resid.ResId

View File

@@ -31,11 +31,12 @@ const (
// A program name, for use in help, finding the XDG_CONFIG_DIR, etc. // A program name, for use in help, finding the XDG_CONFIG_DIR, etc.
ProgramName = "kustomize" ProgramName = "kustomize"
// ConfigAnnoDomain is configuration-related annotation namespace. // ConfigAnnoDomain is internal configuration-related annotation namespace.
ConfigAnnoDomain = "config.kubernetes.io" // 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. // 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 // Label key that indicates the resources are built from Kustomize
ManagedbyLabelKey = "app.kubernetes.io/managed-by" 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 package krusty_test
import ( import (
"os"
"os/exec" "os/exec"
"path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
"sigs.k8s.io/kustomize/kyaml/filesys"
) )
func TestFnExecGenerator(t *testing.T) { const generateDeploymentDotSh = `#!/bin/sh
// Function plugins should not need the env setup done by MakeEnhancedHarness
th := kusttest_test.MakeHarness(t)
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: resources:
- short_secret.yaml - short_secret.yaml
generators: generators:
@@ -19,7 +54,8 @@ generators:
`) `)
// Create some additional resource just to make sure everything is added // 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 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
@@ -32,22 +68,25 @@ stringData:
bootcmd: bootcmd:
- mkdir /mnt/vda - 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 kind: executable
metadata: metadata:
name: demo name: demo
annotations: annotations:
config.kubernetes.io/function: | config.kubernetes.io/function: |
exec: exec:
path: ./fnplugin_test/fnexectest.sh path: ./generateDeployment.sh
spec: spec:
`) `)
o := th.MakeOptionsPluginsEnabled()
o.PluginConfig.FnpLoadingOptions.EnableExec = true m := th.Run(tmpDir.String(), o)
m := th.Run(".", o) assert.NoError(t, err)
th.AssertActualEqualsExpected(m, ` yml, err := m.AsYaml()
apiVersion: v1 assert.NoError(t, err)
assert.Equal(t, `apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
labels: labels:
@@ -63,7 +102,6 @@ apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
config.kubernetes.io/path: deployment_nginx.yaml
tshirt-size: small tshirt-size: small
labels: labels:
app: nginx app: nginx
@@ -80,7 +118,99 @@ spec:
containers: containers:
- image: nginx - image: nginx
name: 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) { func skipIfNoDocker(t *testing.T) {
@@ -146,8 +276,6 @@ type: Opaque
apiVersion: policy/v1beta1 apiVersion: policy/v1beta1
kind: PodDisruptionBudget kind: PodDisruptionBudget
metadata: metadata:
annotations:
config.kubernetes.io/path: config/demo-budget_poddisruptionbudget.yaml
labels: labels:
app: cockroachdb app: cockroachdb
name: demo name: demo
@@ -162,8 +290,6 @@ spec:
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
annotations:
config.kubernetes.io/path: config/demo-public_service.yaml
labels: labels:
app: cockroachdb app: cockroachdb
name: demo name: demo
@@ -184,7 +310,6 @@ apiVersion: v1
kind: Service kind: Service
metadata: metadata:
annotations: annotations:
config.kubernetes.io/path: config/demo_service.yaml
prometheus.io/path: _status/vars prometheus.io/path: _status/vars
prometheus.io/port: "8080" prometheus.io/port: "8080"
prometheus.io/scrape: "true" prometheus.io/scrape: "true"
@@ -209,8 +334,6 @@ spec:
apiVersion: apps/v1 apiVersion: apps/v1
kind: StatefulSet kind: StatefulSet
metadata: metadata:
annotations:
config.kubernetes.io/path: config/demo_statefulset.yaml
labels: labels:
app: cockroachdb app: cockroachdb
name: demo name: demo
@@ -383,7 +506,6 @@ apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
config.kubernetes.io/path: deployment_nginx.yaml
tshirt-size: small tshirt-size: small
labels: labels:
app: nginx app: nginx
@@ -450,8 +572,6 @@ data:
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
annotations:
config.kubernetes.io/path: namespace_my-namespace.yaml
labels: labels:
my-ns-name: function-test my-ns-name: function-test
name: my-namespace name: my-namespace
@@ -459,8 +579,6 @@ metadata:
apiVersion: v1 apiVersion: v1
kind: Namespace kind: Namespace
metadata: metadata:
annotations:
config.kubernetes.io/path: namespace_another-namespace.yaml
labels: labels:
my-ns-name: function-test my-ns-name: function-test
name: another-namespace name: another-namespace
@@ -508,8 +626,6 @@ data:
value: value value: value
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations:
config.kubernetes.io/path: configmap_env.yaml
name: env 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

@@ -685,3 +685,167 @@ spec:
name: new-name name: new-name
`) `)
} }
func TestNameReferenceAfterJsonPatch(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("resources.yaml", `
apiVersion: v1
data:
bar: bar
kind: ConfigMap
metadata:
name: cm
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: foo
spec:
selector:
matchLabels:
foo: foo
template:
metadata:
labels:
foo: foo
spec:
containers:
- name: foo
image: example
volumeMounts:
- mountPath: /path
name: myvol
volumes:
- configMap:
name: cm
name: myvol
`)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namePrefix: foo-
resources:
- resources.yaml
patches:
- target:
group: apps
version: v1
name: foo
patch: |
- op: replace
path: /kind
value: Deployment
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: v1
data:
bar: bar
kind: ConfigMap
metadata:
name: foo-cm
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo-foo
spec:
selector:
matchLabels:
foo: foo
template:
metadata:
labels:
foo: foo
spec:
containers:
- image: example
name: foo
volumeMounts:
- mountPath: /path
name: myvol
volumes:
- configMap:
name: foo-cm
name: myvol
`)
}
func TestNameReferenceAfterJsonPatchConfigMapGenerator(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("statefulset.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: foo
spec:
selector:
matchLabels:
foo: foo
template:
metadata:
labels:
foo: foo
spec:
containers:
- name: foo
image: example
volumeMounts:
- mountPath: /path
name: myvol
volumes:
- configMap:
name: cm
name: myvol
`)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- statefulset.yaml
patches:
- target:
group: apps
version: v1
name: foo
patch: |
- op: replace
path: /kind
value: Deployment
configMapGenerator:
- name: cm
literals:
- bar=bar
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
spec:
selector:
matchLabels:
foo: foo
template:
metadata:
labels:
foo: foo
spec:
containers:
- image: example
name: foo
volumeMounts:
- mountPath: /path
name: myvol
volumes:
- configMap:
name: cm-8hm8224gfd
name: myvol
---
apiVersion: v1
data:
bar: bar
kind: ConfigMap
metadata:
name: cm-8hm8224gfd
`)
}

View File

@@ -230,3 +230,92 @@ spec:
name: configmap-in-base 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

@@ -26,7 +26,7 @@ import (
// used instead of performing an exec to a kustomize CLI subprocess. // used instead of performing an exec to a kustomize CLI subprocess.
// To use, load a filesystem with kustomization files (any // To use, load a filesystem with kustomization files (any
// number of overlays and bases), then make a Kustomizer // number of overlays and bases), then make a Kustomizer
// injected with the given fileystem, then call Run. // injected with the given filesystem, then call Run.
type Kustomizer struct { type Kustomizer struct {
options *Options options *Options
depProvider *provider.DepProvider depProvider *provider.DepProvider

View File

@@ -0,0 +1,70 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
// This test checks that if a resource is annotated as "local-config", this resource won't be ignored until
// all transformations have completed. This makes sure the local resource can be used as a transformation input.
// See https://github.com/kubernetes-sigs/kustomize/issues/4124 for details.
func TestSKipLocalConfigAfterTransform(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- pod.yaml
- deployment.yaml
transformers:
- replacement.yaml
`)
th.WriteF("pod.yaml", `apiVersion: v1
kind: Pod
metadata:
name: buildup
annotations:
config.kubernetes.io/local-config: "true"
spec:
containers:
- name: app
image: nginx
`)
th.WriteF("deployment.yaml", `apiVersion: apps/v1
kind: Deployment
metadata:
name: buildup
`)
th.WriteF("replacement.yaml", `
apiVersion: builtin
kind: ReplacementTransformer
metadata:
name: buildup
replacements:
- source:
kind: Pod
fieldPath: spec
targets:
- select:
kind: Deployment
fieldPaths:
- spec.template.spec
options:
create: true
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1
kind: Deployment
metadata:
name: buildup
spec:
template:
spec:
containers:
- image: nginx
name: app
`)
}

View File

@@ -17,6 +17,11 @@ func writeTestSchema(th kusttest_test.Harness, filepath string) {
th.WriteF(filepath+"mycrd_schema.json", string(bytes)) 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) { func writeCustomResource(th kusttest_test.Harness, filepath string) {
th.WriteF(filepath, ` th.WriteF(filepath, `
apiVersion: example.com/v1alpha1 apiVersion: example.com/v1alpha1
@@ -37,6 +42,26 @@ spec:
`) `)
} }
func writeOtherCustomResource(th kusttest_test.Harness, filepath string) {
th.WriteF(filepath, `
apiVersion: v1alpha1
kind: MyCRD
metadata:
name: service
spec:
template:
spec:
containers:
- name: server
image: server
command: example
ports:
- name: grpc
protocol: TCP
containerPort: 8080
`)
}
func writeTestComponentWithCustomSchema(th kusttest_test.Harness) { func writeTestComponentWithCustomSchema(th kusttest_test.Harness) {
writeTestSchema(th, "comp/") writeTestSchema(th, "comp/")
openapi.ResetOpenAPI() openapi.ResetOpenAPI()
@@ -69,6 +94,32 @@ patchesStrategicMerge:
image: nginx image: nginx
` `
const customSchemaPatchMultipleGvks = `
patchesStrategicMerge:
- |-
apiVersion: example.com/v1alpha1
kind: MyCRD
metadata:
name: service
spec:
template:
spec:
containers:
- name: server
image: nginx
- |-
apiVersion: v1alpha1
kind: MyCRD
metadata:
name: service
spec:
template:
spec:
containers:
- name: server
image: nginx
`
const patchedCustomResource = ` const patchedCustomResource = `
apiVersion: example.com/v1alpha1 apiVersion: example.com/v1alpha1
kind: MyCRD kind: MyCRD
@@ -103,6 +154,69 @@ openapi:
th.AssertActualEqualsExpected(m, patchedCustomResource) th.AssertActualEqualsExpected(m, patchedCustomResource)
} }
func TestCustomOpenApiFieldWithTwoGvks(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- mycrd.yaml
- myothercrd.yaml
openapi:
path: mycrd_schema.json
`+customSchemaPatchMultipleGvks)
writeCustomResource(th, "mycrd.yaml")
writeOtherCustomResource(th, "myothercrd.yaml")
writeTestSchema(th, "./")
openapi.ResetOpenAPI()
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: example.com/v1alpha1
kind: MyCRD
metadata:
name: service
spec:
template:
spec:
containers:
- command: example
image: nginx
name: server
ports:
- containerPort: 8080
name: grpc
protocol: TCP
---
apiVersion: v1alpha1
kind: MyCRD
metadata:
name: service
spec:
template:
spec:
containers:
- command: example
image: nginx
name: server
ports:
- containerPort: 8080
name: grpc
protocol: TCP
`)
}
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 // Error if user tries to specify both builtin version
// and custom schema // and custom schema
func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) { func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
@@ -111,7 +225,7 @@ func TestCustomOpenApiFieldBothPathAndVersion(t *testing.T) {
resources: resources:
- mycrd.yaml - mycrd.yaml
openapi: openapi:
version: v1.20.4 version: v1.21.2
path: mycrd_schema.json path: mycrd_schema.json
`+customSchemaPatch) `+customSchemaPatch)
writeCustomResource(th, "mycrd.yaml") writeCustomResource(th, "mycrd.yaml")
@@ -197,7 +311,7 @@ openapi:
resources: resources:
- ../base - ../base
openapi: openapi:
version: v1.20.4 version: v1.21.2
`+customSchemaPatch) `+customSchemaPatch)
writeCustomResource(th, "base/mycrd.yaml") writeCustomResource(th, "base/mycrd.yaml")
writeTestSchema(th, "base/") writeTestSchema(th, "base/")
@@ -215,7 +329,7 @@ spec:
- image: nginx - image: nginx
name: server name: server
`) `)
assert.Equal(t, "v1204", openapi.GetSchemaVersion()) assert.Equal(t, "v1212", openapi.GetSchemaVersion())
} }
func TestCustomOpenAPIFieldFromComponent(t *testing.T) { func TestCustomOpenAPIFieldFromComponent(t *testing.T) {

View File

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

View File

@@ -4,6 +4,7 @@
package krusty_test package krusty_test
import ( import (
"path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -39,3 +40,124 @@ spec:
name: nginx name: nginx
`, string(yml)) `, 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 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
`)
}

View File

@@ -34,6 +34,11 @@
"group": "example.com", "group": "example.com",
"kind": "MyCRD", "kind": "MyCRD",
"version": "v1alpha1" "version": "v1alpha1"
},
{
"group": "",
"kind": "MyCRD",
"version": "v1alpha1"
} }
] ]
}, },

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

@@ -0,0 +1,78 @@
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
- group: ""
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) r, err := rmF.NewResMapFromConfigMapArgs(kvLdr, tc.input)
assert.NoError(t, err, tc.description) assert.NoError(t, err, tc.description)
r.RemoveBuildAnnotations()
rYaml, err := r.AsYaml() rYaml, err := r.AsYaml()
assert.NoError(t, err, tc.description) assert.NoError(t, err, tc.description)
tc.expected.RemoveBuildAnnotations()
expYaml, err := tc.expected.AsYaml() expYaml, err := tc.expected.AsYaml()
assert.NoError(t, err, tc.description) assert.NoError(t, err, tc.description)
assert.Equal(t, expYaml, rYaml) assert.Equal(t, expYaml, rYaml)
@@ -252,6 +254,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
actual.RemoveBuildAnnotations()
actYaml, err := actual.AsYaml() actYaml, err := actual.AsYaml()
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -136,6 +136,10 @@ type ResMap interface {
// self, then its behavior _cannot_ be merge or replace. // self, then its behavior _cannot_ be merge or replace.
AbsorbAll(ResMap) error 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 returns the yaml form of resources.
AsYaml() ([]byte, error) AsYaml() ([]byte, error)
@@ -210,6 +214,35 @@ type ResMap interface {
// namespaces. Cluster wide objects are never excluded. // namespaces. Cluster wide objects are never excluded.
SubsetThatCouldBeReferencedByResource(*resource.Resource) ResMap 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 copies the ResMap and underlying resources.
DeepCopy() ResMap DeepCopy() ResMap

View File

@@ -9,6 +9,7 @@ import (
"reflect" "reflect"
"github.com/pkg/errors" "github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/annotations"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio" "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 // Select returns a list of resources that
// are selected by a Selector // are selected by a Selector
func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) { func (m *resWrangler) Select(s types.Selector) ([]*resource.Resource, error) {
@@ -593,6 +607,16 @@ func (m *resWrangler) ToRNodeSlice() []*kyaml.RNode {
return result 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. // ApplySmPatch applies the patch, and errors on Id collisions.
func (m *resWrangler) ApplySmPatch( func (m *resWrangler) ApplySmPatch(
selectedSet *resource.IdSet, patch *resource.Resource) error { selectedSet *resource.IdSet, patch *resource.Resource) error {

View File

@@ -343,9 +343,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "new-alice", "name": "new-alice",
"annotations": map[string]interface{}{ "annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "ConfigMap", "internal.config.kubernetes.io/previousKinds": "ConfigMap",
"config.kubernetes.io/previousNames": "alice", "internal.config.kubernetes.io/previousNames": "alice",
"config.kubernetes.io/previousNamespaces": "default", "internal.config.kubernetes.io/previousNamespaces": "default",
}, },
}, },
}) })
@@ -356,9 +356,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "new-bob", "name": "new-bob",
"annotations": map[string]interface{}{ "annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap", "internal.config.kubernetes.io/previousKinds": "ConfigMap,ConfigMap",
"config.kubernetes.io/previousNames": "bob,bob2", "internal.config.kubernetes.io/previousNames": "bob,bob2",
"config.kubernetes.io/previousNamespaces": "default,default", "internal.config.kubernetes.io/previousNamespaces": "default,default",
}, },
}, },
}) })
@@ -370,9 +370,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
"name": "new-bob", "name": "new-bob",
"namespace": "new-happy", "namespace": "new-happy",
"annotations": map[string]interface{}{ "annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "ConfigMap", "internal.config.kubernetes.io/previousKinds": "ConfigMap",
"config.kubernetes.io/previousNames": "bob", "internal.config.kubernetes.io/previousNames": "bob",
"config.kubernetes.io/previousNamespaces": "happy", "internal.config.kubernetes.io/previousNamespaces": "happy",
}, },
}, },
}) })
@@ -384,9 +384,9 @@ func TestGetMatchingResourcesByAnyId(t *testing.T) {
"name": "charlie", "name": "charlie",
"namespace": "happy", "namespace": "happy",
"annotations": map[string]interface{}{ "annotations": map[string]interface{}{
"config.kubernetes.io/previousKinds": "ConfigMap", "internal.config.kubernetes.io/previousKinds": "ConfigMap",
"config.kubernetes.io/previousNames": "charlie", "internal.config.kubernetes.io/previousNames": "charlie",
"config.kubernetes.io/previousNamespaces": "default", "internal.config.kubernetes.io/previousNamespaces": "default",
}, },
}, },
}) })
@@ -845,6 +845,8 @@ func TestAbsorbAll(t *testing.T) {
})) }))
w := makeMap1() w := makeMap1()
assert.NoError(t, w.AbsorbAll(makeMap2(types.BehaviorMerge))) assert.NoError(t, w.AbsorbAll(makeMap2(types.BehaviorMerge)))
expected.RemoveBuildAnnotations()
w.RemoveBuildAnnotations()
assert.NoError(t, expected.ErrorIfNotEqualLists(w)) assert.NoError(t, expected.ErrorIfNotEqualLists(w))
w = makeMap1() w = makeMap1()
assert.NoError(t, w.AbsorbAll(nil)) assert.NoError(t, w.AbsorbAll(nil))
@@ -853,6 +855,7 @@ func TestAbsorbAll(t *testing.T) {
w = makeMap1() w = makeMap1()
w2 := makeMap2(types.BehaviorReplace) w2 := makeMap2(types.BehaviorReplace)
assert.NoError(t, w.AbsorbAll(w2)) assert.NoError(t, w.AbsorbAll(w2))
w2.RemoveBuildAnnotations()
assert.NoError(t, w2.ErrorIfNotEqualLists(w)) assert.NoError(t, w2.ErrorIfNotEqualLists(w))
w = makeMap1() w = makeMap1()
w2 = makeMap2(types.BehaviorUnspecified) 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) { func TestApplySmPatch_General(t *testing.T) {
const ( const (
myDeployment = "Deployment" myDeployment = "Deployment"

View File

@@ -64,18 +64,23 @@ func (rf *Factory) FromMapAndOption(
// TODO: return err instead of log. // TODO: return err instead of log.
log.Fatal(err) log.Fatal(err)
} }
return rf.makeOne(n, types.NewGenArgs(args)) return rf.makeOne(n, args)
} }
// makeOne returns a new instance of Resource. // 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 { if rn == nil {
log.Fatal("RNode must not be null") log.Fatal("RNode must not be null")
} }
if o == nil { resource := &Resource{RNode: *rn}
o = types.NewGenArgs(nil) 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 // SliceFromPatches returns a slice of resources given a patch path
@@ -119,14 +124,34 @@ func (rf *Factory) SliceFromBytes(in []byte) ([]*Resource, error) {
return rf.resourcesFromRNodes(nodes), nil return rf.resourcesFromRNodes(nodes), nil
} }
// DropLocalNodes removes the local nodes by default. Local nodes are detected via the annotation `config.kubernetes.io/local-config: "true"`
func (rf *Factory) DropLocalNodes(nodes []*yaml.RNode) ([]*Resource, error) {
var result []*yaml.RNode
for _, node := range nodes {
if node.IsNilOrEmpty() {
continue
}
md, err := node.GetValidatedMetadata()
if err != nil {
return nil, err
}
if rf.IncludeLocalConfigs {
result = append(result, node)
continue
}
localConfig, exist := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
if !exist || localConfig == "false" {
result = append(result, node)
}
}
return rf.resourcesFromRNodes(result), nil
}
// ResourcesFromRNodes converts RNodes to Resources. // ResourcesFromRNodes converts RNodes to Resources.
func (rf *Factory) ResourcesFromRNodes( func (rf *Factory) ResourcesFromRNodes(
nodes []*yaml.RNode) (result []*Resource, err error) { nodes []*yaml.RNode) (result []*Resource, err error) {
nodes, err = rf.dropBadNodes(nodes) return rf.DropLocalNodes(nodes)
if err != nil {
return nil, err
}
return rf.resourcesFromRNodes(nodes), nil
} }
// resourcesFromRNode assumes all nodes are good. // resourcesFromRNode assumes all nodes are good.
@@ -138,7 +163,7 @@ func (rf *Factory) resourcesFromRNodes(
return return
} }
func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) { func (rf *Factory) RNodesFromBytes(b []byte) ([]*yaml.RNode, error) {
nodes, err := kio.FromBytes(b) nodes, err := kio.FromBytes(b)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -147,9 +172,17 @@ func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return rf.inlineAnyEmbeddedLists(nodes)
}
// inlineAnyEmbeddedLists scans the RNode slice for nodes named FooList.
// Such nodes are expected to be lists of resources, each of type Foo.
// These lists are replaced in the result by their inlined resources.
func (rf *Factory) inlineAnyEmbeddedLists(
nodes []*yaml.RNode) (result []*yaml.RNode, err error) {
var n0 *yaml.RNode
for len(nodes) > 0 { for len(nodes) > 0 {
n0 := nodes[0] n0, nodes = nodes[0], nodes[1:]
nodes = nodes[1:]
kind := n0.GetKind() kind := n0.GetKind()
if !strings.HasSuffix(kind, "List") { if !strings.HasSuffix(kind, "List") {
result = append(result, n0) result = append(result, n0)
@@ -159,7 +192,7 @@ func (rf *Factory) RNodesFromBytes(b []byte) (result []*yaml.RNode, err error) {
var m map[string]interface{} var m map[string]interface{}
m, err = n0.Map() m, err = n0.Map()
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("trouble expanding list of %s; %w", kind, err)
} }
items, ok := m["items"] items, ok := m["items"]
if !ok { if !ok {
@@ -211,38 +244,20 @@ func (rf *Factory) convertObjectSliceToNodeSlice(
func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (rf *Factory) dropBadNodes(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
var result []*yaml.RNode var result []*yaml.RNode
for _, n := range nodes { for _, n := range nodes {
ignore, err := rf.shouldIgnore(n) if n.IsNilOrEmpty() {
if err != nil { continue
}
if _, err := n.GetValidatedMetadata(); err != nil {
return nil, err return nil, err
} }
if !ignore { if foundNil, path := n.HasNilEntryInList(); foundNil {
result = append(result, n) return nil, fmt.Errorf("empty item at %v in object %v", path, n)
} }
result = append(result, n)
} }
return result, nil return result, nil
} }
// shouldIgnore returns true if there's some reason to ignore the node.
func (rf *Factory) shouldIgnore(n *yaml.RNode) (bool, error) {
if n.IsNilOrEmpty() {
return true, nil
}
if !rf.IncludeLocalConfigs {
md, err := n.GetValidatedMetadata()
if err != nil {
return true, err
}
_, ignore := md.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
if ignore {
return true, nil
}
}
if foundNil, path := n.HasNilEntryInList(); foundNil {
return true, fmt.Errorf("empty item at %v in object %v", path, n)
}
return false, nil
}
// SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original // SliceFromBytesWithNames unmarshals bytes into a Resource slice with specified original
// name. // name.
func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) { func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resource, error) {
@@ -265,7 +280,7 @@ func (rf *Factory) MakeConfigMap(kvLdr ifc.KvLoader, args *types.ConfigMapArgs)
if err != nil { if err != nil {
return nil, err 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 // MakeSecret makes an instance of Resource for Secret
@@ -274,5 +289,5 @@ func (rf *Factory) MakeSecret(kvLdr ifc.KvLoader, args *types.SecretArgs) (*Reso
if err != nil { if err != nil {
return nil, err return nil, err
} }
return rf.makeOne(rn, types.NewGenArgs(&args.GeneratorArgs)), nil return rf.makeOne(rn, &args.GeneratorArgs), nil
} }

View File

@@ -5,7 +5,7 @@ package resource_test
import ( import (
"fmt" "fmt"
"reflect" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -13,9 +13,10 @@ import (
. "sigs.k8s.io/kustomize/api/resource" . "sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filesys" "sigs.k8s.io/kustomize/kyaml/filesys"
"sigs.k8s.io/kustomize/kyaml/kio"
) )
func TestSliceFromBytes(t *testing.T) { func TestRNodesFromBytes(t *testing.T) {
type testCase struct { type testCase struct {
input string input string
expected []string expected []string
@@ -399,60 +400,11 @@ binaryData:
} }
} }
func TestSliceFromBytesMore(t *testing.T) { func TestMoreRNodesFromBytes(t *testing.T) {
testConfigMap :=
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "winnie",
},
}
testDeploymentSpec := map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"hostAliases": []interface{}{
map[string]interface{}{
"hostnames": []interface{}{
"a.example.com",
},
"ip": "8.8.8.8",
},
},
},
},
}
testDeploymentA := map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deployment-a",
},
"spec": testDeploymentSpec,
}
testDeploymentB := map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deployment-b",
},
"spec": testDeploymentSpec,
}
testDeploymentList :=
map[string]interface{}{
"apiVersion": "v1",
"kind": "DeploymentList",
"items": []interface{}{
testDeploymentA,
testDeploymentB,
},
}
type expected struct { type expected struct {
out []map[string]interface{} out []string
isErr bool isErr bool
} }
testCases := map[string]struct { testCases := map[string]struct {
input []byte input []byte
exp expected exp expected
@@ -465,16 +417,16 @@ func TestSliceFromBytesMore(t *testing.T) {
}, },
"noBytes": { "noBytes": {
input: []byte{}, input: []byte{},
exp: expected{ exp: expected{},
out: []map[string]interface{}{},
},
}, },
"goodJson": { "goodJson": {
input: []byte(` input: []byte(`
{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie"}} {"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie"}}
`), `),
exp: expected{ exp: expected{
out: []map[string]interface{}{testConfigMap}, out: []string{
`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`,
},
}, },
}, },
"goodYaml1": { "goodYaml1": {
@@ -485,7 +437,12 @@ metadata:
name: winnie name: winnie
`), `),
exp: expected{ exp: expected{
out: []map[string]interface{}{testConfigMap}, out: []string{`
apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
`},
}, },
}, },
"goodYaml2": { "goodYaml2": {
@@ -501,26 +458,17 @@ metadata:
name: winnie name: winnie
`), `),
exp: expected{ exp: expected{
out: []map[string]interface{}{testConfigMap, testConfigMap}, out: []string{`
},
},
"localConfigYaml": {
input: []byte(`
apiVersion: v1
kind: ConfigMap
metadata:
name: winnie-skip
annotations:
# this annotation causes the Resource to be ignored by kustomize
config.kubernetes.io/local-config: ""
---
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: winnie name: winnie
`), `, `
exp: expected{ apiVersion: v1
out: []map[string]interface{}{testConfigMap}, kind: ConfigMap
metadata:
name: winnie
`},
}, },
}, },
"garbageInOneOfTwoObjects": { "garbageInOneOfTwoObjects": {
@@ -545,7 +493,7 @@ WOOOOOOOOOOOOOOOOOOOOOOOOT: woot
`), `),
exp: expected{ exp: expected{
out: []map[string]interface{}{}, out: []string{},
}, },
}, },
"Missing .metadata.name in object": { "Missing .metadata.name in object": {
@@ -591,9 +539,18 @@ items:
name: winnie name: winnie
`), `),
exp: expected{ exp: expected{
out: []map[string]interface{}{ out: []string{`
testConfigMap, apiVersion: v1
testConfigMap}, kind: ConfigMap
metadata:
name: winnie
`, `
apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
`,
},
}, },
}, },
"ConfigMapList": { "ConfigMapList": {
@@ -611,9 +568,9 @@ items:
name: winnie name: winnie
`), `),
exp: expected{ exp: expected{
out: []map[string]interface{}{ out: []string{
testConfigMap, `{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`,
testConfigMap, `{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "winnie"}}`,
}, },
}, },
}, },
@@ -626,7 +583,7 @@ items:
kind: Deployment kind: Deployment
metadata: metadata:
name: deployment-a name: deployment-a
spec: &hostAliases spec: &foo
template: template:
spec: spec:
hostAliases: hostAliases:
@@ -638,23 +595,39 @@ items:
metadata: metadata:
name: deployment-b name: deployment-b
spec: spec:
<<: *hostAliases *foo
`), `),
exp: expected{ exp: expected{
// TODO(3271): This should work. out: []string{
// https://github.com/kubernetes-sigs/kustomize/issues/3271 `{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "deployment-a"}, ` +
// json.Marshal(obj) fails on the 2nd list item. `"spec": {"template": {"spec": {"hostAliases": [{"hostnames": ["a.example.com"], "ip": "8.8.8.8"}]}}}}`,
// The value of the 1st list item's first spec field is `{"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "deployment-b"}, ` +
// map[string]interface{} `"spec": {"template": {"spec": {"hostAliases": [{"hostnames": ["a.example.com"], "ip": "8.8.8.8"}]}}}}`},
// The value of the 2nd list item's first spec field is },
// map[interface{}]interface{} },
// which causes a encoding/json.UnsupportedTypeError. "simpleAnchor": {
isErr: true, input: []byte(`
out: []map[string]interface{}{testDeploymentList}, apiVersion: v1
kind: ConfigMap
metadata:
name: wildcard
data:
color: &color-used blue
feeling: *color-used
`),
exp: expected{
out: []string{`
apiVersion: v1
kind: ConfigMap
metadata:
name: wildcard
data:
color: blue
feeling: blue
`},
}, },
}, },
} }
for n := range testCases { for n := range testCases {
tc := testCases[n] tc := testCases[n]
t.Run(n, func(t *testing.T) { t.Run(n, func(t *testing.T) {
@@ -666,15 +639,100 @@ items:
assert.False(t, tc.exp.isErr) assert.False(t, tc.exp.isErr)
assert.Equal(t, len(tc.exp.out), len(rs)) assert.Equal(t, len(tc.exp.out), len(rs))
for i := range rs { for i := range rs {
rsMap, err := rs[i].Map() actual, err := rs[i].String()
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal( assert.Equal(
t, fmt.Sprintf("%v", tc.exp.out[i]), fmt.Sprintf("%v", rsMap)) t, strings.TrimSpace(tc.exp.out[i]), strings.TrimSpace(actual))
m, _ := rs[i].Map() }
if !reflect.DeepEqual(tc.exp.out[i], m) { })
t.Fatalf("%s:\nexpected: %v\n actual: %v", }
n, tc.exp.out[i], m) }
}
func TestDropLocalNodes(t *testing.T) {
testCases := map[string]struct {
input []byte
expected []byte
}{
"localConfigUnset": {
input: []byte(`apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
`),
expected: []byte(`apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
`),
},
"localConfigSet": {
input: []byte(`apiVersion: v1
kind: ConfigMap
metadata:
name: winnie-skip
annotations:
# this annotation causes the Resource to be ignored by kustomize
config.kubernetes.io/local-config: ""
`),
expected: nil,
},
"localConfigSetToTrue": {
input: []byte(`apiVersion: v1
kind: ConfigMap
metadata:
name: winnie-skip
annotations:
config.kubernetes.io/local-config: "true"
`),
expected: nil,
},
"localConfigSetToFalse": {
input: []byte(`apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
annotations:
config.kubernetes.io/local-config: "false"
`),
expected: []byte(`apiVersion: v1
kind: ConfigMap
metadata:
annotations:
config.kubernetes.io/local-config: "false"
name: winnie
`),
},
"localConfigMultiInput": {
input: []byte(`apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
---
apiVersion: v1
kind: ConfigMap
metadata:
name: winnie-skip
annotations:
config.kubernetes.io/local-config: "true"
`),
expected: []byte(`apiVersion: v1
kind: ConfigMap
metadata:
name: winnie
`),
},
}
for n := range testCases {
tc := testCases[n]
t.Run(n, func(t *testing.T) {
nin, _ := kio.FromBytes(tc.input)
res, err := factory.DropLocalNodes(nin)
assert.NoError(t, err)
if tc.expected == nil {
assert.Equal(t, 0, len(res))
} else {
actual, _ := res[0].AsYAML()
assert.Equal(t, tc.expected, actual)
} }
}) })
} }

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/internal/utils"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/resid" "sigs.k8s.io/kustomize/kyaml/resid"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml" kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
@@ -22,8 +23,6 @@ import (
// paired with metadata used by kustomize. // paired with metadata used by kustomize.
type Resource struct { type Resource struct {
kyaml.RNode kyaml.RNode
options *types.GenArgs
refBy []resid.ResId
refVarNames []string refVarNames []string
} }
@@ -35,6 +34,16 @@ var BuildAnnotations = []string{
utils.BuildAnnotationPreviousNamespaces, utils.BuildAnnotationPreviousNamespaces,
utils.BuildAnnotationAllowNameChange, utils.BuildAnnotationAllowNameChange,
utils.BuildAnnotationAllowKindChange, utils.BuildAnnotationAllowKindChange,
utils.BuildAnnotationsRefBy,
utils.BuildAnnotationsGenBehavior,
utils.BuildAnnotationsGenAddHashSuffix,
kioutil.PathAnnotation,
kioutil.IndexAnnotation,
kioutil.SeqIndentAnnotation,
kioutil.LegacyPathAnnotation,
kioutil.LegacyIndexAnnotation,
} }
func (r *Resource) ResetRNode(incoming *Resource) { func (r *Resource) ResetRNode(incoming *Resource) {
@@ -80,6 +89,8 @@ func (r *Resource) DeepCopy() *Resource {
// CopyMergeMetaDataFieldsFrom copies everything but the non-metadata in // CopyMergeMetaDataFieldsFrom copies everything but the non-metadata in
// the resource. // the resource.
// TODO: move to RNode, use GetMeta to improve performance. // 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. // Must remove the kustomize bit at the end.
func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error { func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
if err := r.SetLabels( if err := r.SetLabels(
@@ -87,7 +98,7 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
return fmt.Errorf("copyMerge cannot set labels - %w", err) return fmt.Errorf("copyMerge cannot set labels - %w", err)
} }
if err := r.SetAnnotations( 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) return fmt.Errorf("copyMerge cannot set annotations - %w", err)
} }
if err := r.SetName(other.GetName()); err != nil { if err := r.SetName(other.GetName()); err != nil {
@@ -101,8 +112,6 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) error {
} }
func (r *Resource) copyKustomizeSpecificFields(other *Resource) { func (r *Resource) copyKustomizeSpecificFields(other *Resource) {
r.options = other.options
r.refBy = other.copyRefBy()
r.refVarNames = copyStringSlice(other.refVarNames) r.refVarNames = copyStringSlice(other.refVarNames)
} }
@@ -144,10 +153,10 @@ func (r *Resource) ErrIfNotEquals(o *Resource) error {
func (r *Resource) ReferencesEqual(other *Resource) bool { func (r *Resource) ReferencesEqual(other *Resource) bool {
setSelf := make(map[resid.ResId]bool) setSelf := make(map[resid.ResId]bool)
setOther := make(map[resid.ResId]bool) setOther := make(map[resid.ResId]bool)
for _, ref := range other.refBy { for _, ref := range other.GetRefBy() {
setOther[ref] = true setOther[ref] = true
} }
for _, ref := range r.refBy { for _, ref := range r.GetRefBy() {
if _, ok := setOther[ref]; !ok { if _, ok := setOther[ref]; !ok {
return false return false
} }
@@ -156,15 +165,6 @@ func (r *Resource) ReferencesEqual(other *Resource) bool {
return len(setSelf) == len(setOther) 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 { func copyStringSlice(s []string) []string {
if s == nil { if s == nil {
return nil return nil
@@ -250,41 +250,45 @@ func (r *Resource) setPreviousId(ns string, n string, k string) *Resource {
// AllowNameChange allows name changes to the resource. // AllowNameChange allows name changes to the resource.
func (r *Resource) AllowNameChange() { func (r *Resource) AllowNameChange() {
annotations := r.GetAnnotations() r.enable(utils.BuildAnnotationAllowNameChange)
annotations[utils.BuildAnnotationAllowNameChange] = utils.Allowed
if err := r.SetAnnotations(annotations); err != nil {
panic(err)
}
} }
// NameChangeAllowed checks if a patch resource is allowed to change another resource's name.
func (r *Resource) NameChangeAllowed() bool { func (r *Resource) NameChangeAllowed() bool {
annotations := r.GetAnnotations() return r.isEnabled(utils.BuildAnnotationAllowNameChange)
v, ok := annotations[utils.BuildAnnotationAllowNameChange]
return ok && v == utils.Allowed
} }
// AllowKindChange allows kind changes to the resource. // AllowKindChange allows kind changes to the resource.
func (r *Resource) AllowKindChange() { 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 := 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 { if err := r.SetAnnotations(annotations); err != nil {
panic(err) 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. // String returns resource as JSON.
func (r *Resource) String() string { func (r *Resource) String() string {
bs, err := r.MarshalJSON() bs, err := r.MarshalJSON()
if err != nil { if err != nil {
return "<" + err.Error() + ">" return "<" + err.Error() + ">"
} }
return strings.TrimSpace(string(bs)) + r.options.String() return strings.TrimSpace(string(bs))
} }
// AsYAML returns the resource in Yaml form. // AsYAML returns the resource in Yaml form.
@@ -306,20 +310,34 @@ func (r *Resource) MustYaml() string {
return string(yml) 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. // Behavior returns the behavior for the resource.
func (r *Resource) Behavior() types.GenerationBehavior { 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 // NeedHashSuffix returns true if a resource content
// hash should be appended to the name of the resource. // hash should be appended to the name of the resource.
func (r *Resource) NeedHashSuffix() bool { 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. // OrgId returns the original, immutable ResId for the resource.
@@ -363,12 +381,18 @@ func (r *Resource) CurId() resid.ResId {
// GetRefBy returns the ResIds that referred to current resource // GetRefBy returns the ResIds that referred to current resource
func (r *Resource) GetRefBy() []resid.ResId { 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 // AppendRefBy appends a ResId into the refBy list
func (r *Resource) AppendRefBy(id resid.ResId) { // Using any type except fmt.Stringer here results in a compilation error
r.refBy = append(r.refBy, id) func (r *Resource) AppendRefBy(id fmt.Stringer) {
r.appendCsvAnnotation(utils.BuildAnnotationsRefBy, id.String())
} }
// GetRefVarNames returns vars that refer to current resource // GetRefVarNames returns vars that refer to current resource
@@ -424,3 +448,17 @@ func mergeStringMaps(maps ...map[string]string) map[string]string {
} }
return result 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 //nolint:gosec
const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}` const configMapAsString = `{"apiVersion":"v1","kind":"ConfigMap","metadata":{"name":"winnie","namespace":"hundred-acre-wood"}}`
@@ -66,17 +64,15 @@ func TestResourceString(t *testing.T) {
}{ }{
{ {
in: testConfigMap, in: testConfigMap,
s: configMapAsString + genArgOptions, s: configMapAsString,
}, },
{ {
in: testDeployment, in: testDeployment,
s: deploymentAsString + genArgOptions, s: deploymentAsString,
}, },
} }
for _, test := range tests { for _, test := range tests {
if test.in.String() != test.s { assert.Equal(t, test.in.String(), test.s)
t.Fatalf("Expected %s == %s", test.in.String(), test.s)
}
} }
} }
@@ -284,6 +280,266 @@ spec:
`, string(bytes)) `, 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) { func TestMergeDataMapFrom(t *testing.T) {
resource, err := factory.FromBytes([]byte(` resource, err := factory.FromBytes([]byte(`
apiVersion: v1 apiVersion: v1
@@ -717,9 +973,9 @@ metadata:
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/previousKinds: Secret internal.config.kubernetes.io/previousKinds: Secret
config.kubernetes.io/previousNames: oldName internal.config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default internal.config.kubernetes.io/previousNamespaces: default
name: newName name: newName
`, `,
}, },
@@ -729,9 +985,9 @@ metadata:
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/previousKinds: Secret internal.config.kubernetes.io/previousKinds: Secret
config.kubernetes.io/previousNames: oldName internal.config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default internal.config.kubernetes.io/previousNamespaces: default
name: oldName2 name: oldName2
`, `,
newName: "newName", newName: "newName",
@@ -740,9 +996,9 @@ metadata:
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/previousKinds: Secret,Secret internal.config.kubernetes.io/previousKinds: Secret,Secret
config.kubernetes.io/previousNames: oldName,oldName2 internal.config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,default internal.config.kubernetes.io/previousNamespaces: default,default
name: newName name: newName
`, `,
}, },
@@ -752,9 +1008,9 @@ metadata:
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/previousKinds: Secret internal.config.kubernetes.io/previousKinds: Secret
config.kubernetes.io/previousNames: oldName internal.config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default internal.config.kubernetes.io/previousNamespaces: default
name: oldName2 name: oldName2
namespace: oldNamespace namespace: oldNamespace
`, `,
@@ -764,9 +1020,9 @@ metadata:
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/previousKinds: Secret,Secret internal.config.kubernetes.io/previousKinds: Secret,Secret
config.kubernetes.io/previousNames: oldName,oldName2 internal.config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,oldNamespace internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
name: newName name: newName
namespace: newNamespace namespace: newNamespace
`, `,
@@ -814,9 +1070,9 @@ metadata:
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/previousKinds: Secret internal.config.kubernetes.io/previousKinds: Secret
config.kubernetes.io/previousNames: oldName internal.config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default internal.config.kubernetes.io/previousNamespaces: default
name: newName name: newName
`, `,
expected: []resid.ResId{ expected: []resid.ResId{
@@ -833,9 +1089,9 @@ metadata:
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/previousKinds: Secret,Secret internal.config.kubernetes.io/previousKinds: Secret,Secret
config.kubernetes.io/previousNames: oldName,oldName2 internal.config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,oldNamespace internal.config.kubernetes.io/previousNamespaces: default,oldNamespace
name: newName name: newName
namespace: newNamespace namespace: newNamespace
`, `,
@@ -1133,3 +1389,41 @@ spec:
t.Fatalf("expected '%s', got '%s'", expected, actual) 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( func (th *HarnessEnhanced) LoadAndRunGenerator(
config string) resmap.ResMap {
rm := th.LoadAndRunGeneratorWithBuildAnnotations(config)
rm.RemoveBuildAnnotations()
return rm
}
func (th *HarnessEnhanced) LoadAndRunGeneratorWithBuildAnnotations(
config string) resmap.ResMap { config string) resmap.ResMap {
res, err := th.rf.RF().FromBytes([]byte(config)) res, err := th.rf.RF().FromBytes([]byte(config))
if err != nil { if err != nil {
@@ -162,7 +169,6 @@ func (th *HarnessEnhanced) LoadAndRunGenerator(
if err != nil { if err != nil {
th.t.Fatalf("generate err: %v", err) th.t.Fatalf("generate err: %v", err)
} }
rm.RemoveBuildAnnotations()
return rm 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 // Inventory appends an object that contains the record
// of all other objects, which can be used in apply, prune and delete // of all other objects, which can be used in apply, prune and delete
Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"` Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory,omitempty"`
// BuildMetadata is a list of strings used to toggle different build options
BuildMetadata []string `json:"buildMetadata,omitempty" yaml:"buildMetadata,omitempty"`
} }
// FixKustomizationPostUnmarshalling fixes things // FixKustomizationPostUnmarshalling fixes things

View File

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

6
cmd/config/OWNERS Normal file
View File

@@ -0,0 +1,6 @@
# See https://github.com/kubernetes/community/blob/master/community-membership.md
approvers:
- kyaml-approvers
reviewers:
- kyaml-reviewers

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 This allows for human authoring of configuration to coexist with changes made by
functions. functions.
### Annotations ### Internal Annotations
The orchestrator annotates resources in the wire format with annotation prefix For orchestration purposes, the orchestrator will use a set of annotations,
`config.kubernetes.io`. These annotations are not persisted when the referred to as _internal annotations_, on resources in `Resources.items`. These
orchestrator writes the resources to the filesystem. The orchestrator sets this annotations are not persisted to resource manifests on the filesystem: The
annotation when reading files from the local filesystem and removes the orchestrator sets this annotation when reading files from the local filesystem
annotation when writing the output of functions back to the 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 Annotation prefix `internal.config.kubernetes.io` is reserved for use for
explicitly listed below. 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 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 path is relative to a fix location on the filesystem. Different orchestrator
implementations can choose different fixed points. implementations can choose different fixed points.
A function SHOULD NOT modify this annotation. A function SHOULD NOT modify these annotations.
Example: Example:
```yaml ```yaml
metadata: metadata:
annotations: 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 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 are separated by three dashes (`---`), and the index represents the position of
the Resource starting from zero. When this annotation is not specified, it the Resource starting from zero. When this annotation is not specified, it
implies a value of `0`. implies a value of `0`.
A function SHOULD NOT modify this annotation. A function SHOULD NOT modify these annotations.
Example: Example:
```yaml ```yaml
metadata: metadata:
annotations: annotations:
config.kubernetes.io/path: "relative/file/path.yaml" internal.config.kubernetes.io/path: "relative/file/path.yaml"
config.kubernetes.io/index: 2 internal.config.kubernetes.io/index: 2
``` ```
This represents the third resource in the file. 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/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/inf.v0 v0.9.1 gopkg.in/inf.v0 v0.9.1
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e
sigs.k8s.io/kustomize/kyaml v0.11.0 sigs.k8s.io/kustomize/kyaml v0.12.0
) )

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/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 h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= 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.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 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= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

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

View File

@@ -164,6 +164,8 @@ metadata:
a: 'b' a: 'b'
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
replicas: 1 replicas: 1
--- ---
@@ -175,6 +177,8 @@ metadata:
a: 'b' a: 'b'
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -191,6 +195,8 @@ metadata:
a: 'b' a: 'b'
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: bar namespace: bar
spec: spec:
replicas: 3 replicas: 3
@@ -206,6 +212,8 @@ metadata:
a: 'b' a: 'b'
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: foo namespace: foo
spec: spec:
replicas: 3 replicas: 3
@@ -222,6 +230,8 @@ metadata:
c: 'd' c: 'd'
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
replicas: 1 replicas: 1
--- ---
@@ -234,6 +244,8 @@ metadata:
c: 'd' c: 'd'
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -251,6 +263,8 @@ metadata:
c: 'd' c: 'd'
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: bar namespace: bar
spec: spec:
replicas: 3 replicas: 3
@@ -267,6 +281,8 @@ metadata:
c: 'd' c: 'd'
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: foo namespace: foo
spec: spec:
replicas: 3 replicas: 3
@@ -281,6 +297,8 @@ metadata:
app: nginx2 app: nginx2
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
replicas: 1 replicas: 1
--- ---
@@ -292,6 +310,8 @@ metadata:
a: 'b' a: 'b'
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -307,6 +327,8 @@ metadata:
config.kubernetes.io/local-config: "true" config.kubernetes.io/local-config: "true"
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: bar namespace: bar
spec: spec:
replicas: 3 replicas: 3
@@ -321,6 +343,8 @@ metadata:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: foo namespace: foo
spec: spec:
replicas: 3 replicas: 3
@@ -335,6 +359,8 @@ metadata:
app: nginx2 app: nginx2
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
replicas: 1 replicas: 1
--- ---
@@ -345,6 +371,8 @@ metadata:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -361,6 +389,8 @@ metadata:
a: 'b' a: 'b'
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: bar namespace: bar
spec: spec:
replicas: 3 replicas: 3
@@ -375,6 +405,8 @@ metadata:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: foo namespace: foo
spec: spec:
replicas: 3 replicas: 3
@@ -389,6 +421,8 @@ metadata:
app: nginx2 app: nginx2
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
replicas: 1 replicas: 1
--- ---
@@ -399,6 +433,8 @@ metadata:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -414,6 +450,8 @@ metadata:
config.kubernetes.io/local-config: "true" config.kubernetes.io/local-config: "true"
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: bar namespace: bar
spec: spec:
replicas: 3 replicas: 3
@@ -429,6 +467,8 @@ metadata:
a: 'b' a: 'b'
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: foo namespace: foo
spec: spec:
replicas: 3 replicas: 3
@@ -443,6 +483,8 @@ metadata:
app: nginx2 app: nginx2
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
replicas: 1 replicas: 1
--- ---
@@ -453,6 +495,8 @@ metadata:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -469,6 +513,8 @@ metadata:
a: 'b' a: 'b'
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: bar namespace: bar
spec: spec:
replicas: 3 replicas: 3
@@ -483,6 +529,8 @@ metadata:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f2.yaml'
namespace: foo namespace: foo
spec: spec:
replicas: 3 replicas: 3
@@ -559,7 +607,7 @@ added annotations in the package
expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1) expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1) expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) { if !assert.Contains(t, actualNormalized, expectedNormalized) {
t.FailNow() t.FailNow()
} }
}) })

View File

@@ -17,6 +17,7 @@ import (
"sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/filters" "sigs.k8s.io/kustomize/kyaml/kio/filters"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
@@ -182,7 +183,7 @@ func (r *CatRunner) out(w io.Writer) ([]kio.Writer, error) {
// remove this annotation explicitly, the ByteWriter won't clear it by // remove this annotation explicitly, the ByteWriter won't clear it by
// default because it doesn't set it // default because it doesn't set it
clear := []string{"config.kubernetes.io/path"} clear := []string{kioutil.LegacyPathAnnotation, kioutil.PathAnnotation}
if r.KeepAnnotations { if r.KeepAnnotations {
clear = nil clear = nil
} }

View File

@@ -32,6 +32,8 @@ func NewCreateSetterRunner(parent string) *CreateSetterRunner {
Example: commands.CreateSetterExamples, Example: commands.CreateSetterExamples,
PreRunE: r.preRunE, PreRunE: r.preRunE,
RunE: r.runE, 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", "", 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 '-'") "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) expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -1) expectedNormalized := strings.Replace(expected, "\\", "/", -1)
if !assert.Equal(t, expectedNormalized, actualNormalized) { if !assert.Contains(t, actualNormalized, expectedNormalized) {
t.FailNow() t.FailNow()
} }
}) })

View File

@@ -23,6 +23,8 @@ func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
PreRun: r.preRun, PreRun: r.preRun,
RunE: r.runE, 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", "", cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
"name of the field to set -- e.g. --field image") "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) expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -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() t.FailNow()
} }
}) })

View File

@@ -26,6 +26,8 @@ func GetInitRunner(name string) *InitRunner {
Long: commands.InitLong, Long: commands.InitLong,
Example: commands.InitExamples, Example: commands.InitExamples,
RunE: r.runE, 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) runner.FixDocs(name, c)
r.Command = c r.Command = c

View File

@@ -31,6 +31,8 @@ func NewListSettersRunner(parent string) *ListSettersRunner {
Example: commands.ListSettersExamples, Example: commands.ListSettersExamples,
PreRunE: r.preRunE, PreRunE: r.preRunE,
RunE: r.runE, 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, c.Flags().BoolVar(&r.Markdown, "markdown", false,
"output as github markdown") "output as github markdown")

View File

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

View File

@@ -27,6 +27,8 @@ func NewSetRunner(parent string) *SetRunner {
Example: commands.SetExamples, Example: commands.SetExamples,
PreRunE: r.preRunE, PreRunE: r.preRunE,
RunE: r.runE, 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) runner.FixDocs(parent, c)
r.Command = c r.Command = c

View File

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

View File

@@ -5,6 +5,7 @@ package commands
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
@@ -47,6 +48,7 @@ Environment Variables:
`, `,
RunE: r.runE, RunE: r.runE,
PreRunE: r.preRunE,
SilenceUsage: true, SilenceUsage: true,
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true}, FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
@@ -78,6 +80,12 @@ func WrapCommand() *cobra.Command {
return GetWrapRunner().Command return GetWrapRunner().Command
} }
func (r *WrapRunner) preRunE(_ *cobra.Command, _ []string) error {
_, err := fmt.Fprintln(os.Stderr, `Command "wrap" is deprecated, this will no longer be available in kustomize v5.
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
return err
}
func (r *WrapRunner) runE(c *cobra.Command, args []string) error { func (r *WrapRunner) runE(c *cobra.Command, args []string) error {
if r.getEnv == nil { if r.getEnv == nil {
r.getEnv = os.Getenv r.getEnv = os.Getenv

View File

@@ -81,6 +81,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_deployment.yaml' config.kubernetes.io/path: 'config/test_deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
spec: spec:
replicas: 11 replicas: 11
selector: selector:
@@ -112,6 +114,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_service.yaml' config.kubernetes.io/path: 'config/test_service.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/test_service.yaml'
spec: spec:
selector: selector:
name: test name: test
@@ -136,6 +140,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_deployment.yaml' config.kubernetes.io/path: 'config/test_deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
spec: spec:
replicas: 11 replicas: 11
selector: selector:
@@ -164,6 +170,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_service.yaml' config.kubernetes.io/path: 'config/test_service.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/test_service.yaml'
spec: spec:
selector: selector:
name: test name: test
@@ -186,6 +194,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml' config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/mysql-deployment_deployment.yaml'
spec: spec:
replicas: 3 replicas: 3
template: template:
@@ -201,6 +211,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml' config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/nosetters-deployment_deployment.yaml'
spec: spec:
replicas: 4 replicas: 4
template: template:
@@ -216,6 +228,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml' config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/storage-deployment_deployment.yaml'
spec: spec:
replicas: 4 replicas: 4
template: template:
@@ -233,6 +247,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_deployment.yaml' config.kubernetes.io/path: 'config/test_deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/test_deployment.yaml'
spec: spec:
replicas: 11 replicas: 11
selector: selector:
@@ -264,6 +280,8 @@ items:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'config/test_service.yaml' config.kubernetes.io/path: 'config/test_service.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'config/test_service.yaml'
spec: spec:
selector: selector:
name: test name: test

View File

@@ -60,6 +60,7 @@ $ kyaml cat pkg/ --function-config config.yaml --wrap-kind ResourceList | kyaml
`, `,
RunE: r.runE, RunE: r.runE,
PreRunE: r.preRunE,
SilenceUsage: true, SilenceUsage: true,
FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true}, FParseErrWhitelist: cobra.FParseErrWhitelist{UnknownFlags: true},
Args: cobra.MinimumNArgs(1), Args: cobra.MinimumNArgs(1),
@@ -84,6 +85,12 @@ func XArgsCommand() *cobra.Command {
return GetXArgsRunner().Command return GetXArgsRunner().Command
} }
func (r *XArgsRunner) preRunE(_ *cobra.Command, _ []string) error {
_, err := fmt.Fprintln(os.Stderr, `Command "xargs" is deprecated, this will no longer be available in kustomize v5.
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
return err
}
func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error { func (r *XArgsRunner) runE(c *cobra.Command, _ []string) error {
if len(r.Args) == 0 { if len(r.Args) == 0 {
r.Args = os.Args r.Args = os.Args

View File

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

View File

@@ -25,6 +25,8 @@ func GetFmtRunner(name string) *FmtRunner {
Example: commands.FmtExamples, Example: commands.FmtExamples,
RunE: r.runE, RunE: r.runE,
PreRunE: r.preRunE, 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) runner.FixDocs(name, c)
c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern, c.Flags().StringVar(&r.FilenamePattern, "pattern", filters.DefaultFilenamePattern,

View File

@@ -78,7 +78,7 @@ func TestFmtCommand_stdin(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
// verify the output // 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 // 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) expected := strings.Replace(test.expected, "${baseDir}", baseDir, -1)
expectedNormalized := strings.Replace(expected, "\\", "/", -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() t.FailNow()
} }
}) })

View File

@@ -79,6 +79,8 @@ metadata:
app: nginx2 app: nginx2
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
replicas: 1 replicas: 1
--- ---
@@ -89,6 +91,8 @@ metadata:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -146,6 +150,7 @@ metadata:
annotations: annotations:
app: nginx2 app: nginx2
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
internal.config.kubernetes.io/index: '0'
spec: spec:
replicas: 1 replicas: 1
--- ---
@@ -155,6 +160,7 @@ metadata:
annotations: annotations:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
internal.config.kubernetes.io/index: '1'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -295,6 +301,8 @@ metadata:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'deployment.yaml' config.kubernetes.io/path: 'deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'deployment.yaml'
spec: spec:
replicas: 3 replicas: 3
template: template:
@@ -314,6 +322,8 @@ metadata:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'deployment.yaml' config.kubernetes.io/path: 'deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'deployment.yaml'
spec: spec:
replicas: 4 replicas: 4
template: template:
@@ -339,6 +349,8 @@ metadata:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'deployment.yaml' config.kubernetes.io/path: 'deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'deployment.yaml'
spec: spec:
replicas: 3 replicas: 3
template: template:
@@ -364,6 +376,8 @@ metadata:
annotations: annotations:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'deployment.yaml' config.kubernetes.io/path: 'deployment.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'deployment.yaml'
spec: spec:
replicas: 4 replicas: 4
template: template:

View File

@@ -43,7 +43,9 @@ kind: Krmfile
t.FailNow() 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() t.FailNow()
} }
} }
@@ -78,7 +80,9 @@ kind: Krmfile
t.FailNow() 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() t.FailNow()
} }
} }

View File

@@ -19,6 +19,8 @@ func GetMergeRunner(name string) *MergeRunner {
Long: commands.MergeLong, Long: commands.MergeLong,
Example: commands.MergeExamples, Example: commands.MergeExamples,
RunE: r.runE, 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) runner.FixDocs(name, c)
r.Command = c r.Command = c

View File

@@ -18,6 +18,8 @@ func GetMerge3Runner(name string) *Merge3Runner {
Long: commands.Merge3Long, Long: commands.Merge3Long,
Example: commands.Merge3Examples, Example: commands.Merge3Examples,
RunE: r.runE, 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) runner.FixDocs(name, c)
c.Flags().StringVar(&r.ancestor, "ancestor", "", c.Flags().StringVar(&r.ancestor, "ancestor", "",

View File

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

View File

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

View File

@@ -4,6 +4,9 @@
package commands package commands
import ( import (
"fmt"
"os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/cmd/config/runner" "sigs.k8s.io/kustomize/cmd/config/runner"
@@ -20,6 +23,7 @@ func GetSinkRunner(name string) *SinkRunner {
Long: commands.SinkLong, Long: commands.SinkLong,
Example: commands.SinkExamples, Example: commands.SinkExamples,
RunE: r.runE, RunE: r.runE,
PreRunE: r.preRunE,
Args: cobra.MaximumNArgs(1), Args: cobra.MaximumNArgs(1),
} }
runner.FixDocs(name, c) runner.FixDocs(name, c)
@@ -36,6 +40,12 @@ type SinkRunner struct {
Command *cobra.Command Command *cobra.Command
} }
func (r *SinkRunner) preRunE(c *cobra.Command, args []string) error {
_, err := fmt.Fprintln(os.Stderr, `Command "sink" is deprecated, this will no longer be available in kustomize v5.
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
return err
}
func (r *SinkRunner) runE(c *cobra.Command, args []string) error { func (r *SinkRunner) runE(c *cobra.Command, args []string) error {
var outputs []kio.Writer var outputs []kio.Writer
if len(args) == 1 { if len(args) == 1 {
@@ -43,7 +53,7 @@ func (r *SinkRunner) runE(c *cobra.Command, args []string) error {
} else { } else {
outputs = []kio.Writer{&kio.ByteWriter{ outputs = []kio.Writer{&kio.ByteWriter{
Writer: c.OutOrStdout(), Writer: c.OutOrStdout(),
ClearAnnotations: []string{kioutil.PathAnnotation}}, ClearAnnotations: []string{kioutil.PathAnnotation, kioutil.LegacyPathAnnotation}},
} }
} }

View File

@@ -5,6 +5,7 @@ package commands
import ( import (
"fmt" "fmt"
"os"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands" "sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
@@ -22,6 +23,7 @@ func GetSourceRunner(name string) *SourceRunner {
Long: commands.SourceLong, Long: commands.SourceLong,
Example: commands.SourceExamples, Example: commands.SourceExamples,
RunE: r.runE, RunE: r.runE,
PreRunE: r.preRunE,
} }
runner.FixDocs(name, c) runner.FixDocs(name, c)
c.Flags().StringVar(&r.WrapKind, "wrap-kind", kio.ResourceListKind, c.Flags().StringVar(&r.WrapKind, "wrap-kind", kio.ResourceListKind,
@@ -47,6 +49,12 @@ type SourceRunner struct {
Command *cobra.Command Command *cobra.Command
} }
func (r *SourceRunner) preRunE(c *cobra.Command, args []string) error {
_, err := fmt.Fprintln(os.Stderr, `Command "source" is deprecated, this will no longer be available in kustomize v5.
See discussion in https://github.com/kubernetes-sigs/kustomize/issues/3953.`)
return err
}
func (r *SourceRunner) runE(c *cobra.Command, args []string) error { func (r *SourceRunner) runE(c *cobra.Command, args []string) error {
// if there is a function-config specified, emit it // if there is a function-config specified, emit it
var functionConfig *yaml.RNode var functionConfig *yaml.RNode

View File

@@ -93,6 +93,8 @@ items:
app: nginx2 app: nginx2
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
replicas: 1 replicas: 1
- kind: Service - kind: Service
@@ -102,6 +104,8 @@ items:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f1.yaml' config.kubernetes.io/path: 'f1.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f1.yaml'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -116,6 +120,8 @@ items:
config.kubernetes.io/local-config: "true" config.kubernetes.io/local-config: "true"
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '0'
internal.config.kubernetes.io/path: 'f2.yaml'
spec: spec:
replicas: 3 replicas: 3
- apiVersion: apps/v1 - apiVersion: apps/v1
@@ -128,6 +134,8 @@ items:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'f2.yaml' config.kubernetes.io/path: 'f2.yaml'
internal.config.kubernetes.io/index: '1'
internal.config.kubernetes.io/path: 'f2.yaml'
spec: spec:
replicas: 3 replicas: 3
`, b.String()) { `, b.String()) {
@@ -194,8 +202,8 @@ func TestSourceCommandJSON(t *testing.T) {
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1 if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList kind: ResourceList
items: items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}} - {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f1.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f1.json'}}, "spec": {"replicas": 1}}
- {"apiVersion": "v1", "kind": "Abstraction", "metadata": {"name": "foo", "annotations": {"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n", "config.kubernetes.io/local-config": "true", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f2.json'}}, "spec": {"replicas": 3}} - {"apiVersion": "v1", "kind": "Abstraction", "metadata": {"name": "foo", "annotations": {"config.kubernetes.io/function": "container:\n image: gcr.io/example/reconciler:v1\n", "config.kubernetes.io/local-config": "true", config.kubernetes.io/index: '0', config.kubernetes.io/path: 'f2.json', internal.config.kubernetes.io/index: '0', internal.config.kubernetes.io/path: 'f2.json'}}, "spec": {"replicas": 3}}
`, b.String()) { `, b.String()) {
return return
} }
@@ -249,6 +257,7 @@ items:
annotations: annotations:
app: nginx2 app: nginx2
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
internal.config.kubernetes.io/index: '0'
spec: spec:
replicas: 1 replicas: 1
- kind: Service - kind: Service
@@ -257,6 +266,7 @@ items:
annotations: annotations:
app: nginx app: nginx
config.kubernetes.io/index: '1' config.kubernetes.io/index: '1'
internal.config.kubernetes.io/index: '1'
spec: spec:
selector: selector:
app: nginx app: nginx
@@ -302,7 +312,7 @@ func TestSourceCommandJSON_Stdin(t *testing.T) {
if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1 if !assert.Equal(t, `apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList kind: ResourceList
items: items:
- {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0'}}, "spec": {"replicas": 1}} - {"kind": "Deployment", "metadata": {"labels": {"app": "nginx2"}, "name": "foo", "annotations": {"app": "nginx2", config.kubernetes.io/index: '0', internal.config.kubernetes.io/index: '0'}}, "spec": {"replicas": 1}}
`, out.String()) { `, out.String()) {
return return
} }

View File

@@ -278,12 +278,13 @@ func (gr *Runner) CheckoutReleaseBranch(
return nil return nil
} }
gr.comment("creating branch") gr.comment("creating branch")
// The branch doesn't exist. Create it. // The branch doesn't exist remotely. Create or reset it locally.
out, err := gr.run(noHarmDone, "checkout", "-b", branch) out, err := gr.run(noHarmDone, "checkout", "-B", branch)
if err != nil { if err != nil {
return err return err
} }
if !strings.Contains(out, "Switched to a new branch ") { // Expected strings: "Switched to a new branch" or "Switched to and reset branch"
if !strings.Contains(out, "Switched to") {
return fmt.Errorf("unexpected branch creation output: %q", out) return fmt.Errorf("unexpected branch creation output: %q", out)
} }
return nil return nil

View File

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

View File

@@ -228,8 +228,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/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 h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= 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/api v0.10.0 h1:HK5gVSlVV24AmZ2fTHUIchZ6osaYNegK1jAdx7lJ/mU=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= sigs.k8s.io/kustomize/api v0.10.0/go.mod h1:syysqD8Oews9oghLfCitMCuCPxxu4MErSJ6uw8ge9jk=
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4=
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 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 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 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 This example defines the `helm` command as
<!-- @defineHelmCommand @testHelm --> <!-- @defineHelmCommand @testHelm -->
``` ```
helmCommand=~/go/bin/helmV3 helmCommand=${MYGOBIN:-~/go/bin}/helmV3
``` ```
This value is needed for testing this example in CI/CD. This value is needed for testing this example in CI/CD.

View File

@@ -19,6 +19,10 @@
set -e 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 where=$PWD
release_url=https://api.github.com/repos/kubernetes-sigs/kustomize/releases 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 # We test against the latest release, and HEAD, and presumably
# any branch using this label, so it should probably get # any branch using this label, so it should probably get
# a new value. # a new value.
export MYGOBIN
mdrip --mode test --blockTimeOut 15m \ mdrip --mode test --blockTimeOut 15m \
--label testAgainstLatestRelease examples --label testAgainstLatestRelease examples

View File

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

@@ -212,7 +212,7 @@ func (mf *kustomizationFile) parseCommentedFields(content []byte) error {
if matched { if matched {
mf.originalFields = append(mf.originalFields, &commentedField{field: field, comment: squash(comments)}) mf.originalFields = append(mf.originalFields, &commentedField{field: field, comment: squash(comments)})
comments = [][]byte{} comments = [][]byte{}
} else if len(comments) > 0 { } else if len(comments) > 0 && len(mf.originalFields) > 0 {
mf.originalFields[len(mf.originalFields)-1].appendComment(squash(comments)) mf.originalFields[len(mf.originalFields)-1].appendComment(squash(comments))
comments = [][]byte{} comments = [][]byte{}
} }

View File

@@ -356,6 +356,53 @@ kind: Kustomization
} }
} }
func TestCommentsWithDocumentSeperatorAtBeginning(t *testing.T) {
kustomizationContentWithComments := []byte(`
# Some comments
# This is some comment we should preserve
# don't delete it
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mynamespace
`)
expected := []byte(`
# Some comments
# This is some comment we should preserve
# don't delete it
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: mynamespace
`)
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(
fSys, kustomizationContentWithComments)
mf, err := NewKustomizationFile(fSys)
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
kustomization, err := mf.Read()
if err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
if err = mf.Write(kustomization); err != nil {
t.Fatalf("Unexpected Error: %v", err)
}
bytes, _ := fSys.ReadFile(mf.path)
if diff := cmp.Diff(expected, bytes); diff != "" {
t.Errorf("Mismatch (-expected, +actual):\n%s", diff)
}
}
func TestUnknownFieldInKustomization(t *testing.T) { func TestUnknownFieldInKustomization(t *testing.T) {
kContent := []byte(` kContent := []byte(`
foo: foo:

View File

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

View File

@@ -8,9 +8,9 @@ require (
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
sigs.k8s.io/kustomize/api v0.8.10 sigs.k8s.io/kustomize/api v0.10.0
sigs.k8s.io/kustomize/cmd/config v0.9.13 sigs.k8s.io/kustomize/cmd/config v0.10.1
sigs.k8s.io/kustomize/kyaml v0.11.0 sigs.k8s.io/kustomize/kyaml v0.12.0
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )
@@ -18,5 +18,3 @@ exclude (
sigs.k8s.io/kustomize/api v0.2.0 sigs.k8s.io/kustomize/api v0.2.0
sigs.k8s.io/kustomize/cmd/config v0.2.0 sigs.k8s.io/kustomize/cmd/config v0.2.0
) )
replace sigs.k8s.io/kustomize/api => ../api

View File

@@ -253,10 +253,12 @@ 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/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 h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= 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/api v0.10.0 h1:HK5gVSlVV24AmZ2fTHUIchZ6osaYNegK1jAdx7lJ/mU=
sigs.k8s.io/kustomize/cmd/config v0.9.13/go.mod h1:7547FLF8W/lTaDf0BDqFTbZxM9zqwEJqCKN9sSR0xSs= sigs.k8s.io/kustomize/api v0.10.0/go.mod h1:syysqD8Oews9oghLfCitMCuCPxxu4MErSJ6uw8ge9jk=
sigs.k8s.io/kustomize/kyaml v0.11.0 h1:9KhiCPKaVyuPcgOLJXkvytOvjMJLoxpjodiycb4gHsA= sigs.k8s.io/kustomize/cmd/config v0.10.1 h1:eqpN9eUSn3XIfvPabit8lpIqUbWKS7f4lOB4D2cr5CQ=
sigs.k8s.io/kustomize/kyaml v0.11.0/go.mod h1:GNMwjim4Ypgp/MueD3zXHLRJEjz7RvtPae0AwlvEMFM= sigs.k8s.io/kustomize/cmd/config v0.10.1/go.mod h1:9W5pDv3cgDfMjOXEga4yC9lUpkgAaecW+lZmHOMeX2I=
sigs.k8s.io/kustomize/kyaml v0.12.0 h1:k08l8SLwnKa/eXXB5GW2/OnEc/4gJF90VDFebsOwqw4=
sigs.k8s.io/kustomize/kyaml v0.12.0/go.mod h1:FTJxEZ86ScK184NpGSAQcfEqee0nul8oLCK30D47m4E=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= 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 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

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