Compare commits

...

374 Commits

Author SHA1 Message Date
Kubernetes Prow Robot
a3103f1e62 Merge pull request #1534 from monopole/configExample
Example of configuring builtin plugin.
2019-09-17 16:43:15 -07:00
Jeffrey Regan
74ed0b30e5 Example of configuring builtin plugin. 2019-09-17 16:29:08 -07:00
Kubernetes Prow Robot
32be1cf4c2 Merge pull request #1532 from monopole/runBuiltinSansFlag
Ease configuring and running the builtin plugins.
2019-09-13 15:28:00 -07:00
jregan
2050afdeb4 Ease doing custom configuration of builtin plugins. 2019-09-13 14:45:36 -07:00
Kubernetes Prow Robot
7e71009283 Merge pull request #1526 from jimmidyson/ignore-prefix-suffix-apiservice
Do not prefix/suffix APIService resources
2019-09-13 11:28:29 -07:00
Kubernetes Prow Robot
72d26c6ad5 Merge pull request #1522 from jcassee/basereusenameprefix-test
Add test for name conflict with base reuse
2019-09-13 11:02:29 -07:00
Jeff Regan
e011f3be4f change "bases:" to "resources:"
per https://github.com/monopole/kustomize/blob/master/docs/fields.md#bases

no big deal
2019-09-13 10:47:35 -07:00
Kubernetes Prow Robot
f725bfc165 Merge pull request #1520 from jcpetruzza/cm-merge-nameprefix
Consider also currentId when replacing/merging resources
2019-09-13 10:40:30 -07:00
Jeff Regan
94ac55f17b Merge pull request #1505 from Liujingfang1/master
add inline patch document
2019-09-13 10:16:40 -07:00
Jimmi Dyson
dd5b3c1e2e Do not prefix/suffix APIService resources 2019-09-12 09:53:33 +01:00
Joost Cassee
e898c5221b Add test for name conflict with base reuse 2019-09-10 23:57:15 +02:00
Daniel Gorin
1237ae43b4 Consider currentId when replacing/merging resources
When merging resources such as the output of a `configMapGenerator`,
we need to consider the `CurrentId`, otherwise, we cannot extend
a common base definition twice by adding different prefixes, and
then further kustomize them.

See #1442.
2019-09-10 11:14:08 +01:00
Kubernetes Prow Robot
cd0187e948 Merge pull request #1515 from yujunz/plugin/go-getter
plugin/go-getter: support urls including `:`
2019-09-09 09:05:22 -07:00
jingfangliu
9516880042 add inline patch document 2019-09-09 09:02:48 -07:00
Yujun Zhang
4cb883863f plugin/go-getter: support urls including : 2019-09-09 15:39:45 +08:00
Kubernetes Prow Robot
9ee35c9afb Merge pull request #1484 from sunny0826/master
update zh doc
2019-09-06 08:44:58 -07:00
guoxudong
e455acc14b fix 2019-09-06 09:20:21 +08:00
Jeff Regan
6a3c2b2893 Merge pull request #1507 from monopole/anotherTest
Add an example of reusable builtin plugins with custom config.
2019-09-05 16:11:53 -07:00
Jeffrey Regan
f59d7998d2 Add an example of reusable builtin plugins with custom config. 2019-09-05 15:53:03 -07:00
Michael
6fcb78403f use kubectl apply -k # (#1495)
`kubectl apply -f ` is executed failed.  ref: #1494
2019-09-05 09:27:39 -07:00
Kubernetes Prow Robot
f87edc8c67 Merge pull request #1488 from yujunz/plugin/go-getter
Add example plugin for go-getter
2019-09-05 08:57:10 -07:00
Yujun Zhang
6a4150d199 Amend go-getter plugin document according to comments 2019-09-05 13:57:20 +08:00
Jeff Regan
143c5dd21d Merge pull request #1489 from damienr74/generic-crawler
tooling performance improvements, better structure
2019-09-03 11:35:43 -07:00
Yujun Zhang
ed920afb2e Support setting command in go-getter plugin
This allows one to use non-kustomization remote source
2019-09-02 14:53:18 +08:00
Jeff Regan
2677f4c4e7 Merge pull request #1492 from monopole/anotherTest
Test custom configuration of a builtin plugin.
2019-08-30 16:54:21 -07:00
jregan
a081534938 Test custom configuration of a builtin plugin. 2019-08-30 16:37:29 -07:00
Kubernetes Prow Robot
4ebad27d7a Merge pull request #1490 from mr-karan/docfix
feat: Add instructions for setting key in configmap
2019-08-30 11:08:57 -07:00
Karan Sharma
716a7307b2 feat: Add instructions for setting key in configmap 2019-08-30 16:21:11 +05:30
Yujun Zhang
ed91bce275 Add example plugin for go-getter 2019-08-30 11:14:46 +08:00
Damien Robichaud
c2d6f09ef3 Crawler performance improvements, better structure
Refactored the crawler implementation to make the whole thing more
testable. Added a document interface to make the crawler generic.
This will be useful for collecting plugins, and other documents.
2019-08-29 22:25:45 -03:00
Kubernetes Prow Robot
119ff5af73 Merge pull request #1487 from laverya/properly-omitempty-kustomization-inventory
properly omitempty for 'inventory' in 'kustomize'
2019-08-29 15:17:00 -07:00
Andrew Lavery
2e7ad48b44 properly omitempty for 'inventory' in 'kustomize'
with the current 'yaml:"inventory:omitempty"', 'inventory:omitempty' is used as the literal name for the yaml field
2019-08-29 14:41:03 -07:00
Kubernetes Prow Robot
6ead3b7b1f Merge pull request #1446 from keleustes/ending
IsInKustomizeCtx should use end of nameprefix array instead of beginning
2019-08-29 12:12:59 -07:00
Jerome Brette
31262cccbe IsInKustomizeCtx should use end of nameprefix array (code review) 2019-08-29 13:59:01 -05:00
Jerome Brette
93cedbaa51 IsInKustomizeCtx should use end of nameprefix array (3/3)
Improve multi level kustomization ctx during candidate selection
testing.
2019-08-29 13:58:03 -05:00
Jerome Brette
6e13acfac3 IsInKustomizeCtx should use end of nameprefix array (2/3)
Update unit tests associated with in IsSameKustomizeCtx
2019-08-29 13:58:03 -05:00
Jerome Brette
2e6dd481e0 IsInKustomizeCtx should use end of nameprefix array (1/3)
Need to use ending prefix and suffix subarray instead of beginning
2019-08-29 13:58:03 -05:00
Kubernetes Prow Robot
a66808a10d Merge pull request #1483 from bianpengyuan/fix-typo
Fix environment variable typo in plugin doc
2019-08-28 13:17:55 -07:00
guoxudong
a4e1ba0593 update zh doc 2019-08-27 13:57:17 +08:00
Pengyuan Bian
73660af10c fix environment variable typo. 2019-08-26 16:24:44 -07:00
Kubernetes Prow Robot
84519c236b Merge pull request #1434 from sunny0826/master
update examples-zh
2019-08-26 09:00:27 -07:00
guoxudong
aedb362565 fix doc 2019-08-24 16:18:34 +08:00
Jeff Regan
6918931728 Merge pull request #1481 from damienr74/modify-crawler
Add configs
2019-08-23 12:59:08 -07:00
Damien Robichaud
3f1b2bb744 Add configs 2019-08-23 12:57:59 -07:00
Kubernetes Prow Robot
33ad02a6b4 Merge pull request #1383 from richardmarshall/smp_patch_directive
Handle ordering patch with SMP delete directives
2019-08-23 11:15:18 -07:00
Kubernetes Prow Robot
bfd6e086de Merge pull request #1474 from keleustes/coverage
Add cover target to Makefile
2019-08-23 10:55:18 -07:00
Jeff Regan
a9f58383d8 Merge pull request #1460 from richardmarshall/namespace_conflicts
Detect ID conflicts in namespace transformer
2019-08-23 10:54:36 -07:00
Ian Howell
aabbbf05ef Add cover target to Makefile 2019-08-23 08:22:00 -05:00
Jeff Regan
40c613d0cd Merge pull request #1476 from damienr74/add-ui
Adds frontend + configs to interal/tools
2019-08-22 16:59:44 -07:00
Jeff Regan
eca5b8796f Merge pull request #1470 from damienr74/index
First draft of documentation for internal/tools
2019-08-22 16:08:44 -07:00
Damien Robichaud
aa2bf7ed08 Adds frontend + configs to interal/tools/ui 2019-08-22 15:39:00 -07:00
Damien Robichaud
351df67e39 First draft of documentation for internal/tools 2019-08-21 17:01:59 -07:00
Kubernetes Prow Robot
8a8698ccdd Merge pull request #1469 from damienr74/index
Add simple service/configs to internal tooling.
2019-08-21 14:58:31 -07:00
Damien Robichaud
66fa2de073 Add main backend service and configurations 2019-08-21 14:29:41 -07:00
Kubernetes Prow Robot
3ace96d7a4 Merge pull request #1467 from richardmarshall/create_fixes
Fixes to create sub-command
2019-08-21 11:38:33 -07:00
Jeff Regan
2b44ba200f Merge pull request #1455 from lcostea/master
Add short version flag
2019-08-21 11:35:23 -07:00
Jeff Regan
4b67a6de12 Merge pull request #1456 from matti/patch-2
fix latest version
2019-08-21 10:57:18 -07:00
Jeff Regan
33bd221a98 Update README.md
removed it rather than keeping it and having it get out of date again
2019-08-21 10:56:43 -07:00
Richard Marshall
594a06d35b Fixes to create sub-command 2019-08-21 08:59:21 -07:00
Kubernetes Prow Robot
e541ff3999 Merge pull request #1414 from richardmarshall/create_subcommand
Create subcommand
2019-08-20 16:47:20 -07:00
Jeff Regan
9ea184c04a Merge pull request #1449 from richardmarshall/git_cycle_detection
Fix indirect cycle detection for git resources
2019-08-20 16:17:36 -07:00
Jeff Regan
993993c6cd Merge pull request #1464 from damienr74/index
Add internal tooling library for index managment.
2019-08-20 15:22:58 -07:00
Jeff Regan
35b39763dd Merge pull request #1445 from liggitt/patch-path
Fix patch path example
2019-08-20 15:15:03 -07:00
Jeff Regan
2c1dda5436 Merge pull request #1437 from fentas/fentas-patch-1
add PriorityClass to the order list
2019-08-20 15:09:57 -07:00
Jeff Regan
653123975c Merge pull request #1435 from lukatera/go-get-submodules
Download submodules when using base from git
2019-08-20 15:08:08 -07:00
Jeff Regan
fb8b314a29 Merge pull request #1426 from fleeto/translate-zh-glossary
translate-zh: glossary.md
2019-08-20 15:04:22 -07:00
Jeff Regan
5cf3f4e275 Merge pull request #1419 from richardmarshall/git_url_handling
Handle git:: prefix in urls containing _git
2019-08-20 15:01:14 -07:00
Jeff Regan
766500508c Merge pull request #1465 from monopole/testHeadAgainstExamples
Test examples against HEAD as well as against latest release.
2019-08-20 14:24:09 -07:00
Jeffrey Regan
423a8a6e0d Test examples against HEAD as well as against latest release. 2019-08-20 14:10:01 -07:00
Damien Robichaud
7783a76b8f Add internal tooling library for index queries. 2019-08-20 11:25:20 -07:00
Jeff Regan
2b6a406dc7 Merge pull request #1462 from monopole/errormessages
in plugin executor remove unnecessary code and improve error messages
2019-08-19 20:35:51 -07:00
jregan
bc303c4629 in plugin executor remove unnecessary code and improve error messages 2019-08-19 20:23:07 -07:00
Jeff Regan
00360f381c Merge pull request #1461 from monopole/fixNonTravisTests
Fix non-travis tests.
2019-08-19 16:29:44 -07:00
jregan
fa834f9541 Fix non-travis tests. 2019-08-19 16:29:00 -07:00
Jeff Regan
a2767cab2a Merge pull request #1374 from alexeldeib/ace/windows
fix: windows builds
2019-08-19 15:03:51 -07:00
Richard Marshall
24c173a49b Detect ID conflicts in namespace transformer 2019-08-19 08:55:54 -07:00
Matti Paksula
d3d4908f95 fix latest version
I don't think it makes sense to have version in README, though
2019-08-18 13:18:22 +03:00
Jeff Regan
be1d5478dc Merge pull request #1450 from damienr74/master
Add internal tooling for kustomize
2019-08-16 14:49:37 -07:00
Damien Robichaud
d3022ccd65 rename to tools directory 2019-08-16 11:25:20 -07:00
Damien Robichaud
fe45157b26 Update crawler to cache web request form github.
- Increase logging signal to noise ratio.
- Allow to specify the `http.Client` for github requests. (This allows
 the use of caching http.Clients).
- Clean up implementation.
2019-08-16 09:48:23 -07:00
Damien Robichaud
df779fd720 Modify document for elasticsearch migration. 2019-08-16 09:48:23 -07:00
Damien Robichaud
e0d388c6f7 Implements search query partitioning by filesize.
Binary searches through different ranges of file sizes to create search
queries with fewer than 1000 results. This is required since github will
only return the first 1000 result of any search query.

The implementation handles the case where some files may be deleted
while the search is running, and (possibly artificially) assures that the
number of files increases monotonically as the filesize range grows.

The implementation also caches queries and is expected to make fewer
than O((#files/1000) * lg(max file size)) API calls to retrieve the
range queries that can be used to index all of the files.

In practice running the search splitting takes a few minutes, while
retrieving all of the data takes a few hours.
2019-08-16 09:48:23 -07:00
Damien Robichaud
62edcae233 Implementation of configurable github crawler.
Currently I've left the search splitting by file size out of this commit
since it's ~200 lines of logic, and I think it's best to get it reviewed
separately.

In it's current state the crawler would only be able to get the last
1000 indexed files by Github, but it does show the general structure of
how the crawler is implemented.
2019-08-16 09:48:23 -07:00
Damien Robichaud
ac6918d70f Implementation of github query helper library.
To make this easier to read, use, and modify, I've abstracted the
important parts of the github query api into crawler/github/query.go
which allows to describe at a high level what is to be searched without
knowing the API syntax.
2019-08-16 09:48:23 -07:00
Damien Robichaud
ca41674df3 Implementation of basic crawler organisation.
`crawler.Crawler` interface is defined, where a crawler has to implement
a `Crawl` method that forwards document found by the crawler to a channel.

A helper function that launches a list of crawlers concurrently and
merges their channels into one main output channel, forwarding errors is
also implemented.

Finally, a test that verifies the correctness and concurrency of the
helper method is provided.
2019-08-16 09:48:23 -07:00
Damien Robichaud
c02b4f3a11 Initial (temporary) implementation of search doc.
Document describing how to convert a kustomization file into a
searchable document on appengine (will be changed to elasticsearch)
soon.
2019-08-16 09:48:23 -07:00
Liviu Costea
64341a81fa Add short version flag 2019-08-16 09:46:37 +03:00
Richard Marshall
fe8ba8e44b Log loader errors during resource accumulation 2019-08-15 07:59:55 -07:00
Richard Marshall
54f1952195 Log output from git on errors 2019-08-15 07:34:38 -07:00
Richard Marshall
44b62a8ebc Fix indirect git resource cycle detection 2019-08-15 07:25:20 -07:00
Jordan Liggitt
8e9c08ea61 Fix patch path example 2019-08-14 15:55:55 -04:00
Kubernetes Prow Robot
c464fb0a81 Merge pull request #1436 from richardmarshall/kubectl_clarity
docs: Additional details for kubectl integration
2019-08-13 15:18:23 -07:00
Richard Marshall
9481e3fba6 docs: Update patchesStrategicMerge documentation 2019-08-13 13:38:42 -07:00
Richard Marshall
0e5206a251 test: Update target tests for SMP directives 2019-08-13 13:38:42 -07:00
Richard Marshall
96c5b4aa3e Handle ordering patches with SMP delete directives
This change enables the SMP patch merging process to support delete
directives in patches regardless of input order.
2019-08-13 13:38:41 -07:00
Jan Guth
6c44da52a2 add PriorityClass to the order list 2019-08-10 07:31:54 +02:00
Kubernetes Prow Robot
694cf23df8 Merge pull request #1432 from richardmarshall/lostreplicas
Retain replicas field in edit marshal path
2019-08-09 14:29:14 -07:00
Richard Marshall
e66656aa7f docs: Additional details for kubectl integration 2019-08-08 17:06:19 -07:00
Richard Marshall
eaae7af5fe Retain replicas field in edit marshal path 2019-08-08 15:45:56 -07:00
Luka Skugor
2de052ecd8 Download submodules when using base from git 2019-08-08 15:49:56 +02:00
guoxudong
6cf8b9e2b8 update examples-zh 2019-08-08 10:16:13 +08:00
Kubernetes Prow Robot
f9fe138114 Merge pull request #1416 from anthonyho007/makefile
add Makefile for local development
2019-08-07 11:04:09 -07:00
Vincent C
78c9729252 translate-zh: glossary.md 2019-08-03 20:40:58 +08:00
Kubernetes Prow Robot
2a2a889c37 Merge pull request #1423 from sunny0826/master
Update zh-README.md & zh-example-README.md
2019-08-02 12:37:55 -07:00
郭旭东
34287e511f fix example-zh-README.md 2019-08-02 09:09:32 +08:00
Anthony Ho
e6fffc8ba4 add makefile 2019-08-01 11:23:38 -04:00
郭旭东
86f221611e Update zh-example-README.md 2019-08-01 15:30:11 +08:00
郭旭东
b4d6e89fa2 Update zh-README.md 2019-08-01 15:19:14 +08:00
Richard Marshall
adbb6228a5 Handle git:: prefix in urls containing _git 2019-07-31 10:23:05 -07:00
Kubernetes Prow Robot
5937bd0259 Merge pull request #1394 from richardmarshall/namerefperformance
Simplify name reference candidate resmap building
2019-07-31 10:18:14 -07:00
Richard Marshall
eeafd43513 Remove import of k8sdeps from create command 2019-07-30 20:52:06 -07:00
Richard Marshall
a68f95b65f Rename commands utility function file 2019-07-30 20:50:52 -07:00
Richard Marshall
ed3c29be12 Simplify name reference candidate resmap building
This patch removes a layer of looping in the name reference candiate
resmap building process by not checking if the resources already exist
in the new resmap.
2019-07-30 17:15:15 -07:00
Kubernetes Prow Robot
3d2e956b19 Merge pull request #1412 from richardmarshall/anchor_resmap_select
Automatically anchor resource selector patterns
2019-07-30 15:47:11 -07:00
Kubernetes Prow Robot
dd9d1f95e9 Merge pull request #1389 from Liujingfang1/repospec
make repospec memebers public
2019-07-30 15:27:51 -07:00
jingfangliu
a279c08f7d make repospec memebers public 2019-07-30 13:56:20 -07:00
Kubernetes Prow Robot
a798109161 Merge pull request #1413 from bai/fix-typo
Fix typo in patches definition
2019-07-30 10:14:51 -07:00
Richard Marshall
5dfa929906 Add create subcommand 2019-07-30 09:17:29 -07:00
Richard Marshall
e904f612f3 Move commands/edit utils package up to commands 2019-07-30 08:45:06 -07:00
Vlad Gorodetsky
bafd6b5423 Fix typo in patches definition 2019-07-30 14:52:02 +03:00
Richard Marshall
963913f9ef Automatically anchor resource selector patterns 2019-07-29 17:57:33 -07:00
Jingfang Liu
46905588ac add document for inline patch (#1411) 2019-07-29 15:15:06 -07:00
Kubernetes Prow Robot
5426888df4 Merge pull request #1405 from Liujingfang1/inlinepatch
add inline patch support for Strategic Merge Patch and JSON patch
2019-07-29 14:28:49 -07:00
jingfangliu
35481ec6d9 add inline patch support for Strategic Merge Patch and JSON patch 2019-07-29 14:10:57 -07:00
Kubernetes Prow Robot
6c92c30e94 Merge pull request #1402 from damienr74/currentid-replicas
Allow replicas to find modified names.
2019-07-29 12:54:47 -07:00
Damien Robichaud
02f6b3ec98 Allow replicas to find modified names.
Also allows to test for modified resmaps instead of directly loading
them.
2019-07-26 18:00:59 -07:00
Kubernetes Prow Robot
a9848f2738 Merge pull request #1403 from Liujingfang1/inlinepatch
add testing for patch transformers
2019-07-26 15:05:57 -07:00
jingfangliu
b4038a6cd2 add testting for patch transformers 2019-07-26 14:02:52 -07:00
Kubernetes Prow Robot
95f3303493 Merge pull request #1400 from keleustes/defaultsa
Force the namespace value for the "default" service object.
2019-07-26 10:51:21 -07:00
Jerome Brette
2faf4a491b Force the namespace value for the "default" service object.
The clusterrolebinding and rolebinding is pointing at a resource
which is not listed in the kustomize
2019-07-25 22:43:59 +00:00
Kubernetes Prow Robot
e646bba1ff Merge pull request #1396 from Liujingfang1/delete
support strategic merge patch with $patch: delete
2019-07-25 12:24:45 -07:00
Kubernetes Prow Robot
99a21b0a3c Merge pull request #1308 from keleustes/varequal
Demonstrate need for Var.DeepEqual method equivalent
2019-07-25 11:26:06 -07:00
Kubernetes Prow Robot
e7a22b6bc5 Merge pull request #1398 from keleustes/doc
Update v3.1.0 release notes.
2019-07-25 10:43:28 -07:00
Jerome Brette
d783bbc0bc DeepEqual method seems cleaner than adding Defaulting before every
reflect.DeepEqual call
2019-07-25 03:52:25 +00:00
Jerome Brette
b7405f3872 Test new types.Var.DeepEqual method. 2019-07-25 03:50:28 +00:00
Jerome Brette
abc419b5f9 Add Absorb method to VarSet and DeepEqual to Var 2019-07-25 03:42:40 +00:00
Jerome Brette
336378b114 Update release notes 2019-07-25 00:12:51 +00:00
jingfangliu
29959551da release note for v3.1.0 2019-07-24 14:16:09 -07:00
jingfangliu
fc78917191 support strategic merge patch with $patch: delete 2019-07-24 12:46:33 -07:00
Kubernetes Prow Robot
ffd95ef5a9 Merge pull request #1378 from keleustes/nameprefix
Name Prefix and Suffix Transformation for multi level level kustomize context
2019-07-24 11:33:21 -07:00
Jerome Brette
230090d790 Fix namereference and stacked kustomization contexts (3/3)
- Update unit and integration tests.
2019-07-24 18:02:29 +00:00
Jerome Brette
8fa3861ba3 Fix namereference and stacked kustomization contexts (2/3)
- Leverage nameprefix and namesuffix contextual data
2019-07-24 18:02:23 +00:00
Jerome Brette
69c90e3427 Fix namereference and stacked kustomization contexts (1/3)
- Update PrefixSuffixTransfomer to add empty prefix and suffix
2019-07-24 10:59:07 -05:00
Kubernetes Prow Robot
5a73f345fd Merge pull request #1388 from Liujingfang1/patch
add example for extended patches
2019-07-23 17:04:16 -07:00
jingfangliu
0e62d759f0 address comments 2019-07-23 16:55:58 -07:00
jingfangliu
b2967d2f77 add example for extended patches 2019-07-23 15:54:25 -07:00
Kubernetes Prow Robot
c23039c07a Merge pull request #1379 from keleustes/namespace
Update Namespace and Name simultaneously
2019-07-23 14:10:14 -07:00
Kubernetes Prow Robot
5747c417c4 Merge pull request #1363 from Liujingfang1/patch
update edit fix to convert the old patches to patchesStrategicMerge
2019-07-23 13:16:14 -07:00
jingfangliu
8c53d77111 update edit fix to convert the old patches to patchesStrategicMerge 2019-07-23 10:38:48 -07:00
Jerome Brette
01667cabde Update Namespace and Name simultaneously (2/2)
Add tests combining prefixsuffix and namespace transformers.
2019-07-23 11:04:59 -05:00
Jerome Brette
f649b62629 Update Namespace and Name simultaneously (1/2)
- Removed RoleBinding and Webhook specific code in the namespacetransformer.
  That code was attempting to perform the task of the namereference
- Updated namereference transformer configuration to suppport the
  Webhooks.
- Prevent the namereference from wiping out the namespace value if
  no referral candidate was selected
- Added unit tests.
2019-07-23 11:04:52 -05:00
Kubernetes Prow Robot
3a4d025b5c Merge pull request #1371 from keleustes/nsvar
Add namespace to variable definition
2019-07-22 11:04:53 -07:00
Ace Eldeib
c2cc93a009 fix: tempfile(?) 2019-07-19 17:38:24 -07:00
Ace Eldeib
af29855802 fix: windows builds 2019-07-19 12:58:06 -07:00
Jerome Brette
99eb08eb1e Add Namespace to var definition to allow disambiguation 2019-07-19 12:08:38 -05:00
Kubernetes Prow Robot
d3f8c0d87f Merge pull request #1327 from keleustes/residrequals-variables
ResId.Equals usable for VariableRef.
2019-07-19 09:41:12 -07:00
Jerome Brette
0bec7b996b Code review implementation for namespace needed in vars 2019-07-18 19:20:10 -05:00
Jerome Brette
dd5674fe6b ResId.Equals usable for VariableRef.
- Namespace need objRef field in variable declaration
- Add namespace conflict test for variables

The replacement of ResId.GkvnEquals reference by ResId.Equals
highligthed the fact it is no possible yet when looking for
variable targets because the namespace field is not allowed yet.
This commit adds two tests to the namespaces_test.go regarding
that use case.
2019-07-18 19:20:10 -05:00
Kubernetes Prow Robot
33159c26df Merge pull request #1369 from Liujingfang1/order
add ResourceQuota to the order list
2019-07-18 16:35:51 -07:00
Kubernetes Prow Robot
afc7dbebe5 Merge pull request #1326 from keleustes/residequals-patchtransformer
Residequals patchtransformer
2019-07-18 13:14:19 -07:00
Jerome Brette
f363acf839 Implement code review changes for ResId.Equals instead of ResId.GkvnEquals 2019-07-18 14:13:51 -05:00
Kubernetes Prow Robot
96d5a7401d Merge pull request #1365 from richardmarshall/fix_integration_test
Fix kustomize install in integration test
2019-07-18 11:56:26 -07:00
jingfangliu
403fa20546 add ResourceQuota and LimitRange to the order list 2019-07-18 11:44:46 -07:00
Richard Marshall
ba4d7ddca8 test: Fix kustomize install in integration test 2019-07-17 20:28:47 -07:00
Jerome Brette
5116e2f210 Improve Transformer with Namespace tests.
- Reorganize test into test tables.
- Ensure that every test case, convers SMP and JSONPatch by
  using Deployment as kind first and then "MyCRD" as kind.
- Add tests involving namespaces.
- Add tests involving reordering of patches.
2019-07-17 15:44:44 -05:00
Jerome Brette
9e0f198227 Start to phase out usage ResId.GvknEquals where possible 2019-07-17 15:44:44 -05:00
Kubernetes Prow Robot
30b378a924 Merge pull request #1325 from keleustes/residequals-namereference
NameReference Transformer needs to account for namespace and cluster wide objects.
2019-07-17 13:20:13 -07:00
Kubernetes Prow Robot
3a843f1eca Merge pull request #1362 from Liujingfang1/doc
update the latest version in readme
2019-07-17 13:04:14 -07:00
Jerome Brette
9b40f8ab47 Implement code review comments to NameReferenceTransformer changes.
- Add comments where code with potentially misleading.
- Rename functions according to comments
2019-07-17 14:10:01 -05:00
jingfangliu
dc6dcd8150 update the latest version in readme 2019-07-17 12:05:12 -07:00
Kubernetes Prow Robot
3cb6c7f1f4 Merge pull request #1349 from yujunz/faq
Add FAQ about how to customize configuration
2019-07-17 11:26:11 -07:00
Kubernetes Prow Robot
7632839bc8 Merge pull request #1350 from yujunz/docs/plugins
Convert go plugin example to GPG based
2019-07-17 10:40:37 -07:00
Yujun Zhang
c3ea109b59 Update goPluginGuidedExample.md 2019-07-17 08:19:50 +08:00
Jerome Brette
579995dc8a Address simultaneous transformation of name and namespace
Namereference handler needs to address simulatenous change of
name and namespace in ClusterRoleBinding for instance.
2019-07-16 18:17:33 -05:00
Jerome Brette
b43bd5440d Update Issue 1264 Reproduction Test 2019-07-16 18:17:33 -05:00
Jerome Brette
c4d899f7f3 Improve NameReference Test cases
- Add more NameReference Namespace tests
- Address issue when mixing empty/no namespace and default namespace.
- Address ClusterRoleBinding subjects field pointing at multiple namespaces.
2019-07-16 18:17:33 -05:00
Jerome Brette
7998ee7036 Addresses slice case with notNamespaceable objects 2019-07-16 18:17:33 -05:00
Kubernetes Prow Robot
878960d7b1 Merge pull request #1355 from Liujingfang1/patch
enable extended patch transformer and add tests
2019-07-16 15:58:34 -07:00
jingfangliu
ed0cfc685b add test for extended patch with overlapping patches 2019-07-16 15:16:00 -07:00
Kubernetes Prow Robot
b0a7345123 Merge pull request #1359 from keleustes/imagetag
Address replacement of digest by ImageTransformer
2019-07-16 13:10:49 -07:00
Jerome Brette
580963ea76 Address replacement of digest by ImageTransformer
- See [Issue 1357](https://github.com/kubernetes-sigs/kustomize/issues/1357)
- Add more plugin tests.
2019-07-16 14:03:56 -05:00
Kubernetes Prow Robot
0707deae95 Merge pull request #1356 from keleustes/droppatch
Test tracking issue patchesStrategicMerge elements can be dropped
2019-07-16 10:50:18 -07:00
Yujun Zhang
fb44880b8c Add back GCP KMS example 2019-07-16 20:10:16 +08:00
Jerome Brette
e5ebca6604 Test tracking issue "patchesStrategicMerge elements can be dropped"
- Issue 1354
- $patch: delete is ignored or not depending of the include order
  in the kustomization.yaml
2019-07-15 19:02:52 -05:00
jingfangliu
f5fc9acb84 fix local test failures 2019-07-15 18:59:16 -05:00
jingfangliu
28d1bad3cb fix the ci failure 2019-07-15 18:58:52 -05:00
jingfangliu
6f74419628 fix local test failures 2019-07-15 16:34:13 -07:00
jingfangliu
8121467c1e fix the ci failure 2019-07-15 16:01:23 -07:00
jingfangliu
a85f297f31 enable extended patch transformer and add tests 2019-07-15 15:45:08 -07:00
Kubernetes Prow Robot
76a7816aeb Merge pull request #1348 from yujunz/nameref
Add storage class name ref
2019-07-15 13:17:25 -07:00
Kubernetes Prow Robot
7872405379 Merge pull request #1336 from richardmarshall/fix_test_flags
Remove go testing flags from kustomize help
2019-07-15 13:13:24 -07:00
Kubernetes Prow Robot
6c17a3409f Merge pull request #1346 from Liujingfang1/patchallkinds
add extended patch transformer
2019-07-15 11:45:24 -07:00
Yujun Zhang
f1dbab9dee Convert go plugin example to GPG based 2019-07-14 11:33:37 +08:00
Yujun Zhang
bfafbbf47f Add FAQ about how to customize configuration 2019-07-14 10:39:45 +08:00
Yujun Zhang
08d7c35da7 Add storage class name ref 2019-07-14 10:05:19 +08:00
Kubernetes Prow Robot
f12704f6c1 Merge pull request #1331 from Rjerk/fix-vp-doc
docs/versioningPolicy.md: fix expired urls
2019-07-12 14:41:05 -07:00
Tony Hsu
0edab60b30 Fix typo: kubectl v1.15 -> kubectl v1.14 (#1333)
* Fix typo: kubectl v1.15 -> kubectl v1.14

Match version number to the version in the link.

* Add both kubectl v1.14 and v1.15

* Add both kubectl v1.14 and v1.15
2019-07-12 14:38:10 -07:00
jingfangliu
3c05e2d664 add extended patch transformer 2019-07-12 14:34:08 -07:00
Kubernetes Prow Robot
aa2313c282 Merge pull request #1344 from Liujingfang1/fix
include nameprefix and namesuffix to find matched reference for cluster level kinds
2019-07-12 11:55:06 -07:00
jingfangliu
eeed1954fb include nameprefix and namesuffix to find matched reference for cluster level kinds 2019-07-12 10:27:02 -07:00
Kubernetes Prow Robot
cd00ce7ab1 Merge pull request #1341 from Liujingfang1/refactor
move strategic merge patch transformer to a builtin transformer
2019-07-12 09:25:05 -07:00
jingfangliu
145d07363f add labels in test patch files 2019-07-12 08:56:34 -07:00
jingfangliu
33fff655db move strategic merge patch transformer to a builtin transformer 2019-07-11 13:39:30 -07:00
Jingfang Liu
31ab347da2 refactor the strategic merge patch transformer toward moving it to a plugin (#1340) 2019-07-11 10:22:56 -07:00
Kubernetes Prow Robot
7a48b2ba8e Merge pull request #1338 from yujunz/transformer/config
Fix missing nameReference in default config
2019-07-11 09:32:55 -07:00
Yujun Zhang
876f2a8236 Fix missing nameReference in default config
Related to #1322
2019-07-11 19:46:29 +08:00
Richard Marshall
095333ffb1 Update references to NewEnvForTest 2019-07-10 20:43:50 -07:00
Richard Marshall
0d8d9e2f2b Move plugin EnvForTest manager into new package
Move the EnvForTest manager into an independent package that is not
imported by any non-test code. Previously this code was directly
embedded in the plugins package resulting in testing flags being exposed
in the main kustomize binary.
2019-07-10 17:16:05 -07:00
Kubernetes Prow Robot
9bff2e8883 Merge pull request #1330 from qiujian16/generate-ns-transformer
Generate updated ns transformer
2019-07-10 08:22:28 -07:00
Liu Lan
120ba6b870 docs/versioningPolicy.md: fix expired urls
Signed-off-by: Liu Lan <liulan@umcloud.com>
2019-07-10 11:41:54 +08:00
Jian Qiu
483188ba89 Generate updated ns transformer 2019-07-10 11:07:31 +08:00
Kubernetes Prow Robot
672bda0c9c Merge pull request #1328 from Liujingfang1/cmgenerator
fix the regression on merging configmap with different namespace
2019-07-09 14:22:24 -07:00
jingfangliu
49b32473ca fix the regression on merging configmap with different namespace 2019-07-09 13:39:19 -07:00
Kubernetes Prow Robot
08400d77a6 Merge pull request #1321 from qiujian16/webhook-ns-transform
Enable ns transformer for webhook
2019-07-09 10:08:03 -07:00
Jian Qiu
c912baeb3a Enable ns transformer for webhook
Add namespace transformer for ValidatingWebhookConfiguration
and MutatingWebhookConfiguration
2019-07-09 13:32:33 +08:00
Kubernetes Prow Robot
433733eb0e Merge pull request #1309 from richardmarshall/go_plugin_guide
Fix typo in the go plugin guide
2019-07-08 09:18:35 -07:00
Richard Marshall
f996ac82c7 Fix typo in the go plugin guide 2019-07-03 20:48:07 -07:00
Jeff Regan
efcb7cc5a5 Update README.md 2019-07-03 12:43:17 -07:00
Jeff Regan
bf7b57537b Merge pull request #1306 from monopole/updateV3Notes
Update v3 notes
2019-07-03 12:40:53 -07:00
Jeffrey Regan
6b597f8711 Update v3 notes 2019-07-03 12:40:30 -07:00
Jeff Regan
088739900f Merge pull request #1305 from monopole/tweakDocs
Update goPluginGuidedExample.md
2019-07-03 12:25:21 -07:00
Jeff Regan
3bf13f83d3 Update goPluginGuidedExample.md 2019-07-03 12:24:23 -07:00
Jeff Regan
c64a72f1f9 Update goPluginGuidedExample.md 2019-07-03 11:34:16 -07:00
Jeff Regan
8b60b456ac Update README.md 2019-07-03 11:22:27 -07:00
Jeff Regan
e0bac6ad19 Merge pull request #1302 from mmb/nginx-auth-tls-secret
Add support for nginx.ingress.kubernetes.io/auth-tls-secret
2019-07-03 11:01:14 -07:00
Kubernetes Prow Robot
d841d1bb36 Merge pull request #1270 from sunny0826/master
Chinese localization & fix fields.md
2019-07-03 09:00:35 -07:00
Matthew M. Boedicker
0d87cd6ba1 Add support for nginx.ingress.kubernetes.io/auth-tls-secret 2019-07-02 20:17:16 -07:00
郭旭东
28ad36b02c Merge remote-tracking branch 'upstream/master' 2019-07-03 09:38:44 +08:00
郭旭东
cad8a7bd3f fix zh/fields.md 2019-07-03 09:21:33 +08:00
Jeff Regan
60a990d660 Merge pull request #1297 from arrikto/feature-complex-overlay-composition
Add complex overlay composition test
2019-07-02 13:41:52 -07:00
Kubernetes Prow Robot
cb3751cea6 Merge pull request #1293 from monopole/fix1281
Fix 1281
2019-07-02 09:45:09 -07:00
Ioannis Androulidakis
5ad012e6d9 Add complex overlay composition test
Signed-off-by: Ioannis Androulidakis <ioannis@arrikto.com>
Signed-off-by: Alex Pyrgiotis <apyrgio@arrikto.com>
2019-07-02 19:26:04 +03:00
Jeffrey Regan
8a454de8f9 Fix 1281 2019-07-01 18:09:48 -07:00
Jeff Regan
57b18b7caa Merge pull request #1294 from monopole/repo1281
Add a test combining imagetag and jsonpatch
2019-07-01 18:05:47 -07:00
Jeffrey Regan
701d2c9597 Add a test combining imagetag and jsonpatch 2019-07-01 17:58:46 -07:00
Kubernetes Prow Robot
e7e844bc95 Merge pull request #1291 from monopole/addTest
Add another resmap test.
2019-07-01 11:55:22 -07:00
Jeffrey Regan
0fe95a2f74 Add another resmap test. 2019-07-01 11:17:46 -07:00
Jeff Regan
eb93d8c389 Merge pull request #1289 from monopole/moreTests
Add more json transformer examples.
2019-07-01 09:14:43 -07:00
jregan
8b373ab587 Add more json transformer examples. 2019-07-01 08:52:55 -07:00
Jeff Regan
c352003f3e Merge pull request #1290 from arrikto/feature-diamond-test-harden
Update the diamond composition test case
2019-07-01 08:34:23 -07:00
Alex Pyrgiotis
79d0de7000 Update the diamond composition test case
Refactor the existing base, so that one of the overlays patches an
already present field of the base resource. Previously, all overlays
added a new field in the base deployment, which made this case easier to
solve, with a merge of the produced YAMLs from each overlay.

Also, fix a typo in the comments.

Signed-off-by: Alex Pyrgiotis <apyrgio@arrikto.com>
Signed-off-by: Ioannis Androulidakis <ioannis@arrikto.com>
2019-07-01 14:57:59 +00:00
Jeff Regan
a32d5ce7ab Merge pull request #1288 from monopole/inlineJson
Push json transform code down to plugin.
2019-06-30 17:49:24 -07:00
jregan
5de0673db1 Push json transform code down to plugin. 2019-06-30 17:30:52 -07:00
Jeff Regan
c2b0b6f781 Merge pull request #1286 from monopole/anotherTest
Stacked patching example
2019-06-30 08:58:08 -07:00
jregan
116b37813a stackedOverlays 2019-06-30 08:45:37 -07:00
Jeff Regan
27f0d29734 Merge pull request #1287 from monopole/defineConstants
Define some constants in a test for later reuse.
2019-06-30 08:44:16 -07:00
jregan
f62af4ebf3 Define some constants in a test for later reuse. 2019-06-30 08:42:15 -07:00
Jeff Regan
faa6d0fd0a Merge pull request #1285 from monopole/v1App
Switch Deployment apiVersion to apps/v1 in a couple of tests.
2019-06-30 07:51:53 -07:00
jregan
0554da9d6e Switch to apps/v1 in various tests. 2019-06-30 07:45:29 -07:00
Jeff Regan
fa1fd9fbd7 Merge pull request #1283 from arrikto/feature-diamond-composition-test
Add diamond composition test with multiple patches
2019-06-30 06:56:56 -07:00
Jeff Regan
3dffc30e83 Merge pull request #1280 from sethpollack/generator_metadata
use ObjectMeta
2019-06-30 06:55:52 -07:00
Seth Pollack
2126b6cf23 use ObjectMeta instead of name and namespace fields 2019-06-29 23:30:50 -04:00
Ioannis Androulidakis
2b052fdd55 Add diamond composition test with multiple patches
The purpose of this commit is to demonstrate a composition use case,
that combines multiple overlays that patch the same base resource.

Signed-off-by: Ioannis Androulidakis <ioannis@arrikto.com>
Signed-off-by: Alex Pyrgiotis <apyrgio@arrikto.com>
2019-06-29 22:05:30 +03:00
Jeff Regan
58faa762cb Merge pull request #1275 from Liujingfang1/patches
add function to find all matched patch targets
2019-06-29 09:36:53 -07:00
jingfangliu
349cfff1cb add function to find all matched patch targets 2019-06-28 17:12:02 -07:00
Jeff Regan
558be8b923 Merge pull request #1284 from monopole/objectmeta
Add ObjectMeta type.
2019-06-28 14:56:45 -07:00
Jeffrey Regan
233b3613ae Add ObjectMeta type. 2019-06-28 14:39:56 -07:00
Jeff Regan
615a41d6be Merge pull request #1282 from monopole/repoBug1044
Repo bug 1044
2019-06-28 13:08:39 -07:00
Jeffrey Regan
0ceefcf39d Repo bug 1044 2019-06-28 11:29:48 -07:00
Jeff Regan
16ae64a722 Merge pull request #1279 from monopole/fix972
Fix JSON patch targetting issue.
2019-06-27 17:43:54 -07:00
Jeff Regan
3f239fb4a5 Update bugs.md 2019-06-27 17:40:34 -07:00
Jeffrey Regan
a60d99fdc9 Fix 972 2019-06-27 17:37:12 -07:00
Kubernetes Prow Robot
dd0334536b Merge pull request #1278 from monopole/addDiamondTest
Add diamond base test, improve patching error messages.
2019-06-27 16:53:21 -07:00
Jeffrey Regan
3cef37bdb2 Add diamond base test. 2019-06-27 16:10:58 -07:00
Jeff Regan
ac27e94dff Update README.md 2019-06-27 16:04:15 -07:00
Jeff Regan
0877aa7e0b Merge pull request #1277 from monopole/deleteGetById
Delete deprecated GetById
2019-06-27 15:28:34 -07:00
Jeffrey Regan
07e5a544fe Delete deprecated GetById 2019-06-27 15:26:03 -07:00
Kubernetes Prow Robot
60c04a5f33 Merge pull request #1271 from etiennecoutaud/support_azure_tfs_url
Add suport for _git in git  url
2019-06-27 11:51:22 -07:00
Jeff Regan
b9b9fb1dd2 Merge pull request #1274 from monopole/plainerPluginSecurityDiscussion
Plainer plugin security discussion.
2019-06-27 11:09:08 -07:00
Jeffrey Regan
e1233a0fbc Plainer plugin security discussion. 2019-06-27 11:08:26 -07:00
Etienne Coutaud
cc8203032c Add suport for _git in git url 2019-06-27 11:54:37 +02:00
郭旭东
7117961234 Chinese localization & fix fields.md 2019-06-27 16:52:00 +08:00
Jeff Regan
d410252cf8 Update goPluginCaveats.md 2019-06-26 16:56:46 -07:00
Jeff Regan
4235c57657 Update goPluginCaveats.md 2019-06-26 16:55:33 -07:00
Jeff Regan
e34c1ce192 Merge pull request #1263 from monopole/pluginsDocs
More plugin docs.
2019-06-26 16:38:53 -07:00
Jeffrey Regan
4d399ad89c More plugin docs. 2019-06-26 16:37:26 -07:00
Jeff Regan
9d6ab24388 Merge pull request #1260 from monopole/gomodtidy
A round of go mod tidy.
2019-06-26 13:27:20 -07:00
Jeffrey Regan
ee9f35d451 A round of go mod tidy. 2019-06-26 13:24:40 -07:00
Jeff Regan
45c11ec733 Update README.md 2019-06-26 13:22:22 -07:00
Jeff Regan
0519df4ad5 Merge pull request #1259 from monopole/releasing
Update notes for releasing.
2019-06-26 13:19:03 -07:00
Jeffrey Regan
55585d8da5 Update notes for releasing. 2019-06-26 13:10:41 -07:00
Jeff Regan
b8b49c3124 Merge pull request #1244 from Liujingfang1/patches
add type for extended patches
2019-06-26 11:21:22 -07:00
Jeff Regan
a41471d895 Merge pull request #1254 from monopole/nonnamespace
Fix some minor nits around namespace code.
2019-06-26 10:25:16 -07:00
Jeffrey Regan
877e9ecf64 Fix some minor nits around namespace code. 2019-06-26 10:02:22 -07:00
Kubernetes Prow Robot
150985bb9c Merge pull request #1200 from sunny0826/master
Chinese configGeneration.md dosc
2019-06-26 09:11:16 -07:00
jingfangliu
039f7669df add type for extended patches 2019-06-26 09:01:17 -07:00
Jeff Regan
6caa042b05 Merge pull request #1255 from monopole/fixNits
Fix some random Go nits.
2019-06-25 21:03:38 -07:00
jregan
cc0fffc67b Fix some random Go nits. 2019-06-25 20:46:56 -07:00
郭旭东
50d40ef941 fix zh/configGeneration.md 2019-06-26 09:57:46 +08:00
Jeff Regan
69d40bd740 Merge pull request #1221 from keleustes/mergens
ConfigMap Generators with identical name in different namespaces
2019-06-25 16:31:18 -07:00
Jerome Brette
4272611593 Change key used sort "not namespaceable objects.
- Use "%no_namespace% instead of "cluster-wide"
- Ensure will be no conflict with a kubernetes valid namespace name.
2019-06-25 17:04:55 -05:00
Jerome Brette
74f5e74b89 Consolidate IsClusterKind and IsNamespaceableKind method to avoid duplication 2019-06-25 13:46:49 -05:00
Jerome Brette
2bba0a6aa3 Support for ConfigMap generator with identical names in different namespaces.
- Attempt to account, at build time, for subsequent kubectl apply behavior.
  (empty or no namespace means default).
- Account for the fact that not all objects have a namespace.
- Add new Equal method to ResId address merge name conflict
- Add GroupByName to resources by namespaces to resolve filenames conflict
- Added corresponding unit tests.
- Change the fail test for issue #1155
2019-06-25 13:39:32 -05:00
Jeff Regan
762d3143eb Merge pull request #1242 from taxpon/add-patch-remover
Add patch remover
2019-06-25 09:51:44 -07:00
Jeff Regan
7f22e25dfe Merge pull request #1239 from sethpollack/fix_plugins
allow reuse of plugins
2019-06-25 09:10:05 -07:00
Kubernetes Prow Robot
41c162a65f Merge pull request #1204 from zeusro/master
Chinese translation:jsonpatch.md
2019-06-25 09:02:40 -07:00
Zeusro
ca521946a5 add missing text 2019-06-25 16:50:27 +08:00
Zeusro
b0e53d2b39 Chinese translation:jsonpatch.md 2019-06-25 16:49:41 +08:00
Takuro Wada
5c93722db8 Update pkg structure to avoid circular import 2019-06-25 11:46:01 +09:00
Takuro Wada
d34c82c905 move globPatterns to edit pkg and make it public 2019-06-25 11:21:29 +09:00
Takuro Wada
f11d083b0a Apply goimports 2019-06-25 11:05:56 +09:00
Takuro Wada
f1a5a7703c Update Copyright to shorter one 2019-06-25 11:05:43 +09:00
Seth Pollack
9cc2c90a4b allow reuse of plugins 2019-06-24 21:09:13 -04:00
Jeff Regan
bc31fa9120 Merge pull request #1247 from monopole/updateReleaseNotes
Update versioning policy.
2019-06-24 17:27:44 -07:00
Jeffrey Regan
7a67645558 Update versioning policy. 2019-06-24 17:26:42 -07:00
Jeff Regan
b0f59358d9 Update v3.0.0.md 2019-06-24 16:17:36 -07:00
Jeff Regan
0e6c7d8af7 Update v3.0.0.md 2019-06-24 16:15:54 -07:00
Jeff Regan
9c20085ca9 Update goPluginGuidedExample.md 2019-06-24 15:27:14 -07:00
Jeff Regan
d48a52055a Update goPluginGuidedExample.md 2019-06-24 15:25:03 -07:00
Jeff Regan
dc433e12fb Merge pull request #1246 from monopole/pluginDocGetsOwnDir
Add another detailed plugin example.
2019-06-24 15:21:04 -07:00
Jeffrey Regan
1740ca6a16 Add another detailed plugin example. 2019-06-24 15:19:49 -07:00
Takuro Wada
2ae8ca1d63 Fix help message to align other cmd 2019-06-24 21:28:44 +09:00
Takuro Wada
674cd89ac9 Add patch remover 2019-06-24 21:25:01 +09:00
Takuro Wada
6ed70add4a Add Delete function to patch pkg 2019-06-24 21:14:26 +09:00
Jeff Regan
ae5ebccec7 Merge pull request #1241 from monopole/hackPluginTesting
Hack for local testing of isolated plugins.
2019-06-23 18:05:23 -07:00
jregan
19c8e23425 Hack for local testing of isolated plugins. 2019-06-23 18:04:20 -07:00
Jeff Regan
b878cd050d Merge pull request #1240 from monopole/v3
Starting v3 release for plugin developers.
2019-06-23 15:18:18 -07:00
jregan
a7df00c07a Starting v3 release for plugin developers.
[doc]: https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher

Per this Go modules [doc] a repo or branch that's
already tagged v2 or higher should increment the major
version (e.g. go to v3) when releasing their first Go
module-based packages.

At the moment, the kustomize repo has these top level
packages in the sigs.k8s.io/kustomize module:

 - `cmd` - holds main program for kustomize

	 Conceivably someone can depend on this
	 package for integration tests.

 - `internal` - intentionally unreleased subpackages

 - `k8sdeps` - an adapter wrapping k8s dependencies

	 This exists only for use in pre-Go-modules kustomize-into-kubectl
	 integration and won't live much longer (as everything involved is
	 switching to Go modules).

 - `pkg` - kustomize packages for export

	 This should shrink in later versions, since
	 the surface area is too large, containing
	 sub-packages that should be in 'internal'.

 - `plugin` - holds main programs for plugins

This PR changes the top level go.mod file from

```
module sigs.k8s.io/kustomize
```

to

```
module sigs.k8s.io/kustomize/v3
```

and adjusts all import statements to
reflect the change.
2019-06-23 15:05:59 -07:00
Jeff Regan
3127f1adc6 Merge pull request #1238 from monopole/removeSopsEncodedSecretsPlugin
Remove SopsEncodedSecretsPlugin
2019-06-23 10:02:15 -07:00
jregan
a722cca80a Remove SopsEncodedSecretsPlugin 2019-06-23 09:46:45 -07:00
Jeff Regan
0ffd78eab6 Merge pull request #1224 from keleustes/varset
Change the backing data structure of VarSet from slice to map
2019-06-23 09:03:48 -07:00
Jeff Regan
694c868048 Merge pull request #1231 from arnodel/fix-1228
Iterate over fieldspecs for name tranformations (fixes #1228)
2019-06-23 09:02:54 -07:00
Jeff Regan
2da2006e2a Merge pull request #1237 from taxpon/add-metadata-remover
Add metadata remover
2019-06-23 08:56:03 -07:00
Jeff Regan
0bc83ca065 Merge pull request #1227 from leoxiongdev/patch-1
Fix typo
2019-06-23 08:36:08 -07:00
Takuro Wada
ab2643ef14 Fix FakeValidator 2019-06-23 19:59:01 +09:00
Takuro Wada
297812ec11 Fix lint 2019-06-23 19:43:49 +09:00
Takuro Wada
158f754f18 Add remove annotation command 2019-06-23 18:43:52 +09:00
Takuro Wada
da3504105e Add remove label command 2019-06-23 18:43:52 +09:00
Arnaud Delobelle
d3f50695b4 Fix configmap name in test 2019-06-22 08:46:22 +01:00
Arnaud Delobelle
5a9a6ab0f6 Fix typo in test 2019-06-22 08:27:23 +01:00
Arnaud Delobelle
b86e78b7a9 Add test for regression 2019-06-22 08:09:39 +01:00
Ian Howell
b1cdf581d0 Change the backing data structure of VarSet from slice to map
This will speed up most operations performed on a large set of Vars
2019-06-21 17:28:57 -05:00
Jeff Regan
8bf20527be Merge pull request #1232 from monopole/moreVarTestCoverage
More var test coverage.
2019-06-21 14:14:53 -07:00
Jeffrey Regan
3eedc40595 More var test coverage. 2019-06-21 13:53:58 -07:00
Arnaud Delobelle
93db0ef3e9 Iterate over fieldspecs for name tranformations 2019-06-21 21:08:59 +01:00
Jeff Regan
6922dbbc70 Merge pull request #1225 from keleustes/fix/compiler
Fixed unit test failures when GOROOT was unset
2019-06-21 09:52:39 -07:00
Jeff Regan
f1b9b27a15 Merge pull request #1220 from keleustes/downwardapi
FieldPath syntax backward compatibility
2019-06-21 09:36:52 -07:00
Leo Xiong
a755558beb Fix typo 2019-06-21 12:29:31 +12:00
Ian Howell
b8423d0f5f Fixed unit test failures when GOROOT was unset 2019-06-20 16:37:20 -05:00
Jeff Regan
42ef4fbcc1 Merge pull request #1222 from monopole/linuxIn30
Another plugin demo.
2019-06-20 11:47:34 -07:00
Jeffrey Regan
69c11780eb Another plugin demo. 2019-06-20 11:45:26 -07:00
Jerome Brette
c925b43090 FieldSpec Backward Compatibility with K8s Downward API 2019-06-20 09:22:47 -05:00
Kubernetes Prow Robot
a5b97cbd9b Merge pull request #1214 from monopole/sops
A secret generator using sops.
2019-06-19 19:50:48 -07:00
Jeffrey Regan
bcb844663f A secret generator using sops. 2019-06-19 18:55:50 -07:00
Jeff Regan
0905ee293c Merge pull request #1213 from monopole/commitGenerated
Commit generated code for image and namespace transformers.
2019-06-19 18:53:02 -07:00
Jeffrey Regan
3325852aab Commit generated code for image and namespace transformers. 2019-06-19 18:52:24 -07:00
Jeff Regan
c437d99c5f Merge pull request #1210 from monopole/pushTagTransformCodeToPlugin
Push image tag transform code to plugin.
2019-06-19 18:26:31 -07:00
Jeff Regan
cacafc63e8 Merge pull request #1211 from monopole/pushNamespaceTransformerCodeToPlugin
Push namespace transformer code to plugin.
2019-06-19 18:26:05 -07:00
Jeff Regan
b08f3383b8 Merge pull request #1206 from jbrette/namespace
Addresses issue discovered in attempting to patch K8s objects across different namespaces.
2019-06-19 13:07:50 -07:00
Jeffrey Regan
2eccf67b1c Push namespace transformer code to plugin. 2019-06-19 12:57:02 -07:00
Kubernetes Prow Robot
293c8bef54 Merge pull request #1202 from mamoit/patch-1
Typo correction in 2.1.0 release notes
2019-06-19 12:38:19 -07:00
Kubernetes Prow Robot
00c7ae0542 Merge pull request #1199 from ramnar/master
Corrected spelling mistake
2019-06-19 12:36:21 -07:00
Jeffrey Regan
cd656faadf Push image tag transform code to plugin. 2019-06-19 12:24:00 -07:00
Kubernetes Prow Robot
056b95ffa9 Merge pull request #1201 from yujunz/fix-typo
Fix typo in docs
2019-06-19 11:28:34 -07:00
Kubernetes Prow Robot
d211df1e73 Merge pull request #1205 from pyaillet/fix-typo
Fix typo in comment
2019-06-19 08:56:36 -07:00
Jerome Brette
934e036b99 Attempting to patch K8s objects across different namespaces.
- Return an error when findTarget fails.
- Add unit test testing the change.
2019-06-19 08:42:12 -05:00
Zeusro
9fc86f92fa Chinese translation:jsonpatch.md 2019-06-19 21:16:40 +08:00
Pierre-Yves Aillet
49c6bd4141 Fix typo in comment 2019-06-19 14:53:29 +02:00
Miguel Almeida
24011cf2a5 Simple flag typo correction
The flag is load_restrictor and not load_restrictions.
2019-06-19 11:35:33 +01:00
郭旭东
83b284dfde fix configGeneration.md 2019-06-19 17:21:07 +08:00
Yujun Zhang
7c9181317f Fix typo in docs 2019-06-19 15:09:31 +08:00
郭旭东
01b410fe9c Chinese configGeneration.md dosc 2019-06-19 14:36:21 +08:00
ramnar
56ac98468d Corrected spelling mistake
Corrected spelling mistake
2019-06-19 08:22:55 +05:30
Jeff Regan
658ebeaa21 Merge pull request #1197 from keleustes/showcasevariables
Demonstrate new capabilities in pkg/target testing
2019-06-18 19:22:27 -07:00
Jeff Regan
59aa898533 Merge pull request #1198 from sunny0826/master
update docs/zh/README.md
2019-06-18 19:21:37 -07:00
Jeff Regan
c88f87cee2 Merge pull request #1196 from keleustes/helperimprovment
Improve robutness of helper code
2019-06-18 18:59:15 -07:00
郭旭东
cc663bb08c update docs/zh/README.md 2019-06-19 09:26:44 +08:00
Jeff Regan
63d647df18 Update secretGeneratorPlugin.md 2019-06-18 16:50:48 -07:00
Jerome Brette
e3a46cb6ce Leverage new variables during testing
Extract the HTTP port (int) from the container section end
use it in the service definition.

Also enable variable replacement for Service object.
2019-06-18 18:37:20 -05:00
Jerome Brette
4556eb3a0c Improve robutness of helper code
As per request, changed usage of pointer to int into plain int.
Use -1 value where nil use to be used.
2019-06-18 18:06:38 -05:00
Jeff Regan
26ed9b7c58 Fix date of release v2.1.0 2019-06-18 15:30:37 -07:00
Jeff Regan
8ff0b5423d Update v2.1.0.md 2019-06-18 15:27:27 -07:00
Jeff Regan
0fbced95a8 Merge pull request #1195 from monopole/updateReleaseLinks
Update release note links.
2019-06-18 15:25:09 -07:00
Jeffrey Regan
66b816aabc Update release note links. 2019-06-18 15:24:10 -07:00
419 changed files with 37251 additions and 4989 deletions

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@
*.dll
*.so
*.dylib
/kustomize
# Test binary, build with `go test -c`
*.test

37
Makefile Normal file
View File

@@ -0,0 +1,37 @@
BIN_NAME=kustomize
COVER_FILE=coverage.out
export GO111MODULE=on
all: test build
test: generate-code test-lint test-go
test-go:
go test -v ./...
test-lint:
golangci-lint run ./...
generate-code:
./plugin/generateBuiltins.sh $(GOPATH)
build:
go build -o $(BIN_NAME) cmd/kustomize/main.go
install:
go install $(PWD)/cmd/kustomize
cover:
# The plugin directory eludes coverage, and is therefore omitted
go test ./pkg/... ./k8sdeps/... ./internal/... -coverprofile=$(COVER_FILE) && \
go tool cover -html=$(COVER_FILE)
clean:
go clean
rm -f $(BIN_NAME)
rm -f $(COVER_FILE)
.PHONY: test build install clean generate-code test-go test-lint cover

View File

@@ -7,7 +7,7 @@ untouched and usable as is.
`kustomize` targets kubernetes; it understands and can
patch [kubernetes style] API objects. It's like
[`make`], in that what it does is declared in a file,
and it's like [`sed`], in that it emits editted text.
and it's like [`sed`], in that it emits edited text.
This tool is sponsored by [sig-cli] ([KEP]), and
inspired by [DAM].
@@ -22,8 +22,16 @@ these [instructions](docs/INSTALL.md).
Browse the [docs](docs) or jump right into the
tested [examples](examples).
kustomize [v2.0.3] is available in [kubectl v1.14][kubectl].
## kubectl integration
Since [v1.14][kubectl announcement] the kustomize build system has been included in kubectl.
| kubectl version | kustomize version |
|---------|--------|
| v1.15.x | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
| v1.14.x | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
For examples and guides for using the kubectl integration please see the [kubectl book] or the [kubernetes documentation].
## Usage
@@ -159,7 +167,9 @@ is governed by the [Kubernetes Code of Conduct].
[imageBase]: docs/images/base.jpg
[imageOverlay]: docs/images/overlay.jpg
[kind/feature]: https://github.com/kubernetes-sigs/kustomize/labels/kind%2Ffeature
[kubectl]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
[kubectl announcement]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
[kubectl book]: https://kubectl.docs.kubernetes.io/pages/app_customization/introduction.html
[kubernetes documentation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
[kubernetes style]: docs/glossary.md#kubernetes-style-object
[kustomization]: docs/glossary.md#kustomization
[overlay]: docs/glossary.md#overlay
@@ -171,4 +181,5 @@ is governed by the [Kubernetes Code of Conduct].
[variant]: docs/glossary.md#variant
[variants]: docs/glossary.md#variant
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
[v2.1.0]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.1.0
[workflows]: docs/workflows.md

View File

@@ -1,43 +0,0 @@
[releases page]: https://github.com/kubernetes-sigs/kustomize/releases
[`cloud-build-local`]: https://github.com/GoogleCloudPlatform/cloud-build-local
[Google Cloud Build]: https://cloud.google.com/cloud-build
Scripts and configuration files for publishing a
`kustomize` release on the [releases page].
### Build a release locally
Install [`cloud-build-local`], then run
```
./build/localbuild.sh
```
to build artifacts under `./dist`.
### Publish a Release
Get on an up-to-date master branch:
```
git checkout master
git fetch upstream
git rebase upstream/master
```
Define the version (see [semver principles](https://semver.org)), e.g.:
```
version=v1.0.3
```
Tag the repo:
```
git tag -a $version -m "$version release"
```
Push the tag upstream:
```
git push upstream $version
```
The new tag will trigger a job in [Google Cloud
Build] to put a new release on the [releases page].

View File

@@ -6,7 +6,7 @@ package main
import (
"os"
"sigs.k8s.io/kustomize/pkg/commands"
"sigs.k8s.io/kustomize/v3/pkg/commands"
)
func main() {

View File

@@ -12,8 +12,17 @@ import (
"path/filepath"
"strings"
"sigs.k8s.io/kustomize/pkg/pgmconfig"
"sigs.k8s.io/kustomize/pkg/plugins"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
"sigs.k8s.io/kustomize/v3/pkg/plugins"
)
//go:generate stringer -type=pluginType
type pluginType int
const (
unknown pluginType = iota
Transformer
Generator
)
func main() {
@@ -34,24 +43,41 @@ func main() {
fmt.Sprintf(
"// Code generated by pluginator on %s; DO NOT EDIT.",
root))
w.write("package builtin")
w.write("package " + plugins.BuiltinPluginPackage)
pType := unknown
for scanner.Scan() {
l := scanner.Text()
if strings.HasPrefix(l, "//go:generate") {
continue
}
if l == "var "+plugins.PluginSymbol+" plugin" {
w.write("func New" + root + "Plugin() *" + root + "Plugin {")
w.write(" return &" + root + "Plugin{}")
w.write("}")
if strings.HasPrefix(l, "//noinspection") {
continue
}
if l == "var "+plugins.PluginSymbol+" plugin" {
continue
}
if strings.Contains(l, " Transform(") {
if pType != unknown {
log.Fatal("unexpected Transform(")
}
pType = Transformer
} else if strings.Contains(l, " Generate(") {
if pType != unknown {
log.Fatal("unexpected Generate(")
}
pType = Generator
}
w.write(l)
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
w.write("")
w.write("func New" + root + "Plugin() resmap." + pType.String() + "Plugin {")
w.write(" return &" + root + "Plugin{}")
w.write("}")
}
func inputFileRoot() string {
@@ -93,7 +119,7 @@ func makeOutputFileName(root string) string {
pgmconfig.DomainName,
pgmconfig.ProgramName,
pgmconfig.PluginRoot,
"builtin",
plugins.BuiltinPluginPackage,
root+".go")
}

View File

@@ -0,0 +1,25 @@
// Code generated by "stringer -type=pluginType"; DO NOT EDIT.
package main
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[unknown-0]
_ = x[Transformer-1]
_ = x[Generator-2]
}
const _pluginType_name = "unknownTransformerGenerator"
var _pluginType_index = [...]uint8{0, 7, 18, 27}
func (i pluginType) String() string {
if i < 0 || i >= pluginType(len(_pluginType_index)-1) {
return "pluginType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _pluginType_name[_pluginType_index[i]:_pluginType_index[i+1]]
}

View File

@@ -23,17 +23,36 @@ kustomization file, and refering to this directory as a
wants to use it. This encourages modularity and
relocatability.
At the moment (in v2.0.3), however, there's no
(released) analogous way to share patch files and other
transformer configuration data between kustomizations.
As a stop-gap until we add base-like behavior for
transformers, we've added a flag to disable the check:
To disable this, use v3, and the `load_restrictor` flag:
```
kustomize build --load_restrictor none $target
```
This flag is not in v2.0.3, but is available from head
(`go install sigs.k8s.io/kustomize`).
## Some field is not transformed by kustomize
Example: [#1319](https://github.com/kubernetes-sigs/kustomize/issues/1319), [#1322](https://github.com/kubernetes-sigs/kustomize/issues/1322), [#1347](https://github.com/kubernetes-sigs/kustomize/issues/1347) and etc.
The fields transformed by kustomize is configured explicitly in [defaultconfig](https://github.com/kubernetes-sigs/kustomize/tree/master/pkg/transformers/config/defaultconfig). The configuration itself can be customized by including `configurations` in `kustomization.yaml`, e.g.
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configurations:
- kustomizeconfig.yaml
```
The configuration directive allows customization of the following transformers:
```yaml
commonAnnotations: []
commonLabels: []
nameprefix: []
namespace: []
varreference: []
namereference: []
images: []
replicas: []
```
To persist the changes to default configuration, submit a PR like [#1338](https://github.com/kubernetes-sigs/kustomize/pull/1338), [#1348](https://github.com/kubernetes-sigs/kustomize/pull/1348) and etc.

View File

@@ -1,13 +1,15 @@
[release page]: https://github.com/kubernetes-sigs/kustomize/releases
[Go]: https://golang.org
## Installation
# Installation
For linux, macOs and Windows,
download a binary from the
[release page].
Binaries at various versions for linux, macOs and Windows
are available on the [release page].
Or...
## Quickly curl the latest
Or try this command:
```
opsys=linux # or darwin, or windows
curl -s https://api.github.com/repos/kubernetes-sigs/kustomize/releases/latest |\
@@ -19,17 +21,19 @@ mv kustomize_*_${opsys}_amd64 kustomize
chmod u+x kustomize
```
To install from head with [Go] v1.12 or higher:
## Install from the HEAD of master branch
<!-- @installkustomize @test -->
Requires [Go] v1.12 or higher:
<!-- @installkustomize @testAgainstLatestRelease -->
```
go install sigs.k8s.io/kustomize/cmd/kustomize
go install sigs.k8s.io/kustomize/v3/cmd/kustomize
```
### Other methods
#### macOS
```
brew install kustomize
```
@@ -44,4 +48,3 @@ For support on the chocolatey package
and prior releases, see:
- [Choco Package](https://chocolatey.org/packages/kustomize)
- [Package Source](https://github.com/kenmaglio/choco-kustomize)

View File

@@ -12,7 +12,7 @@ English | [简体中文](zh/README.md)
* [Kustomize Fields](fields.md) - explanations of the fields
in a [kustomization](glossary.md#kustomization) file.
* [Plugins](plugins.md) - extending kustomize with
* [Plugins](plugins) - extending kustomize with
custom generators and transformers.
* [Workflows](workflows.md) - steps one might take in
@@ -23,12 +23,16 @@ English | [简体中文](zh/README.md)
## Release notes
* [2.1](v_2.1.0.md) - Date TBD, target late May 2019
* [3.1](v3.1.0.md) - Late July 2019. Extended patches and improved resource matching.
* [2.0](v_2.0.0.md) - Mar 2019.
* [3.0](v3.0.0.md) - Late June 2019. Plugin developer release.
* [2.1](v2.1.0.md) - 18 June 2019. Plugins, ordered resources, etc.
* [2.0](v2.0.0.md) - Mar 2019.
kustomize [v2.0.3] is available in [kubectl v1.14][kubectl].
* [1.0](v_1.0.1.md) - May 2018. Initial release after development
* [1.0](v1.0.1.md) - May 2018. Initial release after development
in the [kubectl repository].

View File

@@ -20,7 +20,10 @@ following to improve response time.
kustomize has a simple test harness in the
[target package] for specifying a kustomization's
input and the expected output.
See this [example of a target test].
See this [example of a target test], and contribution
[#971](https://github.com/kubernetes-sigs/kustomize/pull/971),
which does exactly the right thing.
The pattern is
* call `NewKustTestHarness`

1
docs/execPluginIn30sec.md Symbolic link
View File

@@ -0,0 +1 @@
plugins/execPluginGuidedExample.md

View File

@@ -1,5 +1,19 @@
# Kustomization File Fields
[field-name-namespace]: plugins/builtins.md#field-name-namespace
[field-name-images]: plugins/builtins.md#field-name-images
[field-name-namePrefix]: plugins/builtins.md#field-name-prefix
[field-name-nameSuffix]: plugins/builtins.md#field-name-prefix
[field-name-patches]: plugins/builtins.md#field-name-patches
[field-name-patchesStrategicMerge]: plugins/builtins.md#field-name-patchesStrategicMerge
[field-name-patchesJson6902]: plugins/builtins.md#field-name-patchesJson6902
[field-name-replicas]: plugins/builtins.md#field-name-replicas
[field-name-secretGenerator]: plugins/builtins.md#field-name-secretGenerator
[field-name-commonLabels]: plugins/builtins.md#field-name-commonLabels
[field-name-commonAnnotations]: plugins/builtins.md#field-name-commonAnnotations
[field-name-configMapGenerator]: plugins/builtins.md#field-name-configMapGenerator
An explanation of the fields in a [kustomization.yaml](glossary.md#kustomization) file.
@@ -21,7 +35,7 @@ What things should be created (and optionally subsequently customized)?
|[configMapGenerator](#configmapgenerator)| list |Each entry in this list results in the creation of one ConfigMap resource (it's a generator of n maps).|
|[secretGenerator](#secretgenerator)| list |Each entry in this list results in the creation of one Secret resource (it's a generator of n secrets)|
|[generatorOptions](#generatoroptions)|string|generatorOptions modify behavior of all ConfigMap and Secret generators|
|[generators](#generators)|list|[plugin](plugins.md) configuration files|
|[generators](#generators)|list|[plugin](plugins) configuration files|
## Transformers
@@ -38,9 +52,10 @@ What transformations (customizations) should be applied?
| [namePrefix](#nameprefix) | string | Prepends value to the names of all resources |
| [nameSuffix](#namesuffix) | string | The value is appended to the names of all resources. |
| [replicas](#replicas) | list | Replicas modifies the number of replicas of a resource. |
| [patches](#patches) | list | Each entry should resolve to a patch that can be applied to multiple targets. |
|[patchesStrategicMerge](#patchesstrategicmerge)| list |Each entry in this list should resolve to a partial or complete resource definition file.|
|[patchesJson6902](#patchesjson6902)| list |Each entry in this list should resolve to a kubernetes object and a JSON patch that will be applied to the object.|
|[transformers](#transformers)|list|[plugin](plugins.md) configuration files|
|[transformers](#transformers)|list|[plugin](plugins) configuration files|
## Meta
@@ -64,7 +79,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
### bases
The `bases` field was deprecated in v2.1.0.
_The `bases` field was deprecated in v2.1.0._
Move entries into the [resources](#resources)
field. This allows bases - which are still a
@@ -72,51 +87,13 @@ field. This allows bases - which are still a
ordered relative to other input resources.
### commonLabels
Adds labels to all resources and selectors
```
commonLabels:
someName: someValue
owner: alice
app: bingo
```
See [field-name-commonLabels].
### commonAnnotations
Adds annotions (non-identifying metadata) to add
all resources. Like labels, these are key value
pairs.
```
commonAnnotations:
oncallPager: 800-555-1212
```
See [field-name-commonAnnotations].
### configMapGenerator
Each entry in this list results in the creation of
one ConfigMap resource (it's a generator of n maps).
The example below creates two ConfigMaps. One with the
names and contents of the given files, the other with
key/value as data.
Each configMapGenerator item accepts a parameter of
`behavior: [create|replace|merge]`.
This allows an overlay to modify or
replace an existing configMap from the parent.
```
configMapGenerator:
- name: myJavaServerProps
files:
- application.properties
- more.properties
- name: myJavaServerEnvVars
literals:
- JAVA_HOME=/opt/java/jdk
- JAVA_TOOL_OPTIONS=-agentlib:hprof
```
See [field-name-configMapGenerator].
### crds
@@ -143,9 +120,7 @@ The annotations can be put into openAPI definitions are:
- "x-kubernetes-object-ref-kind": "Secret",
- "x-kubernetes-object-ref-name-key": "name",
```
crds:
- crds/typeA.yaml
- crds/typeB.yaml
@@ -173,7 +148,7 @@ generatorOptions:
### generators
A list of generator [plugin](plugins.md) configuration files.
A list of generator [plugin](plugins) configuration files.
```
generators:
@@ -183,42 +158,7 @@ generators:
### images
Images modify the name, tags and/or digest for images without creating patches.
E.g. Given this kubernetes Deployment fragment:
```
containers:
- name: mypostgresdb
image: postgres:8
- name: nginxapp
image: nginx:1.7.9
- name: myapp
image: my-demo-app:latest
- name: alpine-app
image: alpine:3.7
```
one can change the `image` in the following ways:
- `postgres:8` to `my-registry/my-postgres:v1`,
- nginx tag `1.7.9` to `1.8.0`,
- image name `my-demo-app` to `my-app`,
- alpine's tag `3.7` to a digest value
all with the following *kustomization*:
```
images:
- name: postgres
newName: my-registry/my-postgres
newTag: v1
- name: nginx
newTag: 1.8.0
- name: my-demo-app
newName: my-app
- name: alpine
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
```
See [field-name-images].
### inventory
@@ -232,140 +172,33 @@ If missing, this field's value defaults to
kind: Kustomization
```
### namespace
Adds namespace to all resources
```
namespace: my-namespace
```
See [field-name-namespace].
### namePrefix
Prepends value to the names of all resources
Ex. a deployment named `wordpress` would become `alices-wordpress`
```
namePrefix: alices-
```
See [field-name-namePrefix].
### nameSuffix
The value is appended to the names of all
resources. Ex. A deployment named `wordpress`
would become `wordpress-v2`.
See [field-name-nameSuffix].
The suffix is appended before content has if
resource type is ConfigMap or Secret.
### patches
```
nameSuffix: -v2
```
See [field-name-patches].
### patchesStrategicMerge
Each entry in this list should be a relative path
resolving to a partial or complete resource
definition file.
The names in these (possibly partial) resource
files must match names already loaded via the
`resources` field. These entries are used to
_patch_ (modify) the known resources.
Small patches that do one thing are best, e.g. modify
a memory request/limit, change an env var in a
ConfigMap, etc. Small patches are easy to review and
easy to mix together in overlays.
```
patchesStrategicMerge:
- service_port_8888.yaml
- deployment_increase_replicas.yaml
- deployment_increase_memory.yaml
```
See [field-name-patchesStrategicMerge].
### patchesJson6902
Each entry in this list should resolve to
a kubernetes object and a JSON patch that will be applied
to the object.
The JSON patch is documented at https://tools.ietf.org/html/rfc6902
target field points to a kubernetes object within the same kustomization
by the object's group, version, kind, name and namespace.
path field is a relative file path of a JSON patch file.
The content in this patch file can be either in JSON format as
```
[
{"op": "add", "path": "/some/new/path", "value": "value"},
{"op": "replace", "path": "/some/existing/path", "value": "new value"}
]
```
or in YAML format as
- op: add
path: /some/new/path
value: value
- op:replace
path: /some/existing/path
value: new value
```
patchesJson6902:
- target:
version: v1
kind: Deployment
name: my-deployment
path: add_init_container.yaml
- target:
version: v1
kind: Service
name: my-service
path: add_service_annotation.yaml
```
See [field-name-patchesJson6902].
### replicas
Replicas modified the number of replicas for a resource.
E.g. Given this kubernetes Deployment fragment:
```
kind: Deployment
metadata:
name: deployment-name
spec:
replicas: 3
```
one can change the number of replicas to 5
by adding the following to your kustomization:
```
replicas:
- name: deployment-name
count: 5
```
This field accepts a list, so many resources can
be modified at the same time.
#### Limitation
As this declaration does not take in a `kind:` nor a `group:`
it will match any `group` and `kind` that has a matching name and
that is one of:
- `Deployment`
- `ReplicationController`
- `ReplicaSet`
- `StatefulSet`
For more complex use cases, revert to using a patch.
See [field-name-replicas].
### resources
@@ -403,28 +236,7 @@ must contain a `kustomization.yaml` file.
### secretGenerator
Each entry in this list results in the creation of
one Secret resource (it's a generator of n secrets).
```
secretGenerator:
- name: app-tls
files:
- secret/tls.cert
- secret/tls.key
type: "kubernetes.io/tls"
- name: app-tls-namespaced
# you can define a namespace to generate secret in, defaults to: "default"
namespace: apps
files:
- tls.crt=catsecret/tls.cert
- tls.key=secret/tls.key
type: "kubernetes.io/tls"
- name: env_file_secret
envs:
- env.txt
type: Opaque
```
See [field-name-secretGenerator].
### vars

View File

@@ -376,9 +376,8 @@ value is a list.
To change this
default behavior, add a _directive_. Recognized
directives include _replace_ (the default), _merge_
(avoid replacing a list), _delete_ and a few more
(see [these notes][strategic-merge]).
directives in YAML patches are _replace_ (the default)
and _delete_ (see [these notes][strategic-merge]).
Note that for custom resources, SMPs are treated as
[json merge patches][JSONMergePatch].
@@ -405,7 +404,7 @@ A chunk of code used by kustomize, but not necessarily
compiled into kustomize, to generate and/or transform a
kubernetes resource as part of a kustomization.
Details [here](plugins.md).
Details [here](plugins).
## resource

View File

@@ -30,7 +30,7 @@ The _inventory_ ConfigMap contains two special annotations:
struct that contains following information
- all objects within this kustomization target
- all objects that reference within this kustomization target
Here is an example of an Inventory object
```json
{
@@ -49,11 +49,11 @@ The _inventory_ ConfigMap contains two special annotations:
],
"~G_v1_Service|default|mysql":null
}
}
}
```
- kustomize.config.k8s.io/InventoryHash
The value of this annotation is a hash that is
The value of this annotation is a hash that is
computed from the list of items in the Inventory
Basically, this inventory object acts a record of objects that are applied as a group.
@@ -150,7 +150,7 @@ metadata:
annotations:
kustomize.config.k8s.io/Inventory: '{"current":{"apps_v1_Deployment|default|mysql":null,"~G_v1_Secret|default|pass-dfg7h97cf6":[{"group":"apps","version":"v1","kind":"Deployment","name":"mysql","namespace":"default"}],"~G_v1_Service|default|mysql":null}}'
kustomize.config.k8s.io/InventoryHash: 7mgt867b75
name: haha
name: root-cm
namespace: default
```

View File

@@ -1,11 +1,16 @@
# kustomize plugins
Quick guides:
* [linux exec plugin in 60 sec](execPluginGuidedExample.md)
* [linux Go plugin in 60 sec](goPluginGuidedExample.md)
Kustomize offers a plugin framework allowing
people to write their own resource _generators_
and _transformers_.
[generator options]: ../examples/generatorOptions.md
[transformer configs]: ../examples/transformerconfigs
[generator options]: ../../examples/generatorOptions.md
[transformer configs]: ../../examples/transformerconfigs
Write a plugin when changing [generator options]
or [transformer configs] doesn't meet your needs.
@@ -21,9 +26,9 @@ or [transformer configs] doesn't meet your needs.
* A _transformer_ plugin might perform special
container command line edits, or any other
transformation that exceeds the power of the
builtin transformations (`namePrefix`,
`commonLabels`, etc.).
transformation beyond those provided by the
builtin (`namePrefix`, `commonLabels`, etc.)
transformers.
## Specification in `kustomization.yaml`
@@ -43,26 +48,21 @@ Each field accepts a string list:
> - {as above}
> ```
This is exactly like the syntax of the `resources`
field.
The value of each entry in a `generators` or
`transformers` list must be a relative path to a
YAML file, or a path or URL to a [kustomization].
This is the same format as demanded by the
`resources` field.
The value of each entry in a `resources`,
`generators` or `transformers` list must be a
relative path to a YAML file, or a path or URL
to a [kustomization].
[kustomization]: ../glossary.md#kustomization
[kustomization]: glossary.md#kustomization
In the former case the YAML is read from disk directly,
and in the latter case a kustomization is performed,
and its YAML output is merged with the YAML read
directly from files. The net result in all three cases
is a set of YAML objects.
Each object resulting from a `generators` or
`transformers` field is now further interpreted by
YAML files are read from disk directly. Paths or
URLs leading to kustomizations trigger an
in-process kustomization run. Each of the
resulting objects is now further interpreted by
kustomize as a _plugin configuration_ object.
## Configuration
A kustomization file could have the following lines:
@@ -72,11 +72,12 @@ generators:
- chartInflator.yaml
```
Given this, the kustomization process would expect to
find a file called `chartInflator.yaml` in the
kustomization [root](glossary.md#kustomization-root).
Given this, the kustomization process would expect
to find a file called `chartInflator.yaml` in the
kustomization [root](../glossary.md#kustomization-root).
This is the _plugin's configuration file_.
This is the plugin's configuration file;
it contains a YAML configuration object.
The file `chartInflator.yaml` could contain:
@@ -91,19 +92,19 @@ chartName: minecraft
__The `apiVersion` and `kind` fields are
used to locate the plugin.__
[k8s object]: glossary.md#kubernetes-style-object
[k8s object]: ../glossary.md#kubernetes-style-object
> Thus, these fields are required. They are also
> required because a kustomize plugin
> configuration object is also a [k8s object].
Thus, these fields are required. They are also
required because a kustomize plugin configuration
object is also a [k8s object].
To get the plugin ready to generator or transform,
To get the plugin ready to generate or transform,
it is given the entire contents of the
configuration file.
[NameTransformer]: ../plugin/builtin/prefixsuffixtransformer/PrefixSuffixTransformer_test.go
[ChartInflator]: ../plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go
[plugins]: ../plugin/builtin
[NameTransformer]: ../../plugin/builtin/prefixsuffixtransformer/PrefixSuffixTransformer_test.go
[ChartInflator]: ../../plugin/someteam.example.com/v1/chartinflator/ChartInflator_test.go
[plugins]: ../../plugin/builtin
For more examples of plugin configuration YAML,
browse the unit tests below the [plugins] root,
@@ -115,16 +116,18 @@ e.g. the tests for [ChartInflator] or
Each plugin gets its own dedicated directory named
[`XDG_CONFIG_HOME`]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
```
$XDG_CONFIG_HOME/kustomize/plugin
/${apiVersion}/LOWERCASE(${kind})
```
The default value of `XDG_CONFIG_HOME` is
The default value of [`XDG_CONFIG_HOME`] is
`$HOME/.config`.
The one-plugin-per-directory requirement eases
creation of a plugin tarball (source, test, plugin
creation of a plugin bundle (source, tests, plugin
data files, etc.) for sharing.
In the case of a [Go plugin](#go-plugins), it also
@@ -154,9 +157,8 @@ Plugins are only used during a run of the
`kustomize build` command.
Generator plugins are run after processing the
`resources` field (which itself is in some sense a
generator in that it emits resources for further
processing).
`resources` field (which itself can be viewed as a
generator, simply reading objects from disk).
The full set of resources is then passed into the
transformation pipeline, wherein builtin
@@ -166,28 +168,38 @@ in the kustomization file), followed by the
user-specified transformers in the `transformers`
field.
The specified order of transformers in the
`transformers` field should be respected, as
transformers cannot be expected to be commutative.
The order specified in the `transformers` field is
respected, as transformers cannot be expected to
be commutative.
#### No Security
Kustomize plugins do not run in any kind of
kustomize-provided sandbox. There's no notion
of _"plugin security"_.
A `kustomize build` that tries to use plugins but
omits the flag
> `--enable_alpha_plugins`
will fail with a warning about plugin use.
will not load plugins and will fail with a
warning about plugin use.
Flag use is an opt-in acknowledging the absence of
plugin provenance. It's meant to give pause to
someone who blindly downloads a kustomization from
the internet and attempts to run it, without
realizing that it might attempt to run 3rd party
code in plugin form. The plugin would have to be
installed already, but nevertheless the flag is a
reminder.
The use of this flag is an opt-in acknowledging
the unstable (alpha) plugin API, the absence of
plugin provenance, and the fact that a plugin
is not part of kustomize.
To be clear, some kustomize plugin downloaded
from the internet might wonderfully transform
k8s config in a desired manner, while also
quietly doing anything the user could do to the
system running `kustomize build`.
## Writing plugins
## Authoring
There are two kinds of plugins, [exec](#exec-plugins) and [Go](#go-plugins).
### Exec plugins
@@ -196,20 +208,22 @@ single argument on its command line - the name of
a YAML file containing its configuration (the file name
provided in the kustomization file).
> TODO: more restrictions on plugin to allow the same exec
> plugin to be specified in a config under both the
> TODO: restrictions on plugin to allow the _same exec
> plugin_ to be targetted by both the
> `generators` and `transformers` fields.
>
> - first arg could be the fixed string
> `generate` or `transform`,
> (the name of the configuration file moves to
> the 2nd arg), or
> - by default an exec plugin behaves as a tranformer
> - or by default an exec plugin behaves as a tranformer
> unless a flag `-g` is provided, switching the
> exec plugin to behave as a generator.
[helm chart inflator]: ../plugin/someteam.example.com/v1/chartinflator
[bashed config map]: ../plugin/someteam.example.com/v1/bashedconfigmap
[sed transformer]: ../plugin/someteam.example.com/v1/sedtransformer
[helm chart inflator]: ../../plugin/someteam.example.com/v1/chartinflator
[bashed config map]: ../../plugin/someteam.example.com/v1/bashedconfigmap
[sed transformer]: ../../plugin/someteam.example.com/v1/sedtransformer
[hashicorp go-getter]: ../../plugin/someteam.example.com/v1/gogetter
#### Examples
@@ -217,7 +231,7 @@ provided in the kustomization file).
* [bashed config map] - Super simple configMap generation from bash.
* [sed transformer] - Define your unstructured edits using a
plugin like this one.
* [hashicorp go-getter] - Download kustomize layes and build it to generate resources
A generator plugin accepts nothing on `stdin`, but emits
generated resources to `stdout`.
@@ -232,16 +246,27 @@ marshalled resources on `stdin` and capture
### Go plugins
Be sure to read [Go plugin caveats](goPluginCaveats.md).
[Go plugin]: https://golang.org/pkg/plugin/
A [Go plugin] for kustomize looks like this:
A `.go` file can be a [Go plugin] if it declares
'main' as it's package, and exports a symbol to
which useful functions are attached.
It can further be used as a _kustomize_ plugin if
the symbol is named 'KustomizePlugin' and the
attached functions implement the `Configurable`,
`Generator` and `Transformer` interfaces.
A Go plugin for kustomize looks like this:
> ```
> package main
>
> import (
> "sigs.k8s.io/kustomize/pkg/ifc"
> "sigs.k8s.io/kustomize/pkg/resmap"
> "sigs.k8s.io/kustomize/v3/pkg/ifc"
> "sigs.k8s.io/kustomize/v3/pkg/resmap"
> ...
> )
>
@@ -269,27 +294,28 @@ file to be added to the `generators` or
`transformers` field in the kustomization file.
Do one or the other or both as desired.
[secret generator]: ../plugin/someteam.example.com/v1/secretsfromdatabase
[service generator]: ../plugin/someteam.example.com/v1/someservicegenerator
[string prefixer]: ../plugin/someteam.example.com/v1/stringprefixer
[date prefixer]: ../plugin/someteam.example.com/v1/dateprefixer
[secret generator]: ../../plugin/someteam.example.com/v1/secretsfromdatabase
[service generator]: ../../plugin/someteam.example.com/v1/someservicegenerator
[string prefixer]: ../../plugin/someteam.example.com/v1/stringprefixer
[date prefixer]: ../../plugin/someteam.example.com/v1/dateprefixer
[sops encoded secrets]: https://github.com/monopole/sopsencodedsecrets
#### Examples
* [secret generator] - Generate secrets from a database.
* [service generator] - Generate a service from a name and port argument.
* [service generator] - generate a service from a name and port argument.
* [string prefixer] - uses the value in `metadata/name` as the prefix.
This particular example exists to show how a plugin can
transform the behavior of a plugin. See the
`TestTransformedTransformers` test in the `target` package.
* [date prefixer] - prefix the current date to resource names, a simple
example used to modify the string prefixer plugin just mentioned.
* All the builtin plugins [here](../plugin/builtin).
* [secret generator] - generate secrets from a toy database.
* [sops encoded secrets] - a more complex secret generator.
* [All the builtin plugins](../../plugin/builtin).
User authored plugins are
on the same footing as builtin operations.
A plugin can be both a generator and a
A Go plugin can be both a generator and a
transformer. The `Generate` method will run along
with all the other generators before the
`Transform` method runs.
@@ -306,43 +332,3 @@ go build -buildmode plugin \
-o $d/${kind}.so $d/${kind}.go
```
#### Caveats
Go plugins allow kustomize extensions that run
without the cost marshalling/unmarshalling all
resource data to/from a subprocess for each plugin
run.
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
Go plugins work as [defined][Go plugin], but fall
short of common notions associated with the word
_plugin_. Go plugin compilation creates an [ELF]
formatted `.so` file, which by definition has no
information about the provenance of the object
code. Skew between the compilation conditions
(versions of package dependencies, `GOOS`,
`GOARCH`) of the main program ELF and the plugin
ELF will cause plugin load failure.
Exec plugins also lack provenance, but won't
complain about compilation skew.
In either case, a sensible way to share a plugin
is as a tar file of source code, tests and
associated data, unpackable under
`$XDG_CONFIG_HOME/kustomize/plugin` (exactly where
one would develop a plugin).
[Go modules]: https://github.com/golang/go/wiki/Modules
In the case of a Go plugin, an end user accepting
a shared plugin must compile both kustomize and
the plugin. Tooling could be built to make Go
_plugin sharing_ easier, but this requires some
critical mass of _plugin authoring_, which in turn
is hampered by confusion around sharing.
[Go modules], once they are more widely adopted,
will solve one of the biggest plugin sharing
difficulties - ambiguous plugin vs host
dependencies.

683
docs/plugins/builtins.md Normal file
View File

@@ -0,0 +1,683 @@
<!--
TODO: Generate this file (or files) from
data in directories under plugin/builtin.
This file too hard to maintain distinctly
from what's going on in those directories.
We could expand pluginator to do this, since
it already scans the relevant files in the
relevant directory to generate the static
factory methods for plugins.
-->
# Builtin Plugins
A list of kustomize's builtin plugins (both
generators and transformers).
For each plugin, an example is given for
* implicitly triggering
the plugin via a dedicated kustomization
file field (e.g. the `AnnotationsTransformer` is
triggered by the `commonAnnotations` field).
* explicitly triggering the plugin
via the `generators` or `transformers` field
(by providing a config file specifying the
plugin).
The former method is convenient but limited in
power as most of the plugins arguments must
be defaulted. The latter method allows for
complete plugin argument specification.
[types.GeneratorOptions]: ../../pkg/types/generatoroptions.go
[types.SecretArgs]: ../../pkg/types/secretargs.go
[types.ConfigMapArgs]: ../../pkg/types/configmapargs.go
[config.FieldSpec]: ../../pkg/transformers/config/fieldspec.go
[types.ObjectMeta]: ../../pkg/types/objectmeta.go
[types.Selector]: ../../pkg/types/selector.go
[types.Replica]: ../../pkg/types/replica.go
[types.PatchStrategicMerge]: ../../pkg/types/patchstrategicmerge.go
[types.PatchTarget]: ../../pkg/types/patchtarget.go
[image.Image]: ../../pkg/image/image.go
## _AnnotationTransformer_
### Usage via `kustomization.yaml`
#### field name: `commonAnnotations`
Adds annotions (non-identifying metadata) to add
all resources. Like labels, these are key value
pairs.
```
commonAnnotations:
oncallPager: 800-555-1212
```
### Usage via plugin
#### Arguments
> Annotations map\[string\]string
>
> FieldSpecs \[\][config.FieldSpec]
#### Example
> ```
> apiVersion: builtin
> kind: AnnotationsTransformer
> metadata:
> name: not-important-to-example
> annotations:
> app: myApp
> greeting/morning: a string with blanks
> fieldSpecs:
> - path: metadata/annotations
> create: true
> ```
## _ConfigMapGenerator_
### Usage via `kustomization.yaml`
#### field name: `configMapGenerator`
Each entry in this list results in the creation of
one ConfigMap resource (it's a generator of n maps).
The example below creates two ConfigMaps. One with the
names and contents of the given files, the other with
key/value as data.
Each configMapGenerator item accepts a parameter of
`behavior: [create|replace|merge]`.
This allows an overlay to modify or
replace an existing configMap from the parent.
```
configMapGenerator:
- name: my-java-server-props
files:
- application.properties
- more.properties
- name: my-java-server-env-vars
literals:
- JAVA_HOME=/opt/java/jdk
- JAVA_TOOL_OPTIONS=-agentlib:hprof
```
It is also possible to
[define a key](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#define-the-key-to-use-when-creating-a-configmap-from-a-file)
to set a name different than the filename.
The example below creates a ConfigMap
with the name of file as `myFileName.ini`
while the _actual_ filename from which the
configmap is created is `whatever.ini`.
```
configMapGenerator:
- name: app-whatever
files:
- myFileName.ini=whatever.ini
```
### Usage via plugin
#### Arguments
> [types.GeneratorOptions]
>
> [types.ConfigMapArgs]
#### Example
> ```
> apiVersion: builtin
> kind: ConfigMapGenerator
> metadata:
> name: mymap
> envs:
> - devops.env
> - uxteam.env
> literals:
> - FRUIT=apple
> - VEGETABLE=carrot
> ```
## _ImageTagTransformer_
### Usage via `kustomization.yaml`
#### field name: `image`
Images modify the name, tags and/or digest for images
without creating patches. E.g. Given this
kubernetes Deployment fragment:
```
containers:
- name: mypostgresdb
image: postgres:8
- name: nginxapp
image: nginx:1.7.9
- name: myapp
image: my-demo-app:latest
- name: alpine-app
image: alpine:3.7
```
one can change the `image` in the following ways:
- `postgres:8` to `my-registry/my-postgres:v1`,
- nginx tag `1.7.9` to `1.8.0`,
- image name `my-demo-app` to `my-app`,
- alpine's tag `3.7` to a digest value
all with the following *kustomization*:
```
images:
- name: postgres
newName: my-registry/my-postgres
newTag: v1
- name: nginx
newTag: 1.8.0
- name: my-demo-app
newName: my-app
- name: alpine
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
```
### Usage via plugin
#### Arguments
> ImageTag [image.Image]
>
> FieldSpecs \[\][config.FieldSpec]
#### Example
> ```
> apiVersion: builtin
> kind: ImageTagTransformer
> metadata:
> name: not-important-to-example
> imageTag:
> name: nginx
> newTag: v2
> ```
## _LabelTransformer_
### Usage via `kustomization.yaml`
#### field name: `commonLabels`
Adds labels to all resources and selectors
```
commonLabels:
someName: someValue
owner: alice
app: bingo
```
### Usage via plugin
#### Arguments
> Labels map\[string\]string
>
> FieldSpecs \[\][config.FieldSpec]
#### Example
> ```
> apiVersion: builtin
> kind: LabelTransformer
> metadata:
> name: not-important-to-example
> labels:
> app: myApp
> env: production
> fieldSpecs:
> - path: metadata/labels
> create: true
> ```
## _NamespaceTransformer_
### Usage via `kustomization.yaml`
#### field name: `namespace`
Adds namespace to all resources
```
namespace: my-namespace
```
### Usage via plugin
#### Arguments
> [types.ObjectMeta]
>
> FieldSpecs \[\][config.FieldSpec]
#### Example
> ```
> apiVersion: builtin
> kind: NamespaceTransformer
> metadata:
> name: not-important-to-example
> namespace: test
> fieldSpecs:
> - path: metadata/namespace
> create: true
> - path: subjects
> kind: RoleBinding
> group: rbac.authorization.k8s.io
> - path: subjects
> kind: ClusterRoleBinding
> group: rbac.authorization.k8s.io
> ```
## _PatchesJson6902_
### Usage via `kustomization.yaml`
#### field name: `patchesJson6902`
Each entry in this list should resolve to
a kubernetes object and a JSON patch that will be applied
to the object.
The JSON patch is documented at https://tools.ietf.org/html/rfc6902
target field points to a kubernetes object within the same kustomization
by the object's group, version, kind, name and namespace.
path field is a relative file path of a JSON patch file.
The content in this patch file can be either in JSON format as
```
[
{"op": "add", "path": "/some/new/path", "value": "value"},
{"op": "replace", "path": "/some/existing/path", "value": "new value"}
]
```
or in YAML format as
```
- op: add
path: /some/new/path
value: value
- op: replace
path: /some/existing/path
value: new value
```
```
patchesJson6902:
- target:
version: v1
kind: Deployment
name: my-deployment
path: add_init_container.yaml
- target:
version: v1
kind: Service
name: my-service
path: add_service_annotation.yaml
```
The patch content can be an inline string as well:
```
patchesJson6902:
- target:
version: v1
kind: Deployment
name: my-deployment
patch: |-
- op: add
path: /some/new/path
value: value
- op: replace
path: /some/existing/path
value: "new value"
```
### Usage via plugin
#### Arguments
> Target [types.PatchTarget]
>
> Path string
>
> JsonOp string
#### Example
> ```
> apiVersion: builtin
> kind: PatchJson6902Transformer
> metadata:
> name: not-important-to-example
> target:
> group: apps
> version: v1
> kind: Deployment
> name: my-deploy
> path: jsonpatch.json
> ```
## _PatchesStrategicMerge_
### Usage via `kustomization.yaml`
#### field name: `patchesStrategicMerge`
Each entry in this list should be either a relative
file path or an inline content
resolving to a partial or complete resource
definition.
The names in these (possibly partial) resource
files must match names already loaded via the
`resources` field. These entries are used to
_patch_ (modify) the known resources.
Small patches that do one thing are best, e.g. modify
a memory request/limit, change an env var in a
ConfigMap, etc. Small patches are easy to review and
easy to mix together in overlays.
```
patchesStrategicMerge:
- service_port_8888.yaml
- deployment_increase_replicas.yaml
- deployment_increase_memory.yaml
```
The patch content can be a inline string as well.
```
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: nignx:latest
```
Note that kustomize does not support more than one patch
for the same object that contain a _delete_ directive. To remove
several fields / slice elements from an object create a single
patch that performs all the needed deletions.
### Usage via plugin
#### Arguments
> Paths \[\][types.PatchStrategicMerge]
>
> Patches string
#### Example
> ```
> apiVersion: builtin
> kind: PatchStrategicMergeTransformer
> metadata:
> name: not-important-to-example
> paths:
> - patch.yaml
> ```
## _PatchTransformer_
### Usage via `kustomization.yaml`
#### field name: `patches`
Each entry in this list should resolve to an Patch
object, which includes a patch and a target selector.
The patch can be either a strategic merge patch or a
JSON patch. it can be either a patch file or an inline
string. The target selects
resources by group, version, kind, name, namespace,
labelSelector and annotationSelector. A resource
which matches all the specified fields is selected
to apply the patch.
```
patches:
- path: patch.yaml
target:
group: apps
version: v1
kind: Deployment
name: deploy.*
labelSelector: "env=dev"
annotationSelector: "zone=west"
- patch: |-
- op: replace
path: /some/existing/path
value: new value
target:
kind: MyKind
labelSelector: "env=dev"
```
The `name` and `namespace` fields of the patch target selector are
automatically anchored regular expressions. This means that the value `myapp`
is equivalent to `^myapp$`.
### Usage via plugin
#### Arguments
> Path string
>
> Patch string
>
> Target \*[types.Selector]
#### Example
> ```
> apiVersion: builtin
> kind: PatchTransformer
> metadata:
> name: not-important-to-example
> patch: '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "nginx:latest"}]'
> target:
> name: .*Deploy
> kind: Deployment
> ```
## _PrefixSuffixTransformer_
### Usage via `kustomization.yaml`
#### field names: `namePrefix`, `nameSuffix`
Prepends or postfixes the value to the names
of all resources.
E.g. a deployment named `wordpress` could
become `alices-wordpress` or `wordpress-v2`
or `alices-wordpress-v2`.
```
namePrefix: alices-
nameSuffix: -v2
```
The suffix is appended before the content hash if
the resource type is ConfigMap or Secret.
### Usage via plugin
#### Arguments
> Prefix string
>
> Suffix string
>
> FieldSpecs \[\][config.FieldSpec]
#### Example
> ```
> apiVersion: builtin
> kind: PrefixSuffixTransformer
> metadata:
> name: not-important-to-example
> prefix: baked-
> suffix: -pie
> fieldSpecs:
> - path: metadata/name
> ```
## _ReplicaCountTransformer_
### Usage via `kustomization.yaml`
#### field name: `replicas`
Replicas modified the number of replicas for a resource.
E.g. Given this kubernetes Deployment fragment:
```
kind: Deployment
metadata:
name: deployment-name
spec:
replicas: 3
```
one can change the number of replicas to 5
by adding the following to your kustomization:
```
replicas:
- name: deployment-name
count: 5
```
This field accepts a list, so many resources can
be modified at the same time.
As this declaration does not take in a `kind:` nor a `group:`
it will match any `group` and `kind` that has a matching name and
that is one of:
- `Deployment`
- `ReplicationController`
- `ReplicaSet`
- `StatefulSet`
For more complex use cases, revert to using a patch.
### Usage via plugin
#### Arguments
> Replica [types.Replica]
>
> FieldSpecs \[\][config.FieldSpec]
#### Example
> ```
> apiVersion: builtin
> kind: ReplicaCountTransformer
> metadata:
> name: not-important-to-example
> replica:
> name: myapp
> count: 23
> fieldSpecs:
> - path: spec/replicas
> create: true
> kind: Deployment
> - path: spec/replicas
> create: true
> kind: ReplicationController
> ```
## _SecretGenerator_
### Usage via `kustomization.yaml`
#### field name: `secretGenerator`
Each entry in the argument list
results in the creation of
one Secret resource
(it's a generator of n secrets).
```
secretGenerator:
- name: app-tls
files:
- secret/tls.cert
- secret/tls.key
type: "kubernetes.io/tls"
- name: app-tls-namespaced
# you can define a namespace to generate
# a secret in, defaults to: "default"
namespace: apps
files:
- tls.crt=catsecret/tls.cert
- tls.key=secret/tls.key
type: "kubernetes.io/tls"
- name: env_file_secret
envs:
- env.txt
type: Opaque
```
### Usage via plugin
#### Arguments
> [types.ObjectMeta]
>
> [types.GeneratorOptions]
>
> [types.SecretArgs]
#### Example
> ```
> apiVersion: builtin
> kind: SecretGenerator
> metadata:
> name: my-secret
> namespace: whatever
> behavior: merge
> envs:
> - a.env
> - b.env
> files:
> - obscure=longsecret.txt
> literals:
> - FRUIT=apple
> - VEGETABLE=carrot
> ```

View File

@@ -0,0 +1,229 @@
# Exec plugin on linux in 60 seconds
This is a (no reading allowed!) 60 second copy/paste guided
example. Full plugin docs [here](README.md).
This demo writes and uses a somewhat ridiculous
_exec_ plugin (written in bash) that generates a
`ConfigMap`.
This is a guide to try it without damaging your
current setup.
#### requirements
* linux, git, curl, Go 1.12
## Make a place to work
```
DEMO=$(mktemp -d)
```
## Create a kustomization
Make a kustomization directory to
hold all your config:
```
MYAPP=$DEMO/myapp
mkdir -p $MYAPP
```
Make a deployment config:
```
cat <<'EOF' >$MYAPP/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: the-deployment
spec:
replicas: 3
template:
spec:
containers:
- name: the-container
image: monopole/hello:1
command: ["/hello",
"--port=8080",
"--date=$(THE_DATE)",
"--enableRiskyFeature=$(ENABLE_RISKY)"]
ports:
- containerPort: 8080
env:
- name: THE_DATE
valueFrom:
configMapKeyRef:
name: the-map
key: today
- name: ALT_GREETING
valueFrom:
configMapKeyRef:
name: the-map
key: altGreeting
- name: ENABLE_RISKY
valueFrom:
configMapKeyRef:
name: the-map
key: enableRisky
EOF
```
Make a service config:
```
cat <<EOF >$MYAPP/service.yaml
kind: Service
apiVersion: v1
metadata:
name: the-service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 8666
targetPort: 8080
EOF
```
Now make a config file for the plugin
you're about to write.
This config file is just another k8s resource
object. The values of its `apiVersion` and `kind`
fields are used to _find_ the plugin code on your
filesystem (more on this later).
```
cat <<'EOF' >$MYAPP/cmGenerator.yaml
apiVersion: myDevOpsTeam
kind: SillyConfigMapGenerator
metadata:
name: whatever
argsOneLiner: Bienvenue true
EOF
```
Finally, make a kustomization file
referencing all of the above:
```
cat <<EOF >$MYAPP/kustomization.yaml
commonLabels:
app: hello
resources:
- deployment.yaml
- service.yaml
generators:
- cmGenerator.yaml
EOF
```
Review the files
```
ls -C1 $MYAPP
```
## Make a home for plugins
Plugins must live in a particular place for
kustomize to find them.
This demo will use the ephemeral directory:
```
PLUGIN_ROOT=$DEMO/kustomize/plugin
```
The plugin config defined above in
`$MYAPP/cmGenerator.yaml` specifies:
> ```
> apiVersion: myDevOpsTeam
> kind: SillyConfigMapGenerator
> ```
This means the plugin must live in a directory
named:
```
MY_PLUGIN_DIR=$PLUGIN_ROOT/myDevOpsTeam/sillyconfigmapgenerator
mkdir -p $MY_PLUGIN_DIR
```
The directory name is the plugin config's
_apiVersion_ followed by its lower-cased _kind_.
A plugin gets its own directory to hold itself,
its tests and any supplemental data files it
might need.
## Create the plugin
There are two kinds of plugins, _exec_ and _Go_.
Make an _exec_ plugin, installing it to the
correct directory and file name. The file name
must match the plugin's _kind_ (in this case,
`SillyConfigMapGenerator`):
```
cat <<'EOF' >$MY_PLUGIN_DIR/SillyConfigMapGenerator
#!/bin/bash
# Skip the config file name argument.
shift
today=`date +%F`
echo "
kind: ConfigMap
apiVersion: v1
metadata:
name: the-map
data:
today: $today
altGreeting: "$1"
enableRisky: "$2"
"
EOF
```
By definition, an _exec_ plugin must be executable:
```
chmod a+x $MY_PLUGIN_DIR/SillyConfigMapGenerator
```
## Download kustomize 3.0.0
```
mkdir -p $DEMO/bin
gh=https://github.com/kubernetes-sigs/kustomize/releases/download
url=$gh/v3.0.0-pre/kustomize_3.0.0-pre_linux_amd64
curl -o $DEMO/bin/kustomize -L $url
chmod u+x $DEMO/bin/kustomize
```
## Review the layout
```
tree $DEMO
```
## Build your app, using the plugin:
```
XDG_CONFIG_HOME=$DEMO $DEMO/bin/kustomize build --enable_alpha_plugins $MYAPP
```
Above, if you had set
> ```
> PLUGIN_ROOT=$HOME/.config/kustomize/plugin
> ```
there would be no need to use `XDG_CONFIG_HOME` in the
_kustomize_ command above.

View File

@@ -0,0 +1,117 @@
[plugin package]: https://golang.org/pkg/plugin
[Go modules]: https://github.com/golang/go/wiki/Modules
[ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
[tensorflow plugin]: https://www.tensorflow.org/guide/extend/op
# Go plugin Caveats
A _Go plugin_ is a compilation artifact described
by the Go [plugin package]. It is built with
special flags and cannot run on its own.
It must be loaded into a running Go program.
> A normal program written in Go might be usable
> as _exec plugin_, but is not a _Go plugin_.
Go plugins allow kustomize extensions that run
without the cost marshalling/unmarshalling all
resource data to/from a subprocess for each plugin
run. The Go plugin API assures a certain level of
consistency to avoid confusing downstream
transformers.
Go plugins work as described in the [plugin
package], but fall short of common notions
associated with the word _plugin_.
## The skew problem
Go plugin compilation creates an [ELF] formatted
`.so` file, which by definition has no information
about the provenance of the object code.
Skew between the compilation conditions (versions
of package dependencies, `GOOS`, `GOARCH`) of the
main program ELF and the plugin ELF will cause
plugin load failure, with non-helpful error
messages.
Exec plugins also lack provenance, but won't fail
due to compilation skew.
In either case, the only sensible way to share a
plugin is as some kind of _bundle_ (a git repo
URL, a git archive file, a tar file, etc.)
containing source code, tests and associated data,
unpackable under
`$XDG_CONFIG_HOME/kustomize/plugin`.
In the case of a Go plugin, an _end user_
accepting a shared plugin _must compile both
kustomize and the plugin_.
This means a one-time run of
```
GOPATH=${whatever} go get \
sigs.k8s.io/kustomize/cmd/kustomize@${releaseVersion}
```
and then a normal development cycle using
```
go build -buildmode plugin \
-o ${wherever}/${kind}.so ${wherever}/${kind}.go
```
with paths and the release version tag (e.g. `v3.0.0`)
adjusted as needed.
For comparison, consider what one
must do to write a [tensorflow plugin].
## Why support Go plugins?
### Safety
The Go plugin developer sees the same API offered
to native kustomize operations, assuring certain
semantics, invariants, checks, etc. An exec
plugin sub-process dealing with this via
stdin/stdout will have an easier time screwing
things up for downstream transformers and
consumers.
Minor point: if the plugin reads files via
the kustomize-provided file `Loader` interface, it
will be constrained by kustomize file loading
restrictions. Of course, nothing but a code audit
prevents a Go plugin from importing the `io` package
and doing whatever it wants.
### Debugging
A Go plugin developer can debug the plugin _in
situ_, setting breakpoints inside the plugin and
elsewhere while running a plugin in feature tests.
To get the best of both worlds (shareability and safety),
a developer can write an `.go` program that functions
as an _exec plugin_, but can be processed by `go generate`
to emit a _Go plugin_ (or vice versa).
### Unit of contribution
All the builtin generators and transformers
are themselves Go plugins. This means that
the kustomize maintainers can promote a contributed
plugin to a builtin without needing code changes
(beyond those mandated by normal code review).
### Ecosystems grow through use
Tooling could ease Go plugin _sharing_, but this
requires some critical mass of Go plugin
_authoring_, which in turn is hampered by
confusion around sharing. [Go modules], once they
are more widely adopted, will solve the
biggest plugin sharing difficulty: ambiguous
plugin vs host dependencies.

View File

@@ -0,0 +1,367 @@
# Go Plugin Guided Example for Linux
[SopsEncodedSecrets repository]: https://github.com/monopole/sopsencodedsecrets
[Go plugin]: https://golang.org/pkg/plugin
[Go plugin caveats]: goPluginCaveats.md
This is a (no reading allowed!) 60 second copy/paste guided
example.
Full plugin docs [here](README.md).
Be sure to read the [Go plugin caveats].
This demo uses a Go plugin, `SopsEncodedSecrets`,
that lives in the [sopsencodedsecrets repository].
This is an inprocess [Go plugin], not an
sub-process exec plugin that happens to be written
in Go (which is another option for Go authors).
This is a guide to try it without damaging your
current setup.
#### requirements
* linux, git, curl, Go 1.12
For encryption
* gpg
Or
* Google cloud (gcloud) install
* a Google account with KMS permission
## Make a place to work
```shell
# Keeping these separate to avoid cluttering the DEMO dir.
DEMO=$(mktemp -d)
tmpGoPath=$(mktemp -d)
```
## Install kustomize
Need v3.0.0 for what follows, and you must _compile_
it (not download the binary from the release page):
```shell
GOPATH=$tmpGoPath go install sigs.k8s.io/kustomize/v3/cmd/kustomize
```
## Make a home for plugins
A kustomize plugin is fully determined by
its configuration file and source code.
[required fields]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields
Kustomize plugin configuration files are formatted
as kubernetes resource objects, meaning
`apiVersion`, `kind` and `metadata` are [required
fields] in these config files.
The kustomize program reads the config file
(because the config file name appears in the
`generators` or `transformers` field in the
kustomization file), then locates the Go plugin's
object code at the following location:
> ```shell
> $XDG_CONFIG_HOME/kustomize/plugin/$apiVersion/$lKind/$kind.so
> ```
where `lKind` holds the lowercased kind. The
plugin is then loaded and fed its config, and the
plugin's output becomes part of the overall
`kustomize build` process.
The same plugin might be used multiple times in
one kustomize build, but with different config
files. Also, kustomize might customize config
data before sending it to the plugin, for whatever
reason. For these reasons, kustomize owns the
mapping between plugins and config data; it's not
left to plugins to find their own config.
This demo will house the plugin it uses at the
ephemeral directory
```shell
PLUGIN_ROOT=$DEMO/kustomize/plugin
```
and ephemerally set `XDG_CONFIG_HOME` on a command
line below.
### What apiVersion and kind?
At this stage in the development of kustomize
plugins, plugin code doesn't know or care what
`apiVersion` or `kind` appears in the config file
sent to it.
The plugin could check these fields, but it's the
remaining fields that provide actual configuration
data, and at this point the successful parsing of
these other fields are the only thing that matters
to a plugin.
This demo uses a plugin called _SopsEncodedSecrets_,
and it lives in the [SopsEncodedSecrets repository].
Somewhat arbitrarily, we'll chose to install
this plugin with
```shell
apiVersion=mygenerators
kind=SopsEncodedSecrets
```
### Define the plugin's home dir
By convention, the ultimate home of the plugin
code and supplemental data, tests, documentation,
etc. is the lowercase form of its kind.
```shell
lKind=$(echo $kind | awk '{print tolower($0)}')
```
### Download the SopsEncodedSecrets plugin
In this case, the repo name matches the lowercase
kind already, so we just clone the repo and get
the proper directory name automatically:
```shell
mkdir -p $PLUGIN_ROOT/${apiVersion}
cd $PLUGIN_ROOT/${apiVersion}
git clone git@github.com:monopole/sopsencodedsecrets.git
```
Remember this directory:
```shell
MY_PLUGIN_DIR=$PLUGIN_ROOT/${apiVersion}/${lKind}
```
### Try the plugin's own test
Plugins may come with their own tests.
This one does, and it hopefully passes:
```shell
cd $MY_PLUGIN_DIR
go test SopsEncodedSecrets_test.go
```
Build the object code for use by kustomize:
```shell
cd $MY_PLUGIN_DIR
GOPATH=$tmpGoPath go build -buildmode plugin -o ${kind}.so ${kind}.go
```
This step may succeed, but kustomize might
ultimately fail to load the plugin because of
dependency [skew].
[skew]: https://github.com/kubernetes-sigs/kustomize/blob/master/docs/plugins/README.md#caveats
[used in this demo]: #install-kustomize
On load failure
* be sure to build the plugin with the same
version of Go (_go1.12_) on the same `$GOOS`
(_linux_) and `$GOARCH` (_amd64_) used to build
the kustomize being [used in this demo].
* change the plugin's dependencies in its `go.mod`
to match the versions used by kustomize (check
kustomize's `go.mod` used in its tagged commit).
Lacking tools and metadata to allow this to be
automated, there won't be a Go plugin ecosystem.
Kustomize has adopted a Go plugin architecture as
to ease accept new generators and transformers
(just write a plugin), and to be sure that native
operations (also constructed and tested as
plugins) are compartmentalized, orderable and
reusable instead of bizarrely woven throughout the
code as a individual special cases.
## Create a kustomization
Make a kustomization directory to
hold all your config:
```shell
MYAPP=$DEMO/myapp
mkdir -p $MYAPP
```
Make a config file for the SopsEncodedSecrets plugin.
Its `apiVersion` and `kind` allow the plugin to be
found:
```shell
cat <<EOF >$MYAPP/secGenerator.yaml
apiVersion: ${apiVersion}
kind: ${kind}
metadata:
name: mySecretGenerator
name: forbiddenValues
namespace: production
file: myEncryptedData.yaml
keys:
- ROCKET
- CAR
EOF
```
This plugin expects to find more data in
`myEncryptedData.yaml`; we'll get to that shortly.
Make a kustomization file referencing the plugin
config:
```shell
cat <<EOF >$MYAPP/kustomization.yaml
commonLabels:
app: hello
generators:
- secGenerator.yaml
EOF
```
Now generate the real encrypted data.
### Assure you have an encryption tool installed
We're going to use [sops](https://github.com/mozilla/sops) to encode a file. Choose either GPG or Google Cloud KMS as the secret provider to continue.
#### GPG
Try this:
```shell
gpg --list-keys
```
If it returns a list, presumably you've already created keys. If not, try import test keys from sops for dev.
```shell
curl https://raw.githubusercontent.com/mozilla/sops/master/pgp/sops_functional_tests_key.asc | gpg --import
SOPS_PGP_FP="1022470DE3F0BC54BC6AB62DE05550BC07FB1A0A"
```
#### Google Cloude KMS
Try this:
```shell
gcloud kms keys list --location global --keyring sops
```
If it succeeds, presumably you've already created keys and placed them in a keyring called sops. If not, do this:
```shell
gcloud kms keyrings create sops --location global
gcloud kms keys create sops-key --location global \
--keyring sops --purpose encryption
```
Extract your keyLocation for use below:
```shell
keyLocation=$(\
gcloud kms keys list --location global --keyring sops |\
grep GOOGLE | cut -d " " -f1)
echo $keyLocation
```
### Install `sops`
```shell
GOPATH=$tmpGoPath go install go.mozilla.org/sops/cmd/sops
```
### Create data encrypted with your private key
Create raw data to encrypt:
```shell
cat <<EOF >$MYAPP/myClearData.yaml
VEGETABLE: carrot
ROCKET: saturn-v
FRUIT: apple
CAR: dymaxion
EOF
```
Encrypt the data into file the plugin wants to read:
With PGP
```shell
$tmpGoPath/bin/sops --encrypt \
--pgp $SOPS_PGP_FP \
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
```
Or GCP KMS
```shell
$tmpGoPath/bin/sops --encrypt \
--gcp-kms $keyLocation \
$MYAPP/myClearData.yaml >$MYAPP/myEncryptedData.yaml
```
Review the files
```shell
tree $DEMO
```
This should look something like:
> ```shell
> /tmp/tmp.0kIE9VclPt
> ├── kustomize
> │   └── plugin
> │   └── mygenerators
> │   └── sopsencodedsecrets
> │   ├── go.mod
> │   ├── go.sum
> │   ├── LICENSE
> │   ├── README.md
> │   ├── SopsEncodedSecrets.go
> │   ├── SopsEncodedSecrets.so
> │   └── SopsEncodedSecrets_test.go
> └── myapp
> ├── kustomization.yaml
> ├── myClearData.yaml
> ├── myEncryptedData.yaml
> └── secGenerator.yaml
> ```
## Build your app, using the plugin:
```shell
XDG_CONFIG_HOME=$DEMO $tmpGoPath/bin/kustomize build --enable_alpha_plugins $MYAPP
```
This should emit a kubernetes secret, with
encrypted data for the names `ROCKET` and `CAR`.
Above, if you had set
> ```shell
> PLUGIN_ROOT=$HOME/.config/kustomize/plugin
> ```
there would be no need to use `XDG_CONFIG_HOME` in the
_kustomize_ command above.

View File

@@ -13,6 +13,6 @@ History
* Oct 2017: s/kexpand/kinflate/
* Sep 2017: kexpand [starts](https://github.com/kubernetes/kubectl/pull/65)
in [github.com/kubernetes/kubectl]
* Aug 2018: [DAM] authored by Brian Grant
* Aug 2017: [DAM] authored by Brian Grant
[DAM]: https://docs.google.com/document/d/1cLPGweVEYrVqQvBLJg6sxV-TrE5Rm2MNOBA_cxZP2WU

View File

@@ -1,6 +1,5 @@
# kustomize 2.1.0
[Go modules]: https://github.com/golang/go/wiki/Modules
[generator options]: ../examples/generatorOptions.md
[imgModules]: images/goModules.png
@@ -14,7 +13,7 @@
[bases]: glossary.md#base
[_base_]: glossary.md#base
[kustomize inventory object documentation]: inventory_object.md
[kustomize plugin documentation]: plugins.md
[kustomize plugin documentation]: plugins
[root]: glossary.md#kustomization-root
[transformer configs]: ../examples/transformerconfigs
[v1.0.9]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v1.0.9
@@ -26,6 +25,8 @@ Go modules, resource ordering respected, generator and transformer plugins, ease
loading restrictions, the notion of inventory, eased replica count modification.
About ~90 issues closed since [v2.0.3] in ~400 commits.
Download [here][v2.1.0].
## Go modules
![gopher with boxes][imgModules]
@@ -123,7 +124,7 @@ For more information, see the
The following usage:
```
kustomize build --load_restrictions none $target
kustomize build --load_restrictor none $target
```
allows a `kustomization.yaml` file used in this

242
docs/v2.1.0_changelog.md Normal file
View File

@@ -0,0 +1,242 @@
e1b59c93 2.1 release notes
2cf8371a Add --force flag to modify annotations and labels
0fa2d9c3 Add --reorder flag.
2d70526e Add ConfigMapGenerator and test.
4df57686 Add SedTransformerTest
68f6b0be Add Webhookconfiguration in default name references
1545e07d Add a plugin loader test.
449175e3 Add a sorting plugin.
aafc23a6 Add annotation transformer.
9bd456c6 Add bug report page.
0df58838 Add builtin JSON patch transformer
621bb7c6 Add builtin NameTransformer plugin.
45901219 Add builtin label transformer.
79906d73 Add builtin namespace transformer plugin
d9b0c4c8 Add copy method to VarSet
798b61c8 Add copy method to VarSet
d9259397 Add documentation for the replicas transform
2744e058 Add entry for inventory in fields.md
3f2acc90 Add faq
99391157 Add goplugin KV generator example.
3b8c5ee9 Add load_restrictor flag.
8f413f52 Add name reference of storageClass
5e054c9d Add originalName field to resource.
bb9b3163 Add script to run cloud build 'locally'.
ffc16d51 Add secret generator.
755dd3d0 Add some utilities.
c9d903cc Add support for escaping characters in Doc
2825888f Add test for builtin secretgenerator plugin.
e6c1b141 Add test for transformers/image custom config
644dc4b9 Add test showing shared patches disallowed.
96707645 Add test showing shared patches disallowed.
8d9897d5 Add the rmBuilder test helper.
000f81b2 Added test to verify usage of multiline strip chomp in configMapGenerator
5e7ddc86 Adds precommit for windows + documentation
5e33ac4a Allow nil label and annotaion
f38d0c69 Apply LimitRange resources before workloads
b28aaae6 Break a bad dep.
76d370a8 Chart last mile example
f621543d Cleanup kusttarget.
16fe7ced Cleanup plugin builds.
d4842ebd Cleanup the replica plugin implementation.
8991bcb3 Collect existing internal pkgs under one roof.
d0cf0473 Convert image transformer test to a more readable format
81c98c85 Convert inventory transformer to plugin, reduce k8sdeps.
c9a5c03e Convert legacy file based test to in-memory
2e71a3b8 Convert plugins to accept bytes instead of unstruct.
52faa01e Cover #1155 with a test.
fe67bcdb Cut more ties to k8sdeps
e1389649 Cut more ties to k8sdeps
175c754f Define a plugin compiler.
9a850710 Delete kustomizationerror.
6a106546 Delete the KV plugin code.
9a4cb6c9 Delete unused code.
cc531af6 Deprecate 'bases:' field.
939de0cd Dogfood the plugin framework.
267eec55 Fix 918
3a44508d Fix error message
0f571b91 Fix field names
9a4692e6 Fix function comments based on best practices from Effective Go
e207ae4c Fix incorrect default varrefs for CronJob volumeMounts
3d0e2907 Fix markdownlint warnings
31091a8d Fix missing varrefs for CronJob, Job, ReplicaSet
cefb64b6 Fix path
a9145702 Fix some comment nits.
7295a9b3 Fix some nits.
b92ee256 Fix some nits.
57eecd74 Fix test broken by the change in ordering.
e079c20c Fix typo
559efd64 Fix typo in namereference path for cronjobs initContainers.
a7a2589e Fix yaml in generator examples.
9b6f8f0c Format generated code.
2545ea10 Helm chart generator exec plugin
02f37953 Idiom fixes.
5000a2e5 Implement replica transformer as patch alternative
9c36ac28 Improve comments in name transform code.
58d9a510 Improve plugin doc.
529db049 Introduce envs field.
6d309b52 Introduce stacked transformers.
abf538d8 Keep backward compatibility for image transformer
7e12918f Keep var refernce in resources
7130e3ff Leave defautconfig empty for images
3e85c458 Load default config for image transformer
4162dbc2 Maintain resources in order loaded.
3a7c8a03 Make builtin the default pluginType
bcc7412e Make kusttestharness shareable.
cfb0c5ef Make plugin dir match Go conventions.
8d4b6452 Make the replica transformer `kind` aware.
3f8b1fe0 Make the replica transformer `kind` aware.
c470982c Make transformer configs array-aware
cd19d426 Merge remote-tracking branch 'upstream/master'
0b555e1b Modify tests to present expected data in unsorted order.
f17698a8 More release note tweaks.
9a12b551 Move accumulator code to its own package.
ee728d58 Move hashing code out of k8sdeps.
fd2248e7 Move hashing transformer out of k8sdeps.
d2c93065 Move kustomize main to cmd directory.
4bc31f4b Move pluginator to cmd directory.
5653ae69 One plugin per dir.
a09b42b3 Order ValidatingWebhookConfig last.
c63ebbdf Preserve order when merging.
11bb176a Push suffix/prefix code to plugin.
103c1b3a Put goplugins behind flag.
2796e545 Put windows test script next to pre-commit.sh
47c96548 Reduce k8ds deps
4f429d6b Reduce time required for cloning remote bases
b67d713b Remove dependency on ghodss/yaml
78cdff6d Remove kv plugins from docs.
3c58c9d1 Remove local load restrictions.
8767495b Remove some duped code.
b32e041b Remove some duped code.
8c133ef0 Removes mdrip testing for Windows
a2e4f6cf Rename ./bin dir to ./travis.
0e4f9acb Rename ErrorIfNotEqual to ErrorIfNotEqualSets
49d94f53 Rename the prefix/suffix transformer.
c06b9507 Secret/configmap factory cleanup.
3a01a63a Simplify code base.
76a31798 Simplify plugin loader code.
3a85fcd3 Simplify some of the plugin testing code.
3011f180 Sort default varReference config by kind, path
44ac9a9f Standalone ChartInflator plugin test.
5614649d Standalone service generator test
f311ba8d Support custom config for image transformer
e191ff53 Switch to vgo
a5660415 Tell homebrew to update.
ed03818e This commit enhances the UnstructAdapter
e0d2fa57 Translated kustomization.yaml into markdown in fields.md. Updated links to point to fields.md
a352ff39 True and false are mysterious.
7971ac1c Tweak secret docs.
852e7ed5 Typo Fix
1dd448e6 Update 2.1 release notes before release.
0f50be87 Update ChartInflatorExec
72fd31fd Update FAQ.md
185ae510 Update README.md
fa4dc14c Update all.go
ae0510f6 Update chartinflatorexecplugin_test.go
08b6f6f4 Update golinter to 1.17.1
4502e8ff Update inventory_object.md
ca478016 Update minecraft version in example.
efcf8757 Update order of resources to include psps
64bd0692 Update plugins.md
0045d7b7 Update plugins.md
54d1c557 Update plugins.md
86534869 Update remoteBuild.md
2ec8189c Update remoteBuild.md
1afc6c77 Update strategic-merge link
c1dea667 Update travis file.
9edecffc Update v_2.1.0.md
f2295acf Update v_2.1.0.md
71f44d64 Update v_2.1.0.md
bb69e9e7 Updates documentation for support and source
2490e605 Updates in image transformer (#911)
c6476d16 Upgrade version of minecraft used in tests.
af2b101f Use go modules in cloud builder.
5be42092 Vars should expand in ingress/spec/tls/secretName
9203478a Write individual files to output path if it is a directory
942e36e1 a few more changes
5b18c4de add ItemId type
6f4b104c add admission webhook types in the default cluster-scoped resource list
9fc4d388 add builtin envfiles plugin
a8465c95 add builtin files plugin
388d5c2d add builtin plugins
7fa02ce5 add document to explain inventory field (#997)
142879ec add example for transformer plugin
74937321 add generator plugins
deaf0779 add generators/transformers fields in kusotmization.yaml
ba43ecbc add goplugin for exec generators and transformers
d5abe39d add inventory package and refactor inventory transformer
f7cd44be add job initcontainer to varreference config
53a22cbe add note for availability in kubectl
2675bf4b add older release notes
ca6228b5 add remove resource subcommand
18f63282 add secret and configmap generator plugins
f6e01cfd add support for exec plugins
dd59eb38 add test case
c724cb71 add test for empty patch file
445f7392 add test for ensuirng the loader root is correctly passed
a8c476f7 add the Chhinese translation of docs list & install (#1022)
fb9e00bf add the unstructured to ENV of exec plugins
4f1a2350 add transformer plugins
237c54f4 add tutorial for custom images transformer
b4dbac1b add validation transformer
89243aed add zh dir
b07bea40 added field tables
3168b2a1 added link to examples
7a54d998 added links to section headings
e9a3f9f5 address comments
86f0f9a4 address comments
e5d730e1 address comments
ad7ca697 address comments
65886f12 address comments
1d65f24b adds documentation for choco package
e4159d94 allow to set image without a tag
b4fc1e43 change field name: prune -> inventory
404884e2 chinese helloworld doc
7b82154c correct spelling, minor word ordering
ca4aea17 doc/glossary updates for v2.1
3ff5c793 docs add kubectl command
c250f75d enable go module in the integration test (#1153)
3e6ee23a fix README
70def866 fix a link
e6d1de0d fix commonLabels spec for volumeClaimTemplates
4848987a fix configmap/secret name references for cronjobs with projected volumes
fa552d77 fix help msg for set image cmd
a64baed4 fix link
1bd7afe6 fix linter
822420e4 fix mergeFlags
bcb697eb fix namespace transformer for cluster-scoped resources
7765bdd9 fix some doc
a889f97f fix some example ptrs
56965a00 fix test
927b497f fix tests
61d46c26 fix the boilerplate copyright header (#1064)
03751372 fix the bug for patching CRDs
93908602 fix the bug for setting annotations when triggering transformers
21a0cba4 fix the regression of building remote url (#935)
7ab4d284 fix translation
62d3200e fix typo in namereference where serviceaccount name would not resolve
826affb8 generate configmap for pruning
cd9572e0 hey
5c471965 honor XDG_CONFIG_HOME
2aa7e30a minimize test
29cbec37 move parse helpers to util
fc8063f7 pass loader root to exec plugins
c1e2b27c pass resources to transformer plugin all together
e287f615 readded kustomization.yaml
f850ca63 remove extra comment
e17d3033 reorganize the examples layout
bfc3655b skip adding namespace when the object is empty
440d0361 some transformer plugins
61cf67fb start v2.1 release notes
403ede78 tests: demonstrate issue with JSON patch when base adds name prefix
b4efc833 translate example list
16924d79 translate kustomization.yaml
faaf6002 translate kustomization.yaml & update zh/README
540e4023 typo in README
748c88c2 update PruneString for resources
b60fca05 update edit add secrets/configmaps to use plugins
c836de5c update error msg
e4956c55 update examples/README.md
b2c87522 update validation transformer example text
d2103dbf updated grouping and added brief descriptions of sections

69
docs/v3.0.0.md Normal file
View File

@@ -0,0 +1,69 @@
# kustomize 3.0.0
This release is basically [v2.1.0](v2.1.0.md),
with many post-v2.1.0 bugs fixed (in about 150
commits) and a `v3` in Go package paths.
[plugin]: https://github.com/kubernetes-sigs/kustomize/tree/master/docs/plugins
The major version increment to `v3` puts a new
floor on a stable API for [plugin] developers
(both _Go_ plugin developers and _exec_ plugin
developers who happen to use Go).
### Why so soon after v2.1.0?
[semantic versioning]: https://semver.org
[Go modules doc]: https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher
[versioning policy]: versioningPolicy.md
We made a mistake - v2.1.0 should have been
v3.0.0. Per the [Go modules doc] (which have
improved a great deal recently), a release that's
already tagged v2 or higher should increment the
major version when performing their first Go
module-based release.
This advice applies to kustomize, since it was
already at major version 2 when it began using Go
modules to state _its own_ dependencies in v2.1.0.
But the more important reason for `v3` is a change
to the kustomize [versioning policy], forced by
the introduction of plugins.
Historically, kustomize's [versioning policy]
didn't involve Go modules and addressed _only_ the
command line tool's behavior and the fields in a
kustomization file. The underlying packages were
an implementation detail, not under semantic
versioning, because they weren't intended for
export (and should have all been under
`internal`). Thus although the v2.1.0 CLI is
backward compatible with v2.0.3, the underlying
package APIs are not.
[minimal version selection]: https://research.swtch.com/vgo-mvs
With Go modules, the `go` tool must assume that Go
packages respect [semantic versioning], so it can
perform [minimal version selection].
With the introduction of alpha plugins, kustomize
sub-packages - in particular `loader` and
`resmap` - become part of an API formally exposed
to plugin authors, and so must be semantically
versioned. This allows plugins defined in other
repositories to clarify that they depend on
kustomize v3.0.0, and not see confusing errors
arising from incompatibilities between v2.1.0 and
v2.0.3. Hence, the jump to v3.
Aside - the set of kustomize packages outside
`internal` is too large, and over time, informed
by package use, this API surface must shrink.
Such shrinkage will trigger a major version
increment.

127
docs/v3.1.0.md Normal file
View File

@@ -0,0 +1,127 @@
# kustomize 3.1.0
## Extended patches
Since this version, Kustomize allows applying one patch to multiple resources. This works for both Strategic Merge Patch and JSON Patch. Take a look at [patch multiple objects](../examples/patchMultipleObjects.md).
## Improved Resource Matching
Multiple improvements have been made to allow the user to leverage "namespace"
instead/or with "name suffix/prefix" to segregate resources.
### Patch resolution improvement
The following example demonstrates how using the namespace field in the patch definition,
will let the user define two different patches against two different Deployment having the
same "deploy1" name but in different namespaces in the same Kustomize context/folder.
Unless the `namespace:` field has been specified in the kustomization.yaml, no namespace
value will be handled as Kubernetes `default` namespace.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1
namespace: main
spec:
template:
spec:
containers:
- name: nginx
env:
- name: ANOTHERENV
value: TESTVALUE
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1
namespace: production
spec:
template:
spec:
containers:
- name: main
env:
- name: ANOTHERENV
value: PRODVALUE
```
### Variable resolution improvement
It is possible to add namespace field to the variable declaration. In the following example,
two `Service` objects with the same `elasticsearch` name have been declared.
Specifying the namespace in the objRef of the corresponding varriables, allows Kustomize to
resovlve thoses variables.
If the namespace is not specified, Kustomize will handle it has a "wildcard" value.
Extract of kustomization.yaml:
```yaml
vars:
- name: elasticsearch-test-protocol
objref:
kind: Service
name: elasticsearch
namespace: test
apiVersion: v1
fieldref:
fieldpath: spec.ports[0].protocol
- name: elasticsearch-dev-protocol
objref:
kind: Service
name: elasticsearch
namespace: dev
apiVersion: v1
fieldref:
fieldpath: spec.ports[0].protocol
```
### Simultaneous change of names and namespaces
Kustomize is now able to deal with simultaneous changes of name and namespace.
Special attention has been paid the handling of:
- ClusterRoleBinding/RoleBinding "subjects" field,
- ValidatingWebhookConfiguration "webhooks" field.
The user should be able to use a kustomization.yaml as shown in the example bellow
even if ClusterRoleBind,RoleBinding and ValidatingWebookConfiguration are part of the
resources he needs to declare.
Extract of kustomization.yaml:
```yaml
namePrefix: pfx-
nameSuffix: -sfx
namespace: testnamespace
resources:
...
```
### Resource and Kustomize Context matching.
Kustomize is now able to support more aggregation patterns.
If for instance, the top level of kustomization.yaml, is simply
combining sub-components, (as in the following example), Kustomize has improved
resource matching capabilities. This removes some of the constraints which were
present on the utilization of prefix/suffix and namespace transformers in the
individual components.
```yaml
resources:
- ../component1
- ../component2
- ../component3
```
## Other improvements
- Image transformation has been improved. This allows the user to update the sha256 of
an image with another sha256.
- Multiple default transformer configuration entries have been added, removing the need for the
user to add them as part of the `configurations:` section of the kustomization.yaml.
- `kustomize` help command has been tidied up.

View File

@@ -1,13 +1,15 @@
# Versioning
Running `kustomize` means one is running a
particular version of a program, reading a
particular version of a [kustomization] file.
particular version of a program, using a
particular version of underlying packages, and
reading a particular version of a [kustomization]
file.
## Program Versioning
The command `kustomize version` prints a three
field version tag (e.g. `1.0.11`) that aspires to
field version tag (e.g. `v3.0.0`) that aspires to
[semantic versioning].
When enough changes have accumulated to
@@ -15,6 +17,27 @@ warrant a new release, a [release process]
is followed, and the fields in the version
number are bumped per semver.
## Kustomize packages
At the time of writing, the kustomize program and
the packages it uses (and exports) are in the same
Go module (see the top level `go.mod` file in the
repo).
[trailing major version indicator]: https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher
Thus, they share the module's version number, per
its git tag (e.g. `v3.0.0`), whose major verion
number matches the [trailing major version
indicator] in the module name (e.g. the `/v3` in
`sigs.k8s.io/kustomize/v3`).
The non-internal packages in the Go module
`sigs.k8s.io/kustomize/v3`, introduced in
[v3.0.0](v3.0.0.md), conform to [semantic
versioning].
## Kustomization File Versioning
At the time of writing (circa release of v2.0.0):
@@ -209,11 +232,11 @@ moment forward.
[beta-level rules]: https://github.com/kubernetes/community/blob/master/contributors/devel/api_changes.md#alpha-beta-and-stable-versions
[changes]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md
[adapt]: https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/types/kustomization.go#L166
[special]: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#resources
[special]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#resources
[k8s API]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[conventions]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md
[release process]: ../build/README.md
[release process]: ../releasing/README.md
[kustomization]: glossary.md#kustomization
[`kind`]: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#types-kinds
[`kind`]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds
[`apiVersion`]: https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-versioning
[semantic versioning]: https://semver.org

View File

@@ -33,7 +33,7 @@ chmod u+x kustomize
使用 [Go] v1.10.1 或更高版本安装(如果可以访问 [golang.org]
<!-- @installkustomize @test -->
<!-- @installkustomize @testAgainstLatestRelease -->
```
go get sigs.k8s.io/kustomize
go install sigs.k8s.io/kustomize/v3/cmd/kustomize
```

View File

@@ -6,12 +6,11 @@
* [示例](../../examples) - 各种使用流程和概念的详细演示。
* [术语表](../glossary.md) - 用于消除术语歧义。
* [术语表](glossary.md) - 用于消除术语歧义。
* [kustomization.yaml](kustomization.yaml) - 包含
[kustomization](../glossary.md#kustomization) 所有字段的示例文件。
* [Kustomize 字段](fields.md) - 介绍 [kustomization](../glossary.md#kustomization) 文件中各字段的含义。
* [插件](../plugins.md) - 使用自定义的资源生成器和资源转换器来拓展 kustomize 功能。
* [插件](../plugins) - 使用自定义的资源生成器和资源转换器来拓展 kustomize 功能。
* [工作流](workflows.md) - 使用定制及使用现成配置使用的一些步骤。
@@ -20,16 +19,21 @@
## 发行说明
* [2.1](../v_2.1.0.md) - 日期待定,预计2019年5月下旬。
* [3.1](../v3.1.0.md) - 2019年7月下旬,扩展 patches 和改进的资源匹配
* [2.0](../v_2.0.0.md) - 2019年3月
* [3.0](../v3.0.0.md) - 2019年6月下旬插件开发者发布。
* [2.1](../v2.1.0.md) - 2019年6月18日
插件、有序资源等。
* [2.0](../v2.0.0.md) - 2019年3月
可以在 [kubectl v1.14][kubectl] 中使用 kustomize [v2.0.3] 。
* [1.0](../v_1.0.1.md) - 2018年5月
* [1.0](../v1.0.1.md) - 2018年5月
于 [kubectl repository] 开发后的首发版本。
## 政策
## 行为守则
* [版本控制](../versioningPolicy.md) - kustomize 代码及 kustomization 文件的版本控制策略。

436
docs/zh/fields.md Normal file
View File

@@ -0,0 +1,436 @@
# Kustomization 文件字段
介绍 [kustomization](../glossary.md#kustomization) 文件中各字段的含义。
## Resources
现有可定制对象。
| 字段 | 类型 | 说明 |
| --- | --- | --- |
|[resources](#resources) | list | 包含 k8s API 对象的文件,或其他包含 kustomizations 文件的目录。 |
|[CRDs](#crds)| list | CDR 文件,以允许在资源列表中指定自定义资源。 |
## Generators
生成可定制的对象。
| 字段 | 类型 | 说明 |
| --- | --- | --- |
|[configMapGenerator](#configmapgenerator)| list | 列表中的每个条目都将创建一个 ConfigMap 它是n个 ConfigMap 的生成器)。 |
|[secretGenerator](#secretgenerator)| list | 此列表中的每个条目都将创建一个 Secret 资源它是n个 secrets 的生成器)。 |
|[generatorOptions](#generatoroptions)| string | generatorOptions 可以修改所有 ConfigMapGenerator 和 SecretGenerator 的行为。 |
|[generators](#generators)| list | [插件](../plugins)配置文件。 |
## Transformers
可用的转换。
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| [commonLabels](#commonlabels) | string | 为所有资源和 selectors 增加 Labels 。 |
| [commonAnnotations](#commonannotations) | string | 为所有资源增加 Annotations 。 |
| [images](#images) | list | 修改镜像的名称、tag 或 image digest ,而无需使用 patches 。 |
| [inventory](#inventory) | struct | 用于生成一个包含清单信息的对象。 |
| [namespace](#namespace) | string | 为所有 resources 添加 namespace 。 |
| [namePrefix](#nameprefix) | string | 该字段的值将添加在所有资源的名称之前。 |
| [nameSuffix](#namesuffix) | string | 该字段的值将添加在所有资源的名称后面。 |
| [replicas](#replicas) | list | 修改资源的副本数。 |
| [patchesStrategicMerge](#patchesstrategicmerge) | list | 此列表中的每个条目都应可以解析为部分或完整的资源定义文件。 |
| [patchesJson6902](#patchesjson6902) | list | 列表中的每个条目都应可以解析为 kubernetes 对象和将应用于该对象的 JSON patch 。 |
| [transformers](#transformers) | list | [插件](../plugins)配置文件。 |
## Meta
[k8s metadata]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| [vars](#vars) | string | 获取一个对象中的字段并插入到另外的对象中。 |
| [apiVersion](#apiversion) | string | [k8s metadata] 字段。 |
| [kind](#kind) | string | [k8s metadata] 字段。 |
----
### apiVersion
该字段默认值为:
```
apiVersion: kustomize.config.k8s.io/v1beta1
```
### bases
`bases` 字段在 v2.1.0 中已被弃用。
该条目已被移动到 [resources](#resources) 字段中。
### commonLabels
为所有资源和 selectors 增加 Labels
```
commonLabels:
someName: someValue
owner: alice
app: bingo
```
### commonAnnotations
为所有资源增加 Annotations ,和 labels 一样是 key:value 的键值对。
```
commonAnnotations:
oncallPager: 800-555-1212
```
### configMapGenerator
列表中的每个条目都将创建一个 ConfigMap 它是n个 ConfigMap 的生成器)。
下面的示例创建了两个 ConfigMaps
- 一个具有给定文件的名称和内容
- 另一个包含 key/value 键值对数据
每个 configMapGenerator 项都可以使用 `behavior: [create|replace|merge]` 参数。
允许 overlay 从父级修改或替换现有的 configMap。
```
configMapGenerator:
- name: myJavaServerProps
files:
- application.properties
- more.properties
- name: myJavaServerEnvVars
literals:
- JAVA_HOME=/opt/java/jdk
- JAVA_TOOL_OPTIONS=-agentlib:hprof
```
### crds
此列表中的每个条目都应该是自定义资源定义CRD文件的相对路径。
该字段的存在是为了让 kustomize 知道用户自定义的 CRD ,并对这些类型中的对象应用适当的转换。
典型用例CRD 引用 ConfigMap 对象
在 kustomization 中ConfigMap 对象名称可能会通过 namePrefix 、nameSuffix 或 hashing 来更改 CRD 对象中此 ConfigMap 对象的名称,
引用时需要以相同的方式使用 namePrefix 、 nameSuffix 或 hashing 来进行更新。
Annotations 可以放入 openAPI 的定义中:
- "x-kubernetes-annotation": ""
- "x-kubernetes-label-selector": ""
- "x-kubernetes-identity": ""
- "x-kubernetes-object-ref-api-version": "v1",
- "x-kubernetes-object-ref-kind": "Secret",
- "x-kubernetes-object-ref-name-key": "name",
```
crds:
- crds/typeA.yaml
- crds/typeB.yaml
```
### generatorOptions
generatorOptions 修改所有 [ConfigMapGenerator](#configmapgenerator) 和 [SecretGenerator](#secretgenerator) 的行为。
```
generatorOptions:
# 为所有生成的资源添加 labels
labels:
kustomize.generated.resources: somevalue
# 为所有生成的资源添加 annotations
annotations:
kustomize.generated.resource: somevalue
# disableNameSuffixHash 为 true 时将禁止默认的在名称后添加哈希值后缀的行为
disableNameSuffixHash: true
```
### generators
[插件](../plugins)生成器配置文件列表。
```
generators:
- mySecretGeneratorPlugin.yaml
- myAppGeneratorPlugin.yaml
```
### images
修改镜像的名称、tag 或 image digest ,而无需使用 patches 。例如,对于这种 kubernetes Deployment 片段:
```
containers:
- name: mypostgresdb
image: postgres:8
- name: nginxapp
image: nginx:1.7.9
- name: myapp
image: my-demo-app:latest
- name: alpine-app
image: alpine:3.7
```
可以通过以下方式更改 `image`
- `postgres:8` to `my-registry/my-postgres:v1`,
- nginx tag `1.7.9` to `1.8.0`,
- image name `my-demo-app` to `my-app`,
- alpine's tag `3.7` to a digest value
可以在 *kustomization* 中添加以下内容:
```
images:
- name: postgres
newName: my-registry/my-postgres
newTag: v1
- name: nginx
newTag: 1.8.0
- name: my-demo-app
newName: my-app
- name: alpine
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
```
### inventory
详见 [inventory object](inventory_object.md)。
### kind
该字段默认值为:
```
kind: Kustomization
```
### namespace
为所有 resources 添加 namespace 。
```
namespace: my-namespace
```
### namePrefix
该字段的值将添加在所有资源的名称之前,例如 将资源名称 `wordpress` 变为 `alices-wordpress`
```
namePrefix: alices-
```
### nameSuffix
该字段的值将添加在所有资源的名称后面,例如 将资源名称 `wordpress` 变为 `wordpress-v2`
如果资源类型为 ConfigMap 或 Secret ,则在哈希值之前附加后缀。
```
nameSuffix: -v2
```
### patchesStrategicMerge
此列表中的每个条目都应可以解析为部分或完整的资源定义文件。
这些(也可能是部分的)资源文件中的 name 必须与已经通过 `resources` 加载的 name 字段匹配,或者通过 `bases` 中的 name 字段匹配。这些条目将用于 _patch_(修改)已知资源。
推荐使用小的 patches例如修改内存的 request/limit更改 ConfigMap 中的 env 变量等小的 patches 易于维护和查看,并且易于在 overlays 中混合使用。
```
patchesStrategicMerge:
- service_port_8888.yaml
- deployment_increase_replicas.yaml
- deployment_increase_memory.yaml
```
### patchesJson6902
patchesJson6902 列表中的每个条目都应可以解析为 kubernetes 对象和将应用于该对象的 JSON patch
JSON patch 的文档地址https://tools.ietf.org/html/rfc6902
目标字段指向的 kubernetes 对象的 group、 version、 kind、 name 和 namespace 在同一 kustomization 内 path 字段内容是 JSON patch 文件的相对路径。
patch 文件中的内容可以如下这种 JSON 格式:
```
[
{"op": "add", "path": "/some/new/path", "value": "value"},
{"op": "replace", "path": "/some/existing/path", "value": "new value"}
]
```
也可以使用 YAML 格式表示:
```
- op: add
path: /some/new/path
value: value
- op: replace
path: /some/existing/path
value: new value
```
```
patchesJson6902:
- target:
version: v1
kind: Deployment
name: my-deployment
path: add_init_container.yaml
- target:
version: v1
kind: Service
name: my-service
path: add_service_annotation.yaml
```
### replicas
修改资源的副本数。
例如:对于如下 kubernetes Deployment 片段:
```
kind: Deployment
metadata:
name: deployment-name
spec:
replicas: 3
```
在 kustomization 中添加以下内容将副本数更改为5
```
replicas:
- name: deployment-name
count: 5
```
该字段内容为列表,所以可以同时修改许多资源。
#### Limitation
由于这个声明无法设置 `kind:` 或 `group:` 它将匹配任何可以匹配名称的 `group` 和 `kind` ,并且它是以下之一:
- `Deployment`
- `ReplicationController`
- `ReplicaSet`
- `StatefulSet`
对于更复杂的用例,请使用 patch 。
### resources
该条目可以是指向本地目录的相对路径,也可以是指向远程仓库中的目录的 URL例如
```
resource:
- myNamespace.yaml
- sub-dir/some-deployment.yaml
- ../../commonbase
- github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6
- deployment.yaml
- github.com/kubernets-sigs/kustomize//examples/helloWorld?ref=test-branch
```
将以深度优先的顺序读取和处理资源。
文件应包含 YAML 格式的 k8s 资源。一个资源描述文件可以含有多个由(“---”)分隔的资源。
应该包含 `resources` 字段的 kustomization 文件的指定文件目录的相对路径。
[hashicorp URL]: https://github.com/hashicorp/go-getter#url-format
目录规范可以是相对、绝对或部分的 URL。URL 规范应遵循 [hashicorp URL] 格式。该目录必须包含 `kustomization.yaml` 文件。
### secretGenerator
此列表中的每个条目都将创建一个 Secret 资源它是n个 secrets 的生成器)。
```
secretGenerator:
- name: app-tls
files:
- secret/tls.cert
- secret/tls.key
type: "kubernetes.io/tls"
- name: app-tls-namespaced
# you can define a namespace to generate secret in, defaults to: "default"
namespace: apps
files:
- tls.crt=catsecret/tls.cert
- tls.key=secret/tls.key
type: "kubernetes.io/tls"
- name: env_file_secret
envs:
- env.txt
type: Opaque
```
### vars
Vars 用于从一个 resource 字段中获取文本,并将该文本插入指定位置 - 反射功能。
例如,假设需要在容器的 command 中指定了 Service 对象的名称,并在容器的 env 中指定了 Secret 对象的名称来确保以下内容可以正常工作:
```
containers:
- image: myimage
command: ["start", "--host", "$(MY_SERVICE_NAME)"]
env:
- name: SECRET_TOKEN
value: $(SOME_SECRET_NAME)
```
则可以在 `vars` 中添加如下内容:
```
vars:
- name: SOME_SECRET_NAME
objref:
kind: Secret
name: my-secret
apiVersion: v1
- name: MY_SERVICE_NAME
objref:
kind: Service
name: my-service
apiVersion: v1
fieldref:
fieldpath: metadata.name
- name: ANOTHER_DEPLOYMENTS_POD_RESTART_POLICY
objref:
kind: Deployment
name: my-deployment
apiVersion: apps/v1
fieldref:
fieldpath: spec.template.spec.restartPolicy
```
var 是包含该对象的变量名、对象引用和字段引用的元组。
字段引用是可选的,默认为 `metadata.name`,这是正常的默认值,因为 kustomize 用于生成或修改 resources 的名称。
在撰写本文档时,仅支持字符串类型字段,不支持 intsboolsarrays 等。例如在某些pod模板的容器编号2中提取镜像的名称是不可能的。
变量引用,即字符串 '$(FOO)' ,只能放在 kustomize 配置指定的特定对象的特定字段中。
关于 vars 的默认配置数据可以查看:
https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/config/defaultconfig/varreference.go
默认目标是所有容器 command args 和 env 字段。
Vars _不应该_ 被用于 kustomize 已经处理过的配置中插入 names 。
例如, Deployment 可以通过 name 引用 ConfigMap ,如果 kustomize 更改 ConfigMap 的名称,则知道更改 Deployment 中的引用的 name 。

308
docs/zh/glossary.md Normal file
View File

@@ -0,0 +1,308 @@
# 词汇表
[CRD spec]: https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/
[CRD]: #custom-resource-definition
[DAM]: #声明式应用程序管理
[Declarative Application Management]: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/architecture/declarative-application-management.md
[JSON]: https://www.json.org/
[JSONPatch]: https://tools.ietf.org/html/rfc6902
[JSONMergePatch]: https://tools.ietf.org/html/rfc7386
[Resource]: #resource
[YAML]: http://www.yaml.org/start.html
[application]: #application
[apply]: #apply
[apt]: https://en.wikipedia.org/wiki/APT_(Debian)
[base]: #base
[bases]: #base
[bespoke]: #bespoke-configuration
[gitops]: #gitops
[k8s]: #kubernetes
[kubernetes]: #kubernetes
[kustomize]: #kustomize
[kustomization]: #kustomization
[kustomizations]: #kustomization
[off-the-shelf]: #off-the-shelf-configuration
[overlay]: #overlay
[overlays]: #overlay
[patch]: #patch
[patches]: #patch
[patchJson6902]: #patchjson6902
[patchExampleJson6902]: https://github.com/kubernetes-sigs/kustomize/blob/master/examples/jsonpatch.md
[patchesJson6902]: #patchjson6902
[proposal]: https://github.com/kubernetes/community/pull/1629
[rebase]: https://git-scm.com/docs/git-rebase
[资源]: #资源
[resources]: #resource
[root]: #kustomization-root
[rpm]: https://en.wikipedia.org/wiki/Rpm_(software)
[strategic-merge]: https://git.k8s.io/community/contributors/devel/sig-api-machinery/strategic-merge-patch.md
[target]: #target
[transformer]: #transformer
[variant]: #variant
[variants]: #variant
[workflow]: workflows.md
## 应用
**应用**是为某种目的关联起来的一组 Kubernetes 资源,例如一个前有负载均衡器,后有数据库的 Web 服务器。用标签、命名和元数据将[资源]组织起来,可以进行**添加**或**删除**等操作。
有提案([Declarative Application Management])描述了一种称为应用的新的 Kubernetes 资源。更加正式的描述了这一思路,并在应用程序级别提供了运维和仪表盘的支持。
[Kustomize] 对 Kubernetes 资源进行配置,其中描述的应用程序资源只是另一种普通的资源。
## Apply
**Apply** 这个动词在 Kubernetes 的上下文中,指的是一个 Kubernetes 命令以及能够对集群施加影响的进程内 [API 端点](https://goo.gl/UbCRuf)。
用户可以将对集群的运行要求用一组完整的资源列表的形式进行表达,通过 **apply** 命令进行提交。
集群把新提交的资源和之前提交的状态以及当前的实际状态进行合并,形成新的状态。这就是 Kubernetes 的状态管理过程。
## Base
**Base** 指的是会被其它 [Kustomization] 引用的 [Kustomization]。
包括 [Overlay] 在内的任何 Kustomization都可以作为其它 Kustomization 的 Base。
Base 对引用自己的 Overlay 并无感知。
Base 和 [Overlay] 可以作为 Git 仓库中的唯一内容,用于简单的 [GitOps] 管理。对仓库的变更可以触发构建、测试以及部署过程。
## 定制配置
**定制**配置是由组织为满足自身需要,在内部创建和管理的 [Kustomization] 和[资源]。
和**定制配置**关联的 [Workflow] 比关联到通用配置的 [Workflow] 要简单一些,原因是通用配置是共享的,需要周期性的跟踪他人作出的变更。
## Custom resource definition
可以通过定制 CRD ([CRD spec]) 的方式对 Kubernetes API 进行扩展。
CRD 定义的[资源]是一种全新的资源,可以和 ConfigMap、Deployment 之类的原生资源以相同的方式来使用。
Kustomize 能够生成自定义资源,但是要完成这个目标,必须给出对应的 CRD这样才能正确的对这种结构进行处理。
## 声明式应用程序管理
Kustomize 鼓励对声明式应用程序管理([Declarative Application Management])的支持,这种方式是一系列 Kubernetes 集群管理的最佳实践。Kustomize 应该可以:
- 适用于任何配置,例如自有配置、共享配置、无状态、有状态等。
- 支持通用配置,以及创建变体(例如开发、预发布、生产)。
- 开放使用原生 Kubernetes API而不是隐藏它们。
- 不会给版本控制系统和集成的评审和审计工作造成困难。
- 用 Unix 的风格和其它工具进行协作。
- 避免使用模板、领域特定的语言等额外的学习内容。
## 生成器
生成器生成的资源,可以直接使用,也可以输出给转换器([Transformer])。
## GitOps
一种 DevOps 或者 CICD 流程,这种流程以 Git 作为唯一的事实,并且在这种事实发生变化时采取措施(例如构建、测试和部署)。
## Kustomization
**Kustomization** 这个词可以指 `kustomization.yaml` 这个文件,更常见的情况是一个包含了 `kustomization.yaml` 及其所有直接引用文件的相对路径(所有不需要 URL 的本地数据)。
也就是说,如果在 [Kustomize] 的上下文中说到 **Kustomization**,可能是以下的情况之一:
- 一个叫做 `kustomization.yaml` 的文件。
- 一个压缩包(包含 YAML 文件以及它的引用文件)。
- 一个 Git 压缩包。
- 一个 Git 仓库的 URL。
一个 Kustomization 文件包含的[字段](fields.md),分为四个类别:
- `resources`:待定制的现存[资源],示例字段:`resources``crds`
- `generator`:将要创建的**新**资源,示例字段:`configMapGenerator`(传统)、`secretGenerator`(传统)、`generators`v2.1
- `transformer`:对前面提到的新旧资源进行**处理**的方式。示例字段:`namePrefix``nameSuffix``images``commonLabels``patchesJson6902` 等。在 v2.1 中还有更多的 `transformer`
- `meta`:会对上面几种字段产生影响。示例字段:`vars``namespace``apiVersion``kind` 等。
## Kustomization root
直接包含 `kustomization.yaml` 文件的目录。
处理 Kustomization 文件时,可能访问到该目录以内或以外的文件。
像 YAML 资源这样的数据文件,或者用于生成 ConfigMap 或 Secret 的包含 `name=value` 的文本文件,或者用于补丁转换的补丁文件,必须**在这个目录的内部**,需要显式的使用**相对路径**来表达。
v2.1 中有一个特殊选项 `--load_restrictions none` 能够放宽这个限制,从而让不同的 Kustomization 可以共享补丁文件。
可以用 URL、绝对路径或者相对路径引用其它的 Kustomization包含 `kustomization.yaml` 文件的其它目录)。
如果 `A` Kustomization 依赖 `B` Kustomization那么
- `B` 不能包含 `A`
- `B` 不能依赖 `A`,间接依赖也不可以。
`A` 可以包含 `B`,但是这样的话,最简单的方式可能是让 `A` 直接依赖 `B` 的资源,并去除 `B``kustomization.yaml` 文件(就是把 `B` 合并到 `A`)。
通常情况下,`B``A` 处于同级目录,或者 `B` 放在一个完全独立的 Git 仓库里,可以从任意的 Kustomization 进行引用。
常见布局大致如下:
> ```
> ├── base
> │   ├── deployment.yaml
> │   ├── kustomization.yaml
> │   └── service.yaml
> └── overlays
> ├── dev
> │   ├── kustomization.yaml
> │   └── patch.yaml
> ├── prod
> │   ├── kustomization.yaml
> │   └── patch.yaml
> └── staging
> ├── kustomization.yaml
> └── patch.yaml
> ```
`dev``prod` 以及 `staging` 是否依赖于 `base`,要根据 `kustomization.yaml` 具体判断。
## Kubernetes
[Kubernetes](https://kubernetes.io) 是一个开源软件,为容器化应用提供了自动部署、伸缩和管理的能力。
它经常会被简写为 `k8s`
## Kubernetes 风格的对象
[必要字段]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields
用 YAML 或者 JSON 文件表达一个对象,其中包含一些[必要字段]。`kind` 字段用于标识对象类型,`metadata/name` 字段用于区分实例,`apiVersion` 表示的是版本(如果有多个版本的话)。
## Kustomize
`kustomize` 是一个面向 Kubernetes 的命令行工具,用一种无模板、结构化的的方式为为声明式配置提供定制支持。
`面向 Kubernetes` 的意思是 Kustomize 对 API 资源、Kubernetes 概念(例如名称、标签、命名空间等)、以及资源补丁是有支持的。
Kustomize 是 [DAM] 的一个实现。
## 通用配置
通用配置是一种用于共享的 Kustomization 以及资源。
例如创建一个这样的 Github 仓库:
> ```
> github.com/username/someapp/
> kustomization.yaml
> deployment.yaml
> configmap.yaml
> README.md
> ```
其他人可以 `fork` 这个仓库,并把它们的 Fork `clone` 到本地进行定制。
用户可以用这个克隆回来的版本作为 [Base],在此基础上定制 [Overlay] 来满足自身需求。
## Overlay
`Overlay` 是一个 依赖于其它 Kustomization 的 Kustomization。
Overlay 引用通过文件路径、URI 或者别的什么方式)的 [Kustomization] 被称为 [Base]。
Overlay 无法脱离 Base 独立生效。
Overlay 可以作为其它 Overlay 的 Base。
通常 Overlay 都是不止一个的,因为实际情况中就需要为单一 Base 创建不同的[变体],例如 `development``QA``production` 等。
总的说来,这些变体使用的资源基本是一致的,只有一些简单的差异,例如 Deployment 的实例数量、特定 Pod 的 CPU 资源、ConfigMap 中的数据源定义等。
可以这样把配置提交到集群:
> ```
> kustomize build someapp/overlays/staging |\
> kubectl apply -f -
>
> kustomize build someapp/overlays/production |\
> kubectl apply -f -
> ```
对 Base 的使用是隐性的——Overlay 的依赖是指向 Base 的。
请参考 [root]。
## 包
在 Kustomize 中,`包`是没有意义的Kustomize 并无意成为 [apt]、[rpm] 那样的传统包管理工具。
## Patch
修改资源的通用指令。
有两种功能类似但是实现不同的补丁方式:[strategic merge patch](#patchstrategicmerge) 和 [JSON patch](#patchjson6902)。
## patchStrategicMerge
`patchStrategicMerge` 是 [strategic-merge] 风格的补丁SMP
SMP 看上去像个不完整的 Kubernetes 资源 YAML。SMP 中包含 `TypeMeta` 字段,用于表明这个补丁的目标[资源]的 `group/version/kind/name`,剩余的字段是一个嵌套的结构,用于指定新的字段值,例如镜像标签。
缺省情况下SMP 会**替换**目标值。如果目标值是一个字符串,这种行为是合适的,但是如果目标值是个列表,可能就不合适了。
可以加入 `directive` 来修改这种行为,,可以接受的 `directive` 包括 `replace`(缺省)、`merge`(不替换列表)、`delete` 等([相关说明][strategic-merge])。
注意对自定义资源来说SMP 会被当作 [json merge patches][JSONMergePatch].
有趣的事实:所有的资源文件都可以当作 SMP 使用,相同 `group/version/kind/name` 资源中的匹配字段会被替换,其它内容则保持不变。
## patchJson6902
`patchJson6902` 引用一个 Kubernetes 资源,并用 [JSONPatch] 指定了修改这一资源的方法。
`patchJson6902` 几乎可以做到所有 `patchStrategicMerge` 的功能,但是语法更加简单,参考[示例][patchExampleJson6902]
## 插件
Kustomize 可以使用的一段代码,但是无需编译到 Kustomize 内部,可以作为 Kustomization 的一部分,生成或转换 Kubernetes 资源。
[插件](../plugins)的细节。
## 资源
在 REST-ful API 的上下文中,资源是 `GET``PUT` 或者 `POST` 等 HTTP 操作的目标。Kubernetes 提供了 REST-ful API 界面,用于和客户端进行交互。
在 Kustomization 的上下文中,资源是一个相对于 [root] 的相对路径,指向 [YAML] 或者 [JSON] 文件,描述了一个 Kubernetes API 对象,例如 Deployment 或者 ConfigMap或者一个 Kustomization、或者一个指向 Kustomization 的 URL。
或者说任何定义了[对象](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields)的格式正确的 YAML 文件,其中包含了 `kind``metadata/name` 字段,都是资源。
## Root
参看 [kustomization root][root].
## sub-target / sub-application / sub-package
不存在 `sub-xxx`,只有 [Base] 和 [Overlay]。
## Target
`target``kustomize build` 的参数,例如:
> ```
> kustomize build $target
> ```
`$target` 必须是一个指向 [Kustomization] 的路径或者 URL。
要创建用于进行 [Apply] 操作的资源,`target` 中必须包含或者引用所有相关信息。
[Base] 或者 [Overlay] 都可以作为 `target`
## Transformer
转换器能够修改资源,或者在 `kustomize build` 的过程中获取资源的信息。
## 变体
在集群中把 [Overlay] 应用到 [Base] 上的产物称为**变体**。
比如 `staging``production` 两个 Overlay都修改了同样的 Base来创建各自的变体。
`staging` 变体包含了一组用来保障测试过程的资源,或者一些想要看到生产环境下一个版本的外部用户。
`production` 变体用于承载生产流量,可能使用大量的副本,分配更多的 CPU 和内存。

View File

@@ -1,287 +0,0 @@
# Copyright 2018 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ----------------------------------------------------
# Example kustomization.yaml content.
#
# This file declares the customization provided by
# the kustomize program.
#
# Since customization is, by definition, _custom_,
# there are no sensible default values for the fields
# in this file.
#
# The field values used below are merely examples, not
# to be copied literally. The values won't work if
# they happen to be references to external files that
# don't exist.
#
# In practice, fields with no value should simply be
# omitted from kustomization.yaml to reduce the content
# visible in configuration reviews.
# ----------------------------------------------------
# Kustomization 的 apiVersion 和 kind
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# 为所有 resources 添加 namespace
namespace: my-namespace
# 该字段的值将添加在所有资源的名称之前
# 例如 将资源名称 “wordpress” 变为 “alices-wordpress”
namePrefix: alices-
# 该字段的值将添加在所有资源的名称后面
# 例如 将资源名称 “wordpress” 变为 “wordpress-v2”
# 如果资源类型为 ConfigMap 或 Secret ,则在哈希值之前附加后缀
nameSuffix: -v2
# 为所有资源和 selectors 增加 Labels
commonLabels:
someName: someValue
owner: alice
app: bingo
# 和 Labels 一样, 增加 Annotations
# 为 key:value 键值对
commonAnnotations:
oncallPager: 800-555-1212
# 此列表中的每条记录都必须是一个存在的 YAML 资源描述文件
# 一个 YAML 资源描述文件可以含有多个由(“---”)分隔的资源。
# kustomize 将读取这些YAML文件中的资源对其进行修改并
# 发布在 kustomize 的输出中。
#
# 此列表中的每个条目都应解析为包含 kustomization 文件的目录,否则定制将失败
#
# 该条目可以是指向本地目录的相对路径
# 也可以是指向远程仓库中的目录的 URL
# URL 应该遵循 hashicorp/go-getter 中的 URL 格式
# https://github.com/hashicorp/go-getter#url-format
#
# 此字段的存在意味着此文件(您正在阅读的文件)是 _overlay_
# 它将进一步定制这些来自 _bases_ 文件中的配置
#
# 典型用例:开发,演示和生产环境
# 这些环境大部分相同但有些关键方式存在差异(镜像标签,一些服务器参数等,与公共 base 不同的配置)
resources:
- some-service.yaml
- sub-dir/some-deployment.yaml
- ../../base
- github.com/kubernetes-sigs/kustomize/examples/multibases?ref=v1.0.6
- github.com/Liujingfang1/mysql
- github.com/Liujingfang1/kustomize/examples/helloWorld?ref=test-branch
# 列表中的每个条目都将创建一个 ConfigMap 它是n个 ConfigMap 的生成器)
# 下面的示例创建了两个 ConfigMaps
# 一个具有给定文件的名称和内容
# 另一个包含 key/value 键值对数据
# 每个 configMapGenerator 项都可以使用 [create | replace | merge] 参数
# 允许 overlay 从父级修改或替换现有的 configMap
configMapGenerator:
- name: myJavaServerProps
files:
- application.properties
- more.properties
- name: myJavaServerEnvVars
literals:
- JAVA_HOME=/opt/java/jdk
- JAVA_TOOL_OPTIONS=-agentlib:hprof
# 此列表中的每个条目都会导致创建一个Secret资源n个 secrets 的生成器)
secretGenerator:
- name: app-tls
files:
- secret/tls.cert
- secret/tls.key
type: "kubernetes.io/tls"
- name: app-tls-namespaced
# 你可以给生成的 secret 定义一个 namespace ,默认为 ”default“
namespace: apps
files:
- tls.crt=catsecret/tls.cert
- tls.key=secret/tls.key
type: "kubernetes.io/tls"
- name: env_file_secret
# 文件路径以 k=v 键值对的形式,每行一个键值对
envs:
- env.txt
type: Opaque
# generatorOptions 修改所有 ConfigMapGenerator 和 SecretGenerator 的行为
generatorOptions:
# 为所有生成的资源添加 labels
labels:
kustomize.generated.resources: somevalue
# 为所有生成的资源添加 annotations
annotations:
kustomize.generated.resource: somevalue
# disableNameSuffixHash 为 true 时将禁止默认的在名称后添加哈希值后缀的行为
disableNameSuffixHash: true
# 此列表中的每个条目都应可以解析为部分或完整的资源定义文件
#
# 这些(也可能是部分的)资源文件中的 name 必须与已经通过 `resources` 加载的 name 字段匹配
# 或者通过 `bases` 中的 name 字段匹配
# 这些条目将用于 _patch_修改已知资源
#
# 推荐使用小的 patches
# 例如:修改内存的 request/limit更改 ConfigMap 中的 env 变量等
# 小的 patches 易于维护和查看,并且易于在 overlays 中混合使用
patchesStrategicMerge:
- service_port_8888.yaml
- deployment_increase_replicas.yaml
- deployment_increase_memory.yaml
# patchesJson6902 列表中的每个条目都应可以解析为 kubernetes 对象和将应用于该对象的 JSON patch
# JSON patch 的文档地址https://tools.ietf.org/html/rfc6902
#
# 目标字段指向的 kubernetes 对象的 group、 version、 kind、 name 和 namespace 在同一 kustomization 内
# path 字段内容是 JSON patch 文件的相对路径
# patch 文件中的内容可以如下这种 JSON 格式:
#
# [
# {"op": "add", "path": "/some/new/path", "value": "value"},
# {"op": "replace", "path": "/some/existing/path", "value": "new value"}
# ]
#
# 也可以使用 YAML 格式表示:
#
# - op: add
# path: /some/new/path
# value: value
# - op:replace
# path: /some/existing/path
# value: new value
#
patchesJson6902:
- target:
version: v1
kind: Deployment
name: my-deployment
path: add_init_container.yaml
- target:
version: v1
kind: Service
name: my-service
path: add_service_annotation.yaml
# 此列表中的每个条目都应该是 openAPI 定义中自定义资源定义CRD文件的相对路径
#
# 该字段的存在是为了让 kustomize 知道用户自定义的 CRD
# 并对这些类型中的对象应用适当的转换
#
# 典型用例CRD 引用 ConfigMap 对象
# 在 kustomization 中ConfigMap 对象名称可能会通过 namePrefix 、nameSuffix 或 hashing 来更改 CRD 对象中此 ConfigMap 对象的名称
# 引用时需要以相同的方式使用 namePrefix 、 nameSuffix 或 hashing 来进行更新
#
# Annotations 可以放入 openAPI 的定义中:
# "x-kubernetes-annotation": ""
# "x-kubernetes-label-selector": ""
# "x-kubernetes-identity": ""
# "x-kubernetes-object-ref-api-version": "v1",
# "x-kubernetes-object-ref-kind": "Secret",
# "x-kubernetes-object-ref-name-key": "name",
crds:
- crds/typeA.json
- crds/typeB.json
# Vars 用于从一个 resource 字段中获取文本
# 并将该文本插入指定位置
#
# 例如,假设需要在容器的 command 中指定了 Service 对象的名称
# 并在容器的 env 中指定了 Secret 对象的名称
# 来确保以下内容可以正常工作:
# ```
# containers:
# - image: myimage
# command: ["start", "--host", "$(MY_SERVICE_NAME)"]
# env:
# - name: SECRET_TOKEN
# value: $(SOME_SECRET_NAME)
# ```
#
# 则可以在 `vars` 中添加如下内容:
#
vars:
- name: SOME_SECRET_NAME
objref:
kind: Secret
name: my-secret
apiVersion: v1
- name: MY_SERVICE_NAME
objref:
kind: Service
name: my-service
apiVersion: v1
fieldref:
fieldpath: metadata.name
- name: ANOTHER_DEPLOYMENTS_POD_RESTART_POLICY
objref:
kind: Deployment
name: my-deployment
apiVersion: apps/v1
fieldref:
fieldpath: spec.template.spec.restartPolicy
#
# var 是包含该对象的变量名、对象引用和字段引用的元组
#
# 字段引用是可选的,默认为 `metadata.name`
# 这是正常的默认值,因为 kustomize 用于生成或修改 resources 的名称
#
# 在撰写本文档时,仅支持字符串类型字段
# 不支持 intsboolsarrays 等
#
# 变量引用,即字符串 '$(FOO)' ,只能放在 kustomize 配置指定的特定对象的特定字段中
#
# 关于 vars 的默认配置数据可以查看:
# https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/config/defaultconfig/varreference.go
# 默认目标是所有容器 command args 和 env 字段
#
# Vars _不应该_ 被用于 kustomize 已经处理过的配置中插入 names
# 例如, Deployment 可以通过 name 引用 ConfigMap
# 如果 kustomize 更改 ConfigMap 的名称,则知道更改 Deployment 中的引用的 name
# 修改镜像的名称、tag 或 image digest ,而无需使用 patches
# 例如,对于这种 kubernetes Deployment 片段:
# ```
# containers:
# - name: mypostgresdb
# image: postgres:8
# - name: nginxapp
# image: nginx:1.7.9
# - name: myapp
# image: my-demo-app:latest
# - name: alpine-app
# image: alpine:3.7
#```
# 想对 `image` 完成以下修改:
#
# - 将 `postgres:8` 修改为 `my-registry/my-postgres:v1`,
# - 将 nginx 的 tag 从 `1.7.9` 修改为 `1.8.0`,
# - 将 镜像名称从 `my-demo-app` 修改为 `my-app`,
# - 将 alpine 的 tag 从 `3.7` 修改为 digest 值
#
# 可以在 *kustomization* 中添加以下内容:
images:
- name: postgres
newName: my-registry/my-postgres
newTag: v1
- name: nginx
newTag: 1.8.0
- name: my-demo-app
newName: my-app
- name: alpine
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3

View File

@@ -2,71 +2,71 @@ English | [简体中文](zh/README.md)
# Examples
These examples assume that `kustomize` is on your `$PATH`.
To run these examples, your `$PATH` must contain `kustomize`.
See the [installation instructions](../docs/INSTALL.md).
They are covered by [pre-commit](../travis/pre-commit.sh)
tests, and should work with HEAD
<!-- @installkustomize @test -->
```
go get sigs.k8s.io/kustomize/cmd/kustomize
```
These examples are [tested](../travis/pre-commit.sh)
to work with the latest _released_ version of kustomize.
Basic Usage
* [configGenerations](configGeneration.md) -
Rolling update when ConfigMapGenerator changes.
* [combineConfigs](combineConfigs.md) -
Mixing configuration data from different owners
(e.g. devops/SRE and developers).
* [generatorOptions](generatorOptions.md) -
Modifying behavior of all ConfigMap and Secret generators.
Modifying behavior of all ConfigMap and Secret generators.
* [vars](wordpress/README.md) - Injecting k8s runtime data into
container arguments (e.g. to point wordpress to a SQL service) by vars.
* [image names and tags](image.md) - Updating image names and tags without applying a patch.
* [remote target](remoteBuild.md) - Building a kustomization from a github URL
* [json patch](jsonpatch.md) - Apply a json patch in a kustomization
* [json patch](jsonpatch.md) - Apply a json patch in a kustomization
* [patch multiple objects](patchMultipleObjects.md) - Apply a patch to multiple objects
Advanced Usage
- generator plugins:
* [last mile helm](chart.md) - Make last mile modifications to
a helm chart.
* [secret generation](secretGeneratorPlugin.md) - Generating secrets from a plugin.
* [secret generation](secretGeneratorPlugin.md) - Generating secrets from a plugin.
* [remote sources](goGetterGeneratorPlugin.md) - Generating from remote sources.
- transformer plugins:
* [validation transformer](validationTransformer/README.md) -
validate resources through a transformer
- customize builtin transformer configurations
* [transformer configs](transformerconfigs/README.md) - Customize transformer configurations
Multi Variant Examples
* [hello world](helloWorld/README.md) - Deploy multiple
(differently configured) variants of a simple Hello
World server.
* [LDAP](ldap/README.md) - Deploy multiple
(differently configured) variants of a LDAP server.
* [springboot](springboot/README.md) - Create a Spring Boot
application production configuration from scratch.
* [mySql](mySql/README.md) - Create a MySQL production
configuration from scratch.
* [breakfast](breakfast.md) - Customize breakfast for
Alice and Bob.
* [multibases](multibases/README.md) - Composing three variants (dev, staging, production) with a common base.
* [multibases](multibases/README.md) - Composing three variants (dev, staging, production) with a common base.

View File

@@ -6,14 +6,14 @@
Define a place to work:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
Make a place to put the base breakfast configuration:
<!-- @baseDir @test -->
<!-- @baseDir @testAgainstLatestRelease -->
```
mkdir -p $DEMO_HOME/breakfast/base
```
@@ -21,7 +21,7 @@ mkdir -p $DEMO_HOME/breakfast/base
Make a `kustomization` to define what goes into
breakfast. This breakfast has coffee and pancakes:
<!-- @baseKustomization @test -->
<!-- @baseKustomization @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/breakfast/base/kustomization.yaml
resources:
@@ -34,7 +34,7 @@ Here's a _coffee_ type. Give it a `kind` and `metdata/name` field
to conform to [kubernetes API object style]; no other
file or definition is needed:
<!-- @coffee @test -->
<!-- @coffee @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/breakfast/base/coffee.yaml
kind: Coffee
@@ -50,7 +50,7 @@ The `name` field merely distinguishes this instance of
coffee from others (if there were any).
Likewise, define _pancakes_:
<!-- @pancakes @test -->
<!-- @pancakes @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/breakfast/base/pancakes.yaml
kind: Pancakes
@@ -64,7 +64,7 @@ EOF
Make a custom [variant] of breakfast for Alice, who
likes her coffee hot:
<!-- @aliceOverlay @test -->
<!-- @aliceOverlay @testAgainstLatestRelease -->
```
mkdir -p $DEMO_HOME/breakfast/overlays/alice
@@ -87,7 +87,7 @@ EOF
And likewise a [variant] for Bob, who wants _five_ pancakes, with strawberries:
<!-- @bobOverlay @test -->
<!-- @bobOverlay @testAgainstLatestRelease -->
```
mkdir -p $DEMO_HOME/breakfast/overlays/bob
@@ -111,14 +111,14 @@ EOF
One can now generate the configs for Alices breakfast:
<!-- @generateAlice @test -->
<!-- @generateAlice @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME/breakfast/overlays/alice
```
Likewise for Bob:
<!-- @generateBob @test -->
<!-- @generateBob @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME/breakfast/overlays/bob
```

View File

@@ -4,7 +4,7 @@
[stable chart]: https://github.com/helm/charts/tree/master/stable
[Helm charts]: https://github.com/helm/charts
[_minecraft_]: https://github.com/helm/charts/tree/master/stable/minecraft
[plugin]: ../docs/plugins.md
[plugin]: ../docs/plugins
[Helm charts] aren't natively read by kustomize, but
kustomize has a [plugin] system that allows one to

View File

@@ -128,7 +128,7 @@ defined in the [helloworld] demo.
It will all live in this work directory:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -139,7 +139,7 @@ DEMO_HOME=$(mktemp -d)
Make a place to put the base configuration:
<!-- @baseDir @test -->
<!-- @baseDir @testAgainstLatestRelease -->
```
mkdir -p $DEMO_HOME/base
```
@@ -150,7 +150,7 @@ environments. Here we're only defining a java
properties file, and a `kustomization` file that
references it.
<!-- @baseKustomization @test -->
<!-- @baseKustomization @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/base/common.properties
color=blue
@@ -171,14 +171,14 @@ EOF
Make an abbreviation for the parent of the overlay
directories:
<!-- @overlays @test -->
<!-- @overlays @testAgainstLatestRelease -->
```
OVERLAYS=$DEMO_HOME/overlays
```
Create the files that define the _development_ overlay:
<!-- @developmentFiles @test -->
<!-- @developmentFiles @testAgainstLatestRelease -->
```
mkdir -p $OVERLAYS/development
@@ -206,7 +206,7 @@ EOF
One can now generate the configMaps for development:
<!-- @runDev @test -->
<!-- @runDev @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/development
```
@@ -260,7 +260,7 @@ deletes unused configMaps.
Next, create the files for the _production_ overlay:
<!-- @productionFiles @test -->
<!-- @productionFiles @testAgainstLatestRelease -->
```
mkdir -p $OVERLAYS/production
@@ -287,7 +287,7 @@ EOF
One can now generate the configMaps for production:
<!-- @runProd @test -->
<!-- @runProd @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/production
```

View File

@@ -26,7 +26,7 @@ In this demo, the same [hello_world](helloWorld/README.md) is used while the Con
### Establish base and staging
Establish the base with a configMapGenerator
<!-- @establishBase @test -->
<!-- @establishBase @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
@@ -53,7 +53,7 @@ EOF
```
Establish the staging with a patch applied to the ConfigMap
<!-- @establishStaging @test -->
<!-- @establishStaging @testAgainstLatestRelease -->
```
OVERLAYS=$DEMO_HOME/overlays
mkdir -p $OVERLAYS/staging
@@ -91,7 +91,7 @@ configured with data from a configMap.
The deployment refers to this map by name:
<!-- @showDeployment @test -->
<!-- @showDeployment @testAgainstLatestRelease -->
```
grep -C 2 configMapKeyRef $BASE/deployment.yaml
```
@@ -117,7 +117,7 @@ collected](https://github.com/kubernetes-sigs/kustomize/issues/242).
The _staging_ [variant] here has a configMap [patch]:
<!-- @showMapPatch @test -->
<!-- @showMapPatch @testAgainstLatestRelease -->
```
cat $OVERLAYS/staging/map.yaml
```
@@ -128,7 +128,7 @@ resource spec.
The ConfigMap it modifies is declared from a configMapGenerator.
<!-- @showMapBase @test -->
<!-- @showMapBase @testAgainstLatestRelease -->
```
grep -C 4 configMapGenerator $BASE/kustomization.yaml
```
@@ -141,7 +141,7 @@ _not_ what gets used in the cluster. By design,
kustomize modifies names of ConfigMaps declared from ConfigMapGenerator. To see the names
ultimately used in the cluster, just run kustomize:
<!-- @grepStagingName @test -->
<!-- @grepStagingName @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging |\
grep -B 8 -A 1 staging-the-map
@@ -159,7 +159,7 @@ The suffix to the configMap name is generated from a
hash of the maps content - in this case the name suffix
is _k25m8k5k5m_:
<!-- @grepStagingHash @test -->
<!-- @grepStagingHash @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging | grep k25m8k5k5m
```
@@ -167,7 +167,7 @@ kustomize build $OVERLAYS/staging | grep k25m8k5k5m
Now modify the map patch, to change the greeting
the server will use:
<!-- @changeMap @test -->
<!-- @changeMap @testAgainstLatestRelease -->
```
sed -i.bak 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml
```
@@ -181,7 +181,7 @@ kustomize build $OVERLAYS/staging |\
Run kustomize again to see the new configMap names:
<!-- @grepStagingName @test -->
<!-- @grepStagingName @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging |\
grep -B 8 -A 1 staging-the-map
@@ -192,7 +192,7 @@ in three new names ending in _cd7kdh48fd_ - one in the
configMap name itself, and two in the deployment that
uses the map:
<!-- @countHashes @test -->
<!-- @countHashes @testAgainstLatestRelease -->
```
test 3 == \
$(kustomize build $OVERLAYS/staging | grep cd7kdh48fd | wc -l); \

View File

@@ -0,0 +1,333 @@
[builtin operations]: ../docs/plugins/builtins.md
[builtin plugins]: ../docs/plugins/builtins.md
[plugins]: ../docs/plugins
[plugin]: ../docs/plugins
[fields]: ../docs/fields.md
[fields in a kustomization file]: ../docs/fields.md
[TransformerConfig]: ../pkg/transformers/config/transformerconfig.go
[kustomization]: ../docs/glossary.md#kustomization
# Customizing kustomize
The [fields in a kustomization file] allow the user to
specify which resource files to use as input, how to
_generate_ new resources, and how to _transform_ those
resources - add labels, patch them, etc.
These fields are simple (low argument count) directives.
For example, the `commonAnnotations` field demands only a
list of _name:value_ pairs.
If using a field triggers behavior that pleases the user,
everyone's happy.
If not, the user can ask for new behavior to be implemented
in kustomize proper (and wait), or the user can write a
transformer or generator [plugin]. This latter option
generally means writing code; a Go plugin, a Go binary,
a C++ binary, a `bash` script - something.
There's a third option. If one merely wants to tweak
behavior that already exists in kustomize, one may be able
to do so by just writing some YAML.
## Configure the builtin plugins
All of kustomize's [builtin operations] are implemented
and usable as plugins.
Using the fields is convenient and brief, but necessarily
specifies only part of the entire plugin specification. The
unspecified part is defaulted to what are hopefully
generally appealing values.
If, instead, one invokes the plugins directly using the
`transformers` or `generators` field, one can (indeed
_must_) specify the entire plugin configuration.
## Example: field vs plugin
Define a place to work:
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
### Using the `commonLabels` and `commonAnnotations` fields
In this simple example, we'll use just two resources: a deployment and a service.
Define them:
<!-- @makeRes1 @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
spec:
replicas: 10
template:
spec:
containers:
- name: the-container
image: monopole/hello:1
EOF
```
<!-- @makeRes2 @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/service.yaml
apiVersion: v1
kind: Service
metadata:
name: service
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 8666
targetPort: 8080
EOF
```
Now make a kustomization file that causes them
to be read and transformed:
<!-- @makeKustomization @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/kustomization.yaml
namePrefix: hello-
commonLabels:
app: hello
commonAnnotations:
area: "51"
greeting: Take me to your leader
resources:
- deployment.yaml
- service.yaml
EOF
```
And run kustomize:
<!-- @checkLabel @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME
```
The output will be something like
> ```
> apiVersion: v1
> kind: Service
> metadata:
> annotations:
> area: "51"
> greeting: Take me to your leader
> labels:
> app: hello
> name: hello-service
> spec:
> ports:
> - port: 8666
> protocol: TCP
> targetPort: 8080
> selector:
> app: hello
> type: LoadBalancer
> ---
> apiVersion: apps/v1
> kind: Deployment
> metadata:
> annotations:
> area: "51"
> greeting: Take me to your leader
> labels:
> app: hello
> name: hello-deployment
> spec:
> replicas: 10
> selector:
> matchLabels:
> app: hello
> template:
> metadata:
> annotations:
> area: "51"
> greeting: Take me to your leader
> labels:
> app: hello
> spec:
> containers:
> - image: monopole/hello:1
> name: the-container
> ```
Let's say we are unhappy with this result.
We only want the annotations
to be applied down in the pod templates,
and don't want to see them in the metadata
for Service or Deployment.
We like that the label _app: hello_ ended up in
- Service `spec.selector`
- Deployment `spec.selector.matchLabels`
- Deployment `spec.template.metadata.labels`
as this binds the Service (load balancer) to the pods,
and the Deployment itself to its own pods -
but we again don't care to see these labels in
the metadata for the Service and the Deployment.
### Configuring the builtin plugins instead
To fine tune this, invoke the same transformations
using the plugin approach.
Change the kustomization file:
<!-- @makeKustomization @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/kustomization.yaml
namePrefix: hello-
transformers:
- myAnnotator.yaml
- myLabeller.yaml
resources:
- deployment.yaml
- service.yaml
EOF
```
Then make the two plugin configuration files
(`myAnnotator.yaml`, `myLabeller.yaml`)
referred to by the `transformers` field above.
For details about the fields to specify, see
the documentation for the [builtin plugins].
<!-- @makeAnnotatorPluginConfig @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/myAnnotator.yaml
apiVersion: builtin
kind: AnnotationsTransformer
metadata:
name: notImportantHere
annotations:
area: 51
greeting: take me to your leader
fieldSpecs:
- kind: Deployment
path: spec/template/metadata/annotations
create: true
EOF
```
<!-- @makeLabelPluginConfig @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/myLabeller.yaml
apiVersion: builtin
kind: LabelTransformer
metadata:
name: notImportantHere
labels:
app: hello
fieldSpecs:
- kind: Service
path: spec/selector
create: true
- kind: Deployment
path: spec/selector/matchLabels
create: true
- kind: Deployment
path: spec/template/metadata/labels
create: true
EOF
```
Finally, run kustomize again:
<!-- @checkLabel @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME
```
The output should resemble the following,
with fewer labels and annotations.
> ```
> apiVersion: v1
> kind: Service
> metadata:
> name: hello-service
> spec:
> ports:
> - port: 8666
> protocol: TCP
> targetPort: 8080
> selector:
> app: hello
> type: LoadBalancer
> ---
> apiVersion: apps/v1
> kind: Deployment
> metadata:
> name: hello-deployment
> spec:
> replicas: 10
> selector:
> matchLabels:
> app: hello
> template:
> metadata:
> annotations:
> area: "51"
> greeting: take me to your leader
> labels:
> app: hello
> spec:
> containers:
> - image: monopole/hello:1
> name: the-container
> ```
## The old way to do this
The original (and still functional) way to customize
kustomize is to specify a `configurations` field in the
kustomization file.
This field, normally omitted because it overrides hardcoded
data, accepts a list of file path arguments. The files, in
turn, specify which fields in which k8s objects should be
affected by particular builtin transformations. It's a
global configuration cutting across transformations, and
doesn't effect generators at all.
At `build` time, the configuration files are unmarshalled
into one instance of [TransformerConfig]. This
object, _plus_ the field values for `namePrefix`, etc. are
fed into the transformation code to build the output.
The best way to write these custom configuration files is to
generate the files from the hardcoded values built into
kustomize via these commands:
> ```
> mkdir /tmp/junk
> kustomize config save -d /tmp/junk
> ```
One can then edit those file or files, and specify the
resulting edited files in a `configurations:`
field in a kustomization file used in a `build`.
Using plugins _completely ignores_ both hard coded
tranformer configuration, and any configuration loaded by
the `configuration` field.

View File

@@ -13,7 +13,7 @@ DEMO_HOME=$(mktemp -d)
Create a kustomization and add a ConfigMap generator to it.
<!-- @createCMGenerator @test -->
<!-- @createCMGenerator @testAgainstLatestRelease -->
```
cat > $DEMO_HOME/kustomization.yaml << EOF
configMapGenerator:
@@ -25,7 +25,7 @@ EOF
```
Add following generatorOptions
<!-- @addGeneratorOptions @test -->
<!-- @addGeneratorOptions @testAgainstLatestRelease -->
```
cat >> $DEMO_HOME/kustomization.yaml << EOF
generatorOptions:
@@ -39,7 +39,7 @@ EOF
Run `kustomize build` and make sure that the generated ConfigMap
- doesn't have name suffix
<!-- @verify @test -->
<!-- @verify @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep "name: my-configmap$" | wc -l); \

View File

@@ -0,0 +1,132 @@
# Remote Sources
Kustomize supports building a [remote target], but the URLs are limited to common [Git repository specs].
To extend the supported format, Kustomize has a [plugin] system that allows one to integrate third-party tools such as [hashicorp/go-getter] to "download things from a string URL suing a variety of protocols", extract the content and generated resources as part of kustomize build.
[remote target]: https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md
[Git repository specs]: https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/git/repospec_test.go
[plugin]: ../docs/plugins
[hashicorp/go-getter]: https://github.com/hashicorp/go-getter
## Make a place to work
<!-- @makeWorkplace @test -->
```sh
DEMO_HOME=$(mktemp -d)
mkdir -p $DEMO_HOME/base
```
## Use a remote kustomize layer
Define a kustomization representing your _local_ variant (aka environment).
This could involve any number of kustomizations (see other examples), but in this case just add the name prefix `my-` to all resources:
<!-- @writeKustLocal @test -->
```sh
cat <<'EOF' >$DEMO_HOME/kustomization.yaml
namePrefix: my-
resources:
- base/
EOF
```
It refer a remote base defined as below:
<!-- @writeKustLocal @test -->
```sh
cat <<'EOF' >$DEMO_HOME/base/kustomization.yaml
generators:
- goGetter.yaml
EOF
```
The base refers to a generator configuration file called `goGetter.yaml`.
This file lets one specify the source URL, and other things like sub path in the package, defaulting to base directory, and command to run under the path, defaulting to `kustomize build`.
Create the config file `goGetter.yaml`, specifying the arbitrarily chosen name _example_:
<!-- @writeGeneratorConfig @test -->
```sh
cat <<'EOF' >$DEMO_HOME/base/goGetter.yaml
apiVersion: someteam.example.com/v1
kind: GoGetter
metadata:
name: example
url: github.com/kustless/kustomize-examples.git
EOF
```
Because this particular YAML file is listed in the `generators:` stanza of a kustomization file, it is treated as the binding between a generator plugin - identified by the _apiVersion_ and _kind_ fields - and other fields that configure the plugin.
Download the plugin to your `DEMO_HOME` and make it executable:
<!-- @installPlugin @test -->
```sh
plugin=plugin/someteam.example.com/v1/gogetter/GoGetter
curl -s --create-dirs -o \
"$DEMO_HOME/kustomize/$plugin" \
"https://raw.githubusercontent.com/\
kubernetes-sigs/kustomize/master/$plugin"
chmod a+x $DEMO_HOME/kustomize/$plugin
```
Define a helper function to run kustomize with the correct environment and flags for plugins:
<!-- @defineKustomizeIt @test -->
```sh
function kustomizeIt {
XDG_CONFIG_HOME=$DEMO_HOME \
kustomize build --enable_alpha_plugins \
$DEMO_HOME/$1
}
```
Finally, build the local variant. Notice that all
resource names now have the `my-` prefix:
<!-- @doLocal @test -->
```sh
clear
kustomizeIt
```
Compare local variant to remote base:
<!-- @doCompare @test-->
```sh
diff <(kustomizeIt) <(kustomizeIt base) | more
...
< name: my-remote-cm
---
> name: remote-cm
```
To see the unmodified but extracted sources, run kustomize on the base. Every invocation here is re-downloading and re-building the package.
<!-- @showBase @test -->
```sh
kustomizeIt base
```
## Use non-kustomize remote sources
Sometimes the remote sources does not include `kustomization.yaml`. To use that in the plugin, set command to override the default build.
<!-- @setCommand @test -->
```sh
echo "command: cat resources.yaml" >>$DEMO_HOME/base/goGetter.yaml
```
Finally, built it
<!-- @finalLocal @test -->
```sh
kustomizeIt
```
and observe the output includes raw `resources.yaml` instead of building result of remote `kustomization.yaml`.

View File

@@ -22,7 +22,7 @@ Steps:
First define a place to work:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -44,7 +44,7 @@ To keep this document shorter, the base resources are
off in a supplemental data directory rather than
declared here as HERE documents. Download them:
<!-- @downloadBase @test -->
<!-- @downloadBase @testAgainstLatestRelease -->
```
BASE=$DEMO_HOME/base
mkdir -p $BASE
@@ -57,7 +57,7 @@ curl -s -o "$BASE/#1.yaml" "https://raw.githubusercontent.com\
Look at the directory:
<!-- @runTree @test -->
<!-- @runTree @testAgainstLatestRelease -->
```
tree $DEMO_HOME
```
@@ -78,7 +78,7 @@ One could immediately apply these resources to a
cluster:
> ```
> kubectl apply -f $DEMO_HOME/base
> kubectl apply -k $DEMO_HOME/base
> ```
to instantiate the _hello_ service. `kubectl`
@@ -88,7 +88,7 @@ would only recognize the resource files.
The `base` directory has a [kustomization] file:
<!-- @showKustomization @test -->
<!-- @showKustomization @testAgainstLatestRelease -->
```
more $BASE/kustomization.yaml
```
@@ -96,7 +96,7 @@ more $BASE/kustomization.yaml
Optionally, run `kustomize` on the base to emit
customized resources to `stdout`:
<!-- @buildBase @test -->
<!-- @buildBase @testAgainstLatestRelease -->
```
kustomize build $BASE
```
@@ -106,14 +106,14 @@ kustomize build $BASE
A first customization step could be to change the _app
label_ applied to all resources:
<!-- @addLabel @test -->
<!-- @addLabel @testAgainstLatestRelease -->
```
sed -i.bak 's/app: hello/app: my-hello/' \
$BASE/kustomization.yaml
```
See the effect:
<!-- @checkLabel @test -->
<!-- @checkLabel @testAgainstLatestRelease -->
```
kustomize build $BASE | grep -C 3 app:
```
@@ -127,7 +127,7 @@ Create a _staging_ and _production_ [overlay]:
* Web server greetings from these cluster
[variants] will differ from each other.
<!-- @overlayDirectories @test -->
<!-- @overlayDirectories @testAgainstLatestRelease -->
```
OVERLAYS=$DEMO_HOME/overlays
mkdir -p $OVERLAYS/staging
@@ -139,7 +139,7 @@ mkdir -p $OVERLAYS/production
In the `staging` directory, make a kustomization
defining a new name prefix, and some different labels.
<!-- @makeStagingKustomization @test -->
<!-- @makeStagingKustomization @testAgainstLatestRelease -->
```
cat <<'EOF' >$OVERLAYS/staging/kustomization.yaml
namePrefix: staging-
@@ -162,7 +162,7 @@ greeting from _Good Morning!_ to _Have a pineapple!_
Also, enable the _risky_ flag.
<!-- @stagingMap @test -->
<!-- @stagingMap @testAgainstLatestRelease -->
```
cat <<EOF >$OVERLAYS/staging/map.yaml
apiVersion: v1
@@ -180,7 +180,7 @@ EOF
In the production directory, make a kustomization
with a different name prefix and labels.
<!-- @makeProductionKustomization @test -->
<!-- @makeProductionKustomization @testAgainstLatestRelease -->
```
cat <<EOF >$OVERLAYS/production/kustomization.yaml
namePrefix: production-
@@ -202,7 +202,7 @@ EOF
Make a production patch that increases the replica
count (because production takes more traffic).
<!-- @productionDeployment @test -->
<!-- @productionDeployment @testAgainstLatestRelease -->
```
cat <<EOF >$OVERLAYS/production/deployment.yaml
apiVersion: apps/v1
@@ -228,7 +228,7 @@ EOF
Review the directory structure and differences:
<!-- @listFiles @test -->
<!-- @listFiles @testAgainstLatestRelease -->
```
tree $DEMO_HOME
```
@@ -288,12 +288,12 @@ something like
The individual resource sets are:
<!-- @buildStaging @test -->
<!-- @buildStaging @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging
```
<!-- @buildProduction @test -->
<!-- @buildProduction @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/production
```

View File

@@ -3,14 +3,14 @@
Define a place to work:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
Make a `kustomization` containing a pod resource
<!-- @createKustomization @test -->
<!-- @createKustomization @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
@@ -20,7 +20,7 @@ EOF
Declare the pod resource
<!-- @createDeployment @test -->
<!-- @createDeployment @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/pod.yaml
apiVersion: v1
@@ -46,7 +46,7 @@ The image `busybox` and tag `1.29.0` can be changed by adding `images` in `kusto
Add `images`:
<!-- @addImages @test -->
<!-- @addImages @testAgainstLatestRelease -->
```
cd $DEMO_HOME
kustomize edit set image busybox=alpine:3.6
@@ -61,14 +61,14 @@ The following `images` will be added to `kustomization.yaml`:
> ```
Now build this `kustomization`
<!-- @kustomizeBuild @test -->
<!-- @kustomizeBuild @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME
```
Confirm that this replaces _both_ busybox images and tags for `alpine:3.6`:
<!-- @confirmImages @test -->
<!-- @confirmImages @testAgainstLatestRelease -->
```
test 2 = \
$(kustomize build $DEMO_HOME | grep alpine:3.6 | wc -l); \

265
examples/inlinePatch.md Normal file
View File

@@ -0,0 +1,265 @@
[Strategic Merge Patch]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
[JSON Patch]: https://tools.ietf.org/html/rfc6902
# Demo: Inline Patch
A kustomization file supports patching in three ways:
- patchesStrategicMerge: A list of patch files where each file is parsed as a [Stragetic Merge Patch].
- patchesJSON6902: A list of patches and associated targetes, where each file is parsed as a [JSON Patch] and can only be applied to one target resource.
- patches: A list of patches and their associated targets. The patch can be applied to multiple objects. It auto detects whether the patch is a [Strategic Merge Patch] or [JSON Patch].
Since 3.2.0, all three support inline patch, where the patch content is put inside the kustomization file as a single string. With this feature, no separate patch files need to be created.
Make a base kustomization containing a Deployment resource.
<!-- @createKustomization @test -->
```
DEMO_HOME=$(mktemp -d)
BASE=$DEMO_HOME/base
mkdir $BASE
cat <<EOF >$BASE/kustomization.yaml
resources:
- deployments.yaml
EOF
cat <<EOF >$BASE/deployments.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
metadata:
labels:
foo: bar
spec:
containers:
- name: nginx
image: nginx
args:
- one
- two
EOF
```
## Inline Patch for PatchesStrategicMerge
Create an overlay and add an inline patch in `patchesStrategicMerge` field to the kustomization file
to change the image from `nginx` to `nginx:latest`.
<!-- @addSMPatch @test -->
```
SMP_OVERLAY=$DEMO_HOME/smp
mkdir $SMP_OVERLAY
cat <<EOF >$SMP_OVERLAY/kustomization.yaml
resources:
- ../base
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- name: nginx
image: nginx:latest
EOF
```
Running `kustomize build $SMP_OVERLAY`, in the output confirm that image is updated successfully.
<!-- @confirmSMPatch @test -->
```
test 1 == \
$(kustomize build $SMP_OVERLAY | grep "image: nginx:latest" | wc -l); \
echo $?
```
The output is
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
metadata:
labels:
foo: bar
spec:
containers:
- name: nginx
image: nginx:latest
args:
- one
- two
```
`$patch: delete` and `$patch: replace` also work in the inline patch. Change the inline patch to delete the container `nginx`.
<!-- @addDeleteSMPatch @test -->
```
cat <<EOF >$SMP_OVERLAY/kustomization.yaml
resources:
- ../base
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- name: nginx
$patch: delete
EOF
```
Running `kustomize build $SMP_OVERLAY`, in the output confirm that the `nginx` container has been deleted.
<!-- @confirmDeleteSMPatch @test -->
```
test 0 == \
$(kustomize build $SMP_OVERLAY | grep "image: nginx" | wc -l); \
echo $?
```
The output is
```
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
metadata:
labels:
foo: bar
spec:
containers: []
```
## Inline Patch for PatchesJson6902
Create an overlay and add an inline patch in `patchesJSON6902` field to the kustomization file
to change the image from `nginx` to `nginx:latest`.
<!-- @addJSONPatch @test -->
```
JSON_OVERLAY=$DEMO_HOME/json
mkdir $JSON_OVERLAY
cat <<EOF >$JSON_OVERLAY/kustomization.yaml
resources:
- ../base
patchesJSON6902:
- target:
group: apps
version: v1
kind: Deployment
name: deploy
patch: |-
- op: replace
path: /spec/template/spec/containers/0/image
value: nginx:latest
EOF
```
Running `kustomize build $JSON_OVERLAY`, in the output confirm that image is updated successfully.
<!-- @confirmJSONPatch @test -->
```
test 1 == \
$(kustomize build $JSON_OVERLAY | grep "image: nginx:latest" | wc -l); \
echo $?
```
The output is
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
metadata:
labels:
foo: bar
spec:
containers:
- name: nginx
image: nginx:latest
args:
- one
- two
```
## Inline Patch for Patches
Create an overlay and add an inline patch in `patches` field to the kustomization file
to change the image from `nginx` to `nginx:latest`.
<!-- @addPatch @test -->
```
PATCH_OVERLAY=$DEMO_HOME/patch
mkdir $PATCH_OVERLAY
cat <<EOF > $PATCH_OVERLAY/kustomization.yaml
resources:
- ../base
patches:
- target:
kind: Deployment
name: deploy
patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
spec:
containers:
- name: nginx
image: nginx:latest
EOF
```
Running `kustomize build $PATCH_OVERLAY`, in the output confirm that image is updated successfully.
<!-- @confirmPatch @test -->
```
test 1 == \
$(kustomize build $PATCH_OVERLAY | grep "image: nginx:latest" | wc -l); \
echo $?
```
The output is
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy
spec:
template:
metadata:
labels:
foo: bar
spec:
containers:
- name: nginx
image: nginx:latest
args:
- one
- two
```

View File

@@ -28,7 +28,7 @@
# before running it.
#
# At time of writing, its 'call point' was in
# https://github.com/kubernetes/test-infra/blob/master/jobs/config.json
# https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes-sigs/kustomize/kustomize-config.yaml
function exitWith {
local msg=$1
@@ -53,7 +53,7 @@ function setUpEnv {
exitWith "Script must be run from $expectedRepo"
fi
GO111MODULE=on go install . || \
GO111MODULE=on go install ./cmd/kustomize || \
{ exitWith "Failed to install kustomize."; }
PATH=$GOPATH/bin:$PATH

View File

@@ -6,7 +6,7 @@ The example below modifies an `Ingress` object with such a patch.
Make a `kustomization` containing an ingress resource.
<!-- @createIngress @test -->
<!-- @createIngress @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
@@ -36,7 +36,7 @@ Declare a JSON patch file to update two fields of the Ingress object:
- change host from `foo.bar.com` to `foo.bar.io`
- change servicePort from `80` to `8080`
<!-- @addJsonPatch @test -->
<!-- @addJsonPatch @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/ingress_patch.json
[
@@ -48,7 +48,7 @@ EOF
You can also write the patch in YAML format. This example also shows the "add" operation:
<!-- @addYamlPatch @test -->
<!-- @addYamlPatch @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/ingress_patch.yaml
- op: replace
@@ -67,7 +67,7 @@ EOF
Apply the patch by adding _patchesJson6902_ field in kustomization.yaml
<!-- @applyJsonPatch @test -->
<!-- @applyJsonPatch @testAgainstLatestRelease -->
```
cat <<EOF >>$DEMO_HOME/kustomization.yaml
patchesJson6902:
@@ -81,14 +81,14 @@ EOF
```
Running `kustomize build $DEMO_HOME`, in the output confirm that host has been updated correctly.
<!-- @confirmHost @test -->
<!-- @confirmHost @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep "host: foo.bar.io" | wc -l); \
echo $?
```
Running `kustomize build $DEMO_HOME`, in the output confirm that the servicePort has been updated correctly.
<!-- @confirmServicePort @test -->
<!-- @confirmServicePort @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep "servicePort: 8080" | wc -l); \
@@ -97,7 +97,7 @@ test 1 == \
If the patch is YAML-formatted, it will be parsed correctly:
<!-- @applyYamlPatch @test -->
<!-- @applyYamlPatch @testAgainstLatestRelease -->
```
cat <<EOF >>$DEMO_HOME/kustomization.yaml
patchesJson6902:
@@ -110,7 +110,7 @@ patchesJson6902:
EOF
```
<!-- @confirmYamlPatch @test -->
<!-- @confirmYamlPatch @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep "path: /test" | wc -l); \

View File

@@ -18,7 +18,7 @@ Steps:
First define a place to work:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -38,7 +38,7 @@ To keep this document shorter, the base resources are
off in a supplemental data directory rather than
declared here as HERE documents. Download them:
<!-- @downloadBase @test -->
<!-- @downloadBase @testAgainstLatestRelease -->
```
BASE=$DEMO_HOME/base
mkdir -p $BASE
@@ -53,7 +53,7 @@ curl -s -o "$BASE/#1" "$CONTENT/base\
Look at the directory:
<!-- @runTree @test -->
<!-- @runTree @testAgainstLatestRelease -->
```
tree $DEMO_HOME
```
@@ -84,7 +84,7 @@ would only recognize the resource files.
The `base` directory has a [kustomization] file:
<!-- @showKustomization @test -->
<!-- @showKustomization @testAgainstLatestRelease -->
```
more $BASE/kustomization.yaml
```
@@ -92,7 +92,7 @@ more $BASE/kustomization.yaml
Optionally, run `kustomize` on the base to emit
customized resources to `stdout`:
<!-- @buildBase @test -->
<!-- @buildBase @testAgainstLatestRelease -->
```
kustomize build $BASE
```
@@ -101,14 +101,14 @@ kustomize build $BASE
A first customization step could be to set the name prefix to all resources:
<!-- @namePrefix @test -->
<!-- @namePrefix @testAgainstLatestRelease -->
```
cd $BASE
kustomize edit set nameprefix "my-"
```
See the effect:
<!-- @checkNameprefix @test -->
<!-- @checkNameprefix @testAgainstLatestRelease -->
```
kustomize build $BASE | grep -C 3 "my-"
```
@@ -121,7 +121,7 @@ Create a _staging_ and _production_ [overlay]:
* _Production_ has a higher replica count and a persistent disk.
* [variants] will differ from each other.
<!-- @overlayDirectories @test -->
<!-- @overlayDirectories @testAgainstLatestRelease -->
```
OVERLAYS=$DEMO_HOME/overlays
mkdir -p $OVERLAYS/staging
@@ -132,7 +132,7 @@ mkdir -p $OVERLAYS/production
Download the staging customization and patch.
<!-- @downloadStagingKustomization @test -->
<!-- @downloadStagingKustomization @testAgainstLatestRelease -->
```
curl -s -o "$OVERLAYS/staging/#1" "$CONTENT/overlays/staging\
/{config.env,deployment.yaml,kustomization.yaml}"
@@ -159,7 +159,7 @@ as well as 2 replica
#### Production Kustomization
Download the production customization and patch.
<!-- @downloadProductionKustomization @test -->
<!-- @downloadProductionKustomization @testAgainstLatestRelease -->
```
curl -s -o "$OVERLAYS/production/#1" "$CONTENT/overlays/production\
/{deployment.yaml,kustomization.yaml}"
@@ -196,7 +196,7 @@ The production customization adds 6 replica as well as a consistent disk.
Review the directory structure and differences:
<!-- @listFiles @test -->
<!-- @listFiles @testAgainstLatestRelease -->
```
tree $DEMO_HOME
```
@@ -258,12 +258,12 @@ The difference output should look something like
The individual resource sets are:
<!-- @buildStaging @test -->
<!-- @buildStaging @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging
```
<!-- @buildProduction @test -->
<!-- @buildProduction @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/production
```

View File

@@ -20,13 +20,13 @@ that is just a single pod.
Define a place to work:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
Define a common base:
<!-- @makeBase @test -->
<!-- @makeBase @testAgainstLatestRelease -->
```
BASE=$DEMO_HOME/base
mkdir $BASE
@@ -51,7 +51,7 @@ EOF
```
Define a dev variant overlaying base:
<!-- @makeDev @test -->
<!-- @makeDev @testAgainstLatestRelease -->
```
DEV=$DEMO_HOME/dev
mkdir $DEV
@@ -64,7 +64,7 @@ EOF
```
Define a staging variant overlaying base:
<!-- @makeStaging @test -->
<!-- @makeStaging @testAgainstLatestRelease -->
```
STAG=$DEMO_HOME/staging
mkdir $STAG
@@ -77,7 +77,7 @@ EOF
```
Define a production variant overlaying base:
<!-- @makeProd @test -->
<!-- @makeProd @testAgainstLatestRelease -->
```
PROD=$DEMO_HOME/production
mkdir $PROD
@@ -90,7 +90,7 @@ EOF
```
Then define a _Kustomization_ composing three variants together:
<!-- @makeTopLayer @test -->
<!-- @makeTopLayer @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
@@ -119,7 +119,7 @@ Now the workspace has following directories
Confirm that the `kustomize build` output contains three pod objects from dev, staging and production variants.
<!-- @confirmVariants @test -->
<!-- @confirmVariants @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep cluster-a-dev-myapp-pod | wc -l); \

View File

@@ -8,13 +8,13 @@ following demonstrates this using a base that's just one pod.
Define a place to work:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
Define a common base:
<!-- @makeBase @test -->
<!-- @makeBase @testAgainstLatestRelease -->
```
BASE=$DEMO_HOME/base
mkdir $BASE
@@ -39,7 +39,7 @@ EOF
```
Define a variant in namespace-a overlaying base:
<!-- @makeNamespaceA @test -->
<!-- @makeNamespaceA @testAgainstLatestRelease -->
```
NSA=$DEMO_HOME/namespace-a
mkdir $NSA
@@ -60,7 +60,7 @@ EOF
```
Define a variant in namespace-b overlaying base:
<!-- @makeNamespaceB @test -->
<!-- @makeNamespaceB @testAgainstLatestRelease -->
```
NSB=$DEMO_HOME/namespace-b
mkdir $NSB
@@ -81,7 +81,7 @@ EOF
```
Then define a _Kustomization_ composing two variants together:
<!-- @makeTopLayer @test -->
<!-- @makeTopLayer @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
@@ -107,7 +107,7 @@ Now the workspace has following directories
Confirm that the `kustomize build` output contains two pod objects from namespace-a and namespace-b.
<!-- @confirmVariants @test -->
<!-- @confirmVariants @testAgainstLatestRelease -->
```
test 2 == \
$(kustomize build $DEMO_HOME| grep -B 4 "namespace: namespace-[ab]" | grep "name: myapp-pod" | wc -l); \

View File

@@ -11,7 +11,7 @@ In the production environment we want:
- MySQL to use persistent disk for storing data.
First make a place to work:
<!-- @makeDemoHome @test -->
<!-- @makeDemoHome @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -25,7 +25,7 @@ as HERE documents.
Download them:
<!-- @downloadResources @test -->
<!-- @downloadResources @testAgainstLatestRelease -->
```
curl -s -o "$DEMO_HOME/#1.yaml" "https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
@@ -40,14 +40,14 @@ a file called `kustomization.yaml`.
Start this file:
<!-- @kustomizeYaml @test -->
<!-- @kustomizeYaml @testAgainstLatestRelease -->
```
touch $DEMO_HOME/kustomization.yaml
```
### Add the resources
<!-- @addResources @test -->
<!-- @addResources @testAgainstLatestRelease -->
```
cd $DEMO_HOME
@@ -73,7 +73,7 @@ Arrange for the MySQL resources to begin with prefix
_prod-_ (since they are meant for the _production_
environment):
<!-- @customizeLabel @test -->
<!-- @customizeLabel @testAgainstLatestRelease -->
```
cd $DEMO_HOME
@@ -91,7 +91,7 @@ cat kustomization.yaml
This `namePrefix` directive adds _prod-_ to all
resource names.
<!-- @genNamePrefixConfig @test -->
<!-- @genNamePrefixConfig @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME
```
@@ -134,7 +134,7 @@ selector.
`kustomize` does not have `edit set label` command to add
a label, but one can always edit `kustomization.yaml` directly:
<!-- @customizeLabels @test -->
<!-- @customizeLabels @testAgainstLatestRelease -->
```
sed -i.bak 's/app: helloworld/app: prod/' \
$DEMO_HOME/kustomization.yaml
@@ -153,7 +153,7 @@ environment. So we want to use Persistent Disk in
production. kustomize lets you apply `patchesStrategicMerge` to the
resources.
<!-- @createPatchFile @test -->
<!-- @createPatchFile @testAgainstLatestRelease -->
```
cat <<'EOF' > $DEMO_HOME/persistent-disk.yaml
apiVersion: apps/v1beta2 # for versions before 1.9.0 use apps/v1beta2
@@ -173,7 +173,7 @@ EOF
Add the patch file to `kustomization.yaml`:
<!-- @specifyPatch @test -->
<!-- @specifyPatch @testAgainstLatestRelease -->
```
cat <<'EOF' >> $DEMO_HOME/kustomization.yaml
patchesStrategicMerge:
@@ -199,7 +199,7 @@ The output of the following command can now be applied
to the cluster (i.e. piped to `kubectl apply`) to
create the production environment.
<!-- @finalInflation @test -->
<!-- @finalInflation @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME # | kubectl apply -f -
```

View File

@@ -0,0 +1,188 @@
[Strategic Merge Patch]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
[JSON patches]: https://tools.ietf.org/html/rfc6902
[label selector]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
# Demo: applying a patch to multiple resources
A kustomization file supports customizing resources via both
[Strategic Merge Patch] and [JSON patches]. Now one patch can be
applied to multiple resources.
This can be done by specifying a patch and a target selector as follows:
```
patches:
- path: <PatchFile>
target:
group: <Group>
version: <Version>
kind: <Kind>
name: <Name>
namespace: <Namespace>
labelSelector: <LabelSelector>
annotationSelector: <AnnotationSelector>
```
Both `labelSelector` and `annotationSelector` should follow the convention in [label selector].
Kustomize selects the targets which match all the fields in `target` to apply the patch.
The example below shows how to inject a sidecar container for all deployment resources.
Make a `kustomization` containing a Deployment resource.
<!-- @createDeployment @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
- deployments.yaml
EOF
cat <<EOF >$DEMO_HOME/deployments.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- name: nginx
image: nginx
args:
- one
- two
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
metadata:
labels:
key: value
spec:
containers:
- name: busybox
image: busybox
EOF
```
Declare a Strategic Merge Patch file to inject a sidecar container:
<!-- @addPatch @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: not-important
spec:
template:
spec:
containers:
- name: istio-proxy
image: docker.io/istio/proxyv2
args:
- proxy
- sidecar
EOF
```
Apply the patch by adding _patches_ field in kustomization.yaml
<!-- @applyPatch @testAgainstLatestRelease -->
```
cat <<EOF >>$DEMO_HOME/kustomization.yaml
patches:
- path: patch.yaml
target:
kind: Deployment
EOF
```
Running `kustomize build $DEMO_HOME`, in the output confirm that both Deployment resources are patched correctly.
<!-- @confirmPatch @testAgainstLatestRelease -->
```
test 2 == \
$(kustomize build $DEMO_HOME | grep "image: docker.io/istio/proxyv2" | wc -l); \
echo $?
```
The output is as follows:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- args:
- proxy
- sidecar
image: docker.io/istio/proxyv2
name: istio-proxy
- args:
- one
- two
image: nginx
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
metadata:
labels:
key: value
spec:
containers:
- args:
- proxy
- sidecar
image: docker.io/istio/proxyv2
name: istio-proxy
- image: busybox
name: busybox
```
## Target selector
- Select resources with name matching `name*`
```yaml
target:
name: name*
```
- Select all Deployment resources
```yaml
target:
kind: Deployment
```
- Select resources matching label `app=hello`
```yaml
target:
labelSelector: app=hello
```
- Select resources matching annotation `app=hello`
```yaml
target:
annotationSelector: app=hello
```
- Select all Deployment resources matching label `app=hello`
```yaml
target:
kind: Deployment
labelSelector: app=hello
```

View File

@@ -11,7 +11,7 @@ To try this immediately, run a build against the kustomization
in the [multibases](multibases/README.md) example. There's
one pod in the output:
<!-- @remoteOverlayBuild @test -->
<!-- @remoteOverlayBuild @testAgainstLatestRelease -->
```
target="github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6"
@@ -24,7 +24,7 @@ Run against the overlay in that example to get three pods
(the overlay combines the dev, staging and prod bases for
someone who wants to send them all at the same time):
<!-- @remoteBuild @test -->
<!-- @remoteBuild @testAgainstLatestRelease -->
```
target="https://github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6"
test 3 == \
@@ -34,7 +34,7 @@ test 3 == \
A base can be a URL:
<!-- @createOverlay @test -->
<!-- @createOverlay @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
@@ -48,7 +48,7 @@ EOF
Build this to confirm that all three pods from the base
have the `remote-` prefix.
<!-- @remoteBases @test -->
<!-- @remoteBases @testAgainstLatestRelease -->
```
test 3 == \
$(kustomize build $DEMO_HOME | grep remote-.*-myapp-pod | wc -l); \

View File

@@ -25,7 +25,7 @@ etc.
## Make a place to work
<!-- @establishBase @test -->
<!-- @establishBase @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -43,7 +43,7 @@ Here's an example combining all three methods:
Make an env file with some short secrets:
<!-- @makeEnvFile @test -->
<!-- @makeEnvFile @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/foo.env
ROUTER_PASSWORD=admin
@@ -53,7 +53,7 @@ EOF
Make a text file with a long secret:
<!-- @makeLongSecretFile @test -->
<!-- @makeLongSecretFile @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/longsecret.txt
Lorem ipsum dolor sit amet,
@@ -67,7 +67,7 @@ And make a kustomization file referring to the
above and _additionally_ defining some literal KV
pairs in line:
<!-- @makeKustomization1 @test -->
<!-- @makeKustomization1 @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/kustomization.yaml
secretGenerator:
@@ -84,7 +84,7 @@ EOF
Now generate the Secret:
<!-- @build1 @test -->
<!-- @build1 @testAgainstLatestRelease -->
```
result=$(kustomize build $DEMO_HOME)
echo "$result"
@@ -125,10 +125,8 @@ them, etc.
## Secret values from anywhere
> New _alpha_ behavior at HEAD, for v2.1+
A general alternative is to enshrine secret
value generation in a [plugin](../docs/plugins.md).
value generation in a [plugin](../docs/plugins).
The values can then come in via, say, an
authenticated and authorized RPC to a password
@@ -143,7 +141,7 @@ from a database.
Download it
<!-- @copyPlugin @test -->
<!-- @copyPlugin @testAgainstLatestRelease -->
```
repo=https://raw.githubusercontent.com/kubernetes-sigs/kustomize
pPath=plugin/someteam.example.com/v1/secretsfromdatabase
@@ -167,7 +165,7 @@ go build -buildmode plugin \
Create a configuration file for it:
<!-- @makeConfiguration @test -->
<!-- @makeConfiguration @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/secretFromDb.yaml
apiVersion: someteam.example.com/v1
@@ -185,7 +183,7 @@ EOF
Create a new kustomization file
referencing this plugin:
<!-- @makeKustomization2 @test -->
<!-- @makeKustomization2 @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/kustomization.yaml
generators:

View File

@@ -13,7 +13,7 @@ In the production environment we want to customize the following:
- health check and readiness check.
First make a place to work:
<!-- @makeDemoHome @test -->
<!-- @makeDemoHome @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -27,7 +27,7 @@ as HERE documents.
Download them:
<!-- @downloadResources @test -->
<!-- @downloadResources @testAgainstLatestRelease -->
```
CONTENT="https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
@@ -44,14 +44,14 @@ a file called `kustomization.yaml`.
Start this file:
<!-- @kustomizeYaml @test -->
<!-- @kustomizeYaml @testAgainstLatestRelease -->
```
touch $DEMO_HOME/kustomization.yaml
```
### Add the resources
<!-- @addResources @test -->
<!-- @addResources @testAgainstLatestRelease -->
```
cd $DEMO_HOME
@@ -71,7 +71,7 @@ cat kustomization.yaml
### Add configMap generator
<!-- @addConfigMap @test -->
<!-- @addConfigMap @testAgainstLatestRelease -->
```
echo "app.name=Kustomize Demo" >$DEMO_HOME/application.properties
@@ -102,7 +102,7 @@ For Spring Boot application, we can set an active profile through the environmen
the application will pick up an extra `application-<profile>.properties` file. With this, we can customize the configMap in two
steps. Add an environment variable through the patch and add a file to the configMap.
<!-- @customizeConfigMap @test -->
<!-- @customizeConfigMap @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/patch.yaml
apiVersion: apps/v1beta2
@@ -149,7 +149,7 @@ Arrange for the resources to begin with prefix
_prod-_ (since they are meant for the _production_
environment):
<!-- @customizeLabel @test -->
<!-- @customizeLabel @testAgainstLatestRelease -->
```
cd $DEMO_HOME
kustomize edit set nameprefix 'prod-'
@@ -165,7 +165,7 @@ This `namePrefix` directive adds _prod-_ to all
resource names, as can be seen by building the
resources:
<!-- @build1 @test -->
<!-- @build1 @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME | grep prod-
```
@@ -180,7 +180,7 @@ selector.
add a label, but one can always edit
`kustomization.yaml` directly:
<!-- @customizeLabels @test -->
<!-- @customizeLabels @testAgainstLatestRelease -->
```
cat <<EOF >>$DEMO_HOME/kustomization.yaml
commonLabels:
@@ -191,7 +191,7 @@ EOF
Confirm that the resources now all have names prefixed
by `prod-` and the label tuple `env:prod`:
<!-- @build2 @test -->
<!-- @build2 @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME | grep -C 3 env
```
@@ -205,7 +205,7 @@ set JVM options accordingly.
Download the patch `memorylimit_patch.yaml`. It contains the memory limits setup.
<!-- @downloadPatch @test -->
<!-- @downloadPatch @testAgainstLatestRelease -->
```
curl -s -o "$DEMO_HOME/#1.yaml" \
"$CONTENT/overlays/production/{memorylimit_patch}.yaml"
@@ -243,7 +243,7 @@ has end points such as `/actuator/health` for this. We can customize the k8s dep
Download the patch `healthcheck_patch.yaml`. It contains the liveness probes and readyness probes.
<!-- @downloadPatch @test -->
<!-- @downloadPatch @testAgainstLatestRelease -->
```
curl -s -o "$DEMO_HOME/#1.yaml" \
"$CONTENT/overlays/production/{healthcheck_patch}.yaml"
@@ -281,7 +281,7 @@ The output contains
Add these patches to the kustomization:
<!-- @addPatch @test -->
<!-- @addPatch @testAgainstLatestRelease -->
```
cd $DEMO_HOME
kustomize edit add patch memorylimit_patch.yaml
@@ -301,7 +301,7 @@ The output of the following command can now be applied
to the cluster (i.e. piped to `kubectl apply`) to
create the production environment.
<!-- @finalBuild @test -->
<!-- @finalBuild @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME # | kubectl apply -f -
```

View File

@@ -3,7 +3,7 @@
This tutorial shows how to add transformer configurations to support a custom resource.
Create a workspace by
<!-- @createws @test -->
<!-- @createws @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -17,7 +17,7 @@ Consider a CRD of kind `MyKind` with fields
- `.spec.selectors` as the label selectors
Add the following file to configure the transformers for the above fields
<!-- @addConfig @test -->
<!-- @addConfig @testAgainstLatestRelease -->
```
mkdir $DEMO_HOME/kustomizeconfig
cat > $DEMO_HOME/kustomizeconfig/mykind.yaml << EOF
@@ -51,7 +51,7 @@ EOF
Create a file with some resources that
includes an instance of `MyKind`:
<!-- @createResource @test -->
<!-- @createResource @testAgainstLatestRelease -->
```
cat > $DEMO_HOME/resources.yaml << EOF
apiVersion: v1
@@ -88,7 +88,7 @@ EOF
Create a kustomization referring to it:
<!-- @createKustomization @test -->
<!-- @createKustomization @testAgainstLatestRelease -->
```
cat > $DEMO_HOME/kustomization.yaml << EOF
resources:
@@ -112,7 +112,7 @@ EOF
Use the customized transformer configurations by specifying them
in the kustomization file:
<!-- @addTransformerConfigs @test -->
<!-- @addTransformerConfigs @testAgainstLatestRelease -->
```
cat >> $DEMO_HOME/kustomization.yaml << EOF
configurations:
@@ -122,7 +122,7 @@ EOF
Run `kustomize build` and verify that the namereference is correctly resolved.
<!-- @build @test -->
<!-- @build @testAgainstLatestRelease -->
```
test 2 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*Ref" | grep "test-" | wc -l); \
@@ -131,7 +131,7 @@ echo $?
Run `kustomize build` and verify that the vars correctly resolved.
<!-- @verify @test -->
<!-- @verify @testAgainstLatestRelease -->
```
test 0 == \
$(kustomize build $DEMO_HOME | grep "BEE_ACTION" | wc -l); \

View File

@@ -3,7 +3,7 @@
This tutorial shows how to modify images in resources, and create a custom images transformer configuration.
Create a workspace by
<!-- @createws @test -->
<!-- @createws @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -15,7 +15,7 @@ Consider a Custom Resource Definition(CRD) of kind `MyKind` with field
Add the following file to configure the images transformer for the CRD:
<!-- @addConfig @test -->
<!-- @addConfig @testAgainstLatestRelease -->
```
mkdir $DEMO_HOME/kustomizeconfig
cat > $DEMO_HOME/kustomizeconfig/mykind.yaml << EOF
@@ -30,7 +30,7 @@ EOF
Create a file with some resources that includes an instance of `MyKind`:
<!-- @createResource @test -->
<!-- @createResource @testAgainstLatestRelease -->
```
cat > $DEMO_HOME/resources.yaml << EOF
@@ -66,7 +66,7 @@ EOF
Create a kustomization.yaml referring to it:
<!-- @createKustomization @test -->
<!-- @createKustomization @testAgainstLatestRelease -->
```
cat > $DEMO_HOME/kustomization.yaml << EOF
resources:
@@ -90,7 +90,7 @@ EOF
Use the customized transformer configurations by specifying them
in the kustomization file:
<!-- @addTransformerConfigs @test -->
<!-- @addTransformerConfigs @testAgainstLatestRelease -->
```
cat >> $DEMO_HOME/kustomization.yaml << EOF
configurations:
@@ -100,27 +100,27 @@ EOF
Run `kustomize build` and verify that the images have been updated.
<!-- @build @test -->
<!-- @build @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*image" | grep "new-crd-image:new-v1-tag" | wc -l); \
echo $?
```
<!-- @build @test -->
<!-- @build @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*image" | grep "new-app-1:MYNEWTAG-1" | wc -l); \
echo $?
```
<!-- @build @test -->
<!-- @build @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*image" | grep "my-docker2@sha" | wc -l); \
echo $?
```
<!-- @build @test -->
<!-- @build @testAgainstLatestRelease -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 2 ".*image" | grep "prod-mysql:v3" | wc -l); \

View File

@@ -2,7 +2,7 @@
[base]: ../../docs/glossary.md#base
[kubeval]: https://github.com/instrumenta/kubeval
[plugin]: ../../docs/plugins.md
[plugin]: ../../docs/plugins
kustomize doesn't validate either its input or
output beyond the validation provided by the
@@ -24,7 +24,7 @@ loaded by Kustomize.
Make a place to work:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
mkdir -p $DEMO_HOME/valid
@@ -38,7 +38,7 @@ mkdir -p $PLUGINDIR
Download the [kubeval] binary depending on the operating system
and add it to $PATH.
<!-- @downloadKubeval @test -->
<!-- @downloadKubeval @testAgainstLatestRelease -->
```
OS=`uname | sed -e 's/Linux/linux/' -e 's/Darwin/darwin/'`
wget https://github.com/instrumenta/kubeval/releases/download/0.9.2/kubeval-${OS}-amd64.tar.gz
@@ -60,7 +60,7 @@ A transformer plugin for the validation can be written as a
bash script, which execute the [kubeval] binary and return proper
output and exit code.
<!-- @writePlugin @test -->
<!-- @writePlugin @testAgainstLatestRelease -->
```
cat <<'EOF' > $PLUGINDIR/Validator
#!/bin/bash
@@ -95,7 +95,7 @@ chmod +x $PLUGINDIR/Validator
Define a kustomization containing a valid ConfigMap
and the transformer plugin.
<!-- @writeKustomization @test -->
<!-- @writeKustomization @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/valid/configmap.yaml
apiVersion: v1
@@ -125,7 +125,7 @@ EOF
Define a kustomization containing an invalid ConfigMap
and the transformer plugin.
<!-- @writeKustomization @test -->
<!-- @writeKustomization @testAgainstLatestRelease -->
```
cat <<'EOF' >$DEMO_HOME/invalid/configmap.yaml
apiVersion: v1
@@ -175,7 +175,7 @@ The directory structure is as the following:
Define a helper function to run kustomize with the
correct environment and flags for plugins:
<!-- @defineKustomizeBd @test -->
<!-- @defineKustomizeBd @testAgainstLatestRelease -->
```
function kustomizeBd {
XDG_CONFIG_HOME=$DEMO_HOME \
@@ -187,7 +187,7 @@ function kustomizeBd {
Build the valid variant
<!-- @buildValid @test -->
<!-- @buildValid @testAgainstLatestRelease -->
```
kustomizeBd valid
```
@@ -215,7 +215,7 @@ data: Invalid type. Expected: object, given: array
## cleanup
<!-- @cleanup @test -->
<!-- @cleanup @testAgainstLatestRelease -->
```shell
rm -rf $DEMO_HOME
```

View File

@@ -8,7 +8,7 @@ To run WordPress, it's necessary to
- access the service name of MySQL database from WordPress container
First make a place to work:
<!-- @makeDemoHome @test -->
<!-- @makeDemoHome @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
MYSQL_HOME=$DEMO_HOME/mysql
@@ -21,7 +21,7 @@ mkdir -p $WORDPRESS_HOME
Download the resources and `kustomization.yaml` for WordPress.
<!-- @downloadResources @test -->
<!-- @downloadResources @testAgainstLatestRelease -->
```
CONTENT="https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
@@ -33,7 +33,7 @@ curl -s -o "$WORDPRESS_HOME/#1.yaml" \
Download the resources and `kustomization.yaml` for MySQL.
<!-- @downloadResources @test -->
<!-- @downloadResources @testAgainstLatestRelease -->
```
CONTENT="https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
@@ -48,7 +48,7 @@ curl -s -o "$MYSQL_HOME/#1.yaml" \
Create a new kustomization with two bases,
`wordpress` and `mysql`:
<!-- @createKustomization @test -->
<!-- @createKustomization @testAgainstLatestRelease -->
```
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
@@ -65,7 +65,7 @@ In the new kustomization, apply a patch for wordpress deployment. The patch does
- Add an initial container to show the mysql service name
- Add environment variable that allow wordpress to find the mysql database
<!-- @downloadPatch @test -->
<!-- @downloadPatch @testAgainstLatestRelease -->
```
CONTENT="https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
@@ -105,7 +105,7 @@ $(WORDPRESS_SERVICE) and $(MYSQL_SERVICE).
### Bind the Variables to k8s Object Fields
<!-- @addVarRef @test -->
<!-- @addVarRef @testAgainstLatestRelease -->
```
cat <<EOF >>$DEMO_HOME/kustomization.yaml
vars:
@@ -128,7 +128,7 @@ EOF
### Substitution
Confirm the variable substitution:
<!-- @kustomizeBuild @test -->
<!-- @kustomizeBuild @testAgainstLatestRelease -->
```
kustomize build $DEMO_HOME
```

View File

@@ -7,24 +7,26 @@
这些示例通过了 [pre-commit](../../travis/pre-commit.sh) 测试,并且应该与 HEAD 一起使用。
```
go get sigs.k8s.io/kustomize
go get sigs.k8s.io/kustomize/v3/cmd/kustomize
```
基本用法
* [configGenerations](../configGeneration.md) - 当 ConfigMapGenerator 修改时进行滚动更新。
* [configGenerations](configGeneration.md) - 当 ConfigMapGenerator 修改时进行滚动更新。
* [combineConfigs](../combineConfigs.md) - 融合来自不同用户的配置数据(例如来自 devops/SRE 和 developers
* [combineConfigs](combineConfigs.md) - 融合来自不同用户的配置数据(例如来自 devops/SRE 和 developers
* [generatorOptions](../generatorOptions.md) -修改所有 ConfigMapGenerator 和 SecretGenerator 的行为。
* [generatorOptions](generatorOptions.md) -修改所有 ConfigMapGenerator 和 SecretGenerator 的行为。
* [vars](../wordpress/README.md) - 通过 vars 将一个资源的数据注入另一个资源的容器参数 (例如,为 wordpress 指定 SQL 服务)。
* [vars](vars.md) - 通过 vars 将一个资源的数据注入另一个资源的容器参数 (例如,为 wordpress 指定 SQL 服务)。
* [image names and tags](../image.md) - 在不使用 patch 的情况下更新镜像名称和标签。
* [image names and tags](image.md) - 在不使用 patch 的情况下更新镜像名称和标签。
* [remote target](../remoteBuild.md) - 通过 github URL 来构建 kustomization 。
* [remote target](remoteBuild.md) - 通过 github URL 来构建 kustomization 。
* [json patch](../jsonpatch.md) -在 kustomization 中应用 json patch 。
* [json patch](jsonpatch.md) -在 kustomization 中应用 json patch 。
* [patch multiple objects](patchMultipleObjects.md) - 通过一个patch来修改多个资源。
高级用法
@@ -34,6 +36,10 @@ go get sigs.k8s.io/kustomize
* [secret generation](../secretGeneratorPlugin.md) - 生成 Secret。
- transformer 插件:
* [validation transformer](../validationTransformer/README.md) - 通过 transformer 验证资源。
- 定制内建 transformer 配置
* [transformer configs](../transformerconfigs/README.md) - 自定义 transformer 配置。

View File

@@ -0,0 +1,230 @@
[overlay]: ../docs/glossary.md#overlay
[target]: ../docs/glossary.md#target
# 示例devops和开发配合管理配置数据
场景:在生产环境中有一个基于 Java 由多个内部团队(注册、结账和搜索等)共同开发的商店服务。
这个服务在不同的环境中运行_development_、 _testing__staging__production_,从 Java 的 properties 文件中读取配置。
为每个环境维护一个大的 properties 文件是很困难的。这个文件需要频繁的修改,并且这些修改都需要由 devops 工程师来进行,因为:
1. 这个文件包含 devops 工程师需要知道,而开发人员不必知道的值
2. 比如生产环境的 properties 包含敏感数据,比如生产数据库的登录凭据。
## Property sharding
通过一些研究,我们注意到属性可以分为不同的类别。
### Property sharding
例如:国际化数据、物理常量,外部服务位置等静态数据。
_这些无论哪个环境,都一样的配置。_
这些都只需要一组配置。将这组配置放在一个文件中:
* `common.properties`
### Plumbing properties
例如静态资源HTML、CSS、JavaScript的位置产品和用户的数据表负载均衡的端口日志收集等。
_这些属性的不同,恰恰是环境的不同之处。_
DevOps 或 SRE 工程师需要完全控制生产环境中的这些配置;测试需要调整数据库来支持测试;而开发则希望尝试开发中遇到的各种不同的情景。
将这些值放入
* `development/plumbing.properties`
* `staging/plumbing.properties`
* `production/plumbing.properties`
### Secret properties
例如:用户表的位置、数据库凭证、解密密钥等。
_这些需要 devops 工程师控制,其他人没有访问权限。_
将这些值放入
* `development/secret.properties`
* `staging/secret.properties`
* `production/secret.properties`
[kubernetes secret]: https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/
例如使用 unix 文件权限和模式来限制访问控制,或者使用更好的方法-使用专门用于存储密码的服务,并且使用 kustomize 中的 `secretGenerator` 字段在 Kubernetes 中创建 secret 来存储密码。
<!--
secretGenerator:
- name: app-tls
files:
tls.crt=tls.cert
tls.key=tls.key
type: "kubernetes.io/tls"
EOF
-->
## 混合管理方法
基于相同的 base 创建 _n_ 个 overlays 来创建 _n_ 个集群环境的方法。
在本例的其余部分,我们将使用 _n==2_,这里只使用 _development__production_ ,可以使用相同的方法来增加更多的环境。
运行 `kustomize build` 基于 [overlay] 的 [target] 来创建集群环境。
[helloworld]: helloWorld.md
以下示例将执行此操作,但将侧重于 configMap 构建,而不用担心如何将 configMaps 关联到 Deployment[helloworld] 示例中介绍的)。
所有文件(包括共享 property 文件)都将在目录树中创建,目录中包含 base 和 overlay 文件的目录,这些都与 [helloworld] 中演示的一致。
它将全部存在于此工作目录中:
<!-- @makeWorkplace @test -->
```bash
DEMO_HOME=$(mktemp -d)
```
### 创建 base
<!-- kubectl create configmap BOB --dry-run -o yaml --from-file db. -->
创建放置 base 配置的路径:
<!-- @baseDir @test -->
```bash
mkdir -p $DEMO_HOME/base
```
向 base 中的插入数据base 中应该包含所有环境共有的资源,这里我们只定义一个 java properties 文件,以及一个引用他们的 `kustomization` 文件。
<!-- @baseKustomization @test -->
```bash
cat <<EOF >$DEMO_HOME/base/common.properties
color=blue
height=10m
EOF
cat <<EOF >$DEMO_HOME/base/kustomization.yaml
configMapGenerator:
- name: my-configmap
files:
- common.properties
EOF
```
### 创建并使用 overlay 用于 _开发_
创建一个 overlays 目录:
<!-- @overlays @test -->
```bash
OVERLAYS=$DEMO_HOME/overlays
```
创建 _development_ overlay
<!-- @developmentFiles @test -->
```bash
mkdir -p $OVERLAYS/development
cat <<EOF >$OVERLAYS/development/plumbing.properties
port=30000
EOF
cat <<EOF >$OVERLAYS/development/secret.properties
dbpassword=mothersMaidenName
EOF
cat <<EOF >$OVERLAYS/development/kustomization.yaml
resources:
- ../../base
namePrefix: dev-
nameSuffix: -v1
configMapGenerator:
- name: my-configmap
behavior: merge
files:
- plumbing.properties
- secret.properties
EOF
```
现在可以生成开发使用的 configMaps
<!-- @runDev @test -->
```bash
kustomize build $OVERLAYS/development
```
#### 检查 ConfigMap 名称
可以在输出中看到生成的 `ConfigMap` 名称。
名称应该是这样的:`dev-my-configmap-v1-2gccmccgd5`
* `"dev-"` 来自 `namePrefix` 字段
* `"my-configmap"` 来自 `configMapGenerator/name` 字段
* `"-v1"` 来自 `nameSuffix` 字段
* `"-2gccmccgd5"` 为哈希值,是 `kustomize` 根据 configMap 的内容计算的
哈希后缀很关键,如果 configMap 内容发生变化, configMap 的名称也会发生变化,以及从 `kustomize` 出现在 YAML 输出中的对该名称的所有引用。
名称更改意味着如果使用类似命令将此 YAML 应用于群集,则 Deployment 将执行滚动更新重启以获取新数据。
> ```bash
> kustomize build $OVERLAYS/development | kubectl apply -f -
> ```
Deployment 无法自动检测 ConfigMap 是否发生改变。
如果更改 configMap 的数据, 而不更改其名称以及对该名称的所有引用, 则必须重新启动Deployment中的那些Pods以获取更改。
最佳的做法就是将 configMap 视为不变的。
不去编辑 configMap ,而是使用 __ 的名称的 __ configMap并在 Deployment 中引用新的 configMap 。而 `kustomize` 使用 `configMapGenerator` 指令和相关的命名控件使这很容易。
### 创建并且使用 overlay 用于 _生产_
接下来创建 _production_ overlay 的文件:
<!-- @productionFiles @test -->
```bash
mkdir -p $OVERLAYS/production
cat <<EOF >$OVERLAYS/production/plumbing.properties
port=8080
EOF
cat <<EOF >$OVERLAYS/production/secret.properties
dbpassword=thisShouldProbablyBeInASecretInstead
EOF
cat <<EOF >$OVERLAYS/production/kustomization.yaml
resources:
- ../../base
namePrefix: prod-
configMapGenerator:
- name: my-configmap
behavior: merge
files:
- plumbing.properties
- secret.properties
EOF
```
现在可以生成用于生产的 configMap
<!-- @runProd @test -->
```bash
kustomize build $OVERLAYS/production
```
可以直接在 CI/CD 流程中执行如下命令,将应用部署到集群:
> ```bash
> kustomize build $OVERLAYS/production | kubectl apply -f -
> ```

View File

@@ -0,0 +1,184 @@
[patch]: ../../docs/glossary.md#patch
[resource]: ../../docs/glossary.md#resource
[variant]: ../../docs/glossary.md#variant
## ConfigMap 的生成和滚动更新
kustomize 提供了两种添加 ConfigMap 的方法:
- 将 ConfigMap 声明为 [resource]
- 通过 ConfigMapGenerator 声明 ConfigMap
`kustomization.yaml` 中,这两种方法的格式分别如下:
> ```
> # 将 ConfigMap 声明为 resource
> resources:
> - configmap.yaml
>
> # 在 ConfigMapGenerator 中声明 ConfigMap
> configMapGenerator:
> - name: a-configmap
> files:
> - configs/configfile
> - configs/another_configfile
> ```
声明为 [resource] 的 ConfigMaps 的处理方式与其他 resource 相同Kustomize 不会在为 ConfigMap 的名称添加哈希后缀。而在 ConfigMapGenerator 中声明 ConfigMap 的处理方式则与之前不同默认将为名称添加哈希后缀ConfigMap 中的任何更改都将触发滚动更新。
在 [hello_world](helloWorld.md) 示例中,使用 ConfigmapGenerator 来替换将 ConfigMap 声明为 [resource] 的方法。由此生成的 ConfigMap 中的更改将导致哈希值更改和滚动更新。
### 建立 base 和 staging
使用 configMapGenerator 建立 base
<!-- @establishBase @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
BASE=$DEMO_HOME/base
mkdir -p $BASE
curl -s -o "$BASE/#1.yaml" "https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
/master/examples/helloWorld\
/{deployment,service}.yaml"
cat <<'EOF' >$BASE/kustomization.yaml
commonLabels:
app: hello
resources:
- deployment.yaml
- service.yaml
configMapGenerator:
- name: the-map
literals:
- altGreeting=Good Morning!
- enableRisky="false"
EOF
```
通过应用 ConfigMap patch 的方式建立 staging
<!-- @establishStaging @testAgainstLatestRelease -->
```
OVERLAYS=$DEMO_HOME/overlays
mkdir -p $OVERLAYS/staging
cat <<'EOF' >$OVERLAYS/staging/kustomization.yaml
namePrefix: staging-
nameSuffix: -v1
commonLabels:
variant: staging
org: acmeCorporation
commonAnnotations:
note: Hello, I am staging!
resources:
- ../../base
patchesStrategicMerge:
- map.yaml
EOF
cat <<EOF >$OVERLAYS/staging/map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
data:
altGreeting: "Have a pineapple!"
enableRisky: "true"
EOF
```
### Review
在集群中运行的 _hello-world_ 的 deployment 配置了来自 configMap 的数据。
deployment 按照名称引用此 ConfigMap
<!-- @showDeployment @testAgainstLatestRelease -->
```
grep -C 2 configMapKeyRef $BASE/deployment.yaml
```
当 ConfigMap 中的数据需要更新时,更改群集中的实时 ConfigMap 的数据并不是一个好的做法。 由于 Deployment 无法知道其引用的 ConfigMap 已更改,这类更新是无效。
更改 Deployment 配置的推荐方法是:
1. 使用新名称创建一个新的 configMap
2. 为_deployment_ 添加 patch修改相应 `configMapKeyRef` 字段的名称值。
后一种更改会启动对 deployment 中的 pod 的滚动更新。旧的 configMap 在不再被任何其他资源引用时最终会被[垃圾回收](https://github.com/kubernetes-sigs/kustomize/issues/242)。
### 如何使用 kustomize
_staging_ 的 [variant] 包含一个 configMap 的 [patch]
<!-- @showMapPatch @testAgainstLatestRelease -->
```
cat $OVERLAYS/staging/map.yaml
```
根据定义,此 patch 是一个命名但不一定是完整的资源规范,旨在修改完整的资源规范。
在 ConfigMapGenerator 中声明 ConfigMap 的修改。
<!-- @showMapBase @testAgainstLatestRelease -->
```
grep -C 4 configMapGenerator $BASE/kustomization.yaml
```
要使这个 patch 正常工作,`metadata/name` 字段中的名称必须匹配。
但是文件中指定的名称值不是群集中使用的名称值。根据设计kustomize 修改从 ConfigMapGenerator 声明的 ConfigMaps 的名称。要查看最终在群集中使用的名称,只需运行 kustomize
<!-- @grepStagingName @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging |\
grep -B 8 -A 1 staging-the-map
```
根据 `$OVERLAYS/staging/kustomization.yaml` 中的 `namePrefix` 字段configMap 名称以 _staging-_ 为前缀。
根据 `$OVERLAYS/staging/kustomization.yaml` 中的 `nameSuffix` 字段configMap 名称以 _-v1_ 为后缀。
configMap 名称的后缀是由 map 内容的哈希生成的 - 在这种情况下,名称后缀是 _k25m8k5k5m_
<!-- @grepStagingHash @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging | grep k25m8k5k5m
```
现在修改 map patch ,更改该服务将使用的问候消息:
<!-- @changeMap @testAgainstLatestRelease -->
```
sed -i.bak 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml
```
查看新的问候消息:
```
kustomize build $OVERLAYS/staging |\
grep -B 2 -A 3 kiwi
```
再次运行 kustomize 查看新的 configMap 名称:
<!-- @grepStagingName @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging |\
grep -B 8 -A 1 staging-the-map
```
确认 configMap 内容的更改将会生成以 _cd7kdh48fd_ 结尾的三个新名称 - 一个在 configMap 的名称中,另两个在使用 ConfigMap 的 deployment 中:
<!-- @countHashes @testAgainstLatestRelease -->
```
test 3 == \
$(kustomize build $OVERLAYS/staging | grep cd7kdh48fd | wc -l); \
echo $?
```
将这些资源应用于群集将导致 deployment pod 的滚动更新,将它们从 _k25m8k5k5m_ map 重新定位到 _cd7kdh48fd_ map 。系统稍后将垃圾收集未使用的 map。
## 回滚
回滚,可以撤消对源码配置所做的任何编辑,然后在还原的配置上重新运行 kustomize 并将其应用于群集。

View File

@@ -0,0 +1,60 @@
# Generator Options
Kustomize 提供了修改 ConfigMapGenerator 和 SecretGenerator 行为的选项,这些选项包括:
- 不再将基于内容生成的哈希后缀添加到资源名称后
- 为生成的资源添加 labels
- 为生成的资源添加 annotations
这个示例将展示如何运用这些选项,首先创建一个工作空间:
```bash
DEMO_HOME=$(mktemp -d)
```
创建 kustomization 并且为其添加一个 ConfigMapGenerator
<!-- @createCMGenerator @test -->
```bash
cat > $DEMO_HOME/kustomization.yaml << EOF
configMapGenerator:
- name: my-configmap
literals:
- foo=bar
- baz=qux
EOF
```
添加如下 generatorOptions
<!-- @addGeneratorOptions @test -->
```bash
cat >> $DEMO_HOME/kustomization.yaml << EOF
generatorOptions:
disableNameSuffixHash: true
labels:
kustomize.generated.resource: somevalue
annotations:
annotations.only.for.generated: othervalue
EOF
```
运行 `kustomize build` 并且确定生成的 ConfigMap 。
- 确定没有名称后缀
<!-- @verify @test -->
```
test 1 == \
$(kustomize build $DEMO_HOME | grep "name: my-configmap$" | wc -l); \
echo $?
```
- 确定 label `kustomize.generated.resource: somevalue`
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 1 "labels" | grep "kustomize.generated.resource" | wc -l); \
echo $?
```
- 确定 annotation `annotations.only.for.generated: othervalue`
```
test 1 == \
$(kustomize build $DEMO_HOME | grep -A 1 "annotations" | grep "annotations.only.for.generated" | wc -l); \
echo $?
```

View File

@@ -21,7 +21,7 @@
首先创建一个工作空间:
<!-- @makeWorkplace @test -->
<!-- @makeWorkplace @testAgainstLatestRelease -->
```
DEMO_HOME=$(mktemp -d)
```
@@ -38,7 +38,7 @@ DEMO_HOME=$(mktemp -d)
为了使本文档保持简洁base 的资源位于补充目录中,并不在此处,请按照下面的方法下载它们:
<!-- @downloadBase @test -->
<!-- @downloadBase @testAgainstLatestRelease -->
```
BASE=$DEMO_HOME/base
mkdir -p $BASE
@@ -51,7 +51,7 @@ curl -s -o "$BASE/#1.yaml" "https://raw.githubusercontent.com\
观察该目录:
<!-- @runTree @test -->
<!-- @runTree @testAgainstLatestRelease -->
```
tree $DEMO_HOME
```
@@ -80,14 +80,14 @@ tree $DEMO_HOME
`base` 目录中包含一个 [kustomization] 文件:
<!-- @showKustomization @test -->
<!-- @showKustomization @testAgainstLatestRelease -->
```
more $BASE/kustomization.yaml
```
(可选)在 base 目录上运行 `kustomize` 将定制过的 resources 打印到标准输出:
<!-- @buildBase @test -->
<!-- @buildBase @testAgainstLatestRelease -->
```
kustomize build $BASE
```
@@ -96,14 +96,14 @@ kustomize build $BASE
定制 _app label_ 并应用于所有的 resources
<!-- @addLabel @test -->
<!-- @addLabel @testAgainstLatestRelease -->
```
sed -i.bak 's/app: hello/app: my-hello/' \
$BASE/kustomization.yaml
```
查看效果:
<!-- @checkLabel @test -->
<!-- @checkLabel @testAgainstLatestRelease -->
```
kustomize build $BASE | grep -C 3 app:
```
@@ -116,7 +116,7 @@ kustomize build $BASE | grep -C 3 app:
* _Production_ 包含更多的副本数。
* 来自这些集群 [variants] 的问候消息将与来自其他集群的不同。
<!-- @overlayDirectories @test -->
<!-- @overlayDirectories @testAgainstLatestRelease -->
```
OVERLAYS=$DEMO_HOME/overlays
mkdir -p $OVERLAYS/staging
@@ -127,7 +127,7 @@ mkdir -p $OVERLAYS/production
`staging` 目录中创建一个 kustomization 文件,用来定义一个新的名称前缀和一些不同的 labels 。
<!-- @makeStagingKustomization @test -->
<!-- @makeStagingKustomization @testAgainstLatestRelease -->
```
cat <<'EOF' >$OVERLAYS/staging/kustomization.yaml
namePrefix: staging-
@@ -149,7 +149,7 @@ EOF
同时,将 _risky_ 标记设置为 true 。
<!-- @stagingMap @test -->
<!-- @stagingMap @testAgainstLatestRelease -->
```
cat <<EOF >$OVERLAYS/staging/map.yaml
apiVersion: v1
@@ -166,7 +166,7 @@ EOF
`production` 目录中创建一个 kustomization 文件,用来定义一个新的名称前缀和 labels 。
<!-- @makeProductionKustomization @test -->
<!-- @makeProductionKustomization @testAgainstLatestRelease -->
```
cat <<EOF >$OVERLAYS/production/kustomization.yaml
namePrefix: production-
@@ -187,7 +187,7 @@ EOF
因为生产环境需要处理更多的流量,新建一个 production patch 来增加副本数。
<!-- @productionDeployment @test -->
<!-- @productionDeployment @testAgainstLatestRelease -->
```
cat <<EOF >$OVERLAYS/production/deployment.yaml
apiVersion: apps/v1
@@ -210,7 +210,7 @@ EOF
查看目录结构和差异:
<!-- @listFiles @test -->
<!-- @listFiles @testAgainstLatestRelease -->
```
tree $DEMO_HOME
```
@@ -268,12 +268,12 @@ diff \
输出不同 _overlys_ 的配置:
<!-- @buildStaging @test -->
<!-- @buildStaging @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/staging
```
<!-- @buildProduction @test -->
<!-- @buildProduction @testAgainstLatestRelease -->
```
kustomize build $OVERLAYS/production
```

74
examples/zh/image.md Normal file
View File

@@ -0,0 +1,74 @@
# 示例: 改变镜像名称和标签
首先构建一个工作空间:
<!-- @makeWorkplace @testAgainstLatestRelease -->
```bash
DEMO_HOME=$(mktemp -d)
```
创建包含pod资源的 `kustomization`
<!-- @testAgainstLatestRelease to @test -->
```bash
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
- pod.yaml
EOF
```
创建 pod 资源pod.yaml
<!-- @createDeployment @test -->
```bash
cat <<EOF >$DEMO_HOME/pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: busybox:1.29.0
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-mydb
image: busybox:1.29.0
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
EOF
```
`myapp-pod` 包含一个init容器和一个普通容器两者都使用 `busybox1.29.0` 镜像。
`kustomization.yaml` 中添加 `images` 字段来更改镜像 `busybox` 和标签 `1.29.0`
- 通过 `kustomize` 添加 `images`
<!-- @addImages @test -->
```bash
cd $DEMO_HOME
kustomize edit set image busybox=alpine:3.6
```
- 将`images`字段将被添加到`kustomization.yaml`
> ```yaml
> images:
> - name: busybox
> newName: alpine
> newTag: 3.6
> ```
构建 `kustomization`
<!-- @kustomizeBuild @testAgainstLatestRelease -->
```bash
kustomize build $DEMO_HOME
```
确认`busybox`镜像和标签是否被替换为`alpine3.6`
<!-- @confirmImages @testAgainstLatestRelease -->
```
test 2 = \
$(kustomize build $DEMO_HOME | grep alpine:3.6 | wc -l); \
echo $?
```

124
examples/zh/jsonpatch.md Normal file
View File

@@ -0,0 +1,124 @@
# 示例: 应用 json patchjson补丁
kustomization文件支持通过[JSON patches](https://tools.ietf.org/html/rfc6902)来修改已有的资源.
下面的例子将会使用这个功能对`Ingress`加以修改.
首先,创建一个包含`ingress``kustomization`文件.
<!-- @createIngress @testAgainstLatestRelease -->
```bash
DEMO_HOME=$(mktemp -d)
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
- ingress.yaml
EOF
cat <<EOF >$DEMO_HOME/ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: my-api
servicePort: 80
EOF
```
定义一个JSON patch文件以更新`Ingress`对象的2个字段:
- 把 host 从 `foo.bar.com` 改为 `foo.bar.io`
- 把 servicePort 从 `80` 改为 `8080`
<!-- @addJsonPatch @testAgainstLatestRelease -->
```bash
cat <<EOF >$DEMO_HOME/ingress_patch.json
[
{"op": "replace", "path": "/spec/rules/0/host", "value": "foo.bar.io"},
{"op": "replace", "path": "/spec/rules/0/http/paths/0/backend/servicePort", "value": 8080}
]
EOF
```
JSON patch 也可以写成 YAML 的格式.该例子顺便展示了“添加”操作:
<!-- @addYamlPatch @testAgainstLatestRelease -->
```bash
cat <<EOF >$DEMO_HOME/ingress_patch.yaml
- op: replace
path: /spec/rules/0/host
value: foo.bar.io
- op: add
path: /spec/rules/0/http/paths/-
value:
path: '/test'
backend:
serviceName: my-test
servicePort: 8081
EOF
```
在kustomization.yaml文件中增加 _patchesJson6902_ 字段,以应用该补丁
<!-- @applyJsonPatch @testAgainstLatestRelease -->
```bash
cat <<EOF >>$DEMO_HOME/kustomization.yaml
patchesJson6902:
- target:
group: extensions
version: v1beta1
kind: Ingress
name: my-ingress
path: ingress_patch.json
EOF
```
运行 `kustomize build $DEMO_HOME`, 在输出那里确认 host 已经被正确更新.
<!-- @confirmHost @testAgainstLatestRelease -->
```bash
test 1 == \
$(kustomize build $DEMO_HOME | grep "host: foo.bar.io" | wc -l); \
echo $?
```
运行 `kustomize build $DEMO_HOME`, 在输出那里确认 servicePort 已经被正确更新.
<!-- @confirmServicePort @testAgainstLatestRelease -->
```bash
test 1 == \
$(kustomize build $DEMO_HOME | grep "servicePort: 8080" | wc -l); \
echo $?
```
如果 patch 是YAML格式的就能正确解析:
<!-- @applyYamlPatch @testAgainstLatestRelease -->
```bash
cat <<EOF >>$DEMO_HOME/kustomization.yaml
patchesJson6902:
- target:
group: extensions
version: v1beta1
kind: Ingress
name: my-ingress
path: ingress_patch.yaml
EOF
```
运行 `kustomize build $DEMO_HOME`, 在输出那里确认有 `/test` 这个路径.
<!-- @confirmYamlPatch @testAgainstLatestRelease -->
```bash
test 1 == \
$(kustomize build $DEMO_HOME | grep "path: /test" | wc -l); \
echo $?
```

View File

@@ -0,0 +1,186 @@
[Strategic Merge Patch]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-api-machinery/strategic-merge-patch.md
[JSON patches]: https://tools.ietf.org/html/rfc6902
[label selector]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors
# 示例:通过一个 patch 来修改多个资源
kustomization.yaml 支持通过 [Strategic Merge Patch] 和 [JSON patch] 来自定义资源。自 3.1.0 起,一个 patch 可以修改多个资源。
这可以通过指定 patch 和它所修改的 target 来完成,如下所示:
```yaml
patches:
- path: <PatchFile>
target:
group: <Group>
version: <Version>
kind: <Kind>
name: <Name>
namespace: <Namespace>
labelSelector: <LabelSelector>
annotationSelector: <AnnotationSelector>
```
`labelSelector``annotationSelector` 都应遵循 [label selector] 中的约定。Kustomize 选择匹配`target`中所有字段的目标来应用 patch 。
下面的示例展示了如何为所有部署资源注入 sidecar 容器。
创建一个包含 Deployment 资源的 `kustomization`
<!-- @createDeployment @test -->
```bash
DEMO_HOME=$(mktemp -d)
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
- deployments.yaml
EOF
cat <<EOF >$DEMO_HOME/deployments.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- name: nginx
image: nginx
args:
- one
- two
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
metadata:
labels:
key: value
spec:
containers:
- name: busybox
image: busybox
EOF
```
声明 [Strategic Merge Patch] 文件以注入 sidecar 容器:
<!-- @addPatch @test -->
```bash
cat <<EOF >$DEMO_HOME/patch.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: not-important
spec:
template:
spec:
containers:
- name: istio-proxy
image: docker.io/istio/proxyv2
args:
- proxy
- sidecar
EOF
```
在 kustomization.yaml 中添加 _patches_ 字段
<!-- @applyPatch @testAgainstLatestRelease -->
```bash
cat <<EOF >>$DEMO_HOME/kustomization.yaml
patches:
- path: patch.yaml
target:
kind: Deployment
EOF
```
运行 `kustomize build $DEMO_HOME`,可以在输出中确认两个 Deployment 资源都已正确应用。
<!-- @confirmPatch @test -->
```bash
test 2 == \
$(kustomize build $DEMO_HOME | grep "image: docker.io/istio/proxyv2" | wc -l); \
echo $?
```
输出如下:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1
spec:
template:
metadata:
labels:
old-label: old-value
spec:
containers:
- args:
- proxy
- sidecar
image: docker.io/istio/proxyv2
name: istio-proxy
- args:
- one
- two
image: nginx
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2
spec:
template:
metadata:
labels:
key: value
spec:
containers:
- args:
- proxy
- sidecar
image: docker.io/istio/proxyv2
name: istio-proxy
- image: busybox
name: busybox
```
## Target 选择
- 选择名称与 `name*` 匹配的资源
```yaml
target:
name: name*
```
- 选择所有 Deployment 资源
```yaml
target:
kind: Deployment
```
- 选择 label 与 `app=hello` 匹配的资源
```yaml
target:
labelSelector: app=hello
```
- 选择 annotation 与 `app=hello` 匹配的资源
```yaml
target:
annotationSelector: app=hello
```
- 选择所有 label 与 `app=hello` 匹配的 Deployment 资源
```yaml
target:
kind: Deployment
labelSelector: app=hello
```

View File

@@ -0,0 +1,68 @@
# remote targets
`kustomize build` 可以将 URL 作为参数传入并运行.
运行效果与如下操作相同:
如果想要要立即尝试此操作,可以按照 [multibases](../multibases/README.md) 示例运行 kustomization 运行构建。然后查看输出中的pod
<!-- @remoteOverlayBuild @test -->
```bash
target="github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6"
test 1 == \
$(kustomize build $target | grep dev-myapp-pod | wc -l); \
echo $?
```
在该示例中运行 overlay 将获得三个 pod在此 overlay 结合了dev、staging 和 prod 的 bases以便同时将它们全部发送给所有人
<!-- @remoteBuild @test -->
```bash
target="https://github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6"
test 3 == \
$(kustomize build $target | grep cluster-a-.*-myapp-pod | wc -l); \
echo $?
```
将 URL 作为 base
<!-- @createOverlay @test -->
```bash
DEMO_HOME=$(mktemp -d)
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
- github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6
namePrefix: remote-
EOF
```
构建该 base 以确定所有的三个 pod 都有 `remote-` 前缀。
<!-- @remoteBases @testAgainstLatestRelease -->
```bash
test 3 == \
$(kustomize build $DEMO_HOME | grep remote-.*-myapp-pod | wc -l); \
echo $?
```
## URL format
URL 需要遵循 [hashicorp/go-getter URL 格式](https://github.com/hashicorp/go-getter#url-format) 。下面是一些遵循此约定的 Github repos 示例url。
- kustomization.yaml 在根目录
`github.com/Liujingfang1/mysql`
- kustomization.yaml 在 test 分支的根目录
`github.com/Liujingfang1/mysql?ref=test`
- kustomization.yaml 在 v1.0.6 版本的子目录
`github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6`
- kustomization.yaml repoUrl2 分支的子目录
`github.com/Liujingfang1/kustomize//examples/helloWorld?ref=repoUrl2`
- kustomization.yaml commit `7050a45134e9848fca214ad7e7007e96e5042c03` 的子目录
`github.com/Liujingfang1/kustomize//examples/helloWorld?ref=7050a45134e9848fca214ad7e7007e96e5042c03`

148
examples/zh/vars.md Normal file
View File

@@ -0,0 +1,148 @@
# 示例: 将 k8s runtime 数据注入容器
本教程将会介绍如何声明变量以及如何在容器中的命令使用变量。要注意的是变量的查找和替换并不适用于任意字段默认仅适用于容器的envargs和command。
运行WordPress以下是必须的
- WordPress 连接 MySQL 数据库
- MySQL 服务可以被 WordPress 容器访问
首先构建一个工作空间:
<!-- @makeDemoHome @test -->
```bash
DEMO_HOME=$(mktemp -d)
MYSQL_HOME=$DEMO_HOME/mysql
mkdir -p $MYSQL_HOME
WORDPRESS_HOME=$DEMO_HOME/wordpress
mkdir -p $WORDPRESS_HOME
```
### 下载 resources
下载 WordPress 的 resources 和 `kustomization.yaml`
<!-- @downloadResources @test -->
```bash
CONTENT="https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
/master/examples/wordpress/wordpress"
curl -s -o "$WORDPRESS_HOME/#1.yaml" \
"$CONTENT/{deployment,service,kustomization}.yaml"
```
下载 MySQL 的 resources 和 `kustomization.yaml`
<!-- @downloadResources @test -->
```bash
CONTENT="https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
/master/examples/wordpress/mysql"
curl -s -o "$MYSQL_HOME/#1.yaml" \
"$CONTENT/{deployment,service,secret,kustomization}.yaml"
```
### 创建 kustomization.yaml
基于 `wordpress``mysql` 的两个 bases 创建一个新的 `kustomization.yaml`
<!-- @createKustomization @test -->
```bash
cat <<EOF >$DEMO_HOME/kustomization.yaml
resources:
- wordpress
- mysql
namePrefix: demo-
patchesStrategicMerge:
- patch.yaml
EOF
```
### 下载 WordPress 的 patchs
在新的 kustomization 中应用 WordPress Deployment 的 patch ,该 patch 包含:
- 添加初始容器来显示mysql的服务名称
- 添加允许 wordpress 查找到 mysql 数据库的环境变量
<!-- @downloadPatch @test -->
```bash
CONTENT="https://raw.githubusercontent.com\
/kubernetes-sigs/kustomize\
/master/examples/wordpress"
curl -s -o "$DEMO_HOME/#1.yaml" \
"$CONTENT/{patch}.yaml"
```
该 patch 内容如下:
> ```yaml
> apiVersion: apps/v1beta2
> kind: Deployment
> metadata:
> name: wordpress
> spec:
> template:
> spec:
> initContainers:
> - name: init-command
> image: debian
> command:
> - "echo $(WORDPRESS_SERVICE)"
> - "echo $(MYSQL_SERVICE)"
> containers:
> - name: wordpress
> env:
> - name: WORDPRESS_DB_HOST
> value: $(MYSQL_SERVICE)
> - name: WORDPRESS_DB_PASSWORD
> valueFrom:
> secretKeyRef:
> name: mysql-pass
> key: password
> ```
初始化容器的命令需要依赖于k8s资源对象字段的信息由占位符变量 $(WORDPRESS_SERVICE) 和 $(MYSQL_SERVICE) 表示。
### 将变量绑定到k8s对象字段
<!-- @addVarRef @test -->
```bash
cat <<EOF >>$DEMO_HOME/kustomization.yaml
vars:
- name: WORDPRESS_SERVICE
objref:
kind: Service
name: wordpress
apiVersion: v1
fieldref:
fieldpath: metadata.name
- name: MYSQL_SERVICE
objref:
kind: Service
name: mysql
apiVersion: v1
EOF
```
`WORDPRESS_SERVICE` 来自 `wordpress` 服务的 `metadata.name` 字段。如果不指定 `fieldref` ,则使用默认的 `metadata.name` 。因此 `MYSQL_SERVICE` 来自 `mysql` 服务的 `metadata.name` 字段。
### 替换
运行命令查看替换结果:
<!-- @kustomizeBuild @test -->
```bash
kustomize build $DEMO_HOME
```
预期的输出为:
> ```yaml
> (truncated)
> ...
> initContainers:
> - command:
> - echo demo-wordpress
> - echo demo-mysql
> image: debian
> name: init-command
>
> ```

46
go.mod
View File

@@ -1,38 +1,32 @@
module sigs.k8s.io/kustomize
module sigs.k8s.io/kustomize/v3
go 1.12
require (
github.com/PuerkitoBio/purell v1.1.0 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/emicklei/go-restful v2.9.3+incompatible // indirect
github.com/evanphx/json-patch v3.0.0+incompatible
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa // indirect
github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d // indirect
github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee
github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9 // indirect
github.com/gogo/protobuf v1.0.0 // indirect
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf // indirect
github.com/googleapis/gnostic v0.1.0 // indirect
github.com/emicklei/go-restful v2.9.6+incompatible // indirect
github.com/evanphx/json-patch v4.5.0+incompatible
github.com/go-openapi/spec v0.19.2
github.com/gogo/protobuf v1.2.1 // indirect
github.com/golang/protobuf v1.3.1 // indirect
github.com/google/gofuzz v1.0.0 // indirect
github.com/googleapis/gnostic v0.3.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece // indirect
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81 // indirect
github.com/json-iterator/go v1.1.6 // indirect
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481 // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/pkg/errors v0.8.1
github.com/spf13/cobra v0.0.2
github.com/spf13/pflag v1.0.1
github.com/stretchr/testify v1.3.0 // indirect
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3 // indirect
github.com/spf13/pflag v1.0.3
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
golang.org/x/sys v0.0.0-20190621203818-d432491b9138 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.2.1
k8s.io/api v0.0.0-20180510062335-53d615ae3f44
k8s.io/apimachinery v0.0.0-20180510061931-13b73596e4b6
k8s.io/client-go v7.0.0+incompatible
k8s.io/kube-openapi v0.0.0-20180510204742-b3f03f553288
gopkg.in/yaml.v2 v2.2.2
k8s.io/api v0.0.0-20190313235455-40a48860b5ab
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1
k8s.io/client-go v11.0.0+incompatible
k8s.io/klog v0.3.3 // indirect
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208
sigs.k8s.io/yaml v1.1.0
)

153
go.sum
View File

@@ -1,84 +1,132 @@
github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
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/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful v2.9.3+incompatible h1:2OwhVdhtzYUp5P5wuGsVDPagKSRd9JK72sJCHVCXh5g=
github.com/emicklei/go-restful v2.9.3+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v3.0.0+incompatible h1:l91aby7TzBXBdmF8heZqjskeH9f3g7ZOL8/sSe+vTlU=
github.com/evanphx/json-patch v3.0.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1/igzWB8OmvPgoYX7w=
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.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/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa h1:hr8WVDjg4JKtQptZpzyb196TmruCs7PIsdJz8KAOZp8=
github.com/go-openapi/jsonpointer v0.0.0-20180322222829-3a0015ad55fa/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d h1:k3UQ7Z8yFYq0BNkYykKIheY0HlZBl1Hku+pO9HE9FNU=
github.com/go-openapi/jsonreference v0.0.0-20180322222742-3fb327e6747d/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee h1:eo0HQoNFtbiEc7+1gRF9pgW6azx8a1cO2fXcqq1MuD0=
github.com/go-openapi/spec v0.0.0-20180415031709-bcff419492ee/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9 h1:+vsw187FKvA2QUGAcE+vQSfyxqLbUXixPYRRMAzwu04=
github.com/go-openapi/swag v0.0.0-20180405201759-811b1089cde9/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2 h1:A9+F4Dc/MCNB5jibxf6rRvOvR/iFgQdyNx9eIhnGqq0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.19.2 h1:SStNd1jRcYtfKCN7R0laGNs80WYYvn5CbBjM2sOmCrE=
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2 h1:jvO6bCMBEilGwMfHhrd61zIID4oIFdwb76V17SM88dE=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
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/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
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/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece h1:3HJXp/18JmMk5sjBP3LDUBtWjczCvynxaeAF6b6kWp8=
github.com/json-iterator/go v0.0.0-20180315132816-ca39e5af3ece/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856 h1:hOnidOuIWNsFRPcxxStGeN3NNm4n4+w6KJ9cVJIh70o=
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3 h1:/UewZcckqhvnnS0C6r3Sher2hSEbVmM6Ogpcjen08+Y=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481 h1:IaSjLMT6WvkoZZjspGxy3rdaTEmWLoRm49WbtVUi9sA=
github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
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-20180228065516-1df9eeb2bb81 h1:ImOHKpmdLPXWX5KSYquUWXKaopEPuY7TPPUo18u9aOI=
github.com/modern-go/reflect2 v0.0.0-20180228065516-1df9eeb2bb81/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da h1:ZQGIPjr1iTtUPXZFk8WShqb5G+Qg65VHFLtSvmHh+Mw=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/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/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
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.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.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.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/cobra v0.0.2 h1:NfkwRbgViGoyjBKsLI0QMDcuMnhM+SBg3T0cGfpvKDE=
github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-20190225153610-fe579d43d832 h1:2IdId8zoI92l1bUzjAOygcAOkmCe13HY1j0rqPPPzB8=
golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190621203818-d432491b9138 h1:t8BZD9RDjkm9/h7yYN6kE8oaeov5r9aztkB7zKA5Tkg=
golang.org/x/sys v0.0.0-20190621203818-d432491b9138/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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d h1:bt+R27hbE7uVf7PY9S6wpNg9Xo2WRe/XQT0uGq9RQQw=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3 h1:sU3tSV6wDhWsvf9NjL0FzRjgAmYnQL5NEhdmcN16UEg=
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59 h1:QjA/9ArTfVTLfEhClDCG7SGrZkZixxWpwNCDiwJfh88=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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=
@@ -87,13 +135,20 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.0.0-20180510062335-53d615ae3f44 h1:zQ8YhMpuc1QJoor+Vm1moP9iEOyaQgOjSj3bo/zUEXE=
k8s.io/api v0.0.0-20180510062335-53d615ae3f44/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20180510061931-13b73596e4b6 h1:pJrzRmry9HLPxkVGMk57cfeGRy/WG0oYXuji9t4zD1M=
k8s.io/apimachinery v0.0.0-20180510061931-13b73596e4b6/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v7.0.0+incompatible h1:kiH+Y6hn+pc78QS/mtBfMJAMIIaWevHi++JvOGEEQp4=
k8s.io/client-go v7.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/kube-openapi v0.0.0-20180510204742-b3f03f553288 h1:AhFqcaw5JbAAaZHxTe1fT+Jtek0pZmIwwt6FbsMA9to=
k8s.io/kube-openapi v0.0.0-20180510204742-b3f03f553288/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.0.0-20190313235455-40a48860b5ab h1:DG9A67baNpoeweOy2spF1OWHhnVY5KR7/Ek/+U1lVZc=
k8s.io/api v0.0.0-20190313235455-40a48860b5ab/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1 h1:IS7K02iBkQXpCeieSiyJjGoLSdVOv2DbPaWHJ+ZtgKg=
k8s.io/apimachinery v0.0.0-20190313205120-d7deff9243b1/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
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 v0.3.3 h1:niceAagH1tzskmaie/icWd7ci1wbG7Bf2c6YGcQv+3c=
k8s.io/klog v0.3.3/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208 h1:5sW+fEHvlJI3Ngolx30CmubFulwH28DhKjGf70Xmtco=
k8s.io/kube-openapi v0.0.0-20190603182131-db7b694dc208/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -6,11 +6,11 @@ package loadertest
import (
"log"
"sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/loader"
"sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/kustomize/pkg/validators"
"sigs.k8s.io/kustomize/v3/pkg/fs"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/loader"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/v3/pkg/validators"
)
// FakeLoader encapsulates the delegate Loader and the fake file system.

428
internal/tools/README.md Normal file
View File

@@ -0,0 +1,428 @@
## What is this?
### In short
Be the GoDoc.org of k8s configuration files.
### More explicitly
Support k8s document indexing from open-source configurations in order to make
it easy for people to learn to use a new feature, explore k8s configs in a
central hub, and see some metrics about kustomize use.
We want people to be able to support three main classes of queries:
1. Structured document queries: how should I use the following fields
- Grace periods: `spec:template:spec:terminationGracePeriod`?
- Kustomize inline patch: `patches:patch`?
2. Key value queries: how should I use this more specific use case of a
structure configuration.
- HorizontalPodAutoScalers: `kind=HorizontalPodAutoScaler`?
- Patches on StatefulSets: `patches:target:kind=StatefulSet`?
3. Full text search: search the comments and the document text from any
type of k8s config file.
## Road map
There is a lot that can be added in order to improve the state of this
application. Some more details along with general thoughts and comments can be
found in the Roadmap.md file in this directory. This README contains only
what can be considered as mostly complete and iterable parts of this project.
## Running this project
Everything is configured using kubernetes, so it should be easy for people to
spin this up on any k8s cluster. Everything should just work (TM).
The config files live in the `config` directory.
```
config
├── base
│   └── kustomization.yaml
├── crawler
│   ├── base
│   │   ├── github_api_secret.txt
│   │   └── kustomization.yaml
│   ├── cronjob
│   │   ├── cronjob.yaml
│   │   └── kustomization.yaml
│   └── job
│   ├── job.yaml
│   └── kustomization.yaml
├── elastic
│   └── ...
├── redis
│   ├── document_keystore
│   │   ├── kustomization.yaml
│   │   ├── redis.yaml
│   │   └── service.yaml
│   └── http_cache
│      ├── kustomization.yaml
│      ├── redis.yaml
│      └── service.yaml
├── webapp
│ ├── backend
│ │   ├── deployment.yaml
│ │   ├── kustomization.yaml
│ │   └── service.yaml
│ └── frontend
   ├── deployment.yaml
   ├── kustomization.yaml
   └── service.yaml
└── schema_files
   └── kustomization_index
      ├── es_index_mappings.json
      └── es_index_settings.json
```
To get everything up and running you have to:
1. Get some instance of elasticsearch working... and configure the
configmapGenerator in `config/base` to point to the right endpoint(s). The
configurations that need this value to be populated are the following:
- `config/crawler/cronjob` to run periodic crawls.
- `config/crawler/job` to run crawls on demand.
- `config/webapp/backend` to run the search server.
2. Configure the elasticsearch indices:
```
kustomize build config/schema_files/kustomization_index | kubectl apply -f -
```
This will run a `curl` command that reads json data from a ConfigMap. This will
setup the schema. If you want to make more complex modifications to the
schema, you should refer to the elastic docs to figure out whether the mapping
can be added to the current index, or whether you will need to copy the
existing index into a different one with the appropriate mappings. Modifications
can be made by using the elasticsearch go library and writing a simple program,
or it can be made with any http command to the appropriate server endpoint from
within the cluster. Unfortunately I did not have the time to write a few helper
tools for this. Feel free to contact me if you need help with modifying
elasticsearch configs, I'm by no means an expert, but I can try to help.
3. (Optional) run the redis http chache for the crawler:
```
kubectl apply -k config/redis/http_cache
```
This will create a deployment for the cache, and a service. The crawler should
be configured to connect to the `http_cache` if it exists, but you can always
check the logs to make sure it connects, and that the identifiers match in the
crawler configuration and for the service endpoint.
The please be aware that the cache does not have a persistent volume.
4. Configure the main redis instance:
```
kubectl apply -k config/redis/document_keystore
```
This will create a StatefulSet with a volume of 4GiB for a redis instance.
5. Get an access token from GitHub.
To be able to kindly ask GitHub for it's data on k8s config files, you'll need
to create an access\_token. From my understanding, this is the only way to do
these code search queries (without first specifying a repository).
To generate a token, go to your GitHub's account in Settings > Developer
Settings > Personal access tokens. It should look like this.
![GitHub Token 1](
https://sigs.k8s.io/kustomize/internal/tools/pictures/github_token.png)
From here you want to generate a new token and have the following
configuration:
![GitHub Token 1](
https://sigs.k8s.io/kustomize/internal/tools/pictures/token_config.png)
If you have uses for any other data from this token, (org data, or something
else) you can pick and choose, but be careful since it can grant this
application access to your notifications, etc. However, any such extension
is explicitly a non-goal and would not be maintained by this project.
6. Launch the crawler:
```
kustomize build config/crawler/cronjob | kubectl apply -f -
```
This will periodically run the crawler every day according to the cron timing
rules in the cronjob.yaml file.
Instead, to get the crawler running now, you can run:
```
kustomize build config/crawler/cronjob | kubectl apply -f -
```
which will launch a non-periodic version of the crawler. It will take a few
minutes for the crawler to split the search, but then config files should
start to get populated within 20 minutes. It may take a while to do the
first crawl, since it has to fetch rate-limited endpoints for each new file it
finds. It should get significantly faster to update in the future.
5. Launch the search backend
```
kustomize build config/webapp/backend | kubectl apply -f -
```
6. Launch the search frontend
```
kustomize build config/webapp/frontend | kubectl apply -f -
```
## Notes about the components
### Elasticsearch
I will add a basic working setup soon. I just did the lazy thing and used an
already packaged solution. Most clouds will provide their own elastic
environments, however, Elasticsearch is also working on their own
implementation of a
![k8s operator](https://www.elastic.co/elasticsearch-kubernetes), which might
be worth checking out. Please note that it comes with its own license
agreement.
### Redis
There are two Redis instances that are used in this application.
One of them is configured to have on disk persistence, so make sure to have
that set up in your kubernetes cluster. Also note that it is running on a
single master node (i.e. it does not automatically shard keys to multiple head
nodes as part of a highly available cluster). Since it's storing a sparse
graph, I can't imagine this being much of an issue, but it's probably worth
mentioning.
The other Redis instance is running as a HTTP (RFC 7234) cache for etags from
GitHub (or any other document store from which we could crawl/index). This one
does not require full persistent storage on disk. The caching strategy is an
LRU cache which is probably a good starting point. It might be worth it to
investigate other cache policies, but I think LRU will work well since
documents may or may not expire anyway, and the amount of memory allocated for
keys is fairly large, so eviction of frequently used documents seems unlikely
anyway.
### Nginx + Angular
There is a Dockerfile included for generating the container image with Nginx
(using the default package) and adding all of the supporting compiled angular
files. Any modifications to the code-base should be compatible with this setup,
so all that's needed is to rebuild the container image, and possibly modify
the image tags in the k8s file.
### Supporting Go binaries
There are a few go binaries that each have their own Dockerfile to build
containers in which to run them on k8s, namely the crawler and the search
service. Their configurations are not optimal (read: needs to be cleaned up),
but they are functional.
## Technical details
### Overall design and imlpementation
There are a few components that are all running together in order to get
the overall application to work smoothly. This section will provide a brief
overview of each component with the following sections going into more details.
The overall structure is outlined in the following figure:
![overview](
https://sigs.k8s.io/kustomize/internal/tools/pictures/sys_arch.png)
#### Crawler
The leftmost component consists of a crawler with an http cache of GitHub
queries does two things, it first looks at the list of documents in
elasticsearch and tries to update them. In doing so, it maintains a set of
newly updated files to exclude them from other parts of the crawl.
To find newly added documents, the crawler crawls any new dependencies
introduced in the document updating step and it also queries GitHub for the
most recently indexed kustomization.\* files. Each new file will be processed
for efficient text queries and put into the document index. Any new dependency
will also incur more crawl operations. Finally, a graphical
representation of the documents and their dependencies is built in Redis to be
used for graph algorithms such as PageRank and component analysis.
#### Data library
There are a few helper libaries for dealing with Elasticsearch, Redis and
documents. This is not persistent, nor is it centralized. They act as small
components that help to package common pieces of code. Eventually it may make
sense to merge all of it together and make a proper persistent model around
this while providing an external API for document insertion/deletion. But
that is definitely out of scope in terms of getting this to run. However
there are limitations with the current model in terms of minimizing the
API surface for the different components of the application. For now this
problem is mostly mitigated by having the query server only connected to
a data node of the Elasticsearch cluster, but the problem of knowing what
is accessible and what isn't is left to the programmer instead of being
clearly and explicitly supported by the API.
#### Server
Uses the data library to communicate with the data store and answer queries.
Processes the user entered text queries into somewhat optimized elasticsearch
queries. Provides a few endpoints to get different metrics and to eventually
allow for registration of remote repositories.
This application has an exposing service in order to allow users of the
application access to queries and the results.
#### Nginx + Angular
Communicates directly with the backend server to forward user queries and
their results. Presents the results on an interface. It's still pretty simple
looking but it seems usable (to me).
### Crawling GitHub
With the use of API keys, GitHub allows account owners to search for files
using their API.
The search endpoints allow for the use of metadata search
that is fairly useful/powerful. For instance they provide a `filename:` keyword
that permits us to look for `kustomization.yaml`, `kustomization.yml`, etc.
This enables the fetching of a list of kustomization documents, from which
we can get the actual content from another endpoint
(raw.githubusercontent.com).
However, the search API is fairly limited. There is a restriction to the number
of documents that can be retrieved from this method. One possible way to
mitigate this would be to periodically query GitHub for results, sorted by the
last indexed time. This would allow you to collect most documents from this
point forwards. The downside to this is that it may require a large number of
requests to their API since you cannot know when new files will be added.
Furthermore, there is a possibility that you would not be able to get all of
files either, depending on the velocity of growth.
The approach that was taken to mitigate this is to use the `filesize:` keyword
and to shard the search space into contiguous buckets of appropriate size in
order to get all of the documents. This is fairly efficient, since you can find
a good enough way to shard the documents in
`lg(max file size) * number of documents / 1000` API queries. Moreover, since
queries are paginated with at most 100 results per query, this solution is
competitive with getting the optimal (non-contiguous) sharding of result sets.
Furthermore, filesize queries can be cached to minimize the total number of
queries called to the API in order to shard the search space. This is done by
querying for file size intervals that always start with 0..X and binary
searching over the `filesize:` space. This will allow you to reuse a lot of
queries when you're looking for the next range, since it is upper bounded and
lower bounded to a smaller number of queries within a range that has also been
queried. I think this is only true because filesizes are power law distributed,
so searches will typically require less queries as they progress from left to
right.
However, this method in no way depends on intervals of the form 0..X, as
the number of documents in the many intervals of the range search could be
added together to also make this work. This approach just seemed simpler to
implement, maintain, and debug so it was preferred.
To get an idea of how efficient this method is, to shard the search space of
7000 documents, it will only take ~90 API range queries which should only take
a few minutes. While actually fetching the documents and their relevant
metadata (creation time, etc.) will take several hours. Furthermore, this
could be made more efficient if a prior distribution is approximated.
This prior could be scaled to the number of documents that need to be fetched,
and then finding a shard that has an adequate number of requests, will only
take a few queries per shard. It could probably be supported in a constant
number of size queries if the size of each shard is halved which shouldn't
have terrible performance impact for the retrieval. However, there where
more pressing things to implement. I might revisit this later.
### Document Indexing and Processing
In order to support simple text queries the structured documents must be
processed in some way that makes searching them easy. The current method
is to recursively traverse the map of configurations to generate each sub-path
and each key-value pair for the leaf nodes of the recursion tree.
However, note that this means that a document has to be valid yaml/json
format in order for indexing to happen. The rest of the document is treated
as mostly text and uses default text settings from Elasticsearch.
What this means is that for the following yaml document:
```yaml
resources:
- service.yaml
- deployment.yaml
configmapGenerator:
- name: app-configuration
files:
- config.yaml
patchesJson6902:
- target:
version: v1
kind: StatefulSet
name: ss-name
path: ss-patch.yaml
- target:
version: v1
kind: Deployment
name: dep-name
path: dep-patch.yaml
```
the following flattened structure would look like:
```json
{
"identifiers": [
"resources",
"configmapGenerator",
"configmapGenerator:name",
"configmapGenerator:files",
"patchesJson6902",
"patchesJson6902:target",
"patchesJson6902:target:version",
"patchesJson6902:target:kind",
"patchesJson6902:target:name",
"patchesJson6902:path",
],
"values": [
"resources=service.yaml"
"resources=deployment.yaml"
"configmapGenerator:name=app-configuration"
"configmapGenerator:files=config.yaml"
"patchesJson6902:target:version=v1",
"patchesJson6902:target:kind=StatefulSet",
"patchesJson6902:target:name=ss-name",
"patchesJson6902:path=ss-patch.yaml",
"patchesJson6902:target:kind=Deployment",
"patchesJson6902:target:name=dep-name",
"patchesJson6902:path=dep-patch.yaml",
],
...
}
```
Note that unique paths and values are deduplicated.
On the search side, exact queries will be prioritized, but the document paths
and key=value pairs will also be analyzed with 3-grams to have some amount of
fuzzy search. The reason that a Levenshtein-Distance was not used instead, is due
to searching multiple fields at the same time, which is a use case where
Elasticsearch does not support proper fuzzy searching.
### Document Search
Given a text query, each token is considered separately. Each token will be fed
through a handful of analyzers on the Elasticsearch side, and will be compared
with the reverse document index of each document fields. It will then determine
the best matching documents. Text ordering is largely insignificant. This makes
sense for the structured search, but may leave room for improvement for the
text only search within the document.
Each token _must_ be matched, so each white space character acts as a
conjunction of individual queries. There are also ways of telling
Elasticsearch that some things _should_ match, but I think for now it makes
more sense to leave it as is.
I think this behavior is sufficient to make the search feel fairly intuitive
while providing support for fairly complex use cases.
### Metrics Computation
From the each kustomization document that is indexed, we can find it's
resources that are publicly available. This includes other kustomizations.
From this, we can build a directed graph of dependencies and reverse
dependencies.
This opens up the possibility to add a plethora of graph metrics that can
give the project maintainers feedback and insight into how people are using
their tools.
Some of these are useful such as getting an idea for how large the dependency
graphs actually grow in practice, and can be used to find _popular_
kustomizations within the corpus. This lends itself to implementing PageRank
to help bubble up popular results as good search results. I unfortunately
did not have the time to implement the algorithm, but I do plan to revisit
this sometime soon to add a few good and efficient implementations of useful
graph algorithms that would be useful to have. See the Roadmap.md for a more
complete list of features that could be added and how I think they could be
implemented.

176
internal/tools/ROADMAP.md Normal file
View File

@@ -0,0 +1,176 @@
# Road map and comments about this work
From working on this project, here is a collection of thoughts and suggestions
for future improvements. For any questions about this, or to request help do
not hesitate to contact @damienr74 on GitHub, my email should be listed.
I think this project has the potential for the K8s community to promote best
practices. If this becomes popular, It could become easier to find
*subjectively good* configurations. This can act as a way to guide newcomers
to k8s config features that are easy to maintain, practical, and tested in some
real world environment. However, a lot of work remains to be made if this is
to happen. Extracting and ranking semantic-level information from the open
source configuration files, is definitely not trivial, and will require a lot of
though and consideration from the experts and the patterns that successful k8s
project follow. This, is outside of my scope having little to no experience with
k8s other than working on this project; however, if you have ideas I can
probably suggest approaches in order to implement it, having worked a lot on
this project.
### Improving configuration files and container configs
I did not have a lot of time to refactor the images to use configmaps for
everything. This is a good thing to improve, should be fairly easy. Another
thing that could make the user experience of launcing this could be to make all
of the go utilities be subcommands to the same binary/container image. This
would reduce the number of things that would have to be rebuilt, in order to get
it running, and it would make the application (and its components) more self
contained. (also has some disadvantages, so I'll let someone else decide.
### Adding graph metrics
From the Redis graph representation, we are able to run a multitude of graph
algorithms (not all of which are implemented).
The simplest one would be to run kruskal's algorithm to find connected
components, and to compute graph metrics on each component. Here are some of the
metrics that may be useful:
+ Average size and histograms of the sizes of each components.
+ Average size and histograms of the node with the highest in degree (rdeps) of
each component.
+ Average size and histograms of the number of repositories in a connected
component.
+ Any other metric that may be helpful to measure the scale of the kustomize
import graph.
Another cool thing that may be helpful, would be to output the graph
representation of deps/rdeps. This should be fairly easy to do with graphviz/dot
so if anyone really wants this, I (damienr74) should be able to do it. Feel free
to send me an email or to @ mention me in an issue.
Note: dfs could also be used to find connected components, but I think union
find is preferable, since the results can be stored and modified very
efficiently. The only challenging part would be to implement deleting of edges
and nodes from a component efficiently, but I know it is possible to support
these operations with a union find structure.
### Implementing PageRank
The graph is set up to be able to efficiently compute PageRank since the edge
weights are real valued, and the graph representation is sparse which means that
it will fit in the memory of a single machine which will make the processing
much more efficient.
It could also be implemented as a Redis script, but I feel like there's
something fundamentally wrong with implementing PageRank in lua. :P
### Implement feature tracking
Each day, when the crawler finds and indexes these structured documents,
it should insert aggregate data to a separate index. This data could look like the
following:
```json
{
"kind": "kustomization",
"added_identifiers": [
{
"identifier": "some:new:k8s:feature",
"addedIn": [
"docID1",
"docID100",
"docID45",
...
],
}
{
"identifier": "another:k8s:feature",
"documents": [
...
],
}
...
]
"removed_identifiers": [
{
"identifier": "some:deprecated:field",
"documents": [
...
]
}
]
}
```
This would make it fairly easy to get deep insight into:
- the speed at which things can effectively be deprecated.
- how many people are migrating to current best practices.
- how many documents get updated frequently/rarely.
- detailed cross sections of growth/regression over conjunctions of features.
- a world of possibilities.
This is also something that I would be interested to work on sometime soon, so
feel free to contact me (damienr74) or ask questions about this.
As needed, it could be a good idea to also aggregate past data with a larger
granularity. for instance each month, the past 30 days can be aggregated into
weekish durations, And every year these weekly aggregations can be converted
into monthly summaries depending on how much data this ends up being, and how
much you want to pay for the storage of this data.
Another cool way to compress this data would be to dynamically compress this
data into a logarithmic number of buckets with decreasing granularity. But it
seems like overkill for the amount of data that we'd likely get.
### The UI probably needs a lot of work
I'm not much of a UI/UX person and have little to no experience in developing
these types of applications. If anyone with Angular experience wants to dive in
and completely restructure the app to make the UI/UX/Code health better that
would be greatly appreciated.
### Query tuning probably still has to be adjusted
I'm also not an expert in Elasticsearch. From what I could read in the docs,
I think I've made sane decisions in converting user queries into meaningful
Elasticsearch queries, but I'm sure there are a lot of improvements that remain
to be done in order to get more accurate results.
### Some other signals that indicate the presence of a good configuration file
There are lots of heuristics that could be used to achieve this. Here are a
couple in no particular order:
+ Penalize for the number of yaml `---` document splits. I'm not sure what the
general consensus is, but I think it's better to separate them, since it
makes git commits less noisy, it's a trivial transformation, and it makes
config files smaller. However, I can understand the argument that its somewhat
practical to keep an overall view of the configurations together (maybe).
+ Penalize the number of unique identifiers in a structured document. I think
this makes sense, since we don't want to have someone game the search engine
to match documents with every possible path from the k8s docs. PageRank might
help with this to some extent, but with a small corpus it would be fairly easy
to game.
+ Assign weights to the usefulness of certain fields. It would be good to
promote documents that use `keyRefFromConfigMap`, liveness probes, etc.
These are the main ones I can think of, but I'm sure there are a *ton* of
ways to achieve this.
If the corpus gets large enough, we might even be able to use *blockchains*,
*machine learning*, and maybe even self-driving cars.
### Add more support for indexing of other k8s/kustomize related data
One thing that jumps to mind is the use of kustomize plugins. They are easy
to track since they all have an unused global variable: `var KustomizePluggin`
it would be easy to run the pluginator command and generate godocs for each
go file with this unique identifier.
For the sake of completeness, here is the full GitHub query that we can use to
find these:
`api.github.com/search/code?q=var+KustomizePlugin+extension%3A.go&access_token=access_token`
Godoc will not show much, since most packages will be using package main, but
using pluginator we can make it a properly named package such that Godoc would
actually generate the relevant documentation.

View File

@@ -0,0 +1,195 @@
package server
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strconv"
"strings"
"github.com/gorilla/mux"
"github.com/rs/cors"
"sigs.k8s.io/kustomize/internal/tools/index"
)
type kustomizeSearch struct {
ctx context.Context
// Eventually pIndex *index.PlugginIndex
idx *index.KustomizeIndex
router *mux.Router
log *log.Logger
}
// New server. Creating a server does not launch it. To launch simply:
// srv, _ := NewKustomizeSearch(context.Backgroud())
// err := srv.Serve()
// if err != nil {
// // Handle server issues.
// }
//
// The server has three enpoints, two of which are functional:
//
// /search: processes the ?q= parameter for a text query and
// returns a list of 10 resutls starting from the ?from= value provided,
// with the default being zero.
//
// /metrics: returns overall metrics about the files indexed. Returns
// timeseries data for kustomization files, and returns breakdown of file
// counts by their 'kind' fields
//
// /register: not implemented, but meant as an endpoint for adding new
// kustomization files to the corpus.
func NewKustomizeSearch(ctx context.Context) (*kustomizeSearch, error) {
idx, err := index.NewKustomizeIndex(ctx)
if err != nil {
return nil, err
}
ks := &kustomizeSearch{
ctx: ctx,
idx: idx,
router: mux.NewRouter(),
log: log.New(os.Stdout, "Kustomize server: ",
log.LstdFlags|log.Llongfile|log.LUTC),
}
return ks, nil
}
// Set up common middleware and the routes for the server.
func (ks *kustomizeSearch) routes() {
// Setup middleware.
ks.router.Use(func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
handler.ServeHTTP(w, r)
})
})
ks.router.HandleFunc("/liveness", ks.liveness()).Methods(http.MethodGet)
ks.router.HandleFunc("/readiness", ks.readiness()).Methods(http.MethodGet)
ks.router.HandleFunc("/search", ks.search()).Methods(http.MethodGet)
ks.router.HandleFunc("/metrics", ks.metrics()).Methods(http.MethodGet)
ks.router.HandleFunc("/register", ks.register()).Methods(http.MethodPost)
}
// Start listening and serving on the provided port.
func (ks *kustomizeSearch) Serve(port int) error {
ks.routes()
handler := cors.Default().Handler(ks.router)
s := &http.Server{
Addr: fmt.Sprintf(":%d", port),
Handler: handler,
// Timeouts/Limits
}
return s.ListenAndServe()
}
// /liveness endpoint
func (ks *kustomizeSearch) liveness() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
}
// /readyness endpoint
func (ks *kustomizeSearch) readiness() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
opt := index.KustomizeSearchOptions{}
_, err := ks.idx.Search("", opt)
if err != nil {
http.Error(w,
`{ "error": "could not connect to database" }`,
http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
}
// /register endpoint.
func (ks *kustomizeSearch) register() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "not implemented", http.StatusInternalServerError)
}
}
// /search endpoint.
func (ks *kustomizeSearch) search() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
values := r.URL.Query()
queries := values["q"]
ks.log.Println("Query: ", values)
var from int
fromParam := values["from"]
if len(fromParam) > 0 {
from, _ = strconv.Atoi(fromParam[0])
if from < 0 {
from = 0
}
}
_, noKinds := values["nokinds"]
opt := index.KustomizeSearchOptions{
SearchOptions: index.SearchOptions{
Size: 10,
From: from,
},
KindAggregation: !noKinds,
}
results, err := ks.idx.Search(strings.Join(queries, " "), opt)
if err != nil {
ks.log.Println("Error: ", err)
http.Error(w, fmt.Sprintf(
`{ "error": "could not complete the query" }`),
http.StatusInternalServerError)
return
}
enc := json.NewEncoder(w)
setIndent(enc)
if err = enc.Encode(results); err != nil {
http.Error(w, `{ "error": "failed to send back results" }`,
http.StatusInternalServerError)
return
}
return
}
}
// metrics endpoint.
func (ks *kustomizeSearch) metrics() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
res, err := ks.idx.Search("", index.KustomizeSearchOptions{
KindAggregation: true,
TimeseriesAggregation: true,
})
if err != nil {
http.Error(w, `{ "error": "could not perform the search."}`,
http.StatusInternalServerError)
return
}
enc := json.NewEncoder(w)
setIndent(enc)
if err := enc.Encode(res); err != nil {
http.Error(w, `{ "error": "could not format return value" }`,
http.StatusInternalServerError)
return
}
}
}
// make json response human readable.
func setIndent(e *json.Encoder) {
e.SetIndent("", " ")
}

View File

@@ -0,0 +1,14 @@
FROM golang:1.11 AS build
ARG GO111MODULE=on
WORKDIR /go/src/sigs.k8s.io/kustomize/internal/tools
COPY . /go/src/sigs.k8s.io/kustomize/internal/tools
RUN go mod download
RUN CGO_ENABLED=0 go install sigs.k8s.io/kustomize/internal/tools/cmd/backend/
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /go/bin/backend /
ENTRYPOINT ["/backend"]

View File

@@ -0,0 +1,30 @@
package main
import (
"context"
"log"
"os"
"strconv"
"sigs.k8s.io/kustomize/internal/tools/backend"
)
func main() {
portStr := os.Getenv("PORT")
port, err := strconv.Atoi(portStr)
if portStr == "" || err != nil {
log.Fatalf("$PORT(%s) must be set to an integer\n", portStr)
}
ctx := context.Background()
ks, err := server.NewKustomizeSearch(ctx)
if err != nil {
log.Fatalf("Error creating kustomize server: %v", ks)
}
err = ks.Serve(port)
if err != nil {
log.Fatalf("Error while running server: %v", err)
}
}

View File

@@ -0,0 +1,6 @@
configmapGenerator:
- name: elasticsearch-config
literals:
- es-url="http://esbasic-master:9200"
- kustomize-index-name="kustomize"
- plugin-index-name="plugin"

View File

@@ -0,0 +1,13 @@
resources:
- ../../base
configmapGenerator:
- name: crawler-http-cache
literals:
- redis-cache-url="redis://redis-http-cache:6379"
secretGenerator:
- name: github-access-token
files:
- token=github_api_secret.txt

View File

@@ -0,0 +1,30 @@
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: crawler
spec:
schedule: "5 0 * * */1"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: crawler
image: gcr.io/kustomize-search/crawler:latest
env:
- name: GITHUB_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: github-access-token
key: token
- name: ELASTICSEARCH_URL
valueFrom:
configMapKeyRef:
name: elasticsearch-config
key: es-url
- name: REDIS_URL
valueFrom:
configMapKeyRef:
name: crawler-http-cache
key: redis-cache-url

View File

@@ -0,0 +1,3 @@
resources:
- ../base
- cronjob.yaml

View File

@@ -0,0 +1,32 @@
apiVersion: batch/v1
kind: Job
metadata:
name: crawler
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: crawler
image: gcr.io/kustomize-search/crawler:latest
env:
- name: GITHUB_ACCESS_TOKEN
valueFrom:
secretKeyRef:
name: github-access-token
key: token
- name: ELASTICSEARCH_URL
valueFrom:
configMapKeyRef:
name: elasticsearch-config
key: es-url
- name: REDIS_CACHE_URL
valueFrom:
configMapKeyRef:
name: crawler-http-cache
key: redis-cache-url
- name: REDIS_KEY_URL
valueFrom:
configMapKeyRef:
name: redis-keystore
key: keystore-url

View File

@@ -0,0 +1,3 @@
resources:
- ../base
- job.yaml

View File

@@ -0,0 +1,7 @@
resources:
- redis.yaml
- service.yaml
commonLabels:
app: redis
tier: document-keystore

View File

@@ -0,0 +1,37 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-docs-keystore
spec:
serviceName: "redis-docs-keystore"
template:
spec:
containers:
- name: redis
image: redis:5-alpine
imagePullPolicy: Always
args:
- "--save"
- "900"
- "1"
- "--save"
- "30"
- "100"
- "--appendonly"
- "yes"
ports:
- name: redis-docs-port
containerPort: 6379
volumeMounts:
- mountPath: /data
name: redis-docs-keystore-data
restartPolicy: Always
volumeClaimTemplates:
- metadata:
name: redis-docs-keystore-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 4Gi

View File

@@ -0,0 +1,10 @@
apiVersion: v1
kind: Service
metadata:
name: redis-docs-keystore
spec:
clusterIP: None
ports:
- protocol: "TCP"
port: 6379
targetPort: redis-docs-port

View File

@@ -0,0 +1,7 @@
resources:
- redis.yaml
- service.yaml
commonLabels:
app: redis
tier: http-cache

View File

@@ -0,0 +1,16 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-http-cache
spec:
template:
spec:
containers:
- name: redis
image: redis:5-alpine
imagePullPolicy: Always
# see redis.io/topics/lru-cache for other policy options.
args: ["--maxmemory", "1gb", "--maxmemory-policy", "allkeys-lru"]
ports:
- name: http-cache-port
containerPort: 6379

View File

@@ -0,0 +1,10 @@
apiVersion: v1
kind: Service
metadata:
name: redis-http-cache
spec:
clusterIP: None
ports:
- protocol: "TCP"
port: 6379
targetPort: http-cache-port

View File

@@ -0,0 +1,38 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: kustomize-search
spec:
selector:
matchLabels:
app: kustomize-search
tier: backend
replicas: 1
template:
metadata:
labels:
app: kustomize-search
tier: backend
spec:
containers:
- name: kustomize-search
image: gcr.io/kustomize-search/backend:latest
livenessProbe:
httpGet:
path: /liveness
port: backend-port
readinessProbe:
httpGet:
path: /readiness
port: backend-port
ports:
- name: backend-port
containerPort: 8080
env:
- name: ELASTICSEARCH_URL
valueFrom:
configMapKeyRef:
name: elasticsearch-config
key: es-url
- name: PORT
value: "8080"

View File

@@ -0,0 +1,4 @@
resources:
- ../../base
- deployment.yaml
- service.yaml

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: kustomize-search
spec:
selector:
app: kustomize-search
tier: backend
ports:
- protocol: "TCP"
port: 80
targetPort: backend-port
type: LoadBalancer
loadBalancerIP: ""

View File

@@ -0,0 +1,22 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: kustomize-search-ui
spec:
selector:
matchLabels:
app: kustomize-search
tier: frontend
replicas: 1
template:
metadata:
labels:
app: kustomize-search
tier: frontend
spec:
containers:
- name: frontend
image: gcr.io/kustomize-search/frontend:latest
ports:
- name: frontend-port
containerPort: 80

View File

@@ -0,0 +1,4 @@
resources:
- ../../base
- deployment.yaml
- service.yaml

View File

@@ -0,0 +1,14 @@
apiVersion: v1
kind: Service
metadata:
name: kustomize-search-ui
spec:
selector:
app: kustomize-search
tier: frontend
ports:
- protocol: "TCP"
port: 80
targetPort: frontend-port
type: LoadBalancer
loadBalancerIP: ""

View File

@@ -0,0 +1,236 @@
// Package crawler provides helper methods and defines an interface for lauching
// source repository crawlers that retrieve files from a source and forwards
// to a channel for indexing and retrieval.
package crawler
import (
"context"
"fmt"
"log"
"os"
"sync"
_ "github.com/gomodule/redigo/redis"
"sigs.k8s.io/kustomize/internal/tools/doc"
)
var (
logger = log.New(os.Stdout, "Crawler: ", log.LstdFlags|log.LUTC|log.Llongfile)
)
// Crawler forwards documents from source repositories to index and store them
// for searching. Each crawler is responsible for querying it's source of
// information, and forwarding files that have not been seen before or that need
// updating.
type Crawler interface {
// Crawl returns when it is done processing. This method does not take
// ownership of the channel. The channel is write only, and it
// designates where the crawler should forward the documents.
Crawl(ctx context.Context, output chan<- CrawlerDocument) error
// Get the document data given the FilePath, Repo, and Ref/Tag/Branch.
FetchDocument(context.Context, *doc.Document) error
// Write to the document what the created time is.
SetCreated(context.Context, *doc.Document) error
Match(*doc.Document) bool
}
type CrawlerDocument interface {
ID() string
GetDocument() *doc.Document
GetResources() ([]*doc.Document, error)
WasCached() bool
}
type CrawlerSeed []*doc.Document
type IndexFunc func(CrawlerDocument, Crawler) error
type Converter func(*doc.Document) (CrawlerDocument, error)
// Cleaner, more efficient, and more extensible crawler implementation.
// The seed must include the ids of each document in the index.
func CrawlFromSeed(ctx context.Context, seed CrawlerSeed,
crawlers []Crawler, conv Converter, indx IndexFunc) {
seen := make(map[string]struct{})
logIfErr := func(err error) {
if err == nil {
return
}
logger.Println("error: ", err)
}
stack := make(CrawlerSeed, 0)
findMatch := func(d *doc.Document) Crawler {
for _, crawl := range crawlers {
if crawl.Match(d) {
return crawl
}
}
return nil
}
addBranches := func(cdoc CrawlerDocument, match Crawler) {
if _, ok := seen[cdoc.ID()]; ok {
return
}
seen[cdoc.ID()] = struct{}{}
// Insert into index
err := indx(cdoc, match)
logIfErr(err)
if err != nil {
return
}
deps, err := cdoc.GetResources()
logIfErr(err)
if err != nil {
return
}
for _, dep := range deps {
if _, ok := seen[dep.ID()]; ok {
continue
}
stack = append(stack, dep)
}
}
doCrawl := func(docsPtr *CrawlerSeed) {
for len(*docsPtr) > 0 {
back := len(*docsPtr) - 1
next := (*docsPtr)[back]
*docsPtr = (*docsPtr)[:back]
match := findMatch(next)
if match == nil {
logIfErr(fmt.Errorf(
"%v could not match any crawler", next))
continue
}
err := match.FetchDocument(ctx, next)
logIfErr(err)
// If there was no change or there is an error, we don't have
// to branch out, since the dependencies are already in the
// index, or we cannot find the document.
if err != nil || next.WasCached() {
continue
}
cdoc, err := conv(next)
logIfErr(err)
if err != nil {
continue
}
addBranches(cdoc, match)
}
}
// Exploit seed to update bulk of corpus.
logger.Printf("updating %d documents from seed\n", len(seed))
doCrawl(&seed)
// Traverse any new links added while updating corpus.
logger.Printf("crawling %d new documents found in the seed\n", len(stack))
doCrawl(&stack)
ch := make(chan CrawlerDocument, 1<<10)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
for cdoc := range ch {
if _, ok := seen[cdoc.ID()]; ok {
continue
}
match := findMatch(cdoc.GetDocument())
if match == nil {
logIfErr(fmt.Errorf(
"%v could not match any crawler", cdoc))
continue
}
addBranches(cdoc, match)
}
}()
// Exploration through APIs.
errs := CrawlerRunner(ctx, ch, crawlers)
if errs != nil {
for _, err := range errs {
logIfErr(err)
}
}
close(ch)
logger.Println("Processing the new documents from the crawlers' exploration.")
wg.Wait()
// Handle deps of newly discovered documents.
logger.Printf("crawling the %d new documents from the crawlers' exploration.",
len(stack))
doCrawl(&stack)
}
// CrawlerRunner is a blocking function and only returns once all of the
// crawlers are finished with execution.
//
// This function uses the output channel to forward kustomization documents
// from a list of crawlers. The output is to be consumed by a database/search
// indexer for later retrieval.
//
// The return value is an array of errors in which each index represents the
// index of the crawler that emitted the error. Although the errors themselves
// can be nil, the array will always be exactly the size of the crawlers array.
//
// Crawler Runner takes in a seed, which represents the documents stored in an
// index somewhere. The document data is not required to be populated. If there
// are many documents, this is preferable. The order of iteration over the seed
// is not garanteed, but the CrawlerRunner does guarantee that every element
// from the seed will be processed before any other documents from the
// crawlers.
func CrawlerRunner(ctx context.Context,
output chan<- CrawlerDocument, crawlers []Crawler) []error {
errs := make([]error, len(crawlers))
wg := sync.WaitGroup{}
for i, crawler := range crawlers {
// Crawler implementations get their own channels to prevent a
// crawler from closing the main output channel.
docs := make(chan CrawlerDocument)
wg.Add(2)
// Forward all of the documents from this crawler's channel to
// the main output channel.
go func(docs <-chan CrawlerDocument) {
defer wg.Done()
for doc := range docs {
output <- doc
}
}(docs)
// Run this crawler and capture its returned error.
go func(idx int, crawler Crawler,
docs chan<- CrawlerDocument) {
defer func() {
wg.Done()
if r := recover(); r != nil {
errs[idx] = fmt.Errorf(
"%+v panicked: %v, additional error %v",
crawler, r, errs[idx],
)
}
}()
defer close(docs)
errs[idx] = crawler.Crawl(ctx, docs)
}(i, crawler, docs) // Copies the index and the crawler
}
wg.Wait()
return errs
}

View File

@@ -0,0 +1,356 @@
package crawler
import (
"context"
"errors"
"fmt"
"reflect"
"sort"
"strings"
"sync"
"testing"
"time"
"sigs.k8s.io/kustomize/internal/tools/doc"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
)
const (
kustomizeRepo = "https://github.com/kubernetes-sigs/kustomize"
)
// Simple crawler that forwards it's list of documents to a provided channel and
// returns it's error to the caller.
type testCrawler struct {
matchPrefix string
err error
docs []doc.KustomizationDocument
lukp map[string]int
}
func (c testCrawler) Match(d *doc.Document) bool {
return d != nil && strings.HasPrefix(d.ID(), c.matchPrefix)
}
func (c testCrawler) FetchDocument(ctx context.Context, d *doc.Document) error {
if i, ok := c.lukp[d.ID()]; ok {
d.DocumentData = c.docs[i].DocumentData
return nil
}
for _, suffix := range pgmconfig.KustomizationFileNames {
fmt.Println(d.ID(), "/", suffix)
i, ok := c.lukp[d.ID()+"/"+suffix]
if !ok {
continue
}
d.FilePath += "/" + suffix
d.DocumentData = c.docs[i].DocumentData
return nil
}
return fmt.Errorf("Document %v does not exist for matcher: %s",
d, c.matchPrefix)
}
func (c testCrawler) SetCreated(ctx context.Context, d *doc.Document) error {
d.CreationTime = &time.Time{}
return nil
}
func newCrawler(matchPrefix string, err error,
docs []doc.KustomizationDocument) testCrawler {
c := testCrawler{
matchPrefix: matchPrefix,
err: err,
docs: docs,
lukp: make(map[string]int),
}
for i, d := range docs {
c.lukp[d.ID()] = i
}
return c
}
// Crawl implements the Crawler interface for testing.
func (c testCrawler) Crawl(ctx context.Context,
output chan<- CrawlerDocument) error {
for i, d := range c.docs {
isResource := true
for _, suffix := range pgmconfig.KustomizationFileNames {
if strings.HasSuffix(d.FilePath, suffix) {
isResource = false
break
}
}
if isResource {
continue
}
output <- &c.docs[i]
}
return c.err
}
// Used to make sure that we're comparing documents in order. This is needed
// since these documents will be sent concurrently.
type sortableDocs []doc.KustomizationDocument
func (s sortableDocs) Less(i, j int) bool {
return s[i].FilePath < s[j].FilePath
}
func (s sortableDocs) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s sortableDocs) Len() int {
return len(s)
}
func TestCrawlerRunner(t *testing.T) {
fmt.Println("testing CrawlerRunner")
tests := []struct {
tc []Crawler
errs []error
docs sortableDocs
}{
{
tc: []Crawler{
testCrawler{
docs: []doc.KustomizationDocument{
{Document: doc.Document{
FilePath: "crawler1/doc1/kustomization.yaml",
}},
{Document: doc.Document{
FilePath: "crawler1/doc2/kustomization.yaml",
}},
{Document: doc.Document{
FilePath: "crawler1/doc3/kustomization.yaml",
}},
},
},
testCrawler{err: errors.New("crawler2")},
testCrawler{},
testCrawler{
docs: []doc.KustomizationDocument{
{Document: doc.Document{
FilePath: "crawler4/doc1/kustomization.yaml",
}},
{Document: doc.Document{
FilePath: "crawler4/doc2/kustomization.yaml",
}},
},
err: errors.New("crawler4"),
},
},
errs: []error{
nil,
errors.New("crawler2"),
nil,
errors.New("crawler4"),
},
docs: sortableDocs{
{Document: doc.Document{
FilePath: "crawler1/doc1/kustomization.yaml",
}},
{Document: doc.Document{
FilePath: "crawler1/doc2/kustomization.yaml",
}},
{Document: doc.Document{
FilePath: "crawler1/doc3/kustomization.yaml",
}},
{Document: doc.Document{
FilePath: "crawler4/doc1/kustomization.yaml",
}},
{Document: doc.Document{
FilePath: "crawler4/doc2/kustomization.yaml",
}},
},
},
}
for _, test := range tests {
output := make(chan CrawlerDocument)
wg := sync.WaitGroup{}
wg.Add(1)
// Run the Crawler runner with a list of crawlers.
go func() {
defer close(output)
defer wg.Done()
errs := CrawlerRunner(context.Background(),
output, test.tc)
// Check that errors are returned as they should be.
if !reflect.DeepEqual(errs, test.errs) {
t.Errorf("Expected errs (%v) to equal (%v)",
errs, test.errs)
}
}()
// Iterate over the output channel of Crawler runner.
returned := make(sortableDocs, 0, len(test.docs))
for o := range output {
d, ok := o.(*doc.KustomizationDocument)
if !ok || d == nil {
t.Errorf("%T not expected type (%T)",
o, d)
}
returned = append(returned, *d)
}
// Check that all documents are received.
sort.Sort(returned)
if !reflect.DeepEqual(returned, test.docs) {
t.Errorf("Expected docs (%v) to equal (%v)\n",
returned, test.docs)
}
wg.Wait()
}
}
func TestCrawlFromSeed(t *testing.T) {
fmt.Println("testing CrawlFromSeed")
tests := []struct {
seed CrawlerSeed
matcher string
corpus []doc.KustomizationDocument
}{
{
seed: CrawlerSeed{
{
RepositoryURL: kustomizeRepo,
FilePath: "examples/helloWorld/kustomization.yaml",
},
{
RepositoryURL: kustomizeRepo,
FilePath: "examples/other/kustomization.yaml",
},
},
matcher: kustomizeRepo,
corpus: []doc.KustomizationDocument{
// Visited from the seed, will be ignored in the crawl.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/helloWorld/kustomization.yaml",
DocumentData: `
resources:
- deployment.yaml
`,
}},
// Also visited from the seed as a relative resource.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/helloWorld/deployment.yaml",
DocumentData: `
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
`,
}},
// Visited from the seed. Has a remote import.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/other/kustomization.yaml",
DocumentData: `
resources:
- https://github.com/kubernetes-sigs/kustomize/examples/other/overlay
- service.yaml
`,
}},
// Imported as a base from the seed.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/other/overlay/kustomization.yaml",
DocumentData: `
resources:
- https://github.com/kubernetes-sigs/kustomize/examples/seedcrawl1
- https://github.com/kubernetes-sigs/kustomize/examples/seedcrawl2
`,
}},
// Imported as a resource from the seed.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/other/service.yaml",
}},
// Visited from crawling seed.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/seedcrawl1/kustomization.yml",
}},
// Visited from crawling seed.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/seedcrawl2/kustomization.yaml",
DocumentData: `
resources:
- ../base
- job.yaml
`,
}},
// Visited from crawling seed.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/base/kustomization.yml",
}},
// Visited from crawling seed imported as resource.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/seedcrawl2/job.yaml",
}},
// Visited from the crawler runner.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/other/base/kustomization.yaml",
DocumentData: `
resources:
- ../app
`,
}},
// Visited from the crawler runner.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/other/app/kustomization.yaml",
DocumentData: `
resources:
- resource.yaml
`,
}},
// Visited from crawling runner imported as resource.
{Document: doc.Document{
RepositoryURL: kustomizeRepo,
FilePath: "examples/other/app/resource.yaml",
}},
},
},
}
for _, tc := range tests {
cr := newCrawler(tc.matcher, nil, tc.corpus)
visited := make(map[string]int)
CrawlFromSeed(context.Background(), tc.seed, []Crawler{cr},
func(d *doc.Document) (CrawlerDocument, error) {
return &doc.KustomizationDocument{
Document: *d,
}, nil
},
func(d CrawlerDocument, cr Crawler) error {
visited[d.ID()]++
return nil
},
)
if lv, lc := len(visited), len(tc.corpus); lv != lc {
t.Errorf("error: %d of %d documents visited.", lv, lc)
t.Errorf("\nvisited (%v)\nexpected (%v).", visited, cr.lukp)
}
for id, cnt := range visited {
if cnt != 1 {
t.Errorf("%s not visited once (%d)", id, cnt)
}
}
}
}

View File

@@ -0,0 +1,582 @@
// Package github implements the crawler.Crawler interface, getting data
// from the Github search API.
package github
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"math"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"time"
"sigs.k8s.io/kustomize/internal/tools/crawler"
"sigs.k8s.io/kustomize/internal/tools/doc"
"sigs.k8s.io/kustomize/internal/tools/httpclient"
"sigs.k8s.io/kustomize/v3/pkg/git"
"sigs.k8s.io/kustomize/v3/pkg/pgmconfig"
)
var logger = log.New(os.Stdout, "Github Crawler: ",
log.LstdFlags|log.LUTC|log.Llongfile)
// Implements crawler.Crawler.
type githubCrawler struct {
client GitHubClient
query Query
}
type GitHubClient struct {
RequestConfig
retryCount uint64
client *http.Client
}
func NewClient(accessToken string, retryCount uint64, client *http.Client) GitHubClient {
return GitHubClient{
retryCount: retryCount,
client: client,
RequestConfig: RequestConfig{
perPage: githubMaxPageSize,
accessToken: accessToken,
},
}
}
func NewCrawler(accessToken string, retryCount uint64, client *http.Client,
query Query) githubCrawler {
return githubCrawler{
client: GitHubClient{
retryCount: retryCount,
client: client,
RequestConfig: RequestConfig{
perPage: githubMaxPageSize,
accessToken: accessToken,
},
},
query: query,
}
}
// Implements crawler.Crawler.
func (gc githubCrawler) Crawl(
ctx context.Context, output chan<- crawler.CrawlerDocument) error {
noETagClient := GitHubClient{
RequestConfig: gc.client.RequestConfig,
client: &http.Client{Timeout: gc.client.client.Timeout},
retryCount: gc.client.retryCount,
}
// Since Github returns a max of 1000 results per query, we can use
// multiple queries that split the search space into chunks of at most
// 1000 files to get all of the data.
ranges, err := FindRangesForRepoSearch(newCache(noETagClient, gc.query))
if err != nil {
return fmt.Errorf("could not split %v into ranges, %v\n",
gc.query, err)
}
logger.Println("ranges: ", ranges)
// Query each range for files.
errs := make(multiError, 0)
for _, query := range ranges {
err := processQuery(ctx, gc.client, query, output)
if err != nil {
errs = append(errs, err)
}
}
if len(errs) > 0 {
return errs
}
return nil
}
func (gc githubCrawler) FetchDocument(ctx context.Context, d *doc.Document) error {
repoURL := d.RepositoryURL + "/" + d.FilePath + "?ref=" + d.DefaultBranch
repoSpec, err := git.NewRepoSpecFromUrl(repoURL)
if err != nil {
return fmt.Errorf("invalid repospec: %v", err)
}
url := "https://raw.githubusercontent.com/" + repoSpec.OrgRepo +
"/" + repoSpec.Ref + "/" + repoSpec.Path
handle := func(resp *http.Response, err error, path string) error {
if err == nil && resp.StatusCode == http.StatusOK {
d.IsSame = httpclient.FromCache(resp.Header)
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
d.DocumentData = string(data)
d.FilePath = d.FilePath + path
return nil
}
return err
}
resp, err := gc.client.GetRawUserContent(url)
if err := handle(resp, err, ""); err == nil {
return nil
}
for _, file := range pgmconfig.KustomizationFileNames {
resp, err = gc.client.GetRawUserContent(url + "/" + file)
err := handle(resp, err, "/"+file)
if err != nil {
continue
}
}
return fmt.Errorf("File Not Found: %s", url)
}
func (gc githubCrawler) SetCreated(ctx context.Context, d *doc.Document) error {
fs := GithubFileSpec{}
fs.Repository.FullName = d.RepositoryURL + "/" + d.FilePath
creationTime, err := gc.client.GetFileCreationTime(fs)
if err != nil {
return err
}
d.CreationTime = &creationTime
return nil
}
func (gc githubCrawler) Match(d *doc.Document) bool {
url := d.RepositoryURL + "/" + d.FilePath + "?ref=" + "/" +
d.DefaultBranch
repoSpec, err := git.NewRepoSpecFromUrl(url)
if err != nil {
return false
}
return strings.Contains(repoSpec.Host, "github.com")
}
// processQuery follows all of the pages in a query, and updates/adds the
// documents from the crawl to the datastore/index.
func processQuery(ctx context.Context, gcl GitHubClient, query string,
output chan<- crawler.CrawlerDocument) error {
queryPages := make(chan GithubResponseInfo)
go func() {
// Forward the document metadata to the retrieval channel.
// This separation allows for concurrent requests for the code
// search, and the retrieval portions of the API.
err := gcl.ForwardPaginatedQuery(ctx, query, queryPages)
if err != nil {
// TODO(damienr74) handle this error with redis?
logger.Println(err)
}
close(queryPages)
}()
errs := make(multiError, 0)
errorCnt := 0
totalCnt := 0
for page := range queryPages {
if page.Error != nil {
errs = append(errs, page.Error)
continue
}
for _, file := range page.Parsed.Items {
k, err := kustomizationResultAdapter(gcl, file)
if err != nil {
errs = append(errs, err)
errorCnt++
continue
}
output <- k
totalCnt++
}
logger.Printf("got %d files out of %d from API. %d of %d had errors\n",
totalCnt, page.Parsed.TotalCount, errorCnt, totalCnt)
}
return errs
}
func kustomizationResultAdapter(gcl GitHubClient, k GithubFileSpec) (
crawler.CrawlerDocument, error) {
data, err := gcl.GetFileData(k)
if err != nil {
return nil, err
}
if err != nil {
logger.Printf(
"(error: %v) initializing to current time.\n", err)
}
url := gcl.ReposRequest(k.Repository.FullName)
defaultBranch, err := gcl.GetDefaultBranch(url)
if err != nil {
logger.Printf(
"(error: %v) setting default_branch to master\n", err)
defaultBranch = "master"
}
doc := doc.KustomizationDocument{
Document: doc.Document{
DocumentData: string(data),
FilePath: k.Path,
DefaultBranch: defaultBranch,
RepositoryURL: k.Repository.URL,
},
}
return &doc, nil
}
// ForwardPaginatedQuery follows the links to the next pages and performs all of
// the queries for a given search query, relaying the data from each request
// back to an output channel.
func (gcl GitHubClient) ForwardPaginatedQuery(ctx context.Context, query string,
output chan<- GithubResponseInfo) error {
logger.Println("querying: ", query)
response := gcl.parseGithubResponse(query)
if response.Error != nil {
return response.Error
}
output <- response
for response.LastURL != "" && response.NextURL != "" {
select {
case <-ctx.Done():
return nil
default:
response = gcl.parseGithubResponse(response.NextURL)
if response.Error != nil {
return response.Error
}
output <- response
}
}
return nil
}
// GetFileData gets the bytes from a file.
func (gcl GitHubClient) GetFileData(k GithubFileSpec) ([]byte, error) {
url := gcl.ContentsRequest(k.Repository.FullName, k.Path)
resp, err := gcl.GetReposData(url)
if err != nil {
return nil, fmt.Errorf("%+v: could not get '%s' metadata: %v",
k, url, err)
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("%+v: could not read '%s' metadata: %v",
k, url, err)
}
resp.Body.Close()
type githubContentRawURL struct {
DownloadURL string `json:"download_url,omitempty"`
}
var rawURL githubContentRawURL
err = json.Unmarshal(data, &rawURL)
if err != nil {
return nil, fmt.Errorf(
"%+v: could not get 'download_url' from '%s' response: %v",
k, data, err)
}
resp, err = gcl.GetRawUserContent(rawURL.DownloadURL)
if err != nil {
return nil, fmt.Errorf("%+v: could not fetch file raw data '%s': %v",
k, rawURL.DownloadURL, err)
}
defer resp.Body.Close()
data, err = ioutil.ReadAll(resp.Body)
return data, err
}
func (gcl GitHubClient) GetDefaultBranch(url string) (string, error) {
resp, err := gcl.GetReposData(url)
if err != nil {
return "", fmt.Errorf(
"'%s' could not get default_branch: %v", url, err)
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf(
"could not read default_branch: %v", err)
}
type defaultBranch struct {
DefaultBranch string `json:"default_branch,omitempty"`
}
var branch defaultBranch
err = json.Unmarshal(data, &branch)
if err != nil {
return "", fmt.Errorf(
"default_branch json malformed: %v", err)
}
return branch.DefaultBranch, nil
}
// GetFileCreationTime gets the earliest date of a file.
func (gcl GitHubClient) GetFileCreationTime(
k GithubFileSpec) (time.Time, error) {
url := gcl.CommitsRequest(k.Repository.FullName, k.Path)
defaultTime := time.Now()
resp, err := gcl.GetReposData(url)
if err != nil {
return defaultTime, fmt.Errorf(
"%+v: '%s' could not get metadata: %v", k, url, err)
}
type DateSpec struct {
Commit struct {
Author struct {
Date string `json:"date,omitempty"`
} `json:"author,omitempty"`
} `json:"commit,omitempty"`
}
_, lastURL := parseGithubLinkFormat(resp.Header.Get("link"))
if lastURL != "" {
resp, err = gcl.GetReposData(lastURL)
if err != nil {
return defaultTime, fmt.Errorf(
"%+v: '%s' could not get metadata: %v",
k, lastURL, err)
}
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return defaultTime, fmt.Errorf(
"%+v: failed to read metadata: %v", k, err)
}
earliestDate := []DateSpec{}
err = json.Unmarshal(data, &earliestDate)
size := len(earliestDate)
if err != nil || size == 0 {
return defaultTime, fmt.Errorf(
"%+v: server response '%s' not in expected format: %v",
k, data, err)
}
return time.Parse(time.RFC3339, earliestDate[size-1].Commit.Author.Date)
}
// TODO(damienr74) change the tickers to actually check api rate limits, reset
// times, and throttle requests dynamically based off of current utilization,
// instead of hardcoding the documented values, these calls are not quota'd.
// This is now especially important, since caching the API requests will reduce
// API quota use (so we can actually make more requests in the allotted time
// period).
//
// See https://developer.github.com/v3/rate_limit/ for details.
var (
searchRateTicker = time.NewTicker(time.Second * 2)
contentRateTicker = time.NewTicker(time.Second * 1)
)
func throttleSearchAPI() {
<-searchRateTicker.C
}
func throttleRepoAPI() {
<-contentRateTicker.C
}
type multiError []error
func (me multiError) Error() string {
size := len(me) + 2
strs := make([]string, size)
strs[0] = "Errors ["
for i, err := range me {
strs[i+1] = "\t" + err.Error()
}
strs[size-1] = "]"
return strings.Join(strs, "\n")
}
type GithubFileSpec struct {
Path string `json:"path,omitempty"`
Repository struct {
API string `json:"url,omitempty"`
URL string `json:"html_url,omitempty"`
FullName string `json:"full_name,omitempty"`
} `json:"repository,omitempty"`
}
type githubResponse struct {
// MaxUint is reserved as a sentinel value.
// This is the number of files that match the query.
TotalCount uint64 `json:"total_count,omitempty"`
// Github representation of a file.
Items []GithubFileSpec `json:"items,omitempty"`
}
type GithubResponseInfo struct {
*http.Response
Parsed *githubResponse
Error error
NextURL string
LastURL string
}
func parseGithubLinkFormat(links string) (string, string) {
const (
linkNext = "next"
linkLast = "last"
linkInfoURL = 1
linkInfoRel = 2
)
next, last := "", ""
linkInfo := regexp.MustCompile(`<(.*)>.*; rel="(last|next)"`)
for _, link := range strings.Split(links, ",") {
linkParse := linkInfo.FindStringSubmatch(link)
if len(linkParse) != 3 {
continue
}
url := linkParse[linkInfoURL]
switch linkParse[linkInfoRel] {
case linkNext:
next = url
case linkLast:
last = url
default:
}
}
return next, last
}
func (gcl GitHubClient) parseGithubResponse(getRequest string) GithubResponseInfo {
resp, err := gcl.SearchGithubAPI(getRequest)
requestInfo := GithubResponseInfo{
Response: resp,
Error: err,
Parsed: nil,
}
if err != nil || resp == nil {
return requestInfo
}
var data []byte
defer resp.Body.Close()
data, requestInfo.Error = ioutil.ReadAll(resp.Body)
if requestInfo.Error != nil {
return requestInfo
}
if resp.StatusCode != http.StatusOK {
logger.Println("query: ", getRequest)
logger.Println("status not OK at the source")
logger.Println("header dump", resp.Header)
logger.Println("body dump", string(data))
requestInfo.Error = fmt.Errorf("request rejected, status '%s'",
resp.Status)
return requestInfo
}
requestInfo.NextURL, requestInfo.LastURL =
parseGithubLinkFormat(resp.Header.Get("link"))
resultCount := githubResponse{
TotalCount: math.MaxUint64,
}
requestInfo.Error = json.Unmarshal(data, &resultCount)
if requestInfo.Error != nil {
return requestInfo
}
requestInfo.Parsed = &resultCount
return requestInfo
}
// SearchGithubAPI performs a search query and handles rate limitting for
// the 'code/search?' endpoint as well as timed retries in the case of abuse
// prevention.
func (gcl GitHubClient) SearchGithubAPI(query string) (*http.Response, error) {
throttleSearchAPI()
return gcl.getWithRetry(query)
}
// GetReposData performs a search query and handles rate limitting for
// the '/repos' endpoint as well as timed retries in the case of abuse
// prevention.
func (gcl GitHubClient) GetReposData(query string) (*http.Response, error) {
throttleRepoAPI()
return gcl.getWithRetry(query)
}
// User content (file contents) is not API rate limited, so there's no use in
// throttling this call.
func (gcl GitHubClient) GetRawUserContent(query string) (*http.Response, error) {
return gcl.getWithRetry(query)
}
func (gcl GitHubClient) getWithRetry(
query string) (resp *http.Response, err error) {
resp, err = gcl.client.Get(query)
retryCount := gcl.retryCount
for err == nil &&
resp.StatusCode == http.StatusForbidden &&
retryCount > 0 {
retryTime := resp.Header.Get("Retry-After")
i, err := strconv.Atoi(retryTime)
if err != nil {
return resp, fmt.Errorf(
"query '%s' forbidden without 'Retry-After'", query)
}
logger.Printf(
"status forbidden, retring %d more times\n", retryCount)
logger.Printf("waiting %d seconds before retrying\n", i)
time.Sleep(time.Second * time.Duration(i))
retryCount--
resp, err = gcl.client.Get(query)
}
if err != nil {
return resp, fmt.Errorf("query '%s' could not be processed, %v",
query, err)
}
return resp, err
}

View File

@@ -0,0 +1,224 @@
package github
import (
"fmt"
"net/url"
"strings"
)
const (
perPageArg = "per_page"
accessTokenArg = "access_token"
githubMaxPageSize = 100
)
// Implementation detail, not important to external API.
type queryField struct {
name string
value interface{}
}
// Formats a query field.
func (qf queryField) String() string {
var value string
switch v := qf.value.(type) {
case string:
value = v
case rangeFormatter:
value = v.RangeString()
default:
value = fmt.Sprint(v)
}
if qf.name == "" {
return value
}
return fmt.Sprint(qf.name, ":", value)
}
// Example of formating a query:
// QueryWith(
// Filename("kustomization.yaml"),
// Filesize(RangeWithin{64, 192}),
// Keyword("copyright"),
// Keyword("2019"),
// ).String()
//
// Outputs "q=filename:kustomization.yaml+size:64..192+copyright+2018" which
// would search for files that have [64, 192] bytes (inclusive range) and that
// contain the keywords 'copyright' and '2019' somewhere in the file.
type Query []queryField
func QueryWith(qfs ...queryField) Query {
return Query(qfs)
}
func (q Query) String() string {
strs := make([]string, 0, len(q))
for _, elem := range q {
str := elem.String()
if str == "" {
continue
}
strs = append(strs, str)
}
query := strings.Join(strs, "+")
if query == "" {
return query
}
return "q=" + query
}
// Keyword takes a single word, and formats it according to the Github API.
func Keyword(k string) queryField {
return queryField{value: k}
}
// Filesize takes a rangeFormatter and formats it according to the Github API.
func Filesize(r rangeFormatter) queryField {
return queryField{name: "size", value: r}
}
// Filename takes a filename and formats it according to the Github API.
func Filename(f string) queryField {
return queryField{name: "filename", value: f}
}
// Path takes a filepath and formats it according to the Github API.
func Path(p string) queryField {
return queryField{name: "path", value: p}
}
// RequestConfig stores common variables that must be present for the queries.
// - CodeSearchRequests: ask Github to check the code indices given a query.
// - ContentsRequests: ask Github where to download a resource given a repo and a
// file path.
// - CommitsRequests: asks Github to list commits made one a file. Useful to
// determine the date of a file.
type RequestConfig struct {
perPage uint64
accessToken string
}
func NewRequestConfig(perPage uint64, accessToken string) RequestConfig {
return RequestConfig{
perPage: perPage,
accessToken: accessToken,
}
}
// CodeSearchRequestWith given a list of query parameters that specify the
// (patial) query, returns a request object with the (parital) query. Must call
// the URL method to get the string value of the URL. See request.CopyWith, to
// understand why the request object is useful.
func (rc RequestConfig) CodeSearchRequestWith(query Query) request {
req := rc.makeRequest("search/code", query)
req.vals.Set("sort", "indexed")
req.vals.Set("order", "desc")
return req
}
// ContentsRequest given the repo name, and the filepath returns a formatted
// query for the Github API to find the dowload information of this filepath.
func (rc RequestConfig) ContentsRequest(fullRepoName, path string) string {
uri := fmt.Sprintf("repos/%s/contents/%s", fullRepoName, path)
return rc.makeRequest(uri, Query{}).URL()
}
func (rc RequestConfig) ReposRequest(fullRepoName string) string {
uri := fmt.Sprintf("repos/%s", fullRepoName)
return rc.makeRequest(uri, Query{}).URL()
}
// CommitsRequest given the repo name, and a filepath returns a formatted query
// for the Github API to find the commits that affect this file.
func (rc RequestConfig) CommitsRequest(fullRepoName, path string) string {
uri := fmt.Sprintf("repos/%s/commits", fullRepoName)
return rc.makeRequest(uri, Query{Path(path)}).URL()
}
func (rc RequestConfig) makeRequest(path string, query Query) request {
vals := url.Values{}
if rc.accessToken != "" {
vals.Set(accessTokenArg, rc.accessToken)
}
vals.Set(perPageArg, fmt.Sprint(rc.perPage))
return request{
url: url.URL{
Scheme: "https",
Host: "api.github.com",
Path: path,
},
vals: vals,
query: query,
}
}
type request struct {
url url.URL
vals url.Values
query Query
}
// CopyWith copies the requests and adds the extra query parameters. Usefull
// for dynamically adding sizes to a filename only query without modifying it.
func (r request) CopyWith(queryParams ...queryField) request {
cpy := r
cpy.query = append(cpy.query, queryParams...)
return cpy
}
// URL encodes the variables and the URL representation into a string.
func (r request) URL() string {
// Github does not handle URL encoding properly in its API for the
// q='...', so the query parameter is added without any encoding
// manually.
encoded := r.vals.Encode()
query := r.query.String()
sep := "&"
if query == "" {
sep = ""
}
if encoded == "" && query != "" {
sep = "?"
}
r.url.RawQuery = encoded + sep + query
return r.url.String()
}
// Allows to define a range of numbers and print it in the github range
// query format https://help.github.com/en/articles/understanding-the-search-syntax.
type rangeFormatter interface {
RangeString() string
}
// RangeLessThan is a range of values strictly less than (<) size.
type RangeLessThan struct {
size uint64
}
func (r RangeLessThan) RangeString() string {
return fmt.Sprintf("<%d", r.size)
}
// RangeLessThan is a range of values strictly greater than (>) size.
type RangeGreaterThan struct {
size uint64
}
func (r RangeGreaterThan) RangeString() string {
return fmt.Sprintf(">%d", r.size)
}
// RangeWithin is an inclusive range from start to end.
type RangeWithin struct {
start uint64
end uint64
}
func (r RangeWithin) RangeString() string {
return fmt.Sprintf("%d..%d", r.start, r.end)
}

View File

@@ -0,0 +1,119 @@
package github
import (
"testing"
)
func TestQueryFields(t *testing.T) {
testCases := []struct {
formatter queryField
expected string
}{
{
formatter: Keyword("keyword"),
expected: "keyword",
},
{
formatter: Filesize(RangeLessThan{23}),
expected: "size:<23",
},
{
formatter: Filesize(RangeWithin{24, 64}),
expected: "size:24..64",
},
{
formatter: Filesize(RangeGreaterThan{64}),
expected: "size:>64",
},
{
formatter: Path("some/path/to/file"),
expected: "path:some/path/to/file",
},
{
formatter: Filename("kustomization.yaml"),
expected: "filename:kustomization.yaml",
},
}
for _, test := range testCases {
if result := test.formatter.String(); result != test.expected {
t.Errorf("got (%#v = %s), expected %s", test.formatter, result, test.expected)
}
}
}
func TestQueryType(t *testing.T) {
testCases := []struct {
query Query
expected string
}{
{
query: QueryWith(
Filesize(RangeWithin{24, 64}),
Filename("kustomization.yaml"),
Keyword("keyword1"),
Keyword("keyword2"),
),
expected: "q=size:24..64+filename:kustomization.yaml+keyword1+keyword2",
},
}
for _, test := range testCases {
if queryStr := test.query.String(); queryStr != test.expected {
t.Errorf("got (%#v = %s), expected %s", test.query, queryStr, test.expected)
}
}
}
func TestGithubSearchQuery(t *testing.T) {
const (
accessToken = "random_token"
perPage = 100
)
testCases := []struct {
rc RequestConfig
codeQuery Query
fullRepoName string
path string
expectedCodeQuery string
expectedContentsQuery string
expectedCommitsQuery string
}{
{
rc: RequestConfig{
perPage: perPage,
accessToken: accessToken,
},
codeQuery: Query{
Filename("kustomization.yaml"),
Filesize(RangeWithin{64, 128}),
},
fullRepoName: "kubernetes-sigs/kustomize",
path: "examples/helloWorld/kustomization.yaml",
expectedCodeQuery: "https://api.github.com/search/code?" +
"access_token=random_token&order=desc&per_page=100&sort=indexed&q=filename:kustomization.yaml+size:64..128",
expectedContentsQuery: "https://api.github.com/repos/kubernetes-sigs/kustomize/contents/" +
"examples/helloWorld/kustomization.yaml?access_token=random_token&per_page=100",
expectedCommitsQuery: "https://api.github.com/repos/kubernetes-sigs/kustomize/commits?" +
"access_token=random_token&per_page=100&q=path:examples/helloWorld/kustomization.yaml",
},
}
for _, test := range testCases {
if result := test.rc.CodeSearchRequestWith(test.codeQuery).URL(); result != test.expectedCodeQuery {
t.Errorf("Got code query: %s, expected %s", result, test.expectedCodeQuery)
}
if result := test.rc.ContentsRequest(test.fullRepoName, test.path); result != test.expectedContentsQuery {
t.Errorf("Got contents query: %s, expected %s", result, test.expectedContentsQuery)
}
if result := test.rc.CommitsRequest(test.fullRepoName, test.path); result != test.expectedCommitsQuery {
t.Errorf("Got commits query: %s, expected %s", result, test.expectedCommitsQuery)
}
}
}

View File

@@ -0,0 +1,378 @@
package github
// GitHub only returns at most 1000 results per search query,
// this is problematic if you want to retrieve all the results for a given
// search query. However, GitHub allows you to specify as much as you want per
// query to make things more specific. Specifically for files, GitHub allows
// you to specify their sizes with range queries. This is very convenient
// since it allows us to split the search into disjoint sets/shards of results
// from the different file size ranges.
//
// Some important factors to consider:
//
// - These queries are rate limited by the API to roughly once query every two
// seconds.
//
// - The search space for file sizes is in bytes, from 0B to < 512KiB (this is
// a huge search space that cannot be probed linearly in a timely manner if
// granularity is to be expected).
//
// - If you have K files there will likely be ~K/1000 sets that you have find
// from this search space in order to get all of the results.
//
// - If you have O(K) sets it is unlikely that they are all of the same size,
// since (most files are power law distributed). That means that the range
// might be significantly smaller for 1000 small files, than it is for
// 1000 large files.
//
// - This method is a best effort approach. There are some limitations to what
// it can and can't do, so please note the following:
//
// + There may very well be a filesize that has more than 1000 results.
// this method cannot help in this case. However, requerying over time
// (days/weeks/months) while sorting by last indexed values may be
// sufficient to eventually get all of the results.
//
// + It's possible that the github API returns inconsistent counts. This
// is problematic in most cases, since it can cause many issues if the
// case is not handled properly. For instance, if you requested the
// number of files of an interval from size:0..64 and get that there
// are 900 results, you may query at size:0..96 and get that there
// are 800 results. To guarantee that this approach completes and does
// not get into a query loop over the same intervals, it will retry a few
// times and take the largest of the results or the largest previously
// queried value from another range (in this case, the implementation
// could decide that size:0..96 must have 900) results. This makes the
// approach best effort even if there are no single file sizes of over
// 1000 results.
//
//
// The approach that was taken to solve this problem is the following:
//
// 1. Determine the total number of results by querying from the lower bound
// to the upper bound (size:0..max). If there are less than 1000 files,
// return a single range of values (size:0..max) since all results can be
// retrieved.
//
// 2. Otherwise, set a target number of files to be 1000.
//
// 3. Binary search for the range from 0..r that provides a file count that is
// less than or equal to the target. Once this value is found, store the
// upper bound of range (r). If r is the same as the previous value, (or 0)
// increase r by one (this guarantees progress, but will miss out on some
// results).
//
// 4. Increase the target by 1000.
//
// 5. Repeat steps 3 and 4 until the target is at or exceeds the total number
// of files.
//
//
// In general there are other ways to get all of the files from GitHub. In
// some cases it would be sufficient to just get the files that are being
// updated/indexed by github periodically to update the corpus, so this
// complicated approach does not have to be run every time. However, for
// some searches, there may be too many results on a time interval to do
// this simple update search limited to only 1000 results.
//
// There is also a more sophisticated approach that may yield better
// performance:
// - Perform this search once and create a prior distribution of file sizes.
// Each time you want to retrieve the results of the query, scale the
// prior of expected ranges to the current number of files. From each
// expected range of 1000 files, perform a exponential search to find the
// lower bound of the range. This would likely reduce the total number
// of queries by a significant amount since it would only have to search
// for a small set of values around each likely range boundary.
//
// However, actually retrieving the files will be the bottleneck operation
// since the number of queries to find the ranges will be close to:
// log2(maxFileSize) * totalResults / 1000 ~= totalResults / 50
// whereas the number of queries to actually get all of the search results
// are close to:
// apiCallsPerResult * 10(pages) * 100(resultsPerPage) * totalResults / 1000
// = apiCallsPerResult * totalResults.
//
// So it could very well take apiCallsPerResult * 50 times longer to acutally
// fetch the results (assuming the quotas for the API calls are the same as the
// search API), than it does to perform these range searches.
import (
"fmt"
"math/bits"
)
// Files cannot be more than 2^19 bytes, according to
// https://help.github.com/en/articles/searching-code#considerations-for-code-search
const (
githubMaxFileSize = uint64(1 << 19)
githubMaxResultsPerQuery = uint64(1000)
)
// Interface instead of struct for testing purposes.
// Not expecting to have multiple implementations.
type cachedSearch interface {
CountResults(uint64) (uint64, error)
RequestString(filesize rangeFormatter) string
}
// cachedSearch is a simple data structure that maps the upper bound (r) of a
// range from 0 to r to the number of files that have between 0 and r files
// (inclusive). It also guarantees that the counts are monotonically increasing
// (not strict) as the value for r increases, by looking at the maximal
// previous file count for the value that precedes r in the cache.
//
// It uses a bit trick to be more efficient in detecting
// inconsistencies in the returned data from the Github API.
// Therefore, the cache expects a search to always start at 0, and
// it expects the max file size to be a power of 2. If this is to be changed
// there are a few considerations to keep in mind:
//
// 1. The cache is only efficient if the queries can be reused, so if
// the first chunk of files lives in the range 0..x, continuing the
// search for the next chunk from x+1..max (while asymptotically sane)
// may actually be less efficient since the cache is essentially reset
// at every interval. This leads to a larger number of requests in
// practice, and requests are what's expensive (rate limits).
//
// 2. The github API is not perfectly monotonic.. (this is somewhat
// problematic). The current cache implementation looks at the
// predecessor entry to find out if the current value is monotonic.
// This is where the bit trick is used, since each step in the binary
// search is adding or ommiting to add a decreasing power of 2 to the query
// value, we can remove the least significant set bit to find the
// predecessor in constant time. Ultimately since the search is rate
// limited, we could also easily afford to compute this in linear time
// by iterating over cached values. So this trick is not crucial to the
// cache's performance.
type githubCachedSearch struct {
cache map[uint64]uint64
gcl GitHubClient
baseRequest request
}
func newCache(client GitHubClient, query Query) githubCachedSearch {
return githubCachedSearch{
cache: map[uint64]uint64{
0: 0,
},
gcl: client,
baseRequest: client.CodeSearchRequestWith(query),
}
}
func (c githubCachedSearch) CountResults(upperBound uint64) (uint64, error) {
count, cached := c.cache[upperBound]
if cached {
return count, nil
}
sizeRange := RangeWithin{0, upperBound}
rangeRequest := c.RequestString(sizeRange)
result := c.gcl.parseGithubResponse(rangeRequest)
if result.Error != nil {
return count, result.Error
}
// As range search uses powers of 2 for binary search, the previously
// cached value is easy to find by removing the least significant set
// bit from the current upperBound, since each step of the search adds
// least significant set bit.
//
// Finding the predecessor could also be implemented by iterating over
// the map to find the largest key that is smaller than upperBound if
// this approach deemed too complex.
trail := bits.TrailingZeros64(upperBound)
prev := uint64(0)
if trail != 64 {
prev = upperBound - (1 << uint64(trail))
}
// Sometimes the github API is not monotonically increasing, or ouputs
// an erroneous value of 0, or 1. This logic makes sure that it was not
// erroneous, and that the sequence continues to be monotonic by setting
// the current query count to match the previous value. which at least
// guarantees that the range search terminates.
//
// On the other hand, if files are added, then we way loose out on some
// files in a reviously completed range, but these files should be there
// the next time the crawler runs, so this is not really problematic.
retryMonotonicCount := 4
for result.Parsed.TotalCount < c.cache[prev] {
logger.Printf(
"Retrying query... current lower bound: %d, got: %d\n",
c.cache[prev], result.Parsed.TotalCount)
result = c.gcl.parseGithubResponse(rangeRequest)
if result.Error != nil {
return count, result.Error
}
retryMonotonicCount--
if retryMonotonicCount <= 0 {
result.Parsed.TotalCount = c.cache[prev]
logger.Println(
"Retries for monotonic check exceeded,",
" setting value to match predecessor")
}
}
count = result.Parsed.TotalCount
logger.Printf("Caching new query %s, with count %d\n",
sizeRange.RangeString(), count)
c.cache[upperBound] = count
return count, nil
}
func (c githubCachedSearch) RequestString(filesize rangeFormatter) string {
return c.baseRequest.CopyWith(Filesize(filesize)).URL()
}
// Outputs a (possibly incomplete) list of ranges to query to find most search
// results as permissible by the search github search API. Github search only
// allows 1,000 results per query (paginated).
// Source: https://developer.github.com/v3/search/
//
// This leaves the possibility of having file sizes with more than 1000 results,
// This would mean that the search as it is could not find all files. If queries
// are sorted by last indexed, and retrieved on regular intervals, it should be
// sufficient to get most if not all documents.
func FindRangesForRepoSearch(cache cachedSearch) ([]string, error) {
totalFiles, err := cache.CountResults(githubMaxFileSize)
if err != nil {
return nil, err
}
logger.Println("total files: ", totalFiles)
if githubMaxResultsPerQuery >= totalFiles {
return []string{
cache.RequestString(RangeWithin{0, githubMaxFileSize}),
}, nil
}
// Find all the ranges of file sizes such that all files are queryable
// using the Github API. This does not compute an optimal ranges, since
// the number of queries needed to get the information required to
// compute an optimal range is expected to be much larger than the
// number of queries performed this way.
//
// The number of ranges is k = (number of files)/1000, and finding a
// range is logarithmic in the max file size (n = filesize). This means
// that preprocessing takes O(k * lg n) queries to find the ranges with
// a binary search over file sizes.
//
// My intuition is that this approach is competitive to a perfectly
// optimal solution, but I didn't actually take the time to do a
// rigorous proof. Intuitively, since files sizes are typically power
// law distibuted the binary search will be very skewed towards the
// smaller file ranges. This means that in practice this approach will
// make fewer than (#files/1000)*(log(n) = 19) queries for
// preprocessing, since it reuses a lot of the queries in the denser
// ranges. Furthermore, because of the distribution, it should be very
// easy to find ranges that are very close to the upper bound, up to
// the limiting factor of having no more than 1000 files accessible per
// range.
filesAccessible := uint64(0)
sizes := make([]uint64, 0)
for filesAccessible < totalFiles {
target := filesAccessible + githubMaxResultsPerQuery
if target >= totalFiles {
break
}
logger.Printf("%d accessible files, next target = %d\n",
filesAccessible, target)
cur, err := lowerBoundFileCount(cache, target)
if err != nil {
return nil, err
}
// If there are more than 1000 files in the next bucket, we must
// advance anyway and lose out on some files :(.
if l := len(sizes); l > 0 && sizes[l-1] == cur {
cur++
}
nextAccessible, err := cache.CountResults(cur)
if err != nil {
return nil, fmt.Errorf(
"cache should be populated at %d already, got %v",
cur, err)
}
if nextAccessible < filesAccessible {
return nil, fmt.Errorf(
"Number of results dropped from %d to %d within range search",
filesAccessible, nextAccessible)
}
filesAccessible = nextAccessible
if nextAccessible < totalFiles {
sizes = append(sizes, cur)
}
}
return formatFilesizeRanges(cache, sizes), nil
}
// lowerBoundFileCount finds the filesize range from [0, return value] that has
// the largest file count that is smaller than or equal to
// githubMaxResultsPerQuery. It is important to note that this returned value
// could already be in a previous range if the next file size has more than 1000
// results. It is left to the caller to handle this bit of logic and guarantee
// forward progession in this case.
func lowerBoundFileCount(
cache cachedSearch, targetFileCount uint64) (uint64, error) {
// Binary search for file sizes that make up the next <=1000 element
// chunk.
cur := uint64(0)
increase := githubMaxFileSize / 2
for increase > 0 {
mid := cur + increase
count, err := cache.CountResults(mid)
if err != nil {
return count, err
}
if count <= targetFileCount {
cur = mid
}
if count == targetFileCount {
break
}
increase /= 2
}
return cur, nil
}
func formatFilesizeRanges(cache cachedSearch, sizes []uint64) []string {
ranges := make([]string, 0, len(sizes)+1)
if len(sizes) > 0 {
ranges = append(ranges, cache.RequestString(
RangeLessThan{sizes[0] + 1},
))
}
for i := 0; i < len(sizes)-1; i += 1 {
ranges = append(ranges, cache.RequestString(
RangeWithin{sizes[i] + 1, sizes[i+1]},
))
if i != len(sizes)-2 {
continue
}
ranges = append(ranges, cache.RequestString(
RangeGreaterThan{sizes[i+1]},
))
}
return ranges
}

View File

@@ -0,0 +1,90 @@
package github
import (
"fmt"
"reflect"
"testing"
)
type testCachedSearch struct {
cache map[uint64]uint64
}
func (c testCachedSearch) CountResults(upperBound uint64) (uint64, error) {
fmt.Printf("CountResults(%05x)\n", upperBound)
count, ok := c.cache[upperBound]
if !ok {
return count, fmt.Errorf("cache not set at %x", upperBound)
}
return count, nil
}
func (c testCachedSearch) RequestString(filesize rangeFormatter) string {
return filesize.RangeString()
}
// TODO(damienr74) make tests easier to write.. I'm thinking I can make the test
// cache take in a list of (filesize, count) pairs and it can populate the cache
// without relying on how the implementation will create queries. This was only
// a quick and dirty test to make sure that modifications are not going to break
// the functionality.
func TestRangeSplitting(t *testing.T) {
// Keys follow the binary search depending on whether or not the range
// is too small/large to find close to optimal filesize ranges. This
// test is heavily tied to the fact that the search is using powers of two
// to make progress in the search (hence the use of hexadecimal values).
cache := testCachedSearch{
map[uint64]uint64{
0x80000: 5000,
0x40000: 5000,
0x20000: 5000,
0x10000: 5000,
0x08000: 5000,
0x04000: 5000,
0x02000: 5000,
0x01000: 5000,
0x00fff: 3950,
0x00ffe: 3950,
0x00ffc: 3950,
0x00ff8: 3950,
0x00ff0: 3950,
0x00fe0: 3950,
0x00fc0: 3950,
0x00f80: 3950,
0x00f00: 3950,
0x00e00: 3950,
0x00c00: 3950,
0x00800: 3950,
0x00400: 3950,
0x00200: 3688,
0x00180: 3028,
0x00100: 2999,
0x000c0: 2448,
0x00080: 1999,
0x00070: 1600,
0x0006c: 1003,
0x0006b: 1001,
0x0006a: 999,
0x00068: 999,
0x00060: 999,
0x00040: 999,
0x00000: 0,
},
}
requests, err := FindRangesForRepoSearch(cache)
if err != nil {
t.Errorf("Error while finding ranges: %v", err)
}
expected := []string{
"<107", // cache.RequestString(RangeLessThan{0x6b}),
"107..128", // cache.RequestString(RangeWithin{0x6b, 0x80}),
"129..256", // cache.RequestString(RangeWithin{0x81, 0x100}),
"257..4095", // cache.RequestString(RangeWithin{0x101, 0xfff}),
">4095", // cache.RequestString(RangeGreaterThan{0xfff}),
}
if !reflect.DeepEqual(requests, expected) {
t.Errorf("Expected requests (%v) to equal (%v)", requests, expected)
}
}

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