Compare commits

...

221 Commits

Author SHA1 Message Date
Jeff Regan
e653cffab6 Merge pull request #3565 from monopole/pinToCmdConfig
Pin to cmd/config v0.9.1
2021-02-09 10:38:46 -08:00
monopole
e036f85b71 Pin to cmd/config v0.9.1 2021-02-09 10:17:53 -08:00
Jeff Regan
cee1324d18 Merge pull request #3564 from kubernetes-sigs/pinToKyamlAndCliUtils
Pin to kyaml v0.10.9
2021-02-09 10:14:01 -08:00
monopole
b062ce0f66 Pin to kyaml v0.10.9 2021-02-09 09:57:21 -08:00
Jeff Regan
fbcb15b15f Merge pull request #3563 from monopole/commitGeneratedCode
Commit generated doc changes.
2021-02-09 09:19:32 -08:00
monopole
8eb062637f Commit generated doc changes. 2021-02-09 09:18:31 -08:00
Jeff Regan
2fe85be932 Merge pull request #3557 from natasha41575/PatchMergeIssue
fixed ports merging issue and refactored some kyaml/walk code
2021-02-09 08:48:19 -08:00
Jeff Regan
44edfa87fe Merge pull request #3562 from monopole/changeGenMessage
Fix message in generated code.
2021-02-09 08:08:04 -08:00
monopole
bbccee0219 Fix message in generated code. 2021-02-09 07:34:32 -08:00
Jeff Regan
7cdc6cbe2f Merge pull request #3561 from monopole/unpin
Unpin; back to development mode.
2021-02-09 07:13:20 -08:00
monopole
264bfa8998 Unpin; back to development mode. 2021-02-09 06:54:18 -08:00
Jeff Regan
041181afe4 Merge pull request #3560 from monopole/dropQuantityProto
Drop unused quantity protobuf - avoid bad init.
2021-02-09 06:36:56 -08:00
monopole
5db79285a7 Drop unused quantity protobuf - avoid bad init. 2021-02-09 06:19:13 -08:00
Jeff Regan
556692c9f5 Merge pull request #3556 from monopole/dropKubeOpenAi
Drop dependence on k8s.io/kube-openapi
2021-02-08 18:01:02 -08:00
Natasha Sarkar
71f4cecb4c fixed ports merging issue and refactored some kyaml.walk code 2021-02-08 17:49:10 -08:00
monopole
d4d5fca2a5 Drop dependence on k8s.io/kube-openapi 2021-02-08 17:31:54 -08:00
Jeff Regan
811e1dca05 Merge pull request #3554 from monopole/pinToCmdConfig
Pin to cmd/config v0.9.0
2021-02-08 16:46:36 -08:00
monopole
09bc6e76b1 Pin to cmd/config v0.9.0 2021-02-08 16:30:57 -08:00
Jeff Regan
065b14c5c5 Merge pull request #3553 from monopole/pinToKyamlAndCliUtils
Pin to kyaml v0.10.8
2021-02-08 16:24:49 -08:00
monopole
adb2f2237a Pin to kyaml v0.10.8 2021-02-08 16:09:17 -08:00
Jeff Regan
024bbd0777 Merge pull request #3549 from monopole/removeApiMachDep
Remove grep's dependence on apimachinery.
2021-02-08 15:34:53 -08:00
monopole
ca1929abfb Remove grep's dependence on apimachinery. 2021-02-08 15:16:46 -08:00
Jeff Regan
a13ef4da65 Update Makefile 2021-02-08 15:14:47 -08:00
Jeff Regan
81ec59fa62 Update Makefile 2021-02-08 14:58:31 -08:00
Jeff Regan
075a2d6c0f Merge pull request #3552 from monopole/tweakCopier
Fix prefix bug in copier.
2021-02-08 14:45:12 -08:00
monopole
1df430255a Fix prefix bug in copier. 2021-02-08 14:39:25 -08:00
Jeff Regan
94d06e1e18 Merge pull request #3550 from Shell32-Natsu/value-add
fix forward slash in value add path
2021-02-08 13:39:58 -08:00
Jeff Regan
c3b240639d Merge pull request #3551 from monopole/fixKyamlMake
Normalize deps in kyaml makefile.
2021-02-08 13:34:40 -08:00
monopole
4de6db3d59 Normalize deps in makefile. 2021-02-08 13:21:42 -08:00
Donny Xia
58216d1d33 fix forward slash in value add path 2021-02-08 13:11:24 -08:00
Jeff Regan
4c456d60a4 Merge pull request #3548 from monopole/moveCopier
Move the k8s-related code generator.
2021-02-08 12:49:41 -08:00
monopole
582bc4de01 Move the k8s code generator. 2021-02-08 12:32:13 -08:00
Jeff Regan
a3a11bf3f4 Merge pull request #3545 from monopole/testExamplesAgainstLatestReleases
Test examples against latest releases.
2021-02-07 10:52:26 -08:00
monopole
4e96502ec6 Test examples against latest releases. 2021-02-07 10:31:34 -08:00
Jeff Regan
1ae8303bdc Merge pull request #3544 from monopole/pinToApi
Pin to api v0.7.3
2021-02-07 08:56:03 -08:00
monopole
41df2bed1f Pin to api v0.7.3 2021-02-07 08:40:52 -08:00
Jeff Regan
1f1304194d Update cloudbuild.sh 2021-02-07 07:57:10 -08:00
Jeff Regan
4157933c8d Update cloudbuild.sh 2021-02-07 07:47:06 -08:00
Jeff Regan
a79253e02f Merge pull request #3543 from monopole/releaserUpdates
Use goreleaser/v0.155.0, --parallelism 4
2021-02-07 07:21:14 -08:00
monopole
3b35b121b3 Use goreleaser/v0.155.0, --parallelism 4 2021-02-07 07:19:39 -08:00
Jeff Regan
3c94d20599 Deleting goarch=arm build, leaving arm64
Hitting hard step limit of 10m
Increasing timeout field in cloudbuild.yaml past 10m seems to have no effect.
Maybe break goreleaser step into multiple substeps?
2021-02-06 09:45:01 -08:00
Jeff Regan
1faeb91cc4 Set build timeout=800s, goreleaser timeout =720s 2021-02-06 09:16:02 -08:00
Jeff Regan
9f1ef993a1 Set FlagEnableKyamlDefaultValue = true 2021-02-06 08:31:12 -08:00
Jeff Regan
55d8cb3d3a Merge pull request #3541 from monopole/pinToApi
Pin to api v0.6.9
2021-02-06 08:16:35 -08:00
monopole
a684592639 Pin to api v0.6.9 2021-02-06 07:59:54 -08:00
Jeff Regan
386d10834b Set FlagEnableKyamlDefaultValue = false 2021-02-06 07:16:55 -08:00
Jeff Regan
af32126e80 Merge pull request #3540 from monopole/pinToCmdConfig
Pin to cmd/config v0.8.9
2021-02-05 22:01:41 -08:00
monopole
b0cfa15b9c Pin to cmd/config v0.8.9 2021-02-05 21:40:42 -08:00
Jeff Regan
62e7df6812 Merge pull request #3539 from monopole/pinToKyamlAndCliUtils
Pin to kyaml v0.10.7 and cli-utils v0.23.1
2021-02-05 21:29:06 -08:00
monopole
c077ed4b58 Pin to kyaml v0.10.7 and cli-utils v0.23.1 2021-02-05 20:34:05 -08:00
Jeff Regan
156beb300c Update README.md 2021-02-05 20:23:59 -08:00
Jeff Regan
25b02d2d6c Merge pull request #3538 from monopole/fixnits
Make starlarkmixer plugin module name match path.
2021-02-05 18:07:34 -08:00
monopole
781098843e Make starlarkmixer plugin module name match path. 2021-02-05 17:57:12 -08:00
Jeff Regan
af3ffa7059 Merge pull request #3493 from natasha41575/UseIntermediateName
Allow references to intermediate IDs after multiple name transformations
2021-02-05 16:31:31 -08:00
Donny Xia
9d7b8952a0 Merge pull request #3535 from MaXinjian/tmpfile
Cleanup tempfiles introduced in confirmeddir_test.go
2021-02-05 12:57:05 -08:00
Natasha Sarkar
cb400c895e added test to check for resources with the same name in different layers 2021-02-05 12:35:59 -08:00
Ma Xinjian
9a8dcf6a8e Cleanup tempfiles introduced in confirmeddir_test.go
Signed-off-by: Ma Xinjian <maxj.fnst@cn.fujitsu.com>
2021-02-05 13:26:58 +08:00
Natasha Sarkar
384b71b5f5 Allow references to intermediate names 2021-02-04 20:38:08 -08:00
Kubernetes Prow Robot
4345cd2ade Merge pull request #3534 from KnVerey/starlark_genformer
Fix function/exec transformer plugin ability to add/remove resources
2021-02-04 19:54:51 -08:00
Katrina Verey
1460d13d50 Allow exec and function transformers to delete resources 2021-02-04 13:01:09 -08:00
Katrina Verey
97a2b15be6 Allow exec and function transformers to generate resources 2021-02-04 13:01:09 -08:00
Katrina Verey
f927cf0b8e Regression test for inability to add resources in function-based transfomer 2021-02-04 13:01:09 -08:00
Jeff Regan
cbb121e651 Merge pull request #3533 from monopole/updateNRtest
Update namerefence tests for apimachinery code path.
2021-02-04 12:15:17 -08:00
monopole
447b315a61 Update namerefence tests for apimachinery code path. 2021-02-04 12:14:16 -08:00
Kubernetes Prow Robot
b3cb61b80f Merge pull request #3532 from monopole/commandsOption3
Expose some top level kustomize commands.
2021-02-04 10:02:26 -08:00
monopole
b9f05dd357 Expose some top level kustomize commands.
The PR exposes some of the top level kustomize commands
(especially `build`) for reuse in other command line tools
(expecially `kubectl`, see #1500).

This PR represents option 3 from the following list of ways
this exposure could be arranged.

1. Expose the commands in the `api` module.

```
REPO/api/go.mod
REPO/api/builtins
REPO/api/commands <- new
REPO/api/...
```

Disadvantage: This would make `api` module depend on cobra.
That's bad for clients that want to depend on the api, but
want to write their own commands at their own version of
cobra.  The `api` module shouldn't depend on UX libraries
like cobra.

2. Expose the commands in their own `commands` module.

They'd appear alongside `api`, e.g. `

```
REPO/api/go.mod
REPO/api/builtins
REPO/api/...
REPO/commands/go.mod
REPO/commands/build
REPO/commands/edit
REPO/commands/...
```

Advantage: The commands would be consumed by the kustomize
binary and the kubectl binary in the same way.

Disadvantage: The kustomize binary module and the commands
module could evolve separately with their own version
numbers, creating confusion.

3. Expose the commands in the existing `kustomize` module

```
REPO/api/go.mod
REPO/api/builtins
REPO/api/...
REPO/kustomize/go.mod
REPO/kustomize/main.go
REPO/kustomize/commands/build
REPO/kustomize/commands/edit
REPO/kustomize/commands/...
```

Outside users, e.g. kubectl, could then

```
import sigs.k8s.io/kustomize/kustomize/v3/commands/build
```

and hopefully still get the `main` package
as they do now via:

```
go get sigs.k8s.io/kustomize/kustomize/v3
```

Advantage: 1) The kustomize binary ships at the same version
as the commands - which makes sense as the binary's
_version_ refers to how the CLI operates (command names,
flags, etc.).  This makes it easy to related the version of
a kustomize binary with the version of commands running in
some other CLI binary.  2) The path to the kustomize binary
doesn't change.

Disadvantage: It's an atypical Go module arrangement.
Usually `main` packages live as leaves under a directory
called `cmd` inside a module, rather than at the _top_ of
the module.  This might cause some problems.  If so, we can
go with option 4.

4. Same as 3, but move `main.go` (the `main` package) down one step.

```
REPO/api/go.mod
REPO/api/builtins
REPO/api/...
REPO/kustomize/go.mod
REPO/kustomize/cmd/main.go
REPO/kustomize/commands/build
REPO/kustomize/commands/edit
REPO/kustomize/commands/...
```
2021-02-04 08:35:01 -08:00
Jeff Regan
1ee16d9f52 Merge pull request #3525 from natasha41575/RefactorNameTransformers
Refactored resource to store all previous names and namespaces
2021-02-03 16:53:16 -08:00
Natasha Sarkar
43157f5d35 cleaned up resource refactoring 2021-02-03 14:30:25 -08:00
Natasha Sarkar
cd918483f9 removed some prefix methods from resource 2021-02-03 12:02:12 -08:00
Natasha Sarkar
f71854a0c8 Refactored resource to store all previous names and namespaces 2021-02-03 12:01:09 -08:00
Kubernetes Prow Robot
6246262965 Merge pull request #3529 from monopole/fix3489
Fix 3489
2021-02-03 11:04:28 -08:00
monopole
d3ea87220b Fix Issue 3489. 2021-02-03 10:49:17 -08:00
Jeff Regan
61daea0202 Merge pull request #3245 from natasha41575/UseOpenApiVersions
Use `openApi` field in kustomization file to specify OpenAPI schema version
2021-02-03 10:35:08 -08:00
Jeff Regan
a54cd12b39 Delete fixgomod.sh 2021-02-03 10:20:28 -08:00
Kubernetes Prow Robot
11ce6363b4 Merge pull request #3528 from monopole/refactorNameref
Refactor nameref for readability.
2021-02-03 10:16:29 -08:00
monopole
4de26ccf9d Refactor nameref for readability. 2021-02-02 19:02:41 -08:00
Jeff Regan
7801830152 Merge pull request #3527 from monopole/addTests
Add tests in support of Issue 3489.
2021-02-02 18:53:31 -08:00
monopole
aae2be1a79 Add tests in support of Issue 3489. 2021-02-02 18:35:46 -08:00
Kubernetes Prow Robot
79e15c05d5 Merge pull request #3522 from Shell32-Natsu/chart-generator
fix relative path to values file
2021-02-02 15:44:28 -08:00
Donny Xia
507244e6f8 fix relative path to values file 2021-02-01 13:55:48 -08:00
Jeff Regan
3892e3c910 Merge pull request #3519 from monopole/fieldspectest
More fieldspec filter tests and comments.
2021-01-31 20:01:47 -08:00
monopole
dcb26d0901 More fieldspec tests. 2021-01-31 19:08:48 -08:00
Jeff Regan
f5f1a15226 Merge pull request #3518 from monopole/nits
Improve name reference transformer testing.
2021-01-31 09:04:45 -08:00
monopole
64644643d4 Improve name reference transformer testing. 2021-01-31 08:40:47 -08:00
Jeff Regan
4d1eebbb82 Merge pull request #3517 from monopole/pathSplitter
Expose smart path splitter as a utility.
2021-01-31 07:56:42 -08:00
monopole
b3a9314e27 Expose smart path splitter as a utility. 2021-01-31 06:45:04 -08:00
Kubernetes Prow Robot
86168cebbc Merge pull request #3514 from natasha41575/TestForPortsIssue
Add test for ports issue #3513
2021-01-29 12:51:49 -08:00
Natasha Sarkar
57a53797d3 Add test for ports issue #3513 2021-01-29 11:10:00 -08:00
Kubernetes Prow Robot
f5beffe394 Merge pull request #3512 from monopole/improveNameRefTests
Add test for issue 3489 and improve error messages.
2021-01-29 10:49:41 -08:00
monopole
4287e28ff4 Add test for issue 3489 and improve error messages. 2021-01-29 06:55:23 -08:00
Jeff Regan
a5cdd98414 Merge pull request #3498 from piyushpgupta/patch-1
Update README.md
2021-01-27 17:20:22 -08:00
Piyush Gupta
16a49c50c4 Update README.md 2021-01-28 09:17:39 +09:00
Piyush Gupta
a6f29f2bf7 Update README.md 2021-01-28 09:17:10 +09:00
Piyush Gupta
22fcf3b3fa Update README.md 2021-01-28 09:16:28 +09:00
Piyush Gupta
a31b846fa5 Update README.md 2021-01-28 09:10:59 +09:00
Jeff Regan
22fb23071b Merge pull request #3483 from msk-/master
Bug: configMapRefs in patches are not resolved correctly in diamond structure
2021-01-27 15:43:49 -08:00
Jeff Regan
382cf5c2e0 Merge pull request #3506 from natasha41575/FixSmpDirectiveWhenElementNotFound
$patch:delete should not add elements when element is not found
2021-01-27 11:24:38 -08:00
Jeff Regan
8d4508a041 Merge pull request #3484 from kubernetes-sigs/issue3377
Demonstrate issue #3377 is fixed.
2021-01-26 16:53:58 -08:00
Natasha Sarkar
4d5657f037 smpdirectives shouldn't add elements when element isn't found 2021-01-26 16:43:30 -08:00
Natasha Sarkar
5958edda14 don't merge patches until they are applied 2021-01-26 16:43:15 -08:00
Natasha Sarkar
48676fe34b updated patch delete test 2021-01-26 16:42:59 -08:00
Jeff Regan
a8278b6da9 Merge pull request #3495 from MaXinjian/install
Add support to install kustomize to specified directory
2021-01-26 12:30:46 -08:00
Kubernetes Prow Robot
31d6e24fa4 Merge pull request #3491 from Shell32-Natsu/error-message
add error message for AsYAML
2021-01-26 12:26:30 -08:00
Jeff Regan
25e11e9020 Update install_kustomize.sh 2021-01-26 12:15:20 -08:00
Jeff Regan
d8e2a76ef3 Merge pull request #3499 from MaXinjian/validator
Fix wrong Validator dir
2021-01-26 12:01:41 -08:00
Jeff Regan
cff7bd4eb2 Merge pull request #3496 from msk-/use-usr-bin-env-bash-for-scripts
Use `/usr/bin/env bash` instead of `/bin/bash` in scripts
2021-01-26 12:01:01 -08:00
Kubernetes Prow Robot
58db58202c Merge pull request #3500 from Shell32-Natsu/ingress-name-ref
support anchor in filedspec
2021-01-26 11:48:31 -08:00
Donny Xia
fc29d7e108 Merge pull request #3503 from dimw/3487-set-image-tag-name
Add support for setting new tag while preserving the current name
2021-01-26 10:32:33 -08:00
dimw
ae060cc225 Add documentation/help on preserving the digest
Issue: #3487
2021-01-25 22:06:44 +01:00
dimw
9b4fdcf35a Add support for setting the name while keeping the digest
- And the other way around

Issue: #3487
2021-01-25 21:38:28 +01:00
dimw
bb2d63ab58 Use raw string to avoid double escape
Issue: #3487
2021-01-25 21:15:05 +01:00
dimw
b5012385c8 Add support for setting new tag while preserving the current name
- As well as setting new name while keeping the tag

Issue: #3487
2021-01-25 15:31:19 +01:00
Natasha Sarkar
659a7de8f9 edited kyaml libraries to use the openapi field from the kustomization file 2021-01-22 16:07:18 -08:00
Natasha Sarkar
866dbf2017 added an openapi field to the kustomization file 2021-01-22 16:06:57 -08:00
Donny Xia
ef89df6123 support anchor in filedspec 2021-01-22 11:01:07 -08:00
Ma Xinjian
97f23966af Fix wrong Validator dir
Signed-off-by: Ma Xinjian <maxj.fnst@cn.fujitsu.com>
2021-01-22 13:54:34 +08:00
Piyush Gupta
5059033b13 Update README.md
Fixed the typo #
2021-01-21 22:41:54 +09:00
msk-
d91e8af702 Use /usr/bin/env bash instead of /bin/bash for scripts to support more systems 2021-01-21 10:43:18 +00:00
msk-
1bef8c4cdd Added ASCII art per PR feedback 2021-01-21 10:13:42 +00:00
msk-
9b87f78511 Modified test name per PR feedback 2021-01-21 10:04:19 +00:00
msk-
eda827c317 Added line break to all WriteK, WriteF calls per PR feedback 2021-01-21 10:03:18 +00:00
Ma Xinjian
be57e1f6c2 Add support to install kustomize to specified directory
Signed-off-by: Ma Xinjian <maxj.fnst@cn.fujitsu.com>
2021-01-21 15:59:04 +08:00
Kubernetes Prow Robot
021c3ce3fc Merge pull request #3494 from KnVerey/template-cmd-without-cobra
[kyaml] Framework/TemplateCommand usability improvements
2021-01-20 16:49:01 -08:00
Katrina Verey
4f184e8ce3 Add validation hook to template command execution 2021-01-20 15:53:46 -08:00
Katrina Verey
2e0d6d42bf Improve framework usability without cobra 2021-01-20 15:53:10 -08:00
Donny Xia
6cf6eb9f76 use reource.AsYAML in resWrangler.AsYAML 2021-01-20 12:51:25 -08:00
Donny Xia
ffed8f1430 add error message for AsYAML 2021-01-20 10:40:59 -08:00
monopole
3d17503329 Demonstrate that #3377 is fixed. 2021-01-19 19:06:36 -08:00
msk-
7ac37867dc tidy-up, added desired behaviour 2021-01-19 18:00:02 +00:00
msk-
d8f2d2256d Fixed test 2021-01-19 17:57:42 +00:00
msk-
463b776486 Initial test commit 2021-01-19 17:41:44 +00:00
Jeff Regan
d28ce28130 Merge pull request #3478 from kubernetes-sigs/testAgainst3.9.2
Test examples against v3.9.2 and v3.8.9
2021-01-17 11:14:46 -08:00
monopole
4f72faeecb Test examples against v3.9.2 and v3.8.9 2021-01-17 10:54:13 -08:00
Jeff Regan
29814b556b Update README.md 2021-01-17 10:25:19 -08:00
Jeff Regan
d94ed369fa Merge pull request #3477 from kubernetes-sigs/unpinEverything
Back to development mode; unpin the modules
2021-01-17 10:25:10 -08:00
monopole
0cf18987d7 Back to development mode; unpin the modules 2021-01-17 10:10:12 -08:00
Jeff Regan
a6374db2cb Update README.md 2021-01-17 10:09:08 -08:00
Jeff Regan
e98eada736 Merge pull request #3476 from kubernetes-sigs/pinToApi
Pin to api v0.7.2
2021-01-17 09:36:37 -08:00
monopole
8a65ece956 Pin to api v0.7.2 2021-01-17 09:19:41 -08:00
Jeff Regan
4cdc3b0bad Merge pull request #3475 from kubernetes-sigs/pinToCmdConfig
Pin to cmd/config v0.8.8
2021-01-17 09:09:20 -08:00
monopole
40bf89abcd Pin to cmd/config v0.8.8 2021-01-17 08:40:13 -08:00
Jeff Regan
7f548eddd0 Merge pull request #3474 from kubernetes-sigs/pinToKyamlAndCliUtils
Pin to kyaml v0.10.6 and cli-utils v0.22.4
2021-01-17 07:17:43 -08:00
monopole
86e9983bb7 Pin to kyaml v0.10.6 and cli-utils v0.22.4 2021-01-17 06:55:10 -08:00
Jeff Regan
cbbcfde99d Update multiplepatch_test.go 2021-01-16 20:43:57 -08:00
Jeff Regan
304a9e57ee Update factory_test.go 2021-01-16 20:36:46 -08:00
Jeff Regan
f23f26aa05 Update factory_test.go 2021-01-16 20:24:39 -08:00
Jeff Regan
720857623f Merge pull request #3473 from monopole/doImportantStuff
Fix unfiled bug in port replacement where ports were being quoted.
2021-01-16 18:02:06 -08:00
monopole
065c2b861a Fix unfiled bug; don't quote port numbers. 2021-01-16 16:56:48 -08:00
monopole
2a16af80bf Simplify, document and add more tests to var replacement. 2021-01-16 16:48:26 -08:00
Jeff Regan
81d324c68c Merge pull request #3472 from monopole/issue3449
Reproduce issue #3449
2021-01-16 16:47:45 -08:00
monopole
b8702561ef Add test for issue 3449 2021-01-16 16:45:13 -08:00
Jeff Regan
ea039b36bc Merge pull request #3471 from monopole/mergeExpansionRefvar
Merge expansion package into refvar package.
2021-01-16 13:54:32 -08:00
monopole
561cef1d5c Merge expansion package into refvar package. 2021-01-16 13:50:56 -08:00
Jeff Regan
62c5e424a6 Merge pull request #3470 from monopole/annotateIssue3304
Annotate code with decisions on issue 3304 in kyaml conversion
2021-01-16 09:09:49 -08:00
monopole
45b1bf17d3 Annotate decisions on issue 3304 in api. 2021-01-16 08:08:52 -08:00
monopole
11dce34407 Annotate decisions on issue 3304 in plugins. 2021-01-16 08:08:39 -08:00
Kubernetes Prow Robot
550a89295a Merge pull request #3469 from monopole/labelAndAnnotationQuoting
Always quote non-string values in labels and annotations
2021-01-15 18:43:43 -08:00
monopole
8083b3607f Harden anno transformer test. 2021-01-15 17:37:46 -08:00
monopole
cb42142161 Fix api tests that accomodated bad label and anno quoting. 2021-01-15 17:37:46 -08:00
monopole
cb59e0ef5f Always tag label and annotations values as strings. 2021-01-15 17:37:46 -08:00
Jeff Regan
1a4a9fcdaf Update go.mod 2021-01-15 17:33:00 -08:00
Kubernetes Prow Robot
eb8dc5e20a Merge pull request #3462 from mikhail-nikitin/master
$patch: delete of not existing elements
2021-01-15 14:23:43 -08:00
Jeff Regan
0fb30a1010 In plugin tests, yell FAILURE on failure. 2021-01-15 13:58:09 -08:00
Jeff Regan
fdfdfa9e4d Update go.mod 2021-01-15 13:56:18 -08:00
Jeff Regan
6042aca7a4 Update CalvinDuplicator.go 2021-01-15 13:33:46 -08:00
Jeff Regan
94962c8bac Update CalvinDuplicator_test.go 2021-01-15 13:10:40 -08:00
Mikhail Nikitin
f6ddea435c Make test file paths consistent and relative
Signed-off-by: Mikhail Nikitin <mikhail.nikitin@ispringsolutions.com>
2021-01-15 22:50:23 +03:00
Jeff Regan
a9d4b7615f Merge pull request #3466 from monopole/renameAnnotations
Rename id annotations to build annotations.
2021-01-15 11:27:49 -08:00
Jeff Regan
822cac26f9 Merge pull request #3467 from monopole/calvinDuplicator
Add calvin duplicator example plugin.
2021-01-15 07:58:11 -08:00
monopole
97eedc8a43 Add calvin duplicator example plugin. 2021-01-15 07:22:46 -08:00
monopole
2cb972de3b Rename id annotations to build annotations. 2021-01-15 06:43:13 -08:00
Jeff Regan
79d0d6b5e1 Merge pull request #3465 from monopole/pdbTest
Add test covering pod disruption budget treatment.
2021-01-15 06:38:38 -08:00
monopole
fabaf35c72 Add test covering pod disruption budget treatment. 2021-01-15 06:09:52 -08:00
Mikhail Nikitin
e13f8803eb Add test case of deleting not existing elements 2021-01-15 00:08:49 +03:00
Jeff Regan
64ffbcb15d Merge pull request #3461 from monopole/anotherTest
Add another test covering fix of 3412
2021-01-14 11:52:56 -08:00
monopole
b41df2293b Add another test covering fix of 3412 2021-01-14 11:32:34 -08:00
Kubernetes Prow Robot
e3fcec122a Merge pull request #3460 from monopole/fix3412
Fix 3412, retaining quotes in configmap data fields
2021-01-14 11:25:32 -08:00
monopole
1edf9b630c Update configmap test with quotes fixed. 2021-01-14 11:01:37 -08:00
monopole
7c6bf2e21d When merging configmaps, retain proper quoting. 2021-01-14 11:01:06 -08:00
monopole
b3fc306f6a Move some code to make it reusable without import cycles. 2021-01-14 10:29:51 -08:00
Jeff Regan
e92d048af2 Merge pull request #3441 from natasha41575/namePrefixNamespaceBehavior
Update name references by checking both the original and current namespaces
2021-01-13 16:21:05 -08:00
Kubernetes Prow Robot
f76059b824 Merge pull request #3454 from monopole/evenMoreTests
Add more tests and explain some quoting behavior.
2021-01-13 16:05:03 -08:00
monopole
bb41d018b5 Add more tests and explain some strange quotes. 2021-01-13 15:49:12 -08:00
Kubernetes Prow Robot
cf8815b0a0 Merge pull request #3453 from Shell32-Natsu/wnode-field-value
fix GetFieldValue cannot handle slice index
2021-01-13 15:07:03 -08:00
Donny Xia
64beee22e9 fix GetFieldValue cannot handle slice index 2021-01-13 14:14:01 -08:00
Jeff Regan
79afd219a5 Merge pull request #3450 from monopole/anotherTest
Add a test that only loads an annotated resource.
2021-01-13 11:27:14 -08:00
monopole
c68cf40d75 Add a test that only loads an annotated resource. 2021-01-13 10:56:50 -08:00
Kubernetes Prow Robot
c7337a7d87 Merge pull request #3445 from KnVerey/resource_list_empty_items
[kio] Unwrap ResourceList with a functionConfig but no items
2021-01-12 17:04:35 -08:00
Jeff Regan
875e265e5d Merge pull request #3372 from natasha41575/AddNameAnnotations
Refactor resource to use annotations in the yaml instead of fields in resource struct
2021-01-12 16:38:48 -08:00
Katrina Verey
bdbfb28139 Unwrap ResourceList with a functionConfig but no items 2021-01-12 16:13:40 -08:00
Natasha Sarkar
d54bc674f2 Update name references by checking both the original and current namespaces 2021-01-11 18:08:52 -08:00
Natasha Sarkar
bd4580d73a Manage name changes (prefix/suffix) via YAML annotations rather than via in-memory-only fields. 2021-01-11 13:08:45 -08:00
Kubernetes Prow Robot
ea5d08bac5 Merge pull request #3438 from monopole/fix3424
Fix 3424 by avoiding a JSON round trip
2021-01-11 10:18:24 -08:00
monopole
14a1a0e4a8 Fix 3424 by avoiding a JSON round trip 2021-01-10 20:39:01 -08:00
Jeff Regan
497e8038a3 Merge pull request #3439 from monopole/clarifyErrorMessage
Clarify var-related error message.
2021-01-10 20:38:39 -08:00
monopole
44b5acad51 Clarify var-related error message. 2021-01-10 20:35:55 -08:00
Jeff Regan
e5e19f7c09 Merge pull request #3431 from Shell32-Natsu/newline
keep \n in the end of resource yaml
2021-01-10 12:51:36 -08:00
Jeff Regan
a03843dfc7 Merge pull request #3437 from monopole/regressionTestsForKyaml
In kyaml, loosen interpretation of string node and add tests.
2021-01-10 12:28:30 -08:00
monopole
b7cce27d40 In kyaml, loosen interpretation of string node and add tests. 2021-01-10 12:08:50 -08:00
Jeff Regan
126f5481f3 Merge pull request #3436 from monopole/addTestsAroundVarRefTransformer
Add var ref replacement tests and more doc.
2021-01-10 09:38:30 -08:00
monopole
30dcf38609 Add var ref replacement tests and more doc. 2021-01-10 09:16:52 -08:00
Jeff Regan
1a2779b2c3 Merge pull request #3434 from monopole/reduceComplexityInNameReferenceTransformer
Reduce complexity in NameReferenceTransformer.
2021-01-10 07:12:23 -08:00
monopole
658b62c6f1 Reduce complexity in NameReferenceTransformer. 2021-01-10 06:56:06 -08:00
Jeff Regan
cf0bb49610 Merge pull request #3433 from monopole/anotherTowards3412
Improve handling of empty resource maps.
2021-01-09 07:20:19 -08:00
monopole
c2fbb709da Don't swallow error in SM patch and use new RNode Map method. 2021-01-09 07:00:21 -08:00
monopole
1a002005c1 Add RNode.Map method and test to help decoding. 2021-01-09 06:57:01 -08:00
Jeff Regan
4f468fcc90 Merge pull request #3432 from monopole/towards3412
Short circuit anno/label transformer for performance.
2021-01-08 18:53:47 -08:00
monopole
769f65d6c4 Short circuit anno/label transformer for performance. 2021-01-08 18:02:58 -08:00
Donny Xia
378eaedc82 keep \n in the end of resource yaml 2021-01-08 15:36:36 -08:00
Jeff Regan
6f2f401f6b Merge pull request #3428 from monopole/confineApplyToJson
Confine calls to ApplyToJSON.
2021-01-07 20:53:55 -08:00
monopole
614e853db3 Confine calls to ApplyToJSON. 2021-01-07 20:31:10 -08:00
Jeff Regan
33be04db45 Merge pull request #3427 from monopole/avoidCycle
Move plugin lister to avoid import cycle.
2021-01-07 19:27:45 -08:00
monopole
8c6a9f6495 Move plugin lister to avoid import cycle. 2021-01-07 18:46:04 -08:00
Jeff Regan
03b2fff0ee Merge pull request #3425 from monopole/issue3424
Add test for issue 3424
2021-01-07 15:55:47 -08:00
monopole
69cade143f Add test for issue 3424 2021-01-06 16:23:12 -08:00
Jeff Regan
90f45651d1 Merge pull request #3416 from HansK-p/Fix-link-to-lugins-doc
Updated link to Kustomize plugins documentation
2021-01-06 15:49:19 -08:00
Hans Kristian Nordengen
1b740034f7 Updated link to Kustomize plugins documentation 2021-01-03 19:44:32 +01:00
Jeff Regan
a2d8e686de Merge pull request #3411 from monopole/unpinApiKyamlCmdConfig
Unpin kyaml, cmd/config, api
2020-12-29 18:17:48 -08:00
jregan
ce2ab487a5 Unpin kyaml, cmd/config, api 2020-12-29 16:18:12 -08:00
Jeff Regan
7439f1809e Merge pull request #3410 from monopole/pinToApiv0.7.1
Pin to api/v0.7.1 (--enableKyaml=true)
2020-12-29 09:43:42 -08:00
jregan
6977c83a83 Pin to api/v0.7.1 (--enableKyaml=true) 2020-12-29 09:24:25 -08:00
316 changed files with 9855 additions and 2393 deletions

View File

@@ -4,7 +4,7 @@
# Makefile for kustomize CLI and API. # Makefile for kustomize CLI and API.
MYGOBIN := $(shell go env GOPATH)/bin MYGOBIN := $(shell go env GOPATH)/bin
SHELL := /bin/bash SHELL := /usr/bin/env bash
export PATH := $(MYGOBIN):$(PATH) export PATH := $(MYGOBIN):$(PATH)
MODULES := '"cmd/config" "api/" "kustomize/" "kyaml/"' MODULES := '"cmd/config" "api/" "kustomize/" "kyaml/"'
@@ -26,8 +26,8 @@ verify-kustomize: \
lint-kustomize \ lint-kustomize \
test-unit-kustomize-all \ test-unit-kustomize-all \
test-examples-kustomize-against-HEAD \ test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-3.9.0 \ test-examples-kustomize-against-3.9 \
test-examples-kustomize-against-3.8.8 test-examples-kustomize-against-3.8
# 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
@@ -39,8 +39,8 @@ prow-presubmit-check: \
test-unit-cmd-all \ test-unit-cmd-all \
test-go-mod \ test-go-mod \
test-examples-kustomize-against-HEAD \ test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-3.9.0 \ test-examples-kustomize-against-3.9 \
test-examples-kustomize-against-3.8.8 test-examples-kustomize-against-3.8
.PHONY: verify-kustomize-e2e .PHONY: verify-kustomize-e2e
verify-kustomize-e2e: test-examples-e2e-kustomize verify-kustomize-e2e: test-examples-e2e-kustomize
@@ -79,6 +79,11 @@ $(MYGOBIN)/gorepomod:
cd cmd/gorepomod; \ cd cmd/gorepomod; \
go install . go install .
# Build from local source.
$(MYGOBIN)/k8scopy:
cd cmd/k8scopy; \
go install .
# Build from local source. # Build from local source.
$(MYGOBIN)/pluginator: $(MYGOBIN)/pluginator:
cd cmd/pluginator; \ cd cmd/pluginator; \
@@ -98,13 +103,13 @@ $(MYGOBIN)/kustomize:
install-tools: \ install-tools: \
$(MYGOBIN)/goimports \ $(MYGOBIN)/goimports \
$(MYGOBIN)/golangci-lint-kustomize \ $(MYGOBIN)/golangci-lint-kustomize \
$(MYGOBIN)/gh \
$(MYGOBIN)/gorepomod \ $(MYGOBIN)/gorepomod \
$(MYGOBIN)/helm \
$(MYGOBIN)/k8scopy \
$(MYGOBIN)/mdrip \ $(MYGOBIN)/mdrip \
$(MYGOBIN)/pluginator \ $(MYGOBIN)/pluginator \
$(MYGOBIN)/prchecker \ $(MYGOBIN)/prchecker \
$(MYGOBIN)/stringer \ $(MYGOBIN)/stringer
$(MYGOBIN)/helm
### Begin kustomize plugin rules. ### Begin kustomize plugin rules.
# #
@@ -221,7 +226,7 @@ build-kustomize-api: $(builtinplugins)
cd api; go build ./... cd api; go build ./...
.PHONY: generate-kustomize-api .PHONY: generate-kustomize-api
generate-kustomize-api: generate-kustomize-api: $(MYGOBIN)/k8scopy
cd api; go generate ./... cd api; go generate ./...
.PHONY: test-unit-kustomize-api .PHONY: test-unit-kustomize-api
@@ -276,12 +281,12 @@ test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD ./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY: .PHONY:
test-examples-kustomize-against-3.9.0: $(MYGOBIN)/mdrip test-examples-kustomize-against-3.9: $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh v3.9.0 ./hack/testExamplesAgainstKustomize.sh v3.9.3
.PHONY: .PHONY:
test-examples-kustomize-against-3.8.8: $(MYGOBIN)/mdrip test-examples-kustomize-against-3.8: $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh v3.8.8 ./hack/testExamplesAgainstKustomize.sh v3.8.10
# linux only. # linux only.
# This is for testing an example plugin that # This is for testing an example plugin that

View File

@@ -7,7 +7,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/annotations" "sigs.k8s.io/kustomize/api/filters/annotations"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -25,11 +24,14 @@ func (p *AnnotationsTransformerPlugin) Config(
} }
func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error { func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
if len(p.Annotations) == 0 {
return nil
}
for _, r := range m.Resources() { for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(annotations.Filter{ err := r.ApplyFilter(annotations.Filter{
Annotations: p.Annotations, Annotations: p.Annotations,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,
}, r) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -28,6 +28,7 @@ func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error {
if err != nil { if err != nil {
return err return err
} }
res.StorePreviousId()
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
} }
} }

View File

@@ -10,7 +10,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path" "path/filepath"
"regexp" "regexp"
"strings" "strings"
@@ -51,7 +51,7 @@ func (p *HelmChartInflationGeneratorPlugin) Config(h *resmap.PluginHelpers, conf
return fmt.Errorf("chartName cannot be empty") return fmt.Errorf("chartName cannot be empty")
} }
if p.ChartHome == "" { if p.ChartHome == "" {
p.ChartHome = path.Join(p.tmpDir, "chart") p.ChartHome = filepath.Join(p.tmpDir, "chart")
} }
if p.ChartRepoName == "" { if p.ChartRepoName == "" {
p.ChartRepoName = "stable" p.ChartRepoName = "stable"
@@ -60,10 +60,10 @@ func (p *HelmChartInflationGeneratorPlugin) Config(h *resmap.PluginHelpers, conf
p.HelmBin = "helm" p.HelmBin = "helm"
} }
if p.HelmHome == "" { if p.HelmHome == "" {
p.HelmHome = path.Join(p.tmpDir, ".helm") p.HelmHome = filepath.Join(p.tmpDir, ".helm")
} }
if p.Values == "" { if p.Values == "" {
p.Values = path.Join(p.ChartHome, p.ChartName, "values.yaml") p.Values = filepath.Join(p.ChartHome, p.ChartName, "values.yaml")
} }
if p.ValuesMerge == "" { if p.ValuesMerge == "" {
p.ValuesMerge = "override" p.ValuesMerge = "override"
@@ -109,17 +109,16 @@ func (p *HelmChartInflationGeneratorPlugin) EncodeValues(w io.Writer) error {
// useValuesLocal process (merge) inflator config provided values with chart default values.yaml // useValuesLocal process (merge) inflator config provided values with chart default values.yaml
func (p *HelmChartInflationGeneratorPlugin) useValuesLocal() error { func (p *HelmChartInflationGeneratorPlugin) useValuesLocal() error {
fn := path.Join(p.ChartHome, p.ChartName, "kustomize-values.yaml") // not override, merge, none
vf, err := os.Create(fn) if !(p.ValuesMerge == "none" || p.ValuesMerge == "no" || p.ValuesMerge == "false") {
defer vf.Close() var pValues []byte
if err != nil { var err error
return err
} if filepath.IsAbs(p.Values) {
// override, merge, none pValues, err = ioutil.ReadFile(p.Values)
if p.ValuesMerge == "none" || p.ValuesMerge == "no" || p.ValuesMerge == "false" { } else {
p.Values = fn pValues, err = p.h.Loader().Load(p.Values)
} else { }
pValues, err := ioutil.ReadFile(p.Values)
if err != nil { if err != nil {
return err return err
} }
@@ -141,16 +140,48 @@ func (p *HelmChartInflationGeneratorPlugin) useValuesLocal() error {
} }
} }
p.ValuesLocal = chValues p.ValuesLocal = chValues
p.Values = fn
} }
err = p.EncodeValues(vf) b, err := yaml.Marshal(p.ValuesLocal)
if err != nil { if err != nil {
return err return err
} }
vf.Sync() path, err := p.writeValuesBytes(b)
if err != nil {
return err
}
p.Values = path
return nil return nil
} }
// copyValues will copy the relative values file into the temp directory
// to avoid messing up with CWD.
func (p *HelmChartInflationGeneratorPlugin) copyValues() error {
// only copy when the values path is not absolute
if filepath.IsAbs(p.Values) {
return nil
}
// we must use use loader to read values file
b, err := p.h.Loader().Load(p.Values)
if err != nil {
return err
}
path, err := p.writeValuesBytes(b)
if err != nil {
return err
}
p.Values = path
return nil
}
func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(b []byte) (string, error) {
path := filepath.Join(p.ChartHome, p.ChartName, "kustomize-values.yaml")
err := ioutil.WriteFile(path, b, 0644)
if err != nil {
return "", err
}
return path, nil
}
// Generate implements generator // Generate implements generator
func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) { func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) {
// cleanup // cleanup
@@ -174,6 +205,11 @@ func (p *HelmChartInflationGeneratorPlugin) Generate() (resmap.ResMap, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else {
err := p.copyValues()
if err != nil {
return nil, err
}
} }
// render the charts // render the charts
@@ -190,7 +226,7 @@ func (p *HelmChartInflationGeneratorPlugin) getTemplateCommandArgs() []string {
if p.ReleaseName != "" { if p.ReleaseName != "" {
args = append(args, p.ReleaseName) args = append(args, p.ReleaseName)
} }
args = append(args, path.Join(p.ChartHome, p.ChartName)) args = append(args, filepath.Join(p.ChartHome, p.ChartName))
if p.ReleaseNamespace != "" { if p.ReleaseNamespace != "" {
args = append(args, "--namespace", p.ReleaseNamespace) args = append(args, "--namespace", p.ReleaseNamespace)
} }
@@ -220,7 +256,7 @@ func (p *HelmChartInflationGeneratorPlugin) getPullCommandArgs() []string {
// checkLocalChart will return true if the chart does exist in // checkLocalChart will return true if the chart does exist in
// local chart home. // local chart home.
func (p *HelmChartInflationGeneratorPlugin) checkLocalChart() bool { func (p *HelmChartInflationGeneratorPlugin) checkLocalChart() bool {
path := path.Join(p.ChartHome, p.ChartName) path := filepath.Join(p.ChartHome, p.ChartName)
s, err := os.Stat(path) s, err := os.Stat(path)
if err != nil { if err != nil {
return false return false

View File

@@ -11,7 +11,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/imagetag" "sigs.k8s.io/kustomize/api/filters/imagetag"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -32,17 +31,17 @@ func (p *ImageTagTransformerPlugin) Config(
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error { func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() { for _, r := range m.Resources() {
// traverse all fields at first // traverse all fields at first
err := filtersutil.ApplyToJSON(imagetag.LegacyFilter{ err := r.ApplyFilter(imagetag.LegacyFilter{
ImageTag: p.ImageTag, ImageTag: p.ImageTag,
}, r) })
if err != nil { if err != nil {
return err return err
} }
// then use user specified field specs // then use user specified field specs
err = filtersutil.ApplyToJSON(imagetag.Filter{ err = r.ApplyFilter(imagetag.Filter{
ImageTag: p.ImageTag, ImageTag: p.ImageTag,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,
}, r) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,7 +7,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/labels" "sigs.k8s.io/kustomize/api/filters/labels"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -25,11 +24,14 @@ func (p *LabelTransformerPlugin) Config(
} }
func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error { func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error {
if len(p.Labels) == 0 {
return nil
}
for _, r := range m.Resources() { for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(labels.Filter{ err := r.ApplyFilter(labels.Filter{
Labels: p.Labels, Labels: p.Labels,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,
}, r) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -9,7 +9,6 @@ import (
"sigs.k8s.io/kustomize/api/filters/namespace" "sigs.k8s.io/kustomize/api/filters/namespace"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -35,10 +34,11 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
// Don't mutate empty objects? // Don't mutate empty objects?
continue continue
} }
err := filtersutil.ApplyToJSON(namespace.Filter{ r.StorePreviousId()
err := r.ApplyFilter(namespace.Filter{
Namespace: p.Namespace, Namespace: p.Namespace,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,
}, r) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -12,7 +12,6 @@ import (
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -79,9 +78,9 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
return err return err
} }
for _, res := range resources { for _, res := range resources {
err = filtersutil.ApplyToJSON(patchjson6902.Filter{ err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.JsonOp, Patch: p.JsonOp,
}, res) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -60,11 +60,10 @@ func (p *PatchStrategicMergeTransformerPlugin) Config(
"patch appears to be empty; files=%v, Patch=%s", p.Paths, p.Patches) "patch appears to be empty; files=%v, Patch=%s", p.Paths, p.Patches)
} }
// Merge the patches, looking for conflicts. // Merge the patches, looking for conflicts.
m, err := h.ResmapFactory().ConflatePatches(p.loadedPatches) _, err = h.ResmapFactory().ConflatePatches(p.loadedPatches)
if err != nil { if err != nil {
return err return err
} }
p.loadedPatches = m.Resources()
return nil return nil
} }

View File

@@ -12,7 +12,6 @@ import (
"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/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -105,9 +104,10 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
return err return err
} }
for _, res := range resources { for _, res := range resources {
err = filtersutil.ApplyToJSON(patchjson6902.Filter{ res.StorePreviousId()
err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.Patch, Patch: p.Patch,
}, res) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -10,7 +10,6 @@ import (
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -67,14 +66,18 @@ func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
// this will add a prefix and a suffix // this will add a prefix and a suffix
// to the resource even if those are // to the resource even if those are
// empty // empty
r.AddNamePrefix(p.Prefix) r.AddNamePrefix(p.Prefix)
r.AddNameSuffix(p.Suffix) r.AddNameSuffix(p.Suffix)
if p.Prefix != "" || p.Suffix != "" {
r.StorePreviousId()
}
} }
err := filtersutil.ApplyToJSON(prefixsuffix.Filter{ err := r.ApplyFilter(prefixsuffix.Filter{
Prefix: p.Prefix, Prefix: p.Prefix,
Suffix: p.Suffix, Suffix: p.Suffix,
FieldSpec: fs, FieldSpec: fs,
}, r) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,8 +7,6 @@ import (
"fmt" "fmt"
"sigs.k8s.io/kustomize/api/filters/replicacount" "sigs.k8s.io/kustomize/api/filters/replicacount"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
@@ -33,19 +31,17 @@ func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
found := false found := false
for _, fs := range p.FieldSpecs { for _, fs := range p.FieldSpecs {
matcher := p.createMatcher(fs) matcher := p.createMatcher(fs)
matchOriginal := m.GetMatchingResourcesByOriginalId(matcher) resList := m.GetMatchingResourcesByAnyId(matcher)
resList := append(
matchOriginal, m.GetMatchingResourcesByCurrentId(matcher)...)
if len(resList) > 0 { if len(resList) > 0 {
found = true found = true
for _, r := range resList { for _, r := range resList {
// There are redundant checks in the filter // There are redundant checks in the filter
// that we'll live with until resolution of // that we'll live with until resolution of
// https://github.com/kubernetes-sigs/kustomize/issues/2506 // https://github.com/kubernetes-sigs/kustomize/issues/2506
err := filtersutil.ApplyToJSON(replicacount.Filter{ err := r.ApplyFilter(replicacount.Filter{
Replica: p.Replica, Replica: p.Replica,
FieldSpec: fs, FieldSpec: fs,
}, r) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -13,7 +13,6 @@ import (
"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/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -119,15 +118,15 @@ func (p *ValueAddTransformerPlugin) Transform(m resmap.ResMap) (err error) {
// TODO: consider t.NotSelector if implemented // TODO: consider t.NotSelector if implemented
for _, res := range resources { for _, res := range resources {
if t.FieldPath == types.MetadataNamespacePath { if t.FieldPath == types.MetadataNamespacePath {
err = filtersutil.ApplyToJSON(namespace.Filter{ err = res.ApplyFilter(namespace.Filter{
Namespace: p.Value, Namespace: p.Value,
}, res) })
} else { } else {
err = filtersutil.ApplyToJSON(valueadd.Filter{ err = res.ApplyFilter(valueadd.Filter{
Value: p.Value, Value: p.Value,
FieldPath: t.FieldPath, FieldPath: t.FieldPath,
FilePathPosition: t.FilePathPosition, FilePathPosition: t.FilePathPosition,
}, res) })
} }
if err != nil { if err != nil {
return err return err

View File

@@ -4,6 +4,7 @@
package filesys_test package filesys_test
import ( import (
"os"
"path/filepath" "path/filepath"
"testing" "testing"
@@ -98,6 +99,7 @@ func TestNewTempConfirmDir(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
defer os.RemoveAll(string(tmp))
delinked, err := filepath.EvalSymlinks(string(tmp)) delinked, err := filepath.EvalSymlinks(string(tmp))
if err != nil { if err != nil {

View File

@@ -24,7 +24,7 @@ type Filter struct {
var _ kio.Filter = Filter{} var _ kio.Filter = Filter{}
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
keys := filtersutil.SortedMapKeys(f.Annotations) keys := yaml.SortedMapKeys(f.Annotations)
_, err := kio.FilterAll(yaml.FilterFunc( _, err := kio.FilterAll(yaml.FilterFunc(
func(node *yaml.RNode) (*yaml.RNode, error) { func(node *yaml.RNode) (*yaml.RNode, error) {
for _, k := range keys { for _, k := range keys {

View File

@@ -28,7 +28,9 @@ metadata:
`)}}, `)}},
Filters: []kio.Filter{Filter{ Filters: []kio.Filter{Filter{
Annotations: map[string]string{ Annotations: map[string]string{
"foo": "bar", "foo": "bar",
"booleanValue": "true",
"numberValue": "42",
}, },
FsSlice: fss, FsSlice: fss,
}}, }},
@@ -44,12 +46,16 @@ metadata:
// metadata: // metadata:
// name: instance // name: instance
// annotations: // annotations:
// booleanValue: "true"
// foo: bar // foo: bar
// numberValue: "42"
// --- // ---
// apiVersion: example.com/v1 // apiVersion: example.com/v1
// kind: Bar // kind: Bar
// metadata: // metadata:
// name: instance // name: instance
// annotations: // annotations:
// booleanValue: "true"
// foo: bar // foo: bar
// numberValue: "42"
} }

View File

@@ -4,9 +4,11 @@
package fieldspec package fieldspec
import ( import (
"fmt"
"strings" "strings"
"sigs.k8s.io/kustomize/api/filters/filtersutil" "sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/internal/utils"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
@@ -14,7 +16,16 @@ import (
var _ yaml.Filter = Filter{} var _ yaml.Filter = Filter{}
// Filter applies a single fieldSpec to a single object // Filter possibly mutates its object argument using a FieldSpec.
// If the object matches the FieldSpec, and the node found
// by following the fieldSpec's path is non-null, this filter calls
// the setValue function on the node at the end of the path.
// If any part of the path doesn't exist, the filter returns
// without doing anything and without error, unless it was set
// to create the path. If set to create, it creates a tree of maps
// along the path, and the leaf node gets the setValue called on it.
// Error on GVK mismatch, empty or poorly formed path.
// Filter expect kustomize style paths, not JSON paths.
// Filter stores internal state and should not be reused // Filter stores internal state and should not be reused
type Filter struct { type Filter struct {
// FieldSpec contains the path to the value to set. // FieldSpec contains the path to the value to set.
@@ -37,15 +48,17 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
if match, err := isMatchGVK(fltr.FieldSpec, obj); !match || err != nil { if match, err := isMatchGVK(fltr.FieldSpec, obj); !match || err != nil {
return obj, errors.Wrap(err) return obj, errors.Wrap(err)
} }
fltr.path = splitPath(fltr.FieldSpec.Path) fltr.path = utils.PathSplitter(fltr.FieldSpec.Path)
if err := fltr.filter(obj); err != nil { err := fltr.filter(obj)
if err != nil {
s, _ := obj.String() s, _ := obj.String()
return nil, errors.WrapPrefixf(err, return nil, errors.WrapPrefixf(err,
"obj '%s' at path '%v'", s, fltr.FieldSpec.Path) "considering field '%s' of object\n%v", fltr.FieldSpec.Path, s)
} }
return obj, nil return obj, nil
} }
// Recursively called.
func (fltr Filter) filter(obj *yaml.RNode) error { func (fltr Filter) filter(obj *yaml.RNode) error {
if len(fltr.path) == 0 { if len(fltr.path) == 0 {
// found the field -- set its value // found the field -- set its value
@@ -56,25 +69,30 @@ func (fltr Filter) filter(obj *yaml.RNode) error {
} }
switch obj.YNode().Kind { switch obj.YNode().Kind {
case yaml.SequenceNode: case yaml.SequenceNode:
return fltr.seq(obj) return fltr.handleSequence(obj)
case yaml.MappingNode: case yaml.MappingNode:
return fltr.field(obj) return fltr.handleMap(obj)
case yaml.AliasNode:
return fltr.filter(yaml.NewRNode(obj.YNode().Alias))
default: default:
return errors.Errorf("expected sequence or mapping node") return errors.Errorf("expected sequence or mapping node")
} }
} }
// field calls filter on the field matching the next path element // handleMap calls filter on the map field matching the next path element
func (fltr Filter) field(obj *yaml.RNode) error { func (fltr Filter) handleMap(obj *yaml.RNode) error {
fieldName, isSeq := isSequenceField(fltr.path[0]) fieldName, isSeq := isSequenceField(fltr.path[0])
if fieldName == "" {
return fmt.Errorf("cannot set or create an empty field name")
}
// lookup the field matching the next path element // lookup the field matching the next path element
var lookupField yaml.Filter var operation yaml.Filter
var kind yaml.Kind var kind yaml.Kind
tag := yaml.NodeTagEmpty tag := yaml.NodeTagEmpty
switch { switch {
case !fltr.FieldSpec.CreateIfNotPresent || fltr.CreateKind == 0 || isSeq: case !fltr.FieldSpec.CreateIfNotPresent || fltr.CreateKind == 0 || isSeq:
// dont' create the field if we don't find it // don't create the field if we don't find it
lookupField = yaml.Lookup(fieldName) operation = yaml.Lookup(fieldName)
if isSeq { if isSeq {
// The query path thinks this field should be a sequence; // The query path thinks this field should be a sequence;
// accept this hint for use later if the tag is NodeTagNull. // accept this hint for use later if the tag is NodeTagNull.
@@ -82,21 +100,25 @@ func (fltr Filter) field(obj *yaml.RNode) error {
} }
case len(fltr.path) <= 1: case len(fltr.path) <= 1:
// create the field if it is missing: use the provided node kind // create the field if it is missing: use the provided node kind
lookupField = yaml.LookupCreate(fltr.CreateKind, fieldName) operation = yaml.LookupCreate(fltr.CreateKind, fieldName)
kind = fltr.CreateKind kind = fltr.CreateKind
tag = fltr.CreateTag tag = fltr.CreateTag
default: default:
// create the field if it is missing: must be a mapping node // create the field if it is missing: must be a mapping node
lookupField = yaml.LookupCreate(yaml.MappingNode, fieldName) operation = yaml.LookupCreate(yaml.MappingNode, fieldName)
kind = yaml.MappingNode kind = yaml.MappingNode
tag = yaml.NodeTagMap tag = yaml.NodeTagMap
} }
// locate (or maybe create) the field // locate (or maybe create) the field
field, err := obj.Pipe(lookupField) field, err := obj.Pipe(operation)
if err != nil || field == nil { if err != nil {
return errors.WrapPrefixf(err, "fieldName: %s", fieldName) return errors.WrapPrefixf(err, "fieldName: %s", fieldName)
} }
if field == nil {
// No error if field not found.
return nil
}
// if the value exists, but is null and kind is set, // if the value exists, but is null and kind is set,
// then change it to the creation type // then change it to the creation type
@@ -114,7 +136,7 @@ func (fltr Filter) field(obj *yaml.RNode) error {
} }
// seq calls filter on all sequence elements // seq calls filter on all sequence elements
func (fltr Filter) seq(obj *yaml.RNode) error { func (fltr Filter) handleSequence(obj *yaml.RNode) error {
if err := obj.VisitElements(func(node *yaml.RNode) error { if err := obj.VisitElements(func(node *yaml.RNode) error {
// recurse on each element -- re-allocating a Filter is // recurse on each element -- re-allocating a Filter is
// not strictly required, but is more consistent with field // not strictly required, but is more consistent with field
@@ -125,16 +147,14 @@ func (fltr Filter) seq(obj *yaml.RNode) error {
return errors.WrapPrefixf(err, return errors.WrapPrefixf(err,
"visit traversal on path: %v", fltr.path) "visit traversal on path: %v", fltr.path)
} }
return nil return nil
} }
// isSequenceField returns true if the path element is for a sequence field. // isSequenceField returns true if the path element is for a sequence field.
// isSequence also returns the path element with the '[]' suffix trimmed // isSequence also returns the path element with the '[]' suffix trimmed
func isSequenceField(name string) (string, bool) { func isSequenceField(name string) (string, bool) {
isSeq := strings.HasSuffix(name, "[]") shorter := strings.TrimSuffix(name, "[]")
name = strings.TrimSuffix(name, "[]") return shorter, shorter != name
return name, isSeq
} }
// isMatchGVK returns true if the fs.GVK matches the obj GVK. // isMatchGVK returns true if the fs.GVK matches the obj GVK.
@@ -163,18 +183,3 @@ func isMatchGVK(fs types.FieldSpec, obj *yaml.RNode) (bool, error) {
return true, nil return true, nil
} }
func splitPath(path string) []string {
ps := strings.Split(path, "/")
var res []string
res = append(res, ps[0])
for i := 1; i < len(ps); i++ {
lastIndex := len(res) - 1
if strings.HasSuffix(res[lastIndex], "\\") {
res[lastIndex] = strings.TrimSuffix(res[lastIndex], "\\") + "/" + ps[i]
} else {
res = append(res, ps[i])
}
}
return res
}

View File

@@ -15,206 +15,243 @@ import (
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
type TestCase struct { func TestFilter_Filter(t *testing.T) {
name string testCases := map[string]struct {
input string input string
expected string expected string
filter fieldspec.Filter filter fieldspec.Filter
fieldSpec string fieldSpec string
error string error string
} }{
"path not found": {
var tests = []TestCase{ fieldSpec: `
{
name: "update",
fieldSpec: `
path: a/b path: a/b
group: foo group: foo
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo
kind: Bar
xxx:
`,
expected: `
apiVersion: foo
kind: Bar
xxx:
`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
"empty path": {
fieldSpec: `
group: foo
kind: Bar
`,
input: `
apiVersion: foo
kind: Bar
xxx:
`,
expected: `
apiVersion: foo
kind: Bar
xxx:
`,
error: `considering field '' of object
apiVersion: foo
kind: Bar
xxx:
: cannot set or create an empty field name`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
},
"update": {
fieldSpec: `
path: a/b
group: foo
kind: Bar
`,
input: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: a:
b: c b: c
`, `,
expected: ` expected: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: a:
b: e b: e
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
},
}, },
},
{ "update-kind-not-match": {
name: "update-kind-not-match", fieldSpec: `
fieldSpec: `
path: a/b path: a/b
group: foo group: foo
kind: Bar1 kind: Bar1
`, `,
input: ` input: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar2 kind: Bar2
a: a:
b: c b: c
`, `,
expected: ` expected: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar2 kind: Bar2
a: a:
b: c b: c
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
},
}, },
},
{ "update-group-not-match": {
name: "update-group-not-match", fieldSpec: `
fieldSpec: `
path: a/b path: a/b
group: foo1 group: foo1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo2/v1beta1 apiVersion: foo2/v1beta1
kind: Bar kind: Bar
a: a:
b: c b: c
`, `,
expected: ` expected: `
apiVersion: foo2/v1beta1 apiVersion: foo2/v1beta1
kind: Bar kind: Bar
a: a:
b: c b: c
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
},
}, },
},
{ "update-version-not-match": {
name: "update-version-not-match", fieldSpec: `
fieldSpec: `
path: a/b path: a/b
group: foo group: foo
version: v1beta1 version: v1beta1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo/v1beta2 apiVersion: foo/v1beta2
kind: Bar kind: Bar
a: a:
b: c b: c
`, `,
expected: ` expected: `
apiVersion: foo/v1beta2 apiVersion: foo/v1beta2
kind: Bar kind: Bar
a: a:
b: c b: c
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
},
}, },
},
{ "bad-version": {
name: "bad-version", fieldSpec: `
fieldSpec: `
path: a/b path: a/b
group: foo group: foo
version: v1beta1 version: v1beta1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo/v1beta2/something apiVersion: foo/v1beta2/something
kind: Bar kind: Bar
a: a:
b: c b: c
`, `,
expected: ` expected: `
apiVersion: foo/v1beta2/something apiVersion: foo/v1beta2/something
kind: Bar kind: Bar
a: a:
b: c b: c
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
},
}, },
},
{ "bad-meta": {
name: "bad-meta", fieldSpec: `
fieldSpec: `
path: a/b path: a/b
group: foo group: foo
version: v1beta1 version: v1beta1
kind: Bar kind: Bar
`, `,
input: ` input: `
a: a:
b: c b: c
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
},
error: "missing Resource metadata",
}, },
error: "missing Resource metadata",
},
{ "miss-match-type": {
name: "miss-match-type", fieldSpec: `
fieldSpec: `
path: a/b/c path: a/b/c
kind: Bar kind: Bar
`, `,
input: ` input: `
kind: Bar kind: Bar
a: a:
b: a b: a
`, `,
error: "obj 'kind: Bar\na:\n b: a\n' at path 'a/b/c': " + error: `considering field 'a/b/c' of object
"expected sequence or mapping node", kind: Bar
filter: fieldspec.Filter{ a:
SetValue: filtersutil.SetScalar("e"), b: a
: expected sequence or mapping node`,
filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"),
},
}, },
},
{ "add": {
name: "add", fieldSpec: `
fieldSpec: `
path: a/b/c/d path: a/b/c/d
group: foo group: foo
create: true create: true
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: {} a: {}
`, `,
expected: ` expected: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: {b: {c: {d: e}}} a: {b: {c: {d: e}}}
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
},
{ "update-in-sequence": {
name: "update-in-sequence", fieldSpec: `
fieldSpec: `
path: a/b[]/c/d path: a/b[]/c/d
group: foo group: foo
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: a:
@@ -222,7 +259,7 @@ a:
- c: - c:
d: a d: a
`, `,
expected: ` expected: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: a:
@@ -230,244 +267,237 @@ a:
- c: - c:
d: e d: e
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
},
}, },
},
// Don't create a sequence // Don't create a sequence
{ "empty-sequence-no-create": {
name: "empty-sequence-no-create", fieldSpec: `
fieldSpec: `
path: a/b[]/c/d path: a/b[]/c/d
group: foo group: foo
create: true create: true
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: {} a: {}
`, `,
expected: ` expected: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: {} a: {}
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
},
// Create a new field for an element in a sequence // Create a new field for an element in a sequence
{ "empty-sequence-create": {
name: "empty-sequence-create", fieldSpec: `
fieldSpec: `
path: a/b[]/c/d path: a/b[]/c/d
group: foo group: foo
create: true create: true
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: a:
b: b:
- c: {} - c: {}
`, `,
expected: ` expected: `
apiVersion: foo/v1beta1 apiVersion: foo/v1beta1
kind: Bar kind: Bar
a: a:
b: b:
- c: {d: e} - c: {d: e}
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
},
{ "group v1": {
name: "group v1", fieldSpec: `
fieldSpec: `
path: a/b path: a/b
group: v1 group: v1
create: true create: true
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
`, `,
expected: ` expected: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
},
{ "version v1": {
name: "version v1", fieldSpec: `
fieldSpec: `
path: a/b path: a/b
version: v1 version: v1
create: true create: true
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
`, `,
expected: ` expected: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
a: a:
b: e b: e
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("e"), SetValue: filtersutil.SetScalar("e"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
},
{ "successfully set field on array entry no sequence hint": {
name: "successfully set field on array entry no sequence hint", fieldSpec: `
fieldSpec: `
path: spec/containers/image path: spec/containers/image
version: v1 version: v1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
spec: spec:
containers: containers:
- image: foo - image: foo
`, `,
expected: ` expected: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
spec: spec:
containers: containers:
- image: bar - image: bar
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("bar"), SetValue: filtersutil.SetScalar("bar"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
},
{ "successfully set field on array entry with sequence hint": {
name: "successfully set field on array entry with sequence hint", fieldSpec: `
fieldSpec: `
path: spec/containers[]/image path: spec/containers[]/image
version: v1 version: v1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
spec: spec:
containers: containers:
- image: foo - image: foo
`, `,
expected: ` expected: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
spec: spec:
containers: containers:
- image: bar - image: bar
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("bar"), SetValue: filtersutil.SetScalar("bar"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
}, "failure to set field on array entry with sequence hint in path": {
{ fieldSpec: `
name: "failure to set field on array entry with sequence hint in path",
fieldSpec: `
path: spec/containers[]/image path: spec/containers[]/image
version: v1 version: v1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
spec: spec:
containers: containers:
`, `,
expected: ` expected: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
spec: spec:
containers: [] containers: []
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("bar"), SetValue: filtersutil.SetScalar("bar"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
},
{ "failure to set field on array entry, no sequence hint in path": {
name: "failure to set field on array entry, no sequence hint in path", fieldSpec: `
fieldSpec: `
path: spec/containers/image path: spec/containers/image
version: v1 version: v1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
spec: spec:
containers: containers:
`, `,
expected: ` expected: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
spec: spec:
containers: containers:
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("bar"), SetValue: filtersutil.SetScalar("bar"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
}, "fieldname with slash '/'": {
{ fieldSpec: `
name: "filedname with slash '/'",
fieldSpec: `
path: a/b\/c/d path: a/b\/c/d
version: v1 version: v1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
a: a:
b/c: b/c:
d: foo d: foo
`, `,
expected: ` expected: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
a: a:
b/c: b/c:
d: bar d: bar
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("bar"), SetValue: filtersutil.SetScalar("bar"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
}, "fieldname with multiple '/'": {
{ fieldSpec: `
name: "filedname with multiple '/'",
fieldSpec: `
path: a/b\/c/d\/e/f path: a/b\/c/d\/e/f
version: v1 version: v1
kind: Bar kind: Bar
`, `,
input: ` input: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
a: a:
@@ -475,7 +505,7 @@ a:
d/e: d/e:
f: foo f: foo
`, `,
expected: ` expected: `
apiVersion: v1 apiVersion: v1
kind: Bar kind: Bar
a: a:
@@ -483,25 +513,24 @@ a:
d/e: d/e:
f: bar f: bar
`, `,
filter: fieldspec.Filter{ filter: fieldspec.Filter{
SetValue: filtersutil.SetScalar("bar"), SetValue: filtersutil.SetScalar("bar"),
CreateKind: yaml.ScalarNode, CreateKind: yaml.ScalarNode,
},
}, },
}, }
}
func TestFilter_Filter(t *testing.T) { for n := range testCases {
for i := range tests { tc := testCases[n]
test := tests[i] t.Run(n, func(t *testing.T) {
t.Run(test.name, func(t *testing.T) { err := yaml.Unmarshal([]byte(tc.fieldSpec), &tc.filter.FieldSpec)
err := yaml.Unmarshal([]byte(test.fieldSpec), &test.filter.FieldSpec)
if !assert.NoError(t, err) { if !assert.NoError(t, err) {
t.FailNow() t.FailNow()
} }
out := &bytes.Buffer{} out := &bytes.Buffer{}
rw := &kio.ByteReadWriter{ rw := &kio.ByteReadWriter{
Reader: bytes.NewBufferString(test.input), Reader: bytes.NewBufferString(tc.input),
Writer: out, Writer: out,
OmitReaderAnnotations: true, OmitReaderAnnotations: true,
} }
@@ -509,11 +538,11 @@ func TestFilter_Filter(t *testing.T) {
// run the filter // run the filter
err = kio.Pipeline{ err = kio.Pipeline{
Inputs: []kio.Reader{rw}, Inputs: []kio.Reader{rw},
Filters: []kio.Filter{kio.FilterAll(test.filter)}, Filters: []kio.Filter{kio.FilterAll(tc.filter)},
Outputs: []kio.Writer{rw}, Outputs: []kio.Writer{rw},
}.Execute() }.Execute()
if test.error != "" { if tc.error != "" {
if !assert.EqualError(t, err, test.error) { if !assert.EqualError(t, err, tc.error) {
t.FailNow() t.FailNow()
} }
// stop rest of test // stop rest of test
@@ -526,7 +555,7 @@ func TestFilter_Filter(t *testing.T) {
// check results // check results
if !assert.Equal(t, if !assert.Equal(t,
strings.TrimSpace(test.expected), strings.TrimSpace(tc.expected),
strings.TrimSpace(out.String())) { strings.TrimSpace(out.String())) {
t.FailNow() t.FailNow()
} }

View File

@@ -1,21 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filtersutil
import (
"sort"
)
// SortedMapKeys returns a sorted slice of keys to the given map.
// Writing this function never gets old.
func SortedMapKeys(m map[string]string) []string {
keys := make([]string, len(m))
i := 0
for k := range m {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}

View File

@@ -25,7 +25,7 @@ type Filter struct {
var _ kio.Filter = Filter{} var _ kio.Filter = Filter{}
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
keys := filtersutil.SortedMapKeys(f.Labels) keys := yaml.SortedMapKeys(f.Labels)
_, err := kio.FilterAll(yaml.FilterFunc( _, err := kio.FilterAll(yaml.FilterFunc(
func(node *yaml.RNode) (*yaml.RNode, error) { func(node *yaml.RNode) (*yaml.RNode, error) {
for _, k := range keys { for _, k := range keys {

View File

@@ -5,100 +5,189 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filters/fieldspec" "sigs.k8s.io/kustomize/api/filters/fieldspec"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"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/api/types" "sigs.k8s.io/kustomize/api/types"
kyaml_filtersutil "sigs.k8s.io/kustomize/kyaml/filtersutil" "sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
// Filter will update the name reference // Filter updates a name references.
type Filter struct { type Filter struct {
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` // Referrer refers to another resource X by X's name.
Referrer *resource.Resource // E.g. A Deployment can refer to a ConfigMap.
Target resid.Gvk // The Deployment is the Referrer,
// the ConfigMap is the ReferralTarget.
// This filter seeks to repair the reference in Deployment, given
// that the ConfigMap's name may have changed.
Referrer *resource.Resource
// NameFieldToUpdate is the field in the Referrer
// that holds the name requiring an update.
// This is the field to write.
NameFieldToUpdate types.FieldSpec
// ReferralTarget is the source of the new value for
// the name, always in the 'metadata/name' field.
// This is the field to read.
ReferralTarget resid.Gvk
// Set of resources to scan to find the ReferralTarget.
ReferralCandidates resmap.ResMap ReferralCandidates resmap.ResMap
isRoleRef bool
} }
// At time of writing, in practice this is called with a slice with only
// one entry, the node also referred to be the resource in the Referrer field.
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes) return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
} }
// The node passed in here is the same node as held in Referrer;
// that's how the referrer's name field is updated.
// Currently, however, this filter still needs the extra methods on Referrer
// to consult things like the resource Id, its namespace, etc.
// TODO(3455): No filter should use the Resource api; all information
// about names should come from annotations, with helper methods
// on the RNode object. Resource should get stupider, RNode smarter.
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) { func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
err := node.PipeE(fieldspec.Filter{ if err := f.confirmNodeMatchesReferrer(node); err != nil {
FieldSpec: f.FieldSpec, // sanity check.
return nil, err
}
if err := node.PipeE(fieldspec.Filter{
FieldSpec: f.NameFieldToUpdate,
SetValue: f.set, SetValue: f.set,
}) }); err != nil {
return node, err return nil, errors.Wrapf(
err, "updating name reference in '%s' field of '%s'",
f.NameFieldToUpdate.Path, f.Referrer.CurId().String())
}
return node, nil
} }
// This function is called on the node found at FieldSpec.Path.
// It's some node in the Referrer.
func (f Filter) set(node *yaml.RNode) error { func (f Filter) set(node *yaml.RNode) error {
if yaml.IsMissingOrNull(node) { if yaml.IsMissingOrNull(node) {
return nil return nil
} }
if strings.HasSuffix(f.FieldSpec.Path, "roleRef/name") {
f.isRoleRef = true
}
switch node.YNode().Kind { switch node.YNode().Kind {
case yaml.ScalarNode: case yaml.ScalarNode:
return f.setScalar(node) return f.setScalar(node)
case yaml.MappingNode: case yaml.MappingNode:
// Kind: ValidatingWebhookConfiguration
// FieldSpec is webhooks/clientConfig/service
return f.setMapping(node) return f.setMapping(node)
case yaml.SequenceNode: case yaml.SequenceNode:
return f.setSequence(node) return applyFilterToSeq(seqFilter{
setScalarFn: f.setScalar,
setMappingFn: f.setMapping,
}, node)
default: default:
return fmt.Errorf( return fmt.Errorf("node must be a scalar, sequence or map")
"node is expected to be either a string or a slice of string or a map of string")
} }
} }
func (f Filter) setSequence(node *yaml.RNode) error { // This method used when NameFieldToUpdate doesn't lead to
return applyFilterToSeq(seqFilter{ // one scalar field (typically called 'name'), but rather
setScalarFn: f.setScalar, // leads to a map field (called anything). In this case we
setMappingFn: f.setMapping, // must complete the field path, looking for both a 'name'
}, node) // and a 'namespace' field to help select the proper
// ReferralTarget to read the name and namespace from.
func (f Filter) setMapping(node *yaml.RNode) error {
if node.YNode().Kind != yaml.MappingNode {
return fmt.Errorf("expect a mapping node")
}
nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"})
if err != nil {
return errors.Wrap(err, "trying to match 'name' field")
}
if nameNode == nil {
// This is a _configuration_ error; the field path
// specified in NameFieldToUpdate.Path doesn't resolve
// to a map with a 'name' field, so we have no idea what
// field to update with a new name.
return fmt.Errorf("path config error; no 'name' field in node")
}
candidates, err := f.filterMapCandidatesByNamespace(node)
if err != nil {
return err
}
oldName := nameNode.YNode().Value
referral, err := f.selectReferral(oldName, candidates)
if err != nil || referral == nil {
// Nil referral means nothing to do.
return err
}
f.recordTheReferral(referral)
if referral.GetName() == oldName && referral.GetNamespace() == "" {
// The name has not changed, nothing to do.
return nil
}
if err = node.PipeE(yaml.FieldSetter{
Name: "name",
StringValue: referral.GetName(),
}); err != nil {
return err
}
if referral.GetNamespace() == "" {
// Don't write an empty string into the namespace field, as
// it should not replace the value "default". The empty
// string is handled as a wild card here, not as an implicit
// specification of the "default" k8s namespace.
return nil
}
return node.PipeE(yaml.FieldSetter{
Name: "namespace",
StringValue: referral.GetNamespace(),
})
} }
func (f Filter) setMapping(node *yaml.RNode) error { func (f Filter) filterMapCandidatesByNamespace(
return setNameAndNs( node *yaml.RNode) ([]*resource.Resource, error) {
node, namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"})
f.Referrer, if err != nil {
f.Target, return nil, errors.Wrap(err, "trying to match 'namespace' field")
f.ReferralCandidates, }
f.isRoleRef, if namespaceNode == nil {
) return f.ReferralCandidates.Resources(), nil
}
namespace := namespaceNode.YNode().Value
nsMap := f.ReferralCandidates.GroupedByOriginalNamespace()
if candidates, ok := nsMap[namespace]; ok {
return candidates, nil
}
nsMap = f.ReferralCandidates.GroupedByCurrentNamespace()
// This could be nil, or an empty list.
return nsMap[namespace], nil
} }
func (f Filter) setScalar(node *yaml.RNode) error { func (f Filter) setScalar(node *yaml.RNode) error {
newValue, err := getSimpleNameField( referral, err := f.selectReferral(
node.YNode().Value, node.YNode().Value, f.ReferralCandidates.Resources())
f.Referrer, if err != nil || referral == nil {
f.Target, // Nil referral means nothing to do.
f.ReferralCandidates,
f.ReferralCandidates.Resources(),
f.isRoleRef,
)
if err != nil {
return err return err
} }
err = filtersutil.SetScalar(newValue)(node) f.recordTheReferral(referral)
if err != nil { if referral.GetName() == node.YNode().Value {
return err // The name has not changed, nothing to do.
return nil
} }
return nil return node.PipeE(yaml.FieldSetter{StringValue: referral.GetName()})
}
// In the resource, make a note that it is referred to by the Referrer.
func (f Filter) recordTheReferral(referral *resource.Resource) {
referral.AppendRefBy(f.Referrer.CurId())
} }
// getRoleRefGvk returns a Gvk in the roleRef field. Return error // getRoleRefGvk returns a Gvk in the roleRef field. Return error
// if the roleRef, roleRef/apiGroup or roleRef/kind is missing. // if the roleRef, roleRef/apiGroup or roleRef/kind is missing.
func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) { func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) {
n, err := kyaml_filtersutil.GetRNode(res) n, err := filtersutil.GetRNode(res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -114,14 +203,16 @@ func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) {
return nil, err return nil, err
} }
if apiGroup.IsNil() { if apiGroup.IsNil() {
return nil, fmt.Errorf("apiGroup cannot be found in roleRef %s", roleRef.MustString()) return nil, fmt.Errorf(
"apiGroup cannot be found in roleRef %s", roleRef.MustString())
} }
kind, err := roleRef.Pipe(yaml.Lookup("kind")) kind, err := roleRef.Pipe(yaml.Lookup("kind"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if kind.IsNil() { if kind.IsNil() {
return nil, fmt.Errorf("kind cannot be found in roleRef %s", roleRef.MustString()) return nil, fmt.Errorf(
"kind cannot be found in roleRef %s", roleRef.MustString())
} }
return &resid.Gvk{ return &resid.Gvk{
Group: apiGroup.YNode().Value, Group: apiGroup.YNode().Value,
@@ -129,169 +220,183 @@ func getRoleRefGvk(res json.Marshaler) (*resid.Gvk, error) {
}, nil }, nil
} }
func filterReferralCandidates( // sieveFunc returns true if the resource argument satisfies some criteria.
referrer *resource.Resource, type sieveFunc func(*resource.Resource) bool
matches []*resource.Resource,
target resid.Gvk, // doSieve uses a function to accept or ignore resources from a list.
) []*resource.Resource { // If list is nil, returns immediately.
var ret []*resource.Resource // It's a filter obviously, but that term is overloaded here.
for _, m := range matches { func doSieve(list []*resource.Resource, fn sieveFunc) (s []*resource.Resource) {
// If target kind is not ServiceAccount, we shouldn't consider condidates which for _, r := range list {
// doesn't have same namespace. if fn(r) {
if target.Kind != "ServiceAccount" && m.GetNamespace() != referrer.GetNamespace() { s = append(s, r)
continue
} }
if !referrer.PrefixesSuffixesEquals(m) {
continue
}
ret = append(ret, m)
} }
return ret return
} }
// selectReferral picks the referral among a subset of candidates. func acceptAll(r *resource.Resource) bool {
// It returns the current name and namespace of the selected candidate. return true
// Note that the content of the referricalCandidateSubset slice is most of the time }
// identical to the referralCandidates resmap. Still in some cases, such
// as ClusterRoleBinding, the subset only contains the resources of a specific func previousNameMatches(name string) sieveFunc {
// namespace. return func(r *resource.Resource) bool {
func selectReferral( for _, id := range r.PrevIds() {
oldName string, if id.Name == name {
referrer *resource.Resource, return true
target resid.Gvk,
referralCandidates resmap.ResMap,
referralCandidateSubset []*resource.Resource,
isRoleRef bool) (string, string, error) {
var roleRefGvk *resid.Gvk
if isRoleRef {
var err error
roleRefGvk, err = getRoleRefGvk(referrer)
if err != nil {
return "", "", err
}
}
for _, res := range referralCandidateSubset {
id := res.OrgId()
// If the we are processing a roleRef, the apiGroup and Kind in the
// roleRef are needed to be considered.
if (!isRoleRef || id.IsSelected(roleRefGvk)) &&
id.IsSelected(&target) && res.GetOriginalName() == oldName {
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
// If there's more than one match,
// filter the matches by prefix and suffix
if len(matches) > 1 {
filteredMatches := filterReferralCandidates(referrer, matches, target)
if len(filteredMatches) > 1 {
return "", "", fmt.Errorf(
"multiple matches for %s:\n %v",
id, getIds(filteredMatches))
}
// Check is the match the resource we are working on
if len(filteredMatches) == 0 || res != filteredMatches[0] {
continue
}
} }
// In the resource, note that it is referenced }
// by the referrer. return false
res.AppendRefBy(referrer.CurId()) }
// Return transformed name of the object, }
// complete with prefixes, hashes, etc.
return res.GetName(), res.GetNamespace(), nil func previousIdSelectedByGvk(gvk *resid.Gvk) sieveFunc {
return func(r *resource.Resource) bool {
for _, id := range r.PrevIds() {
if id.IsSelected(gvk) {
return true
}
}
return false
}
}
// If the we are updating a 'roleRef/name' field, the 'apiGroup' and 'kind'
// fields in the same 'roleRef' map must be considered.
// If either object is cluster-scoped (!IsNamespaceableKind), there
// can be a referral.
// E.g. a RoleBinding (which exists in a namespace) can refer
// to a ClusterRole (cluster-scoped) object.
// https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole
// Likewise, a ClusterRole can refer to a Secret (in a namespace).
// Objects in different namespaces generally cannot refer to other
// with some exceptions (e.g. RoleBinding and ServiceAccount are both
// namespaceable, but the former can refer to accounts in other namespaces).
func (f Filter) roleRefFilter() sieveFunc {
if !strings.HasSuffix(f.NameFieldToUpdate.Path, "roleRef/name") {
return acceptAll
}
roleRefGvk, err := getRoleRefGvk(f.Referrer)
if err != nil {
return acceptAll
}
return previousIdSelectedByGvk(roleRefGvk)
}
func prefixSuffixEquals(other resource.ResCtx) sieveFunc {
return func(r *resource.Resource) bool {
return r.PrefixesSuffixesEquals(other)
}
}
func (f Filter) sameCurrentNamespaceAsReferrer() sieveFunc {
referrerCurId := f.Referrer.CurId()
if !referrerCurId.IsNamespaceableKind() {
// If the referrer is cluster-scoped, let anything through.
return acceptAll
}
return func(r *resource.Resource) bool {
if !r.CurId().IsNamespaceableKind() {
// Allow cluster-scoped through.
return true
}
if r.GetKind() == "ServiceAccount" {
// Allow service accounts through, even though they
// are in a namespace. A RoleBinding in another namespace
// can reference them.
return true
}
return referrerCurId.IsNsEquals(r.CurId())
}
}
// selectReferral picks the best referral from a list of candidates.
func (f Filter) selectReferral(
// The name referral that may need to be updated.
oldName string,
candidates []*resource.Resource) (*resource.Resource, error) {
candidates = doSieve(candidates, previousNameMatches(oldName))
candidates = doSieve(candidates, previousIdSelectedByGvk(&f.ReferralTarget))
candidates = doSieve(candidates, f.roleRefFilter())
candidates = doSieve(candidates, f.sameCurrentNamespaceAsReferrer())
if len(candidates) == 1 {
return candidates[0], nil
}
candidates = doSieve(candidates, prefixSuffixEquals(f.Referrer))
if len(candidates) == 1 {
return candidates[0], nil
}
if len(candidates) == 0 {
return nil, nil
}
if allNamesAreTheSame(candidates) {
// Just take the first one.
return candidates[0], nil
}
ids := getIds(candidates)
f.failureDetails(candidates)
return nil, fmt.Errorf(" found multiple possible referrals: %s", ids)
}
func (f Filter) failureDetails(resources []*resource.Resource) {
fmt.Printf(
"\n**** Too many possible referral targets to referrer:\n%s\n",
f.Referrer.MustYaml())
for i, r := range resources {
fmt.Printf(
"--- possible referral %d:\n%s", i, r.MustYaml())
fmt.Println("------")
}
}
func allNamesAreTheSame(resources []*resource.Resource) bool {
name := resources[0].GetName()
for i := 1; i < len(resources); i++ {
if name != resources[i].GetName() {
return false
} }
} }
return true
return oldName, "", nil
} }
// utility function to replace a simple string by the new name func getIds(rs []*resource.Resource) string {
func getSimpleNameField(
oldName string,
referrer *resource.Resource,
target resid.Gvk,
referralCandidates resmap.ResMap,
referralCandidateSubset []*resource.Resource,
isRoleRef bool) (string, error) {
newName, _, err := selectReferral(oldName, referrer, target,
referralCandidates, referralCandidateSubset, isRoleRef)
return newName, err
}
func getIds(rs []*resource.Resource) []string {
var result []string var result []string
for _, r := range rs { for _, r := range rs {
result = append(result, r.CurId().String()+"\n") result = append(result, r.CurId().String())
} }
return result return strings.Join(result, ", ")
} }
// utility function to replace name field within a map RNode func checkEqual(k, a, b string) error {
// and leverage the namespace field. if a != b {
func setNameAndNs( return fmt.Errorf(
in *yaml.RNode, "node-referrerOriginal '%s' mismatch '%s' != '%s'",
referrer *resource.Resource, k, a, b)
target resid.Gvk, }
referralCandidates resmap.ResMap, return nil
isRoleRef bool) error { }
if in.YNode().Kind != yaml.MappingNode { func (f Filter) confirmNodeMatchesReferrer(node *yaml.RNode) error {
return fmt.Errorf("expect a mapping node") meta, err := node.GetMeta()
} if err != nil {
return err
// Get name field }
nameNode, err := in.Pipe(yaml.FieldMatcher{ gvk := f.Referrer.GetGvk()
Name: "name", if err = checkEqual(
}) "APIVersion", meta.APIVersion, gvk.ApiVersion()); err != nil {
if err != nil || nameNode == nil { return err
return fmt.Errorf("cannot find field 'name' in node") }
} if err = checkEqual(
"Kind", meta.Kind, gvk.Kind); err != nil {
// Get namespace field return err
namespaceNode, err := in.Pipe(yaml.FieldMatcher{ }
Name: "namespace", if err = checkEqual(
}) "Name", meta.Name, f.Referrer.GetName()); err != nil {
if err != nil { return err
return fmt.Errorf("error when find field 'namespace'") }
} if err = checkEqual(
"Namespace", meta.Namespace, f.Referrer.GetNamespace()); err != nil {
// check is namespace matched return err
// name will bot be updated if the namespace doesn't match
subset := referralCandidates.Resources()
if namespaceNode != nil {
namespace := namespaceNode.YNode().Value
bynamespace := referralCandidates.GroupedByOriginalNamespace()
if _, ok := bynamespace[namespace]; !ok {
return nil
}
subset = bynamespace[namespace]
}
oldName := nameNode.YNode().Value
newname, newnamespace, err := selectReferral(oldName, referrer, target,
referralCandidates, subset, isRoleRef)
if err != nil {
return err
}
if (newname == oldName) && (newnamespace == "") {
// no candidate found.
return nil
}
// set name
in.Pipe(yaml.FieldSetter{
Name: "name",
StringValue: newname,
})
if newnamespace != "" {
// We don't want value "" to replace value "default" since
// the empty string is handled as a wild card here not default namespace
// by kubernetes.
in.Pipe(yaml.FieldSetter{
Name: "namespace",
StringValue: newnamespace,
})
} }
return nil return nil
} }

View File

@@ -14,14 +14,14 @@ import (
func TestNamerefFilter(t *testing.T) { func TestNamerefFilter(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
input string referrerOriginal string
candidates string candidates string
expected string referrerFinal string
filter Filter filter Filter
originalNames []string originalNames []string
}{ }{
"simple scalar": { "simple scalar": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -40,8 +40,8 @@ kind: NotSecret
metadata: metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", ""}, originalNames: []string{"oldName", "newName2"},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -50,8 +50,8 @@ ref:
name: newName name: newName
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -59,7 +59,7 @@ ref:
}, },
}, },
"sequence": { "sequence": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -79,8 +79,8 @@ kind: NotSecret
metadata: metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName1", ""}, originalNames: []string{"oldName1", "newName2"},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -90,8 +90,8 @@ seq:
- oldName2 - oldName2
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "seq"}, NameFieldToUpdate: types.FieldSpec{Path: "seq"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -99,7 +99,7 @@ seq:
}, },
}, },
"mapping": { "mapping": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -118,8 +118,8 @@ kind: NotSecret
metadata: metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", ""}, originalNames: []string{"oldName", "newName2"},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -128,8 +128,8 @@ map:
name: newName name: newName
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "map"}, NameFieldToUpdate: types.FieldSpec{Path: "map"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -137,40 +137,47 @@ map:
}, },
}, },
"mapping with namespace": { "mapping with namespace": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: dep name: dep
namespace: someNs
map: map:
name: oldName name: oldName
namespace: oldNs namespace: someNs
`, `,
candidates: ` candidates: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Secret kind: Secret
metadata: metadata:
name: newName name: newName
namespace: oldNs namespace: someNs
--- ---
apiVersion: apps/v1 apiVersion: apps/v1
kind: NotSecret kind: NotSecret
metadata: metadata:
name: newName2 name: newName2
---
apiVersion: apps/v1
kind: Secret
metadata:
name: thirdName
`, `,
originalNames: []string{"oldName", ""}, originalNames: []string{"oldName", "oldName", "oldName"},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: dep name: dep
namespace: someNs
map: map:
name: newName name: newName
namespace: oldNs namespace: someNs
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "map"}, NameFieldToUpdate: types.FieldSpec{Path: "map"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -178,7 +185,7 @@ map:
}, },
}, },
"null value": { "null value": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -197,8 +204,8 @@ kind: NotSecret
metadata: metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", ""}, originalNames: []string{"oldName", "newName2"},
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -207,8 +214,8 @@ map:
name: null name: null
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "map"}, NameFieldToUpdate: types.FieldSpec{Path: "map"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -220,7 +227,7 @@ map:
for tn, tc := range testCases { for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) { t.Run(tn, func(t *testing.T) {
factory := provider.NewDefaultDepProvider().GetResourceFactory() factory := provider.NewDefaultDepProvider().GetResourceFactory()
referrer, err := factory.FromBytes([]byte(tc.input)) referrer, err := factory.FromBytes([]byte(tc.referrerOriginal))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -236,10 +243,10 @@ map:
candidates := resMapFactory.FromResourceSlice(candidatesRes) candidates := resMapFactory.FromResourceSlice(candidatesRes)
tc.filter.ReferralCandidates = candidates tc.filter.ReferralCandidates = candidates
result := filtertest_test.RunFilter(t, tc.referrerOriginal, tc.filter)
if !assert.Equal(t, if !assert.Equal(t,
strings.TrimSpace(tc.expected), strings.TrimSpace(tc.referrerFinal),
strings.TrimSpace( strings.TrimSpace(result)) {
filtertest_test.RunFilter(t, tc.input, tc.filter))) {
t.FailNow() t.FailNow()
} }
}) })
@@ -248,14 +255,14 @@ map:
func TestNamerefFilterUnhappy(t *testing.T) { func TestNamerefFilterUnhappy(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
input string referrerOriginal string
candidates string candidates string
expected string referrerFinal string
filter Filter filter Filter
originalNames []string originalNames []string
}{ }{
"multiple match": { "multiple match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -275,10 +282,10 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", "oldName"}, originalNames: []string{"oldName", "oldName"},
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -286,7 +293,7 @@ metadata:
}, },
}, },
"no name": { "no name": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -306,10 +313,10 @@ metadata:
name: newName2 name: newName2
`, `,
originalNames: []string{"oldName", "oldName"}, originalNames: []string{"oldName", "oldName"},
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref"}, NameFieldToUpdate: types.FieldSpec{Path: "ref"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -321,7 +328,7 @@ metadata:
for tn, tc := range testCases { for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) { t.Run(tn, func(t *testing.T) {
factory := provider.NewDefaultDepProvider().GetResourceFactory() factory := provider.NewDefaultDepProvider().GetResourceFactory()
referrer, err := factory.FromBytes([]byte(tc.input)) referrer, err := factory.FromBytes([]byte(tc.referrerOriginal))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -337,11 +344,11 @@ metadata:
candidates := resMapFactory.FromResourceSlice(candidatesRes) candidates := resMapFactory.FromResourceSlice(candidatesRes)
tc.filter.ReferralCandidates = candidates tc.filter.ReferralCandidates = candidates
_, err = filtertest_test.RunFilterE(t, tc.input, tc.filter) _, err = filtertest_test.RunFilterE(t, tc.referrerOriginal, tc.filter)
if err == nil { if err == nil {
t.Fatalf("expect an error") t.Fatalf("expect an error")
} }
if tc.expected != "" && !assert.EqualError(t, err, tc.expected) { if tc.referrerFinal != "" && !assert.EqualError(t, err, tc.referrerFinal) {
t.FailNow() t.FailNow()
} }
}) })
@@ -350,19 +357,19 @@ metadata:
func TestCandidatesWithDifferentPrefixSuffix(t *testing.T) { func TestCandidatesWithDifferentPrefixSuffix(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
input string referrerOriginal string
candidates string candidates string
expected string referrerFinal string
filter Filter filter Filter
originalNames []string originalNames []string
prefix []string prefix []string
suffix []string suffix []string
inputPrefix string inputPrefix string
inputSuffix string inputSuffix string
err bool err bool
}{ }{
"prefix match": { "prefix match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -386,7 +393,7 @@ metadata:
suffix: []string{"", "suffix2"}, suffix: []string{"", "suffix2"},
inputPrefix: "prefix1", inputPrefix: "prefix1",
inputSuffix: "", inputSuffix: "",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -395,8 +402,8 @@ ref:
name: newName name: newName
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -405,7 +412,7 @@ ref:
err: false, err: false,
}, },
"suffix match": { "suffix match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -429,7 +436,7 @@ metadata:
suffix: []string{"suffix1", "suffix2"}, suffix: []string{"suffix1", "suffix2"},
inputPrefix: "", inputPrefix: "",
inputSuffix: "suffix1", inputSuffix: "suffix1",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -438,8 +445,8 @@ ref:
name: newName name: newName
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -448,7 +455,7 @@ ref:
err: false, err: false,
}, },
"prefix suffix both match": { "prefix suffix both match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -472,7 +479,7 @@ metadata:
suffix: []string{"suffix1", "suffix2"}, suffix: []string{"suffix1", "suffix2"},
inputPrefix: "prefix1", inputPrefix: "prefix1",
inputSuffix: "suffix1", inputSuffix: "suffix1",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -481,8 +488,8 @@ ref:
name: newName name: newName
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -491,7 +498,7 @@ ref:
err: false, err: false,
}, },
"multiple match: both": { "multiple match: both": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -515,10 +522,10 @@ metadata:
suffix: []string{"suffix", "suffix"}, suffix: []string{"suffix", "suffix"},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -527,7 +534,7 @@ metadata:
err: true, err: true,
}, },
"multiple match: only prefix": { "multiple match: only prefix": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -551,10 +558,10 @@ metadata:
suffix: []string{"", ""}, suffix: []string{"", ""},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "", inputSuffix: "",
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -563,7 +570,7 @@ metadata:
err: true, err: true,
}, },
"multiple match: only suffix": { "multiple match: only suffix": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -587,10 +594,10 @@ metadata:
suffix: []string{"suffix", "suffix"}, suffix: []string{"suffix", "suffix"},
inputPrefix: "", inputPrefix: "",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: "", referrerFinal: "",
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -599,7 +606,7 @@ metadata:
err: true, err: true,
}, },
"no match: neither match": { "no match: neither match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -623,7 +630,7 @@ metadata:
suffix: []string{"suffix1", "suffix2"}, suffix: []string{"suffix1", "suffix2"},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -632,8 +639,8 @@ ref:
name: oldName name: oldName
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -642,7 +649,7 @@ ref:
err: false, err: false,
}, },
"no match: prefix doesn't match": { "no match: prefix doesn't match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -666,7 +673,7 @@ metadata:
suffix: []string{"suffix", "suffix"}, suffix: []string{"suffix", "suffix"},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -675,8 +682,8 @@ ref:
name: oldName name: oldName
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -685,7 +692,7 @@ ref:
err: false, err: false,
}, },
"no match: suffix doesn't match": { "no match: suffix doesn't match": {
input: ` referrerOriginal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -709,7 +716,7 @@ metadata:
suffix: []string{"suffix1", "suffix2"}, suffix: []string{"suffix1", "suffix2"},
inputPrefix: "prefix", inputPrefix: "prefix",
inputSuffix: "suffix", inputSuffix: "suffix",
expected: ` referrerFinal: `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
@@ -718,8 +725,8 @@ ref:
name: oldName name: oldName
`, `,
filter: Filter{ filter: Filter{
FieldSpec: types.FieldSpec{Path: "ref/name"}, NameFieldToUpdate: types.FieldSpec{Path: "ref/name"},
Target: resid.Gvk{ ReferralTarget: resid.Gvk{
Group: "apps", Group: "apps",
Version: "v1", Version: "v1",
Kind: "Secret", Kind: "Secret",
@@ -732,7 +739,7 @@ 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) {
factory := provider.NewDefaultDepProvider().GetResourceFactory() factory := provider.NewDefaultDepProvider().GetResourceFactory()
referrer, err := factory.FromBytes([]byte(tc.input)) referrer, err := factory.FromBytes([]byte(tc.referrerOriginal))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -764,13 +771,15 @@ ref:
if !tc.err { if !tc.err {
if !assert.Equal(t, if !assert.Equal(t,
strings.TrimSpace(tc.expected), strings.TrimSpace(tc.referrerFinal),
strings.TrimSpace( strings.TrimSpace(
filtertest_test.RunFilter(t, tc.input, tc.filter))) { filtertest_test.RunFilter(
t, tc.referrerOriginal, tc.filter))) {
t.FailNow() t.FailNow()
} }
} else { } else {
_, err := filtertest_test.RunFilterE(t, tc.input, tc.filter) _, err := filtertest_test.RunFilterE(
t, tc.referrerOriginal, tc.filter)
if err == nil { if err == nil {
t.Fatalf("an error is expected") t.Fatalf("an error is expected")
} }

View File

@@ -4,6 +4,7 @@
package patchstrategicmerge package patchstrategicmerge
import ( import (
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/kustomize/kyaml/yaml/merge2" "sigs.k8s.io/kustomize/kyaml/yaml/merge2"
@@ -15,6 +16,7 @@ type Filter struct {
var _ kio.Filter = Filter{} var _ kio.Filter = Filter{}
// Filter does a strategic merge patch, which can delete nodes.
func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
var result []*yaml.RNode var result []*yaml.RNode
for i := range nodes { for i := range nodes {
@@ -27,7 +29,9 @@ func (pf Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
result = append(result, r) if !konfig.FlagEnableKyamlDefaultValue || r != nil {
result = append(result, r)
}
} }
return result, nil return result, nil
} }

View File

@@ -672,6 +672,63 @@ spec:
ports: ports:
- containerPort: 80 - containerPort: 80
- containerPort: 8080 - containerPort: 8080
`,
},
// Test for issue #3513
// Currently broken; when one port has only containerPort, the output
// should not merge containerPort 8301 together
// This occurs because when protocol is missing on the first port,
// the merge code uses [containerPort] as the merge key rather than
// [containerPort, protocol]
"list map keys - protocol only present on some ports": {
input: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
spec:
template:
spec:
containers:
- name: consul
image: "hashicorp/consul:1.9.1"
ports:
- containerPort: 8500
name: http
- containerPort: 8301
protocol: "TCP"
- containerPort: 8301
protocol: "UDP"
`,
patch: yaml.MustParse(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
labels:
test: label
`),
expected: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deployment
labels:
test: label
spec:
template:
spec:
containers:
- name: consul
image: "hashicorp/consul:1.9.1"
ports:
- containerPort: 8301
protocol: "TCP"
- containerPort: 8301
protocol: "UDP"
- containerPort: 8500
name: http
`, `,
}, },
} }

View File

@@ -1,3 +1,3 @@
// Package refvar contains a kio.Filter implementation of the kustomize // Package refvar contains a kio.Filter implementation of the kustomize
// refvar transformer. // refvar transformer (find and replace $(FOO) style variables in strings).
package refvar package refvar

View File

@@ -1,12 +1,12 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// Package expansion provides functions find and replace $(FOO) style variables in strings. package refvar
package expansion
import ( import (
"bytes"
"fmt" "fmt"
"log"
"strings"
) )
const ( const (
@@ -17,38 +17,64 @@ const (
// syntaxWrap returns the input string wrapped by the expansion syntax. // syntaxWrap returns the input string wrapped by the expansion syntax.
func syntaxWrap(input string) string { func syntaxWrap(input string) string {
return string(operator) + string(referenceOpener) + input + string(referenceCloser) var sb strings.Builder
sb.WriteByte(operator)
sb.WriteByte(referenceOpener)
sb.WriteString(input)
sb.WriteByte(referenceCloser)
return sb.String()
} }
// MappingFuncFor returns a mapping function for use with Expand that // MappingFunc maps a string to anything.
// implements the expansion semantics defined in the expansion spec; it type MappingFunc func(string) interface{}
// returns the input string wrapped in the expansion syntax if no mapping
// for the input is found. // MakePrimitiveReplacer returns a MappingFunc that uses a map to do
func MappingFuncFor( // replacements, and a histogram to count map hits.
counts map[string]int, //
context ...map[string]interface{}) func(string) interface{} { // Func behavior:
return func(input string) interface{} { //
for _, vars := range context { // If the input key is NOT found in the map, the key is wrapped up as
val, ok := vars[input] // as a variable declaration string and returned, e.g. key FOO becomes $(FOO).
if ok { // This string is presumably put back where it was found, and might get replaced
counts[input]++ // later.
switch typedV := val.(type) { //
case string, int64, float64, bool: // If the key is found in the map, the value is returned if it is a primitive
return typedV // type (string, bool, number), and the hit is counted.
default: //
return syntaxWrap(input) // If it's not a primitive type (e.g. a map, struct, func, etc.) then this
} // function doesn't know what to do with it and it returns the key wrapped up
// again as if it had not been replaced. This should probably be an error.
func MakePrimitiveReplacer(
counts map[string]int, someMap map[string]interface{}) MappingFunc {
return func(key string) interface{} {
if value, ok := someMap[key]; ok {
switch typedV := value.(type) {
case string, int, int32, int64, float32, float64, bool:
counts[key]++
return typedV
default:
// If the value is some complicated type (e.g. a map or struct),
// this function doesn't know how to jam it into a string,
// so just pretend it was a cache miss.
// Likely this should be an error instead of a silent failure,
// since the programmer passed an impossible value.
log.Printf(
"MakePrimitiveReplacer: bad replacement type=%T val=%v",
typedV, typedV)
return syntaxWrap(key)
} }
} }
return syntaxWrap(input) // If unable to return the mapped variable, return it
// as it was found, and a later mapping might be able to
// replace it.
return syntaxWrap(key)
} }
} }
// Expand replaces variable references in the input string according to // DoReplacements replaces variable references in the input string
// the expansion spec using the given mapping function to resolve the // using the mapping function.
// values of variables. func DoReplacements(input string, mapping MappingFunc) interface{} {
func Expand(input string, mapping func(string) interface{}) interface{} { var buf strings.Builder
var buf bytes.Buffer
checkpoint := 0 checkpoint := 0
for cursor := 0; cursor < len(input); cursor++ { for cursor := 0; cursor < len(input); cursor++ {
if input[cursor] == operator && cursor+1 < len(input) { if input[cursor] == operator && cursor+1 < len(input) {

View File

@@ -1,13 +1,14 @@
// Copyright 2019 The Kubernetes Authors. // Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package expansion_test package refvar_test
import ( import (
"fmt" "fmt"
"testing" "testing"
. "sigs.k8s.io/kustomize/api/internal/accumulator/expansion" "github.com/stretchr/testify/assert"
. "sigs.k8s.io/kustomize/api/filters/refvar"
) )
type expected struct { type expected struct {
@@ -15,6 +16,48 @@ type expected struct {
edited string edited string
} }
func TestPrimitiveReplacer(t *testing.T) {
varCounts := make(map[string]int)
f := MakePrimitiveReplacer(
varCounts,
map[string]interface{}{
"FOO": "bar",
"ZOO": "$(FOO)-1",
"BLU": "$(ZOO)-2",
"EIGHT": 8,
"PI": 3.14159,
"ZINT": "$(INT)",
"BOOL": "true",
"HUGENUMBER": int64(9223372036854775807),
"CRAZYMAP": map[string]int{"crazy": 200},
"ZBOOL": "$(BOOL)",
})
assert.Equal(t, "$()", f(""))
assert.Equal(t, "$( )", f(" "))
assert.Equal(t, "$(florida)", f("florida"))
assert.Equal(t, "$(0)", f("0"))
assert.Equal(t, "bar", f("FOO"))
assert.Equal(t, "bar", f("FOO"))
assert.Equal(t, "bar", f("FOO"))
assert.Equal(t, 8, f("EIGHT"))
assert.Equal(t, 8, f("EIGHT"))
assert.Equal(t, 3.14159, f("PI"))
assert.Equal(t, "true", f("BOOL"))
assert.Equal(t, int64(9223372036854775807), f("HUGENUMBER"))
assert.Equal(t, "$(FOO)-1", f("ZOO"))
assert.Equal(t, "$(CRAZYMAP)", f("CRAZYMAP"))
assert.Equal(t,
map[string]int{
"FOO": 3,
"EIGHT": 2,
"BOOL": 1,
"PI": 1,
"ZOO": 1,
"HUGENUMBER": 1,
},
varCounts)
}
func TestMapReference(t *testing.T) { func TestMapReference(t *testing.T) {
type env struct { type env struct {
Name string Name string
@@ -51,7 +94,7 @@ func TestMapReference(t *testing.T) {
}, },
} }
declaredEnv := map[string]interface{}{ varMap := map[string]interface{}{
"FOO": "bar", "FOO": "bar",
"ZOO": "$(FOO)-1", "ZOO": "$(FOO)-1",
"BLU": "$(ZOO)-2", "BLU": "$(ZOO)-2",
@@ -61,11 +104,11 @@ func TestMapReference(t *testing.T) {
"ZBOOL": "$(BOOL)", "ZBOOL": "$(BOOL)",
} }
counts := make(map[string]int) varCounts := make(map[string]int)
mapping := MappingFuncFor(counts, declaredEnv)
for _, env := range envs { for _, env := range envs {
declaredEnv[env.Name] = Expand(fmt.Sprintf("%v", env.Value), mapping) varMap[env.Name] = DoReplacements(
fmt.Sprintf("%v", env.Value),
MakePrimitiveReplacer(varCounts, varMap))
} }
expectedEnv := map[string]expected{ expectedEnv := map[string]expected{
@@ -79,45 +122,20 @@ func TestMapReference(t *testing.T) {
} }
for k, v := range expectedEnv { for k, v := range expectedEnv {
if e, a := v, declaredEnv[k]; e.edited != a || e.count != counts[k] { if e, a := v, varMap[k]; e.edited != a || e.count != varCounts[k] {
t.Errorf("Expected %v count=%d, got %v count=%d", t.Errorf("Expected %v count=%d, got %v count=%d",
e.edited, e.count, a, counts[k]) e.edited, e.count, a, varCounts[k])
} else { } else {
delete(declaredEnv, k) delete(varMap, k)
} }
} }
if len(declaredEnv) != 0 { if len(varMap) != 0 {
t.Errorf("Unexpected keys in declared env: %v", declaredEnv) t.Errorf("Unexpected keys in declared env: %v", varMap)
} }
} }
func TestMapping(t *testing.T) { func TestMapping(t *testing.T) {
context := map[string]interface{}{
"VAR_A": "A",
"VAR_B": "B",
"VAR_C": "C",
"VAR_REF": "$(VAR_A)",
"VAR_EMPTY": "",
}
doExpansionTest(t, context)
}
func TestMappingDual(t *testing.T) {
context := map[string]interface{}{
"VAR_A": "A",
"VAR_EMPTY": "",
}
context2 := map[string]interface{}{
"VAR_B": "B",
"VAR_C": "C",
"VAR_REF": "$(VAR_A)",
}
doExpansionTest(t, context, context2)
}
func doExpansionTest(t *testing.T, context ...map[string]interface{}) {
cases := []struct { cases := []struct {
name string name string
input string input string
@@ -333,11 +351,17 @@ func doExpansionTest(t *testing.T, context ...map[string]interface{}) {
expected: "\n", expected: "\n",
}, },
} }
for _, tc := range cases { for _, tc := range cases {
counts := make(map[string]int) counts := make(map[string]int)
mapping := MappingFuncFor(counts, context...) expanded := DoReplacements(
expanded := Expand(fmt.Sprintf("%v", tc.input), mapping) fmt.Sprintf("%v", tc.input),
MakePrimitiveReplacer(counts, map[string]interface{}{
"VAR_A": "A",
"VAR_B": "B",
"VAR_C": "C",
"VAR_REF": "$(VAR_A)",
"VAR_EMPTY": "",
}))
if e, a := tc.expected, expanded; e != a { if e, a := tc.expected, expanded; e != a {
t.Errorf("%v: expected %q, got %q", tc.name, e, a) t.Errorf("%v: expected %q, got %q", tc.name, e, a)
} }
@@ -347,8 +371,7 @@ func doExpansionTest(t *testing.T, context ...map[string]interface{}) {
} }
if len(tc.counts) > 0 { if len(tc.counts) > 0 {
for k, expectedCount := range tc.counts { for k, expectedCount := range tc.counts {
c, ok := counts[k] if c, ok := counts[k]; ok {
if ok {
if c != expectedCount { if c != expectedCount {
t.Errorf( t.Errorf(
"%v: k=%s, expected count %d, got %d", "%v: k=%s, expected count %d, got %d",

View File

@@ -8,15 +8,13 @@ import (
"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/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
) )
// Filter updates $(VAR) style variables with values. // Filter updates $(VAR) style variables with values.
// The fieldSpecs are the places to look for occurrences of $(VAR). // The fieldSpecs are the places to look for occurrences of $(VAR).
type Filter struct { type Filter struct {
MappingFunc func(string) interface{} `json:"mappingFunc,omitempty" yaml:"mappingFunc,omitempty"` MappingFunc MappingFunc `json:"mappingFunc,omitempty" yaml:"mappingFunc,omitempty"`
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"` FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
} }
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
@@ -49,12 +47,21 @@ func (f Filter) set(node *yaml.RNode) error {
func updateNodeValue(node *yaml.Node, newValue interface{}) { func updateNodeValue(node *yaml.Node, newValue interface{}) {
switch newValue := newValue.(type) { switch newValue := newValue.(type) {
case int:
node.Value = strconv.FormatInt(int64(newValue), 10)
node.Tag = yaml.NodeTagInt
case int32:
node.Value = strconv.FormatInt(int64(newValue), 10)
node.Tag = yaml.NodeTagInt
case int64: case int64:
node.Value = strconv.FormatInt(newValue, 10) node.Value = strconv.FormatInt(newValue, 10)
node.Tag = yaml.NodeTagInt node.Tag = yaml.NodeTagInt
case bool: case bool:
node.SetString(strconv.FormatBool(newValue)) node.SetString(strconv.FormatBool(newValue))
node.Tag = yaml.NodeTagBool node.Tag = yaml.NodeTagBool
case float32:
node.SetString(strconv.FormatFloat(float64(newValue), 'f', -1, 32))
node.Tag = yaml.NodeTagFloat
case float64: case float64:
node.SetString(strconv.FormatFloat(newValue, 'f', -1, 64)) node.SetString(strconv.FormatFloat(newValue, 'f', -1, 64))
node.Tag = yaml.NodeTagFloat node.Tag = yaml.NodeTagFloat
@@ -69,7 +76,7 @@ func (f Filter) setScalar(node *yaml.RNode) error {
if !yaml.IsYNodeString(node.YNode()) { if !yaml.IsYNodeString(node.YNode()) {
return nil return nil
} }
v := expansion2.Expand(node.YNode().Value, f.MappingFunc) v := DoReplacements(node.YNode().Value, f.MappingFunc)
updateNodeValue(node.YNode(), v) updateNodeValue(node.YNode(), v)
return nil return nil
} }
@@ -78,12 +85,14 @@ func (f Filter) setMap(node *yaml.RNode) error {
contents := node.YNode().Content contents := node.YNode().Content
for i := 0; i < len(contents); i += 2 { for i := 0; i < len(contents); i += 2 {
if !yaml.IsYNodeString(contents[i]) { if !yaml.IsYNodeString(contents[i]) {
return fmt.Errorf("invalid map key: %s, type: %s", contents[i].Value, contents[i].Tag) return fmt.Errorf(
"invalid map key: value='%s', tag='%s'",
contents[i].Value, contents[i].Tag)
} }
if !yaml.IsYNodeString(contents[i+1]) { if !yaml.IsYNodeString(contents[i+1]) {
continue continue
} }
newValue := expansion2.Expand(contents[i+1].Value, f.MappingFunc) newValue := DoReplacements(contents[i+1].Value, f.MappingFunc)
updateNodeValue(contents[i+1], newValue) updateNodeValue(contents[i+1], newValue)
} }
return nil return nil
@@ -94,7 +103,7 @@ func (f Filter) setSeq(node *yaml.RNode) error {
if !yaml.IsYNodeString(item) { if !yaml.IsYNodeString(item) {
return fmt.Errorf("invalid value type expect a string") return fmt.Errorf("invalid value type expect a string")
} }
newValue := expansion2.Expand(item.Value, f.MappingFunc) newValue := DoReplacements(item.Value, f.MappingFunc)
updateNodeValue(item, newValue) updateNodeValue(item, newValue)
} }
return nil return nil

View File

@@ -1,18 +1,22 @@
package refvar package refvar_test
import ( import (
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion" . "sigs.k8s.io/kustomize/api/filters/refvar"
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest" filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
) )
var makeMf = func(theMap map[string]interface{}) MappingFunc {
ignored := make(map[string]int)
return MakePrimitiveReplacer(ignored, theMap)
}
func TestFilter(t *testing.T) { func TestFilter(t *testing.T) {
replacementCounts := make(map[string]int)
testCases := map[string]struct { testCases := map[string]struct {
input string input string
@@ -35,7 +39,7 @@ metadata:
spec: spec:
replicas: 5`, replicas: 5`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5), "VAR": int64(5),
}), }),
FieldSpec: types.FieldSpec{Path: "spec/replicas"}, FieldSpec: types.FieldSpec{Path: "spec/replicas"},
@@ -57,7 +61,7 @@ metadata:
spec: spec:
replicas: 1`, replicas: 1`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5), "VAR": int64(5),
}), }),
FieldSpec: types.FieldSpec{Path: "spec/replicas"}, FieldSpec: types.FieldSpec{Path: "spec/replicas"},
@@ -79,7 +83,7 @@ metadata:
spec: spec:
replicas: 1`, replicas: 1`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5), "VAR": int64(5),
}), }),
FieldSpec: types.FieldSpec{Path: "a/b/c"}, FieldSpec: types.FieldSpec{Path: "a/b/c"},
@@ -111,7 +115,7 @@ data:
- false - false
- 1.23`, - 1.23`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"FOO": "foo", "FOO": "foo",
"BAR": "bar", "BAR": "bar",
"BOOL": false, "BOOL": false,
@@ -142,7 +146,7 @@ data:
BAZ: $(BAZ) BAZ: $(BAZ)
PLUS: foo+bar`, PLUS: foo+bar`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"FOO": "foo", "FOO": "foo",
"BAR": "bar", "BAR": "bar",
}), }),
@@ -181,7 +185,7 @@ data:
SLICE: SLICE:
- $(FOO)`, - $(FOO)`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"FOO": "foo", "FOO": "foo",
"BAR": "bar", "BAR": "bar",
}), }),
@@ -204,8 +208,10 @@ metadata:
data: data:
FOO: null`, FOO: null`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{}), MappingFunc: makeMf(map[string]interface{}{
FieldSpec: types.FieldSpec{Path: "data/FOO"}, // no replacements!
}),
FieldSpec: types.FieldSpec{Path: "data/FOO"},
}, },
}, },
} }
@@ -223,8 +229,6 @@ data:
} }
func TestFilterUnhappy(t *testing.T) { func TestFilterUnhappy(t *testing.T) {
replacementCounts := make(map[string]int)
testCases := map[string]struct { testCases := map[string]struct {
input string input string
expectedError string expectedError string
@@ -239,7 +243,8 @@ metadata:
data: data:
slice: slice:
- false`, - false`,
expectedError: `obj 'apiVersion: apps/v1 expectedError: `considering field 'data/slice' of object
apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: dep name: dep
@@ -248,9 +253,9 @@ metadata:
data: data:
slice: slice:
- false - false
' at path 'data/slice': invalid value type expect a string`, : invalid value type expect a string`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5), "VAR": int64(5),
}), }),
FieldSpec: types.FieldSpec{Path: "data/slice"}, FieldSpec: types.FieldSpec{Path: "data/slice"},
@@ -264,7 +269,8 @@ metadata:
name: dep name: dep
data: data:
1: str`, 1: str`,
expectedError: `obj 'apiVersion: apps/v1 expectedError: `considering field 'data' of object
apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: dep name: dep
@@ -272,9 +278,9 @@ metadata:
config.kubernetes.io/index: '0' config.kubernetes.io/index: '0'
data: data:
1: str 1: str
' at path 'data': invalid map key: 1, type: ` + yaml.NodeTagInt, : invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`,
filter: Filter{ filter: Filter{
MappingFunc: expansion2.MappingFuncFor(replacementCounts, map[string]interface{}{ MappingFunc: makeMf(map[string]interface{}{
"VAR": int64(5), "VAR": int64(5),
}), }),
FieldSpec: types.FieldSpec{Path: "data"}, FieldSpec: types.FieldSpec{Path: "data"},

View File

@@ -98,7 +98,17 @@ var _ kio.Filter = Filter{}
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
_, err := kio.FilterAll(yaml.FilterFunc( _, err := kio.FilterAll(yaml.FilterFunc(
func(node *yaml.RNode) (*yaml.RNode, error) { func(node *yaml.RNode) (*yaml.RNode, error) {
fields := strings.Split(f.FieldPath, "/") var fields []string
// if there is forward slash '/' in the field name, a back slash '\'
// will be used to escape it.
for _, f := range strings.Split(f.FieldPath, "/") {
if len(fields) > 0 && strings.HasSuffix(fields[len(fields)-1], "\\") {
concatField := strings.TrimSuffix(fields[len(fields)-1], "\\") + "/" + f
fields = append(fields[:len(fields)-1], concatField)
} else {
fields = append(fields, f)
}
}
// TODO: support SequenceNode. // TODO: support SequenceNode.
// Presumably here one could look for array indices (digits) at // Presumably here one could look for array indices (digits) at
// the end of the field path (as described in IETF RFC 6902 JSON), // the end of the field path (as described in IETF RFC 6902 JSON),

View File

@@ -94,6 +94,20 @@ spec:
FilePathPosition: 2, FilePathPosition: 2,
}, },
}, },
"backSlash": {
input: `
kind: SomeKind
`,
expectedOutput: `
kind: SomeKind
spec:
resourceRef/external: valueAdded
`,
filter: Filter{
Value: "valueAdded",
FieldPath: "spec/resourceRef\\/external",
},
},
} }
for tn, tc := range testCases { for tn, tc := range testCases {

View File

@@ -20,7 +20,6 @@ require (
k8s.io/api v0.17.0 k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0 k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0 k8s.io/client-go v0.17.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a sigs.k8s.io/kustomize/kyaml v0.10.9
sigs.k8s.io/kustomize/kyaml v0.10.5
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

View File

@@ -599,8 +599,8 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphD
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/kyaml v0.10.5 h1:PbJcsZsEM7O3hHtUWTR+4WkHVbQRW9crSy75or1gRbI= sigs.k8s.io/kustomize/kyaml v0.10.9 h1:n3WNdvPPReRNDxW+XXd2JlyZ8EII721I21D1DBpBVBE=
sigs.k8s.io/kustomize/kyaml v0.10.5/go.mod h1:P6Oy/ah/GZMKzJMIJA2a3/bc8YrBkuL5kJji13PSIzY= sigs.k8s.io/kustomize/kyaml v0.10.9/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=

View File

@@ -9,7 +9,6 @@ import (
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"github.com/pkg/errors" "github.com/pkg/errors"
"k8s.io/kube-openapi/pkg/common"
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
@@ -18,8 +17,16 @@ import (
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
// OpenAPIDefinition describes single type.
// Normally these definitions are auto-generated using gen-openapi.
// Same as in k8s.io / kube-openapi / pkg / common.
type OpenAPIDefinition struct {
Schema spec.Schema
Dependencies []string
}
type myProperties map[string]spec.Schema type myProperties map[string]spec.Schema
type nameToApiMap map[string]common.OpenAPIDefinition type nameToApiMap map[string]OpenAPIDefinition
// LoadConfigFromCRDs parse CRD schemas from paths into a TransformerConfig // LoadConfigFromCRDs parse CRD schemas from paths into a TransformerConfig
func LoadConfigFromCRDs( func LoadConfigFromCRDs(
@@ -162,7 +169,7 @@ func loadCrdIntoConfig(
err = theConfig.AddNamereferenceFieldSpec( err = theConfig.AddNamereferenceFieldSpec(
builtinconfig.NameBackReferences{ builtinconfig.NameBackReferences{
Gvk: resid.Gvk{Kind: kind, Version: version}, Gvk: resid.Gvk{Kind: kind, Version: version},
FieldSpecs: []types.FieldSpec{ Referrers: []types.FieldSpec{
makeFs(theGvk, append(path, propName, nameKey))}, makeFs(theGvk, append(path, propName, nameKey))},
}) })
if err != nil { if err != nil {

View File

@@ -8,10 +8,9 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/loader"
. "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/loader"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
@@ -140,21 +139,19 @@ func TestLoadCRDs(t *testing.T) {
nbrs := []builtinconfig.NameBackReferences{ nbrs := []builtinconfig.NameBackReferences{
{ {
Gvk: resid.Gvk{Kind: "Secret", Version: "v1"}, Gvk: resid.Gvk{Kind: "Secret", Version: "v1"},
FieldSpecs: []types.FieldSpec{ Referrers: []types.FieldSpec{
{ {
CreateIfNotPresent: false, Gvk: resid.Gvk{Kind: "MyKind"},
Gvk: resid.Gvk{Kind: "MyKind"}, Path: "spec/secretRef/name",
Path: "spec/secretRef/name",
}, },
}, },
}, },
{ {
Gvk: resid.Gvk{Kind: "Bee", Version: "v1beta1"}, Gvk: resid.Gvk{Kind: "Bee", Version: "v1beta1"},
FieldSpecs: []types.FieldSpec{ Referrers: []types.FieldSpec{
{ {
CreateIfNotPresent: false, Gvk: resid.Gvk{Kind: "MyKind"},
Gvk: resid.Gvk{Kind: "MyKind"}, Path: "spec/beeRef/name",
Path: "spec/beeRef/name",
}, },
}, },
}, },

View File

@@ -4,23 +4,29 @@
package accumulator package accumulator
import ( import (
"fmt"
"log" "log"
"sigs.k8s.io/kustomize/api/filters/nameref" "sigs.k8s.io/kustomize/api/filters/nameref"
"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/kyaml/filtersutil" "sigs.k8s.io/kustomize/api/resource"
) )
type nameReferenceTransformer struct { type nameReferenceTransformer struct {
backRefs []builtinconfig.NameBackReferences backRefs []builtinconfig.NameBackReferences
} }
const doDebug = false
var _ resmap.Transformer = &nameReferenceTransformer{} var _ resmap.Transformer = &nameReferenceTransformer{}
type filterMap map[*resource.Resource][]nameref.Filter
// newNameReferenceTransformer constructs a nameReferenceTransformer // newNameReferenceTransformer constructs a nameReferenceTransformer
// with a given slice of NameBackReferences. // with a given slice of NameBackReferences.
func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.Transformer { func newNameReferenceTransformer(
br []builtinconfig.NameBackReferences) resmap.Transformer {
if br == nil { if br == nil {
log.Fatal("backrefs not expected to be nil") log.Fatal("backrefs not expected to be nil")
} }
@@ -33,13 +39,61 @@ func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.T
// //
// For example, a HorizontalPodAutoscaler (HPA) // For example, a HorizontalPodAutoscaler (HPA)
// necessarily refers to a Deployment, the thing that // necessarily refers to a Deployment, the thing that
// the HPA scales. The Deployment name might change // an HPA scales. In this case:
// (e.g. prefix added), and the reference in the HPA
// has to be fixed.
// //
// In the outer loop over the ResMap below, say we // - the HPA instance is the Referrer,
// encounter a specific HPA. Then, in scanning backrefs, // - the Deployment instance is the ReferralTarget.
// we encounter an entry like //
// If the Deployment's name changes, e.g. a prefix is added,
// then the HPA's reference to the Deployment must be fixed.
//
func (t *nameReferenceTransformer) Transform(m resmap.ResMap) error {
fMap := t.determineFilters(m.Resources())
debug(fMap)
for r, fList := range fMap {
c := m.SubsetThatCouldBeReferencedByResource(r)
for _, f := range fList {
f.Referrer = r
f.ReferralCandidates = c
if err := f.Referrer.ApplyFilter(f); err != nil {
return err
}
}
}
return nil
}
func debug(fMap filterMap) {
if !doDebug {
return
}
fmt.Printf("filterMap has %d entries:\n", len(fMap))
rCount := 0
for r, fList := range fMap {
yml, _ := r.AsYAML()
rCount++
fmt.Printf(`
---- %3d. possible referrer -------------
%s
---------`, rCount, string(yml),
)
for i, f := range fList {
fmt.Printf(`
%3d/%3d update: %s
from: %s
`, rCount, i+1, f.NameFieldToUpdate.Path, f.ReferralTarget,
)
}
}
}
// Produce a map from referrer resources that might need to be fixed
// to filters that might fix them. The keys to this map are potential
// referrers, so won't include resources like ConfigMap or Secret.
//
// In the inner loop over the resources below, say we
// encounter an HPA instance. Then, in scanning the set
// of all known backrefs, we encounter an entry like
// //
// - kind: Deployment // - kind: Deployment
// fieldSpecs: // fieldSpecs:
@@ -48,54 +102,53 @@ func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.T
// //
// This entry says that an HPA, via its // This entry says that an HPA, via its
// 'spec/scaleTargetRef/name' field, may refer to a // 'spec/scaleTargetRef/name' field, may refer to a
// Deployment. This match to HPA means we may need to // Deployment.
// modify the value in its 'spec/scaleTargetRef/name'
// field, by searching for the thing it refers to,
// and getting its new name.
// //
// As a filter, and search optimization, we compute a // This means that a filter will need to hunt for the right Deployment,
// subset of all resources that the HPA could refer to, // obtain it's new name, and write that name into the HPA's
// by excluding objects from other namespaces, and // 'spec/scaleTargetRef/name' field. Return a filter that can do that.
// excluding objects that don't have the same prefix- func (t *nameReferenceTransformer) determineFilters(
// suffix mods as the HPA. resources []*resource.Resource) (fMap filterMap) {
// fMap = make(filterMap)
// We look in this subset for all Deployment objects for _, backReference := range t.backRefs {
// with a resId that has a Name matching the field value for _, referrerSpec := range backReference.Referrers {
// present in the HPA. If no match do nothing; if more for _, res := range resources {
// than one match, it's an error. if res.OrgId().IsSelected(&referrerSpec.Gvk) {
// // If this is true, the res might be a referrer, and if
// We overwrite the HPA name field with the value found // so, the name reference it holds might need an update.
// in the Deployment's name field (the name in the raw if resHasField(res, referrerSpec.Path) {
// object - the modified name - not the unmodified name // Optimization - the referrer has the field
// in the Deployment's resId). // that might need updating.
// fMap[res] = append(fMap[res], nameref.Filter{
// This process assumes that the name stored in a ResId // Name field to write in the Referrer.
// (the ResMap key) isn't modified by name transformers. // If the path specified here isn't found in
// Name transformers should only modify the name in the // the Referrer, nothing happens (no error,
// body of the resource object (the value in the ResMap). // no field creation).
// NameFieldToUpdate: referrerSpec,
func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error { // Specification of object class to read from.
// TODO: Too much looping, here and in transitive calls. // Always read from metadata/name field.
for _, referrer := range m.Resources() { ReferralTarget: backReference.Gvk,
var candidates resmap.ResMap })
for _, target := range o.backRefs {
for _, fSpec := range target.FieldSpecs {
if referrer.OrgId().IsSelected(&fSpec.Gvk) {
if candidates == nil {
candidates = m.SubsetThatCouldBeReferencedByResource(referrer)
}
err := filtersutil.ApplyToJSON(nameref.Filter{
FieldSpec: fSpec,
Referrer: referrer,
Target: target.Gvk,
ReferralCandidates: candidates,
}, referrer)
if err != nil {
return err
} }
} }
} }
} }
} }
return nil return fMap
}
// TODO: check res for field existence here to avoid extra work.
// res.GetFieldValue, which uses yaml.Lookup under the hood, doesn't know
// how to parse fieldspec-style paths that make no distinction
// between maps and sequences. This means it cannot lookup commonly
// used "indeterminate" paths like
// spec/containers/env/valueFrom/configMapKeyRef/name
// ('containers' is a list, not a map).
// However, the fieldspec filter does know how to handle this;
// extract that code and call it here?
func resHasField(res *resource.Resource, path string) bool {
return true
// fld := strings.Join(utils.PathSplitter(path), ".")
// _, e := res.GetFieldValue(fld)
// return e == nil
} }

View File

@@ -8,12 +8,15 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig" "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/provider" "sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest" resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
) )
const notEqualErrFmt = "expected (self) doesn't match actual (other): %v"
func TestNameReferenceHappyRun(t *testing.T) { func TestNameReferenceHappyRun(t *testing.T) {
m := resmaptest_test.NewRmBuilderDefault(t).AddWithName( m := resmaptest_test.NewRmBuilderDefault(t).AddWithName(
"cm1", "cm1",
@@ -470,7 +473,7 @@ func TestNameReferenceHappyRun(t *testing.T) {
} }
if err = expected.ErrorIfNotEqualLists(m); err != nil { if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf(notEqualErrFmt, err)
} }
} }
@@ -518,7 +521,27 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
}, },
}, },
}).ResMap(), }).ResMap(),
expectedErr: "cannot find field 'name' in node"}, // TODO(#3304): DECISION - kyaml better; not a bug.
expectedErr: konfig.IfApiMachineryElseKyaml(
`updating name reference in 'rules/resourceNames' field of `+
`'rbac.authorization.k8s.io_v1_ClusterRole|~X|cr'`+
`: considering field 'rules/resourceNames' of object
{"apiVersion": "rbac.authorization.k8s.io/v1", "kind": "ClusterRole", "metadata": {
"name": "cr"}, "rules": [{"resourceNames": {"foo": "bar"}, "resources": ["secrets"]}]}
: visit traversal on path: [resourceNames]: path config error; no 'name' field in node`,
`updating name reference in 'rules/resourceNames' field of `+
`'rbac.authorization.k8s.io_v1_ClusterRole|~X|cr'`+
`: considering field 'rules/resourceNames' of object
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cr
rules:
- resourceNames:
foo: bar
resources:
- secrets
: visit traversal on path: [resourceNames]: path config error; no 'name' field in node`)},
} }
nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference) nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
@@ -529,7 +552,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
} }
if !strings.Contains(err.Error(), test.expectedErr) { if !strings.Contains(err.Error(), test.expectedErr) {
t.Fatalf("Incorrect error.\nExpected: %s, but got %v", t.Fatalf("Incorrect error.\nExpected:\n %s\nGot:\n%v",
test.expectedErr, err) test.expectedErr, err)
} }
} }
@@ -587,7 +610,7 @@ func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
v2.AppendRefBy(c2.CurId()) v2.AppendRefBy(c2.CurId())
if err := m1.ErrorIfNotEqualLists(m2); err != nil { if err := m1.ErrorIfNotEqualLists(m2); err != nil {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf(notEqualErrFmt, err)
} }
} }
@@ -721,8 +744,9 @@ func TestNameReferenceNamespace(t *testing.T) {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil { if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf(notEqualErrFmt, err)
} }
} }
@@ -882,8 +906,9 @@ func TestNameReferenceClusterWide(t *testing.T) {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil { if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf(notEqualErrFmt, err)
} }
} }
@@ -1008,8 +1033,9 @@ func TestNameReferenceNamespaceTransformation(t *testing.T) {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil { if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf(notEqualErrFmt, err)
} }
} }
@@ -1044,7 +1070,8 @@ func TestNameReferenceCandidateSelection(t *testing.T) {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
m.RemoveBuildAnnotations()
if err = expected.ErrorIfNotEqualLists(m); err != nil { if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf(notEqualErrFmt, err)
} }
} }

View File

@@ -4,19 +4,15 @@
package accumulator package accumulator
import ( import (
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
"sigs.k8s.io/kustomize/api/filters/refvar" "sigs.k8s.io/kustomize/api/filters/refvar"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
) )
type refVarTransformer struct { type refVarTransformer struct {
varMap map[string]interface{} varMap map[string]interface{}
replacementCounts map[string]int replacementCounts map[string]int
fieldSpecs []types.FieldSpec fieldSpecs []types.FieldSpec
mappingFunc func(string) interface{}
} }
// newRefVarTransformer returns a new refVarTransformer // newRefVarTransformer returns a new refVarTransformer
@@ -35,8 +31,7 @@ func newRefVarTransformer(
func (rv *refVarTransformer) UnusedVars() []string { func (rv *refVarTransformer) UnusedVars() []string {
var unused []string var unused []string
for k := range rv.varMap { for k := range rv.varMap {
_, ok := rv.replacementCounts[k] if _, ok := rv.replacementCounts[k]; !ok {
if !ok {
unused = append(unused, k) unused = append(unused, k)
} }
} }
@@ -46,14 +41,13 @@ func (rv *refVarTransformer) UnusedVars() []string {
// Transform replaces $(VAR) style variables with values. // Transform replaces $(VAR) style variables with values.
func (rv *refVarTransformer) Transform(m resmap.ResMap) error { func (rv *refVarTransformer) Transform(m resmap.ResMap) error {
rv.replacementCounts = make(map[string]int) rv.replacementCounts = make(map[string]int)
rv.mappingFunc = expansion2.MappingFuncFor( mf := refvar.MakePrimitiveReplacer(rv.replacementCounts, rv.varMap)
rv.replacementCounts, rv.varMap)
for _, res := range m.Resources() { for _, res := range m.Resources() {
for _, fieldSpec := range rv.fieldSpecs { for _, fieldSpec := range rv.fieldSpecs {
err := filtersutil.ApplyToJSON(refvar.Filter{ err := res.ApplyFilter(refvar.Filter{
MappingFunc: rv.mappingFunc, MappingFunc: mf,
FieldSpec: fieldSpec, FieldSpec: fieldSpec,
}, res) })
if err != nil { if err != nil {
return err return err
} }

View File

@@ -7,6 +7,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest" resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
@@ -23,14 +24,12 @@ func TestRefVarTransformer(t *testing.T) {
res resmap.ResMap res resmap.ResMap
unused []string unused []string
} }
testCases := []struct { testCases := map[string]struct {
description string given given
given given expected expected
expected expected errMessage string
errMessage string
}{ }{
{ "var replacement in map[string]": {
description: "var replacement in map[string]",
given: given{ given: given{
varMap: map[string]interface{}{ varMap: map[string]interface{}{
"FOO": "replacementForFoo", "FOO": "replacementForFoo",
@@ -105,8 +104,7 @@ func TestRefVarTransformer(t *testing.T) {
unused: []string{"BAR"}, unused: []string{"BAR"},
}, },
}, },
{ "var replacement panic in map[string]": {
description: "var replacement panic in map[string]",
given: given{ given: given{
varMap: map[string]interface{}{}, varMap: map[string]interface{}{},
fs: []types.FieldSpec{ fs: []types.FieldSpec{
@@ -123,11 +121,23 @@ func TestRefVarTransformer(t *testing.T) {
"slice": []interface{}{5}, // noticeably *not* a []string "slice": []interface{}{5}, // noticeably *not* a []string
}}).ResMap(), }}).ResMap(),
}, },
errMessage: `obj '{"apiVersion": "v1", "data": {"slice": [5]}, "kind": "ConfigMap", "metadata": {"name": "cm1"}} // TODO(#3304): DECISION - kyaml better; not a bug.
' at path 'data/slice': invalid value type expect a string`, errMessage: konfig.IfApiMachineryElseKyaml(
`considering field 'data/slice' of object
{"apiVersion": "v1", "data": {"slice": [5]}, "kind": "ConfigMap", "metadata": {"name": "cm1"}}
: invalid value type expect a string`,
`considering field 'data/slice' of object
apiVersion: v1
data:
slice:
- 5
kind: ConfigMap
metadata:
name: cm1
: invalid value type expect a string`,
),
}, },
{ "var replacement in nil": {
description: "var replacement in nil",
given: given{ given: given{
varMap: map[string]interface{}{}, varMap: map[string]interface{}{},
fs: []types.FieldSpec{ fs: []types.FieldSpec{
@@ -159,20 +169,18 @@ func TestRefVarTransformer(t *testing.T) {
}, },
} }
for _, tc := range testCases { for tn, tc := range testCases {
t.Run(tc.description, func(t *testing.T) { t.Run(tn, func(t *testing.T) {
// arrange
tr := newRefVarTransformer(tc.given.varMap, tc.given.fs) tr := newRefVarTransformer(tc.given.varMap, tc.given.fs)
// act
err := tr.Transform(tc.given.res) err := tr.Transform(tc.given.res)
// assert
if tc.errMessage != "" { if tc.errMessage != "" {
if err == nil { if err == nil {
t.Fatalf("missing expected error %v", tc.errMessage) t.Fatalf("missing expected error %v", tc.errMessage)
} else if err.Error() != tc.errMessage { } else if err.Error() != tc.errMessage {
t.Fatalf("actual error doesn't match expected error: \nACTUAL: %v\nEXPECTED: %v", err.Error(), tc.errMessage) t.Fatalf(`actual error doesn't match expected error:
ACTUAL: %v
EXPECTED: %v`,
err.Error(), tc.errMessage)
} }
} else { } else {
if err != nil { if err != nil {
@@ -182,7 +190,13 @@ func TestRefVarTransformer(t *testing.T) {
a, e := tc.given.res, tc.expected.res a, e := tc.given.res, tc.expected.res
if !reflect.DeepEqual(a, e) { if !reflect.DeepEqual(a, e) {
err = e.ErrorIfNotEqualLists(a) err = e.ErrorIfNotEqualLists(a)
t.Fatalf("actual doesn't match expected: \nACTUAL:\n%v\nEXPECTED:\n%v\nERR: %v", a, e, err) t.Fatalf(`actual doesn't match expected:
ACTUAL:
%v
EXPECTED:
%v
ERR: %v`,
a, e, err)
} }
} }
}) })

View File

@@ -59,6 +59,15 @@ func (ra *ResAccumulator) GetTransformerConfig() *builtinconfig.TransformerConfi
return ra.tConfig return ra.tConfig
} }
// MergeVars accumulates vars into ResAccumulator.
// A Var is a tuple of name, object reference and field reference.
// This func takes a list of vars from the current kustomization file and
// annotates the accumulated resources with the names of the vars that match
// those resources. E.g. if there's a var named "sam" that wants to get
// its data from a ConfigMap named "james", and the resource list contains a
// ConfigMap named "james", then that ConfigMap will be annotated with the
// var name "sam". Later this annotation is used to find the data for "sam"
// by digging into a particular fieldpath of "james".
func (ra *ResAccumulator) MergeVars(incoming []types.Var) error { func (ra *ResAccumulator) MergeVars(incoming []types.Var) error {
for _, v := range incoming { for _, v := range incoming {
targetId := resid.NewResIdWithNamespace(v.ObjRef.GVK(), v.ObjRef.Name, v.ObjRef.Namespace) targetId := resid.NewResIdWithNamespace(v.ObjRef.GVK(), v.ObjRef.Name, v.ObjRef.Namespace)
@@ -68,7 +77,7 @@ func (ra *ResAccumulator) MergeVars(incoming []types.Var) error {
// wildcard search on the namespace hence we still use GvknEquals // wildcard search on the namespace hence we still use GvknEquals
idMatcher = targetId.Equals idMatcher = targetId.Equals
} }
matched := ra.resMap.GetMatchingResourcesByOriginalId(idMatcher) matched := ra.resMap.GetMatchingResourcesByAnyId(idMatcher)
if len(matched) > 1 { if len(matched) > 1 {
return fmt.Errorf( return fmt.Errorf(
"found %d resId matches for var %s "+ "found %d resId matches for var %s "+
@@ -104,12 +113,10 @@ func (ra *ResAccumulator) findVarValueFromResources(v types.Var) (interface{}, e
"field specified in var '%v' "+ "field specified in var '%v' "+
"not found in corresponding resource", v) "not found in corresponding resource", v)
} }
return s, nil return s, nil
} }
} }
} }
return "", fmt.Errorf( return "", fmt.Errorf(
"var '%v' cannot be mapped to a field "+ "var '%v' cannot be mapped to a field "+
"in the set of known resources", v) "in the set of known resources", v)
@@ -125,10 +132,8 @@ func (ra *ResAccumulator) makeVarReplacementMap() (map[string]interface{}, error
if err != nil { if err != nil {
return nil, err return nil, err
} }
result[v.Name] = s result[v.Name] = s
} }
return result, nil return result, nil
} }
@@ -159,6 +164,6 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
if ra.tConfig.NameReference == nil { if ra.tConfig.NameReference == nil {
return nil return nil
} }
return ra.Transform(newNameReferenceTransformer( return ra.Transform(
ra.tConfig.NameReference)) newNameReferenceTransformer(ra.tConfig.NameReference))
} }

View File

@@ -344,19 +344,20 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
}, },
}, },
}}). }}).
// Make it seem like this resource
// went through a prefix transformer.
Add(map[string]interface{}{ Add(map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "Service", "kind": "Service",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "backendOne", "name": "sub-backendOne",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousNames": "backendOne",
"config.kubernetes.io/previousNamespaces": "default",
"config.kubernetes.io/prefixes": "sub-",
},
}}).ResMap() }}).ResMap()
// Make it seem like this resource
// went through a prefix transformer.
r := m.GetByIndex(1)
r.AddNamePrefix("sub-")
r.SetName("sub-backendOne") // original name remains "backendOne"
err = ra2.AppendAll(m) err = ra2.AppendAll(m)
if err != nil { if err != nil {
t.Fatalf("unexpected err: %v", err) t.Fatalf("unexpected err: %v", err)

View File

@@ -534,8 +534,8 @@ k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/kyaml v0.10.5 h1:PbJcsZsEM7O3hHtUWTR+4WkHVbQRW9crSy75or1gRbI= sigs.k8s.io/kustomize/kyaml v0.10.9 h1:n3WNdvPPReRNDxW+XXd2JlyZ8EII721I21D1DBpBVBE=
sigs.k8s.io/kustomize/kyaml v0.10.5/go.mod h1:P6Oy/ah/GZMKzJMIJA2a3/bc8YrBkuL5kJji13PSIzY= sigs.k8s.io/kustomize/kyaml v0.10.9/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -4,7 +4,6 @@
package generators package generators
import ( import (
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
@@ -38,14 +37,9 @@ func MakeConfigMap(
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, k := range filtersutil.SortedMapKeys(m) { if err = rn.LoadMapIntoConfigMapData(m); err != nil {
fldName, vrN := makeConfigMapValueRNode(m[k]) return nil, err
if _, err = rn.Pipe(
yaml.LookupCreate(yaml.MappingNode, fldName),
yaml.SetField(k, vrN)); err != nil {
return nil, err
}
} }
copyLabelsAndAnnotations(rn, args.Options) copyLabelsAndAnnotations(rn, args.Options)
return rn, err return rn, nil
} }

View File

@@ -4,7 +4,6 @@
package generators package generators
import ( import (
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
@@ -51,14 +50,9 @@ func MakeSecret(
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, k := range filtersutil.SortedMapKeys(m) { if err = rn.LoadMapIntoSecretData(m); err != nil {
vrN := makeSecretValueRNode(m[k]) return nil, err
if _, err = rn.Pipe(
yaml.LookupCreate(yaml.MappingNode, yaml.DataField),
yaml.SetField(k, vrN)); err != nil {
return nil, err
}
} }
copyLabelsAndAnnotations(rn, args.Options) copyLabelsAndAnnotations(rn, args.Options)
return rn, err return rn, nil
} }

View File

@@ -4,13 +4,9 @@
package generators package generators
import ( import (
"encoding/base64"
"fmt" "fmt"
"strings"
"unicode/utf8"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"sigs.k8s.io/kustomize/api/filters/filtersutil"
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml" "sigs.k8s.io/kustomize/kyaml/yaml"
@@ -66,13 +62,13 @@ func copyLabelsAndAnnotations(
if opts == nil { if opts == nil {
return nil return nil
} }
for _, k := range filtersutil.SortedMapKeys(opts.Labels) { for _, k := range yaml.SortedMapKeys(opts.Labels) {
v := opts.Labels[k] v := opts.Labels[k]
if _, err := rn.Pipe(yaml.SetLabel(k, v)); err != nil { if _, err := rn.Pipe(yaml.SetLabel(k, v)); err != nil {
return err return err
} }
} }
for _, k := range filtersutil.SortedMapKeys(opts.Annotations) { for _, k := range yaml.SortedMapKeys(opts.Annotations) {
v := opts.Annotations[k] v := opts.Annotations[k]
if _, err := rn.Pipe(yaml.SetAnnotation(k, v)); err != nil { if _, err := rn.Pipe(yaml.SetAnnotation(k, v)); err != nil {
return err return err
@@ -80,60 +76,3 @@ func copyLabelsAndAnnotations(
} }
return nil return nil
} }
// In a secret, all data is base64 encoded, regardless of its conformance
// or lack thereof to UTF-8.
func makeSecretValueRNode(s string) *yaml.RNode {
yN := &yaml.Node{Kind: yaml.ScalarNode}
// Purposely don't use YAML tags to identify the data as being plain text or
// binary. It kubernetes Secrets the values in the `data` map are expected
// to be base64 encoded, and in ConfigMaps that same can be said for the
// values in the `binaryData` field.
yN.Tag = yaml.NodeTagString
yN.Value = encodeBase64(s)
if strings.Contains(yN.Value, "\n") {
yN.Style = yaml.LiteralStyle
}
return yaml.NewRNode(yN)
}
func makeConfigMapValueRNode(s string) (field string, rN *yaml.RNode) {
yN := &yaml.Node{Kind: yaml.ScalarNode}
yN.Tag = yaml.NodeTagString
if utf8.ValidString(s) {
field = yaml.DataField
yN.Value = s
} else {
field = yaml.BinaryDataField
yN.Value = encodeBase64(s)
}
if strings.Contains(yN.Value, "\n") {
yN.Style = yaml.LiteralStyle
}
return field, yaml.NewRNode(yN)
}
// encodeBase64 encodes s as base64 that is broken up into multiple lines
// as appropriate for the resulting length.
func encodeBase64(s string) string {
const lineLen = 70
encLen := base64.StdEncoding.EncodedLen(len(s))
lines := encLen/lineLen + 1
buf := make([]byte, encLen*2+lines)
in := buf[0:encLen]
out := buf[encLen:]
base64.StdEncoding.Encode(in, []byte(s))
k := 0
for i := 0; i < len(in); i += lineLen {
j := i + lineLen
if j > len(in) {
j = len(in)
}
k += copy(out[k:], in[i:j])
if lines > 1 {
out[k] = '\n'
k++
}
}
return string(out[:k])
}

View File

@@ -10,8 +10,8 @@ import (
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
// NameBackReferences is an association between a gvk.GVK and a list // NameBackReferences is an association between a gvk.GVK (a ReferralTarget)
// of FieldSpec instances that could refer to it. // and a list of Referrers that could refer to it.
// //
// It is used to handle name changes, and can be thought of as a // It is used to handle name changes, and can be thought of as a
// a contact list. If you change your own contact info (name, // a contact list. If you change your own contact info (name,
@@ -19,16 +19,20 @@ import (
// know about the change. // know about the change.
// //
// For example, ConfigMaps can be used by Pods and everything that // For example, ConfigMaps can be used by Pods and everything that
// contains a Pod; Deployment, Job, StatefulSet, etc. To change // contains a Pod; Deployment, Job, StatefulSet, etc.
// the name of a ConfigMap instance from 'alice' to 'bob', one // The ConfigMap is the ReferralTarget, the others are Referrers.
// must visit all objects that could refer to the ConfigMap, see if //
// they mention 'alice', and if so, change the reference to 'bob'. // If the the name of a ConfigMap instance changed from 'alice' to 'bob',
// one must
// - visit all objects that could refer to the ConfigMap (the Referrers)
// - see if they mention 'alice',
// - if so, change the Referrer's name reference to 'bob'.
// //
// The NameBackReferences instance to aid in this could look like // The NameBackReferences instance to aid in this could look like
// { // {
// kind: ConfigMap // kind: ConfigMap
// version: v1 // version: v1
// FieldSpecs: // fieldSpecs:
// - kind: Pod // - kind: Pod
// version: v1 // version: v1
// path: spec/volumes/configMap/name // path: spec/volumes/configMap/name
@@ -39,13 +43,15 @@ import (
// (etc.) // (etc.)
// } // }
type NameBackReferences struct { type NameBackReferences struct {
resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"` resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
FieldSpecs types.FsSlice `json:"FieldSpecs,omitempty" yaml:"FieldSpecs,omitempty"` // TODO: rename json 'fieldSpecs' to 'referrers' for clarity.
// This will, however, break anyone using a custom config.
Referrers types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
} }
func (n NameBackReferences) String() string { func (n NameBackReferences) String() string {
var r []string var r []string
for _, f := range n.FieldSpecs { for _, f := range n.Referrers {
r = append(r, f.String()) r = append(r, f.String())
} }
return n.Gvk.String() + ": (\n" + return n.Gvk.String() + ": (\n" +
@@ -77,7 +83,7 @@ func (s nbrSlice) mergeOne(other NameBackReferences) (nbrSlice, error) {
found := false found := false
for _, c := range s { for _, c := range s {
if c.Gvk.Equals(other.Gvk) { if c.Gvk.Equals(other.Gvk) {
c.FieldSpecs, err = c.FieldSpecs.MergeAll(other.FieldSpecs) c.Referrers, err = c.Referrers.MergeAll(other.Referrers)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -50,13 +50,13 @@ func TestMergeAll(t *testing.T) {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "ConfigMap", Kind: "ConfigMap",
}, },
FieldSpecs: fsSlice1, Referrers: fsSlice1,
}, },
{ {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "Secret", Kind: "Secret",
}, },
FieldSpecs: fsSlice2, Referrers: fsSlice2,
}, },
} }
nbrsSlice2 := nbrSlice{ nbrsSlice2 := nbrSlice{
@@ -64,13 +64,13 @@ func TestMergeAll(t *testing.T) {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "ConfigMap", Kind: "ConfigMap",
}, },
FieldSpecs: fsSlice1, Referrers: fsSlice1,
}, },
{ {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "Secret", Kind: "Secret",
}, },
FieldSpecs: fsSlice2, Referrers: fsSlice2,
}, },
} }
expected := nbrSlice{ expected := nbrSlice{
@@ -78,13 +78,13 @@ func TestMergeAll(t *testing.T) {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "ConfigMap", Kind: "ConfigMap",
}, },
FieldSpecs: fsSlice1, Referrers: fsSlice1,
}, },
{ {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "Secret", Kind: "Secret",
}, },
FieldSpecs: fsSlice2, Referrers: fsSlice2,
}, },
} }
actual, err := nbrsSlice1.mergeAll(nbrsSlice2) actual, err := nbrsSlice1.mergeAll(nbrsSlice2)

View File

@@ -24,7 +24,7 @@ func TestAddNamereferenceFieldSpec(t *testing.T) {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "KindA", Kind: "KindA",
}, },
FieldSpecs: []types.FieldSpec{ Referrers: []types.FieldSpec{
{ {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "KindB", Kind: "KindB",
@@ -89,7 +89,7 @@ func TestMerge(t *testing.T) {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "KindA", Kind: "KindA",
}, },
FieldSpecs: []types.FieldSpec{ Referrers: []types.FieldSpec{
{ {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "KindB", Kind: "KindB",
@@ -103,7 +103,7 @@ func TestMerge(t *testing.T) {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "KindA", Kind: "KindA",
}, },
FieldSpecs: []types.FieldSpec{ Referrers: []types.FieldSpec{
{ {
Gvk: resid.Gvk{ Gvk: resid.Gvk{
Kind: "KindC", Kind: "KindC",

View File

@@ -45,6 +45,7 @@ s/$BAR/bar baz/g
"argsFromFile": "sed-input.txt", "argsFromFile": "sed-input.txt",
}) })
pluginConfig.RemoveBuildAnnotations()
p := NewExecPlugin( p := NewExecPlugin(
pLdr.AbsolutePluginPath( pLdr.AbsolutePluginPath(
konfig.DisabledPluginConfig(), konfig.DisabledPluginConfig(),

View File

@@ -4,6 +4,7 @@
package utils package utils
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@@ -13,8 +14,8 @@ import (
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/konfig" "sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid"
"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/yaml" "sigs.k8s.io/yaml"
) )
@@ -135,9 +136,6 @@ func GetResMapWithIDAnnotation(rm resmap.ResMap) (resmap.ResMap, error) {
return nil, err return nil, err
} }
annotations := r.GetAnnotations() annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
annotations[idAnnotation] = string(idString) annotations[idAnnotation] = string(idString)
r.SetAnnotations(annotations) r.SetAnnotations(annotations)
} }
@@ -147,41 +145,65 @@ func GetResMapWithIDAnnotation(rm resmap.ResMap) (resmap.ResMap, error) {
// UpdateResMapValues updates the Resource value in the given ResMap // UpdateResMapValues updates the Resource value in the given ResMap
// with the emitted Resource values in output. // with the emitted Resource values in output.
func UpdateResMapValues(pluginName string, h *resmap.PluginHelpers, output []byte, rm resmap.ResMap) error { func UpdateResMapValues(pluginName string, h *resmap.PluginHelpers, output []byte, rm resmap.ResMap) error {
outputRM, err := h.ResmapFactory().NewResMapFromBytes(output) mapFactory := h.ResmapFactory()
resFactory := mapFactory.RF()
resources, err := resFactory.SliceFromBytes(output)
if err != nil { if err != nil {
return err return err
} }
for _, r := range outputRM.Resources() { // Don't use resources here, or error message will be unfriendly to plugin builders
// for each emitted Resource, find the matching Resource in the original ResMap newMap, err := mapFactory.NewResMapFromBytes([]byte{})
// using its id if err != nil {
annotations := r.GetAnnotations() return err
idString, ok := annotations[idAnnotation] }
if !ok {
return fmt.Errorf("the transformer %s should not remove annotation %s", for _, r := range resources {
pluginName, idAnnotation) removeIDAnnotation(r) // stale--not manipulated by plugin transformers
// Add to the new map, checking for duplicates
if err := newMap.Append(r); err != nil {
prettyID, err := json.Marshal(r.CurId())
if err != nil {
prettyID = []byte(r.CurId().String())
}
return fmt.Errorf("plugin %s generated duplicate resource: %s", pluginName, prettyID)
} }
id := resid.ResId{}
err := yaml.Unmarshal([]byte(idString), &id) // Add to or update the old map
oldIdx, err := rm.GetIndexOfCurrentId(r.CurId())
if err != nil { if err != nil {
return err return err
} }
res, err := rm.GetByCurrentId(id) if oldIdx != -1 {
if err != nil { rm.GetByIndex(oldIdx).ResetPrimaryData(r)
return fmt.Errorf("unable to find unique match to %s", id.String()) } else {
if err := rm.Append(r); err != nil {
return err
}
} }
// remove the annotation set by Kustomize to track the resource
delete(annotations, idAnnotation)
if len(annotations) == 0 {
annotations = nil
}
r.SetAnnotations(annotations)
// update the resource value with the transformed object
res.ResetPrimaryData(r)
} }
// Remove items the transformer deleted from the old map
for _, id := range rm.AllIds() {
newIdx, _ := newMap.GetIndexOfCurrentId(id)
if newIdx == -1 {
rm.Remove(id)
}
}
return nil return nil
} }
func removeIDAnnotation(r *resource.Resource) {
// remove the annotation set by Kustomize to track the resource
annotations := r.GetAnnotations()
delete(annotations, idAnnotation)
if len(annotations) == 0 {
annotations = nil
}
r.SetAnnotations(annotations)
}
// UpdateResourceOptions updates the generator options for each resource in the // UpdateResourceOptions updates the generator options for each resource in the
// given ResMap based on plugin provided annotations. // given ResMap based on plugin provided annotations.
func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) { func UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {

View File

@@ -19,6 +19,7 @@ import (
"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/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -382,6 +383,11 @@ 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())
} }
err = openapi.SetSchemaVersion(subKt.Kustomization().OpenAPI, false)
if err != nil {
return nil, errors.Wrapf(
err, "couldn't set openapi version for path '%s'", ldr.Root())
}
if isComponent && subKt.kustomization.Kind != types.ComponentKind { if isComponent && subKt.kustomization.Kind != types.ComponentKind {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"expected kind '%s' for path '%s' but got '%s'", types.ComponentKind, ldr.Root(), subKt.kustomization.Kind) "expected kind '%s' for path '%s' but got '%s'", types.ComponentKind, ldr.Root(), subKt.kustomization.Kind)

View File

@@ -244,6 +244,7 @@ metadata:
t.Fatalf("unexpected error %v", err) t.Fatalf("unexpected error %v", err)
} }
} }
expected.RemoveBuildAnnotations()
expYaml, err := expected.AsYaml() expYaml, err := expected.AsYaml()
assert.NoError(t, err) assert.NoError(t, err)
@@ -251,6 +252,7 @@ metadata:
assert.NoError(t, kt.Load()) assert.NoError(t, kt.Load())
actual, err := kt.MakeCustomizedResMap() actual, err := kt.MakeCustomizedResMap()
assert.NoError(t, err) assert.NoError(t, err)
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, expYaml, actYaml)

View File

@@ -0,0 +1,22 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package utils
import "strings"
// PathSplitter splits a slash delimited string, permitting escaped slashes.
func PathSplitter(path string) []string {
ps := strings.Split(path, "/")
var res []string
res = append(res, ps[0])
for i := 1; i < len(ps); i++ {
last := len(res) - 1
if strings.HasSuffix(res[last], `\`) {
res[last] = strings.TrimSuffix(res[last], `\`) + "/" + ps[i]
} else {
res = append(res, ps[i])
}
}
return res
}

View File

@@ -0,0 +1,49 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package utils_test
import (
"testing"
"github.com/stretchr/testify/assert"
. "sigs.k8s.io/kustomize/api/internal/utils"
)
func TestPathSplitter(t *testing.T) {
for _, tc := range []struct {
exp []string
path string
}{
{
path: "",
exp: []string{""},
},
{
path: "s",
exp: []string{"s"},
},
{
path: "a/b/c",
exp: []string{"a", "b", "c"},
},
{
path: `a/b[]/c`,
exp: []string{"a", "b[]", "c"},
},
{
path: `a/b\/c/d\/e/f`,
exp: []string{"a", "b/c", "d/e", "f"},
},
{
// The actual reason for this.
path: `metadata/annotations/nginx.ingress.kubernetes.io\/auth-secret`,
exp: []string{
"metadata",
"annotations",
"nginx.ingress.kubernetes.io/auth-secret"},
},
} {
assert.Equal(t, tc.exp, PathSplitter(tc.path))
}
}

View File

@@ -58,7 +58,7 @@ func (k *WNodeFactory) SliceFromBytes(bs []byte) ([]ifc.Kunstructured, error) {
// shouldDropObject returns true if the resource should not be accumulated. // shouldDropObject returns true if the resource should not be accumulated.
func shouldDropObject(m yaml.ResourceMeta) bool { func shouldDropObject(m yaml.ResourceMeta) bool {
_, y := m.ObjectMeta.Annotations[konfig.IgnoredByKustomizeResourceAnnotation] _, y := m.ObjectMeta.Annotations[konfig.IgnoredByKustomizeAnnotation]
return y return y
} }

View File

@@ -6,6 +6,8 @@ package wrappy
import ( import (
"fmt" "fmt"
"log" "log"
"regexp"
"strconv"
"strings" "strings"
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
@@ -43,6 +45,10 @@ func FromRNode(node *yaml.RNode) *WNode {
return &WNode{node: node} return &WNode{node: node}
} }
func (wn *WNode) AsRNode() *yaml.RNode {
return wn.node
}
func (wn *WNode) demandMetaData(label string) yaml.ResourceMeta { func (wn *WNode) demandMetaData(label string) yaml.ResourceMeta {
meta, err := wn.node.GetMeta() meta, err := wn.node.GetMeta()
if err != nil { if err != nil {
@@ -62,9 +68,35 @@ func (wn *WNode) GetAnnotations() map[string]string {
return wn.demandMetaData("GetAnnotations").Annotations return wn.demandMetaData("GetAnnotations").Annotations
} }
// convertSliceIndex traverses the items in `fields` and find
// if there is a slice index in the item and change it to a
// valid Lookup field path. For example, 'ports[0]' will be
// converted to 'ports' and '0'.
func convertSliceIndex(fields []string) []string {
var res []string
for _, s := range fields {
if !strings.HasSuffix(s, "]") {
res = append(res, s)
continue
}
re := regexp.MustCompile(`^(.*)\[(\d+)\]$`)
groups := re.FindStringSubmatch(s)
if len(groups) == 0 {
// no match, add to result
res = append(res, s)
continue
}
if groups[1] != "" {
res = append(res, groups[1])
}
res = append(res, groups[2])
}
return res
}
// GetFieldValue implements ifc.Kunstructured. // GetFieldValue implements ifc.Kunstructured.
func (wn *WNode) GetFieldValue(path string) (interface{}, error) { func (wn *WNode) GetFieldValue(path string) (interface{}, error) {
fields := strings.Split(path, ".") fields := convertSliceIndex(strings.Split(path, "."))
rn, err := wn.node.Pipe(yaml.Lookup(fields...)) rn, err := wn.node.Pipe(yaml.Lookup(fields...))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -96,9 +128,29 @@ func (wn *WNode) GetFieldValue(path string) (interface{}, error) {
} }
return result, nil return result, nil
} }
if yn.Kind != yaml.ScalarNode {
return nil, fmt.Errorf("expected ScalarNode, got Kind=%d", yn.Kind)
}
// Return value value directly for all other (ScalarNode) kinds // TODO: When doing kustomize var replacement, which is likely a
return yn.Value, nil // a primary use of this function and the reason it returns interface{}
// rather than string, we do conversion from Nodes to Go types and back
// to nodes. We should figure out how to do replacement using raw nodes,
// assuming we keep the var feature in kustomize.
// The other end of this is: refvar.go:updateNodeValue.
switch yn.Tag {
case yaml.NodeTagString:
return yn.Value, nil
case yaml.NodeTagInt:
return strconv.Atoi(yn.Value)
case yaml.NodeTagFloat:
return strconv.ParseFloat(yn.Value, 64)
case yaml.NodeTagBool:
return strconv.ParseBool(yn.Value)
default:
// Possibly this should be an error or log.
return yn.Value, nil
}
} }
// GetGvk implements ifc.Kunstructured. // GetGvk implements ifc.Kunstructured.
@@ -159,12 +211,7 @@ func (wn *WNode) GetString(path string) (string, error) {
// Map implements ifc.Kunstructured. // Map implements ifc.Kunstructured.
func (wn *WNode) Map() map[string]interface{} { func (wn *WNode) Map() map[string]interface{} {
var result map[string]interface{} return wn.node.Map()
if err := wn.node.YNode().Decode(&result); err != nil {
// Log and die since interface doesn't allow error.
log.Fatalf("failed to decode ynode: %v", err)
}
return result
} }
// MarshalJSON implements ifc.Kunstructured. // MarshalJSON implements ifc.Kunstructured.

View File

@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml" kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
@@ -32,6 +33,38 @@ const (
"area": "51", "area": "51",
"greeting": "Take me to your leader." "greeting": "Take me to your leader."
} }
},
"spec": {
"template": {
"spec": {
"containers": [
{
"env": [
{
"name": "CM_FOO",
"valueFrom": {
"configMapKeyRef": {
"key": "somekey",
"name": "myCm"
}
}
},
{
"name": "SECRET_FOO",
"valueFrom": {
"secretKeyRef": {
"key": "someKey",
"name": "mySecret"
}
}
}
],
"image": "nginx:1.7.9",
"name": "nginx"
}
]
}
}
} }
} }
` `
@@ -357,6 +390,51 @@ func TestGetFieldValueReturnsMap(t *testing.T) {
} }
} }
func TestGetFieldValueReturnsStuff(t *testing.T) {
wn := NewWNode()
if err := wn.UnmarshalJSON([]byte(deploymentBiggerJson)); err != nil {
t.Fatalf("unexpected unmarshaljson err: %v", err)
}
expected := []interface{}{
map[string]interface{}{
"env": []interface{}{
map[string]interface{}{
"name": "CM_FOO",
"valueFrom": map[string]interface{}{
"configMapKeyRef": map[string]interface{}{
"key": "somekey",
"name": "myCm",
},
},
},
map[string]interface{}{
"name": "SECRET_FOO",
"valueFrom": map[string]interface{}{
"secretKeyRef": map[string]interface{}{
"key": "someKey",
"name": "mySecret",
},
},
},
},
"image": string("nginx:1.7.9"),
"name": string("nginx"),
},
}
actual, err := wn.GetFieldValue("spec.template.spec.containers")
if err != nil {
t.Fatalf("error getting field value: %v", err)
}
if diff := cmp.Diff(expected, actual); diff != "" {
t.Fatalf("actual map does not deep equal expected map:\n%v", diff)
}
// Cannot go deeper yet.
_, err = wn.GetFieldValue("spec.template.spec.containers.env")
if err == nil {
t.Fatalf("expected err %v", err)
}
}
func TestGetFieldValueReturnsSlice(t *testing.T) { func TestGetFieldValueReturnsSlice(t *testing.T) {
bytes, err := yaml.Marshal(makeBigMap()) bytes, err := yaml.Marshal(makeBigMap())
if err != nil { if err != nil {
@@ -480,6 +558,10 @@ func TestGetSlice(t *testing.T) {
} }
} }
func TestMapEmpty(t *testing.T) {
assert.Equal(t, 0, len(NewWNode().Map()))
}
func TestMap(t *testing.T) { func TestMap(t *testing.T) {
wn := NewWNode() wn := NewWNode()
if err := wn.UnmarshalJSON([]byte(deploymentLittleJson)); err != nil { if err := wn.UnmarshalJSON([]byte(deploymentLittleJson)); err != nil {

View File

@@ -125,7 +125,7 @@ func (kf *KunstructuredFactoryImpl) skipResource(u unstructured.Unstructured) bo
return false return false
} }
// check if the Resource has opt-ed out of kustomize // check if the Resource has opt-ed out of kustomize
_, found := an[konfig.IgnoredByKustomizeResourceAnnotation] _, found := an[konfig.IgnoredByKustomizeAnnotation]
return found return found
} }

View File

@@ -3,6 +3,9 @@
package builtinpluginconsts package builtinpluginconsts
// TODO: rename 'fieldSpecs' to 'referrers' for clarity.
// This will, however, break anyone using a custom config.
const ( const (
nameReferenceFieldSpecs = ` nameReferenceFieldSpecs = `
nameReference: nameReference:

View File

@@ -56,8 +56,11 @@ 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 = "config.kubernetes.io"
// If a resource has this annotation, kustomize will drop it. // If a resource has this annotation, kustomize will drop it.
IgnoredByKustomizeResourceAnnotation = "config.kubernetes.io/local-config" IgnoredByKustomizeAnnotation = ConfigAnnoDomain + "/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

@@ -9,7 +9,6 @@ import (
"runtime" "runtime"
"sigs.k8s.io/kustomize/api/filesys" "sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
@@ -158,15 +157,3 @@ func pwdEnv() string {
} }
return "PWD" return "PWD"
} }
// GetBuiltinPluginNames returns a list of builtin plugin names
func GetBuiltinPluginNames() []string {
var ret []string
for k := range builtinhelpers.GeneratorFactories {
ret = append(ret, k.String())
}
for k := range builtinhelpers.TransformerFactories {
ret = append(ret, k.String())
}
return ret
}

View File

@@ -93,3 +93,70 @@ resources:
t.Fatalf("unexpected error: %q", err) t.Fatalf("unexpected error: %q", err)
} }
} }
func TestResourceHasAnchor(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
resources:
- ingress.yaml
`)
th.WriteF("/app/ingress.yaml", `
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog
spec:
tls:
- hosts:
- xyz.me
- www.xyz.me
secretName: cert-tls
rules:
- host: xyz.me
http: &xxx_rules
paths:
- path: /
pathType: Prefix
backend:
service:
name: service
port:
number: 80
- host: www.xyz.me
http: *xxx_rules
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog
spec:
rules:
- host: xyz.me
http:
paths:
- backend:
service:
name: service
port:
number: 80
path: /
pathType: Prefix
- host: www.xyz.me
http:
paths:
- backend:
service:
name: service
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- xyz.me
- www.xyz.me
secretName: cert-tls
`)
}

102
api/krusty/basic_io_test.go Normal file
View File

@@ -0,0 +1,102 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestBasicIO_1(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
if !opts.UseKyaml {
// This test won't pass under apimachinery, because in the bowels of
// that code (see GetAnnotations in v0.17.0 of
// k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go)
// an error returned from NestedStringMap is discarded, and an
// empty annotation map is silently returned, making this test fail
// The swallowed error arises from code like:
// var v interface{}
// v = true
// if str, ok := v.(string); ok {
// save the value in a map (doesn't happen)
// } else {
// return an error (that is then ignored by GetAnnotations)
// }
// The error happens when any annotation value can be interpreted as
// a boolean or number. Such annotations cannot be successfully applied
// to an object in a cluster unless they are quoted.
t.SkipNow()
}
th.WriteK(".", `
resources:
- service.yaml
`)
th.WriteF("service.yaml", `
apiVersion: v1
kind: Service
metadata:
annotations:
port: 8080
happy: true
color: green
name: demo
spec:
clusterIP: None
`)
m := th.Run(".", opts)
// The annotations are sorted by key, hence the order change.
// Quotes are added intentionally.
th.AssertActualEqualsExpected(
m, `
apiVersion: v1
kind: Service
metadata:
annotations:
color: green
happy: "true"
port: "8080"
name: demo
spec:
clusterIP: None
`)
}
func TestBasicIO_2(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
th.WriteK(".", `
resources:
- service.yaml
`)
// All the annotation values are quoted in the input.
th.WriteF("service.yaml", `
apiVersion: v1
kind: Service
metadata:
annotations:
port: "8080"
happy: "true"
color: green
name: demo
spec:
clusterIP: None
`)
m := th.Run(".", opts)
// The annotations are sorted by key, hence the order change.
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Service
metadata:
annotations:
color: green
happy: "true"
port: "8080"
name: demo
spec:
clusterIP: None
`)
}

View File

@@ -27,10 +27,6 @@ configMapGenerator:
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
annotations:
port: 8080
happy: true
color: green
name: demo name: demo
spec: spec:
clusterIP: None clusterIP: None
@@ -41,10 +37,6 @@ spec:
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
annotations:
color: green
happy: true
port: 8080
name: demo name: demo
spec: spec:
clusterIP: None clusterIP: None
@@ -60,7 +52,6 @@ metadata:
`) `)
} }
// Observation: Numbers no longer quoted
func TestGeneratorIntVsStringWithMerge(t *testing.T) { func TestGeneratorIntVsStringWithMerge(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
th.WriteK("base", ` th.WriteK("base", `
@@ -80,22 +71,52 @@ configMapGenerator:
literals: literals:
- month=12 - month=12
`) `)
opts := th.MakeDefaultOptions() m := th.Run("overlay", th.MakeDefaultOptions())
m := th.Run("overlay", opts) th.AssertActualEqualsExpected(m, `apiVersion: v1
expFmt := `apiVersion: v1
data: data:
crisis: %s crisis: "true"
fruit: Indian Gooseberry fruit: Indian Gooseberry
month: %s month: "12"
year: %s year: "2020"
kind: ConfigMap kind: ConfigMap
metadata: metadata:
name: bob-%s name: bob-bk46gm59c6
` `)
th.AssertActualEqualsExpected( }
m, opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt, `"true"`, `"12"`, `"2020"`, `bk46gm59c6`), func TestGeneratorFromProperties(t *testing.T) {
fmt.Sprintf(expFmt, `true`, `12`, `2020`, `bkmtk2t2fb`))) th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
configMapGenerator:
- name: test-configmap
behavior: create
envs:
- properties
`)
th.WriteF("base/properties", `
VAR1=100
`)
th.WriteK("overlay", `
resources:
- ../base
configMapGenerator:
- name: test-configmap
behavior: "merge"
envs:
- properties
`)
th.WriteF("overlay/properties", `
VAR2=200
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: v1
data:
VAR1: "100"
VAR2: "200"
kind: ConfigMap
metadata:
name: test-configmap-hdghb5ddkg
`)
} }
// Generate a Secret and a ConfigMap from the same data // Generate a Secret and a ConfigMap from the same data
@@ -193,12 +214,16 @@ metadata:
type: Opaque type: Opaque
` `
th.AssertActualEqualsExpected( th.AssertActualEqualsExpected(
m, opts.IfApiMachineryElseKyaml( m,
fmt.Sprintf(expFmt, // TODO(#3304): DECISION - kyaml better; not a bug.
opts.IfApiMachineryElseKyaml(
fmt.Sprintf(
expFmt,
`CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbnVjbGVhcgo=`, `CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbnVjbGVhcgo=`,
`CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aGUgZXZpbCBkYXlzIGNvbWUgbm90Lgo=`, `CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aGUgZXZpbCBkYXlzIGNvbWUgbm90Lgo=`,
`ftht6hfgmb`), `ftht6hfgmb`),
fmt.Sprintf(expFmt, `| fmt.Sprintf(
expFmt, `|
CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbn CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbn
VjbGVhcgo=`, `| VjbGVhcgo=`, `|
CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aG CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aG
@@ -440,3 +465,40 @@ metadata:
name: cm-o2-5k95kd76ft name: cm-o2-5k95kd76ft
`) `)
} }
func TestConfigMapGeneratorLiteralNewline(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
generators:
- configmaps.yaml
`)
th.WriteF("/app/configmaps.yaml", `
apiVersion: builtin
kind: ConfigMapGenerator
metadata:
name: testing
literals:
- |
initial.txt=greetings
everyone
- |
final.txt=different
behavior
---
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(
m, `
apiVersion: v1
data:
final.txt: |
different
behavior
initial.txt: |
greetings
everyone
kind: ConfigMap
metadata:
name: testing-tt4769fb52
`)
}

View File

@@ -0,0 +1,227 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
// In the following structure `top` patches `left-deploy` and `right-deploy` to reference the
// configMap in `bottom`. This test verifies `configMapRef.name` in `left-deploy` and
// `right-deploy` is modified correctly.
//
// top
// / \
// left right
// / \ / \
// left-service bottom bottom right-service
// | \ / |
// left-deploy bottom right-deploy
// |
// configMap
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestConfMapNameResolutionInDiamondWithPatches(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("bottom", `
configMapGenerator:
- name: bottom
literals:
- KEY=value
`)
th.WriteK("left-service", `
resources:
- deployment.yaml
`)
th.WriteF("left-service/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: left-deploy
labels:
app.kubernetes.io/name: left-deploy
spec:
selector:
matchLabels:
app.kubernetes.io/name: left-pod
template:
metadata:
labels:
app.kubernetes.io/name: left-pod
spec:
containers:
- image: left-image:v1.0
name: service
`)
th.WriteK("right-service", `
resources:
- deployment.yaml
`)
th.WriteF("right-service/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: right-deploy
labels:
app.kubernetes.io/name: right-deploy
spec:
selector:
matchLabels:
app.kubernetes.io/name: right-pod
template:
metadata:
labels:
app.kubernetes.io/name: right-pod
spec:
containers:
- image: right-image:v1.0
name: service
`)
th.WriteK("top", `
resources:
- ./left
- ./right
`)
th.WriteK("top/left", `
resources:
- ../../left-service
- ./bottom
patches:
- target:
kind: Deployment
name: left-deploy
patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: ignored-by-kustomize
spec:
template:
spec:
containers:
- name: service
env:
- name: MAPPED_CONFIG_ITEM
valueFrom:
configMapKeyRef:
name: left-bottom
key: KEY
`)
th.WriteK("top/left/bottom", `
namePrefix: left-
resources:
- ../../../bottom
`)
th.WriteK("top/right", `
resources:
- ../../right-service
- ./bottom
patches:
- target:
kind: Deployment
name: right-deploy
patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: ignored-by-kustomize
spec:
template:
spec:
containers:
- name: service
env:
- name: MAPPED_CONFIG_ITEM
valueFrom:
configMapKeyRef:
name: right-bottom
key: KEY
`)
th.WriteK("top/right/bottom", `
namePrefix: right-
resources:
- ../../../bottom
`)
m := th.Run("top", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: left-deploy
name: left-deploy
spec:
selector:
matchLabels:
app.kubernetes.io/name: left-pod
template:
metadata:
labels:
app.kubernetes.io/name: left-pod
spec:
containers:
- env:
- name: MAPPED_CONFIG_ITEM
valueFrom:
configMapKeyRef:
key: KEY
name: left-bottom-9f2t6f5h6d
image: left-image:v1.0
name: service
---
apiVersion: v1
data:
KEY: value
kind: ConfigMap
metadata:
name: left-bottom-9f2t6f5h6d
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: right-deploy
name: right-deploy
spec:
selector:
matchLabels:
app.kubernetes.io/name: right-pod
template:
metadata:
labels:
app.kubernetes.io/name: right-pod
spec:
containers:
- env:
- name: MAPPED_CONFIG_ITEM
valueFrom:
configMapKeyRef:
key: KEY
name: right-bottom-9f2t6f5h6d
image: right-image:v1.0
name: service
---
apiVersion: v1
data:
KEY: value
kind: ConfigMap
metadata:
name: right-bottom-9f2t6f5h6d
`)
}

View File

@@ -1,7 +1,6 @@
package krusty_test package krusty_test
import ( import (
"fmt"
"os/exec" "os/exec"
"testing" "testing"
@@ -129,9 +128,8 @@ stringData:
bootcmd: bootcmd:
- mkdir /mnt/vda - mkdir /mnt/vda
`) `)
opts := th.MakeOptionsPluginsEnabled() m := th.Run("/app", th.MakeOptionsPluginsEnabled())
m := th.Run("/app", opts) th.AssertActualEqualsExpected(m, `
expFmt := `
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
@@ -154,7 +152,7 @@ metadata:
name: demo name: demo
name: demo-budget name: demo-budget
spec: spec:
minAvailable: 67%% minAvailable: 67%
selector: selector:
matchLabels: matchLabels:
app: cockroachdb app: cockroachdb
@@ -187,7 +185,9 @@ metadata:
annotations: annotations:
config.kubernetes.io/path: config/demo_service.yaml config.kubernetes.io/path: config/demo_service.yaml
prometheus.io/path: _status/vars prometheus.io/path: _status/vars
%s prometheus.io/port: "8080"
prometheus.io/scrape: "true"
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
labels: labels:
app: cockroachdb app: cockroachdb
name: demo name: demo
@@ -244,7 +244,7 @@ spec:
- /bin/bash - /bin/bash
- -ecx - -ecx
- | - |
# The use of qualified ` + "`hostname -f`" + ` is crucial: # The use of qualified `+"`hostname -f`"+` is crucial:
# Other nodes aren't able to look up the unqualified hostname. # Other nodes aren't able to look up the unqualified hostname.
CRARGS=("start" "--logtostderr" "--insecure" "--host" "$(hostname -f)" "--http-host" "0.0.0.0") CRARGS=("start" "--logtostderr" "--insecure" "--host" "$(hostname -f)" "--http-host" "0.0.0.0")
# We only want to initialize a new cluster (by omitting the join flag) # We only want to initialize a new cluster (by omitting the join flag)
@@ -302,14 +302,7 @@ spec:
resources: resources:
requests: requests:
storage: 1Gi storage: 1Gi
` `)
th.AssertActualEqualsExpected(m, opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt, ` prometheus.io/port: "8080"
prometheus.io/scrape: "true"
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"`),
fmt.Sprintf(expFmt, ` prometheus.io/port: 8080
prometheus.io/scrape: true
service.alpha.kubernetes.io/tolerate-unready-endpoints: true`)))
} }
func TestFnContainerTransformer(t *testing.T) { func TestFnContainerTransformer(t *testing.T) {

View File

@@ -0,0 +1,387 @@
package krusty_test
import (
"testing"
"github.com/stretchr/testify/assert"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
// Checks that a patch at the top of the stack can refer to resources
// by an intermediate name after it has gone through multiple name
// transformations.
// Ref: Issue #3455
func TestIntermediateName(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/gcp", `
namePrefix: gcp-
resources:
- ../emea
patchesStrategicMerge:
- depPatch.yaml
`)
th.WriteF("/app/gcp/depPatch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: prod-foo
spec:
replicas: 999
`)
th.WriteK("/app/emea", `
namePrefix: emea-
resources:
- ../prod
`)
th.WriteK("/app/prod", `
namePrefix: prod-
resources:
- ../base
`)
th.WriteK("/app/base", `
resources:
- deployment.yaml
`)
th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- image: whatever
`)
m := th.Run("/app/gcp", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: gcp-emea-prod-foo
spec:
replicas: 999
template:
spec:
containers:
- image: whatever
`)
}
// Tests that if resources in different layers (containing name
// transformations) have the same name, there is no conflict
func TestIntermediateNameSameNameDifferentLayer(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/gcp", `
namePrefix: gcp-
resources:
- ../emea
patchesStrategicMerge:
- depPatch.yaml
`)
th.WriteF("/app/gcp/depPatch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: prod-foo
spec:
replicas: 999
`)
th.WriteK("/app/emea", `
namePrefix: emea-
resources:
- ../prod
- deployment.yaml
`)
th.WriteF("/app/emea/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- image: whatever
`)
th.WriteK("/app/prod", `
namePrefix: prod-
resources:
- ../base
`)
th.WriteK("/app/base", `
resources:
- deployment.yaml
`)
th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- image: whatever
`)
m := th.Run("/app/gcp", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: gcp-emea-prod-foo
spec:
replicas: 999
template:
spec:
containers:
- image: whatever
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gcp-emea-foo
spec:
template:
spec:
containers:
- image: whatever
`)
}
// Same as above test but tries to refer to the name foo
// instead of prod-foo
func TestIntermediateNameAmbiguous(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/gcp", `
namePrefix: gcp-
resources:
- ../emea
patchesStrategicMerge:
- depPatch.yaml
`)
th.WriteF("/app/gcp/depPatch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
spec:
replicas: 999
`)
th.WriteK("/app/emea", `
namePrefix: emea-
resources:
- ../prod
- deployment.yaml
`)
th.WriteF("/app/emea/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- image: whatever
`)
th.WriteK("/app/prod", `
namePrefix: prod-
resources:
- ../base
`)
th.WriteK("/app/base", `
resources:
- deployment.yaml
`)
th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
spec:
template:
spec:
containers:
- image: whatever
`)
err := th.RunWithErr("/app/gcp", th.MakeDefaultOptions())
assert.Error(t, err)
}
// Test for issue #3228
// References to resources by their intermediate names after multiple
// name transformations should be supported
func TestIntermediateNameSecretKeyRefDiamond(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
namePrefix: project-
resources:
- app`)
th.WriteK("/app", `
resources:
- resources/deployment.yaml
- resources/xql
`)
th.WriteK("/app/resources/xql", `
resources:
- xql-zero
- xql-one
configurations:
- ./kustomizeconfig.yaml
`)
th.WriteF("/app/resources/xql/kustomizeconfig.yaml", `
varReference:
- path: spec/template/spec/containers/env/valueFrom/secretKeyRef/name
`)
th.WriteK("/app/resources/xql/xql-one", `
namePrefix: xql-one-
resources:
- ../../../../bases/xql
secretGenerator:
- name: xql-secret
behavior: merge
envs:
- config/xql-one-secret.env
vars:
- name: PROJECT_XQL_ONE_SECRET_NAME
objref:
kind: Secret
name: xql-secret
apiVersion: v1
fieldref:
fieldpath: metadata.name
`)
th.WriteF("/app/resources/xql/xql-one/config/xql-one-secret.env", `
arg=1
`)
th.WriteK("/app/resources/xql/xql-zero", `
namePrefix: xql-zero-
resources:
- ../../../../bases/xql
secretGenerator:
- name: xql-secret
behavior: merge
envs:
- config/xql-zero-secret.env
vars:
- name: PROJECT_XQL_ZERO_SECRET_NAME
objref:
kind: Secret
name: xql-secret
apiVersion: v1
fieldref:
fieldpath: metadata.name
`)
th.WriteF("/app/resources/xql/xql-zero/config/xql-zero-secret.env", `
arg=0
`)
th.WriteF("app/resources/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
spec:
template:
spec:
containers:
- name: app
image: example.com/app:latest
imagePullPolicy: Always
env:
- name: XQL_ZERO_ARG
valueFrom:
secretKeyRef:
name: $(PROJECT_XQL_ZERO_SECRET_NAME)
key: arg
- name: XQL_ZERO_PASSWORD
valueFrom:
secretKeyRef:
name: xql-zero-xql-secret
key: password
- name: XQL_ONE_ARG
valueFrom:
secretKeyRef:
name: $(PROJECT_XQL_ONE_SECRET_NAME)
key: arg
- name: XQL_ONE_PASSWORD
valueFrom:
secretKeyRef:
name: xql-one-xql-secret
key: password
`)
th.WriteK("/bases/xql", `
secretGenerator:
- name: xql-secret
envs:
- config/xql-secret.env
`)
th.WriteF("bases/xql/config/xql-secret.env", `
password=SUPER_SECRET_PASSWORD
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: project-app
spec:
template:
spec:
containers:
- env:
- name: XQL_ZERO_ARG
valueFrom:
secretKeyRef:
key: arg
name: project-xql-zero-xql-secret-6khmtc56hm
- name: XQL_ZERO_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: project-xql-zero-xql-secret-6khmtc56hm
- name: XQL_ONE_ARG
valueFrom:
secretKeyRef:
key: arg
name: project-xql-one-xql-secret-79mhmf5dgt
- name: XQL_ONE_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: project-xql-one-xql-secret-79mhmf5dgt
image: example.com/app:latest
imagePullPolicy: Always
name: app
---
apiVersion: v1
data:
arg: MA==
password: U1VQRVJfU0VDUkVUX1BBU1NXT1JE
kind: Secret
metadata:
name: project-xql-zero-xql-secret-6khmtc56hm
type: Opaque
---
apiVersion: v1
data:
arg: MQ==
password: U1VQRVJfU0VDUkVUX1BBU1NXT1JE
kind: Secret
metadata:
name: project-xql-one-xql-secret-79mhmf5dgt
type: Opaque
`)
}

View File

@@ -0,0 +1,283 @@
// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestIssue3377(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
th.WriteK(".", `
resources:
- service-a.yaml
- service-b.yaml
patchesJson6902:
- path: service-a-patch.yaml
target:
version: v1
group: networking.k8s.io
kind: Ingress
name: service-a
- path: service-b-patch.yaml
target:
version: v1
group: networking.k8s.io
kind: Ingress
name: service-b
vars:
- name: SERVICE_A_DNS_NAME
objref:
apiVersion: networking.k8s.io/v1
kind: Ingress
name: service-a
fieldref:
fieldpath: spec.rules[0].host
- name: SERVICE_B_DNS_NAME
objref:
apiVersion: networking.k8s.io/v1
kind: Ingress
name: service-b
fieldref:
fieldpath: spec.rules[0].host
`)
th.WriteF("service-a.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-a
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: service-a
template:
metadata:
labels:
app.kubernetes.io/component: service-a
spec:
containers:
- image: repository/service-a:v1
imagePullPolicy: Always
name: service-b
ports:
- containerPort: 8080
env:
- name: REDIRECT_URI
value: "http://$(SERVICE_B_DNS_NAME):80/oauth_redir"
---
apiVersion: v1
kind: Service
metadata:
name: service-a
labels:
app.kubernetes.io/component: service-a
spec:
type: LoadBalancer
ports:
- port: 8080
selector:
app.kubernetes.io/component: service-a
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: service-a
spec:
rules:
- host: service-a.k8s.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-a
port:
number: 8080
`)
th.WriteF("service-a-patch.yaml", `
- op: replace
path: /spec/rules/0/host
value: new-service-a.k8s.com
`)
th.WriteF("service-b.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-b
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: service-b
template:
metadata:
labels:
app.kubernetes.io/component: service-b
spec:
containers:
- image: repository/service-b:v1
imagePullPolicy: Always
name: service-b
ports:
- containerPort: 8080
env:
- name: REDIRECT_URI
value: "http://$(SERVICE_A_DNS_NAME):80/oauth_redir"
---
apiVersion: v1
kind: Service
metadata:
name: service-b
labels:
app.kubernetes.io/component: service-b
spec:
type: LoadBalancer
ports:
- port: 8080
selector:
app.kubernetes.io/component: service-b
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: service-b
spec:
rules:
- host: service-b.k8s.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service-b
port:
number: 8080
`)
th.WriteF("service-b-patch.yaml", `
- op: replace
path: /spec/rules/0/host
value: new-service-b.k8s.com
`)
m := th.Run(".", opts)
th.AssertActualEqualsExpected(
m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-a
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: service-a
template:
metadata:
labels:
app.kubernetes.io/component: service-a
spec:
containers:
- env:
- name: REDIRECT_URI
value: http://new-service-b.k8s.com:80/oauth_redir
image: repository/service-a:v1
imagePullPolicy: Always
name: service-b
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: service-a
name: service-a
spec:
ports:
- port: 8080
selector:
app.kubernetes.io/component: service-a
type: LoadBalancer
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: service-a
spec:
rules:
- host: new-service-a.k8s.com
http:
paths:
- backend:
service:
name: service-a
port:
number: 8080
path: /
pathType: Prefix
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: service-b
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/component: service-b
template:
metadata:
labels:
app.kubernetes.io/component: service-b
spec:
containers:
- env:
- name: REDIRECT_URI
value: http://new-service-a.k8s.com:80/oauth_redir
image: repository/service-b:v1
imagePullPolicy: Always
name: service-b
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: service-b
name: service-b
spec:
ports:
- port: 8080
selector:
app.kubernetes.io/component: service-b
type: LoadBalancer
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: service-b
spec:
rules:
- host: new-service-b.k8s.com
http:
paths:
- backend:
service:
name: service-b
port:
number: 8080
path: /
pathType: Prefix
`)
}

View File

@@ -16,6 +16,7 @@ import (
"sigs.k8s.io/kustomize/api/provider" "sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/openapi"
) )
// Kustomizer performs kustomizations. It's meant to behave // Kustomizer performs kustomizations. It's meant to behave
@@ -73,6 +74,10 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = openapi.SetSchemaVersion(kt.Kustomization().OpenAPI, true)
if err != nil {
return nil, err
}
var m resmap.ResMap var m resmap.ResMap
m, err = kt.MakeCustomizedResMap() m, err = kt.MakeCustomizedResMap()
if err != nil { if err != nil {
@@ -93,5 +98,6 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
} }
t.Transform(m) t.Transform(m)
} }
m.RemoveBuildAnnotations()
return m, nil return m, nil
} }

View File

@@ -318,8 +318,9 @@ spec:
volumes:%s volumes:%s
name: nginx-persistent-storage name: nginx-persistent-storage
` `
// TODO(#3394)
th.AssertActualEqualsExpected( th.AssertActualEqualsExpected(
// TODO(#3394): Should be possible to delete emptyDir with a patch.
// TODO(#3304): DECISION - still a bug, emptyDir should be deleted.
m, opts.IfApiMachineryElseKyaml( m, opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt, ` fmt.Sprintf(expFmt, `
- gcePersistentDisk: - gcePersistentDisk:
@@ -1123,3 +1124,92 @@ metadata:
} }
} }
} }
// test for #3513
func TestSmpWithDifferentKeysOnDifferentPorts(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- resource.yaml
patches:
- path: patch.yaml
target:
kind: StatefulSet
name: myapp
`)
th.WriteF("resource.yaml", `
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: consul
image: "hashicorp/consul:1.9.1"
ports:
- containerPort: 8500
name: http
- containerPort: 8501
name: https
- containerPort: 8301
protocol: "TCP"
name: serflan-tcp
- containerPort: 8301
protocol: "UDP"
name: serflan-udp
- containerPort: 8302
name: serfwan
- containerPort: 8300
name: server
- containerPort: 8600
name: dns-tcp
protocol: "TCP"
- containerPort: 8600
name: dns-udp
protocol: "UDP"`)
th.WriteF("patch.yaml", `
kind: StatefulSet
metadata:
name: myapp
labels:
test: label
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
test: label
name: myapp
spec:
template:
spec:
containers:
- image: hashicorp/consul:1.9.1
name: consul
ports:
- containerPort: 8301
name: serflan-tcp
protocol: TCP
- containerPort: 8301
name: serflan-udp
protocol: UDP
- containerPort: 8600
name: dns-tcp
protocol: TCP
- containerPort: 8600
name: dns-udp
protocol: UDP
- containerPort: 8500
name: http
- containerPort: 8501
name: https
- containerPort: 8302
name: serfwan
- containerPort: 8300
name: server
`)
}

View File

@@ -15,7 +15,6 @@ func TestNamePrefixSuffixPatch(t *testing.T) {
th.WriteF("handlers/kustomization.yaml", ` th.WriteF("handlers/kustomization.yaml", `
nameSuffix: -suffix nameSuffix: -suffix
resources: resources:
- deployment.yaml - deployment.yaml
`) `)

View File

@@ -1,11 +1,517 @@
package krusty_test package krusty_test
import ( import (
"fmt"
"testing" "testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest" kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
) )
func TestIssue3489Simplified(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
namespace: kube-system
resources:
- aa
- bb
`)
th.WriteK("aa", `
resources:
- ../base
`)
th.WriteK("bb", `
resources:
- ../base
nameSuffix: -private
`)
th.WriteK("base", `
resources:
- deployment.yaml
- serviceAccount.yaml
`)
th.WriteF("base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDep
spec:
template:
spec:
serviceAccountName: mySvcAcct
containers:
- name: whatever
image: k8s.gcr.io/governmentCheese
`)
th.WriteF("base/serviceAccount.yaml", `
apiVersion: v1
kind: ServiceAccount
metadata:
name: mySvcAcct
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDep
namespace: kube-system
spec:
template:
spec:
containers:
- image: k8s.gcr.io/governmentCheese
name: whatever
serviceAccountName: mySvcAcct
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: mySvcAcct
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDep-private
namespace: kube-system
spec:
template:
spec:
containers:
- image: k8s.gcr.io/governmentCheese
name: whatever
serviceAccountName: mySvcAcct-private
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: mySvcAcct-private
namespace: kube-system
`)
}
func TestIssue3489(t *testing.T) {
const assets = `{
"tenantId": "XXXXX-XXXXXX-XXXXX-XXXXXX-XXXXXX",
"subscriptionId": "XXXXX-XXXXXX-XXXXX-XXXXXX-XXXXXX",
"resourceGroup": "DNS-EUW-XXX-RG",
"useManagedIdentityExtension": true,
"userAssignedIdentityID": "XXXXX-XXXXXX-XXXXX-XXXXXX-XXXXXX"
}
`
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
namespace: kube-system
resources:
- external-dns
- external-dns-private
`)
th.WriteK("external-dns", `
resources:
- ../base
commonLabels:
app: external-dns
instance: public
images:
- name: k8s.gcr.io/external-dns/external-dns
newName: xxx.azurecr.io/external-dns
newTag: v0.7.4_sylr.1
- name: quay.io/sylr/external-dns
newName: xxx.azurecr.io/external-dns
newTag: v0.7.4_sylr.1
secretGenerator:
- name: azure-config-file
behavior: replace
files:
- assets/azure.json
patches:
- target:
group: apps
version: v1
kind: Deployment
name: external-dns
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args
value:
- --txt-owner-id="aks"
- --txt-prefix=external-dns-
- --source=service
- --provider=azure
- --registry=txt
- --domain-filter=dev.company.com
`)
th.WriteF("external-dns/assets/azure.json", assets)
th.WriteK("external-dns-private", `
resources:
- ../base
nameSuffix: -private
commonLabels:
app: external-dns
instance: private
images:
- name: k8s.gcr.io/external-dns/external-dns
newName: xxx.azurecr.io/external-dns
newTag: v0.7.4_sylr.1
- name: quay.io/sylr/external-dns
newName: xxx.azurecr.io/external-dns
newTag: v0.7.4_sylr.1
secretGenerator:
- name: azure-config-file
behavior: replace
files:
- assets/azure.json
patches:
- target:
group: apps
version: v1
kind: Deployment
name: external-dns
patch: |-
- op: replace
path: /spec/template/spec/containers/0/args
value:
- --txt-owner-id="aks"
- --txt-prefix=external-dns-private-
- --source=service
- --provider=azure-private-dns
- --registry=txt
- --domain-filter=static.company.az
`)
th.WriteF("external-dns-private/assets/azure.json", assets)
th.WriteK("base", `
resources:
- clusterrole.yaml
- clusterrolebinding.yaml
- deployment.yaml
- serviceaccount.yaml
commonLabels:
app: external-dns
instance: public
images:
- name: k8s.gcr.io/external-dns/external-dns
newName: quay.io/sylr/external-dns
newTag: v0.7.4-73-g00a9a0c7
secretGenerator:
- name: azure-config-file
files:
- assets/azure.json
`)
th.WriteF("base/assets/azure.json", assets)
th.WriteF("base/clusterrolebinding.yaml", `
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
`)
th.WriteF("base/clusterrole.yaml", `
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: ['']
resources: ['endpoints', 'pods', 'services', 'nodes']
verbs: ['get', 'watch', 'list']
- apiGroups: ['extensions', 'networking.k8s.io']
resources: ['ingresses']
verbs: ['get', 'watch', 'list']
`)
th.WriteF("base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels: {}
template:
metadata: {}
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: k8s.gcr.io/external-dns/external-dns
args:
- --domain-filter=""
- --txt-owner-id=""
- --txt-prefix=external-dns-
- --source=service
- --provider=azure
- --registry=txt
resources: {}
volumeMounts:
- name: azure-config-file
mountPath: /etc/kubernetes
readOnly: true
volumes:
- name: azure-config-file
secret:
secretName: azure-config-file
`)
th.WriteF("base/serviceaccount.yaml", `
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
`)
opts := th.MakeDefaultOptions()
m := th.Run(".", opts)
expFmt := `
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app: external-dns
instance: public
name: external-dns
rules:
- apiGroups:
- ""
resources:
- endpoints
- pods
- services
- nodes
verbs:
- get
- watch
- list
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
verbs:
- get
- watch
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app: external-dns
instance: public
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: external-dns
instance: public
name: external-dns
namespace: kube-system
spec:
selector:
matchLabels:
app: external-dns
instance: public
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
instance: public
spec:
containers:
- args:
- --txt-owner-id="aks"
- --txt-prefix=external-dns-
- --source=service
- --provider=azure
- --registry=txt
- --domain-filter=dev.company.com
image: xxx.azurecr.io/external-dns:v0.7.4_sylr.1
name: external-dns
resources: {}
volumeMounts:
- mountPath: /etc/kubernetes
name: azure-config-file
readOnly: true
serviceAccountName: external-dns
volumes:
- name: azure-config-file
secret:
secretName: azure-config-file-%s
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: external-dns
instance: public
name: external-dns
namespace: kube-system
---
apiVersion: v1
data:
azure.json: %s
kind: Secret
metadata:
labels:
app: external-dns
instance: public
name: azure-config-file-%s
namespace: kube-system
type: Opaque
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app: external-dns
instance: private
name: external-dns-private
rules:
- apiGroups:
- ""
resources:
- endpoints
- pods
- services
- nodes
verbs:
- get
- watch
- list
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
verbs:
- get
- watch
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
app: external-dns
instance: private
name: external-dns-viewer-private
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns-private
subjects:
- kind: ServiceAccount
name: external-dns-private
namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: external-dns
instance: private
name: external-dns-private
namespace: kube-system
spec:
selector:
matchLabels:
app: external-dns
instance: private
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
instance: private
spec:
containers:
- args:
- --txt-owner-id="aks"
- --txt-prefix=external-dns-private-
- --source=service
- --provider=azure-private-dns
- --registry=txt
- --domain-filter=static.company.az
image: xxx.azurecr.io/external-dns:v0.7.4_sylr.1
name: external-dns
resources: {}
volumeMounts:
- mountPath: /etc/kubernetes
name: azure-config-file
readOnly: true
serviceAccountName: external-dns-private
volumes:
- name: azure-config-file
secret:
secretName: azure-config-file-private-%s
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: external-dns
instance: private
name: external-dns-private
namespace: kube-system
---
apiVersion: v1
data:
azure.json: %s
kind: Secret
metadata:
labels:
app: external-dns
instance: private
name: azure-config-file-private-%s
namespace: kube-system
type: Opaque
`
const (
nameHashKyaml = "66cc4224mm"
contentKyaml = `|
ewoJInRlbmFudElkIjogIlhYWFhYLVhYWFhYWC1YWFhYWC1YWFhYWFgtWFhYWFhYIiwKCS
JzdWJzY3JpcHRpb25JZCI6ICJYWFhYWC1YWFhYWFgtWFhYWFgtWFhYWFhYLVhYWFhYWCIs
CgkicmVzb3VyY2VHcm91cCI6ICJETlMtRVVXLVhYWC1SRyIsCgkidXNlTWFuYWdlZElkZW
50aXR5RXh0ZW5zaW9uIjogdHJ1ZSwKCSJ1c2VyQXNzaWduZWRJZGVudGl0eUlEIjogIlhY
WFhYLVhYWFhYWC1YWFhYWC1YWFhYWFgtWFhYWFhYIgp9Cg==`
nameHashApiMach = "g2k4bkgt4d"
// nolint: lll
contentApiMach = `ewoJInRlbmFudElkIjogIlhYWFhYLVhYWFhYWC1YWFhYWC1YWFhYWFgtWFhYWFhYIiwKCSJzdWJzY3JpcHRpb25JZCI6ICJYWFhYWC1YWFhYWFgtWFhYWFgtWFhYWFhYLVhYWFhYWCIsCgkicmVzb3VyY2VHcm91cCI6ICJETlMtRVVXLVhYWC1SRyIsCgkidXNlTWFuYWdlZElkZW50aXR5RXh0ZW5zaW9uIjogdHJ1ZSwKCSJ1c2VyQXNzaWduZWRJZGVudGl0eUlEIjogIlhYWFhYLVhYWFhYWC1YWFhYWC1YWFhYWFgtWFhYWFhYIgp9Cg==`
)
th.AssertActualEqualsExpected(
m,
// TODO(#3304): DECISION - kyaml better; not a bug.
opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt,
nameHashApiMach,
contentApiMach, nameHashApiMach,
nameHashApiMach,
contentApiMach, nameHashApiMach),
fmt.Sprintf(expFmt,
nameHashKyaml,
contentKyaml, nameHashKyaml,
nameHashKyaml,
contentKyaml, nameHashKyaml)))
}
func TestEmptyFieldSpecValue(t *testing.T) { func TestEmptyFieldSpecValue(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
th.WriteK("/app", ` th.WriteK("/app", `

View File

@@ -50,10 +50,10 @@ resources:
- secrets.yaml - secrets.yaml
- role.yaml - role.yaml
`) `)
// This validates Fix #1444. This should not be an error anymore - // This validates fix for Issue #1044. This should not be an error anymore -
// the secrets have the same name but are in different namespaces. // the secrets have the same name but are in different namespaces.
// The ClusterRole (by def) is not in a namespace, // The ClusterRole (by def) is not in a namespace,
// an in this case applies to *any* Secret resource // and in this case applies to *any* Secret resource
// named "dummy" // named "dummy"
m := th.Run("/app", th.MakeDefaultOptions()) m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
@@ -91,13 +91,134 @@ rules:
`) `)
} }
func TestNameReferenceDeploymentIssue3489(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
resources:
- cm.yaml
- dep.yaml
`)
th.WriteF("base/cm.yaml", `
apiVersion: v1
kind: ConfigMap
metadata:
name: myMap
`)
th.WriteF("base/dep.yaml", `
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: myDep
spec:
template:
spec:
containers:
- env:
- name: CM_FOO
valueFrom:
configMapKeyRef:
key: foo
name: myMap
`)
th.WriteK("ov1", `
resources:
- ../base
namePrefix: pp-
`)
th.WriteK("ov2", `
resources:
- ../base
nameSuffix: -ss
`)
th.WriteK("ov3", `
resources:
- ../base
namespace: fred
nameSuffix: -xx
`)
th.WriteK(".", `
resources:
- ../ov1
- ../ov2
- ../ov3
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: ConfigMap
metadata:
name: pp-myMap
---
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: pp-myDep
spec:
template:
spec:
containers:
- env:
- name: CM_FOO
valueFrom:
configMapKeyRef:
key: foo
name: pp-myMap
---
apiVersion: v1
kind: ConfigMap
metadata:
name: myMap-ss
---
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: myDep-ss
spec:
template:
spec:
containers:
- env:
- name: CM_FOO
valueFrom:
configMapKeyRef:
key: foo
name: myMap-ss
---
apiVersion: v1
kind: ConfigMap
metadata:
name: myMap-xx
namespace: fred
---
apiVersion: v1
group: apps
kind: Deployment
metadata:
name: myDep-xx
namespace: fred
spec:
template:
spec:
containers:
- env:
- name: CM_FOO
valueFrom:
configMapKeyRef:
key: foo
name: myMap-xx
`)
}
// TestNameAndNsTransformation validates that NamespaceTransformer, // TestNameAndNsTransformation validates that NamespaceTransformer,
// PrefixSuffixTransformer and namereference transformers are // PrefixSuffixTransformer and namereference transformers are
// able to deal with simultaneous change of namespace and name. // able to deal with simultaneous change of namespace and name.
func TestNameAndNsTransformation(t *testing.T) { func TestNameAndNsTransformation(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
th.WriteK("/nameandns", ` th.WriteK(".", `
namePrefix: p1- namePrefix: p1-
nameSuffix: -s1 nameSuffix: -s1
namespace: newnamespace namespace: newnamespace
@@ -105,7 +226,7 @@ resources:
- resources.yaml - resources.yaml
`) `)
th.WriteF("/nameandns/resources.yaml", ` th.WriteF("resources.yaml", `
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:
@@ -204,7 +325,7 @@ kind: PersistentVolume
metadata: metadata:
name: pv1 name: pv1
`) `)
m := th.Run("/nameandns", th.MakeDefaultOptions()) m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
@@ -532,9 +653,6 @@ vars:
func TestVariablesAmbiguousWorkaround(t *testing.T) { func TestVariablesAmbiguousWorkaround(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions() opts := th.MakeDefaultOptions()
if opts.UseKyaml {
t.Skip("TODO(#3396)")
}
th.WriteK("dev", namespaceNeedInVarDevFolder) th.WriteK("dev", namespaceNeedInVarDevFolder)
th.WriteF("dev/elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources) th.WriteF("dev/elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources)
th.WriteK("test", namespaceNeedInVarTestFolder) th.WriteK("test", namespaceNeedInVarTestFolder)
@@ -591,13 +709,68 @@ vars:
// to the variable declarations allows to disambiguate the variables. // to the variable declarations allows to disambiguate the variables.
func TestVariablesDisambiguatedWithNamespace(t *testing.T) { func TestVariablesDisambiguatedWithNamespace(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
if opts.UseKyaml {
t.Skip("TODO(#3396)")
}
th.WriteK(".", namespaceNeedInVarMyAppWithNamespace) th.WriteK(".", namespaceNeedInVarMyAppWithNamespace)
th.WriteF("elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources) th.WriteF("elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources)
th.WriteF("elasticsearch-test-service.yaml", namespaceNeedInVarTestResources) th.WriteF("elasticsearch-test-service.yaml", namespaceNeedInVarTestResources)
m := th.Run(".", th.MakeDefaultOptions()) m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, namespaceNeedInVarExpectedOutput) th.AssertActualEqualsExpected(m, namespaceNeedInVarExpectedOutput)
} }
// TestAddNamePrefixWithNamespace tests that adding a name prefix works within
// namespaces other than the default namespace.
// Test for issue #3430
func TestAddNamePrefixWithNamespace(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("/app/serviceaccount.yaml", `
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
`)
th.WriteF("/app/clusterrolebinding.yaml", `
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: iter8-monitoring
`)
th.WriteK("/app", `
namePrefix: iter8-
namespace: iter8-monitoring
resources:
- clusterrolebinding.yaml
- serviceaccount.yaml
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: iter8-prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: iter8-prometheus
namespace: iter8-monitoring
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: iter8-prometheus
namespace: iter8-monitoring
`)
}

View File

@@ -0,0 +1,301 @@
package krusty_test
import (
"testing"
"github.com/stretchr/testify/assert"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
"sigs.k8s.io/kustomize/kyaml/openapi"
"sigs.k8s.io/kustomize/kyaml/openapi/kubernetesapi"
)
func TestOpenApiFieldBasicUsage(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
openapi:
version: v1.18.8
resources:
- deployment.yaml
`)
th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
assert.Equal(t, "v1188", openapi.GetSchemaVersion())
}
func TestOpenApiFieldNotBuiltin(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
openapi:
version: v1.14.1
resources:
- deployment.yaml
`)
th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
err := th.RunWithErr("/app", th.MakeDefaultOptions())
if err == nil {
t.Fatalf("expected an error")
}
}
func TestOpenApiFieldDefaultVersion(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app", `
resources:
- deployment.yaml
`)
th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion())
}
func TestOpenApiFieldFromBase(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
openapi:
version: v1.19.0
namePrefix: a-
resources:
- deployment.yaml
`)
th.WriteF("base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
th.WriteK("overlay", `
namePrefix: b-
resources:
- ../base
patchesStrategicMerge:
- depPatch.yaml
`)
th.WriteF("overlay/depPatch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
replicas: 999
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: b-a-myDeployment
spec:
replicas: 999
template:
spec:
containers:
- image: whatever
`)
assert.Equal(t, "v1190", openapi.GetSchemaVersion())
}
func TestOpenApiFieldFromOverlay(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
namePrefix: a-
resources:
- deployment.yaml
`)
th.WriteF("base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
th.WriteK("overlay", `
openapi:
version: v1.18.8
namePrefix: b-
resources:
- ../base
patchesStrategicMerge:
- depPatch.yaml
`)
th.WriteF("overlay/depPatch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
replicas: 999
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: b-a-myDeployment
spec:
replicas: 999
template:
spec:
containers:
- image: whatever
`)
assert.Equal(t, "v1188", openapi.GetSchemaVersion())
}
func TestOpenApiFieldOverlayTakesPrecedence(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("base", `
openapi:
version: v1.19.0
namePrefix: a-
resources:
- deployment.yaml
`)
th.WriteF("base/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
template:
spec:
containers:
- image: whatever
`)
th.WriteK("overlay", `
openapi:
version: v1.18.8
namePrefix: b-
resources:
- ../base
patchesStrategicMerge:
- depPatch.yaml
`)
th.WriteF("overlay/depPatch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: myDeployment
spec:
replicas: 999
`)
m := th.Run("overlay", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: b-a-myDeployment
spec:
replicas: 999
template:
spec:
containers:
- image: whatever
`)
assert.Equal(t, "v1188", openapi.GetSchemaVersion())
}
func TestOpenAPIFieldFromComponentDefault(t *testing.T) {
input := []FileGen{writeTestBase, writeTestComponent, writeOverlayProd}
runPath := "/app/prod"
th := kusttest_test.MakeHarness(t)
for _, f := range input {
f(th)
}
th.Run(runPath, th.MakeDefaultOptions())
assert.Equal(t, kubernetesapi.DefaultOpenAPI, openapi.GetSchemaVersion())
}
func writeTestComponentWithOlderOpenAPIVersion(th kusttest_test.Harness) {
th.WriteC("/app/comp", `
openapi:
version: v1.18.8
`)
th.WriteF("/app/comp/stub.yaml", `
apiVersion: v1
kind: Deployment
metadata:
name: stub
spec:
replicas: 1
`)
}
func TestOpenAPIFieldFromComponent(t *testing.T) {
input := []FileGen{
writeTestBase,
writeTestComponentWithOlderOpenAPIVersion,
writeOverlayProd}
runPath := "/app/prod"
th := kusttest_test.MakeHarness(t)
for _, f := range input {
f(th)
}
th.Run(runPath, th.MakeDefaultOptions())
assert.Equal(t, "v1188", openapi.GetSchemaVersion())
}

View File

@@ -4,6 +4,7 @@
package krusty package krusty
import ( import (
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
"sigs.k8s.io/kustomize/api/konfig" "sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
@@ -61,3 +62,15 @@ func (o Options) IfApiMachineryElseKyaml(s1, s2 string) string {
} }
return s2 return s2
} }
// GetBuiltinPluginNames returns a list of builtin plugin names
func GetBuiltinPluginNames() []string {
var ret []string
for k := range builtinhelpers.GeneratorFactories {
ret = append(ret, k.String())
}
for k := range builtinhelpers.TransformerFactories {
ret = append(ret, k.String())
}
return ret
}

View File

@@ -0,0 +1,74 @@
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestPatchDeleteOfNotExistingAttributesShouldNotAddExtraElements(t *testing.T) {
th := kusttest_test.MakeEnhancedHarness(t)
defer th.Reset()
th.WriteF("resource.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: whatever
spec:
template:
spec:
containers:
- env:
- name: EXISTING
value: EXISTING_VALUE
- name: FOR_REMOVAL
value: FOR_REMOVAL_VALUE
name: whatever
image: helloworld
`)
th.WriteF("patch.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: whatever
spec:
template:
spec:
containers:
- name: whatever
env:
- name: FOR_REMOVAL
$patch: delete
- name: NOT_EXISTING_FOR_REMOVAL
$patch: delete
`)
th.WriteK(".", `
resources:
- resource.yaml
patches:
- path: patch.yaml
target:
kind: Deployment
`)
// It's expected that removal of not existing elements should not introduce extra values,
// as a patch can be applied to multiple resources, not all of them can have all the elements being deleted.
expected := `
apiVersion: apps/v1
kind: Deployment
metadata:
name: whatever
spec:
template:
spec:
containers:
- env:
- name: EXISTING
value: EXISTING_VALUE
image: helloworld
name: whatever
`
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, expected)
}

View File

@@ -0,0 +1,134 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"fmt"
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestPodDisruptionBudgetBasics(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteF("pdbLiteral.yaml", `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdbLiteral
spec:
maxUnavailable: 90
`)
th.WriteF("pdbPercentage.yaml", `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdbPercentage
spec:
maxUnavailable: 90%
`)
th.WriteK(".", `
resources:
- pdbLiteral.yaml
- pdbPercentage.yaml
`)
m := th.Run(".", th.MakeDefaultOptions())
// In a PodDisruptionBudget, the fields maxUnavailable
// minAvailable are mutually exclusive, and both can hold
// either an integer, i.e. 10, or string that has to be
// an int followed by a percent sign, e.g. 10%.
th.AssertActualEqualsExpected(m, `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdbLiteral
spec:
maxUnavailable: 90
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: pdbPercentage
spec:
maxUnavailable: 90%
`)
}
func TestPodDisruptionBudgetMerging(t *testing.T) {
th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
th.WriteF("pdb-patch.yaml", `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: generic-pdb
spec:
maxUnavailable: 1
`)
th.WriteF("my_file.yaml", `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: championships-api
labels:
faceit-pdb: default
spec:
maxUnavailable: 100%
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: championships-api-2
labels:
faceit-pdb: default
spec:
maxUnavailable: 100%
`)
th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: pdb-patch.yaml
target:
kind: PodDisruptionBudget
labelSelector: faceit-pdb=default
resources:
- my_file.yaml
`)
m := th.Run(".", opts)
expFmt := `
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
labels:
faceit-pdb: default
name: championships-api
spec:
maxUnavailable: %s
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
labels:
faceit-pdb: default
name: championships-api-2
spec:
maxUnavailable: %s
`
// In a PodDisruptionBudget, the fields maxUnavailable
// minAvailable are mutually exclusive, and both can hold
// either an integer, i.e. 10, or string that has to be
// an int followed by a percent sign, e.g. 10%.
// In the former case - bare integer - they should NOT be quoted
// as the api server will reject it. In the latter case with
// the percent sign, quotes can be added and the API server will
// accept it, but they don't have to be added.
th.AssertActualEqualsExpected(
m,
// TODO(#3304): DECISION - kyaml better; not a bug.
opts.IfApiMachineryElseKyaml(
fmt.Sprintf(expFmt, `"1"`, `"1"`),
fmt.Sprintf(expFmt, `1`, `1`)))
}

View File

@@ -362,7 +362,7 @@ metadata:
if err == nil { if err == nil {
t.Fatalf("expected error") t.Fatalf("expected error")
} }
if !strings.Contains(err.Error(), "multiple matches for ~G_v1_ServiceAccount|~X|serviceaccount") { if !strings.Contains(err.Error(), "found multiple possible referrals") {
t.Fatalf("unexpected error %v", err) t.Fatalf("unexpected error %v", err)
} }
} }

View File

@@ -80,7 +80,7 @@ data:
`) `)
m := th.Run("/app", th.MakeOptionsPluginsEnabled()) m := th.Run("/app", th.MakeOptionsPluginsEnabled())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpectedNoIdAnnotations(m, `
apiVersion: v1 apiVersion: v1
data: data:
foo: foo foo: foo

View File

@@ -360,12 +360,189 @@ resources:
} }
} }
func TestSimpleServicePortVarReplace(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- service.yaml
- statefulset.yaml
vars:
- name: THE_PORT
objref:
kind: StatefulSet
name: cockroachdb
apiVersion: apps/v1beta1
fieldref:
fieldpath: spec.template.spec.containers[0].ports[1].containerPort
`)
th.WriteF("service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: myService
spec:
ports:
- port: $(THE_PORT)
targetPort: $(THE_PORT)
name: grpc
`)
th.WriteF("statefulset.yaml", `
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: cockroachdb
spec:
template:
spec:
containers:
- name: cockroachdb
image: cockroachdb/cockroach:v1.1.5
ports:
- containerPort: 26257
name: grpc
- containerPort: 8888
name: http
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: v1
kind: Service
metadata:
name: myService
spec:
ports:
- name: grpc
port: 8888
targetPort: 8888
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: cockroachdb
spec:
template:
spec:
containers:
- image: cockroachdb/cockroach:v1.1.5
name: cockroachdb
ports:
- containerPort: 26257
name: grpc
- containerPort: 8888
name: http
`)
}
// TODO(3449): Yield bare primitives in var replacements from configmaps.
// The ConfigMap data field is always strings, and anything that looks
// like a boolean or int or float must be quoted, or the API server won't
// accept the map. This creates a problem if one wants to use a var to
// inject a raw number or raw boolean sourced from a configmap, because as
// far as the configmap representation is concerned, it's a string.
// A workaround would be to source the var from another Kind, from a field
// that allowed unquoted vars or booleans.
func TestIssue3449(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- workflow.yaml
configurations:
- kustomization-config.yaml
configMapGenerator:
- name: kustomize-vars
envs:
- vars.env
vars:
- name: DBT_TARGET
objref: &config-map-ref
kind: ConfigMap
name: kustomize-vars
apiVersion: v1
fieldref:
fieldpath: data.DBT_TARGET
- name: SUSPENDED
objref: *config-map-ref
fieldref:
fieldpath: data.SUSPENDED
`)
th.WriteF("workflow.yaml", `
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
name: cron-core-load-workflow
spec:
schedule: "45 2 * * *"
timezone: "Europe/Vienna"
concurrencyPolicy: Forbid
suspend: $(SUSPENDED)
workflowMetadata:
labels:
workflowName: core-load-workflow
workflowSpec:
workflowTemplateRef:
name: core-load-pipeline
arguments:
parameters:
- name: dbt_target
value: $(DBT_TARGET)
`)
th.WriteF("kustomization-config.yaml", `
nameReference:
- kind: ConfigMap
version: v1
fieldSpecs:
- kind: CronWorkflow
version: v1alpha1
path: spec/workflowSpec/arguments/parameters/value
varReference:
- path: spec/workflowSpec/arguments/parameters/value
kind: CronWorkflow
apiVersion: argoproj.io/v1alpha1
- path: spec
kind: CronWorkflow
apiVersion: argoproj.io/v1alpha1
`)
th.WriteF("vars.env", `
DBT_TARGET=development
SUSPENDED=True
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
name: cron-core-load-workflow
spec:
concurrencyPolicy: Forbid
schedule: 45 2 * * *
suspend: "True"
timezone: Europe/Vienna
workflowMetadata:
labels:
workflowName: core-load-workflow
workflowSpec:
arguments:
parameters:
- name: dbt_target
value: development
workflowTemplateRef:
name: core-load-pipeline
---
apiVersion: v1
data:
DBT_TARGET: development
SUSPENDED: "True"
kind: ConfigMap
metadata:
name: kustomize-vars-7mhm8cg5kg
`)
}
func TestVarRefBig(t *testing.T) { func TestVarRefBig(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
opts := th.MakeDefaultOptions()
if opts.UseKyaml {
t.Skip("TODO(#3396)")
}
th.WriteK("/app/base", ` th.WriteK("/app/base", `
namePrefix: base- namePrefix: base-
resources: resources:
@@ -682,7 +859,7 @@ namePrefix: dev-
resources: resources:
- ../../base - ../../base
`) `)
m := th.Run("/app/overlay/staging", opts) m := th.Run("/app/overlay/staging", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: v1 apiVersion: v1
kind: ServiceAccount kind: ServiceAccount
@@ -929,7 +1106,64 @@ metadata:
`) `)
} }
func TestVariableRefIngress(t *testing.T) { func TestVariableRefIngressBasic(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK(".", `
resources:
- ingress.yaml
- deployment.yaml
vars:
- name: DEPLOYMENT_NAME
objref:
apiVersion: apps/v1
kind: Deployment
name: nginxDep
fieldref:
fieldpath: metadata.name
`)
th.WriteF("deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginxDep
`)
th.WriteF("ingress.yaml", `
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginxIngress
spec:
rules:
- host: $(DEPLOYMENT_NAME).example.com
tls:
- hosts:
- $(DEPLOYMENT_NAME).example.com
secretName: $(DEPLOYMENT_NAME).example.com-tls
`)
m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginxIngress
spec:
rules:
- host: nginxDep.example.com
tls:
- hosts:
- nginxDep.example.com
secretName: nginxDep.example.com-tls
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginxDep
`)
}
func TestVariableRefIngressOverlay(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", ` th.WriteK("/app/base", `
resources: resources:
@@ -1976,67 +2210,64 @@ spec:
func TestDeploymentAnnotations(t *testing.T) { func TestDeploymentAnnotations(t *testing.T) {
th := kusttest_test.MakeHarness(t) th := kusttest_test.MakeHarness(t)
th.WriteK("/app", ` th.WriteK(".", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator: configMapGenerator:
- name: testConfigMap - name: theConfigMap
envs: envs:
- test.properties - test.properties
vars: vars:
- name: FOO - name: SOMERIVER
objref: objref:
kind: ConfigMap kind: ConfigMap
name: testConfigMap name: theConfigMap
apiVersion: v1 apiVersion: v1
fieldref: fieldref:
fieldpath: data.foo fieldpath: data.waterway
commonAnnotations: commonAnnotations:
foo: $(FOO) river: $(SOMERIVER)
resources: resources:
- deployment.yaml - deployment.yaml
`) `)
th.WriteF("/app/deployment.yaml", ` th.WriteF("deployment.yaml", `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
name: test name: theDeployment
spec: spec:
template: template:
spec: spec:
containers: containers:
- name: test - name: test
`) `)
th.WriteF("/app/test.properties", `foo=bar`) th.WriteF("test.properties", `waterway=mississippi`)
m := th.Run("/app", th.MakeDefaultOptions()) m := th.Run(".", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, ` th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
metadata: metadata:
annotations: annotations:
foo: bar river: mississippi
name: test name: theDeployment
spec: spec:
template: template:
metadata: metadata:
annotations: annotations:
foo: bar river: mississippi
spec: spec:
containers: containers:
- name: test - name: test
--- ---
apiVersion: v1 apiVersion: v1
data: data:
foo: bar waterway: mississippi
kind: ConfigMap kind: ConfigMap
metadata: metadata:
annotations: annotations:
foo: bar river: mississippi
name: testConfigMap-798k5k7g9f name: theConfigMap-hdd8h8cgdt
`) `)
} }

View File

@@ -389,10 +389,10 @@ spec:
rm, err := rmF.ConflatePatches([]*resource.Resource{r1, r2}) rm, err := rmF.ConflatePatches([]*resource.Resource{r1, r2})
assert.NoError(t, err) assert.NoError(t, err)
yml, err = rm.AsYaml() yml, err = rm.AsYaml()
assert.NoError(t, err) assert.NoError(t, err)
// TODO(#3304): DECISION - kyaml better; not a bug.
assert.Equal(t, konfig.IfApiMachineryElseKyaml(`apiVersion: example.com/v1 assert.Equal(t, konfig.IfApiMachineryElseKyaml(`apiVersion: example.com/v1
kind: Foo kind: Foo
metadata: metadata:

View File

@@ -47,7 +47,7 @@ func (m *merginator) ConflatePatches(in []*resource.Resource) (ResMap, error) {
func (m *merginator) appendIfNoMatch(index int) (*resource.Resource, error) { func (m *merginator) appendIfNoMatch(index int) (*resource.Resource, error) {
candidate := m.incoming[index] candidate := m.incoming[index]
matchedResources := m.result.GetMatchingResourcesByOriginalId( matchedResources := m.result.GetMatchingResourcesByAnyId(
candidate.OrgId().Equals) candidate.OrgId().Equals)
if len(matchedResources) == 0 { if len(matchedResources) == 0 {
m.result.Append(candidate) m.result.Append(candidate)

View File

@@ -142,24 +142,18 @@ type ResMap interface {
// who's CurId is matched by the argument. // who's CurId is matched by the argument.
GetMatchingResourcesByCurrentId(matches IdMatcher) []*resource.Resource GetMatchingResourcesByCurrentId(matches IdMatcher) []*resource.Resource
// GetMatchingResourcesByOriginalId returns the resources // GetMatchingResourcesByAnyId returns the resources
// who's OriginalId is matched by the argument. // who's current or previous IDs is matched by the argument.
GetMatchingResourcesByOriginalId(matches IdMatcher) []*resource.Resource GetMatchingResourcesByAnyId(matches IdMatcher) []*resource.Resource
// GetByCurrentId is shorthand for calling // GetByCurrentId is shorthand for calling
// GetMatchingResourcesByCurrentId with a matcher requiring // GetMatchingResourcesByCurrentId with a matcher requiring
// an exact match, returning an error on multiple or no matches. // an exact match, returning an error on multiple or no matches.
GetByCurrentId(resid.ResId) (*resource.Resource, error) GetByCurrentId(resid.ResId) (*resource.Resource, error)
// GetByOriginalId is shorthand for calling // GetById is shorthand for calling
// GetMatchingResourcesByOriginalId with a matcher requiring // GetMatchingResourcesByAnyId with a matcher requiring
// an exact match, returning an error on multiple or no matches. // an exact match, returning an error on multiple or no matches.
GetByOriginalId(resid.ResId) (*resource.Resource, error)
// GetById is a helper function which first
// attempts GetByOriginalId, then GetByCurrentId,
// returning an error if both fail to find a single
// match.
GetById(resid.ResId) (*resource.Resource, error) GetById(resid.ResId) (*resource.Resource, error)
// GroupedByCurrentNamespace returns a map of namespace // GroupedByCurrentNamespace returns a map of namespace
@@ -245,4 +239,7 @@ type ResMap interface {
// selected set of resources. // selected set of resources.
ApplySmPatch( ApplySmPatch(
selectedSet *resource.IdSet, patch *resource.Resource) error selectedSet *resource.IdSet, patch *resource.Resource) error
// RemoveBuildAnnotations removes annotations created by the build process.
RemoveBuildAnnotations()
} }

View File

@@ -155,8 +155,7 @@ func (m *resWrangler) GetIndexOfCurrentId(id resid.ResId) (int, error) {
type IdFromResource func(r *resource.Resource) resid.ResId type IdFromResource func(r *resource.Resource) resid.ResId
func GetOriginalId(r *resource.Resource) resid.ResId { return r.OrgId() } func GetCurrentId(r *resource.Resource) resid.ResId { return r.CurId() }
func GetCurrentId(r *resource.Resource) resid.ResId { return r.CurId() }
// GetMatchingResourcesByCurrentId implements ResMap. // GetMatchingResourcesByCurrentId implements ResMap.
func (m *resWrangler) GetMatchingResourcesByCurrentId( func (m *resWrangler) GetMatchingResourcesByCurrentId(
@@ -164,10 +163,19 @@ func (m *resWrangler) GetMatchingResourcesByCurrentId(
return m.filteredById(matches, GetCurrentId) return m.filteredById(matches, GetCurrentId)
} }
// GetMatchingResourcesByOriginalId implements ResMap. // GetMatchingResourcesByAnyId implements ResMap.
func (m *resWrangler) GetMatchingResourcesByOriginalId( func (m *resWrangler) GetMatchingResourcesByAnyId(
matches IdMatcher) []*resource.Resource { matches IdMatcher) []*resource.Resource {
return m.filteredById(matches, GetOriginalId) var result []*resource.Resource
for _, r := range m.rList {
for _, id := range append(r.PrevIds(), r.CurId()) {
if matches(id) {
result = append(result, r)
break
}
}
}
return result
} }
func (m *resWrangler) filteredById( func (m *resWrangler) filteredById(
@@ -187,26 +195,16 @@ func (m *resWrangler) GetByCurrentId(
return demandOneMatch(m.GetMatchingResourcesByCurrentId, id, "Current") return demandOneMatch(m.GetMatchingResourcesByCurrentId, id, "Current")
} }
// GetByOriginalId implements ResMap.
func (m *resWrangler) GetByOriginalId(
id resid.ResId) (*resource.Resource, error) {
return demandOneMatch(m.GetMatchingResourcesByOriginalId, id, "Original")
}
// GetById implements ResMap. // GetById implements ResMap.
func (m *resWrangler) GetById( func (m *resWrangler) GetById(
id resid.ResId) (*resource.Resource, error) { id resid.ResId) (*resource.Resource, error) {
match, err1 := m.GetByOriginalId(id) r, err := demandOneMatch(m.GetMatchingResourcesByAnyId, id, "Id")
if err1 == nil { if err != nil {
return match, nil return nil, fmt.Errorf(
"%s; failed to find unique target for patch %s",
err.Error(), id.GvknString())
} }
match, err2 := m.GetByCurrentId(id) return r, nil
if err2 == nil {
return match, nil
}
return nil, fmt.Errorf(
"%s; %s; failed to find unique target for patch %s",
err1.Error(), err2.Error(), id.GvknString())
} }
type resFinder func(IdMatcher) []*resource.Resource type resFinder func(IdMatcher) []*resource.Resource
@@ -218,7 +216,7 @@ func demandOneMatch(
return r[0], nil return r[0], nil
} }
if len(r) > 1 { if len(r) > 1 {
return nil, fmt.Errorf("multiple matches for %sId %s", s, id) return nil, fmt.Errorf("multiple matches for %s %s", s, id)
} }
return nil, fmt.Errorf("no matches for %sId %s", s, id) return nil, fmt.Errorf("no matches for %sId %s", s, id)
} }
@@ -272,9 +270,9 @@ func (m *resWrangler) AsYaml() ([]byte, error) {
var b []byte var b []byte
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
for _, res := range m.Resources() { for _, res := range m.Resources() {
out, err := yaml.Marshal(res.Map()) out, err := res.AsYAML()
if err != nil { if err != nil {
return nil, err return nil, errors.Wrapf(err, "%#v", res.Map())
} }
if firstObj { if firstObj {
firstObj = false firstObj = false
@@ -377,52 +375,59 @@ func (m *resWrangler) makeCopy(copier resCopier) ResMap {
// SubsetThatCouldBeReferencedByResource implements ResMap. // SubsetThatCouldBeReferencedByResource implements ResMap.
func (m *resWrangler) SubsetThatCouldBeReferencedByResource( func (m *resWrangler) SubsetThatCouldBeReferencedByResource(
inputRes *resource.Resource) ResMap { referrer *resource.Resource) ResMap {
referrerId := referrer.CurId()
if !referrerId.IsNamespaceableKind() {
// A cluster scoped resource can refer to anything.
return m
}
result := newOne() result := newOne()
inputId := inputRes.CurId() roleBindingNamespaces := getNamespacesForRoleBinding(referrer)
isInputIdNamespaceable := inputId.IsNamespaceableKind() for _, possibleTarget := range m.Resources() {
subjectNamespaces := getNamespacesForRoleBinding(inputRes) id := possibleTarget.CurId()
for _, r := range m.Resources() { if !id.IsNamespaceableKind() {
// Need to match more accuratly both at the time of selection and transformation. // A cluster-scoped resource can be referred to by anything.
// OutmostPrefixSuffixEquals is not accurate enough since it is only using result.append(possibleTarget)
// the outer most suffix and the last prefix. Use PrefixedSuffixesEquals instead. continue
resId := r.CurId() }
if !isInputIdNamespaceable || !resId.IsNamespaceableKind() || resId.IsNsEquals(inputId) || if id.IsNsEquals(referrerId) {
isRoleBindingNamespace(&subjectNamespaces, r.GetNamespace()) { // The two objects are in the same namespace.
result.append(r) result.append(possibleTarget)
continue
}
// The two objects are namespaced (not cluster-scoped), AND
// are in different namespaces.
// There's still a chance they can refer to each other.
ns := possibleTarget.GetNamespace()
if roleBindingNamespaces[ns] {
result.append(possibleTarget)
} }
} }
return result return result
} }
// isRoleBindingNamespace returns true is the namespace `ns` is in role binding // getNamespacesForRoleBinding returns referenced ServiceAccount namespaces
// namespaces `m` // if the resource is a RoleBinding
func isRoleBindingNamespace(m *map[string]bool, ns string) bool { func getNamespacesForRoleBinding(r *resource.Resource) map[string]bool {
return (*m)[ns] result := make(map[string]bool)
} if r.GetKind() != "RoleBinding" {
return result
// getNamespacesForRoleBinding returns referenced ServiceAccount namespaces if the inputRes is
// a RoleBinding
func getNamespacesForRoleBinding(inputRes *resource.Resource) map[string]bool {
res := make(map[string]bool)
if inputRes.GetKind() != "RoleBinding" {
return res
} }
subjects, err := inputRes.GetSlice("subjects") subjects, err := r.GetSlice("subjects")
if err != nil || subjects == nil { if err != nil || subjects == nil {
return res return result
} }
for _, s := range subjects { for _, s := range subjects {
subject := s.(map[string]interface{}) subject := s.(map[string]interface{})
if subject["namespace"] == nil || subject["kind"] == nil || if ns, ok1 := subject["namespace"]; ok1 {
subject["kind"].(string) != "ServiceAccount" { if kind, ok2 := subject["kind"]; ok2 {
continue if kind.(string) == "ServiceAccount" {
result[ns.(string)] = true
}
}
} }
res[subject["namespace"].(string)] = true
} }
return result
return res
} }
func (m *resWrangler) append(res *resource.Resource) { func (m *resWrangler) append(res *resource.Resource) {
@@ -458,10 +463,7 @@ func (m *resWrangler) AbsorbAll(other ResMap) error {
func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error { func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
id := res.CurId() id := res.CurId()
matches := m.GetMatchingResourcesByOriginalId(id.Equals) matches := m.GetMatchingResourcesByAnyId(id.Equals)
if len(matches) == 0 {
matches = m.GetMatchingResourcesByCurrentId(id.Equals)
}
switch len(matches) { switch len(matches) {
case 0: case 0:
switch res.Behavior() { switch res.Behavior() {
@@ -586,8 +588,7 @@ func (m *resWrangler) ApplySmPatch(
continue continue
} }
patchCopy := patch.DeepCopy() patchCopy := patch.DeepCopy()
patchCopy.SetName(res.GetName()) patchCopy.CopyMergeMetaDataFieldsFrom(patch)
patchCopy.SetNamespace(res.GetNamespace())
patchCopy.SetGvk(res.GetGvk()) patchCopy.SetGvk(res.GetGvk())
err := res.ApplySmPatch(patchCopy) err := res.ApplySmPatch(patchCopy)
if err != nil { if err != nil {
@@ -619,3 +620,9 @@ func (m *resWrangler) ApplySmPatch(
m.AppendAll(newRm) m.AppendAll(newRm)
return nil return nil
} }
func (m *resWrangler) RemoveBuildAnnotations() {
for _, r := range m.Resources() {
r.RemoveBuildAnnotations()
}
}

View File

@@ -331,6 +331,134 @@ func TestGetMatchingResourcesByCurrentId(t *testing.T) {
} }
} }
func TestGetMatchingResourcesByAnyId(t *testing.T) {
r1 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "new-alice",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousNames": "alice",
"config.kubernetes.io/previousNamespaces": "default",
},
},
})
r2 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "new-bob",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousNames": "bob,bob2",
"config.kubernetes.io/previousNamespaces": "default,default",
},
},
})
r3 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "new-bob",
"namespace": "new-happy",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousNames": "bob",
"config.kubernetes.io/previousNamespaces": "happy",
},
},
})
r4 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "charlie",
"namespace": "happy",
"annotations": map[string]interface{}{
"config.kubernetes.io/previousNames": "charlie",
"config.kubernetes.io/previousNamespaces": "default",
},
},
})
r5 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "charlie",
"namespace": "happy",
},
})
m := resmaptest_test.NewRmBuilder(t, rf).
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).ResMap()
// nolint:goconst
tests := []struct {
name string
matcher IdMatcher
count int
}{
{
"match everything",
func(resid.ResId) bool { return true },
5,
},
{
"match nothing",
func(resid.ResId) bool { return false },
0,
},
{
"name is alice",
func(x resid.ResId) bool { return x.Name == "alice" },
1,
},
{
"name is charlie",
func(x resid.ResId) bool { return x.Name == "charlie" },
2,
},
{
"name is bob",
func(x resid.ResId) bool { return x.Name == "bob" },
2,
},
{
"happy namespace",
func(x resid.ResId) bool {
return x.Namespace == "happy"
},
3,
},
{
"happy deployment",
func(x resid.ResId) bool {
return x.Namespace == "happy" &&
x.Gvk.Kind == "Deployment"
},
1,
},
{
"happy ConfigMap",
func(x resid.ResId) bool {
return x.Namespace == "happy" &&
x.Gvk.Kind == "ConfigMap"
},
2,
},
}
for _, tst := range tests {
result := m.GetMatchingResourcesByAnyId(tst.matcher)
if len(result) != tst.count {
t.Fatalf("test '%s'; actual: %d, expected: %d",
tst.name, len(result), tst.count)
}
}
}
func TestSubsetThatCouldBeReferencedByResource(t *testing.T) { func TestSubsetThatCouldBeReferencedByResource(t *testing.T) {
r1 := rf.FromMap( r1 := rf.FromMap(
map[string]interface{}{ map[string]interface{}{
@@ -745,7 +873,6 @@ rules:
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
rnodes, err := rm.ToRNodeSlice() rnodes, err := rm.ToRNodeSlice()
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@@ -999,6 +1126,7 @@ spec:
return return
} }
assert.False(t, tc.errorExpected) assert.False(t, tc.errorExpected)
m.RemoveBuildAnnotations()
yml, err := m.AsYaml() yml, err := m.AsYaml()
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, strings.Join(tc.expected, "---\n"), string(yml)) assert.Equal(t, strings.Join(tc.expected, "---\n"), string(yml))
@@ -1100,18 +1228,22 @@ $patch: delete
finalMapSize: 0, finalMapSize: 0,
}, },
} }
for name, test := range tests { for name := range tests {
m, err := rmF.NewResMapFromBytes([]byte(target)) tc := tests[name]
assert.NoError(t, err, name) t.Run(name, func(t *testing.T) {
idSet := resource.MakeIdSet(m.Resources()) m, err := rmF.NewResMapFromBytes([]byte(target))
assert.Equal(t, 1, idSet.Size(), name) assert.NoError(t, err, name)
p, err := rf.FromBytes([]byte(test.patch)) idSet := resource.MakeIdSet(m.Resources())
assert.NoError(t, err, name) assert.Equal(t, 1, idSet.Size(), name)
assert.NoError(t, m.ApplySmPatch(idSet, p), name) p, err := rf.FromBytes([]byte(tc.patch))
assert.Equal(t, test.finalMapSize, m.Size(), name) assert.NoError(t, err, name)
yml, err := m.AsYaml() assert.NoError(t, m.ApplySmPatch(idSet, p), name)
assert.NoError(t, err, name) assert.Equal(t, tc.finalMapSize, m.Size(), name)
assert.Equal(t, test.expected, string(yml), name) m.RemoveBuildAnnotations()
yml, err := m.AsYaml()
assert.NoError(t, err, name)
assert.Equal(t, tc.expected, string(yml), name)
})
} }
} }

View File

@@ -11,6 +11,7 @@ import (
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/kusterr" "sigs.k8s.io/kustomize/api/internal/kusterr"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
@@ -35,17 +36,12 @@ func (rf *Factory) FromMap(m map[string]interface{}) *Resource {
// FromMapWithName returns a new instance with the given "original" name. // FromMapWithName returns a new instance with the given "original" name.
func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource { func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalName(n) return rf.FromMapWithNamespaceAndName(resid.DefaultNamespace, n, m)
}
// FromMapWithNamespace returns a new instance with the given "original" namespace.
func (rf *Factory) FromMapWithNamespace(n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(n)
} }
// FromMapWithNamespaceAndName returns a new instance with the given "original" namespace. // FromMapWithNamespaceAndName returns a new instance with the given "original" namespace.
func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource { func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).setOriginalNs(ns).setOriginalName(n) return rf.makeOne(rf.kf.FromMap(m), nil).setPreviousNamespaceAndName(ns, n)
} }
// FromMapAndOption returns a new instance of Resource with given options. // FromMapAndOption returns a new instance of Resource with given options.
@@ -72,7 +68,7 @@ func (rf *Factory) makeOne(
kunStr: u, kunStr: u,
options: o, options: o,
} }
return r.setOriginalName(r.kunStr.GetName()).setOriginalNs(r.GetNamespace()) return r
} }
// SliceFromPatches returns a slice of resources given a patch path // SliceFromPatches returns a slice of resources given a patch path
@@ -157,7 +153,7 @@ func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resour
return nil, fmt.Errorf("number of names doesn't match number of resources") return nil, fmt.Errorf("number of names doesn't match number of resources")
} }
for i, res := range result { for i, res := range result {
res.originalName = names[i] res.setPreviousNamespaceAndName(resid.DefaultNamespace, names[i])
} }
return result, nil return result, nil
} }

View File

@@ -343,14 +343,13 @@ kind: List
name: "listWithAnchorReference", name: "listWithAnchorReference",
input: []types.PatchStrategicMerge{patchList2}, input: []types.PatchStrategicMerge{patchList2},
expectedOut: []*Resource{testDeploymentA, testDeploymentB}, expectedOut: []*Resource{testDeploymentA, testDeploymentB},
// See https://github.com/kubernetes-sigs/kustomize/issues/3271
// This test should not have an error, but does when kyaml is used.
// The error using kyaml is: // The error using kyaml is:
// json: unsupported type: map[interface {}]interface {} // json: unsupported type: map[interface {}]interface {}
// probably arising from too many conversions between // maybe arising from too many conversions between
// yaml, json, Resource, RNode, Unstructured etc. // yaml, json, Resource, RNode, Unstructured etc.
// These conversions can be removed after closing // These conversions go away after closing #3506
// https://github.com/kubernetes-sigs/kustomize/issues/2506 // TODO(#3271) This shouldn't have an error, but does when kyaml is used.
// TODO(#3304): DECISION - still a bug, but not a blocker to #3304 or #2506
expectedErr: konfig.FlagEnableKyamlDefaultValue, expectedErr: konfig.FlagEnableKyamlDefaultValue,
}, },
{ {

View File

@@ -4,15 +4,21 @@
package resource package resource
import ( import (
"errors"
"fmt" "fmt"
"log"
"reflect" "reflect"
"strings" "strings"
"sigs.k8s.io/kustomize/api/filters/patchstrategicmerge" "sigs.k8s.io/kustomize/api/filters/patchstrategicmerge"
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/wrappy"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil" "sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/kustomize/kyaml/kio"
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
@@ -20,14 +26,24 @@ import (
// paired with metadata used by kustomize. // paired with metadata used by kustomize.
// For more history, see sigs.k8s.io/kustomize/api/ifc.Unstructured // For more history, see sigs.k8s.io/kustomize/api/ifc.Unstructured
type Resource struct { type Resource struct {
kunStr ifc.Kunstructured kunStr ifc.Kunstructured
originalName string options *types.GenArgs
originalNs string refBy []resid.ResId
options *types.GenArgs refVarNames []string
refBy []resid.ResId }
refVarNames []string
namePrefixes []string const (
nameSuffixes []string buildAnnotationPreviousNames = konfig.ConfigAnnoDomain + "/previousNames"
buildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
buildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
buildAnnotationPreviousNamespaces = konfig.ConfigAnnoDomain + "/previousNamespaces"
)
var buildAnnotations = []string{
buildAnnotationPreviousNames,
buildAnnotationPrefixes,
buildAnnotationSuffixes,
buildAnnotationPreviousNamespaces,
} }
func (r *Resource) ResetPrimaryData(incoming *Resource) { func (r *Resource) ResetPrimaryData(incoming *Resource) {
@@ -35,7 +51,11 @@ func (r *Resource) ResetPrimaryData(incoming *Resource) {
} }
func (r *Resource) GetAnnotations() map[string]string { func (r *Resource) GetAnnotations() map[string]string {
return r.kunStr.GetAnnotations() annotations := r.kunStr.GetAnnotations()
if annotations == nil {
return make(map[string]string)
}
return annotations
} }
func (r *Resource) Copy() ifc.Kunstructured { func (r *Resource) Copy() ifc.Kunstructured {
@@ -138,8 +158,6 @@ func (r *Resource) UnmarshalJSON(s []byte) error {
type ResCtx interface { type ResCtx interface {
AddNamePrefix(p string) AddNamePrefix(p string)
AddNameSuffix(s string) AddNameSuffix(s string)
GetOutermostNamePrefix() string
GetOutermostNameSuffix() string
GetNamePrefixes() []string GetNamePrefixes() []string
GetNameSuffixes() []string GetNameSuffixes() []string
} }
@@ -169,13 +187,9 @@ func (r *Resource) CopyMergeMetaDataFieldsFrom(other *Resource) {
} }
func (r *Resource) copyOtherFields(other *Resource) { func (r *Resource) copyOtherFields(other *Resource) {
r.originalName = other.originalName
r.originalNs = other.originalNs
r.options = other.options r.options = other.options
r.refBy = other.copyRefBy() r.refBy = other.copyRefBy()
r.refVarNames = copyStringSlice(other.refVarNames) r.refVarNames = copyStringSlice(other.refVarNames)
r.namePrefixes = copyStringSlice(other.namePrefixes)
r.nameSuffixes = copyStringSlice(other.nameSuffixes)
} }
func (r *Resource) MergeDataMapFrom(o *Resource) { func (r *Resource) MergeDataMapFrom(o *Resource) {
@@ -192,21 +206,27 @@ func (r *Resource) ErrIfNotEquals(o *Resource) error {
return err return err
} }
if !r.ReferencesEqual(o) { if !r.ReferencesEqual(o) {
return fmt.Errorf("references unequal") return fmt.Errorf(
`unequal references - self:
%sreferenced by: %s
--- other:
%sreferenced by: %s
`, meYaml, r.GetRefBy(), otherYaml, o.GetRefBy())
} }
if string(meYaml) != string(otherYaml) { if string(meYaml) != string(otherYaml) {
return fmt.Errorf("--- self:\n"+ return fmt.Errorf(`--- self:
"%s\n"+ %s
"--- other:\n"+ --- other:
"%s\n", meYaml, otherYaml) %s
`, meYaml, otherYaml)
} }
return nil return nil
} }
func (r *Resource) ReferencesEqual(o *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 o.refBy { for _, ref := range other.refBy {
setOther[ref] = true setOther[ref] = true
} }
for _, ref := range r.refBy { for _, ref := range r.refBy {
@@ -242,44 +262,37 @@ func copyStringSlice(s []string) []string {
// Implements ResCtx AddNamePrefix // Implements ResCtx AddNamePrefix
func (r *Resource) AddNamePrefix(p string) { func (r *Resource) AddNamePrefix(p string) {
r.namePrefixes = append(r.namePrefixes, p) r.appendCsvAnnotation(buildAnnotationPrefixes, p)
} }
// Implements ResCtx AddNameSuffix // Implements ResCtx AddNameSuffix
func (r *Resource) AddNameSuffix(s string) { func (r *Resource) AddNameSuffix(s string) {
r.nameSuffixes = append(r.nameSuffixes, s) r.appendCsvAnnotation(buildAnnotationSuffixes, s)
} }
// Implements ResCtx GetOutermostNamePrefix func (r *Resource) appendCsvAnnotation(name, value string) {
func (r *Resource) GetOutermostNamePrefix() string { if value == "" {
if len(r.namePrefixes) == 0 { return
return ""
} }
return r.namePrefixes[len(r.namePrefixes)-1] annotations := r.GetAnnotations()
if existing, ok := annotations[name]; ok {
annotations[name] = existing + "," + value
} else {
annotations[name] = value
}
r.SetAnnotations(annotations)
} }
// Implements ResCtx GetOutermostNameSuffix func SameEndingSubarray(shortest, longest []string) bool {
func (r *Resource) GetOutermostNameSuffix() string { if len(shortest) > len(longest) {
if len(r.nameSuffixes) == 0 { longest, shortest = shortest, longest
return ""
} }
return r.nameSuffixes[len(r.nameSuffixes)-1] diff := len(longest) - len(shortest)
} if len(shortest) == 0 {
return diff == 0
func sameEndingSubarray(a, b []string) bool {
compareLen := len(b)
if len(a) < len(b) {
compareLen = len(a)
} }
for i := len(shortest) - 1; i >= 0; i-- {
if compareLen == 0 { if longest[i+diff] != shortest[i] {
return true
}
alen := len(a) - 1
blen := len(b) - 1
for i := 0; i <= compareLen-1; i++ {
if a[alen-i] != b[blen-i] {
return false return false
} }
} }
@@ -288,52 +301,46 @@ func sameEndingSubarray(a, b []string) bool {
// Implements ResCtx GetNamePrefixes // Implements ResCtx GetNamePrefixes
func (r *Resource) GetNamePrefixes() []string { func (r *Resource) GetNamePrefixes() []string {
return r.namePrefixes return r.getCsvAnnotation(buildAnnotationPrefixes)
} }
// Implements ResCtx GetNameSuffixes // Implements ResCtx GetNameSuffixes
func (r *Resource) GetNameSuffixes() []string { func (r *Resource) GetNameSuffixes() []string {
return r.nameSuffixes return r.getCsvAnnotation(buildAnnotationSuffixes)
} }
// OutermostPrefixSuffixEquals returns true if both resources func (r *Resource) getCsvAnnotation(name string) []string {
// outer suffix and prefix matches. annotations := r.GetAnnotations()
func (r *Resource) OutermostPrefixSuffixEquals(o ResCtx) bool { if _, ok := annotations[name]; !ok {
return (r.GetOutermostNamePrefix() == o.GetOutermostNamePrefix()) && (r.GetOutermostNameSuffix() == o.GetOutermostNameSuffix()) return nil
}
return strings.Split(annotations[name], ",")
} }
// PrefixesSuffixesEquals is conceptually doing the same task // PrefixesSuffixesEquals is conceptually doing the same task
// as OutermostPrefixSuffix but performs a deeper comparison // as OutermostPrefixSuffix but performs a deeper comparison
// of the suffix and prefix slices. // of the suffix and prefix slices.
//
// Important note: The PrefixSuffixTransformer is stacking the
// prefix values in the reverse order of appearance in
// the transformed name. For this reason the sameEndingSubarray
// method is used (as opposed to the sameBeginningSubarray)
// to compare the prefix slice. In the same spirit, the
// GetOutermostNamePrefix is using the last element of the
// nameprefix slice and not the first.
func (r *Resource) PrefixesSuffixesEquals(o ResCtx) bool { func (r *Resource) PrefixesSuffixesEquals(o ResCtx) bool {
return sameEndingSubarray(r.GetNamePrefixes(), o.GetNamePrefixes()) && sameEndingSubarray(r.GetNameSuffixes(), o.GetNameSuffixes()) return SameEndingSubarray(r.GetNamePrefixes(), o.GetNamePrefixes()) && SameEndingSubarray(r.GetNameSuffixes(), o.GetNameSuffixes())
} }
func (r *Resource) GetOriginalName() string { // RemoveBuildAnnotations removes annotations created by the build process.
return r.originalName // These are internal-only to kustomize, added to the data pipeline to
// track name changes so name references can be fixed.
func (r *Resource) RemoveBuildAnnotations() {
annotations := r.GetAnnotations()
if len(annotations) == 0 {
return
}
for _, a := range buildAnnotations {
delete(annotations, a)
}
r.SetAnnotations(annotations)
} }
// Making this public would be bad. func (r *Resource) setPreviousNamespaceAndName(ns string, n string) *Resource {
func (r *Resource) setOriginalName(n string) *Resource { r.appendCsvAnnotation(buildAnnotationPreviousNames, n)
r.originalName = n r.appendCsvAnnotation(buildAnnotationPreviousNamespaces, ns)
return r
}
func (r *Resource) GetOriginalNs() string {
return r.originalNs
}
// Making this public would be bad.
func (r *Resource) setOriginalNs(n string) *Resource {
r.originalNs = n
return r return r
} }
@@ -356,6 +363,15 @@ func (r *Resource) AsYAML() ([]byte, error) {
return yaml.JSONToYAML(json) return yaml.JSONToYAML(json)
} }
// MustYaml returns YAML or panics.
func (r *Resource) MustYaml() string {
yml, err := r.AsYAML()
if err != nil {
log.Fatal(err)
}
return string(yml)
}
// SetOptions updates the generator options for the resource. // SetOptions updates the generator options for the resource.
func (r *Resource) SetOptions(o *types.GenArgs) { func (r *Resource) SetOptions(o *types.GenArgs) {
r.options = o r.options = o
@@ -381,10 +397,42 @@ func (r *Resource) GetNamespace() string {
// OrgId returns the original, immutable ResId for the resource. // OrgId returns the original, immutable ResId for the resource.
// This doesn't have to be unique in a ResMap. // This doesn't have to be unique in a ResMap.
// TODO: compute this once and save it in the resource.
func (r *Resource) OrgId() resid.ResId { func (r *Resource) OrgId() resid.ResId {
return resid.NewResIdWithNamespace( ids := r.PrevIds()
r.GetGvk(), r.GetOriginalName(), r.GetOriginalNs()) if len(ids) > 0 {
return ids[0]
}
return r.CurId()
}
// PrevIds returns a list of ResIds that includes every
// previous ResId the resource has had through all of its
// GVKN transformations, in the order that it had that ID.
// I.e. the oldest ID is first.
// The returned array does not include the resource's current
// ID. If there are no previous IDs, this will return nil.
func (r *Resource) PrevIds() []resid.ResId {
var ids []resid.ResId
// TODO: merge previous names and namespaces into one list of
// pairs on one annotation so there is no chance of error
names := r.getCsvAnnotation(buildAnnotationPreviousNames)
ns := r.getCsvAnnotation(buildAnnotationPreviousNamespaces)
if len(names) != len(ns) {
panic(errors.New(
"number of previous names not equal to " +
"number of previous namespaces"))
}
for i := range names {
ids = append(ids, resid.NewResIdWithNamespace(
r.GetGvk(), names[i], ns[i]))
}
return ids
}
// StorePreviousId stores the resource's current ID via build annotations.
func (r *Resource) StorePreviousId() {
id := r.CurId()
r.setPreviousNamespaceAndName(id.EffectiveNamespace(), id.Name)
} }
// CurId returns a ResId for the resource using the // CurId returns a ResId for the resource using the
@@ -422,9 +470,12 @@ func (r *Resource) ApplySmPatch(patch *Resource) error {
return err return err
} }
n, ns := r.GetName(), r.GetNamespace() n, ns := r.GetName(), r.GetNamespace()
err = filtersutil.ApplyToJSON(patchstrategicmerge.Filter{ err = r.ApplyFilter(patchstrategicmerge.Filter{
Patch: node, Patch: node,
}, r) })
if err != nil {
return err
}
if !r.IsEmpty() { if !r.IsEmpty() {
r.SetName(n) r.SetName(n)
r.SetNamespace(ns) r.SetNamespace(ns)
@@ -432,6 +483,18 @@ func (r *Resource) ApplySmPatch(patch *Resource) error {
return err return err
} }
func (r *Resource) ApplyFilter(f kio.Filter) error {
if wn, ok := r.kunStr.(*wrappy.WNode); ok {
l, err := f.Filter([]*kyaml.RNode{wn.AsRNode()})
if len(l) == 0 {
// Hack to deal with deletion.
r.kunStr = wrappy.NewWNode()
}
return err
}
return filtersutil.ApplyToJSON(f, r)
}
func mergeStringMaps(maps ...map[string]string) map[string]string { func mergeStringMaps(maps ...map[string]string) map[string]string {
result := map[string]string{} result := map[string]string{}
for _, m := range maps { for _, m := range maps {

View File

@@ -695,6 +695,168 @@ spec:
} }
} }
func TestResource_StorePreviousId(t *testing.T) {
tests := map[string]struct {
input string
newName string
newNs string
expected string
}{
"default namespace, first previous name": {
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: oldName
`,
newName: "newName",
newNs: "",
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default
name: newName
`,
},
"default namespace, second previous name": {
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default
name: oldName2
`,
newName: "newName",
newNs: "",
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,default
name: newName
`,
},
"non-default namespace": {
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default
name: oldName2
namespace: oldNamespace
`,
newName: "newName",
newNs: "newNamespace",
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,oldNamespace
name: newName
namespace: newNamespace
`,
},
}
factory := provider.NewDefaultDepProvider().GetResourceFactory()
for i := range tests {
test := tests[i]
t.Run(i, func(t *testing.T) {
resources, err := factory.SliceFromBytes([]byte(test.input))
if !assert.NoError(t, err) || len(resources) == 0 {
t.FailNow()
}
r := resources[0]
r.StorePreviousId()
r.SetName(test.newName)
if test.newNs != "" {
r.SetNamespace(test.newNs)
}
bytes, err := r.AsYAML()
if !assert.NoError(t, err) {
t.FailNow()
}
assert.Equal(t, test.expected, string(bytes))
})
}
}
func TestResource_PrevIds(t *testing.T) {
tests := map[string]struct {
input string
expected []resid.ResId
}{
"no previous IDs": {
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: name
`,
expected: nil,
},
"one previous ID": {
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousNames: oldName
config.kubernetes.io/previousNamespaces: default
name: newName
`,
expected: []resid.ResId{
{
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
Name: "oldName",
Namespace: resid.DefaultNamespace,
},
},
},
"two ids": {
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/previousNames: oldName,oldName2
config.kubernetes.io/previousNamespaces: default,oldNamespace
name: newName
namespace: newNamespace
`,
expected: []resid.ResId{
{
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
Name: "oldName",
Namespace: resid.DefaultNamespace,
},
{
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
Name: "oldName2",
Namespace: "oldNamespace",
},
},
},
}
factory := provider.NewDefaultDepProvider().GetResourceFactory()
for i := range tests {
test := tests[i]
t.Run(i, func(t *testing.T) {
resources, err := factory.SliceFromBytes([]byte(test.input))
if !assert.NoError(t, err) || len(resources) == 0 {
t.FailNow()
}
r := resources[0]
assert.Equal(t, test.expected, r.PrevIds())
})
}
}
// baseResource produces a base object which used to test // baseResource produces a base object which used to test
// patch transformation // patch transformation
// Also the structure is matching the Deployment syntax // Also the structure is matching the Deployment syntax
@@ -858,3 +1020,55 @@ spec:
name: nginx name: nginx
`, imagename) `, imagename)
} }
func TestSameEndingSubarray(t *testing.T) {
testCases := map[string]struct {
a []string
b []string
expected bool
}{
"both nil": {
expected: true,
},
"one nil": {
b: []string{},
expected: true,
},
"both empty": {
a: []string{},
b: []string{},
expected: true,
},
"no1": {
a: []string{"a"},
b: []string{},
expected: false,
},
"no2": {
a: []string{"b", "a"},
b: []string{"b"},
expected: false,
},
"yes1": {
a: []string{"a", "b"},
b: []string{"b"},
expected: true,
},
"yes2": {
a: []string{"a", "b", "c"},
b: []string{"b", "c"},
expected: true,
},
"yes3": {
a: []string{"a", "b", "c", "d", "e", "f"},
b: []string{"f"},
expected: true,
},
}
for n := range testCases {
tc := testCases[n]
t.Run(n, func(t *testing.T) {
assert.Equal(t, tc.expected, SameEndingSubarray(tc.a, tc.b))
})
}
}

View File

@@ -126,6 +126,11 @@ func (th Harness) AssertActualEqualsExpected(
th.AssertActualEqualsExpectedWithTweak(m, nil, expected) th.AssertActualEqualsExpectedWithTweak(m, nil, expected)
} }
func (th Harness) AssertActualEqualsExpectedNoIdAnnotations(m resmap.ResMap, expected string) {
m.RemoveBuildAnnotations()
th.AssertActualEqualsExpectedWithTweak(m, nil, expected)
}
func (th Harness) AssertActualEqualsExpectedWithTweak( func (th Harness) AssertActualEqualsExpectedWithTweak(
m resmap.ResMap, tweaker func([]byte) []byte, expected string) { m resmap.ResMap, tweaker func([]byte) []byte, expected string) {
assertActualEqualsExpectedWithTweak(th, m, tweaker, expected) assertActualEqualsExpectedWithTweak(th, m, tweaker, expected)

View File

@@ -41,6 +41,7 @@ func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
pte := newPluginTestEnv(t).set() pte := newPluginTestEnv(t).set()
pc, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys) pc, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
pc.FnpLoadingOptions.EnableStar = true
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -109,6 +110,7 @@ func (th *HarnessEnhanced) LoadAndRunGenerator(
if err != nil { if err != nil {
th.t.Fatalf("Err: %v", err) th.t.Fatalf("Err: %v", err)
} }
rm.RemoveBuildAnnotations()
return rm return rm
} }
@@ -124,7 +126,7 @@ func (th *HarnessEnhanced) LoadAndRunTransformer(
func (th *HarnessEnhanced) RunTransformerAndCheckResult( func (th *HarnessEnhanced) RunTransformerAndCheckResult(
config, input, expected string) { config, input, expected string) {
resMap := th.LoadAndRunTransformer(config, input) resMap := th.LoadAndRunTransformer(config, input)
th.AssertActualEqualsExpected(resMap, expected) th.AssertActualEqualsExpectedNoIdAnnotations(resMap, expected)
} }
func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer( func (th *HarnessEnhanced) ErrorFromLoadAndRunTransformer(

View File

@@ -7,6 +7,7 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/api/provider" "sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
) )
@@ -48,15 +49,7 @@ func (rm *rmBuilder) AddR(r *resource.Resource) *rmBuilder {
} }
func (rm *rmBuilder) AddWithName(n string, m map[string]interface{}) *rmBuilder { func (rm *rmBuilder) AddWithName(n string, m map[string]interface{}) *rmBuilder {
err := rm.m.Append(rm.rf.FromMapWithName(n, m)) err := rm.m.Append(rm.rf.FromMapWithNamespaceAndName(resid.DefaultNamespace, n, m))
if err != nil {
rm.t.Fatalf("test setup failure: %v", err)
}
return rm
}
func (rm *rmBuilder) AddWithNs(ns string, m map[string]interface{}) *rmBuilder {
err := rm.m.Append(rm.rf.FromMapWithNamespace(ns, m))
if err != nil { if err != nil {
rm.t.Fatalf("test setup failure: %v", err) rm.t.Fatalf("test setup failure: %v", err)
} }

View File

@@ -5,7 +5,6 @@ package types
import ( import (
"fmt" "fmt"
"strings"
"sigs.k8s.io/kustomize/api/resid" "sigs.k8s.io/kustomize/api/resid"
) )
@@ -34,11 +33,6 @@ type FieldSpec struct {
CreateIfNotPresent bool `json:"create,omitempty" yaml:"create,omitempty"` CreateIfNotPresent bool `json:"create,omitempty" yaml:"create,omitempty"`
} }
const (
escapedForwardSlash = "\\/"
tempSlashReplacement = "???"
)
func (fs FieldSpec) String() string { func (fs FieldSpec) String() string {
return fmt.Sprintf( return fmt.Sprintf(
"%s:%v:%s", fs.Gvk.String(), fs.CreateIfNotPresent, fs.Path) "%s:%v:%s", fs.Gvk.String(), fs.CreateIfNotPresent, fs.Path)
@@ -49,34 +43,6 @@ func (fs FieldSpec) effectivelyEquals(other FieldSpec) bool {
return fs.IsSelected(&other.Gvk) && fs.Path == other.Path return fs.IsSelected(&other.Gvk) && fs.Path == other.Path
} }
// PathSlice converts the path string to a slice of strings,
// separated by a '/'. Forward slash can be contained in a
// fieldname. such as ingress.kubernetes.io/auth-secret in
// Ingress annotations. To deal with this special case, the
// path to this field should be formatted as
//
// metadata/annotations/ingress.kubernetes.io\/auth-secret
//
// Then PathSlice will return
//
// []string{
// "metadata",
// "annotations",
// "ingress.auth-secretkubernetes.io/auth-secret"
// }
func (fs FieldSpec) PathSlice() []string {
if !strings.Contains(fs.Path, escapedForwardSlash) {
return strings.Split(fs.Path, "/")
}
s := strings.Replace(fs.Path, escapedForwardSlash, tempSlashReplacement, -1)
paths := strings.Split(s, "/")
var result []string
for _, path := range paths {
result = append(result, strings.Replace(path, tempSlashReplacement, "/", -1))
}
return result
}
type FsSlice []FieldSpec type FsSlice []FieldSpec
func (s FsSlice) Len() int { return len(s) } func (s FsSlice) Len() int { return len(s) }

View File

@@ -13,30 +13,6 @@ import (
. "sigs.k8s.io/kustomize/api/types" . "sigs.k8s.io/kustomize/api/types"
) )
func TestPathSlice(t *testing.T) {
type path struct {
input string
parsed []string
}
paths := []path{
{
input: "spec/metadata/annotations",
parsed: []string{"spec", "metadata", "annotations"},
},
{
input: `metadata/annotations/nginx.ingress.kubernetes.io\/auth-secret`,
parsed: []string{"metadata", "annotations", "nginx.ingress.kubernetes.io/auth-secret"},
},
}
for _, p := range paths {
fs := FieldSpec{Path: p.input}
actual := fs.PathSlice()
if !reflect.DeepEqual(actual, p.parsed) {
t.Fatalf("expected %v, but got %v", p.parsed, actual)
}
}
}
var mergeTests = []struct { var mergeTests = []struct {
name string name string
original FsSlice original FsSlice

View File

@@ -25,6 +25,9 @@ type Kustomization struct {
// MetaData is a pointer to avoid marshalling empty struct // MetaData is a pointer to avoid marshalling empty struct
MetaData *ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"` MetaData *ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
// OpenAPI contains information about what kubernetes schema to use.
OpenAPI map[string]string `json:"openapi,omitempty" yaml:"openapi,omitempty"`
// //
// Operators - what kustomize can do. // Operators - what kustomize can do.
// //

View File

@@ -1,14 +1,48 @@
# Copyright 2019 The Kubernetes Authors. # Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy .PHONY: generate license fix vet fmt test build tidy clean
GOBIN := $(shell go env GOPATH)/bin GOBIN := $(shell go env GOPATH)/bin
$(GOBIN)/addlicense:
go get github.com/google/addlicense
$(GOBIN)/golangci-lint:
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1
$(GOBIN)/k8scopy:
( cd ../k8scopy; go install . )
$(GOBIN)/mdtogo:
go get sigs.k8s.io/kustomize/cmd/mdtogo
build: build:
go build -v -o $(GOBIN)/kubectl-krm ./kubectl-krm go build -v -o $(GOBIN)/kubectl-krm ./kubectl-krm
all: generate build license fix vet fmt test lint tidy all: build license fix vet fmt test lint tidy
k8sGenDir := internal/commands/internal/k8sgen/pkg
generate: $(GOBIN)/mdtogo $(GOBIN)/k8scopy
GOBIN=$(GOBIN) go generate ./...
clean:
rm -rf $(k8sGenDir)
lint: $(GOBIN)/golangci-lint
$(GOBIN)/golangci-lint \
--skip-dirs $(k8sGenDir) \
run ./...
license: $(GOBIN)/addlicense
$(GOBIN)/addlicense \
-y 2021 \
-c "The Kubernetes Authors." \
-f LICENSE_TEMPLATE .
test:
go test -v -timeout 45m -cover ./...
fix: fix:
go fix ./... go fix ./...
@@ -16,24 +50,8 @@ fix:
fmt: fmt:
go fmt ./... go fmt ./...
generate:
(which $(GOBIN)/mdtogo || go get sigs.k8s.io/kustomize/cmd/mdtogo)
GOBIN=$(GOBIN) go generate ./...
license:
(which $(GOBIN)/addlicense || go get github.com/google/addlicense)
$(GOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy: tidy:
go mod tidy go mod tidy
lint:
(which $(GOBIN)/golangci-lint || go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.19.1)
$(GOBIN)/golangci-lint run ./...
test:
go test -v -timeout 45m -cover ./...
vet: vet:
go vet ./... go vet ./...

View File

@@ -6,14 +6,16 @@ require (
github.com/go-errors/errors v1.0.1 github.com/go-errors/errors v1.0.1
github.com/go-openapi/spec v0.19.5 github.com/go-openapi/spec v0.19.5
github.com/google/go-cmp v0.5.2 // indirect github.com/google/go-cmp v0.5.2 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/olekukonko/tablewriter v0.0.4 github.com/olekukonko/tablewriter v0.0.4
github.com/spf13/cobra v1.0.0 github.com/spf13/cobra v1.0.0
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect
golang.org/x/text v0.3.4 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
k8s.io/apimachinery v0.18.10 gopkg.in/inf.v0 v0.9.1
sigs.k8s.io/kustomize/kyaml v0.10.5 sigs.k8s.io/kustomize/kyaml v0.10.9
) )

View File

@@ -1,14 +1,11 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE= github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
@@ -41,15 +38,9 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk= github.com/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
@@ -59,8 +50,6 @@ github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
@@ -71,13 +60,11 @@ github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQH
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
@@ -93,7 +80,6 @@ github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
github.com/go-openapi/runtime v0.19.4 h1:csnOgcgAiuGoM/Po7PEpKDoNulCcF3FGbSnbHfxgjMI= github.com/go-openapi/runtime v0.19.4 h1:csnOgcgAiuGoM/Po7PEpKDoNulCcF3FGbSnbHfxgjMI=
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
@@ -106,7 +92,6 @@ github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+Z
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9wWIM= github.com/go-openapi/strfmt v0.19.5 h1:0utjKrw+BAh8s57XE9Xz8DUBsVvPmRUB6styvl9wWIM=
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
@@ -120,19 +105,14 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -140,34 +120,22 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -181,7 +149,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -195,34 +162,19 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk= github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -250,7 +202,6 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -297,10 +248,8 @@ golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -312,17 +261,15 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -331,18 +278,17 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c h1:Vco5b+cuG5NNfORVxZy6bYZQ7rsigisU1WQFkvQ0L5E= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c h1:Vco5b+cuG5NNfORVxZy6bYZQ7rsigisU1WQFkvQ0L5E=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -362,36 +308,18 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/apimachinery v0.18.10 h1:Zupk3lPrUfhCF9puTpA8EvEfPsrhNZtrpOqdp66mKVs= sigs.k8s.io/kustomize/kyaml v0.10.9 h1:n3WNdvPPReRNDxW+XXd2JlyZ8EII721I21D1DBpBVBE=
k8s.io/apimachinery v0.18.10/go.mod h1:PF5taHbXgTEJLU+xMypMmYTXTWPJ5LaW8bfsisxnEXk= sigs.k8s.io/kustomize/kyaml v0.10.9/go.mod h1:K9yg1k/HB/6xNOf5VH3LhTo1DK9/5ykSZO5uIv+Y/1k=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
sigs.k8s.io/kustomize/kyaml v0.10.5 h1:PbJcsZsEM7O3hHtUWTR+4WkHVbQRW9crSy75or1gRbI=
sigs.k8s.io/kustomize/kyaml v0.10.5/go.mod h1:P6Oy/ah/GZMKzJMIJA2a3/bc8YrBkuL5kJji13PSIzY=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -10,8 +10,8 @@ import (
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/resource"
"sigs.k8s.io/kustomize/cmd/config/ext" "sigs.k8s.io/kustomize/cmd/config/ext"
"sigs.k8s.io/kustomize/cmd/config/internal/commands/internal/k8sgen/pkg/api/resource"
"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"
"sigs.k8s.io/kustomize/kyaml/kio" "sigs.k8s.io/kustomize/kyaml/kio"

View File

@@ -0,0 +1,7 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// All code below this directory is generated.
// See {repo}/cmd/k8scopy/main.go for more info.
//go:generate k8scopy k8scopy.yaml internal/commands
package k8sgen

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