Compare commits

...

815 Commits

Author SHA1 Message Date
Jeff Regan
05829497d8 Merge pull request #1950 from monopole/dropPluginatorFromApiDeps
Drop api module's explicit dependence on pluginator.
2019-12-12 16:50:40 -08:00
Jeffrey Regan
87cf1231c3 Drop api module's explicit dependence on pluginator. 2019-12-12 16:36:57 -08:00
Kubernetes Prow Robot
129206a3c8 Merge pull request #1947 from pwittrock/apply
expose apply Options so we can tune them
2019-12-12 16:02:55 -08:00
Phillip Wittrock
7bdb5d3cfa expose apply Options so we can tune them 2019-12-12 15:22:27 -08:00
Jeff Regan
6309af43a7 Merge pull request #1946 from monopole/dropLastDirectRefsToPseudoK8s
Drop last direct refs to pseudo/k8s.
2019-12-12 15:04:47 -08:00
Jeffrey Regan
ac1f4fec4a Drop last direct refs to pseudo/k8s. 2019-12-12 15:02:04 -08:00
Jeff Regan
792caeac82 Merge pull request #1945 from monopole/modTidyB4Release
Go mod tidy b4 release.
2019-12-12 14:45:49 -08:00
Jeffrey Regan
bb03507d98 Go mod tidy b4 release. 2019-12-12 14:24:06 -08:00
Kubernetes Prow Robot
4fc859b62e Merge pull request #1876 from lalyos/fix-process-args-fields
Fix process args fields
2019-12-12 14:16:32 -08:00
Jeff Regan
7cdb157a55 Update execplugin_test.go 2019-12-12 13:57:00 -08:00
Jeff Regan
dae03a9618 Merge branch 'master' into fix-process-args-fields 2019-12-12 13:41:50 -08:00
Jeff Regan
6dd48142cf Update execplugin_test.go 2019-12-12 13:41:08 -08:00
Jeff Regan
3f1b100edc Merge pull request #1944 from monopole/ahJs
Update npm deps.
2019-12-12 13:20:36 -08:00
Jeffrey Regan
61c5afdf83 Update npm deps. 2019-12-12 13:18:30 -08:00
Kubernetes Prow Robot
71e75283de Merge pull request #1942 from pwittrock/internal
refactor cmd/config to internal
2019-12-12 13:06:32 -08:00
Phillip Wittrock
98d2be5550 refactor cmd/config to internal 2019-12-12 12:46:31 -08:00
Jeff Regan
634c780d1b Merge pull request #1943 from monopole/tweakGoPluginBuildScript
Tweak Go plugin build script and targets.
2019-12-12 11:10:01 -08:00
Jeffrey Regan
400140a401 Tweak Go plugin build script and targets. 2019-12-12 11:06:44 -08:00
Kubernetes Prow Robot
e80bd10374 Merge pull request #1940 from pwittrock/apply
Publish apply and diff from kustomize
2019-12-12 10:54:33 -08:00
Phillip Wittrock
9242269b5b update kyaml travis to include new directories and check for generated files 2019-12-12 10:28:11 -08:00
Phillip Wittrock
7e847dc907 publish apply / diff as alpha commands 2019-12-12 10:28:10 -08:00
Phillip Wittrock
6dbed7c16c copy apply and diff commands from kubectl 2019-12-12 10:23:47 -08:00
Phillip Wittrock
1a3f622207 regenerate docs and license 2019-12-12 10:22:12 -08:00
Phillip Wittrock
1a28e493cd Fix client-go version to match apimachinery version 2019-12-12 10:22:12 -08:00
Jeff Regan
f5805c64b2 Merge pull request #1920 from bzub/1919-build_go_plugins_script
Add script to detect/build Go plugins.
2019-12-12 09:56:37 -08:00
Jeff Regan
f299b28693 Merge pull request #1941 from monopole/upgradeToMDrip101
Upgrade to mdrip 1.0.1
2019-12-12 09:53:36 -08:00
Jeffrey Regan
ee678d6ce9 Upgrade to mdrip 1.0.1 2019-12-12 09:26:11 -08:00
Kubernetes Prow Robot
d5b44461b9 Merge pull request #1611 from sunny0826/zh
add zh doc transformerconfigs.md validationTransformer.md
2019-12-12 09:04:33 -08:00
Kubernetes Prow Robot
57059b74f5 Merge pull request #1904 from artmello/enable_gocritic
kyaml: Enable gocritic Go linter
2019-12-12 06:56:32 -08:00
Kubernetes Prow Robot
cd0b06c4fd Merge pull request #1937 from mortent/FixStatusRules
Fix issues with rules for Deployment and ReplicaSet
2019-12-12 06:40:31 -08:00
Kubernetes Prow Robot
057893a254 Merge pull request #1931 from pwittrock/vendor
Disable Alpha commands by default
2019-12-12 06:36:32 -08:00
Phillip Wittrock
1e14cc93c2 Merge branch 'master' into enable_gocritic 2019-12-12 06:35:33 -08:00
Kubernetes Prow Robot
54e8aab374 Merge pull request #1939 from pwittrock/kyaml-comments
Fix panic in merge2 comment merging
2019-12-11 20:02:32 -08:00
Phillip Wittrock
4312ab3f4d Fix panic in merge2 comment merging 2019-12-11 19:05:51 -08:00
Phillip Wittrock
6484259632 Disable alpha kustomize commands by default.
- enable alpha commands with env KUSTOMIZE_SHOW_ALPHA_COMMANDS=true
2019-12-11 18:42:46 -08:00
guoxudong
92dcc02ff1 fix zh doc 2019-12-12 10:21:18 +08:00
Kubernetes Prow Robot
fe6dffff2e Merge pull request #1935 from pwittrock/unk8s2
Remove psuedo/k8s module
2019-12-11 16:20:32 -08:00
Kubernetes Prow Robot
e1205698e6 Merge pull request #1933 from haiyanmeng/expose-es
Expose ElasticSearch as a LoadBalancer-type service
2019-12-11 16:16:32 -08:00
Kubernetes Prow Robot
a6535c4be2 Merge pull request #1934 from pwittrock/unk8s
revert pseudo/k8s deps
2019-12-11 16:08:32 -08:00
Morten Torkildsen
a28c353eab Fix issues with rules for Deployment and ReplicaSet 2019-12-11 15:51:30 -08:00
Phillip Wittrock
4791bf8e0b remove pseudo/k8s module 2019-12-11 15:29:06 -08:00
Jeff Regan
4025603396 Merge pull request #1936 from kubernetes-sigs/startAddressing1898
Introduce latest_kustomize tag in release process
2019-12-11 15:28:15 -08:00
Phillip Wittrock
c2dc7ae789 revert pseudo/k8s deps 2019-12-11 15:28:04 -08:00
Phillip Wittrock
484ad36529 remove verify deps -- use k8s directly 2019-12-11 15:28:03 -08:00
Jeff Regan
0e251ed96e Update README.md 2019-12-11 15:22:30 -08:00
Haiyan Meng
afd24c6faf Expose ElasticSearch as a LoadBalancer-type service 2019-12-11 15:05:10 -08:00
Jeff Regan
6b7236a206 Update INSTALL.md 2019-12-11 14:26:09 -08:00
Kubernetes Prow Robot
f65cac96d6 Merge pull request #1932 from haiyanmeng/fix
Avoid processing the nil pointer returned by kustomizationResultAdapter
2019-12-11 14:22:32 -08:00
Haiyan Meng
0d79219e46 Avoid processing the nil pointer returned by kustomizationResultAdapter
Currently, the crawler job panics whenever a nil pointer is returned by
kustomizationResultAdapter.
2019-12-11 13:54:01 -08:00
Kubernetes Prow Robot
e4635b456a Merge pull request #1930 from mortent/statusCli
cli for status
2019-12-11 13:34:32 -08:00
Morten Torkildsen
1b3b8522f9 cli for status 2019-12-11 13:13:09 -08:00
Jeff Regan
4098d6fdfb Merge pull request #1928 from haiyanmeng/empty
Mulitple improvements of the crawler
2019-12-11 13:04:29 -08:00
Jeff Regan
4ba69b27f8 Merge pull request #1922 from bzub/cmd_config_docs
cmd/config: Documentation nits + updates.
2019-12-11 12:59:41 -08:00
Haiyan Meng
bffc0d7071 Mulitple improvements of the crawler
1) Set document IDs to avoid duplicating documents;
2) Set the `creationTime` field of each document in the index;
3) set the `values`, `kinds` and `identifiers` fields for all documents;
4) Add a `Copy` method into the `Document` struct: this fixes the issue
where all the documents existing in the index point to the same Document
object;
5) Avoid using keystore redis;
6) Set imagePullPolicy to `Always` for crawler jobs.
2019-12-11 11:10:48 -08:00
Jeff Regan
54b1549586 Merge pull request #1909 from bzub/1908-plugin_test_path
Detect path to plugins from krusty tests.
2019-12-11 10:24:14 -08:00
bzub
3803541bfd Remove extraneous whitespace.
make all (generate docs)

Update examples in cmd docs.

make all (generate docs)

functions/examples: Whitespace cleanup.

functions/examples: Fix some example commands.
2019-12-11 12:12:32 -06:00
Kubernetes Prow Robot
2102ddab7c Merge pull request #1916 from bzub/plugins_doc_go_link
Update plugins/doc.go link.
2019-12-10 15:50:06 -08:00
Kubernetes Prow Robot
f186a0d6bb Merge pull request #1927 from frankfarzan/config_io_docs
Expand and format Configuration IO API Semantics.
2019-12-10 13:24:05 -08:00
Frank Farzan
b0f1f66d9a Document the default value 2019-12-10 12:27:02 -08:00
Frank Farzan
8c037ba109 Index is a number not a string 2019-12-10 12:15:28 -08:00
Frank Farzan
5e5e3b19f5 Expand and format Configuration IO API Semantics. 2019-12-10 12:02:49 -08:00
Kubernetes Prow Robot
ad94f2c0eb Merge pull request #1921 from pwittrock/fixes
kyaml: fix error handling
2019-12-09 17:07:30 -08:00
Phillip Wittrock
b333115314 kyaml: fix error handling 2019-12-09 16:38:56 -08:00
Kubernetes Prow Robot
50c9916eae Merge pull request #1905 from pwittrock/autocomplete
shell completion for kustomize commands
2019-12-09 16:37:30 -08:00
bzub
e1b18d125d Add script to detect/build Go plugins. 2019-12-09 17:59:59 -06:00
Kubernetes Prow Robot
8d72bf6e5c Merge pull request #1917 from frankfarzan/fix_run
Fix 'config run' by removing default mount.
2019-12-09 15:04:40 -08:00
Frank Farzan
a4c69d9cde Fix 'config run' by removing default mount.
In, pull #1822 mount logic was refactored where the default
mount using zero-value no longer makes sense and leads to
this failure:

"invalid argument "type=,src=,dst=:ro" for "--mount" flag: type is
required"

I think the intent here was to remove default mount.
2019-12-09 14:37:57 -08:00
bzub
ecb9b9efa4 Update plugins/doc.go link. 2019-12-09 15:45:05 -06:00
Phillip Wittrock
2eacbeaa87 shell completion for kustomize commands 2019-12-09 09:22:14 -08:00
Kubernetes Prow Robot
46be801a48 Merge pull request #1910 from artmello/enable_goconst
kyaml: Enable goconst Go Linter
2019-12-09 08:40:02 -08:00
Arthur Mello
0bace652c3 Move repeated variable content to constants following linter suggestion 2019-12-08 07:12:48 -03:00
Arthur Mello
f0779cd02f kyaml: Enable goconst Go Linter 2019-12-07 16:27:25 -03:00
bzub
105a8e57c8 Detect path to plugins from krusty tests. 2019-12-07 13:16:15 -06:00
Arthur Mello
0a8d7c2be2 Fix code to address complains from gocritic (assignOp, ifElseChain and appendAssign) 2019-12-06 16:34:27 -03:00
Arthur Mello
20d995c87e kyaml: Enable gocritic Go linter 2019-12-06 16:01:12 -03:00
Kubernetes Prow Robot
3703450931 Merge pull request #1893 from artmello/enable_linters
kyaml: Enable Go linters (lll, stylecheck, unparam, whitespace)
2019-12-06 08:32:50 -08:00
lalyos
95ad5d05b3 fix exec plugin args handling 2019-12-06 13:34:42 +01:00
Arthur Mello
d4fa006ccb Remove leading/trailing newlines following whitespace linter recommendation 2019-12-05 23:49:00 -03:00
Jeff Regan
b2814c5310 Update INSTALL.md 2019-12-05 18:22:37 -08:00
Jeff Regan
c00cf120ff Update INSTALL.md 2019-12-05 18:22:23 -08:00
Jeff Regan
decca7fcd3 Merge pull request #1891 from mortent/FixControllerRuntimeDeps
Fix dependencies for kstatus
2019-12-05 18:17:31 -08:00
Kubernetes Prow Robot
9914017f60 Merge pull request #1897 from pwittrock/workspace
support writing run output to files
2019-12-05 18:08:49 -08:00
Arthur Mello
a077482e99 Rename struct field to solve issue raised by go vet 2019-12-05 23:06:19 -03:00
Arthur Mello
849e0f357a Unname unused parameters following unparam linter recommendation 2019-12-05 22:50:37 -03:00
Arthur Mello
df7688002a Remove init function following gochecknoinits linter recommendation 2019-12-05 22:44:14 -03:00
Arthur Mello
e811da14d8 Rename variable, constants and struct fields following stylecheck linter recommendation 2019-12-05 22:39:29 -03:00
Phillip Wittrock
9e5af98a94 support writing run output to files
so that it isn't necessary to wrap in bash to redirect to a file
2019-12-05 17:25:01 -08:00
Arthur Mello
f2c7066088 Remove leading/trailing newlines following whitespace linter recommendation 2019-12-05 22:23:35 -03:00
Jeff Regan
3f5dc1e80a Merge pull request #1896 from monopole/tweakInstall
Improve install script error message
2019-12-05 15:18:42 -08:00
Jeffrey Regan
04bfb3e94d Tweak install. 2019-12-05 15:17:33 -08:00
Jeff Regan
567bc834c9 Update INSTALL.md 2019-12-05 14:47:11 -08:00
Jeff Regan
49f8b2bee8 Update INSTALL.md 2019-12-05 14:43:58 -08:00
Jeff Regan
b710a76818 Add kustomize install script. 2019-12-05 14:02:16 -08:00
Jeff Regan
823c59666b Merge pull request #1894 from monopole/installScript
Kustomize installation script.
2019-12-05 13:51:53 -08:00
Jeffrey Regan
e14c441e77 Kustomize installation script. 2019-12-05 13:50:53 -08:00
Arthur Mello
de4d50386d kyaml: Enable Go linters (lll, stylecheck, unparam, whitespace) 2019-12-05 16:32:17 -03:00
Jeff Regan
969a1e8f37 Merge pull request #1892 from monopole/getRidOfFakeLoader
Get rid of the 'fake' loader.
2019-12-05 11:22:47 -08:00
Jeff Regan
184735fa89 Merge pull request #1887 from Liujingfang1/replacement-poc
add replacement type and an example transformer
2019-12-05 11:10:35 -08:00
Jeffrey Regan
caa71a73fe Get rid of the fake loader. 2019-12-05 10:50:42 -08:00
Morten Torkildsen
2625502ddb Fix dependencies for kstatus 2019-12-04 21:56:55 -08:00
Kubernetes Prow Robot
7c1b477ff6 Merge pull request #1888 from haiyanmeng/duplicates
Remove duplicates in the `kinds` field of the kustomize ElasticSearch index
2019-12-04 12:04:57 -08:00
Haiyan Meng
d25b6ff3dc Remove duplicates in kinds 2019-12-04 11:24:54 -08:00
Haiyan Meng
68a196dbe5 Add a test case to demonstrate kinds have duplicates 2019-12-04 11:24:23 -08:00
Jingfang Liu
c7600bc079 add replacement type and an example transformer 2019-12-04 11:13:05 -08:00
Jeff Regan
79884240ae Merge pull request #1883 from haiyanmeng/mapping
Set the ElasticSearch index creation configuration
2019-12-03 19:09:55 -08:00
Kubernetes Prow Robot
3880b8df1d Merge pull request #1822 from joncwong/container-local-vol
Add Local Volume support to ContainerFilter
2019-12-03 16:30:58 -08:00
Haiyan Meng
8aaa3f56f5 Set the ElasticSearch index creation configuration
Currently, the `kustomize` index in ElasticSearch is using dynamic
mapping, which sets the types of all the fields to `text`. However,
`text` field type is good for full-text value matching, and not good for
exact-value matching.  For exact-value matching, the `keyword` filed
type should be used.
2019-12-03 15:16:32 -08:00
Jeff Regan
4a7ade6421 Merge pull request #1882 from monopole/crawlerNitSweep
Fix some nits in the crawler and elsewhere.
2019-12-03 11:22:36 -08:00
Jeffrey Regan
e9ab3da164 Fix some nits in the crawler and elsewhere. 2019-12-03 10:44:44 -08:00
Jonathan Wong
dff30b926e Nit fixes and proper RunFns integration 2019-12-03 00:17:12 -08:00
Jeff Regan
fd5db20a48 Update harness.go 2019-12-02 18:47:07 -08:00
Jeff Regan
8e99dbdaf0 Merge pull request #1881 from monopole/pluginSimplification
Simplify test framework
2019-12-02 18:37:04 -08:00
Jeffrey Regan
861c86a70a Simplify plugin tests. 2019-12-02 17:12:59 -08:00
Jeff Regan
0a19a5dbd9 Merge pull request #1879 from monopole/consolidateTestHarness
Consolidate test harness to one package.
2019-12-02 12:48:15 -08:00
Jeffrey Regan
382c330f5b Consolidate test harness to one package. 2019-12-02 12:29:10 -08:00
Kubernetes Prow Robot
ce935448c1 Merge pull request #1877 from joncwong/patch-5
Fix three typos
2019-12-02 07:49:05 -08:00
Jonathan Wong
49287a0f8f Fix verb noun agreement mistake 2019-12-02 02:01:00 -08:00
Jonathan Wong
1433ea4faa Fix two typos 2019-12-02 01:55:20 -08:00
Jeff Regan
daa14ae4bd Merge pull request #1875 from lalyos/fix-sed-plugin
fix quotation issues by using a bash array
2019-12-01 07:54:07 -08:00
Jeff Regan
46af583a57 Update bugs.md 2019-12-01 07:48:10 -08:00
Jeff Regan
f33069fc15 Update bugs.md 2019-12-01 07:24:56 -08:00
lalyos
cf5d3e73c0 fix quotation issues by using a bash array
see: http://mywiki.wooledge.org/BashFAQ/050
2019-11-30 21:51:32 +01:00
Jeff Regan
4f8eaacc9b Update kustomizer_test.go 2019-11-30 11:49:40 -08:00
Jeff Regan
47d6d498d6 Merge pull request #1874 from monopole/oopsMoreTest
More tests/examples.
2019-11-30 11:34:40 -08:00
jregan
1f85ce454d More tests/examples. 2019-11-30 11:32:08 -08:00
Jeff Regan
fc8c45cff5 Merge pull request #1872 from monopole/printPluginEnvPlugin
Add PrintPluginEnv plugin.
2019-11-30 10:29:37 -08:00
jregan
d98af3f06a Add PrintPluginEnv plugin. 2019-11-30 09:58:11 -08:00
Jeff Regan
d942e6fa59 Merge pull request #1871 from monopole/moar2
Move remaining examples.
2019-11-30 08:28:53 -08:00
jregan
fdd2cc5004 Move another test. 2019-11-30 07:31:38 -08:00
Jeff Regan
59b1c81e7e Merge pull request #1870 from monopole/moar
Move more examples up.
2019-11-30 06:58:35 -08:00
jregan
ae0658869a Move more examples up. 2019-11-30 06:57:57 -08:00
Kubernetes Prow Robot
5491202d69 Merge pull request #1869 from Dingshujie/mv_test_example_to_krusty
mv /api/internal/target example to /api/krusty
2019-11-30 06:19:02 -08:00
Dingshujie
c3716ff3e0 mv diamonds to api/krusty/, Provide another high level example. 2019-11-30 17:35:09 +08:00
Dingshujie
8e7c53b9e7 mv extendedpatch_test, inlinepatch_test to api/krusty/, Provide another high level example. 2019-11-30 17:35:08 +08:00
Dingshujie
cbfe314778 mv generatormergeandreplace_test, generatoroptions_test to api/krusty/, Provide another high level example. 2019-11-30 17:35:08 +08:00
Dingshujie
2d39d64d3a mv mutiplepath_test, nullvalues_test to api/krusty/, Provide another high level example. 2019-11-30 17:35:08 +08:00
Dingshujie
cd84b65972 mv pruneconfigmap_test to api/krusty/, Provide another high level example. 2019-11-30 17:35:08 +08:00
Dingshujie
fd7574cd61 mv variableref_test to api/krusty/, Provide another high level example. 2019-11-30 17:35:08 +08:00
Dingshujie
d28ef820ea mv resourceconflict_test to api/krusty/, Provide another high level example. 2019-11-30 14:50:36 +08:00
Dingshujie
e5c314a3ea mv namespace_test to api/krusty/, Provide another high level example. 2019-11-30 14:25:02 +08:00
Dingshujie
189f65dab9 mv crd_test to api/krusty/, Provide another high level example. 2019-11-30 09:41:56 +08:00
Dingshujie
9942a9278f mv configmaps_test to api/krusty/, Provide another high level example. 2019-11-30 09:34:01 +08:00
Jeff Regan
44db041682 Merge pull request #1867 from monopole/moarTests
More tests.
2019-11-29 08:53:10 -08:00
jregan
a49a764705 More tests. 2019-11-29 08:31:51 -08:00
Jeff Regan
680a0812c6 Merge pull request #1866 from monopole/anotherTestMove
Move another test up.
2019-11-29 08:02:03 -08:00
jregan
50c63d0021 Move another test up. 2019-11-29 08:00:03 -08:00
Jeff Regan
37e167e8d1 Merge pull request #1865 from monopole/simplifyPluginTestHarness
Simplify and document plugin test harness.
2019-11-29 07:55:27 -08:00
jregan
038c070626 Simplify and document plugin test harness. 2019-11-29 07:30:42 -08:00
Jeff Regan
8bb8637213 Update replicas.md 2019-11-29 07:00:23 -08:00
Jeff Regan
7ae21cb933 Merge pull request #1864 from Dingshujie/master
mv basereusenameprefix_test to api/krusty/
2019-11-29 06:55:42 -08:00
Jeff Regan
8fb64ffb0c Merge pull request #1863 from lundbird/patch-1
Add example for replicas in kustomization.yaml
2019-11-29 05:07:51 -08:00
Jeff Regan
f5a7227e26 Merge pull request #1859 from utilitywarehouse/master
Fallback to full clone if git fetch fails
2019-11-29 05:05:30 -08:00
Dingshujie
8a2b3cd1d9 mv basereusenameprefix_test to api/krusty/, Provide another high level example. 2019-11-29 20:42:56 +08:00
john
61133f3e2e remove flag logic 2019-11-29 08:58:14 +00:00
john
bf339173c8 unify git cloner behaviour 2019-11-29 08:58:04 +00:00
Jonathan Wong
e46108ada0 Add in struct for mounted storage options 2019-11-28 15:43:17 -08:00
Alex Lundberg
9e16c8ca50 Add example for replicas in kustomization.yaml 2019-11-28 18:38:05 -05:00
Jeff Regan
763cb4e925 Merge pull request #1861 from monopole/anotherExampleMoved
Provide another high level example.
2019-11-28 08:47:43 -08:00
jregan
f0153997e1 Provide another high level example. 2019-11-28 08:18:12 -08:00
Jeff Regan
6d141f2ad0 Merge pull request #1860 from monopole/improveExampleVisibility
Start making examples more visible.
2019-11-28 08:17:07 -08:00
jregan
89e7b76d48 Start making examples more visible. 2019-11-28 07:23:37 -08:00
John
b7855dc959 Merge pull request #1 from utilitywarehouse/deep-git-clone-flag
add --deep_git_clone flag
2019-11-28 12:06:15 +00:00
john
6485a7cf3e add --deep_git_clone flag 2019-11-28 11:54:03 +00:00
Kubernetes Prow Robot
fc92f4acd0 Merge pull request #1856 from pwittrock/workspace
cmd/config: update naming of commands
2019-11-27 13:03:04 -08:00
Phillip Wittrock
52a5e6ec99 rename run-fns to run 2019-11-27 12:40:10 -08:00
Jeff Regan
752ddd087b Merge pull request #1845 from haiyanmeng/work
Make the crawler work
2019-11-27 12:07:23 -08:00
Jeff Regan
2b22bfc16b Merge pull request #1855 from monopole/makeWalkWork
Make filesys.Walk work.
2019-11-27 12:05:59 -08:00
Phillip Wittrock
7ce1f7e95a Update cmd/config docs from using yaml to using kustomize config 2019-11-27 11:59:45 -08:00
Jeffrey Regan
c722d4cd17 Make filesys walk work. 2019-11-27 11:33:10 -08:00
Kubernetes Prow Robot
1a9d62617e Merge pull request #1850 from pwittrock/workspace
cmd/config: Add examples and tutorials for config functions
2019-11-26 20:27:20 -08:00
Phillip Wittrock
dc66de6bf3 cmd/config: Add examples and tutorials for config functions
- Add examples under `functions`
- Add built-in tutorial for functions
2019-11-26 20:08:23 -08:00
Jeff Regan
cb64e19da3 Merge pull request #1849 from monopole/moarRefactor
More tests, better errors.
2019-11-26 19:46:24 -08:00
Haiyan Meng
9bba761a14 Add config for creating an ElasticSearch Cluster 2019-11-26 19:38:17 -08:00
jregan
f3e735153f More tests, better errors. 2019-11-26 19:29:06 -08:00
Haiyan Meng
31c5e89b1f Add String method to KustomizationDocument to avoid printing the
content of kustomization.yaml
2019-11-26 14:49:44 -08:00
Kubernetes Prow Robot
a2b84fce86 Merge pull request #1818 from mortent/WaitStatus
Add APIs for computing status based on fetching resource info from a cluster
2019-11-26 14:25:21 -08:00
Jonathan Wong
eccef3bb0d Add appropriate test for read only LocalVolume 2019-11-26 13:52:27 -08:00
Morten Torkildsen
a489f30183 Add APIs for computing status based on fetching resource info from a
cluster
2019-11-26 13:33:21 -08:00
Jonathan Wong
7eaaedf9f6 Make LocalVolume read only 2019-11-26 13:29:50 -08:00
Jeff Regan
d1b33e7468 Merge pull request #1847 from monopole/refactorForNewWalk
Refactor filesys to prep for new filesys.Walk
2019-11-26 12:31:24 -08:00
Jeffrey Regan
3b2988bda8 Refactor to prep for new filesys.Walk 2019-11-26 11:41:23 -08:00
Haiyan Meng
84b75afae4 Make the crawler work
1) add the crawler binary and fix the crawler library
2) remove the readiness probe in the search backend
3) add config for redis keystore
4) add github_api_secret.txt file with instructions
2019-11-26 09:50:51 -08:00
Kubernetes Prow Robot
73fb32c85a Merge pull request #1842 from pwittrock/workspace
cmd/config: add built-in tutorials
2019-11-26 07:51:11 -08:00
Phillip Wittrock
5876a8cce0 cmd/config: add built-in tutorials 2019-11-25 22:48:21 -08:00
Kubernetes Prow Robot
c9ee7f3787 Merge pull request #1840 from pwittrock/workspace
mdtogo: support for alternate license headers
2019-11-25 20:51:10 -08:00
Phillip Wittrock
23f9f819eb mdtogo: support for alternate license headers 2019-11-25 20:32:27 -08:00
Kubernetes Prow Robot
7fe518b0c6 Merge pull request #1837 from pwittrock/workspace
Publish `cmd/config` as Kustomize subcommand group
2019-11-25 13:26:09 -08:00
Phillip Wittrock
7baabf7a97 cmd/config: mark config command group as [Alpha] 2019-11-25 10:33:31 -08:00
Phillip Wittrock
4a65ea8056 Publish cmd/config as a Kustomize sub-command 2019-11-25 10:25:02 -08:00
Phillip Wittrock
598854440a cmd/config: expose target for embedding config commands 2019-11-25 10:20:34 -08:00
Jeff Regan
9f2163ae54 Merge pull request #1836 from monopole/unpinAllPlugins
Unpin all the plugins (non-builtins too).
2019-11-25 08:49:46 -08:00
Jeffrey Regan
ee8598dcbd Unpin all the plugins (non-builtins too). 2019-11-25 08:47:30 -08:00
Kubernetes Prow Robot
f8a25cc2b3 Merge pull request #1834 from bzub/fix_builtin_docs_fields_links
docs: Fix images and name{Prefix,Suffix} field links.
2019-11-25 08:17:26 -08:00
bzub
80dc481f9c Fix images + name{Prefix,Suffix} field links. 2019-11-23 14:29:06 -06:00
Jeff Regan
be4d6f77b2 Merge pull request #1832 from monopole/fixNits
Fix nit leftover from 1820
2019-11-22 16:21:58 -08:00
jregan
2a35bbffe4 Fix nit leftover from 1820 2019-11-22 16:02:59 -08:00
Jeff Regan
59a70525fa Merge pull request #1720 from oke-py/apps/v1
migrate Deployment from apps/v1beta2 to apps/v1
2019-11-22 15:55:35 -08:00
Jeff Regan
ee4f404ae8 Merge pull request #1820 from haiyanmeng/image
Skip updating empty containers in image tranformer
2019-11-22 15:53:28 -08:00
Haiyan Meng
752ca4b37c Skip updating empty containers in image tranformer 2019-11-22 14:18:59 -08:00
Haiyan Meng
964a5082b1 Add a unit test to demonstrate the issue 1747 2019-11-22 14:18:55 -08:00
Kubernetes Prow Robot
81d809f15d Merge pull request #1830 from pwittrock/docs-4
Add api documentation for annotations and fns
2019-11-22 14:18:42 -08:00
Jeff Regan
5f80480068 Merge pull request #1831 from monopole/unpinBuiltins
Unpin the builtin plugins.
2019-11-22 14:13:11 -08:00
Jeffrey Regan
416e1fcc1a Unpin the builtin plugins. 2019-11-22 14:08:24 -08:00
Phillip Wittrock
bf8af8efba kyaml: New documentation for annotations and fns (apis) 2019-11-22 13:10:39 -08:00
Kubernetes Prow Robot
980209e87c Merge pull request #1829 from pwittrock/docs-3
kyaml: refactor command documentation into .md files from go files
2019-11-22 13:02:42 -08:00
Phillip Wittrock
3345464b25 kyaml: refactor command documentation into .md files from go files
No new documentation added.
2019-11-22 12:22:25 -08:00
Phillip Wittrock
2a5f513bc3 Merge pull request #1828 from pwittrock/makefile
kyaml: fixup Makefile
2019-11-22 12:11:50 -08:00
Phillip Wittrock
f531ac065d kyaml: fixup Makefile
- Change name of generated binary to `config` to match module name
- Use GOBIN instead of GOPATH to simplify commands
- Export GOBIN environment variable to the `go generate` command so it can find built commands
2019-11-22 11:53:14 -08:00
Phillip Wittrock
b240092058 Merge pull request #1827 from pwittrock/makefile
Add utility for generating cobra documentation from .md files
2019-11-22 11:48:13 -08:00
Phillip Wittrock
f0ca8f9c1a Add utility for generating cobra documentation from .md files 2019-11-22 11:47:35 -08:00
Kubernetes Prow Robot
5d4e45c24d Merge pull request #1824 from ibidani/fix-docs-links
Fix docs broken files links as Github relative links
2019-11-22 08:07:28 -08:00
Idan Bidani
04516803f2 Fix docs broken links as Github relative links 2019-11-21 23:52:53 -05:00
Jeff Regan
56d57c3088 Merge pull request #1823 from kubernetes-sigs/revert-1631-replacement-poc
Revert "Replacement poc"
2019-11-21 18:32:46 -08:00
Jeff Regan
b6d760dc6f Update eschewedFeatures.md 2019-11-21 17:00:09 -08:00
Jeff Regan
c856f800d0 Revert "Replacement poc" 2019-11-21 16:39:57 -08:00
Jeff Regan
b86bea9749 Merge pull request #1631 from Liujingfang1/replacement-poc
Replacement poc
2019-11-21 15:11:37 -08:00
Kubernetes Prow Robot
675faaced2 Merge pull request #1821 from monopole/improveGenRules
Improve code generation rules.
2019-11-21 14:49:29 -08:00
Jeffrey Regan
248d0d57d6 Improve builtin generation rules. 2019-11-21 12:42:45 -08:00
Jonathan Wong
a7cff1c75b Add local volume support to container filters 2019-11-21 12:16:09 -08:00
Jeff Regan
96c2873cc0 Merge pull request #1814 from haiyanmeng/dockerfile
Fix dir paths in crawler backend Dockerfile
2019-11-21 12:05:02 -08:00
Jonathan Wong
ff60138efd Merge branch 'master' of https://github.com/kubernetes-sigs/kustomize 2019-11-21 11:55:24 -08:00
Kubernetes Prow Robot
672153f021 Merge pull request #1815 from joncwong/rename-kyaml
Rename cmd/kyaml to cmd/config
2019-11-21 10:01:29 -08:00
Kubernetes Prow Robot
d81913de42 Merge pull request #1819 from annp1987/fix_typo
fix typo
2019-11-21 09:59:28 -08:00
Nguyen Phuong An
3ec583a3df fix typo
Signed-off-by: Nguyen Phuong An <AnNP@vn.fujitsu.com>
2019-11-21 09:26:32 +07:00
Jonathan Wong
14d4059f46 Rename cmd/kyaml to cmd/config in travis scripts 2019-11-19 18:42:18 -08:00
Jonathan Wong
99701a1932 Rename directory cmd/kyaml to cmd/config 2019-11-19 18:41:35 -08:00
Jonathan Wong
d8e5891498 Rename cmd/kyaml to cmd/config 2019-11-18 15:09:38 -08:00
Haiyan Meng
53b5e0f602 Fix dir paths in crawler backend Dockerfile 2019-11-18 13:35:16 -08:00
Jeff Regan
e5382c59a2 Merge pull request #1813 from monopole/testElaboratingCustomConfig
Add test elaborating on custom config.
2019-11-18 09:47:55 -08:00
Jeffrey Regan
bf119bf5b7 Add test elaborating on custom config. 2019-11-18 09:24:12 -08:00
Jeff Regan
277f565f2e Update genargs.go 2019-11-18 09:23:06 -08:00
Jeff Regan
8019cb0405 Merge pull request #1812 from monopole/activateLintTypecheck
Activate lint typecheck
2019-11-18 08:20:11 -08:00
Jeffrey Regan
362454413a Activate lint typecheck 2019-11-18 07:46:43 -08:00
Jeff Regan
562aaadf2c Merge pull request #1810 from justinsb/typo_suing
Fix typo: suing -> using
2019-11-18 06:49:07 -08:00
Jeff Regan
c868f01f82 Merge pull request #1811 from monopole/activateLintUnused
Activate lint for unused code.
2019-11-18 06:45:53 -08:00
Jeffrey Regan
83db25d6c4 Activate lint for unused code. 2019-11-18 06:41:01 -08:00
Jonathan Wong
e6306f60f4 Add docs command boilerplate 2019-11-17 19:51:19 -08:00
Justin SB
dd3ff341e0 Fix typo: suing -> using 2019-11-17 14:08:09 -08:00
Jeff Regan
5381daf95a Merge pull request #1792 from rcmorano/feature/add-chartRelease-and-chartVersion-support
Add support to choose 'chartRelease' and 'chartVersion' while kustomizing remote helm charts
2019-11-16 08:35:37 -08:00
Jeff Regan
fd588879e8 Merge pull request #1808 from monopole/fixMakeTabs
Fix makefile tabs (sigh).
2019-11-16 08:13:54 -08:00
Jeff Regan
03dfb03a16 Merge pull request #1807 from monopole/activateLintStaticCheck
Activate lint staticcheck.
2019-11-16 07:22:31 -08:00
jregan
927d0d0c63 Fix makefile tabs (sigh). 2019-11-16 07:16:20 -08:00
jregan
0924269e76 Activate lint staticcheck. 2019-11-16 07:04:53 -08:00
Jeff Regan
2326362fd2 Merge pull request #1805 from monopole/lintGosimple
Activate lint gosimple.
2019-11-16 06:37:52 -08:00
jregan
3da0afdda0 Activate lint gosimple. 2019-11-16 06:09:54 -08:00
Kubernetes Prow Robot
85d34531a2 Merge pull request #1795 from mortent/status
Library for computing status for Kubernetes resources
2019-11-15 17:05:41 -08:00
Kubernetes Prow Robot
4db30d16a6 Merge pull request #1804 from hypnoglow/linters
kyaml: enable errcheck & golint; fix gofmt
2019-11-15 12:40:32 -08:00
Morten Torkildsen
a49507c79e Library for computing status for Kubernetes resources 2019-11-15 10:26:47 -08:00
Igor Zibarev
7f1e93c6f5 kyaml: enable errcheck & golint; fix gofmt 2019-11-15 21:14:41 +03:00
Jeff Regan
7b71263583 Merge pull request #1801 from monopole/raiseLinterBar
Raise the golinter bar.
2019-11-14 18:40:30 -08:00
Jeffrey Regan
75b8103215 Raise the golinter bar. 2019-11-14 16:37:49 -08:00
Jeff Regan
2fa944b1cd Merge pull request #1800 from haiyanmeng/move-crawler
Move crawler from hack under api/internal
2019-11-14 15:16:40 -08:00
Haiyan Meng
aa09e3f3f9 Run go mod tidy 2019-11-14 13:57:29 -08:00
Haiyan Meng
8aaac77397 Update the module path in go.mod 2019-11-14 13:56:20 -08:00
Haiyan Meng
9255c991f4 Replace the sigs.k8s.io/kustomize/hack/crawl/* import path with
`sigs.k8s.io/kustomize/api/internal/crawl/*`
2019-11-14 13:38:18 -08:00
Haiyan Meng
d08140d3f7 Remove api/internal/hack/crawl/crawler/git dir, use api/internal/git
instead.
2019-11-14 13:35:00 -08:00
Haiyan Meng
f69d2d2e69 Move hack/crawl under api/internal 2019-11-14 13:17:28 -08:00
Jeff Regan
d0b874a643 Update Makefile 2019-11-14 13:08:05 -08:00
Jeff Regan
b52c6a77f4 Merge pull request #1784 from joncwong/patch-2
Fix broken redirect for contributor cheatsheet
2019-11-14 13:05:57 -08:00
Jeff Regan
0a84a0d525 Merge pull request #1793 from haiyanmeng/fix-git
Fix two broken deps to allow tests under hack/crawl to pass
2019-11-14 13:04:21 -08:00
Kubernetes Prow Robot
12a26e1cbc Merge pull request #1786 from joncwong/patch-3
Add tree to necessary tools
2019-11-14 13:03:36 -08:00
Jeff Regan
1a7cb3137e Merge pull request #1798 from jeanfabrice/master
fix typo in pseudo/README.md
2019-11-14 12:48:17 -08:00
Kubernetes Prow Robot
2605c124cc Merge pull request #1797 from oke-py/whitespace-path
quote PATH to run `make` successfully even if PATH includes whitespace
2019-11-14 12:41:36 -08:00
Kubernetes Prow Robot
95f4d9d2bb Merge pull request #1785 from hypnoglow/container-filter-network
kyaml: add Network to ContainerFilter
2019-11-14 12:39:35 -08:00
Kubernetes Prow Robot
7164f840aa Merge pull request #1787 from hypnoglow/fix-scopelint
kyaml: fix scopelint issues
2019-11-14 12:35:36 -08:00
Jeff Regan
c0d1bde30c Update macDevGuide.md 2019-11-14 11:59:09 -08:00
Phillip Wittrock
75a761112c Merge branch 'master' into container-filter-network 2019-11-14 11:56:49 -08:00
Kubernetes Prow Robot
73f94c1981 Merge pull request #1799 from monopole/makeFTW
Drive kustomize repo verification entirely from Makefile
2019-11-14 11:53:35 -08:00
Jeffrey Regan
003b3eccc2 Drive kustomize repo verification from make. 2019-11-14 11:31:42 -08:00
Haiyan Meng
df25f3a93a Run go mod tidy to clean up go.sum 2019-11-14 10:16:58 -08:00
Haiyan Meng
4448bcf22c Update go.sum and go.mod by running go build|test 2019-11-14 10:16:58 -08:00
Haiyan Meng
5eef5f2777 Fix two broken deps to allow tests under hack/crawl to pass
1) copy api/internal/git to hack/crawl/crawler/: internal packages are
not allowed to be imported;
2) change the import path from api/pgmconfig to api/kconfig
2019-11-14 10:16:25 -08:00
jeanfabrice
52466ffe83 fix typo in pseudo/README.md 2019-11-14 15:00:35 +01:00
Naoki Oketani
4542e4a122 quote PATH to run make successfully even if PATH includes whitespace 2019-11-14 19:00:20 +09:00
jregan
875385609e Simplify precommit. 2019-11-13 21:19:30 -08:00
Kubernetes Prow Robot
24f3d7556f Merge pull request #1789 from pwittrock/run
Add kyaml command for invoking container filters
2019-11-13 11:31:21 -08:00
Roberto C. Morano
ffeae451ab added support for 'chartRelease' (stable|incubator) and 'chartVersion' (0.0.1) 2019-11-13 19:30:46 +01:00
Phillip Wittrock
c99278c67b Add kyaml command for invoking container filters 2019-11-13 07:33:28 -08:00
Jeff Regan
867b795158 Update README.md 2019-11-12 14:41:16 -08:00
Igor Zibarev
301e529a4a kyaml: fix scopelint issues 2019-11-12 23:42:02 +03:00
Jonathan Wong
563564a198 Add tree to necessary tools
I don't believe "tree" is under the branch of gnu tools, so it might be misleading to put it under "Install gnu tools." Should we leave it like this or change the title to "Install gnu/unix tools"
2019-11-12 12:37:40 -08:00
Igor Zibarev
bfa5c59dc6 kyaml: add Network to ContainerFilter 2019-11-12 23:28:55 +03:00
Jonathan Wong
f202ce34eb Fix broken redirect for contributor cheatsheet 2019-11-12 10:57:03 -08:00
Kubernetes Prow Robot
912a9c3baa Merge pull request #1783 from pwittrock/errors
Improve error handling in kyaml libraries
2019-11-12 10:48:54 -08:00
Phillip Wittrock
b473faccca Improve error handling in kyaml libraries 2019-11-12 08:53:55 -08:00
Jeff Regan
1bbd8b2c43 Merge pull request #1781 from monopole/pinToPluginatorV2
Pin api HEAD to pluginator v2.
2019-11-11 22:38:12 -08:00
Jeffrey Regan
1be5292eb6 Pin api to pluginator v2. 2019-11-11 22:03:39 -08:00
Naoki Oketani
a8ece4b5f7 migrate Deploymnet from apps/v1beta2 to apps/v1 2019-11-12 15:02:54 +09:00
Jeff Regan
a6a5976304 Merge pull request #1780 from monopole/pluginatorV2
Pluginator v2
2019-11-11 21:38:20 -08:00
Jeffrey Regan
c91e2283db Pluginator v2 2019-11-11 21:35:53 -08:00
Jeff Regan
2c9635967a Merge pull request #1779 from monopole/pinAllAtApiV020
Pin kustomize and the plugins to kust Api v0.2.0
2019-11-11 20:48:40 -08:00
Jeffrey Regan
2dd148af24 Pin kustomize and the plugins to kust Api v0.2.0 2019-11-11 19:47:28 -08:00
Jeff Regan
bae6418ca2 Merge pull request #1777 from monopole/pinKustomizeApiToPseudo
Pin kustomize API to pseudok8s v0.1.0
2019-11-11 18:17:35 -08:00
Jeffrey Regan
1db0248748 Pin kustomize API to pseudok8s v0.1.0 2019-11-11 17:45:19 -08:00
Jeff Regan
387afef19b Merge pull request #1776 from monopole/moarTweaksDarnPseudo
Tweaks to accomodate pseudo/k8s.
2019-11-11 17:11:31 -08:00
Jeffrey Regan
9e47e585d4 Tweaks to accomodate pseudo/k8s. 2019-11-11 17:09:17 -08:00
Jeff Regan
1f8696865f Merge pull request #1775 from monopole/addPseudoReleaseOption
Set up for pseudok8s release.
2019-11-11 15:38:59 -08:00
Jeff Regan
87b6a4d6bc Set up for pseudok8s release. 2019-11-11 15:37:53 -08:00
Kubernetes Prow Robot
5fe0b9e1b2 Merge pull request #1774 from kubernetes-sigs/pseudoK8sReleaseStuff
Set up for pseudok8s release.
2019-11-11 15:06:06 -08:00
Jeffrey Regan
9a25cb96b7 Set up for pseudok8s release. 2019-11-11 12:37:12 -08:00
Jeff Regan
0f81cf52e7 Update README.md 2019-11-11 11:10:01 -08:00
Jeff Regan
eb75c039e5 Merge pull request #1773 from monopole/pseudoMain
A pseudo main to make goreleaser happier.
2019-11-11 10:59:46 -08:00
jregan
0f0c6618a0 A pseudo main to make goreleaser happier. 2019-11-11 10:57:35 -08:00
Jingfang Liu
3e4354dfb8 add test with diamond shape 2019-11-11 09:06:51 -08:00
Jingfang Liu
06e10cc084 add replacement transformer 2019-11-11 09:03:17 -08:00
Jeff Regan
97f2f33460 Merge pull request #1769 from monopole/removeOpsysCommentFromGeneratedCode
Remove opsys comment from generated code
2019-11-11 08:47:38 -08:00
jregan
382981fb43 Remove opsys comment from generated code. 2019-11-11 08:32:35 -08:00
Jeff Regan
519dacc10f Merge pull request #1768 from monopole/makeRepoMoreRelocatable
Add a relative path option for finding plugin source.
2019-11-11 08:08:30 -08:00
jregan
02c0c2692f Make repo more relocatable on developer's workstation. 2019-11-11 08:03:58 -08:00
Jeff Regan
6df4efd145 Merge pull request #1766 from monopole/lessGopath
Simplify pre-submit.sh
2019-11-09 16:19:02 -08:00
jregan
4dab6268da Less GOPATH dependence. 2019-11-09 15:01:26 -08:00
Jeff Regan
4150026ec7 Merge pull request #1767 from monopole/updateGeneratedCodeAndGoSums
Update plugin go.sums
2019-11-09 14:43:46 -08:00
jregan
10cd82ce31 Update go.sums 2019-11-09 14:42:29 -08:00
Jeff Regan
d0ce4fcf15 Merge pull request #1765 from monopole/makaMaka
Move code generation to makefile.
2019-11-09 08:54:15 -08:00
Jeffrey Regan
d54ff23560 Move code generation to makefile. 2019-11-09 08:32:09 -08:00
Phillip Wittrock
8e4609a850 Merge pull request #1764 from pwittrock/cmd
Switch cmd/kyaml to pseudo/k8s
2019-11-08 17:51:24 -08:00
Phillip Wittrock
eabb476461 Switch cmd/kyaml to pseudo/k8s 2019-11-08 17:23:04 -08:00
Kubernetes Prow Robot
74255f6bad Merge pull request #1763 from pwittrock/deps-fresh1
Switch kustomize to depend on pseudo/k8s
2019-11-08 17:05:59 -08:00
Phillip Wittrock
3dfe62fe55 switch to pseudo/k8s deps 2019-11-08 16:48:42 -08:00
Phillip Wittrock
ad9b869ddb Use local kustomize api for pluginator and hack/crawl 2019-11-08 16:38:01 -08:00
Phillip Wittrock
5c4c19bf19 Script to update pseudo references 2019-11-08 16:38:01 -08:00
Jeff Regan
1f86a0ca5d Merge pull request #1749 from pwittrock/cmd
Create kyaml command to expose running kyaml filters on the cli
2019-11-08 13:24:13 -08:00
Kubernetes Prow Robot
db630a9d07 Merge pull request #1762 from pwittrock/deps-fresh1
Fetch pseudo modules
2019-11-08 13:21:25 -08:00
Jeff Regan
388dd13a5f Merge pull request #1753 from oke-py/gnu-sed
brew install gnu-sed
2019-11-08 12:53:02 -08:00
Phillip Wittrock
67c9d469c4 Enable pseudo dependency checking in travis 2019-11-08 12:52:00 -08:00
Jeff Regan
28a55bbd9c Merge pull request #1761 from monopole/rmScript
Remove hack/fork_k8s_and_fix_deps.sh to avoid confusion.
2019-11-08 12:50:54 -08:00
Jeffrey Regan
bdacb941ab Remove hack/fork_k8s_and_fix_deps.sh to avoid confusion. 2019-11-08 12:49:58 -08:00
Phillip Wittrock
d87ad523de Fix pseudo/README.md script name 2019-11-08 12:49:20 -08:00
Phillip Wittrock
ec36993d42 Run pseudo/init-pseudo-module.sh to fetch packages 2019-11-08 12:48:06 -08:00
Kubernetes Prow Robot
6b5aebac22 Merge pull request #1760 from pwittrock/deps-fresh1
pseudo: script for creating psuedo modules
2019-11-08 12:44:52 -08:00
Phillip Wittrock
883714e2e5 Scripts for adding psuedo modules
psuedo modules will contain modules copied from k8s where we cannot depend directly on the k8s modules
2019-11-08 12:25:13 -08:00
Phillip Wittrock
8b0f4bf714 kyaml: Add kyaml filters as cli commands 2019-11-08 09:26:02 -08:00
Kubernetes Prow Robot
e83b97ea1f Merge pull request #1748 from pwittrock/commands
kyaml: rename annotations and fix linting
2019-11-07 20:00:20 -08:00
Jeff Regan
48dfcf5a3e Merge pull request #1750 from monopole/invokePluginUnitTests
Invoke plugin unit tests in presubmit.
2019-11-07 17:29:15 -08:00
Jeff Regan
cb4498594b Merge pull request #1758 from monopole/scriptToForkAndFixDeps
Script to fork and fix k8s deps.
2019-11-07 17:28:33 -08:00
Jeffrey Regan
b0bd4b5410 Script to fork and fix k8s deps. 2019-11-07 17:20:15 -08:00
Jeffrey Regan
dbf8a0fde4 Invoke plugin unit tests in presubmit. 2019-11-07 15:54:20 -08:00
Phillip Wittrock
3db1111f8e kyaml: rename annotations and fix linting 2019-11-07 15:46:30 -08:00
Kubernetes Prow Robot
e482ffa3f9 Merge pull request #1755 from monopole/unpinThePlugins
Repin the plugins on head for release testing.
2019-11-07 15:44:19 -08:00
Jeffrey Regan
2b1749778f Unpin all the plugins, so we can test against head b4 release. 2019-11-07 15:28:04 -08:00
Naoki Oketani
6af51a1bfe brew install gnu-sed 2019-11-08 08:17:48 +09:00
Phillip Wittrock
e0b46acf2f Merge pull request #1746 from pwittrock/kyaml
Kyaml documentation improvements
2019-11-07 09:42:42 -08:00
Phillip Wittrock
2e33a69388 Update documentation for kyaml package 2019-11-07 09:16:27 -08:00
Phillip Wittrock
018698ec85 Update .gitignore to ignore Intellij metadata files 2019-11-07 08:02:52 -08:00
Phillip Wittrock
0029a8ce32 Update Makefile with licenseadder and golangci-lint 2019-11-07 07:57:37 -08:00
Jeff Regan
04d5494246 Merge pull request #1745 from monopole/likelyLastMove
Remove more k8sdeps from external API.
2019-11-06 21:00:57 -08:00
jregan
41a8bd208d Remove more k8sdeps from external API. 2019-11-06 20:43:26 -08:00
Kubernetes Prow Robot
6bb470dbd0 Merge pull request #1744 from monopole/removePluginsPackagesFromExternalApi
Remove remaining plugins packages from external API.
2019-11-06 19:48:43 -08:00
jregan
9fa0391ce9 Remove remaining plugins packages from external API. 2019-11-06 19:32:53 -08:00
Kubernetes Prow Robot
bda22d08cc Merge pull request #1743 from monopole/lowercaseBuiltins
Lowercase the generated filenames per Go style.
2019-11-06 19:30:43 -08:00
jregan
09faaa1b2c Lowercase the generated filenames per Go style. 2019-11-06 19:12:12 -08:00
Jeff Regan
9a65634df6 Merge pull request #1742 from monopole/moveBuiltinsToTop
Move the builtins to the top level as part of API
2019-11-06 18:50:49 -08:00
Jeffrey Regan
300383959d Remove plugins packages from external API. 2019-11-06 18:31:33 -08:00
Kubernetes Prow Robot
ab17d8dd74 Merge pull request #1741 from monopole/RemoveCMandSecretPackageFromApi
Remove CMSecret package from API.
2019-11-06 16:00:43 -08:00
Jeffrey Regan
c5ba2ced3b Remove CMSecret package from API. 2019-11-06 15:35:18 -08:00
Kubernetes Prow Robot
4103580834 Merge pull request #1740 from monopole/removeTargetFromExposedApi
Remove target package from exposed API.
2019-11-06 15:25:50 -08:00
Jeffrey Regan
d36e3f015d Remove target package from exposed API. 2019-11-06 15:10:11 -08:00
Jeff Regan
975e071394 Merge pull request #1739 from monopole/removeGitFromExposedApi
Remove git package from exposed API.
2019-11-06 13:16:08 -08:00
Jeffrey Regan
6542af600d Remove 'git' package from exposed API. 2019-11-06 13:08:47 -08:00
Kubernetes Prow Robot
9e6e928725 Merge pull request #1732 from monopole/moarMakefile
Migrate some presubmit functionality to Makefile.
2019-11-05 15:44:59 -08:00
Jeffrey Regan
ad24ba2234 Migrate some presubmit functionality to Makefile. 2019-11-05 12:37:08 -08:00
Jeff Regan
c7fd1dee8b Merge pull request #1716 from bzub/node_cm_namereference
Support node configmap namereference.
2019-11-04 14:38:22 -08:00
Jeff Regan
606eb8a74c Merge pull request #1731 from monopole/noKubevalOnTravis2
No kubeval dependent test on travis.
2019-11-04 14:37:27 -08:00
Jeffrey Regan
2340c98f6a No kubeval dependent test on travis. 2019-11-04 14:20:39 -08:00
Kubernetes Prow Robot
a7607c20e3 Merge pull request #1730 from monopole/noKubevalOnTravis
No kubeval dependent test on travis.
2019-11-04 14:08:12 -08:00
Phillip Wittrock
0b5cf74945 Merge pull request #1728 from pwittrock/kyaml
kyaml: initial support for yaml and resource manipulation
2019-11-04 14:04:01 -08:00
Jeffrey Regan
0687699d27 No kubeval dependent test on travis. 2019-11-04 13:01:37 -08:00
Kubernetes Prow Robot
357126fc4e Merge pull request #1729 from gliptak/patch-1
Correct typo
2019-11-04 12:56:12 -08:00
Gábor Lipták
0aeb53a2b3 Correct typo 2019-11-04 15:36:13 -05:00
Phillip Wittrock
efd7c8e3f7 kyaml: initial support for yaml and resource manipulation 2019-11-04 11:36:35 -08:00
bzub
152ee44b92 Support node configmap namereference. 2019-11-04 12:38:28 -06:00
Jeff Regan
588297f1f9 Update whatApi.sh 2019-11-04 09:50:43 -08:00
Jeff Regan
6b81ae9a93 Merge pull request #1726 from monopole/konfigPalooza
Improve config package names and reduce API exposure.
2019-11-04 09:24:28 -08:00
jregan
077c7b2d20 Improve config package names and reduce API exposure. 2019-11-03 16:39:44 -08:00
Jeff Regan
1a4330a7cb Merge pull request #1725 from monopole/tryThisOne
Improve plugin home defaulting.
2019-11-03 15:58:33 -08:00
jregan
d08690a6aa Improve plugin home defaulting. 2019-11-03 15:47:43 -08:00
Jeff Regan
ba925e833d Merge pull request #1724 from monopole/removeErrorFromConstructor
Improve error messaging around plugin loading problems.
2019-11-03 06:46:31 -08:00
jregan
4c15c42447 Remove error return from constructor. 2019-11-03 06:37:04 -08:00
Jeff Regan
690e01c2ba Update whatApi.sh 2019-11-02 14:12:15 -07:00
Jeff Regan
133d8bff5e Merge pull request #1723 from monopole/renaming
Improved plugin loading docs.
2019-11-02 13:56:55 -07:00
jregan
079c3307c1 Improved plugin loading docs. 2019-11-02 13:47:47 -07:00
Jeff Regan
e0070f7ec5 Merge pull request #1722 from monopole/moveLoadRestrictions
push flag code from api module to kustomize internal
2019-11-02 11:07:36 -07:00
Jeffrey Regan
a45eca7e22 move load restrictions 2019-11-02 10:34:33 -07:00
Jeff Regan
79c5f8a977 Merge pull request #1719 from monopole/mvPluginConfig
Move plugin config to main config package.
2019-11-01 16:32:42 -07:00
Jeffrey Regan
69d1699963 Move plugin config to main config package. 2019-11-01 16:31:00 -07:00
Jeff Regan
b6b8f4396f Merge pull request #1717 from liztio/patch-1
strategic-merge-patch documentation has moved
2019-11-01 16:11:32 -07:00
Liz
9ca5284e4f strategic-merge-patch documentation has moved 2019-10-31 14:31:19 -04:00
Kubernetes Prow Robot
335292fe8a Merge pull request #1714 from monopole/addTests
Add some kustomization tests.
2019-10-31 09:19:37 -07:00
Jeff Regan
cf9edbfb3c Merge pull request #1712 from pwittrock/mac-docs
Add Mac development guide
2019-10-30 16:36:17 -07:00
Jeffrey Regan
364a2342df Add some kustomization tests. 2019-10-30 16:20:23 -07:00
Phillip Wittrock
3301913178 Add Mac development guide 2019-10-30 09:11:25 -07:00
Jeff Regan
ac3c53557e Merge pull request #1709 from pwittrock/skip
Support for ignoring specific Resources via annotation.
2019-10-29 17:19:59 -07:00
Phillip Wittrock
8bf98dd255 Support for ignoring specific Resources via annotation.
Resources annotated with `config.kubernetes.io/local-config` will be ignored by Kustomize
2019-10-29 16:51:38 -07:00
Jeff Regan
f2368201d9 Merge pull request #1704 from monopole/whatApi
Script to report on api usage.
2019-10-28 14:41:07 -07:00
Jeffrey Regan
f44574cf43 Script to report on api usage. 2019-10-28 14:40:24 -07:00
Jeff Regan
3054d69dd9 Merge pull request #1702 from monopole/buryBuildCode
Reduce the size of the API used by the build command
2019-10-28 14:36:07 -07:00
Jeff Regan
739c2a5bac Merge pull request #1703 from monopole/timApple
Update travis.yaml for osx
2019-10-28 14:14:40 -07:00
Jeffrey Regan
a780bd6465 Update travis.yaml for osx 2019-10-28 13:51:24 -07:00
Jeffrey Regan
4716cb026f Simplify building api. 2019-10-28 13:36:14 -07:00
Jeff Regan
a186144a78 Reinstate simplified makefile
Partially undo
2ea4762d0f

The original makefile was annoying people who naturally expected it to work.
2019-10-28 13:35:49 -07:00
Jeff Regan
2ea4762d0f Delete makefile
This isn't maintained because it's not used by ./travis/pre-commit.sh or any other automated process.
It's just causing confusion.
2019-10-28 11:54:44 -07:00
Jeff Regan
eb268fa722 Merge pull request #1701 from monopole/replaceApiWithLocalApi
Point kustomize to local API for development.
2019-10-28 11:29:57 -07:00
Jeffrey Regan
3b3a1309e4 Point kustomize to local API for development. 2019-10-28 11:17:34 -07:00
Jeff Regan
72db6dde9e Merge pull request #1699 from monopole/makeKrusty
Make krusty package - high-level CLI-like package.
2019-10-27 08:28:33 -07:00
jregan
6d30bc5c35 Make krusty package - high-level CLI-like package. 2019-10-27 08:19:56 -07:00
Jeff Regan
773629e544 Merge pull request #1698 from monopole/mvHacksToHack
mv hacks dir to hack (match pattern in k/k repo).
2019-10-27 05:13:30 -07:00
jregan
cd50bf4e1e Move hacks to hack (match k8s pattern). 2019-10-27 05:05:23 -07:00
Jeff Regan
ff59e9b52f Merge pull request #1681 from rcmorano/feature/add-releaseName-and-releaseNamespace-support
Add support to render charts using '.Release.Name' and .Release.Namespace'
2019-10-25 10:33:59 -07:00
Jeff Regan
9a954d4f0b Merge pull request #1693 from monopole/fixSomeNits
Fix some nits.
2019-10-25 10:30:28 -07:00
jregan
5c63c20390 Fix some nits. 2019-10-25 10:29:48 -07:00
Kubernetes Prow Robot
a84f8d65db Merge pull request #1692 from monopole/movePluginsToApiV011
Pin the plugins to api/v0.1.1
2019-10-24 16:59:38 -07:00
Jeffrey Regan
6e2335ec3d Move plugins to api/v0.1.1 2019-10-24 16:32:12 -07:00
Jeff Regan
4cf6630fc0 Merge pull request #1671 from beautytiger/dev-191022-dup
delete duplicated entry in slice
2019-10-24 15:34:23 -07:00
Jeff Regan
d239d52217 Merge pull request #1690 from monopole/tuneUpCrawl
Tune up hack/crawl.
2019-10-24 13:29:59 -07:00
Jeffrey Regan
c260105212 Tune up hack/crawl. 2019-10-24 13:29:14 -07:00
Jeff Regan
44cdf98a47 Filter out pluginator in install curl script. 2019-10-24 12:40:06 -07:00
Kubernetes Prow Robot
92390eabe4 Merge pull request #1663 from chaosteil/pluginloadfail
Refactor exec plugin load to error if plugin isn't executable
2019-10-24 11:55:41 -07:00
Jeff Regan
7b8fa51ec5 More fixes to releasing instructions. 2019-10-24 11:50:34 -07:00
Jeff Regan
af8e17a1ed Fix curl command in INSTALL.md 2019-10-24 11:34:09 -07:00
Jeff Regan
e2eeb90639 Update INSTALL.md 2019-10-24 11:23:21 -07:00
Jeff Regan
1704977a4b Explain using go to install kustomize. 2019-10-24 11:08:21 -07:00
Kubernetes Prow Robot
7050c6a7b6 Merge pull request #1688 from monopole/moveKustBackToV3
Move kustomize back to v3
2019-10-24 10:45:13 -07:00
Jeffrey Regan
02f9b98b5a Move kustomize back to v3 2019-10-24 10:32:11 -07:00
Jeff Regan
ce7ebe3299 Make all releases draft by default 2019-10-23 17:03:01 -07:00
Roberto C. Morano
3928ada0e5 Add support to inflate charts including '.Release.Name' and .Release.Namespace' references in templates 2019-10-23 10:15:54 +02:00
Jeff Regan
0a8faced8f Merge pull request #1679 from monopole/updateCloudbuildAgain
Improve the release process instructions
2019-10-22 16:16:36 -07:00
Jeff Regan
3c06debf98 Merge pull request #1678 from monopole/fixCliInternalDeps
Fix internal kustomize CLI deps
2019-10-22 16:14:39 -07:00
Jeff Regan
fcee91eafd Merge pull request #1678 from monopole/fixCliInternalDeps
Fix internal kustomize CLI deps
2019-10-22 15:37:37 -07:00
Jeffrey Regan
b0b3a705f4 Fix internal kustomize CLI deps 2019-10-22 15:27:57 -07:00
Jeff Regan
67c8fbcc3c Merge pull request #1676 from monopole/useLatestCloudbuilderSigh
Use latest cloudbuilder image.
2019-10-22 12:50:33 -07:00
Jeffrey Regan
9b50b78ec8 Use latest cloudbuilder image. 2019-10-22 12:49:38 -07:00
Jeff Regan
943a1b0195 Merge pull request #1675 from monopole/pinKustomizeToApiV011
Pin kustomize CLI to API v0.1.1
2019-10-22 12:40:12 -07:00
Jeffrey Regan
f77143cd34 Pin kustomize CLI to API v0.1.1 2019-10-22 12:32:05 -07:00
Jeff Regan
b7d2ba2376 Merge pull request #1674 from monopole/dealWithDeprecationsInGoReleaser
Deal with goreleaser field deprecations.
2019-10-22 12:12:34 -07:00
Jeffrey Regan
c28a0eb83d Deal with goreleaser field deprecations. 2019-10-22 12:11:13 -07:00
Jeff Regan
f7e5b5138b Merge pull request #1673 from monopole/moreTweaks
Add tag check to release script
2019-10-22 11:52:55 -07:00
Jeffrey Regan
61149cbf21 Add tag check to release script 2019-10-22 11:51:26 -07:00
Jeff Regan
310d516030 Update README.md 2019-10-22 10:16:51 -07:00
Jeff Regan
a9e3fe155b Merge pull request #1672 from monopole/moreReleasingTweaks
Pin the cloudbuild step container tags.
2019-10-22 10:13:32 -07:00
Jeffrey Regan
24837bad40 More releasing tweaks. 2019-10-22 10:10:47 -07:00
Guangming Wang
9686cc9861 delete duplicated entry in slice 2019-10-22 21:45:55 +08:00
Dominykas Djacenko
a4784ee5ec Refactor exec plugin load to error if plugin isn't executable 2019-10-21 19:49:27 -07:00
Jeff Regan
3063560e77 Merge pull request #1670 from monopole/installKubevalNotes
Add kubeval install notes.
2019-10-21 19:34:41 -07:00
jregan
bff0604bee Add kubeval install notes. 2019-10-21 19:33:45 -07:00
Jeff Regan
9f8faa7d7e Merge pull request #1669 from monopole/cloudBuildTake9834
Cloud build adjustments.
2019-10-21 19:03:03 -07:00
jregan
335077eade Cloud build adjustments. 2019-10-21 19:00:52 -07:00
Jeff Regan
491baa74cb Merge pull request #1668 from monopole/tweakReleaseProcess
Experimenting with cloud build and goreleaser
2019-10-21 17:33:52 -07:00
Jeff Regan
2f2d078669 Update README.md 2019-10-21 17:31:40 -07:00
Jeff Regan
b7bcb90057 Update README.md 2019-10-21 16:43:35 -07:00
Jeff Regan
35dc15b16b Merge pull request #1667 from monopole/setupApiRelease
Define the API release process.
2019-10-21 16:37:41 -07:00
Jeffrey Regan
83f70877c8 Define the API release process. 2019-10-21 16:24:16 -07:00
Jeff Regan
98cd31b820 Merge pull request #1666 from monopole/modulesForAll
Each plugin gets its own module.
2019-10-21 16:16:25 -07:00
Jeffrey Regan
5416ae7365 Introduce API module. 2019-10-21 15:21:04 -07:00
Kubernetes Prow Robot
644f2ddcdc Merge pull request #1665 from monopole/introduceApiModule
Introduce API module.
2019-10-21 15:13:22 -07:00
Jeffrey Regan
46524d3b6a Introduce API module. 2019-10-21 13:30:31 -07:00
Jeff Regan
4f014d0262 Merge pull request #1664 from monopole/moveGeneratedCode
Move generated code
2019-10-21 13:21:19 -07:00
Jeffrey Regan
0cf2057fc5 Install generated plugin code into the API module. 2019-10-21 11:15:51 -07:00
Jeffrey Regan
3f08e1546c Move generated plugin code. 2019-10-21 09:48:11 -07:00
Jeff Regan
10619fb0f7 Merge pull request #1662 from monopole/builtinNestingPlace
Make new nesting place for generated builtin code.
2019-10-20 17:53:02 -07:00
jregan
c88e8cc057 Make new nesting place for generated builtin code. 2019-10-20 17:41:09 -07:00
Jeff Regan
327a3f5300 Merge pull request #1661 from monopole/moveK8sdeps
Move ks8deps to api for now.
2019-10-20 16:51:47 -07:00
jregan
fddde81f9c Move ks8deps to api for now. 2019-10-20 16:49:53 -07:00
Jeff Regan
22d07ed37d Merge pull request #1660 from monopole/drainTopInternal
Drain top internal folder
2019-10-20 16:14:08 -07:00
jregan
dee1c425da Drain the top level internal. 2019-10-20 15:51:04 -07:00
jregan
951d15bf17 Make api/plugins 2019-10-20 15:12:13 -07:00
Jeff Regan
0f82d2932c Merge pull request #1658 from monopole/makeInventoryPublic
Make inventory public.
2019-10-20 11:46:09 -07:00
jregan
e2d7a06e9f Make inventory public. 2019-10-20 11:43:36 -07:00
Jeff Regan
286b9c1aed Merge pull request #1657 from monopole/makeGitPrivate
Make git package private
2019-10-20 11:43:25 -07:00
jregan
f54d4a5837 Make git package private 2019-10-20 11:36:10 -07:00
Jeff Regan
d9031fb2c9 Merge pull request #1656 from monopole/makeResourcePublic
Make resource, resmap and ifc public.
2019-10-20 11:14:22 -07:00
jregan
3af5a8afea Make resource, resmap public. 2019-10-20 10:51:20 -07:00
Jeff Regan
e2fd33c54a Merge pull request #1655 from monopole/drainPkgTransformers
Drain pkg transformers
2019-10-20 09:50:59 -07:00
jregan
c90e0a4080 Drain pkg/transformers. 2019-10-20 09:19:07 -07:00
jregan
5de000ee3d Move FieldSpec to API. 2019-10-20 06:52:13 -07:00
Jeff Regan
c28b82510c Merge pull request #1654 from monopole/makeSmpPrivate
Make SMP code private to CLI.
2019-10-18 17:16:10 -07:00
jregan
fda3ba8af9 Make SMP code private to CLI. 2019-10-18 17:14:43 -07:00
Kubernetes Prow Robot
fd1356e5d8 Merge pull request #1653 from monopole/docsUpdate
Update docs
2019-10-18 13:13:37 -07:00
jregan
a62f1364fe Update docs 2019-10-18 12:46:36 -07:00
Jeff Regan
d1240bcc63 Update INSTALL.md 2019-10-18 10:48:54 -07:00
Jeff Regan
1c24fe7d16 Merge pull request #1651 from monopole/startApi
Start api directory, which will become the api module.
2019-10-17 15:03:57 -07:00
Jeffrey Regan
e5c8b5ec8f Start api directory, which will become a module. 2019-10-17 14:01:20 -07:00
Jeff Regan
180429774a Merge pull request #1649 from monopole/fixNits
fixNits
2019-10-17 11:16:46 -07:00
Jeffrey Regan
586bba0b31 fixNits 2019-10-17 11:15:10 -07:00
Jeff Regan
2ce138ab3a Update awker.sh 2019-10-16 20:40:21 -07:00
Jeff Regan
5e99ad000e Merge pull request #1645 from monopole/docTypes
Document the types package.
2019-10-16 17:25:05 -07:00
jregan
0f0e740c21 Document the types package. 2019-10-16 17:24:21 -07:00
Kubernetes Prow Robot
33600189bc Merge pull request #1644 from monopole/makeTypesPublic
Make types package public.
2019-10-16 16:58:38 -07:00
jregan
07d2500ee3 Make types package public. 2019-10-16 16:41:43 -07:00
Jeff Regan
de6eb14867 Merge pull request #1643 from monopole/cleanupTypes
Cleanup types package before going public.
2019-10-16 16:24:40 -07:00
jregan
85b71a31e3 Cleanup types package before going public. 2019-10-16 16:02:01 -07:00
Jeff Regan
16e7638220 Merge pull request #1642 from monopole/makeHasherPublic
Maker hasher public for now.
2019-10-16 14:21:44 -07:00
jregan
04c23b2085 Maker hasher public for now. 2019-10-16 12:25:09 -07:00
Kubernetes Prow Robot
24db94dd0d Merge pull request #1638 from dbachrach/f-edit-set-replicas
Add support for kustomize edit set replicas
2019-10-15 19:44:07 -07:00
Kubernetes Prow Robot
5f862ba17c Merge pull request #1639 from dbachrach/b-zero-replica-formatting
Fix yaml formatting of replicas count when count is 0
2019-10-15 17:00:13 -07:00
Jeff Regan
18ba3ee91b Merge pull request #1640 from monopole/moveKvStuff
Extract kv loader from file loader, and place in public package.
2019-10-15 16:59:21 -07:00
Jeffrey Regan
4e9d42fae7 Move kv loader code to public package. 2019-10-15 16:52:03 -07:00
Dustin Bachrach
52e57dab7f FIx yaml formatting of replicas count when count is 0
Previously, a count of 0 would cause the count field to be omitted from the yaml
2019-10-15 15:47:53 -07:00
Dustin Bachrach
ba464a5e11 Add support for kustomize edit set replicas
New command line tool for editing replica counts for resources.
Example:
  kustomize edit set replicas app=3 other-app=1
2019-10-15 15:43:10 -07:00
Jeff Regan
2734085fb0 Merge pull request #1634 from tkellen/gh-1600-failing
make failing test showing spurious variable conflicts
2019-10-15 14:24:28 -07:00
Tyler
d21ff7cfe6 make test pass with reference to bug 2019-10-15 16:59:32 -04:00
Jeff Regan
3a15f450a9 Merge pull request #1637 from monopole/renameDataSources
Rename DataSources to KvPairSources and remove deprecated env field.
2019-10-15 13:47:44 -07:00
Jeffrey Regan
bb77e7491a Rename DataSources to KvPairSources and remove deprecated env field. 2019-10-15 13:39:42 -07:00
Tyler
aa82240b4c use namespace value when making Var 2019-10-15 14:47:05 -04:00
Kubernetes Prow Robot
cac7b46ebd Merge pull request #1635 from beautytiger/dev-191015-trimfix
fix string trim in normalizeGitHostSpec func
2019-10-15 09:27:53 -07:00
Guangming Wang
367d0e042c fix string trim in normalizeGitHostSpec func
Signed-off-by: Guangming Wang <guangming.wang@daocloud.io>
2019-10-16 00:06:02 +08:00
Tyler
d851305c33 make failing test showing spurious variable conflicts 2019-10-15 00:33:33 -04:00
Jeff Regan
0c52bd71ba Merge pull request #1633 from monopole/startPlugLib
Start pluglib, a set of public, plugin specific functions.
2019-10-14 20:25:15 -07:00
Jeffrey Regan
41a008e9a3 Start pluglib, a set of public, plugin specific functions. 2019-10-14 19:43:55 -07:00
Jeff Regan
2fadb4dd59 Merge pull request #1630 from monopole/moveFilesysUp
Move filesys package up to make public.
2019-10-14 12:50:15 -07:00
Jeffrey Regan
a88ee3f93c Move filesys package up to make public. 2019-10-14 12:40:09 -07:00
Jeff Regan
237848a80b Merge pull request #1629 from monopole/renamePackageFsToFileSys
Rename fs package to filesys
2019-10-14 11:01:43 -07:00
Jeffrey Regan
9e3b837093 Rename fs package to filesys 2019-10-14 10:50:27 -07:00
Jeff Regan
c4eca908ac Merge pull request #1623 from monopole/removeVersionCheckFromMinecraft
Remove minecraft version check from chart plugin tests.
2019-10-12 13:03:22 -07:00
jregan
72d9b4cbca Remove minecraft version check from chart plugin tests. 2019-10-12 12:52:36 -07:00
Jeff Regan
19d94110b1 Merge pull request #1621 from scottnuma/patch-1
Update broken URL in Exec Plugin Tutorial
2019-10-12 10:10:41 -07:00
Jeff Regan
1756765dbc Merge pull request #1622 from monopole/fixNits
Fix some nits
2019-10-12 07:43:03 -07:00
jregan
b306f8511c Fix some nits 2019-10-12 07:42:04 -07:00
Scott Numamoto
9778f867b5 Update broken URL in Exec Plugin Tutorial
- link created in the scripts of Exec plugin on linux in 60 seconds to download the kustomize executable does not work
- used the [releases page](https://github.com/kubernetes-sigs/kustomize/releases/tag/v3.0.0) to fix
2019-10-12 01:02:16 -07:00
Jeff Regan
a69ebf2b11 Merge pull request #1576 from chenrui333/go-1.13
Upgrade golang to v1.13
2019-10-11 11:04:23 -07:00
Jeff Regan
cf9c81f908 Merge pull request #1615 from monopole/moreCodeToDiscussIn1297
More composition testing
2019-10-11 11:01:02 -07:00
Jeffrey Regan
b95164b9a8 More code to discuss in 1297 2019-10-11 10:37:29 -07:00
Rui Chen
0551338958 Merge branch 'master' of github.com:kubernetes-sigs/kustomize into go-1.13
* 'master' of github.com:kubernetes-sigs/kustomize: (27 commits)
  Update complexcomposition_test.go
  doc: add configmap generator key example
  tweakNamesInTest
  cleanup on errors in git loader
  refactor complexcomposition_test in prep for addition
  Update INSTALL.md
  Update INSTALL.md
  Introduce dummy program to help with API releases.
  fix zh-doc
  v3.3.0 release notes
  Move pluginator to kustomize Go API v3.3.0
  Three builders.
  Update README.md
  update release process doc
  improve tests for alternative kustomization file names
  Reduce size of pgmconfig package
  Break the dep between fs and pgmconfig.
  Improve fs package and doc in prep to officially go public
  Update versioning notes.
  Fix: documentation link for plugins
  ...
2019-10-11 11:18:20 -04:00
Kubernetes Prow Robot
40b7ad23ea Merge pull request #1618 from seriousben/add-configmap-key-example
doc: add configmap generator key example
2019-10-10 15:19:21 -07:00
Kubernetes Prow Robot
ce66ceeed6 Merge pull request #1616 from jeffmhastings/cleanup-git-clone-errors
cleanup on errors in git loader
2019-10-10 15:17:21 -07:00
Jeff Regan
4e45af6265 Update complexcomposition_test.go 2019-10-10 13:16:45 -07:00
Benjamin Boudreau
07a9454215 doc: add configmap generator key example 2019-10-10 16:11:37 -04:00
Jeff Regan
9f5a936236 Merge pull request #1617 from monopole/tweakNamesInTest
tweakNamesInTest
2019-10-10 13:04:25 -07:00
Jeffrey Regan
e6770e5f1e tweakNamesInTest 2019-10-10 13:03:55 -07:00
Jeff Hastings
705b4ab212 cleanup on errors in git loader 2019-10-10 15:54:56 -04:00
Jeff Regan
2cb964ab8e Merge pull request #1614 from monopole/refactorComplexTest
refactor complexcomposition_test in prep for addition
2019-10-10 11:28:27 -07:00
Jeffrey Regan
949b10bf93 refactor complexcomposition_test in prep for addition 2019-10-10 11:25:38 -07:00
guoxudong
0115fbc3da add zh doc transformerconfigs.md validationTransformer.md 2019-10-10 15:27:10 +08:00
Jeff Regan
cc4341c546 Update INSTALL.md 2019-10-09 11:16:19 -07:00
Jeff Regan
d0caea0ce1 Update INSTALL.md
fix #1604
2019-10-09 11:15:48 -07:00
Jeff Regan
f2ac5a2d0d Merge pull request #1603 from monopole/kustapiversion
Introduce dummy program to help with API releases.
2019-10-08 15:39:44 -07:00
jregan
78d14d0d75 Introduce dummy program to help with API releases. 2019-10-08 15:15:24 -07:00
Kubernetes Prow Robot
d5034af5ca Merge pull request #1519 from sunny0826/zh
zh example:chart,secret generator plugin
2019-10-08 11:25:50 -07:00
guoxudong
40ed9e6a44 fix zh-doc 2019-10-08 09:49:55 +08:00
Jeff Regan
c1d20546ec Merge pull request #1597 from monopole/v330releaseNotes
v3.3.0 release notes
2019-10-07 17:35:02 -07:00
Jeffrey Regan
3cf6b8ec4d v3.3.0 release notes 2019-10-07 17:34:21 -07:00
Jeff Regan
3aee7a9081 Merge pull request #1596 from monopole/updatePluginator
Move pluginator to kustomize Go API v3.3.0
2019-10-07 16:47:28 -07:00
Jeffrey Regan
abefa2b155 Move pluginator to kustomize Go API v3.3.0 2019-10-07 16:46:16 -07:00
Jeff Regan
5d800f0b0a Merge pull request #1595 from monopole/threeReleases
Differentiate the cloud builders.
2019-10-07 15:45:01 -07:00
Jeffrey Regan
4eb2d5bcc2 Three builders. 2019-10-07 15:40:36 -07:00
Jeff Regan
988af1ff61 Update README.md 2019-10-07 12:38:21 -07:00
Kubernetes Prow Robot
1617183ea4 Merge pull request #1590 from monopole/releaseProcessUpdate
update release process doc
2019-10-07 11:39:11 -07:00
jregan
ee72746481 update release process doc 2019-10-07 10:21:29 -07:00
Jeff Regan
c9e7dc3bfa Merge pull request #1589 from monopole/moreTestsAroundKustFileName
improve tests for alternative kustomization file names
2019-10-04 10:22:04 -07:00
Jeffrey Regan
07e0e46ac7 improve tests for alternative kustomization file names 2019-10-04 10:17:49 -07:00
Jeff Regan
404d2d631a Merge pull request #1587 from monopole/reducePgmconfig
Reduce size of pgmconfig package
2019-10-03 18:37:27 -07:00
Jeffrey Regan
baa0296a12 Reduce size of pgmconfig package 2019-10-03 18:22:05 -07:00
Jeff Regan
0f665ac153 Merge pull request #1544 from ptux/add-transformer-href
add transformer href
2019-10-03 16:38:47 -07:00
Jeff Regan
14b0a65091 Merge pull request #1581 from monopole/refactorFs
Break the dep between fs and pgmconfig.
2019-10-02 12:39:02 -07:00
Jeffrey Regan
2d58f8b81c Break the dep between fs and pgmconfig. 2019-10-02 12:01:45 -07:00
Jeff Regan
9a43ca53cc Merge pull request #1578 from nlamirault/fix/build-plugins-doc
Fix: documentation link for plugins
2019-10-02 11:15:41 -07:00
Jeff Regan
5372fc6f6c Merge pull request #1579 from monopole/fsPackageCleanup
Improve fs package and doc in prep to officially go public
2019-10-01 12:16:38 -07:00
Kubernetes Prow Robot
86bc344057 Merge pull request #1513 from nimohunter/fix_empty_list_item
empty list or map item return error
2019-10-01 12:13:34 -07:00
Jeff Regan
a014f7d414 Merge pull request #1561 from beautytiger/dev-190925
improve test code coverage in transformers
2019-10-01 12:13:00 -07:00
Jeffrey Regan
9a94bcb854 Improve fs package and doc in prep to officially go public 2019-10-01 11:12:27 -07:00
Jeff Regan
07634ef098 Merge pull request #1575 from monopole/versioning
Update versioning notes.
2019-09-30 18:42:14 -07:00
jregan
995f88d60c Update versioning notes. 2019-09-30 18:41:09 -07:00
Rui Chen
5caba59073 Upgrade golang to v1.13 2019-09-29 16:34:18 -04:00
Nicolas Lamirault
334a64676f Fix: documentation link for plugins
Signed-off-by: Nicolas Lamirault <nicolas.lamirault@gmail.com>
2019-09-27 09:22:35 +02:00
Guangming Wang
08963ba503 improve test code coverage in transformers
Signed-off-by: Guangming Wang <guangming.wang@daocloud.io>
2019-09-27 14:53:58 +08:00
Jeff Regan
326fb689be Merge pull request #1570 from bzub/1234-go_plugin_doc
Fix compile kustomize example.
2019-09-26 17:40:11 -07:00
Jeff Regan
970ce67c34 Update goPluginCaveats.md 2019-09-26 17:38:50 -07:00
Jeff Regan
98d1893057 Update INSTALL.md 2019-09-26 17:34:53 -07:00
Jeff Regan
d89b448c74 Fix git tag recovery in cloud build.
Deleted in previous PR.  Hard to test locally.
2019-09-26 17:08:58 -07:00
Jeff Regan
17bf9d325b Update releasing README. 2019-09-26 16:58:22 -07:00
Kubernetes Prow Robot
a99aff1d1c Merge pull request #1571 from monopole/updateCloudBuildProcess
Update cloud build process for kustomize.
2019-09-26 16:39:34 -07:00
Jeffrey Regan
a694ac7b63 Update cloud build process for kustomize. 2019-09-26 16:05:35 -07:00
bzub
b5b11ef6e9 Fix compile kustomize example. 2019-09-26 17:37:53 -05:00
Jeff Regan
fa1af6f51e Merge pull request #1473 from richardmarshall/execpluginhash
Support resource generator options in exec plugins
2019-09-26 10:12:52 -07:00
Jeff Regan
9288dec02a Fix failing BashedConfigMapTest 2019-09-26 09:56:04 -07:00
Kubernetes Prow Robot
1a45dd0b4f Merge pull request #1566 from monopole/releaseNotes3.2.1
v3.2.1 release notes
2019-09-26 09:09:08 -07:00
Richard Marshall
592c5acf5a docs: Exec plugin generator options 2019-09-26 08:41:41 -07:00
Richard Marshall
ac9424fa3e tests: Add unit tests for update resource options 2019-09-26 08:41:41 -07:00
Richard Marshall
79fbe7c4cc Support resource generator options in exec plugins 2019-09-26 08:41:41 -07:00
Jeff Regan
f69d526fa3 v3.2.1 release notes 2019-09-25 19:48:37 -07:00
Jeff Regan
07a95a60f6 Merge pull request #1565 from monopole/tweakBinaryDepsBeforeTagging
Pin the kustomize binary's dependence on kustomize libs.
2019-09-25 18:29:38 -07:00
jregan
032b385711 Pin the kustomize binary's dependence on kustomize libs. 2019-09-25 18:09:45 -07:00
Kubernetes Prow Robot
810629596a Merge pull request #1564 from monopole/moveKustomizeBinaryToOwnModule
Move the kustomize binary to its own module.
2019-09-25 16:53:06 -07:00
Jeffrey Regan
b82a8fd316 Move the kustomize binary to its own module. 2019-09-25 15:37:48 -07:00
Kubernetes Prow Robot
2d0c22d6a4 Merge pull request #1562 from keleustes/tools
Pin tool versions using go modules
2019-09-25 13:50:06 -07:00
Ian Howell
aa342deff7 Pin tool versions using go modules
This pins down the versions of various tools used in the building,
testing, and linting of kustomize. This will provide more consistency
across contributors' and travis' environments.
2019-09-25 15:27:47 -05:00
Kubernetes Prow Robot
10786ec0a7 Merge pull request #1554 from keleustes/readme
Update README.md to include Kubernetes 1.16
2019-09-25 09:12:02 -07:00
Jerome Brette
7c7056877b Update README.md to include Kubernetes 1.16 2019-09-25 12:51:51 +00:00
Jeff Regan
e8933d9789 Merge pull request #1560 from monopole/precommitTuneup
Make pre-commit more portable and less tricky.
2019-09-24 21:42:35 -07:00
jregan
9d7b65446f Make pre-commit more portable and less tricky. 2019-09-24 21:10:58 -07:00
Jeff Regan
7a0946a922 Merge pull request #1558 from monopole/dependOnNewPluginatorModule
Depend on new pluginator location.
2019-09-24 18:18:18 -07:00
Jeffrey Regan
def4f04572 Depend on new pluginator location. 2019-09-24 16:43:53 -07:00
Jeff Regan
2f2408f1cd Merge pull request #1559 from monopole/compressCopyright
Compress copyright in the commands package.
2019-09-24 16:43:26 -07:00
Jeffrey Regan
3b9bcc48a0 Compress copyright in the commands package. 2019-09-24 16:40:32 -07:00
Jeff Regan
d0429ff43b Merge pull request #1557 from monopole/pluginatorModule
Copy pluginator to its own module.
2019-09-24 14:38:38 -07:00
Jeffrey Regan
33deefc307 Copy pluginator to its own module. 2019-09-24 11:24:13 -07:00
Jeff Regan
9b3de82b45 Merge pull request #1506 from Liujingfang1/release
add release note for v3.2.0
2019-09-23 10:14:17 -07:00
Kubernetes Prow Robot
d217074fbf Merge pull request #1550 from keleustes/apiversion
Fix typo yaml declaration of APIVersion field of Kustomization type
2019-09-23 09:25:24 -07:00
Jerome Brette
1d90ba7c7b Fix typo in apiVersion yaml declaration 2019-09-22 05:24:58 +00:00
Kubernetes Prow Robot
eeeb4c36a1 Merge pull request #1547 from keleustes/extensions
Update Ingress apiVersion to networking.k8s.io/v1beta1
2019-09-20 13:17:26 -07:00
Jerome Brette
b1faa989f4 Update Ingress apiVersion to networking.k8s.io/v1beta1 2019-09-20 19:37:05 +00:00
nimohunter
d8250c9ee2 move test case 2019-09-19 15:45:31 +08:00
Wang(わん)
c950046659 add transformer href 2019-09-19 11:34:01 +09:00
Kubernetes Prow Robot
0c32691e9e Merge pull request #1537 from jaypipes/gomod-install-note
add note about GO111MODULE for source install
2019-09-18 11:43:31 -07:00
Jeff Regan
88b1d62740 Merge pull request #1541 from rtnpro/patch-1
Fix typo in transformerconfigs README
2019-09-18 11:34:18 -07:00
Jeff Regan
aec8206695 Update INSTALL.md 2019-09-18 11:33:18 -07:00
Jeff Regan
20c2b53a46 Merge pull request #1542 from monopole/tweakFilePathsInTest
Tweak file path handling and logging in test.
2019-09-18 11:19:08 -07:00
Jeffrey Regan
274b5c3b4e Tweak file path handling and logging in test. 2019-09-18 11:17:21 -07:00
Ratnadeep Debnath
b1fdaa2311 Fix typo in transformerconfigs README 2019-09-18 19:01:02 +05:30
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
nimohunter
b5d5e70bdc empty list or map item return error 2019-09-16 09:42:49 +08:00
nimohunter
2e82985380 empty list or map item return error 2019-09-16 09:30:01 +08:00
Jay Pipes
55941f5769 add note about GO111MODULE for source install
It's not immediately obvious that in order to
get the `go install sigs.k8s.io/kustomize/v3/cmd/kustomize` instructions
to work successfully, you need to have `GO111MODULE=on` set, so I added
a note about that.

Issues #1536, #1314
2019-09-14 18:59:07 -04: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
guoxudong
281f932814 zh example:chart,secret generator plugin 2019-09-10 10:19:18 +08: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
nimohunter
9e226001e3 empty list or map item return error 2019-09-09 13:42:18 +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
jingfangliu
77b63f96d1 add release note for v3.2.0 2019-09-05 13:42:43 -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
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
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
859 changed files with 83696 additions and 11584 deletions

3
.gitignore vendored
View File

@@ -5,6 +5,9 @@
*.so
*.dylib
.idea
*.iml
# Test binary, build with `go test -c`
*.test

View File

@@ -4,25 +4,38 @@ run:
linters:
disable-all: true
enable:
- bodyclose
- deadcode
- depguard
- dupl
- goconst
- gocyclo
- gofmt
- goimports
- golint
- gosec
- gosimple
- govet
- ineffassign
- interfacer
- lll
- misspell
- nakedret
- staticcheck
- structcheck
# stylecheck demands that acronyms not be treated as words
# in camelCase, so JsonOp become JSONOp, etc. Yuck.
# - stylecheck
- typecheck
- unconvert
- unused
- unparam
- varcheck
linters-settings:
dupl:
threshold: 400
lll:
threshold: 400
lll:
line-length: 170
gocyclo:
min-complexity: 15

View File

@@ -1,7 +1,10 @@
os:
- linux
- osx
# TODO: Uncomment when tests running on Windows.
# TODO: Speed up the slowness of the osx travis runs
# Maybe cache brew installs?
#
# TODO: Uncomment when some gets the tests running on Windows.
# - windows
addons:
@@ -20,24 +23,19 @@ git:
language: go
go:
- "1.12"
- "1.13"
go_import_path: sigs.k8s.io/kustomize
before_install:
- source ./travis/consider-early-travis-exit.sh
- curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(go env GOPATH)/bin v1.17.1
- go get -u github.com/monopole/mdrip
# The following would install Helm if needed for some reason.
# - wget https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz
# - tar -xvzf helm-v2.13.1-linux-amd64.tar.gz
# - sudo mv linux-amd64/helm /usr/local/bin/helm
# Skip the install process; let pre-commit.sh do it.
install: true
script:
- ./travis/pre-commit.sh
- make verify-kustomize
- ./travis/kyaml-pre-commit.sh
# TBD. Suppressing for now.
notifications:

View File

@@ -6,11 +6,15 @@ _As contributors and maintainers of this project, and in the interest of fosteri
## Getting Started
Dev guides:
- [Mac](docs/macDevGuide.md)
We have full documentation on how to get started contributing here:
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing)
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet.md) - Common resources for existing developers
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md) - Common resources for existing developers
## Mentorship

250
Makefile Normal file
View File

@@ -0,0 +1,250 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
#
# Makefile for kustomize CLI and API.
MYGOBIN := $(shell go env GOPATH)/bin
SHELL := /bin/bash
export PATH := $(MYGOBIN):$(PATH)
.PHONY: all
all: verify-kustomize
.PHONY: verify-kustomize
verify-kustomize: \
lint-kustomize \
test-unit-kustomize-all \
test-examples-kustomize-against-HEAD \
test-examples-kustomize-against-latest
# Other builds in this repo might want a different linter version.
# Without one Makefile to rule them all, the different makes
# cannot assume that golanci-lint is at the version they want
# since everything uses the same implicit GOPATH.
# This installs in a temp dir to avoid overwriting someone else's
# linter, then installs in MYGOBIN with a new name.
# Version pinned by api/go.mod
$(MYGOBIN)/golangci-lint-kustomize:
( \
set -e; \
export GOBIN=$$(mktemp -d) \
cd api; \
GO111MODULE=on go install github.com/golangci/golangci-lint/cmd/golangci-lint; \
mv $$GOBIN/golangci-lint $(MYGOBIN)/golangci-lint-kustomize \
)
# Version pinned by api/go.mod
$(MYGOBIN)/mdrip:
cd api; \
go install github.com/monopole/mdrip
# Version pinned by api/go.mod
$(MYGOBIN)/stringer:
cd api; \
go install golang.org/x/tools/cmd/stringer
# Version pinned by api/go.mod
$(MYGOBIN)/goimports:
cd api; \
go install golang.org/x/tools/cmd/goimports
# To pin pluginator, use this recipe instead:
# cd api;
# go install sigs.k8s.io/kustomize/pluginator/v2
$(MYGOBIN)/pluginator:
cd pluginator; \
go install .
# Install kustomize from whatever is checked out.
$(MYGOBIN)/kustomize:
cd kustomize; \
go install .
.PHONY: install-tools
install-tools: \
$(MYGOBIN)/goimports \
$(MYGOBIN)/golangci-lint-kustomize \
$(MYGOBIN)/mdrip \
$(MYGOBIN)/pluginator \
$(MYGOBIN)/stringer
### Begin kustomize plugin rules.
#
# The rules to deal with builtin plugins are a bit
# complicated because
#
# - Every builtin plugin is a Go plugin -
# meaning it gets its own module directory
# (outside of the api module) with Go
# code in a 'main' package per Go plugin rules.
# - kustomize locates plugins using the
# 'apiVersion' and 'kind' fields from the
# plugin config file.
# - k8s wants CamelCase in 'kind' fields.
# - The module name (the last name in the path)
# must be the lowercased 'kind' of the
# plugin because Go and related tools
# demand lowercase in import paths, but
# allow CamelCase in file names.
# - the generated code must live in the api
# module (it's linked into the api).
# Where all generated builtin plugin code should go.
pGen=api/builtins
# Where the builtin Go plugin modules live.
pSrc=plugin/builtin
_builtinplugins = \
AnnotationsTransformer.go \
ConfigMapGenerator.go \
HashTransformer.go \
ImageTagTransformer.go \
InventoryTransformer.go \
LabelTransformer.go \
LegacyOrderTransformer.go \
NamespaceTransformer.go \
PatchJson6902Transformer.go \
PatchStrategicMergeTransformer.go \
PatchTransformer.go \
PrefixSuffixTransformer.go \
ReplicaCountTransformer.go \
SecretGenerator.go
# Maintaining this explicit list of generated files, and
# adding it as a dependency to a few targets, to assure
# they get recreated if deleted. The rules below on how
# to make them don't, by themselves, assure they will be
# recreated if deleted.
builtinplugins = $(patsubst %,$(pGen)/%,$(_builtinplugins))
# These rules are verbose, but assure that if a source file
# is modified, the corresponding generated file, and only
# that file, will be recreated.
$(pGen)/AnnotationsTransformer.go: $(pSrc)/annotationstransformer/AnnotationsTransformer.go
$(pGen)/ConfigMapGenerator.go: $(pSrc)/configmapgenerator/ConfigMapGenerator.go
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
$(pGen)/InventoryTransformer.go: $(pSrc)/inventorytransformer/InventoryTransformer.go
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
$(pGen)/LegacyOrderTransformer.go: $(pSrc)/legacyordertransformer/LegacyOrderTransformer.go
$(pGen)/NamespaceTransformer.go: $(pSrc)/namespacetransformer/NamespaceTransformer.go
$(pGen)/PatchJson6902Transformer.go: $(pSrc)/patchjson6902transformer/PatchJson6902Transformer.go
$(pGen)/PatchStrategicMergeTransformer.go: $(pSrc)/patchstrategicmergetransformer/PatchStrategicMergeTransformer.go
$(pGen)/PatchTransformer.go: $(pSrc)/patchtransformer/PatchTransformer.go
$(pGen)/PrefixSuffixTransformer.go: $(pSrc)/prefixsuffixtransformer/PrefixSuffixTransformer.go
$(pGen)/ReplicaCountTransformer.go: $(pSrc)/replicacounttransformer/ReplicaCountTransformer.go
$(pGen)/SecretGenerator.go: $(pSrc)/secretgenerator/SecretGenerator.go
# The (verbose but portable) Makefile way to convert to lowercase.
toLowerCase = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
$(pGen)/%.go: $(MYGOBIN)/pluginator
@echo "generating $*"
( \
set -e; \
cd $(pSrc)/$(call toLowerCase,$*); \
go generate .; \
cd ../../../$(pGen); \
$(MYGOBIN)/goimports -w $*.go \
)
# Target is for debugging.
.PHONY: generate-kustomize-builtin-plugins
generate-kustomize-builtin-plugins: $(builtinplugins)
.PHONY: kustomize-external-go-plugin-build
kustomize-external-go-plugin-build:
./hack/buildExternalGoPlugins.sh ./plugin
.PHONY: kustomize-external-go-plugin-clean
kustomize-external-go-plugin-clean:
./hack/buildExternalGoPlugins.sh ./plugin clean
### End kustomize plugin rules.
.PHONY: lint-kustomize
lint-kustomize: install-tools $(builtinplugins)
cd api; \
$(MYGOBIN)/golangci-lint-kustomize -c ../.golangci-kustomize.yml run ./...
cd kustomize; \
$(MYGOBIN)/golangci-lint-kustomize -c ../.golangci-kustomize.yml run ./...
cd pluginator; \
$(MYGOBIN)/golangci-lint-kustomize -c ../.golangci-kustomize.yml run ./...
.PHONY: test-unit-kustomize-api
test-unit-kustomize-api: $(builtinplugins)
cd api; go test ./...
.PHONY: test-unit-kustomize-plugins
test-unit-kustomize-plugins:
./hack/testUnitKustomizePlugins.sh
.PHONY: test-unit-kustomize-cli
test-unit-kustomize-cli:
cd kustomize; go test ./...
.PHONY: test-unit-kustomize-all
test-unit-kustomize-all: \
test-unit-kustomize-api \
test-unit-kustomize-cli \
test-unit-kustomize-plugins
.PHONY:
test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY:
test-examples-kustomize-against-latest: $(MYGOBIN)/mdrip
( \
set -e; \
/bin/rm -f $(MYGOBIN)/kustomize; \
echo "Installing kustomize from latest."; \
GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v3; \
./hack/testExamplesAgainstKustomize.sh latest; \
echo "Reinstalling kustomize from HEAD."; \
cd kustomize; go install .; \
)
# linux only.
# This is for testing an example plugin that
# uses kubeval for validation.
# Don't want to add a hard dependence in go.mod file
# to github.com/instrumenta/kubeval.
# Instead, download the binary.
$(MYGOBIN)/kubeval:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz; \
tar xf kubeval-linux-amd64.tar.gz; \
mv kubeval $(MYGOBIN); \
rm -rf $$d; \
)
# linux only.
# This is for testing an example plugin that
# uses helm to inflate a chart for subsequent kustomization.
# Don't want to add a hard dependence in go.mod file
# to helm.
# Instead, download the binary.
$(MYGOBIN)/helm:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
wget https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz; \
tar -xvzf helm-v2.13.1-linux-amd64.tar.gz; \
mv linux-amd64/helm $(MYGOBIN); \
rm -rf $$d \
)
.PHONY: clean
clean: kustomize-external-go-plugin-clean
go clean --cache
rm -f $(builtinplugins)
rm -f $(MYGOBIN)/pluginator
rm -f $(MYGOBIN)/kustomize
rm -f $(MYGOBIN)/golangci-lint-kustomize
.PHONY: nuke
nuke: clean
sudo rm -rf $(shell go env GOPATH)/pkg/mod/sigs.k8s.io

View File

@@ -1,7 +1,5 @@
# kustomize
_[v3.0.0](https://github.com/kubernetes-sigs/kustomize/releases/tag/v3.0.0) is the latest release._
`kustomize` lets you customize raw, template-free YAML
files for multiple purposes, leaving the original YAML
untouched and usable as is.
@@ -24,8 +22,17 @@ 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.15][kubectl].
## kubectl integration
Since [v1.14][kubectl announcement] the kustomize build system has been included in kubectl.
| kubectl version | kustomize version |
|---------|--------|
| v1.16.0 | [v2.0.3](/../../tree/v2.0.3) |
| v1.15.x | [v2.0.3](/../../tree/v2.0.3) |
| v1.14.x | [v2.0.3](/../../tree/v2.0.3) |
For examples and guides for using the kubectl integration please see the [kubectl book] or the [kubernetes documentation].
## Usage
@@ -160,18 +167,20 @@ is governed by the [Kubernetes Code of Conduct].
[eschewed feature list]: docs/eschewedFeatures.md
[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
[kind/feature]: /../../labels/kind%2Ffeature
[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
[overlays]: docs/glossary.md#overlay
[release page]: https://github.com/kubernetes-sigs/kustomize/releases
[release page]: /../../releases
[resource]: docs/glossary.md#resource
[resources]: docs/glossary.md#resource
[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md
[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
[v2.0.3]: /../../releases/tag/v2.0.3
[v2.1.0]: /../../releases/tag/v2.1.0
[workflows]: docs/workflows.md

View File

@@ -0,0 +1,39 @@
// Code generated by pluginator on AnnotationsTransformer; DO NOT EDIT.
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
// Add the given annotations to the given field specifications.
type AnnotationsTransformerPlugin struct {
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *AnnotationsTransformerPlugin) Config(
_ *resmap.PluginHelpers, c []byte) (err error) {
p.Annotations = nil
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
t, err := transform.NewMapTransformer(
p.FieldSpecs,
p.Annotations,
)
if err != nil {
return err
}
return t.Transform(m)
}
func NewAnnotationsTransformerPlugin() resmap.TransformerPlugin {
return &AnnotationsTransformerPlugin{}
}

View File

@@ -1,28 +1,24 @@
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/api/kv"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
type ConfigMapGeneratorPlugin struct {
ldr ifc.Loader
rf *resmap.Factory
h *resmap.PluginHelpers
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
types.GeneratorOptions
types.ConfigMapArgs
}
//noinspection GoUnusedGlobalVariable
func NewConfigMapGeneratorPlugin() *ConfigMapGeneratorPlugin {
return &ConfigMapGeneratorPlugin{}
}
func (p *ConfigMapGeneratorPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
h *resmap.PluginHelpers, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.ConfigMapArgs = types.ConfigMapArgs{}
err = yaml.Unmarshal(config, p)
@@ -32,11 +28,16 @@ func (p *ConfigMapGeneratorPlugin) Config(
if p.ConfigMapArgs.Namespace == "" {
p.ConfigMapArgs.Namespace = p.Namespace
}
p.ldr = ldr
p.rf = rf
p.h = h
return
}
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
return p.rf.FromConfigMapArgs(p.ldr, &p.GeneratorOptions, p.ConfigMapArgs)
return p.h.ResmapFactory().FromConfigMapArgs(
kv.NewLoader(p.h.Loader(), p.h.Validator()),
&p.GeneratorOptions, p.ConfigMapArgs)
}
func NewConfigMapGeneratorPlugin() resmap.GeneratorPlugin {
return &ConfigMapGeneratorPlugin{}
}

View File

@@ -1,25 +1,22 @@
// Code generated by pluginator on HashTransformer; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resmap"
)
type HashTransformerPlugin struct {
hasher ifc.KunstructuredHasher
}
//noinspection GoUnusedGlobalVariable
func NewHashTransformerPlugin() *HashTransformerPlugin {
return &HashTransformerPlugin{}
}
func (p *HashTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
p.hasher = rf.RF().Hasher()
h *resmap.PluginHelpers, _ []byte) (err error) {
p.hasher = h.ResmapFactory().RF().Hasher()
return nil
}
@@ -36,3 +33,7 @@ func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error {
}
return nil
}
func NewHashTransformerPlugin() resmap.TransformerPlugin {
return &HashTransformerPlugin{}
}

View File

@@ -1,34 +1,30 @@
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
"regexp"
"strings"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/image"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/transformers"
"sigs.k8s.io/kustomize/v3/pkg/transformers/config"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/yaml"
)
// Find matching image declarations and replace
// the name, tag and/or digest.
type ImageTagTransformerPlugin struct {
ImageTag image.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
//noinspection GoUnusedGlobalVariable
func NewImageTagTransformerPlugin() *ImageTagTransformerPlugin {
return &ImageTagTransformerPlugin{}
ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *ImageTagTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.ImageTag = image.Image{}
_ *resmap.PluginHelpers, c []byte) (err error) {
p.ImageTag = types.Image{}
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
@@ -39,7 +35,7 @@ func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
if !r.OrgId().IsSelected(&path.Gvk) {
continue
}
err := transformers.MutateField(
err := transform.MutateField(
r.Map(), path.PathSlice(), false, p.mutateImage)
if err != nil {
return err
@@ -86,7 +82,7 @@ func (p *ImageTagTransformerPlugin) findAndReplaceImage(obj map[string]interface
updated := false
for _, path := range paths {
containers, found := obj[path]
if found {
if found && containers != nil {
if _, err := p.updateContainers(containers); err != nil {
return err
}
@@ -149,7 +145,7 @@ func (p *ImageTagTransformerPlugin) findContainers(obj map[string]interface{}) e
func isImageMatched(s, t string) bool {
// Tag values are limited to [a-zA-Z0-9_.-].
pattern, _ := regexp.Compile("^" + t + "(:[a-zA-Z0-9_.-]*)?$")
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.-]*)?$")
return pattern.MatchString(s)
}
@@ -175,7 +171,7 @@ func split(imageName string) (name string, tag string) {
}
i := ic
if ic < 0 {
if ia > 0 {
i = ia
}
@@ -183,3 +179,7 @@ func split(imageName string) (name string, tag string) {
tag = imageName[i:]
return
}
func NewImageTagTransformerPlugin() resmap.TransformerPlugin {
return &ImageTagTransformerPlugin{}
}

View File

@@ -1,36 +1,30 @@
// Code generated by pluginator on InventoryTransformer; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/pkg/resource"
"sigs.k8s.io/kustomize/v3/pkg/hasher"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/inventory"
"sigs.k8s.io/kustomize/v3/pkg/resid"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/api/hasher"
"sigs.k8s.io/kustomize/api/inventory"
"sigs.k8s.io/kustomize/api/kv"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
type InventoryTransformerPlugin struct {
ldr ifc.Loader
rf *resmap.Factory
h *resmap.PluginHelpers
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
Policy string `json:"policy,omitempty" yaml:"policy,omitempty"`
}
//noinspection GoUnusedGlobalVariable
func NewInventoryTransformerPlugin() *InventoryTransformerPlugin {
return &InventoryTransformerPlugin{}
}
func (p *InventoryTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.ldr = ldr
p.rf = rf
h *resmap.PluginHelpers, c []byte) (err error) {
p.h = h
err = yaml.Unmarshal(c, p)
if err != nil {
return err
@@ -79,7 +73,8 @@ func (p *InventoryTransformerPlugin) Transform(m resmap.ResMap) error {
return err
}
cm, err := p.rf.RF().MakeConfigMap(p.ldr, opts, &args)
cm, err := p.h.ResmapFactory().RF().MakeConfigMap(
kv.NewLoader(p.h.Loader(), p.h.Validator()), opts, &args)
if err != nil {
return err
}
@@ -127,3 +122,7 @@ func computeRefs(
}
return
}
func NewInventoryTransformerPlugin() resmap.TransformerPlugin {
return &InventoryTransformerPlugin{}
}

View File

@@ -0,0 +1,39 @@
// Code generated by pluginator on LabelTransformer; DO NOT EDIT.
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
// Add the given labels to the given field specifications.
type LabelTransformerPlugin struct {
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *LabelTransformerPlugin) Config(
_ *resmap.PluginHelpers, c []byte) (err error) {
p.Labels = nil
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error {
t, err := transform.NewMapTransformer(
p.FieldSpecs,
p.Labels,
)
if err != nil {
return err
}
return t.Transform(m)
}
func NewLabelTransformerPlugin() resmap.TransformerPlugin {
return &LabelTransformerPlugin{}
}

View File

@@ -1,12 +1,14 @@
// Code generated by pluginator on LegacyOrderTransformer; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource"
"sort"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
)
// Sort the resources using an ordering defined in the Gvk class.
@@ -16,14 +18,9 @@ import (
// (like ValidatingWebhookConfiguration) last.
type LegacyOrderTransformerPlugin struct{}
//noinspection GoUnusedGlobalVariable
func NewLegacyOrderTransformerPlugin() *LegacyOrderTransformerPlugin {
return &LegacyOrderTransformerPlugin{}
}
// Nothing needed for configuration.
func (p *LegacyOrderTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
_ *resmap.PluginHelpers, _ []byte) (err error) {
return nil
}
@@ -43,3 +40,7 @@ func (p *LegacyOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) {
}
return nil
}
func NewLegacyOrderTransformerPlugin() resmap.TransformerPlugin {
return &LegacyOrderTransformerPlugin{}
}

View File

@@ -0,0 +1,131 @@
// Code generated by pluginator on NamespaceTransformer; DO NOT EDIT.
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
// Change or set the namespace of non-cluster level resources.
type NamespaceTransformerPlugin struct {
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *NamespaceTransformerPlugin) Config(
_ *resmap.PluginHelpers, c []byte) (err error) {
p.Namespace = ""
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
if len(p.Namespace) == 0 {
return nil
}
for _, r := range m.Resources() {
if len(r.Map()) == 0 {
// Don't mutate empty objects?
continue
}
id := r.OrgId()
applicableFs := p.applicableFieldSpecs(id)
for _, fs := range applicableFs {
err := transform.MutateField(
r.Map(), fs.PathSlice(), fs.CreateIfNotPresent,
p.changeNamespace(r))
if err != nil {
return err
}
}
matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals)
if len(matches) != 1 {
return fmt.Errorf("namespace tranformation produces ID conflict: %#v", matches)
}
}
return nil
}
const metaNamespace = "metadata/namespace"
// Special casing metadata.namespace since
// all objects have it, even "ClusterKind" objects
// that don't exist in a namespace (the Namespace
// object itself doesn't live in a namespace).
func (p *NamespaceTransformerPlugin) applicableFieldSpecs(id resid.ResId) []types.FieldSpec {
var res []types.FieldSpec
for _, fs := range p.FieldSpecs {
if id.IsSelected(&fs.Gvk) && (fs.Path != metaNamespace || (fs.Path == metaNamespace && id.IsNamespaceableKind())) {
res = append(res, fs)
}
}
return res
}
func (p *NamespaceTransformerPlugin) changeNamespace(
_ *resource.Resource) func(in interface{}) (interface{}, error) {
return func(in interface{}) (interface{}, error) {
switch in.(type) {
case string:
// will happen when the metadata/namespace
// value is replaced
return p.Namespace, nil
case []interface{}:
l, _ := in.([]interface{})
for idx, item := range l {
switch item.(type) {
case map[string]interface{}:
// Will happen when mutating the subjects
// field of ClusterRoleBinding and RoleBinding
inMap, _ := item.(map[string]interface{})
if _, ok := inMap["name"]; !ok {
continue
}
name, ok := inMap["name"].(string)
if !ok {
continue
}
// The only case we need to force the namespace
// if for the "service account". "default" is
// kind of hardcoded here for right now.
if name != "default" {
continue
}
inMap["namespace"] = p.Namespace
l[idx] = inMap
default:
// nothing to do for right now
}
}
return in, nil
case map[string]interface{}:
// Will happen if the createField=true
// when the namespace is added to the
// object
inMap := in.(map[string]interface{})
if len(inMap) == 0 {
return p.Namespace, nil
} else {
return in, nil
}
default:
return in, nil
}
}
}
func NewNamespaceTransformerPlugin() resmap.TransformerPlugin {
return &NamespaceTransformerPlugin{}
}

View File

@@ -1,15 +1,17 @@
// Code generated by pluginator on PatchJson6902Transformer; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/v3/pkg/gvk"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resid"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
@@ -21,14 +23,9 @@ type PatchJson6902TransformerPlugin struct {
JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"`
}
//noinspection GoUnusedGlobalVariable
func NewPatchJson6902TransformerPlugin() *PatchJson6902TransformerPlugin {
return &PatchJson6902TransformerPlugin{}
}
func (p *PatchJson6902TransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.ldr = ldr
h *resmap.PluginHelpers, c []byte) (err error) {
p.ldr = h.Loader()
err = yaml.Unmarshal(c, p)
if err != nil {
return err
@@ -74,7 +71,7 @@ func (p *PatchJson6902TransformerPlugin) Config(
func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
id := resid.NewResIdWithNamespace(
gvk.Gvk{
resid.Gvk{
Group: p.Target.Group,
Version: p.Target.Version,
Kind: p.Target.Kind,
@@ -97,3 +94,7 @@ func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
}
return obj.UnmarshalJSON(modifiedObj)
}
func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin {
return &PatchJson6902TransformerPlugin{}
}

View File

@@ -1,32 +1,27 @@
// Code generated by pluginator on PatchStrategicMergeTransformer; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resource"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
type PatchStrategicMergeTransformerPlugin struct {
ldr ifc.Loader
rf *resmap.Factory
h *resmap.PluginHelpers
loadedPatches []*resource.Resource
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
Patches string `json:patches,omitempty" yaml:"patches,omitempty"`
}
//noinspection GoUnusedGlobalVariable
func NewPatchStrategicMergeTransformerPlugin() *PatchStrategicMergeTransformerPlugin {
return &PatchStrategicMergeTransformerPlugin{}
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
}
func (p *PatchStrategicMergeTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
p.ldr = ldr
p.rf = rf
h *resmap.PluginHelpers, c []byte) (err error) {
p.h = h
err = yaml.Unmarshal(c, p)
if err != nil {
return err
@@ -35,14 +30,22 @@ func (p *PatchStrategicMergeTransformerPlugin) Config(
return fmt.Errorf("empty file path and empty patch content")
}
if len(p.Paths) != 0 {
res, err := p.rf.RF().SliceFromPatches(ldr, p.Paths)
if err != nil {
return err
for _, onePath := range p.Paths {
res, err := p.h.ResmapFactory().RF().SliceFromBytes([]byte(onePath))
if err == nil {
p.loadedPatches = append(p.loadedPatches, res...)
continue
}
res, err = p.h.ResmapFactory().RF().SliceFromPatches(
p.h.Loader(), []types.PatchStrategicMerge{onePath})
if err != nil {
return err
}
p.loadedPatches = append(p.loadedPatches, res...)
}
p.loadedPatches = res
}
if p.Patches != "" {
res, err := p.rf.RF().SliceFromBytes([]byte(p.Patches))
res, err := p.h.ResmapFactory().RF().SliceFromBytes([]byte(p.Patches))
if err != nil {
return err
}
@@ -57,7 +60,7 @@ func (p *PatchStrategicMergeTransformerPlugin) Config(
}
func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error {
patches, err := p.rf.MergePatches(p.loadedPatches)
patches, err := p.h.ResmapFactory().MergePatches(p.loadedPatches)
if err != nil {
return err
}
@@ -70,6 +73,18 @@ func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error
if err != nil {
return err
}
// remove the resource from resmap
// when the patch is to $patch: delete that target
if len(target.Map()) == 0 {
err = m.Remove(target.CurId())
if err != nil {
return err
}
}
}
return nil
}
func NewPatchStrategicMergeTransformerPlugin() resmap.TransformerPlugin {
return &PatchStrategicMergeTransformerPlugin{}
}

View File

@@ -0,0 +1,145 @@
// Code generated by pluginator on PatchTransformer; DO NOT EDIT.
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
type PatchTransformerPlugin struct {
loadedPatch *resource.Resource
decodedPatch jsonpatch.Patch
Path string `json:"path,omitempty" yaml:"path,omitempty"`
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
}
func (p *PatchTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if p.Patch == "" && p.Path == "" {
err = fmt.Errorf(
"must specify one of patch and path in\n%s", string(c))
return
}
if p.Patch != "" && p.Path != "" {
err = fmt.Errorf(
"patch and path can't be set at the same time\n%s", string(c))
return
}
var in []byte
if p.Path != "" {
in, err = h.Loader().Load(p.Path)
if err != nil {
return
}
}
if p.Patch != "" {
in = []byte(p.Patch)
}
patchSM, errSM := h.ResmapFactory().RF().FromBytes(in)
patchJson, errJson := jsonPatchFromBytes(in)
if errSM != nil && errJson != nil {
err = fmt.Errorf(
"unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch)
return
}
if errSM == nil && errJson != nil {
p.loadedPatch = patchSM
}
if errJson == nil && errSM != nil {
p.decodedPatch = patchJson
}
if patchSM != nil && patchJson != nil {
err = fmt.Errorf(
"a patch can't be both a Strategic Merge Patch and JSON patch 6902 %s", p.Patch)
}
return nil
}
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
if p.loadedPatch != nil && p.Target == nil {
target, err := m.GetById(p.loadedPatch.OrgId())
if err != nil {
return err
}
err = target.Patch(p.loadedPatch.Kunstructured)
if err != nil {
return err
}
return nil
}
if p.Target == nil {
return fmt.Errorf("must specify a target for patch %s", p.Patch)
}
resources, err := m.Select(*p.Target)
if err != nil {
return err
}
for _, res := range resources {
if p.decodedPatch != nil {
rawObj, err := res.MarshalJSON()
if err != nil {
return err
}
modifiedObj, err := p.decodedPatch.Apply(rawObj)
if err != nil {
return errors.Wrapf(
err, "failed to apply json patch '%s'", p.Patch)
}
err = res.UnmarshalJSON(modifiedObj)
if err != nil {
return err
}
}
if p.loadedPatch != nil {
patchCopy := p.loadedPatch.DeepCopy()
patchCopy.SetName(res.GetName())
patchCopy.SetNamespace(res.GetNamespace())
patchCopy.SetGvk(res.GetGvk())
err = res.Patch(patchCopy.Kunstructured)
if err != nil {
return err
}
}
}
return nil
}
// jsonPatchFromBytes loads a Json 6902 patch from
// a bytes input
func jsonPatchFromBytes(
in []byte) (jsonpatch.Patch, error) {
ops := string(in)
if ops == "" {
return nil, fmt.Errorf("empty json patch operations")
}
if ops[0] != '[' {
jsonOps, err := yaml.YAMLToJSON(in)
if err != nil {
return nil, err
}
ops = string(jsonOps)
}
return jsonpatch.DecodePatch([]byte(ops))
}
func NewPatchTransformerPlugin() resmap.TransformerPlugin {
return &PatchTransformerPlugin{}
}

View File

@@ -1,40 +1,39 @@
// Code generated by pluginator on PrefixSuffixTransformer; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"errors"
"fmt"
"sigs.k8s.io/kustomize/v3/pkg/gvk"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resid"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/transformers"
"sigs.k8s.io/kustomize/v3/pkg/transformers/config"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/yaml"
)
// Add the given prefix and suffix to the field.
type PrefixSuffixTransformerPlugin struct {
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
//noinspection GoUnusedGlobalVariable
func NewPrefixSuffixTransformerPlugin() *PrefixSuffixTransformerPlugin {
return &PrefixSuffixTransformerPlugin{}
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
// Not placed in a file yet due to lack of demand.
var prefixSuffixFieldSpecsToSkip = []config.FieldSpec{
var prefixSuffixFieldSpecsToSkip = []types.FieldSpec{
{
Gvk: gvk.Gvk{Kind: "CustomResourceDefinition"},
Gvk: resid.Gvk{Kind: "CustomResourceDefinition"},
},
{
Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"},
},
}
func (p *PrefixSuffixTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
_ *resmap.PluginHelpers, c []byte) (err error) {
p.Prefix = ""
p.Suffix = ""
p.FieldSpecs = nil
@@ -49,23 +48,41 @@ func (p *PrefixSuffixTransformerPlugin) Config(
}
func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
if len(p.Prefix) == 0 && len(p.Suffix) == 0 {
return nil
}
// Even if both the Prefix and Suffix are empty we want
// to proceed with the transformation. This allows to add contextual
// information to the resources (AddNamePrefix and AddNameSuffix).
for _, r := range m.Resources() {
if p.shouldSkip(r.OrgId()) {
// Don't change the actual definition
// of a CRD.
continue
}
id := r.OrgId()
// current default configuration contains
// only one entry: "metadata/name" with no GVK
for _, path := range p.FieldSpecs {
if !id.IsSelected(&path.Gvk) {
// With the currrent default configuration,
// because no Gvk is specified, so a wild
// card
continue
}
if smellsLikeANameChange(&path) {
// "metadata/name" is the only field.
// this will add a prefix and a suffix
// to the resource even if those are
// empty
r.AddNamePrefix(p.Prefix)
r.AddNameSuffix(p.Suffix)
}
err := transformers.MutateField(
// the addPrefixSuffix method will not
// change the name if both the prefix and suffix
// are empty.
err := transform.MutateField(
r.Map(),
path.PathSlice(),
path.CreateIfNotPresent,
@@ -78,7 +95,7 @@ func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
return nil
}
func smellsLikeANameChange(fs *config.FieldSpec) bool {
func smellsLikeANameChange(fs *types.FieldSpec) bool {
return fs.Path == "metadata/name"
}
@@ -100,3 +117,7 @@ func (p *PrefixSuffixTransformerPlugin) addPrefixSuffix(
}
return fmt.Sprintf("%s%s%s", p.Prefix, s, p.Suffix), nil
}
func NewPrefixSuffixTransformerPlugin() resmap.TransformerPlugin {
return &PrefixSuffixTransformerPlugin{}
}

View File

@@ -1,42 +1,43 @@
// Code generated by pluginator on ReplicaCountTransformer; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resid"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/transformers"
"sigs.k8s.io/kustomize/v3/pkg/transformers/config"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
// Find matching replicas declarations and replace the count.
// Eases the kustomization configuration of replica changes.
type ReplicaCountTransformerPlugin struct {
Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"`
FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
//noinspection GoUnusedGlobalVariable
func NewReplicaCountTransformerPlugin() *ReplicaCountTransformerPlugin {
return &ReplicaCountTransformerPlugin{}
Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
}
func (p *ReplicaCountTransformerPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
_ *resmap.PluginHelpers, c []byte) (err error) {
p.Replica = types.Replica{}
p.FieldSpecs = nil
return yaml.Unmarshal(c, p)
}
func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
found := false
for i, replicaSpec := range p.FieldSpecs {
for _, res := range m.GetMatchingResourcesByOriginalId(p.createMatcher(i)) {
err := transformers.MutateField(
matcher := p.createMatcher(i)
matchOriginal := m.GetMatchingResourcesByOriginalId(matcher)
matchCurrent := m.GetMatchingResourcesByCurrentId(matcher)
for _, res := range append(matchOriginal, matchCurrent...) {
found = true
err := transform.MutateField(
res.Map(), replicaSpec.PathSlice(),
replicaSpec.CreateIfNotPresent, p.addReplicas)
if err != nil {
@@ -45,6 +46,15 @@ func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
}
}
if !found {
gvks := make([]string, len(p.FieldSpecs))
for i, replicaSpec := range p.FieldSpecs {
gvks[i] = replicaSpec.Gvk.String()
}
return fmt.Errorf("resource with name %s does not match a config with the following GVK %v",
p.Replica.Name, gvks)
}
return nil
}
@@ -72,3 +82,7 @@ func (p *ReplicaCountTransformerPlugin) addReplicas(in interface{}) (interface{}
}
return p.Replica.Count, nil
}
func NewReplicaCountTransformerPlugin() resmap.TransformerPlugin {
return &ReplicaCountTransformerPlugin{}
}

View File

@@ -1,28 +1,23 @@
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
package builtin
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/api/kv"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
type SecretGeneratorPlugin struct {
ldr ifc.Loader
rf *resmap.Factory
h *resmap.PluginHelpers
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
types.GeneratorOptions
types.SecretArgs
}
//noinspection GoUnusedGlobalVariable
func NewSecretGeneratorPlugin() *SecretGeneratorPlugin {
return &SecretGeneratorPlugin{}
}
func (p *SecretGeneratorPlugin) Config(
ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) {
func (p *SecretGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
p.GeneratorOptions = types.GeneratorOptions{}
p.SecretArgs = types.SecretArgs{}
err = yaml.Unmarshal(config, p)
@@ -32,11 +27,16 @@ func (p *SecretGeneratorPlugin) Config(
if p.SecretArgs.Namespace == "" {
p.SecretArgs.Namespace = p.Namespace
}
p.ldr = ldr
p.rf = rf
p.h = h
return
}
func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) {
return p.rf.FromSecretArgs(p.ldr, &p.GeneratorOptions, p.SecretArgs)
return p.h.ResmapFactory().FromSecretArgs(
kv.NewLoader(p.h.Loader(), p.h.Validator()),
&p.GeneratorOptions, p.SecretArgs)
}
func NewSecretGeneratorPlugin() resmap.GeneratorPlugin {
return &SecretGeneratorPlugin{}
}

8
api/builtins/doc.go Normal file
View File

@@ -0,0 +1,8 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package builtins holds code generated from the builtin plugins.
// The "builtin" plugins are written as normal plugins and can
// be used as such, but they are also used to generate the code
// in this package so they can be statically linked to client code.
package builtins

View File

@@ -1,20 +1,7 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
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.
*/
package fs
package filesys
import (
"io/ioutil"
@@ -27,7 +14,7 @@ import (
type ConfirmedDir string
// NewTmpConfirmedDir returns a temporary dir, else error.
// The directory is cleaned, no symlinks, etc. so its
// The directory is cleaned, no symlinks, etc. so it's
// returned as a ConfirmedDir.
func NewTmpConfirmedDir() (ConfirmedDir, error) {
n, err := ioutil.TempDir("", "kustomize-")
@@ -36,13 +23,12 @@ func NewTmpConfirmedDir() (ConfirmedDir, error) {
}
// In MacOs `ioutil.TempDir` creates a directory
// with root in the `/var` folder, which is in turn a symlinked path
// to `/private/var`.
// with root in the `/var` folder, which is in turn
// a symlinked path to `/private/var`.
// Function `filepath.EvalSymlinks`is used to
// resolve the real absolute path.
deLinked, err := filepath.EvalSymlinks(n)
return ConfirmedDir(deLinked), err
}
// HasPrefix returns true if the directory argument

View File

@@ -1,28 +1,17 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
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.
*/
package fs
package filesys_test
import (
"path/filepath"
"testing"
. "sigs.k8s.io/kustomize/api/filesys"
)
func TestJoin(t *testing.T) {
fSys := MakeFakeFS()
fSys := MakeFsInMemory()
err := fSys.Mkdir("/foo")
if err != nil {
t.Fatalf("unexpected err: %v", err)
@@ -40,7 +29,8 @@ func TestJoin(t *testing.T) {
}
func TestHasPrefix_Slash(t *testing.T) {
d, f, err := MakeFakeFS().CleanedAbs("/")
fSys := MakeFsInMemory()
d, f, err := fSys.CleanedAbs("/")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@@ -56,7 +46,7 @@ func TestHasPrefix_Slash(t *testing.T) {
}
func TestHasPrefix_SlashFoo(t *testing.T) {
fSys := MakeFakeFS()
fSys := MakeFsInMemory()
err := fSys.Mkdir("/foo")
if err != nil {
t.Fatalf("unexpected err: %v", err)
@@ -77,7 +67,7 @@ func TestHasPrefix_SlashFoo(t *testing.T) {
}
func TestHasPrefix_SlashFooBar(t *testing.T) {
fSys := MakeFakeFS()
fSys := MakeFsInMemory()
err := fSys.MkdirAll("/foo/bar")
if err != nil {
t.Fatalf("unexpected err: %v", err)

15
api/filesys/file.go Normal file
View File

@@ -0,0 +1,15 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"io"
"os"
)
// File groups the basic os.File methods.
type File interface {
io.ReadWriteCloser
Stat() (os.FileInfo, error)
}

34
api/filesys/fileinfo.go Normal file
View File

@@ -0,0 +1,34 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"os"
"time"
)
var _ os.FileInfo = fileInfo{}
// fileInfo implements os.FileInfo for a fileInMemory instance.
type fileInfo struct {
node *fsNode
}
// Name returns the name of the file
func (fi fileInfo) Name() string { return fi.node.Name() }
// Size returns the size of the file
func (fi fileInfo) Size() int64 { return fi.node.Size() }
// Mode returns the file mode
func (fi fileInfo) Mode() os.FileMode { return 0777 }
// ModTime returns a bogus time
func (fi fileInfo) ModTime() time.Time { return time.Time{} }
// IsDir returns true if it is a directory
func (fi fileInfo) IsDir() bool { return fi.node.isNodeADir() }
// Sys should return underlying data source, but it now returns nil
func (fi fileInfo) Sys() interface{} { return nil }

27
api/filesys/fileondisk.go Normal file
View File

@@ -0,0 +1,27 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"os"
)
var _ File = &fileOnDisk{}
// fileOnDisk implements File using the local filesystem.
type fileOnDisk struct {
file *os.File
}
// Close closes a file.
func (f *fileOnDisk) Close() error { return f.file.Close() }
// Read reads a file's content.
func (f *fileOnDisk) Read(p []byte) (n int, err error) { return f.file.Read(p) }
// Write writes bytes to a file
func (f *fileOnDisk) Write(p []byte) (n int, err error) { return f.file.Write(p) }
// Stat returns an interface which has all the information regarding the file.
func (f *fileOnDisk) Stat() (os.FileInfo, error) { return f.file.Stat() }

50
api/filesys/filesystem.go Normal file
View File

@@ -0,0 +1,50 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package filesys provides a file system abstraction layer.
package filesys
import (
"path/filepath"
)
const (
Separator = string(filepath.Separator)
SelfDir = "."
ParentDir = ".."
)
// FileSystem groups basic os filesystem methods.
// It's supposed be functional subset of https://golang.org/pkg/os
type FileSystem interface {
// Create a file.
Create(path string) (File, error)
// MkDir makes a directory.
Mkdir(path string) error
// MkDirAll makes a directory path, creating intervening directories.
MkdirAll(path string) error
// RemoveAll removes path and any children it contains.
RemoveAll(path string) error
// Open opens the named file for reading.
Open(path string) (File, error)
// IsDir returns true if the path is a directory.
IsDir(path string) bool
// CleanedAbs converts the given path into a
// directory and a file name, where the directory
// is represented as a ConfirmedDir and all that implies.
// If the entire path is a directory, the file component
// is an empty string.
CleanedAbs(path string) (ConfirmedDir, string, error)
// Exists is true if the path exists in the file system.
Exists(path string) bool
// Glob returns the list of matching files,
// emulating https://golang.org/pkg/path/filepath/#Glob
Glob(pattern string) ([]string, error)
// ReadFile returns the contents of the file at the given path.
ReadFile(path string) ([]byte, error)
// WriteFile writes the data to a file at the given path,
// overwriting anything that's already there.
WriteFile(path string, data []byte) error
// Walk walks the file system with the given WalkFunc.
Walk(path string, walkFn filepath.WalkFunc) error
}

557
api/filesys/fsnode.go Normal file
View File

@@ -0,0 +1,557 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"github.com/pkg/errors"
)
var _ File = &fsNode{}
var _ FileSystem = &fsNode{}
// fsNode is either a file or a directory.
type fsNode struct {
// What node owns me?
parent *fsNode
// Value to return as the Name() when the
// parent is nil.
nilParentName string
// A directory mapping names to nodes.
// If dir is nil, then self node is a file.
// If dir is non-nil, then self node is a directory,
// albeit possibly an empty directory.
dir map[string]*fsNode
// if this node is a file, this is the content.
content []byte
// if this node is a file, this tracks whether or
// not it is "open".
open bool
}
// MakeEmptyDirInMemory returns an empty directory.
// The paths of nodes in this object will never
// report a leading Separator, meaning they
// aren't "absolute" in the sense defined by
// https://golang.org/pkg/path/filepath/#IsAbs.
func MakeEmptyDirInMemory() *fsNode {
return &fsNode{
dir: make(map[string]*fsNode),
}
}
// MakeFsInMemory returns an empty 'file system'.
// The paths of nodes in this object will always
// report a leading Separator, meaning they
// are "absolute" in the sense defined by
// https://golang.org/pkg/path/filepath/#IsAbs.
// This is a relevant difference when using Walk,
// Glob, Match, etc.
func MakeFsInMemory() FileSystem {
return &fsNode{
nilParentName: Separator,
dir: make(map[string]*fsNode),
}
}
// Name returns the name of the node.
func (n *fsNode) Name() string {
if n.parent == nil {
// Unable to lookup name in parent.
return n.nilParentName
}
if !n.parent.isNodeADir() {
log.Fatal("parent not a dir")
}
for key, value := range n.parent.dir {
if value == n {
return key
}
}
log.Fatal("unable to find fsNode name")
return ""
}
// Path returns the full path to the node.
func (n *fsNode) Path() string {
if n.parent == nil {
return n.nilParentName
}
if !n.parent.isNodeADir() {
log.Fatal("parent not a dir, structural error")
}
return filepath.Join(n.parent.Path(), n.Name())
}
// mySplit trims trailing separators from the directory
// result of filepath.Split.
func mySplit(s string) (string, string) {
dName, fName := filepath.Split(s)
return StripTrailingSeps(dName), fName
}
func (n *fsNode) addFile(name string, c []byte) (result *fsNode, err error) {
parent := n
dName, fileName := mySplit(name)
if dName != "" {
parent, err = parent.addDir(dName)
if err != nil {
return nil, err
}
}
if !isLegalFileNameForCreation(fileName) {
return nil, fmt.Errorf(
"illegal name '%s' in file creation", fileName)
}
result, ok := parent.dir[fileName]
if ok {
// File already exists; overwrite it.
result.content = c
return result, nil
}
result = &fsNode{
content: c,
parent: parent,
}
parent.dir[fileName] = result
return result, nil
}
// Create implements FileSystem.
// Create makes an empty file.
func (n *fsNode) Create(path string) (result File, err error) {
return n.AddFile(path, []byte{})
}
// WriteFile implements FileSystem.
func (n *fsNode) WriteFile(path string, d []byte) error {
_, err := n.AddFile(path, d)
return err
}
// AddFile adds a file and any necessary containing
// directories to the node.
func (n *fsNode) AddFile(
name string, c []byte) (result *fsNode, err error) {
if n.dir == nil {
return nil, fmt.Errorf(
"cannot add a file to a non-directory '%s'", n.Name())
}
return n.addFile(cleanQueryPath(name), c)
}
func (n *fsNode) addDir(path string) (result *fsNode, err error) {
parent := n
dName, subDirName := mySplit(path)
if dName != "" {
parent, err = n.addDir(dName)
if err != nil {
return nil, err
}
}
switch subDirName {
case "", SelfDir:
return n, nil
case ParentDir:
if n.parent == nil {
return nil, fmt.Errorf(
"cannot add a directory above '%s'", n.Path())
}
return n.parent, nil
default:
if !isLegalFileNameForCreation(subDirName) {
return nil, fmt.Errorf(
"illegal name '%s' in directory creation", subDirName)
}
result, ok := parent.dir[subDirName]
if ok {
if result.isNodeADir() {
// it's already there.
return result, nil
}
return nil, fmt.Errorf(
"cannot make dir '%s'; a file of that name already exists in '%s'",
subDirName, parent.Name())
}
result = &fsNode{
dir: make(map[string]*fsNode),
parent: parent,
}
parent.dir[subDirName] = result
return result, nil
}
}
// Mkdir implements FileSystem.
// Mkdir creates a directory.
func (n *fsNode) Mkdir(path string) error {
_, err := n.AddDir(path)
return err
}
// MkdirAll implements FileSystem.
// MkdirAll creates a directory.
func (n *fsNode) MkdirAll(path string) error {
_, err := n.AddDir(path)
return err
}
// AddDir adds a directory to the node, not complaining
// if it is already there.
func (n *fsNode) AddDir(path string) (result *fsNode, err error) {
if n.dir == nil {
return nil, fmt.Errorf(
"cannot add a directory to file node '%s'", n.Name())
}
return n.addDir(cleanQueryPath(path))
}
// CleanedAbs implements FileSystem.
func (n *fsNode) CleanedAbs(path string) (ConfirmedDir, string, error) {
node, err := n.Find(path)
if err != nil {
return "", "", errors.Wrap(err, "unable to clean")
}
if node == nil {
return "", "", fmt.Errorf("'%s' doesn't exist", path)
}
if node.isNodeADir() {
return ConfirmedDir(node.Path()), "", nil
}
return ConfirmedDir(node.parent.Path()), node.Name(), nil
}
// Exists implements FileSystem.
// Exists returns true if the path exists.
func (n *fsNode) Exists(path string) bool {
if !n.isNodeADir() {
return n.Name() == path
}
result, err := n.Find(path)
if err != nil {
return false
}
return result != nil
}
func cleanQueryPath(path string) string {
// Always ignore leading separator?
// Remember that filepath.Clean returns "." if
// given an empty string argument.
return filepath.Clean(StripLeadingSeps(path))
}
// Find finds the given node, else nil if not found.
// Return error on structural/argument errors.
func (n *fsNode) Find(path string) (*fsNode, error) {
if !n.isNodeADir() {
return nil, fmt.Errorf("can only find inside a dir")
}
if path == "" {
// Special case; check *before* cleaning and *before*
// comparison to nilParentName.
return nil, nil
}
if (n.parent == nil && path == n.nilParentName) || path == SelfDir {
// Special case
return n, nil
}
return n.findIt(cleanQueryPath(path))
}
func (n *fsNode) findIt(path string) (result *fsNode, err error) {
parent := n
dName, item := mySplit(path)
if dName != "" {
parent, err = n.findIt(dName)
if err != nil {
return nil, err
}
if parent == nil {
// all done, target doesn't exist.
return nil, nil
}
}
if !parent.isNodeADir() {
return nil, fmt.Errorf("'%s' is not a directory", parent.Path())
}
return parent.dir[item], nil
}
// RemoveAll implements FileSystem.
// RemoveAll removes an item and everything it contains.
func (n *fsNode) RemoveAll(path string) error {
result, err := n.Find(path)
if err != nil {
return err
}
if result == nil {
return fmt.Errorf("cannot find '%s' to remove it", path)
}
return result.Remove()
}
// Remove drop the node, and everything it contains, from its parent.
func (n *fsNode) Remove() error {
if n.parent == nil {
return fmt.Errorf("cannot remove a root node")
}
if !n.parent.isNodeADir() {
log.Fatal("parent not a dir")
}
for key, value := range n.parent.dir {
if value == n {
delete(n.parent.dir, key)
return nil
}
}
log.Fatal("unable to find self in parent")
return nil
}
// isNodeADir returns true if the node is a directory.
// Cannot collide with the poorly named "IsDir".
func (n *fsNode) isNodeADir() bool {
return n.dir != nil
}
// IsDir implements FileSystem.
// IsDir returns true if the argument resolves
// to a directory rooted at the node.
func (n *fsNode) IsDir(path string) bool {
result, err := n.Find(path)
if err != nil || result == nil {
return false
}
return result.isNodeADir()
}
// Size returns the size of the node.
func (n *fsNode) Size() int64 {
if n.isNodeADir() {
return int64(len(n.dir))
}
return int64(len(n.content))
}
// Open implements FileSystem.
// Open opens the node for reading (just marks it).
func (n *fsNode) Open(path string) (File, error) {
result, err := n.Find(path)
if err != nil {
return nil, err
}
if result == nil {
return nil, fmt.Errorf("cannot find '%s' to open it", path)
}
result.open = true
return result, nil
}
// Close marks the node closed.
func (n *fsNode) Close() error {
n.open = false
return nil
}
// ReadFile implements FileSystem.
func (n *fsNode) ReadFile(path string) (c []byte, err error) {
result, err := n.Find(path)
if err != nil {
return nil, err
}
if result == nil {
return nil, fmt.Errorf("cannot find '%s' to read it", path)
}
c = make([]byte, len(result.content))
_, err = result.Read(c)
return c, err
}
// Read returns the content of the file node.
func (n *fsNode) Read(d []byte) (c int, err error) {
if n.isNodeADir() {
return 0, fmt.Errorf(
"cannot read content from non-file '%s'", n.Path())
}
return copy(d, n.content), nil
}
// Write saves the contents of the argument to the file node.
func (n *fsNode) Write(p []byte) (c int, err error) {
if n.isNodeADir() {
return 0, fmt.Errorf(
"cannot write content to non-file '%s'", n.Path())
}
n.content = make([]byte, len(p))
return copy(n.content, p), nil
}
// ContentMatches returns true if v matches fake file's content.
func (n *fsNode) ContentMatches(v []byte) bool {
return bytes.Equal(v, n.content)
}
// GetContent the content of a fake file.
func (n *fsNode) GetContent() []byte {
return n.content
}
// Stat returns an instance of FileInfo.
func (n *fsNode) Stat() (os.FileInfo, error) {
return fileInfo{node: n}, nil
}
// Walk implements FileSystem.
func (n *fsNode) Walk(path string, walkFn filepath.WalkFunc) error {
result, err := n.Find(path)
if err != nil {
return err
}
if result == nil {
return fmt.Errorf("cannot find '%s' to walk it", path)
}
return result.WalkMe(walkFn)
}
// Walk runs the given walkFn on each node.
func (n *fsNode) WalkMe(walkFn filepath.WalkFunc) error {
fi, err := n.Stat()
// always visit self first
err = walkFn(n.Path(), fi, err)
if !n.isNodeADir() {
// it's a file, so nothing more to do
return err
}
// process self as a directory
if err == filepath.SkipDir {
return nil
}
// Walk is supposed to visit in lexical order.
for _, k := range n.sortedDirEntries() {
if err := n.dir[k].WalkMe(walkFn); err != nil {
if err == filepath.SkipDir {
// stop processing this directory
break
}
// bail out completely
return err
}
}
return nil
}
func (n *fsNode) sortedDirEntries() []string {
keys := make([]string, len(n.dir))
i := 0
for k := range n.dir {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
// FileCount returns a count of files.
// Directories, empty or otherwise, not counted.
func (n *fsNode) FileCount() int {
count := 0
n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
count++
}
return nil
})
return count
}
func (n *fsNode) DebugPrint() {
n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("err '%v' at path %q\n", err, path)
return nil
}
if info.IsDir() {
if info.Size() == 0 {
fmt.Println("empty dir: " + path)
}
} else {
fmt.Println(" file: " + path)
}
return nil
})
}
var legalFileNamePattern = regexp.MustCompile("^[a-zA-Z0-9-_.]+$")
// This rules enforced here should be simpler and tighter
// than what's allowed on a real OS.
// Should be fine for testing or in-memory purposes.
func isLegalFileNameForCreation(n string) bool {
if n == "" || n == SelfDir || !legalFileNamePattern.MatchString(n) {
return false
}
return !strings.Contains(n, ParentDir)
}
// RegExpGlob returns a list of file paths matching the regexp.
// Excludes directories.
func (n *fsNode) RegExpGlob(pattern string) ([]string, error) {
var result []string
var expression = regexp.MustCompile(pattern)
n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
if expression.MatchString(path) {
result = append(result, path)
}
}
return nil
})
sort.Strings(result)
return result, nil
}
// Glob implements FileSystem.
// Glob returns the list of file paths matching
// per filepath.Match semantics, i.e. unlike RegExpGlob,
// Match("foo/a*") will not match sub-sub directories of foo.
// This is how /bin/ls behaves.
func (n *fsNode) Glob(pattern string) ([]string, error) {
var result []string
n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
match, err := filepath.Match(pattern, path)
if err != nil {
return err
}
if match {
result = append(result, path)
}
}
return nil
})
sort.Strings(result)
return result, nil
}

788
api/filesys/fsnode_test.go Normal file
View File

@@ -0,0 +1,788 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"testing"
)
const content = `
Lorem ipsum dolor sit amet,
consectetur adipiscing elit,
sed do eiusmod tempor incididunt
ut labore et dolore magna aliqua.
`
const shortContent = "hi"
var topCases = []pathCase{
{
what: "dotdot",
arg: ParentDir,
errStr: "illegal name '..' in file creation",
},
{
what: "empty",
arg: "",
name: "",
errStr: "illegal name '.' in file creation",
},
{
what: "simple",
arg: "bob",
name: "bob",
path: "bob",
},
{
what: "longer",
arg: filepath.Join("longer", "bob"),
name: "bob",
path: filepath.Join("longer", "bob"),
},
{
what: "longer yet",
arg: filepath.Join("longer", "foo", "bar", "beans", "bob"),
name: "bob",
path: filepath.Join("longer", "foo", "bar", "beans", "bob"),
},
{
what: "tricky",
arg: filepath.Join("bob", ParentDir, "sally"),
name: "sally",
path: "sally",
},
{
what: "trickier",
arg: filepath.Join("bob", "sally", ParentDir, ParentDir, "jean"),
name: "jean",
path: "jean",
},
}
func TestMakeEmptyDirInMemory(t *testing.T) {
n := MakeEmptyDirInMemory()
if !n.isNodeADir() {
t.Fatalf("not a directory")
}
if n.Size() != 0 {
t.Fatalf("unexpected size %d", n.Size())
}
if n.Name() != "" {
t.Fatalf("unexpected name '%s'", n.Name())
}
if n.Path() != "" {
t.Fatalf("unexpected path '%s'", n.Path())
}
runBasicOperations(
t, "MakeEmptyDirInMemory", false, topCases, n)
}
func TestMakeFsInMemory(t *testing.T) {
runBasicOperations(
t, "MakeFsInMemory", true, topCases, MakeFsInMemory())
}
//nolint:gocyclo
func runBasicOperations(
t *testing.T, tName string, isFSysRooted bool,
cases []pathCase, fSys FileSystem) {
buff := make([]byte, 500)
for _, c := range cases {
err := fSys.WriteFile(c.arg, []byte(content))
if c.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error writing to '%s'!", c.what, c.arg)
}
if !strings.Contains(err.Error(), c.errStr) {
t.Fatalf("%s; expected err containing '%s', got '%v'",
c.what, c.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if !fSys.Exists(c.path) {
t.Fatalf("%s; expect existence of '%s'", c.what, c.path)
}
stuff, err := fSys.ReadFile(c.path)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if string(stuff) != content {
t.Fatalf("%s; unexpected content '%s'", c.what, stuff)
}
f, err := fSys.Open(c.arg)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
fi, err := f.Stat()
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if fi.Name() != c.name {
t.Fatalf("%s; expected name '%s', got '%s'", c.what, c.name, fi.Name())
}
count, err := f.Read(buff)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if string(buff[:count]) != content {
t.Fatalf("%s; unexpected buff '%s'", c.what, buff)
}
count, err = f.Write([]byte(shortContent))
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if count != len(shortContent) {
t.Fatalf("%s; unexpected count: %d", c.what, len(shortContent))
}
stuff, err = fSys.ReadFile(c.path)
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
if string(stuff) != shortContent {
t.Fatalf("%s; unexpected content '%s'", c.what, stuff)
}
}
var actualPaths []string
var err error
prefix := ""
{
root := SelfDir
if isFSysRooted {
root = Separator
prefix = Separator
}
err = fSys.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("err '%v' at path %q\n", err, path)
return nil
}
if !info.IsDir() {
actualPaths = append(actualPaths, path)
}
return nil
})
}
if err != nil {
t.Fatalf("unexpected error %v", err)
}
var expectedPaths []string
for _, c := range cases {
if c.errStr == "" {
expectedPaths = append(expectedPaths, prefix+c.path)
}
}
sort.Strings(expectedPaths)
assertEqualStringSlices(t, expectedPaths, actualPaths, tName)
}
type pathCase struct {
what string
arg string
name string
path string
errStr string
}
func TestAddDir(t *testing.T) {
cases := []pathCase{
{
what: "dotdot",
arg: ParentDir,
errStr: "cannot add a directory above ''",
},
{
what: "empty",
arg: "",
name: "",
path: "",
},
{
what: "simple",
arg: "bob",
name: "bob",
path: "bob",
},
{
what: "longer",
arg: filepath.Join("longer", "bob"),
name: "bob",
path: filepath.Join("longer", "bob"),
},
{
what: "longer yet",
arg: filepath.Join("longer", "foo", "bar", "beans", "bob"),
name: "bob",
path: filepath.Join("longer", "foo", "bar", "beans", "bob"),
},
{
what: "tricky",
arg: filepath.Join("bob", ParentDir, "sally"),
name: "sally",
path: "sally",
},
{
what: "trickier",
arg: filepath.Join("bob", "sally", ParentDir, ParentDir, "jean"),
name: "jean",
path: "jean",
},
}
for _, c := range cases {
n := MakeEmptyDirInMemory()
f, err := n.AddDir(c.arg)
if c.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error!", c.what)
}
if !strings.Contains(err.Error(), c.errStr) {
t.Fatalf(
"%s; expected error with '%s', got '%v'",
c.what, c.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error: %v", c.what, err)
}
checkNode(t, c.what, f, c.name, 0, true, c.path)
checkOsStat(t, c.what, f, f.Name(), 0, true)
}
}
var bagOfCases = []pathCase{
{
what: "empty",
arg: "",
errStr: "illegal name '.' in file creation",
},
{
what: "simple",
arg: "bob",
name: "bob",
path: "bob",
},
{
what: "longer",
arg: filepath.Join("longer", "bob"),
name: "bob",
path: filepath.Join("longer", "bob"),
},
{
what: "longer",
arg: filepath.Join("longer", "sally"),
name: "sally",
path: filepath.Join("longer", "sally"),
},
{
what: "even longer",
arg: filepath.Join("longer", "than", "the", "other", "bob"),
name: "bob",
path: filepath.Join("longer", "than", "the", "other", "bob"),
},
{
what: "even longer",
arg: filepath.Join("even", "much", "longer", "than", "the", "other", "bob"),
name: "bob",
path: filepath.Join("even", "much", "longer", "than", "the", "other", "bob"),
},
}
func TestAddFile(t *testing.T) {
n := MakeEmptyDirInMemory()
if n.FileCount() != 0 {
t.Fatalf("expected no files, got %d", n.FileCount())
}
expectedFileCount := 0
for _, c := range bagOfCases {
f, err := n.AddFile(c.arg, []byte(content))
if c.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error!", c.what)
}
if !strings.Contains(err.Error(), c.errStr) {
t.Fatalf("%s; expected err containing '%s', got '%v'",
c.what, c.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error %v", c.what, err)
}
checkNode(t, c.what, f, c.name, len(content), false, c.path)
checkOsStat(t, c.what, f, f.Name(), len(content), false)
result, err := n.Find(c.arg)
if err != nil {
t.Fatalf("%s; unexpected find error %v", c.what, err)
}
if result != f {
t.Fatalf("%s; unexpected find result %v", c.what, result)
}
result, err = n.Find(filepath.Join("longer", "bogus"))
if err != nil {
t.Fatalf("%s; unexpected find error %v", c.what, err)
}
if result != nil {
t.Fatalf("%s; unexpected find result %v", c.what, result)
}
expectedFileCount++
fc := n.FileCount()
if fc != expectedFileCount {
t.Fatalf("expected file count %d, got %d",
expectedFileCount, fc)
}
}
}
func checkNode(
t *testing.T, what string, f *fsNode, name string,
size int, isDir bool, path string) {
if f.isNodeADir() != isDir {
t.Fatalf("%s; unexpected isNodeADir = %v", what, f.isNodeADir())
}
if f.Size() != int64(size) {
t.Fatalf("%s; unexpected size %d", what, f.Size())
}
if name != f.Name() {
t.Fatalf("%s; expected name '%s', got '%s'", what, name, f.Name())
}
if path != f.Path() {
t.Fatalf("%s; expected path '%s', got '%s'", what, path, f.Path())
}
}
func checkOsStat(
t *testing.T, what string, f File, name string,
size int, isDir bool) {
info, err := f.Stat()
if err != nil {
t.Fatalf("%s; unexpected stat error %v", what, err)
}
if info.IsDir() != isDir {
t.Fatalf("%s; unexpected info.isNodeADir = %v", what, info.IsDir())
}
if info.Size() != int64(size) {
t.Fatalf("%s; unexpected info.size %d", what, info.Size())
}
if info.Name() != name {
t.Fatalf("%s; expected name '%s', got info.Name '%s'", what, name, info.Name())
}
}
var bunchOfFiles = []struct {
path string
addAsDir bool
}{
{
path: filepath.Join("b", "e", "a", "c", "g"),
},
{
path: filepath.Join("z", "r", "a", "b", "g"),
},
{
path: filepath.Join("b", "q", "a", "c", "g"),
},
{
path: filepath.Join("b", "a", "a", "m", "g"),
addAsDir: true,
},
{
path: filepath.Join("b", "w"),
},
{
path: filepath.Join("b", "d", "a", "c", "m"),
},
{
path: filepath.Join("b", "d", "z"),
},
{
path: filepath.Join("b", "d", "y"),
},
{
path: filepath.Join("b", "d", "ignore", "c", "n"),
},
{
path: filepath.Join("b", "d", "x"),
},
{
path: filepath.Join("b", "d", "ignore", "c", "o"),
},
{
path: filepath.Join("b", "d", "ignore", "c", "m"),
},
{
path: filepath.Join("b", "d", "a", "c", "i"),
addAsDir: true,
},
{
path: filepath.Join("x"),
},
{
path: filepath.Join("y"),
},
{
path: filepath.Join("b", "d", "a", "c", "i", "beans"),
},
{
path: filepath.Join("b", "d", "a", "c", "r", "w"),
addAsDir: true,
},
{
path: filepath.Join("b", "d", "a", "c", "u"),
},
}
func makeLoadedFileTree(t *testing.T) *fsNode {
n := MakeEmptyDirInMemory()
var err error
expectedFileCount := 0
for _, item := range bunchOfFiles {
if item.addAsDir {
_, err = n.AddDir(item.path)
} else {
_, err = n.AddFile(item.path, []byte(content))
expectedFileCount++
}
if err != nil {
t.Fatalf("unexpected error %v", err)
}
}
fc := n.FileCount()
if fc != expectedFileCount {
t.Fatalf("expected file count %d, got %d",
expectedFileCount, fc)
}
return n
}
func TestWalkMe(t *testing.T) {
n := makeLoadedFileTree(t)
var actualPaths []string
err := n.WalkMe(func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("err '%v' at path %q\n", err, path)
return nil
}
if info.IsDir() {
if info.Name() == "ignore" {
return filepath.SkipDir
}
} else {
actualPaths = append(actualPaths, path)
}
return nil
})
if err != nil {
t.Fatalf("unexpected error %v", err)
}
var expectedPaths []string
for _, c := range bunchOfFiles {
if !c.addAsDir && !strings.Contains(c.path, "ignore") {
expectedPaths = append(expectedPaths, c.path)
}
}
sort.Strings(expectedPaths)
assertEqualStringSlices(t, expectedPaths, actualPaths, "testWalkMe")
}
func TestRemove(t *testing.T) {
n := makeLoadedFileTree(t)
orgCount := n.FileCount()
// Remove the "ignore" directory and everything below it.
path := filepath.Join("b", "d", "ignore")
result, err := n.Find(path)
if err != nil {
t.Fatalf("%s; unexpected error %v", path, err)
}
if result == nil {
t.Fatalf("%s; expected to find '%s'", path, path)
}
if !result.isNodeADir() {
t.Fatalf("%s; expected to find a directory", path)
}
err = result.Remove()
if err != nil {
t.Fatalf("%s; unable to remove: %v", path, err)
}
result, err = n.Find(path)
if err != nil {
// Just because it's gone doesn't mean error.
t.Fatalf("%s; unexpected error %v", path, err)
}
if result != nil {
t.Fatalf("%s; should not have been able to find '%s'", path, path)
}
// There were three files below "ignore".
orgCount -= 3
// Now drop one more for a total of four dropped.
result, _ = n.Find(filepath.Join("y"))
err = result.Remove()
if err != nil {
t.Fatalf("%s; unable to remove: %v", path, err)
}
orgCount -= 1
fc := n.FileCount()
if fc != orgCount {
t.Fatalf("expected file count %d, got %d",
orgCount, fc)
}
}
func TestExists(t *testing.T) {
n := makeLoadedFileTree(t)
path := filepath.Join("b", "d", "a")
if !n.Exists(path) {
t.Fatalf("expected existence at %s", path)
}
if !n.IsDir(path) {
t.Fatalf("expected directory at %s", path)
}
}
func TestRegExpGlob(t *testing.T) {
n := makeLoadedFileTree(t)
expected := []string{
filepath.Join("b", "d", "a", "c", "i", "beans"),
filepath.Join("b", "d", "a", "c", "m"),
filepath.Join("b", "d", "a", "c", "u"),
filepath.Join("b", "d", "ignore", "c", "m"),
filepath.Join("b", "d", "ignore", "c", "n"),
filepath.Join("b", "d", "ignore", "c", "o"),
filepath.Join("b", "d", "x"),
filepath.Join("b", "d", "y"),
filepath.Join("b", "d", "z"),
}
paths, err := n.RegExpGlob("b/d/*")
if err != nil {
t.Fatalf("glob error: %v", err)
}
assertEqualStringSlices(t, expected, paths, "glob test")
}
func TestGlob(t *testing.T) {
n := makeLoadedFileTree(t)
expected := []string{
filepath.Join("b", "d", "x"),
filepath.Join("b", "d", "y"),
filepath.Join("b", "d", "z"),
}
paths, err := n.Glob("b/d/*")
if err != nil {
t.Fatalf("glob error: %v", err)
}
assertEqualStringSlices(t, expected, paths, "glob test")
}
func assertEqualStringSlices(t *testing.T, expected, actual []string, message string) {
if len(expected) != len(actual) {
t.Fatalf(
"%s; unequal sizes; len(expected)=%d, len(actual)=%d\n%+v\n%+v\n",
message, len(expected), len(actual), expected, actual)
}
for i := range expected {
if expected[i] != actual[i] {
t.Fatalf(
"%s; unequal entries; expected=%s, actual=%s",
message, expected[i], actual[i])
}
}
}
func TestFind(t *testing.T) {
cases := []struct {
what string
arg string
expectDir bool
expectFile bool
errStr string
}{
{
what: "garbage",
arg: "///1(*&SA",
},
{
what: "simple",
arg: "bob",
},
{
what: "no directory",
arg: filepath.Join("b", "rrrrrr"),
},
{
what: "is a directory",
arg: filepath.Join("b", "d", "ignore"),
expectDir: true,
},
{
what: "longer, ending in file",
arg: filepath.Join("b", "d", "x"),
expectFile: true,
},
{
what: "moar longer, ending in file",
arg: filepath.Join("b", "d", "a", "c", "u"),
expectFile: true,
},
{
what: "directory",
arg: filepath.Join("b"),
expectDir: true,
},
{
// Querying for the empty string could
// 1) be an error,
// 2) return no result (and no error) as with
// any illegal and therefore non-existent
// file name,
// 3) return the node itself, like running
// 'ls' with no argument.
// Going with option 2 (no result, no error),
// since at this low level it makes more sense
// if the results for the empty string query
// differ from the results for the "." query.
what: "empty name",
arg: "",
},
{
what: "self dir",
arg: SelfDir,
expectDir: true,
},
{
what: "parent dir - doesn't exist",
arg: ParentDir,
},
{
what: "many parents - doesn't exist",
arg: filepath.Join(ParentDir, ParentDir, ParentDir),
},
}
n := makeLoadedFileTree(t)
for _, item := range cases {
result, err := n.Find(item.arg)
if item.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error", item.what)
}
if !strings.Contains(err.Error(), item.errStr) {
t.Fatalf("%s; expected err containing '%s', got '%v'",
item.what, item.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error: %v", item.what, err)
}
if result == nil {
if item.expectDir {
t.Fatalf(
"%s; expected to find directory '%s'", item.what, item.arg)
}
if item.expectFile {
t.Fatalf(
"%s; expected to find file '%s'", item.what, item.arg)
}
continue
}
if item.expectDir {
if !result.isNodeADir() {
t.Fatalf(
"%s; expected '%s' to be a directory", item.what, item.arg)
}
continue
}
if item.expectFile {
if result.isNodeADir() {
t.Fatalf("%s; expected '%s' to be a file", item.what, item.arg)
}
continue
}
t.Fatalf(
"%s; expected nothing for '%s', but got '%s'",
item.what, item.arg, result.Path())
}
}
func TestCleanedAbs(t *testing.T) {
cases := []struct {
what string
full string
cDir string
name string
errStr string
}{
{
what: "empty",
full: "",
errStr: "doesn't exist",
},
{
what: "simple",
full: "bob",
errStr: "'bob' doesn't exist",
},
{
what: "no directory",
full: filepath.Join("b", "rrrrrr"),
errStr: "'b/rrrrrr' doesn't exist",
},
{
what: "longer, ending in file",
full: filepath.Join("b", "d", "x"),
cDir: filepath.Join("b", "d"),
name: "x",
},
{
what: "moar longer, ending in file",
full: filepath.Join("b", "d", "a", "c", "u"),
cDir: filepath.Join("b", "d", "a", "c"),
name: "u",
},
{
what: "directory",
full: filepath.Join("b", "d"),
cDir: filepath.Join("b", "d"),
name: "",
},
}
n := makeLoadedFileTree(t)
for _, item := range cases {
cDir, name, err := n.CleanedAbs(item.full)
if item.errStr != "" {
if err == nil {
t.Fatalf("%s; expected error", item.what)
}
if !strings.Contains(err.Error(), item.errStr) {
t.Fatalf("%s; expected err containing '%s', got '%v'",
item.what, item.errStr, err)
}
continue
}
if err != nil {
t.Fatalf("%s; unexpected error: %v", item.what, err)
}
if cDir != ConfirmedDir(item.cDir) {
t.Fatalf("%s; expected cDir=%s, got '%s'", item.what, item.cDir, cDir)
}
if name != item.name {
t.Fatalf("%s; expected name=%s, got '%s'", item.what, item.name, name)
}
}
}

View File

@@ -1,20 +1,7 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
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.
*/
package fs
package filesys
import (
"fmt"
@@ -24,43 +11,43 @@ import (
"path/filepath"
)
var _ FileSystem = realFS{}
var _ FileSystem = fsOnDisk{}
// realFS implements FileSystem using the local filesystem.
type realFS struct{}
// fsOnDisk implements FileSystem using the local filesystem.
type fsOnDisk struct{}
// MakeRealFS makes an instance of realFS.
func MakeRealFS() FileSystem {
return realFS{}
// MakeFsOnDisk makes an instance of fsOnDisk.
func MakeFsOnDisk() FileSystem {
return fsOnDisk{}
}
// Create delegates to os.Create.
func (realFS) Create(name string) (File, error) { return os.Create(name) }
func (fsOnDisk) Create(name string) (File, error) { return os.Create(name) }
// Mkdir delegates to os.Mkdir.
func (realFS) Mkdir(name string) error {
func (fsOnDisk) Mkdir(name string) error {
return os.Mkdir(name, 0777|os.ModeDir)
}
// MkdirAll delegates to os.MkdirAll.
func (realFS) MkdirAll(name string) error {
func (fsOnDisk) MkdirAll(name string) error {
return os.MkdirAll(name, 0777|os.ModeDir)
}
// RemoveAll delegates to os.RemoveAll.
func (realFS) RemoveAll(name string) error {
func (fsOnDisk) RemoveAll(name string) error {
return os.RemoveAll(name)
}
// Open delegates to os.Open.
func (realFS) Open(name string) (File, error) { return os.Open(name) }
func (fsOnDisk) Open(name string) (File, error) { return os.Open(name) }
// CleanedAbs returns a cleaned, absolute path
// with no symbolic links split into directory
// and file components. If the entire path is
// a directory, the file component is an empty
// string.
func (x realFS) CleanedAbs(
// CleanedAbs converts the given path into a
// directory and a file name, where the directory
// is represented as a ConfirmedDir and all that implies.
// If the entire path is a directory, the file component
// is an empty string.
func (x fsOnDisk) CleanedAbs(
path string) (ConfirmedDir, string, error) {
absRoot, err := filepath.Abs(path)
if err != nil {
@@ -94,18 +81,18 @@ func (x realFS) CleanedAbs(
}
// Exists returns true if os.Stat succeeds.
func (realFS) Exists(name string) bool {
func (fsOnDisk) Exists(name string) bool {
_, err := os.Stat(name)
return err == nil
}
// Glob returns the list of matching files
func (realFS) Glob(pattern string) ([]string, error) {
func (fsOnDisk) Glob(pattern string) ([]string, error) {
return filepath.Glob(pattern)
}
// IsDir delegates to os.Stat and FileInfo.IsDir
func (realFS) IsDir(name string) bool {
func (fsOnDisk) IsDir(name string) bool {
info, err := os.Stat(name)
if err != nil {
return false
@@ -114,9 +101,14 @@ func (realFS) IsDir(name string) bool {
}
// ReadFile delegates to ioutil.ReadFile.
func (realFS) ReadFile(name string) ([]byte, error) { return ioutil.ReadFile(name) }
func (fsOnDisk) ReadFile(name string) ([]byte, error) { return ioutil.ReadFile(name) }
// WriteFile delegates to ioutil.WriteFile with read/write permissions.
func (realFS) WriteFile(name string, c []byte) error {
func (fsOnDisk) WriteFile(name string, c []byte) error {
return ioutil.WriteFile(name, c, 0666)
}
// Walk delegates to filepath.Walk.
func (fsOnDisk) Walk(path string, walkFn filepath.WalkFunc) error {
return filepath.Walk(path, walkFn)
}

View File

@@ -1,20 +1,7 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
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.
*/
package fs
package filesys_test
import (
"io/ioutil"
@@ -23,10 +10,12 @@ import (
"path/filepath"
"reflect"
"testing"
. "sigs.k8s.io/kustomize/api/filesys"
)
func makeTestDir(t *testing.T) (FileSystem, string) {
x := MakeRealFS()
fSys := MakeFsOnDisk()
td, err := ioutil.TempDir("", "kustomize_testing_dir")
if err != nil {
t.Fatalf("unexpected error %s", err)
@@ -35,20 +24,20 @@ func makeTestDir(t *testing.T) (FileSystem, string) {
if err != nil {
t.Fatalf("unexpected error %s", err)
}
if !x.Exists(testDir) {
if !fSys.Exists(testDir) {
t.Fatalf("expected existence")
}
if !x.IsDir(testDir) {
if !fSys.IsDir(testDir) {
t.Fatalf("expected directory")
}
return x, testDir
return fSys, testDir
}
func TestCleanedAbs_1(t *testing.T) {
x, testDir := makeTestDir(t)
fSys, testDir := makeTestDir(t)
defer os.RemoveAll(testDir)
d, f, err := x.CleanedAbs("")
d, f, err := fSys.CleanedAbs("")
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
@@ -65,10 +54,10 @@ func TestCleanedAbs_1(t *testing.T) {
}
func TestCleanedAbs_2(t *testing.T) {
x, testDir := makeTestDir(t)
fSys, testDir := makeTestDir(t)
defer os.RemoveAll(testDir)
d, f, err := x.CleanedAbs("/")
d, f, err := fSys.CleanedAbs("/")
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
@@ -81,16 +70,16 @@ func TestCleanedAbs_2(t *testing.T) {
}
func TestCleanedAbs_3(t *testing.T) {
x, testDir := makeTestDir(t)
fSys, testDir := makeTestDir(t)
defer os.RemoveAll(testDir)
err := x.WriteFile(
err := fSys.WriteFile(
filepath.Join(testDir, "foo"), []byte(`foo`))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
d, f, err := x.CleanedAbs(filepath.Join(testDir, "foo"))
d, f, err := fSys.CleanedAbs(filepath.Join(testDir, "foo"))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
@@ -100,25 +89,24 @@ func TestCleanedAbs_3(t *testing.T) {
if f != "foo" {
t.Fatalf("unexpected f=%s", f)
}
}
func TestCleanedAbs_4(t *testing.T) {
x, testDir := makeTestDir(t)
fSys, testDir := makeTestDir(t)
defer os.RemoveAll(testDir)
err := x.MkdirAll(filepath.Join(testDir, "d1", "d2"))
err := fSys.MkdirAll(filepath.Join(testDir, "d1", "d2"))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
err = x.WriteFile(
err = fSys.WriteFile(
filepath.Join(testDir, "d1", "d2", "bar"),
[]byte(`bar`))
if err != nil {
t.Fatalf("unexpected err=%v", err)
}
d, f, err := x.CleanedAbs(
d, f, err := fSys.CleanedAbs(
filepath.Join(testDir, "d1", "d2"))
if err != nil {
t.Fatalf("unexpected err=%v", err)
@@ -130,7 +118,7 @@ func TestCleanedAbs_4(t *testing.T) {
t.Fatalf("unexpected f=%s", f)
}
d, f, err = x.CleanedAbs(
d, f, err = fSys.CleanedAbs(
filepath.Join(testDir, "d1", "d2", "bar"))
if err != nil {
t.Fatalf("unexpected err=%v", err)
@@ -144,26 +132,26 @@ func TestCleanedAbs_4(t *testing.T) {
}
func TestReadFilesRealFS(t *testing.T) {
x, testDir := makeTestDir(t)
fSys, testDir := makeTestDir(t)
defer os.RemoveAll(testDir)
err := x.WriteFile(path.Join(testDir, "foo"), []byte(`foo`))
err := fSys.WriteFile(path.Join(testDir, "foo"), []byte(`foo`))
if err != nil {
t.Fatalf("unexpected error %s", err)
}
if !x.Exists(path.Join(testDir, "foo")) {
if !fSys.Exists(path.Join(testDir, "foo")) {
t.Fatalf("expected foo")
}
if x.IsDir(path.Join(testDir, "foo")) {
if fSys.IsDir(path.Join(testDir, "foo")) {
t.Fatalf("expected foo not to be a directory")
}
err = x.WriteFile(path.Join(testDir, "bar"), []byte(`bar`))
err = fSys.WriteFile(path.Join(testDir, "bar"), []byte(`bar`))
if err != nil {
t.Fatalf("unexpected error %s", err)
}
files, err := x.Glob(path.Join("testDir", "*"))
files, err := fSys.Glob(path.Join("testDir", "*"))
expected := []string{
path.Join(testDir, "bar"),
path.Join(testDir, "foo"),

30
api/filesys/util.go Normal file
View File

@@ -0,0 +1,30 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filesys
import "path/filepath"
// RootedPath returns a rooted path, e.g. "/foo/bar" as
// opposed to "foo/bar".
func RootedPath(elem ...string) string {
return Separator + filepath.Join(elem...)
}
// StripTrailingSeps trims trailing filepath separators from input.
func StripTrailingSeps(s string) string {
k := len(s)
for k > 0 && s[k-1] == filepath.Separator {
k--
}
return s[:k]
}
// StripLeadingSeps trims leading filepath separators from input.
func StripLeadingSeps(s string) string {
k := 0
for k < len(s) && s[k] == filepath.Separator {
k++
}
return s[k:]
}

205
api/filesys/util_test.go Normal file
View File

@@ -0,0 +1,205 @@
package filesys_test
import (
"path/filepath"
"testing"
. "sigs.k8s.io/kustomize/api/filesys"
)
// Confirm behavior of filepath.Match
func TestFilePathMatch(t *testing.T) {
cases := []struct {
pattern string
path string
expected bool
}{
{
pattern: "*e*",
path: "hey",
expected: true,
},
{
pattern: "*e*",
path: "hay",
expected: false,
},
{
pattern: "*e*",
path: filepath.Join("h", "e", "y"),
expected: false,
},
{
pattern: "*/e/*",
path: filepath.Join("h", "e", "y"),
expected: true,
},
{
pattern: "h/e/*",
path: filepath.Join("h", "e", "y"),
expected: true,
},
{
pattern: "*/e/y",
path: filepath.Join("h", "e", "y"),
expected: true,
},
{
pattern: "*/*/*",
path: filepath.Join("h", "e", "y"),
expected: true,
},
{
pattern: "*/*/*",
path: filepath.Join("h", "e", "y", "there"),
expected: false,
},
{
pattern: "*/*/*/t*e",
path: filepath.Join("h", "e", "y", "there"),
expected: true,
},
}
for _, item := range cases {
match, err := filepath.Match(item.pattern, item.path)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if match != item.expected {
t.Fatalf("'%s' '%s' %v\n", item.pattern, item.path, match)
}
}
}
// Confirm behavior of filepath.Split
func TestFilePathSplit(t *testing.T) {
cases := []struct {
full string
dir string
file string
}{
{
full: "",
dir: "",
file: "",
},
{
full: SelfDir,
dir: "",
file: SelfDir,
},
{
full: "rabbit.jpg",
dir: "",
file: "rabbit.jpg",
},
{
full: "/beans",
dir: "/",
file: "beans",
},
{
full: "/home/foo/bar",
dir: "/home/foo/",
file: "bar",
},
{
full: "/usr/local/",
dir: "/usr/local/",
file: "",
},
{
full: "/usr//local//go",
dir: "/usr//local//",
file: "go",
},
}
for _, p := range cases {
dir, file := filepath.Split(p.full)
if dir != p.dir || file != p.file {
t.Fatalf(
"in '%s',\ngot dir='%s' (expected '%s'),\n got file='%s' (expected %s).",
p.full, dir, p.dir, file, p.file)
}
}
}
func TestStripTrailingSeps(t *testing.T) {
cases := []struct {
full string
rem string
}{
{
full: "foo",
rem: "foo",
},
{
full: "",
rem: "",
},
{
full: "foo/",
rem: "foo",
},
{
full: "foo///bar///",
rem: "foo///bar",
},
{
full: "/////",
rem: "",
},
{
full: "/",
rem: "",
},
}
for _, p := range cases {
dir := StripTrailingSeps(p.full)
if dir != p.rem {
t.Fatalf(
"in '%s', got dir='%s' (expected '%s')",
p.full, dir, p.rem)
}
}
}
func TestStripLeadingSeps(t *testing.T) {
cases := []struct {
full string
rem string
}{
{
full: "foo",
rem: "foo",
},
{
full: "",
rem: "",
},
{
full: "/foo",
rem: "foo",
},
{
full: "///foo///bar///",
rem: "foo///bar///",
},
{
full: "/////",
rem: "",
},
{
full: "/",
rem: "",
},
}
for _, p := range cases {
dir := StripLeadingSeps(p.full)
if dir != p.rem {
t.Fatalf(
"in '%s', got dir='%s' (expected '%s')",
p.full, dir, p.rem)
}
}
}

19
api/go.mod Normal file
View File

@@ -0,0 +1,19 @@
module sigs.k8s.io/kustomize/api
go 1.13
require (
github.com/evanphx/json-patch v4.5.0+incompatible
github.com/go-openapi/spec v0.19.4
github.com/golangci/golangci-lint v1.21.0
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/monopole/mdrip v1.0.1
github.com/pkg/errors v0.8.1
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff
gopkg.in/yaml.v2 v2.2.4
k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
sigs.k8s.io/yaml v1.1.0
)

537
api/go.sum Normal file
View File

@@ -0,0 +1,537 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
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/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bombsimon/wsl v1.2.5 h1:9gTOkIwVtoDZywvX802SDHokeX4kW1cKnV8ZTVAPkRs=
github.com/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw=
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.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
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/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
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 v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db h1:GYXWx7Vr3+zv833u+8IoXbNnQY0AdXsxAgI0kX7xcwA=
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.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.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg=
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b h1:ekuhfTjngPhisSjOJ0QWKpPQE8/rbknHaes6WVJj5Hw=
github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks=
github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.19.1/go.mod h1:2CEc4Fxx3vxDv7g8DyXkHCBF73AOzAymcJAprs2vCps=
github.com/golangci/golangci-lint v1.21.0 h1:HxAxpR8Z0M8omihvQdsD3PF0qPjlqYqp2vMJzstoKeI=
github.com/golangci/golangci-lint v1.21.0/go.mod h1:phxpHK52q7SE+5KpPnti4oZTdFCEsn/tKN+nFvCKXfk=
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI=
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA=
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/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/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f h1:9oNbS1z4rVpbnkHBdPZU4jo9bSmrLpII768arSyMFgk=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.0 h1:UykbtMB/w5No2LmE16gINgLj+r/vbziTgaoERQv6U+0=
github.com/gorilla/mux v1.6.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v0.0.0-20160422134519-667fe4e3466a h1:YH0IojQwndMQdeRWdw1aPT8bkbiWaYR3WD+Zf5e09DU=
github.com/gorilla/securecookie v0.0.0-20160422134519-667fe4e3466a/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v0.0.0-20160922145804-ca9ada445741 h1:OuuPl66BpF1q3OEkaPpp+VfzxrBBY62ATGdWqql/XX8=
github.com/gorilla/sessions v0.0.0-20160922145804-ca9ada445741/go.mod h1:+WVp8kdw6VhyKExm03PAMRn2ZxnPtm58pV0dBVPdhHE=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
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/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
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/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matoous/godox v0.0.0-20190910121045-032ad8106c86/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUCe2BNSOz4tVy2yGyXhvYDvxGgeE=
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monopole/mdrip v1.0.0 h1:RFDBa+tab6mW+gX4Ww2SZDc4kS6p01FwnLtgz64Il+I=
github.com/monopole/mdrip v1.0.0/go.mod h1:N1/ppRG9CaPeUKAUHZ3dUlfOT81lTpKZLkyhCvTETwM=
github.com/monopole/mdrip v1.0.1 h1:LzwNWb8ge3+4iBZmxIE6VfUR5KIxhF7DKdf85t8Yvos=
github.com/monopole/mdrip v1.0.1/go.mod h1:/7E04hlzRG9Jrp6WILZfYYm/REoJWL2l+MlsCO1eH74=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
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.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/securego/gosec v0.0.0-20190912120752-140048b2a218/go.mod h1:q6oYAujd2qyeU4cJqIri4LBIgdHXGvxWHZ1E29HNFRE=
github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d h1:BzRvVq1EHuIjxpijCEKpAxzKUUMurOQ4sknehIATRh8=
github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do=
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbdo=
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.3/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg=
github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517 h1:ChMKTho2hWKpks/nD/FL2KqM1wuVt62oJeiE8+eFpGs=
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM=
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911230505-6bfd74cf029c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/russross/blackfriday.v2 v2.0.0 h1:+FlnIV8DSQnT7NZ43hcVKcdJdzZoeCmJj4Ql8gq5keA=
gopkg.in/russross/blackfriday.v2 v2.0.0/go.mod h1:6sSBNz/GtOm/pJTuh5UmBK2ZHfmnxGbl2NZg1UliSOI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/api v0.17.0 h1:H9d/lw+VkZKEVIUc8F3wgiQ+FUXTTr21M87jXLU7yqM=
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/apimachinery v0.17.0 h1:xRBnuie9rXcPxUkDizUsGvPf1cnlZCFu210op7J7LJo=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/client-go v0.17.0 h1:8QOGvUGdqDMFrm9sD6IUFl256BcffynGoe80sxgTEDg=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
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.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/api v0.2.0/go.mod h1:zVtMg179jW1gr74jo9fc2Ac9dLYLTZZThc3DDb9lDW4=
sigs.k8s.io/kustomize/pluginator/v2 v2.0.0 h1:sES7e09G19Q0VjRp4ATSYKpTXoWaX8WMSHfw6u3G2Ok=
sigs.k8s.io/kustomize/pluginator/v2 v2.0.0/go.mod h1:zrXhTv8BAKt0egmZX/8AtMOSFUSWM9YuoHvvqz8/eHE=
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0/go.mod h1:bl/gVJgYYhJZCZdYU2BfnaKYAlqFkgbJEkpl302jEss=
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=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@@ -6,7 +6,7 @@ package hasher_test
import (
"testing"
. "sigs.k8s.io/kustomize/v3/pkg/hasher"
. "sigs.k8s.io/kustomize/api/hasher"
)
func TestSortArrayAndComputeHash(t *testing.T) {

View File

@@ -5,8 +5,8 @@
package ifc
import (
"sigs.k8s.io/kustomize/v3/pkg/gvk"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
)
// Validator provides functions to validate annotations and labels
@@ -20,6 +20,12 @@ type Validator interface {
IsEnvVarName(k string) error
}
// KvLoader reads and validates KV pairs.
type KvLoader interface {
Validator() Validator
Load(args types.KvPairSources) (all []types.Pair, err error)
}
// Loader interface exposes methods to read bytes.
type Loader interface {
// Root returns the root location for this Loader.
@@ -30,10 +36,6 @@ type Loader interface {
Load(location string) ([]byte, error)
// Cleanup cleans the loader
Cleanup() error
// Validator validates data for use in various k8s fields.
Validator() Validator
// Loads pairs.
LoadKvPairs(args types.GeneratorArgs) ([]types.Pair, error)
}
// Kunstructured allows manipulation of k8s objects
@@ -53,7 +55,8 @@ type Kunstructured interface {
GetMap(path string) (map[string]interface{}, error)
MarshalJSON() ([]byte, error)
UnmarshalJSON([]byte) error
GetGvk() gvk.Gvk
GetGvk() resid.Gvk
SetGvk(resid.Gvk)
GetKind() string
GetName() string
SetName(string)
@@ -73,11 +76,11 @@ type KunstructuredFactory interface {
FromMap(m map[string]interface{}) Kunstructured
Hasher() KunstructuredHasher
MakeConfigMap(
ldr Loader,
kvLdr KvLoader,
options *types.GeneratorOptions,
args *types.ConfigMapArgs) (Kunstructured, error)
MakeSecret(
ldr Loader,
kvLdr KvLoader,
options *types.GeneratorOptions,
args *types.SecretArgs) (Kunstructured, error)
}

View File

@@ -1,18 +1,5 @@
/*
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.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package expansion provides functions find and replace $(FOO) style variables in strings.
package expansion

View File

@@ -1,18 +1,5 @@
/*
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.
*/
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package expansion_test
@@ -20,7 +7,7 @@ import (
"fmt"
"testing"
. "sigs.k8s.io/kustomize/v3/pkg/expansion"
. "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
)
type expected struct {

View File

@@ -1,20 +1,7 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
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.
*/
package config
package accumulator
import (
"encoding/json"
@@ -23,8 +10,11 @@ import (
"github.com/go-openapi/spec"
"github.com/pkg/errors"
"k8s.io/kube-openapi/pkg/common"
"sigs.k8s.io/kustomize/v3/pkg/gvk"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
@@ -33,8 +23,8 @@ type nameToApiMap map[string]common.OpenAPIDefinition
// LoadConfigFromCRDs parse CRD schemas from paths into a TransformerConfig
func LoadConfigFromCRDs(
ldr ifc.Loader, paths []string) (*TransformerConfig, error) {
tc := MakeEmptyConfig()
ldr ifc.Loader, paths []string) (*builtinconfig.TransformerConfig, error) {
tc := builtinconfig.MakeEmptyConfig()
for _, path := range paths {
content, err := ldr.Load(path)
if err != nil {
@@ -65,13 +55,13 @@ func makeNameToApiMap(content []byte) (result nameToApiMap, err error) {
return
}
func makeConfigFromApiMap(m nameToApiMap) (*TransformerConfig, error) {
result := MakeEmptyConfig()
func makeConfigFromApiMap(m nameToApiMap) (*builtinconfig.TransformerConfig, error) {
result := builtinconfig.MakeEmptyConfig()
for name, api := range m {
if !looksLikeAk8sType(api.Schema.SchemaProps.Properties) {
continue
}
tc := MakeEmptyConfig()
tc := builtinconfig.MakeEmptyConfig()
err := loadCrdIntoConfig(
tc, makeGvkFromTypeName(name), m, name, []string{})
if err != nil {
@@ -88,10 +78,10 @@ func makeConfigFromApiMap(m nameToApiMap) (*TransformerConfig, error) {
// TODO: Get Group and Version for CRD from the
// openAPI definition once
// "x-kubernetes-group-version-kind" is available in CRD
func makeGvkFromTypeName(n string) gvk.Gvk {
names := strings.Split(n, ".")
func makeGvkFromTypeName(n string) resid.Gvk {
names := strings.Split(n, filesys.SelfDir)
kind := names[len(names)-1]
return gvk.Gvk{Kind: kind}
return resid.Gvk{Kind: kind}
}
func looksLikeAk8sType(properties myProperties) bool {
@@ -104,10 +94,7 @@ func looksLikeAk8sType(properties myProperties) bool {
return false
}
_, ok = properties["metadata"]
if !ok {
return false
}
return true
return ok
}
const (
@@ -133,7 +120,7 @@ const (
// loadCrdIntoConfig loads a CRD spec into a TransformerConfig
func loadCrdIntoConfig(
theConfig *TransformerConfig, theGvk gvk.Gvk, theMap nameToApiMap,
theConfig *builtinconfig.TransformerConfig, theGvk resid.Gvk, theMap nameToApiMap,
typeName string, path []string) (err error) {
api, ok := theMap[typeName]
if !ok {
@@ -173,9 +160,9 @@ func loadCrdIntoConfig(
nameKey = "name"
}
err = theConfig.AddNamereferenceFieldSpec(
NameBackReferences{
Gvk: gvk.Gvk{Kind: kind, Version: version},
FieldSpecs: []FieldSpec{
builtinconfig.NameBackReferences{
Gvk: resid.Gvk{Kind: kind, Version: version},
FieldSpecs: []types.FieldSpec{
makeFs(theGvk, append(path, propName, nameKey))},
})
if err != nil {
@@ -192,8 +179,8 @@ func loadCrdIntoConfig(
return nil
}
func makeFs(in gvk.Gvk, path []string) FieldSpec {
return FieldSpec{
func makeFs(in resid.Gvk, path []string) types.FieldSpec {
return types.FieldSpec{
CreateIfNotPresent: false,
Gvk: in,
Path: strings.Join(path, "/"),

View File

@@ -1,28 +1,19 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
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.
*/
package config
package accumulator_test
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/v3/internal/loadertest"
"sigs.k8s.io/kustomize/v3/pkg/gvk"
"sigs.k8s.io/kustomize/v3/pkg/ifc"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/loader"
. "sigs.k8s.io/kustomize/api/internal/accumulator"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
)
// This defines two CRD's: Bee and MyKind.
@@ -145,44 +136,42 @@ If it is not set we generate a secret dynamically",
`
)
func makeLoader(t *testing.T) ifc.Loader {
ldr := loadertest.NewFakeLoader("/testpath")
err := ldr.AddFile("/testpath/crd.json", []byte(crdContent))
if err != nil {
t.Fatalf("Failed to setup fake ldr.")
}
return ldr
}
func TestLoadCRDs(t *testing.T) {
nbrs := []NameBackReferences{
nbrs := []builtinconfig.NameBackReferences{
{
Gvk: gvk.Gvk{Kind: "Secret", Version: "v1"},
FieldSpecs: []FieldSpec{
Gvk: resid.Gvk{Kind: "Secret", Version: "v1"},
FieldSpecs: []types.FieldSpec{
{
CreateIfNotPresent: false,
Gvk: gvk.Gvk{Kind: "MyKind"},
Gvk: resid.Gvk{Kind: "MyKind"},
Path: "spec/secretRef/name",
},
},
},
{
Gvk: gvk.Gvk{Kind: "Bee", Version: "v1beta1"},
FieldSpecs: []FieldSpec{
Gvk: resid.Gvk{Kind: "Bee", Version: "v1beta1"},
FieldSpecs: []types.FieldSpec{
{
CreateIfNotPresent: false,
Gvk: gvk.Gvk{Kind: "MyKind"},
Gvk: resid.Gvk{Kind: "MyKind"},
Path: "spec/beeRef/name",
},
},
},
}
expectedTc := &TransformerConfig{
expectedTc := &builtinconfig.TransformerConfig{
NameReference: nbrs,
}
actualTc, err := LoadConfigFromCRDs(makeLoader(t), []string{"crd.json"})
fSys := filesys.MakeFsInMemory()
fSys.WriteFile("/testpath/crd.json", []byte(crdContent))
ldr, err := loader.NewLoader(loader.RestrictionRootOnly, "/testpath", fSys)
if err != nil {
t.Fatalf("unexpected error:%v", err)
}
actualTc, err := LoadConfigFromCRDs(ldr, []string{"crd.json"})
if err != nil {
t.Fatalf("unexpected error:%v", err)
}

View File

@@ -0,0 +1,269 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package accumulator
import (
"fmt"
"log"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/transform"
)
type nameReferenceTransformer struct {
backRefs []builtinconfig.NameBackReferences
}
var _ resmap.Transformer = &nameReferenceTransformer{}
// newNameReferenceTransformer constructs a nameReferenceTransformer
// with a given slice of NameBackReferences.
func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.Transformer {
if br == nil {
log.Fatal("backrefs not expected to be nil")
}
return &nameReferenceTransformer{backRefs: br}
}
// Transform updates name references in resource A that
// refer to resource B, given that B's name may have
// changed.
//
// For example, a HorizontalPodAutoscaler (HPA)
// necessarily refers to a Deployment, the thing that
// the HPA scales. The Deployment name might change
// (e.g. prefix added), and the reference in the HPA
// has to be fixed.
//
// In the outer loop over the ResMap below, say we
// encounter a specific HPA. Then, in scanning backrefs,
// we encounter an entry like
//
// - kind: Deployment
// fieldSpecs:
// - kind: HorizontalPodAutoscaler
// path: spec/scaleTargetRef/name
//
// This entry says that an HPA, via its
// 'spec/scaleTargetRef/name' field, may refer to a
// Deployment. This match to HPA means we may need to
// modify the value in its 'spec/scaleTargetRef/name'
// field, by searching for the thing it refers to,
// and getting its new name.
//
// As a filter, and search optimization, we compute a
// subset of all resources that the HPA could refer to,
// by excluding objects from other namespaces, and
// excluding objects that don't have the same prefix-
// suffix mods as the HPA.
//
// We look in this subset for all Deployment objects
// with a resId that has a Name matching the field value
// present in the HPA. If no match do nothing; if more
// than one match, it's an error.
//
// We overwrite the HPA name field with the value found
// in the Deployment's name field (the name in the raw
// object - the modified name - not the unmodified name
// in the Deployment's resId).
//
// This process assumes that the name stored in a ResId
// (the ResMap key) isn't modified by name transformers.
// Name transformers should only modify the name in the
// body of the resource object (the value in the ResMap).
//
func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
// TODO: Too much looping, here and in transitive calls.
for _, referrer := range m.Resources() {
var candidates resmap.ResMap
for _, target := range o.backRefs {
for _, fSpec := range target.FieldSpecs {
if referrer.OrgId().IsSelected(&fSpec.Gvk) {
if candidates == nil {
candidates = m.SubsetThatCouldBeReferencedByResource(referrer)
}
err := transform.MutateField(
referrer.Map(),
fSpec.PathSlice(),
fSpec.CreateIfNotPresent,
o.getNewNameFunc(
// referrer could be an HPA instance,
// target could be Gvk for Deployment,
// candidate a list of resources "reachable"
// from the HPA.
referrer, target.Gvk, candidates))
if err != nil {
return err
}
}
}
}
}
return nil
}
// selectReferral picks the referral among a subset of candidates.
// It returns the current name and namespace of the selected candidate.
// Note that the content of the referricalCandidateSubset slice is most of the time
// identical to the referralCandidates resmap. Still in some cases, such
// as ClusterRoleBinding, the subset only contains the resources of a specific
// namespace.
func (o *nameReferenceTransformer) selectReferral(
oldName string,
referrer *resource.Resource,
target resid.Gvk,
referralCandidates resmap.ResMap,
referralCandidateSubset []*resource.Resource) (interface{}, interface{}, error) {
for _, res := range referralCandidateSubset {
id := res.OrgId()
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
// If there's more than one match, there's no way
// to know which one to pick, so emit error.
if len(matches) > 1 {
return nil, nil, fmt.Errorf(
"multiple matches for %s:\n %v",
id, getIds(matches))
}
// In the resource, note that it is referenced
// by the referrer.
res.AppendRefBy(referrer.CurId())
// Return transformed name of the object,
// complete with prefixes, hashes, etc.
return res.GetName(), res.GetNamespace(), nil
}
}
return oldName, nil, nil
}
// utility function to replace a simple string by the new name
func (o *nameReferenceTransformer) getSimpleNameField(
oldName string,
referrer *resource.Resource,
target resid.Gvk,
referralCandidates resmap.ResMap,
referralCandidateSubset []*resource.Resource) (interface{}, error) {
newName, _, err := o.selectReferral(oldName, referrer, target,
referralCandidates, referralCandidateSubset)
return newName, err
}
// utility function to replace name field within a map[string]interface{}
// and leverage the namespace field.
func (o *nameReferenceTransformer) getNameAndNsStruct(
inMap map[string]interface{},
referrer *resource.Resource,
target resid.Gvk,
referralCandidates resmap.ResMap) (interface{}, error) {
// Example:
if _, ok := inMap["name"]; !ok {
return nil, fmt.Errorf(
"%#v is expected to contain a name field", inMap)
}
oldName, ok := inMap["name"].(string)
if !ok {
return nil, fmt.Errorf(
"%#v is expected to contain a name field of type string", oldName)
}
subset := referralCandidates.Resources()
if namespacevalue, ok := inMap["namespace"]; ok {
namespace := namespacevalue.(string)
bynamespace := referralCandidates.GroupedByOriginalNamespace()
if _, ok := bynamespace[namespace]; !ok {
return inMap, nil
}
subset = bynamespace[namespace]
}
newname, newnamespace, err := o.selectReferral(oldName, referrer, target,
referralCandidates, subset)
if err != nil {
return nil, err
}
if (newname == oldName) && (newnamespace == nil) {
// no candidate found.
return inMap, nil
}
inMap["name"] = newname
if newnamespace != "" {
// We don't want value "" to replace value "default" since
// the empty string is handled as a wild card here not default namespace
// by kubernetes.
inMap["namespace"] = newnamespace
}
return inMap, nil
}
func (o *nameReferenceTransformer) getNewNameFunc(
referrer *resource.Resource,
target resid.Gvk,
referralCandidates resmap.ResMap) func(in interface{}) (interface{}, error) {
return func(in interface{}) (interface{}, error) {
switch thing := in.(type) {
case string:
return o.getSimpleNameField(thing, referrer, target,
referralCandidates, referralCandidates.Resources())
case map[string]interface{}:
// Kind: ValidatingWebhookConfiguration
// FieldSpec is webhooks/clientConfig/service
return o.getNameAndNsStruct(thing, referrer, target,
referralCandidates)
case []interface{}:
for idx, item := range thing {
switch value := item.(type) {
case string:
// Kind: Role/ClusterRole
// FieldSpec is rules.resourceNames
newName, err := o.getSimpleNameField(value, referrer, target,
referralCandidates, referralCandidates.Resources())
if err != nil {
return nil, err
}
thing[idx] = newName
case map[string]interface{}:
// Kind: RoleBinding/ClusterRoleBinding
// FieldSpec is subjects
// Note: The corresponding fieldSpec had been changed from
// from path: subjects/name to just path: subjects. This is
// what get mutatefield to request the mapping of the whole
// map containing namespace and name instead of just a simple
// string field containing the name
newMap, err := o.getNameAndNsStruct(value, referrer, target,
referralCandidates)
if err != nil {
return nil, err
}
thing[idx] = newMap
default:
return nil, fmt.Errorf(
"%#v is expected to be either a []string or a []map[string]interface{}", in)
}
}
return in, nil
default:
return nil, fmt.Errorf(
"%#v is expected to be either a string or a []interface{}", in)
}
}
}
func getIds(rs []*resource.Resource) []string {
var result []string
for _, r := range rs {
result = append(result, r.CurId().String()+"\n")
}
return result
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +1,31 @@
/*
Copyright 2018 The Kubernetes Authors.
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
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.
*/
package transformers
package accumulator
import (
"fmt"
"sigs.k8s.io/kustomize/v3/pkg/expansion"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/transformers/config"
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
)
type RefVarTransformer struct {
type refVarTransformer struct {
varMap map[string]interface{}
replacementCounts map[string]int
fieldSpecs []config.FieldSpec
fieldSpecs []types.FieldSpec
mappingFunc func(string) interface{}
}
// NewRefVarTransformer returns a new RefVarTransformer
// newRefVarTransformer returns a new refVarTransformer
// that replaces $(VAR) style variables with values.
// The fieldSpecs are the places to look for occurrences of $(VAR).
func NewRefVarTransformer(
varMap map[string]interface{}, fs []config.FieldSpec) *RefVarTransformer {
return &RefVarTransformer{
func newRefVarTransformer(
varMap map[string]interface{}, fs []types.FieldSpec) *refVarTransformer {
return &refVarTransformer{
varMap: varMap,
fieldSpecs: fs,
}
@@ -45,12 +35,12 @@ func NewRefVarTransformer(
// embedded instances of $VAR style variables, e.g. a container command string.
// The function returns the string with the variables expanded to their final
// values.
func (rv *RefVarTransformer) replaceVars(in interface{}) (interface{}, error) {
func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
switch vt := in.(type) {
case []interface{}:
var xs []interface{}
for _, a := range in.([]interface{}) {
xs = append(xs, expansion.Expand(a.(string), rv.mappingFunc))
xs = append(xs, expansion2.Expand(a.(string), rv.mappingFunc))
}
return xs, nil
case map[string]interface{}:
@@ -67,7 +57,7 @@ func (rv *RefVarTransformer) replaceVars(in interface{}) (interface{}, error) {
// This field can potentially contains a $(VAR) since it is
// of string type. For instance .spec.replicas: $(REPLICAS)
// in a Deployment object
xs[k] = expansion.Expand(s, rv.mappingFunc)
xs[k] = expansion2.Expand(s, rv.mappingFunc)
}
}
return xs, nil
@@ -79,7 +69,10 @@ func (rv *RefVarTransformer) replaceVars(in interface{}) (interface{}, error) {
}
// This field can potentially contain a $(VAR) since it is
// of string type.
return expansion.Expand(s, rv.mappingFunc), nil
return expansion2.Expand(s, rv.mappingFunc), nil
// staticcheck erroneously claims that `case nil`
// is unreachable here, so suppressing it.
//nolint:staticcheck
case nil:
return nil, nil
default:
@@ -89,7 +82,7 @@ func (rv *RefVarTransformer) replaceVars(in interface{}) (interface{}, error) {
// UnusedVars returns slice of Var names that were unused
// after a Transform run.
func (rv *RefVarTransformer) UnusedVars() []string {
func (rv *refVarTransformer) UnusedVars() []string {
var unused []string
for k := range rv.varMap {
_, ok := rv.replacementCounts[k]
@@ -101,14 +94,14 @@ func (rv *RefVarTransformer) UnusedVars() []string {
}
// Transform replaces $(VAR) style variables with values.
func (rv *RefVarTransformer) Transform(m resmap.ResMap) error {
func (rv *refVarTransformer) Transform(m resmap.ResMap) error {
rv.replacementCounts = make(map[string]int)
rv.mappingFunc = expansion.MappingFuncFor(
rv.mappingFunc = expansion2.MappingFuncFor(
rv.replacementCounts, rv.varMap)
for _, res := range m.Resources() {
for _, fieldSpec := range rv.fieldSpecs {
if res.OrgId().IsSelected(&fieldSpec.Gvk) {
if err := MutateField(
if err := transform.MutateField(
res.Map(), fieldSpec.PathSlice(),
false, rv.replaceVars); err != nil {
return err

View File

@@ -0,0 +1,137 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package accumulator
import (
"reflect"
"testing"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
"sigs.k8s.io/kustomize/api/types"
)
func TestRefVarTransformer(t *testing.T) {
type given struct {
varMap map[string]interface{}
fs []types.FieldSpec
res resmap.ResMap
}
type expected struct {
res resmap.ResMap
unused []string
}
testCases := []struct {
description string
given given
expected expected
}{
{
description: "var replacement in map[string]",
given: given{
varMap: map[string]interface{}{
"FOO": "replacementForFoo",
"BAR": "replacementForBar",
"BAZ": int64(5),
"BOO": true,
},
fs: []types.FieldSpec{
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/map"},
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/slice"},
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/interface"},
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/nil"},
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/num"},
},
res: resmaptest_test.NewRmBuilder(
t, resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
},
"data": map[string]interface{}{
"map": map[string]interface{}{
"item1": "$(FOO)",
"item2": "bla",
"item3": "$(BAZ)",
"item4": "$(BAZ)+$(BAZ)",
"item5": "$(BOO)",
"item6": "if $(BOO)",
"item7": 2019,
},
"slice": []interface{}{
"$(FOO)",
"bla",
"$(BAZ)",
"$(BAZ)+$(BAZ)",
"$(BOO)",
"if $(BOO)",
},
"interface": "$(FOO)",
"nil": nil,
"num": 2019,
}}).ResMap(),
},
expected: expected{
res: resmaptest_test.NewRmBuilder(
t, resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
},
"data": map[string]interface{}{
"map": map[string]interface{}{
"item1": "replacementForFoo",
"item2": "bla",
"item3": int64(5),
"item4": "5+5",
"item5": true,
"item6": "if true",
"item7": 2019,
},
"slice": []interface{}{
"replacementForFoo",
"bla",
int64(5),
"5+5",
true,
"if true",
},
"interface": "replacementForFoo",
"nil": nil,
"num": 2019,
}}).ResMap(),
unused: []string{"BAR"},
},
},
}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
// arrange
tr := newRefVarTransformer(tc.given.varMap, tc.given.fs)
// act
err := tr.Transform(tc.given.res)
// assert
if err != nil {
t.Errorf("unexpected error: %v", err)
}
a, e := tc.given.res, tc.expected.res
if !reflect.DeepEqual(a, e) {
err = e.ErrorIfNotEqualLists(a)
t.Fatalf("actual doesn't match expected: \nACTUAL:\n%v\nEXPECTED:\n%v\nERR: %v", a, e, err)
}
})
}
}

View File

@@ -8,11 +8,10 @@ import (
"log"
"strings"
"sigs.k8s.io/kustomize/v3/pkg/resid"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/transformers"
"sigs.k8s.io/kustomize/v3/pkg/transformers/config"
"sigs.k8s.io/kustomize/v3/pkg/types"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
)
// ResAccumulator accumulates resources and the rules
@@ -20,14 +19,14 @@ import (
// plus stuff needed to modify the ResMap.
type ResAccumulator struct {
resMap resmap.ResMap
tConfig *config.TransformerConfig
tConfig *builtinconfig.TransformerConfig
varSet types.VarSet
}
func MakeEmptyAccumulator() *ResAccumulator {
ra := &ResAccumulator{}
ra.resMap = resmap.New()
ra.tConfig = &config.TransformerConfig{}
ra.tConfig = &builtinconfig.TransformerConfig{}
ra.varSet = types.NewVarSet()
return ra
}
@@ -53,19 +52,25 @@ func (ra *ResAccumulator) AbsorbAll(
}
func (ra *ResAccumulator) MergeConfig(
tConfig *config.TransformerConfig) (err error) {
tConfig *builtinconfig.TransformerConfig) (err error) {
ra.tConfig, err = ra.tConfig.Merge(tConfig)
return err
}
func (ra *ResAccumulator) GetTransformerConfig() *config.TransformerConfig {
func (ra *ResAccumulator) GetTransformerConfig() *builtinconfig.TransformerConfig {
return ra.tConfig
}
func (ra *ResAccumulator) MergeVars(incoming []types.Var) error {
for _, v := range incoming {
matched := ra.resMap.GetMatchingResourcesByOriginalId(
resid.NewResId(v.ObjRef.GVK(), v.ObjRef.Name).GvknEquals)
targetId := resid.NewResIdWithNamespace(v.ObjRef.GVK(), v.ObjRef.Name, v.ObjRef.Namespace)
idMatcher := targetId.GvknEquals
if targetId.Namespace != "" || !targetId.IsNamespaceableKind() {
// Preserve backward compatibility. An empty namespace means
// wildcard search on the namespace hence we still use GvknEquals
idMatcher = targetId.Equals
}
matched := ra.resMap.GetMatchingResourcesByOriginalId(idMatcher)
if len(matched) > 1 {
return fmt.Errorf(
"found %d resId matches for var %s "+
@@ -129,7 +134,7 @@ func (ra *ResAccumulator) makeVarReplacementMap() (map[string]interface{}, error
return result, nil
}
func (ra *ResAccumulator) Transform(t transformers.Transformer) error {
func (ra *ResAccumulator) Transform(t resmap.Transformer) error {
return t.Transform(ra.resMap)
}
@@ -141,7 +146,7 @@ func (ra *ResAccumulator) ResolveVars() error {
if len(replacementMap) == 0 {
return nil
}
t := transformers.NewRefVarTransformer(
t := newRefVarTransformer(
replacementMap, ra.tConfig.VarReference)
err = ra.Transform(t)
if len(t.UnusedVars()) > 0 {
@@ -156,6 +161,6 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
if ra.tConfig.NameReference == nil {
return nil
}
return ra.Transform(transformers.NewNameReferenceTransformer(
return ra.Transform(newNameReferenceTransformer(
ra.tConfig.NameReference))
}

View File

@@ -10,19 +10,19 @@ import (
"strings"
"testing"
"sigs.k8s.io/kustomize/v3/k8sdeps/kunstruct"
. "sigs.k8s.io/kustomize/v3/pkg/accumulator"
"sigs.k8s.io/kustomize/v3/pkg/gvk"
"sigs.k8s.io/kustomize/v3/pkg/resmap"
"sigs.k8s.io/kustomize/v3/pkg/resmaptest"
"sigs.k8s.io/kustomize/v3/pkg/resource"
"sigs.k8s.io/kustomize/v3/pkg/transformers/config"
"sigs.k8s.io/kustomize/v3/pkg/types"
. "sigs.k8s.io/kustomize/api/internal/accumulator"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
"sigs.k8s.io/kustomize/api/types"
)
func makeResAccumulator(t *testing.T) (*ResAccumulator, *resource.Factory) {
ra := MakeEmptyAccumulator()
err := ra.MergeConfig(config.MakeDefaultConfig())
err := ra.MergeConfig(builtinconfig.MakeDefaultConfig())
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
@@ -75,13 +75,13 @@ func TestResolveVarsHappy(t *testing.T) {
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne"},
},
{
Name: "SERVICE_TWO",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "backendTwo"},
},
})
@@ -104,13 +104,13 @@ func TestResolveVarsOneUnused(t *testing.T) {
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne"},
},
{
Name: "SERVICE_UNUSED",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "backendTwo"},
},
})
@@ -165,7 +165,7 @@ func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne",
},
},
@@ -179,13 +179,94 @@ func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
}
}
func makeNamespacedConfigMapWithDataProviderValue(
namespace string,
value string,
) map[string]interface{} {
return map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "environment",
"namespace": namespace,
},
"data": map[string]interface{}{
"provider": value,
},
}
}
func makeVarToNamepaceAndPath(
name string,
namespace string,
path string,
) types.Var {
return types.Var{
Name: name,
ObjRef: types.Target{
Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"},
Name: "environment",
Namespace: namespace,
},
FieldRef: types.FieldSelector{FieldPath: path},
}
}
func TestResolveVarConflicts(t *testing.T) {
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
// create configmaps in foo and bar namespaces with `data.provider` values.
fooAws := makeNamespacedConfigMapWithDataProviderValue("foo", "aws")
barAws := makeNamespacedConfigMapWithDataProviderValue("bar", "aws")
barGcp := makeNamespacedConfigMapWithDataProviderValue("bar", "gcp")
// create two variables with (apparently) conflicting names that point to
// fieldpaths that could be generalized.
varFoo := makeVarToNamepaceAndPath("PROVIDER", "foo", "data.provider")
varBar := makeVarToNamepaceAndPath("PROVIDER", "bar", "data.provider")
// create accumulators holding apparently conflicting vars that are not
// actually in conflict because they point to the same concrete value.
rm0 := resmap.New()
rm0.Append(rf.FromMap(fooAws))
ac0 := MakeEmptyAccumulator()
ac0.AppendAll(rm0)
ac0.MergeVars([]types.Var{varFoo})
rm1 := resmap.New()
rm1.Append(rf.FromMap(barAws))
ac1 := MakeEmptyAccumulator()
ac1.AppendAll(rm1)
ac1.MergeVars([]types.Var{varBar})
// validate that two vars of the same name which reference the same concrete
// value do not produce a conflict.
err := ac0.MergeAccumulator(ac1)
if err == nil {
t.Fatalf("see bug gh-1600")
}
// create an accumulator will have an actually conflicting value with the
// two above (because it contains a variable whose name is used in the other
// accumulators AND whose concrete values are different).
rm2 := resmap.New()
rm2.Append(rf.FromMap(barGcp))
ac2 := MakeEmptyAccumulator()
ac2.AppendAll(rm2)
ac2.MergeVars([]types.Var{varBar})
err = ac1.MergeAccumulator(ac2)
if err == nil {
t.Fatalf("dupe vars w/ different concrete values should conflict")
}
}
func TestResolveVarsGoodResIdBadField(t *testing.T) {
ra, _ := makeResAccumulator(t)
err := ra.MergeVars([]types.Var{
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne"},
FieldRef: types.FieldSelector{FieldPath: "nope_nope_nope"},
},
@@ -210,7 +291,7 @@ func TestResolveVarsUnmappableVar(t *testing.T) {
{
Name: "SERVICE_THREE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "doesNotExist"},
},
})
@@ -234,7 +315,7 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
{
Name: "SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne",
},
},
@@ -289,7 +370,7 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
{
Name: "SUB_SERVICE_ONE",
ObjRef: types.Target{
Gvk: gvk.Gvk{Version: "v1", Kind: "Service"},
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
Name: "backendOne",
},
},

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:
```
{
"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.

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:
```
{
"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/api/internal/crawl/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/api/internal/crawl
COPY . /go/src/sigs.k8s.io/kustomize/api/internal/crawl
RUN go mod download
RUN CGO_ENABLED=0 go install sigs.k8s.io/kustomize/api/internal/crawl/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,29 @@
package main
import (
"context"
"log"
"os"
server "sigs.k8s.io/kustomize/api/internal/crawl/backend"
"strconv"
)
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,15 @@
FROM golang:1.11 AS build
ARG GO111MODULE=on
WORKDIR /go/src/sigs.k8s.io/kustomize/api/internal/crawl
COPY . /go/src/sigs.k8s.io/kustomize//api/internal/crawl
RUN go mod download
RUN CGO_ENABLED=0 go install -v ./cmd/crawler/crawler.go
FROM scratch
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /go/bin/crawler /
ENTRYPOINT ["/crawler"]
CMD []

View File

@@ -0,0 +1,93 @@
package main
import (
"context"
"fmt"
"net/http"
"os"
"time"
"sigs.k8s.io/kustomize/api/internal/crawl/crawler"
"sigs.k8s.io/kustomize/api/internal/crawl/crawler/github"
"sigs.k8s.io/kustomize/api/internal/crawl/doc"
"sigs.k8s.io/kustomize/api/internal/crawl/httpclient"
"sigs.k8s.io/kustomize/api/internal/crawl/index"
"github.com/gomodule/redigo/redis"
)
const (
githubAccessTokenVar = "GITHUB_ACCESS_TOKEN"
redisCacheURL = "REDIS_CACHE_URL"
redisKeyURL = "REDIS_KEY_URL"
retryCount = 3
)
func main() {
githubToken := os.Getenv(githubAccessTokenVar)
if githubToken == "" {
fmt.Printf("Must set the variable '%s' to make github requests.\n",
githubAccessTokenVar)
return
}
ctx := context.Background()
idx, err := index.NewKustomizeIndex(ctx)
if err != nil {
fmt.Printf("Could not create an index: %v\n", err)
return
}
cacheURL := os.Getenv(redisCacheURL)
query := []byte(`{ "query":{ "match_all":{} } }`)
it := idx.IterateQuery(query, 10000, 60*time.Second)
docs := make(crawler.CrawlSeed, 0)
for it.Next() {
for _, hit := range it.Value().Hits.Hits {
docs = append(docs, hit.Document.Copy())
}
}
if err := it.Err(); err != nil {
fmt.Printf("Error iterating: %v\n", err)
}
cache, err := redis.DialURL(cacheURL)
clientCache := &http.Client{}
if err != nil {
fmt.Printf("Error: redis could not make a connection: %v\n", err)
} else {
clientCache = httpclient.NewClient(cache)
}
ghCrawler := github.NewCrawler(githubToken, retryCount, clientCache,
github.QueryWith(
github.Filename("kustomization.yaml"),
github.Filename("kustomization.yml")),
)
crawler.CrawlFromSeed(ctx, docs, []crawler.Crawler{ghCrawler},
// Converter takes in a plain document and processes it for the
// index.
func(d *doc.Document) (crawler.CrawledDocument, error) {
kdoc := doc.KustomizationDocument{
Document: *d,
}
err := kdoc.ParseYAML()
return &kdoc, err
},
// IndexFunc updates the value in the index.
func(cdoc crawler.CrawledDocument, crwlr crawler.Crawler) error {
switch d := cdoc.(type) {
case *doc.KustomizationDocument:
fmt.Println("Inserting: ", d.ID(), d)
_, err := idx.Put(d.ID(), d)
return err
default:
return fmt.Errorf("type %T not supported", d)
}
},
)
}

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,2 @@
<ADD YOUR GITHUB PERSONAL ACCESS TOKEN HERE WITHOUT A TRAILING NEWLINE>
Run: printf "<your-token>" > github_api_secret.txt

View File

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

View File

@@ -0,0 +1,31 @@
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
imagePullPolicy: Always
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,33 @@
apiVersion: batch/v1
kind: Job
metadata:
name: crawler
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: crawler
image: gcr.io/kustomize-search/crawler:latest
imagePullPolicy: Always
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,43 @@
apiVersion: elasticsearch.cloud.google.com/v1alpha1
kind: ESCluster
metadata:
name: esbasic
spec:
plugin:
pluginList:
- repository-gcs
- ingest-user-agent
- ingest-geoip
config:
env:
example: test
nodegroups:
- name: di
replicas: 2
data: true
ingest: true
config:
jvm:
- Djava.net.preferIPv4Stack=true
- Xms2g
- Xmx2g
es:
path.repo: '["/tmp/es_backup_basic"]'
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- topologyKey: kubernetes.io/hostname
labelSelector:
matchLabels:
es/nodegroup: di
resources:
requests:
memory: 3Gi
limits:
memory: 3Gi
- name: m
replicas: 2
master: true
config:
es:
path.repo: '["/tmp/es_backup_basic"]'

View File

@@ -0,0 +1,16 @@
apiVersion: v1
kind: Service
metadata:
name: elasticsearch
spec:
selector:
custom-resource: v1alpha1.ESCluster
custom-resource-name: esbasic
custom-resource-namespace: default
es/data: "true"
using: escluster.Cluster
ports:
- protocol: "TCP"
port: 9200
type: LoadBalancer
loadBalancerIP: ""

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,35 @@
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
imagePullPolicy: Always
livenessProbe:
httpGet:
path: /liveness
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,23 @@
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
imagePullPolicy: Always
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,238 @@
// 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/api/internal/crawl/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<- CrawledDocument) 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 CrawledDocument interface {
ID() string
GetDocument() *doc.Document
GetResources() ([]*doc.Document, error)
WasCached() bool
}
type CrawlSeed []*doc.Document
type IndexFunc func(CrawledDocument, Crawler) error
type Converter func(*doc.Document) (CrawledDocument, 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 CrawlSeed,
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(CrawlSeed, 0)
findMatch := func(d *doc.Document) Crawler {
for _, crawl := range crawlers {
if crawl.Match(d) {
return crawl
}
}
return nil
}
addBranches := func(cdoc CrawledDocument, 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 *CrawlSeed) {
n := len(*docsPtr)
for i := 0; i < n; i++ {
next := (*docsPtr)[i]
match := findMatch(next)
if match == nil {
logIfErr(fmt.Errorf(
"%v could not match any crawler", next))
continue
}
logger.Println("Crawling ", next.RepositoryURL, next.FilePath)
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() {
if next.WasCached() {
logger.Println(next.RepositoryURL, next.FilePath, "is cached already")
}
continue
}
logIfErr(match.SetCreated(ctx, next))
cdoc, err := conv(next)
logIfErr(err)
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 CrawledDocument, 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 := CRunner(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)
}
// CRunner 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.
//
// CRunner 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 guaranteed, but the CRunner does guarantee that every element
// from the seed will be processed before any other documents from the
// crawlers.
func CRunner(ctx context.Context,
output chan<- CrawledDocument, 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 CrawledDocument)
wg.Add(2)
// Forward all of the documents from this crawler's channel to
// the main output channel.
go func(docs <-chan CrawledDocument) {
defer wg.Done()
for d := range docs {
output <- d
}
}(docs)
// Run this crawler and capture its returned error.
go func(idx int, crawler Crawler,
docs chan<- CrawledDocument) {
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/api/internal/crawl/doc"
"sigs.k8s.io/kustomize/api/konfig"
)
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
}
func (c testCrawler) FetchDocument(_ 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 konfig.RecognizedKustomizationFileNames() {
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(_ 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(_ context.Context,
output chan<- CrawledDocument) error {
for i, d := range c.docs {
isResource := true
for _, suffix := range konfig.RecognizedKustomizationFileNames() {
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 CRunner")
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 CrawledDocument)
wg := sync.WaitGroup{}
wg.Add(1)
// Run the Crawler runner with a list of crawlers.
go func() {
defer close(output)
defer wg.Done()
errs := CRunner(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 CrawlSeed
matcher string
corpus []doc.KustomizationDocument
}{
{
seed: CrawlSeed{
{
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) (CrawledDocument, error) {
return &doc.KustomizationDocument{
Document: *d,
}, nil
},
func(d CrawledDocument, 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,598 @@
// 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/api/internal/crawl/crawler"
"sigs.k8s.io/kustomize/api/internal/crawl/doc"
"sigs.k8s.io/kustomize/api/internal/crawl/httpclient"
"sigs.k8s.io/kustomize/api/internal/git"
"sigs.k8s.io/kustomize/api/konfig"
)
var logger = log.New(os.Stdout, "Github Crawler: ",
log.LstdFlags|log.LUTC|log.Llongfile)
// Implements crawler.Crawler.
type githubCrawler struct {
client GhClient
query Query
}
type GhClient struct {
RequestConfig
retryCount uint64
client *http.Client
accessToken string
}
func NewCrawler(accessToken string, retryCount uint64, client *http.Client,
query Query) githubCrawler {
return githubCrawler{
client: GhClient{
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.CrawledDocument) error {
noETagClient := GhClient{
RequestConfig: gc.client.RequestConfig,
client: &http.Client{Timeout: gc.client.client.Timeout},
retryCount: gc.client.retryCount,
accessToken: gc.client.accessToken,
}
// 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(_ 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 konfig.RecognizedKustomizationFileNames() {
resp, err = gc.client.GetRawUserContent(url + "/" + file)
err := handle(resp, err, "/"+file)
if err != nil {
continue
}
}
return fmt.Errorf("file not found: %s, error: %v", url, err)
}
func (gc githubCrawler) SetCreated(_ context.Context, d *doc.Document) error {
fs := GhFileSpec{
Path: d.FilePath,
Repository: GitRepository{
FullName: d.RepositoryFullName(),
},
}
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 GhClient, query string,
output chan<- crawler.CrawledDocument) error {
queryPages := make(chan GhResponseInfo)
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 {
logger.Printf("kustomizationResultAdapter failed: %v", err)
errs = append(errs, err)
errorCnt++
}
if k != nil {
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 GhClient, k GhFileSpec) (
crawler.CrawledDocument, error) {
data, err := gcl.GetFileData(k)
if err != nil {
return nil, 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"
}
d := doc.KustomizationDocument{
Document: doc.Document{
DocumentData: string(data),
FilePath: k.Path,
DefaultBranch: defaultBranch,
RepositoryURL: k.Repository.URL,
},
}
logger.Printf("Set the creationTime field")
creationTime, err := gcl.GetFileCreationTime(k)
if err != nil {
logger.Printf("GetFileCreationTime failed: %v", err)
return &d, err
}
d.CreationTime = &creationTime
if err := d.ParseYAML(); err != nil {
logger.Printf("ParseYAML failed: %v", err)
return &d, err
}
return &d, 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 GhClient) ForwardPaginatedQuery(ctx context.Context, query string,
output chan<- GhResponseInfo) 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 GhClient) GetFileData(k GhFileSpec) ([]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 GhClient) 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 GhClient) GetFileCreationTime(
k GhFileSpec) (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)
}
var 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 (e multiError) Error() string {
size := len(e) + 2
strs := make([]string, size)
strs[0] = "Errors ["
for i, err := range e {
strs[i+1] = "\t" + err.Error()
}
strs[size-1] = "]"
return strings.Join(strs, "\n")
}
type GitRepository struct {
API string `json:"url,omitempty"`
URL string `json:"html_url,omitempty"`
FullName string `json:"full_name,omitempty"`
}
type GhFileSpec struct {
Path string `json:"path,omitempty"`
Repository GitRepository `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 []GhFileSpec `json:"items,omitempty"`
}
type GhResponseInfo 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 GhClient) parseGithubResponse(getRequest string) GhResponseInfo {
resp, err := gcl.SearchGithubAPI(getRequest)
requestInfo := GhResponseInfo{
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 GhClient) 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 GhClient) 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 GhClient) GetRawUserContent(query string) (*http.Response, error) {
return gcl.getWithRetry(query)
}
func (gcl GhClient) Do(query string) (*http.Response, error) {
req, err := http.NewRequest("GET", query, nil)
if err != nil {
return nil, err
}
req.Header.Add("Authorization", fmt.Sprintf("token %s", gcl.accessToken))
return gcl.client.Do(req)
}
func (gcl GhClient) getWithRetry(
query string) (resp *http.Response, err error) {
resp, err = gcl.Do(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.Do(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,212 @@
package github
import (
"fmt"
"net/url"
"strings"
)
const (
perPageArg = "per_page"
)
const 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 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
}
// 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{}
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 = query + sep + encoded
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,117 @@
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 (
perPage = 100
)
testCases := []struct {
rc RequestConfig
codeQuery Query
fullRepoName string
path string
expectedCodeQuery string
expectedContentsQuery string
expectedCommitsQuery string
}{
{
rc: RequestConfig{
perPage: perPage,
},
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?" +
"q=filename:kustomization.yaml+size:64..128&order=desc&per_page=100&sort=indexed",
expectedContentsQuery: "https://api.github.com/repos/kubernetes-sigs/kustomize/contents/" +
"examples/helloWorld/kustomization.yaml?per_page=100",
expectedCommitsQuery: "https://api.github.com/repos/kubernetes-sigs/kustomize/commits?" +
"q=path:examples/helloWorld/kustomization.yaml&per_page=100",
},
}
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 GhClient
baseRequest request
}
func newCache(client GhClient, 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)
}
}

View File

@@ -0,0 +1,213 @@
package doc
import (
"fmt"
"strings"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/konfig"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
var fileReader = kunstruct.NewKunstructuredFactoryImpl()
// This document is meant to be used at the elasticsearch document type.
// Fields are serialized as-is to elasticsearch, where indices are built
// to facilitate text search queries. Identifiers, Values, FilePath,
// RepositoryURL and DocumentData are meant to be searched for text queries
// directly, while the other fields can either be used as a filter, or as
// additional metadata displayed in the UI.
//
// The fields of the document and their purpose are listed below:
// - DocumentData contains the contents of the kustomization file.
// - Kinds Represents the kubernetes Kinds that are in this file.
// - Identifiers are a list of (partial and full) identifier paths that can be
// found by users. Each part of a path is delimited by ":" e.g. spec:replicas.
// - Values are a list of identifier paths and their values that can be found by
// search queries. The path is delimited by ":" and the value follows the "="
// symbol e.g. spec:replicas=4.
// - FilePath is the path of the file.
// - RepositoryURL is the URL of the source repository.
// - CreationTime is the time at which the file was created.
//
// Representing each Identifier and Value as a flat string representation
// facilitates the use of complex text search features from elasticsearch such
// as fuzzy searching, regex, wildcards, etc.
type KustomizationDocument struct {
Document
Kinds []string `json:"kinds,omitempty"`
Identifiers []string `json:"identifiers,omitempty"`
Values []string `json:"values,omitempty"`
}
type set map[string]struct{}
func (doc *KustomizationDocument) String() string {
return fmt.Sprintf("%s %s %s %v %v %v len(identifiers):%v len(values):%v",
doc.RepositoryURL, doc.FilePath, doc.DefaultBranch, doc.CreationTime,
doc.IsSame, doc.Kinds, len(doc.Identifiers), len(doc.Values))
}
// Implements the CrawlerDocument interface.
func (doc *KustomizationDocument) GetResources() ([]*Document, error) {
isResource := true
for _, suffix := range konfig.RecognizedKustomizationFileNames() {
if strings.HasSuffix(doc.FilePath, "/"+suffix) {
isResource = false
}
}
if isResource {
return []*Document{}, nil
}
content := []byte(doc.DocumentData)
content, err := FixKustomizationPreUnmarshallingNonFatal(content)
if err != nil {
return nil, fmt.Errorf("could not fix kustomize file: %v", err)
}
var k types.Kustomization
err = yaml.Unmarshal(content, &k)
if err != nil {
return nil, fmt.Errorf(
"could not parse kustomization: %v", err)
}
k.FixKustomizationPostUnmarshalling()
res := make([]*Document, 0, len(k.Resources))
for _, r := range k.Resources {
next, err := doc.Document.FromRelativePath(r)
if err != nil {
fmt.Printf("GetResources error: %v\n", err)
continue
}
res = append(res, &next)
}
return res, nil
}
func (doc *KustomizationDocument) readBytes() ([]map[string]interface{}, error) {
data := []byte(doc.DocumentData)
for _, suffix := range konfig.RecognizedKustomizationFileNames() {
if !strings.HasSuffix(doc.FilePath, "/"+suffix) {
continue
}
var config map[string]interface{}
err := yaml.Unmarshal(data, &config)
if err != nil {
return nil, fmt.Errorf(
"unable to parse kustomization: %v", err)
}
return []map[string]interface{}{config}, nil
}
configs := make([]map[string]interface{}, 0)
ks, err := fileReader.SliceFromBytes(data)
if err != nil {
return nil, fmt.Errorf("unable to parse resource: %v", err)
}
for _, k := range ks {
configs = append(configs, k.Map())
}
return configs, nil
}
func (doc *KustomizationDocument) ParseYAML() error {
doc.Identifiers = make([]string, 0)
doc.Values = make([]string, 0)
doc.Kinds = make([]string, 0, 1)
identifierSet := make(set)
valueSet := make(set)
kindSet := make(set)
getKind := func(m map[string]interface{}) string {
const defaultStr = "Kustomization"
kind, ok := m["kind"]
if !ok {
return defaultStr
}
if str, ok := kind.(string); ok && str != "" {
return str
}
return defaultStr
}
ks, err := doc.readBytes()
if err != nil {
return err
}
for _, contents := range ks {
kindSet[getKind(contents)] = struct{}{}
createFlatStructure(identifierSet, valueSet, contents)
}
for val := range kindSet {
doc.Kinds = append(doc.Kinds, val)
}
for val := range valueSet {
doc.Values = append(doc.Values, val)
}
for key := range identifierSet {
doc.Identifiers = append(doc.Identifiers, key)
}
return nil
}
func createFlatStructure(identifierSet set, valueSet set, contents map[string]interface{}) {
type Map struct {
data map[string]interface{}
prefix string
}
toVisit := []Map{
{
data: contents,
prefix: "",
},
}
for i := 0; i < len(toVisit); i++ {
visiting := toVisit[i]
for k, v := range visiting.data {
identifier := fmt.Sprintf("%s:%s", visiting.prefix, k)
// noop after the first iteration.
identifier = strings.TrimLeft(identifier, ":")
// Recursive function traverses structure to find
// identifiers and values. These later get formatted
// into doc.Identifiers and doc.Values respectively.
var traverseStructure func(interface{})
traverseStructure = func(arg interface{}) {
switch value := arg.(type) {
case map[string]interface{}:
toVisit = append(toVisit, Map{
data: value,
prefix: identifier,
})
case []interface{}:
for _, val := range value {
traverseStructure(val)
}
case interface{}:
esc := fmt.Sprintf("%v", value)
valuePath := fmt.Sprintf("%s=%v",
identifier, esc)
valueSet[valuePath] = struct{}{}
}
}
traverseStructure(v)
identifierSet[identifier] = struct{}{}
}
}
}

View File

@@ -0,0 +1,286 @@
package doc
import (
"reflect"
"sort"
"strings"
"testing"
)
func TestParseYAML(t *testing.T) {
testCases := []struct {
identifiers []string
values []string
kinds []string
filepath string
yaml string
}{
{
identifiers: []string{
"namePrefix",
"metadata",
"metadata:name",
"kind",
},
values: []string{
"kind=",
"namePrefix=dev-",
"metadata:name=app",
},
kinds: []string{
"Kustomization",
},
filepath: "some/path/to/kustomization.yaml",
yaml: `
namePrefix: dev-
metadata:
name: app
kind: ""
`,
},
{
identifiers: []string{
"namePrefix",
"metadata",
"metadata:name",
"metadata:spec",
"metadata:spec:replicas",
"kind",
"replicas",
"replicas:name",
"replicas:count",
"resource",
},
values: []string{
"namePrefix=dev-",
"metadata:name=n1",
"metadata:spec:replicas=3",
"kind=Kustomization",
"replicas:name=n1",
"replicas:name=n2",
"replicas:count=3",
"resource=file1.yaml",
"resource=file2.yaml",
},
kinds: []string{
"Kustomization",
},
filepath: "./kustomization.yaml",
yaml: `
namePrefix: dev-
# map of map
metadata:
name: n1
spec:
replicas: 3
kind: Kustomization
#list of map
replicas:
- name: n1
count: 3
- name: n2
count: 3
# list
resource:
- file1.yaml
- file2.yaml
`,
},
{
identifiers: []string{
"kind",
"metadata",
"metadata:name",
},
values: []string{
"kind=Deployment",
"kind=Service",
"kind=Custom",
"metadata:name=app",
"metadata:name=app-service",
"metadata:name=app-crd",
},
kinds: []string{
"Deployment",
"Service",
"Custom",
},
filepath: "resources.yaml",
yaml: `
---
kind: Deployment
metadata:
name: app
---
kind: Service
metadata:
name: app-service
---
kind: Custom
metadata:
name: app-crd
`,
},
{
identifiers: []string{
"kind",
"metadata",
"metadata:name",
},
values: []string{
"kind=Deployment",
"kind=Service",
"metadata:name=app1",
"metadata:name=app2",
},
kinds: []string{
"Deployment",
"Service",
},
filepath: "resources.yaml",
yaml: `
---
kind: Deployment
metadata:
name: app1
---
kind: Deployment
metadata:
name: app2
---
kind: Service
metadata:
name: app1
`,
},
}
for _, test := range testCases {
doc := KustomizationDocument{
Document: Document{
DocumentData: test.yaml,
FilePath: test.filepath,
},
}
err := doc.ParseYAML()
if err != nil {
t.Errorf("Document error error: %s", err)
}
cmpStrings := func(got, expected []string, label string) {
sort.Strings(got)
sort.Strings(expected)
if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected %s (%v) to be equal to (%v)\n",
label,
strings.Join(got, ","),
strings.Join(expected, ","))
}
}
cmpStrings(doc.Identifiers, test.identifiers, "identifiers")
cmpStrings(doc.Values, test.values, "values")
cmpStrings(doc.Kinds, test.kinds, "kinds")
}
}
func TestGetResources(t *testing.T) {
tests := []struct {
doc KustomizationDocument
resources []*Document
}{
{
doc: KustomizationDocument{
Document: Document{
RepositoryURL: "sigs.k8s.io/kustomize",
FilePath: "some/path/to/kdir/kustomization.yaml",
DocumentData: `
bases:
- ../base
- ../otherbase
resources:
- file.yaml
- https://github.com/kubernetes-sigs/kustomize/examples/helloWorld?ref=v3.1.0
`},
},
resources: []*Document{
{
RepositoryURL: "sigs.k8s.io/kustomize",
FilePath: "some/path/to/base",
},
{
RepositoryURL: "sigs.k8s.io/kustomize",
FilePath: "some/path/to/otherbase",
},
{
RepositoryURL: "sigs.k8s.io/kustomize",
FilePath: "some/path/to/kdir/file.yaml",
},
{
RepositoryURL: "https://github.com/kubernetes-sigs/kustomize",
FilePath: "examples/helloWorld",
DefaultBranch: "v3.1.0",
},
},
},
{
doc: KustomizationDocument{
Document: Document{
RepositoryURL: "https://github.com/some/repo",
FilePath: "some/resource.yaml",
DocumentData: `
bases:
- ../base
- ../overlay
resources:
- https://github.com/kubernetes-sigs/kustomize/examples/helloWorld?ref=v3.1.0
- some/file.yaml
`,
},
},
resources: []*Document{},
},
}
for _, test := range tests {
res, err := test.doc.GetResources()
if err != nil {
t.Errorf("Unexpected error: %v\n", err)
continue
}
if len(test.resources) != len(res) {
t.Errorf("Number of resources does not match.")
continue
}
cmp := func(docs []*Document) func(i, j int) bool {
return func(i, j int) bool {
if docs[i].RepositoryURL != docs[j].RepositoryURL {
return docs[i].RepositoryURL <
docs[j].RepositoryURL
}
if docs[i].FilePath != docs[j].FilePath {
return docs[i].FilePath <
docs[j].FilePath
}
return docs[i].DefaultBranch < docs[j].DefaultBranch
}
}
sort.Slice(test.resources, cmp(test.resources))
sort.Slice(res, cmp(res))
for i, r := range test.resources {
if !reflect.DeepEqual(res[i], r) {
t.Errorf("Expected '%+v' to equal '%+v'\n",
res[i], r)
}
}
}
}

View File

@@ -0,0 +1,88 @@
package doc
import (
"crypto/sha256"
"fmt"
"path"
"strings"
"time"
"sigs.k8s.io/kustomize/api/internal/git"
)
type Document struct {
RepositoryURL string `json:"repositoryUrl,omitempty"`
FilePath string `json:"filePath,omitempty"`
DefaultBranch string `json:"defaultBranch,omitempty"`
DocumentData string `json:"document,omitempty"`
CreationTime *time.Time `json:"creationTime,omitempty"`
IsSame bool `json:"-"`
}
// Implements the CrawlerDocument interface.
func (doc *Document) GetDocument() *Document {
return doc
}
func (doc *Document) Copy() *Document {
return &Document{
RepositoryURL: doc.RepositoryURL,
FilePath: doc.FilePath,
DefaultBranch: doc.DefaultBranch,
DocumentData: doc.DocumentData,
CreationTime: doc.CreationTime,
IsSame: doc.IsSame,
}
}
// Implements the CrawlerDocument interface.
func (doc *Document) WasCached() bool {
return doc.IsSame
}
func (doc *Document) FromRelativePath(newFile string) (Document, error) {
repoSpec, err := git.NewRepoSpecFromUrl(newFile)
if err == nil {
return Document{
RepositoryURL: repoSpec.Host + path.Clean(repoSpec.OrgRepo),
FilePath: path.Clean(repoSpec.Path),
DefaultBranch: repoSpec.Ref,
}, nil
}
// else document is probably relative path.
ret := Document{
RepositoryURL: doc.RepositoryURL,
DefaultBranch: doc.DefaultBranch,
}
ogDir, _ := path.Split(doc.FilePath)
cleaned := path.Clean(newFile)
if !path.IsAbs(cleaned) {
cleaned = path.Clean(ogDir + "/" + cleaned)
}
ret.FilePath = cleaned
return ret, nil
}
func (doc *Document) ID() string {
sum := sha256.Sum256([]byte(strings.Join(
[]string{
doc.RepositoryURL,
doc.DefaultBranch,
doc.FilePath,
},
"---|---")))
return fmt.Sprintf("%x", sum)
}
func (doc *Document) RepositoryFullName() string {
doc.RepositoryURL = strings.TrimRight(doc.RepositoryURL, "/")
sections := strings.Split(doc.RepositoryURL, "/")
l := len(sections)
if l < 2 {
return doc.RepositoryURL
}
return path.Join(sections[l-2], sections[l-1])
}

View File

@@ -0,0 +1,105 @@
package doc
import (
"reflect"
"testing"
)
func TestFromRelativePath(t *testing.T) {
type Case struct {
RelativePath string
Expected Document
}
testCases := []struct {
BaseDoc Document
Cases []Case
}{
{
BaseDoc: Document{
RepositoryURL: "example.com/repo",
FilePath: "path/to/file/kustomization.yaml",
DefaultBranch: "master",
},
Cases: []Case{
{
RelativePath: "../other/file/resource.yaml",
Expected: Document{
RepositoryURL: "example.com/repo",
FilePath: "path/to/other/file/resource.yaml",
DefaultBranch: "master",
},
},
{
RelativePath: "../file/../../something/../to/other/file/patch.yaml",
Expected: Document{
RepositoryURL: "example.com/repo",
FilePath: "path/to/other/file/patch.yaml",
DefaultBranch: "master",
},
},
{
RelativePath: "service.yaml",
Expected: Document{
RepositoryURL: "example.com/repo",
FilePath: "path/to/file/service.yaml",
DefaultBranch: "master",
},
},
},
},
}
for _, tc := range testCases {
for _, c := range tc.Cases {
rd, err := tc.BaseDoc.FromRelativePath(c.RelativePath)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(rd, c.Expected) {
t.Errorf("document mismatch expected %v, got %v", c.Expected, rd)
}
}
}
}
func TestDocument_RepositoryFullName(t *testing.T) {
testCases := []struct {
doc Document
expectedRepositoryFullName string
}{
{
doc: Document{
RepositoryURL: "https://github.com/user/repo",
},
expectedRepositoryFullName: "user/repo",
},
{
doc: Document{
RepositoryURL: "https://github.com//user/repo////",
},
expectedRepositoryFullName: "user/repo",
},
{
doc: Document{
RepositoryURL: "repo/",
},
expectedRepositoryFullName: "repo",
},
{
doc: Document{
RepositoryURL: "",
},
expectedRepositoryFullName: "",
},
}
for _, tc := range testCases {
returnedRepositoryFullName := tc.doc.RepositoryFullName()
if returnedRepositoryFullName != tc.expectedRepositoryFullName {
t.Errorf("RepositoryFullName expected %s, got %s",
tc.expectedRepositoryFullName,
returnedRepositoryFullName)
}
}
}

View File

@@ -0,0 +1,51 @@
package doc
import (
"fmt"
"regexp"
"sigs.k8s.io/yaml"
)
func FixKustomizationPreUnmarshallingNonFatal(data []byte) ([]byte, error) {
deprecateFieldsMap := map[string]string{
"imageTags:": "images:",
}
for oldname, newname := range deprecateFieldsMap {
pattern := regexp.MustCompile(oldname)
data = pattern.ReplaceAll(data, []byte(newname))
}
found, err := useLegacyPatch(data)
if err == nil && found {
pattern := regexp.MustCompile("patches:")
data = pattern.ReplaceAll(data, []byte("patchesStrategicMerge:"))
}
return data, err
}
func useLegacyPatch(data []byte) (bool, error) {
found := false
var object map[string]interface{}
err := yaml.Unmarshal(data, &object)
if err != nil {
return false, fmt.Errorf("invalid content from %s",
string(data))
}
if rawPatches, ok := object["patches"]; ok {
patches, ok := rawPatches.([]interface{})
if !ok {
return false, fmt.Errorf("invalid patches from %v",
rawPatches)
}
for _, p := range patches {
_, ok := p.(string)
if ok {
found = true
}
}
}
return found, nil
}

13
api/internal/crawl/go.mod Normal file
View File

@@ -0,0 +1,13 @@
module sigs.k8s.io/kustomize/api/internal/crawl
go 1.13
require (
github.com/elastic/go-elasticsearch/v6 v6.8.2
github.com/gomodule/redigo v2.0.0+incompatible
github.com/gorilla/mux v1.7.3
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
github.com/rs/cors v1.7.0
sigs.k8s.io/kustomize/api v0.2.0
sigs.k8s.io/yaml v1.1.0
)

393
api/internal/crawl/go.sum Normal file
View File

@@ -0,0 +1,393 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elastic/go-elasticsearch/v6 v6.8.2 h1:rp5DGrd63V5c6nHLjF6QEXUpZSvs0+QM3ld7m9VhV2g=
github.com/elastic/go-elasticsearch/v6 v6.8.2/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
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/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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
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 v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.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.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
github.com/golangci/golangci-lint v1.19.1/go.mod h1:2CEc4Fxx3vxDv7g8DyXkHCBF73AOzAymcJAprs2vCps=
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
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/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v0.0.0-20160422134519-667fe4e3466a/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v0.0.0-20160922145804-ca9ada445741/go.mod h1:+WVp8kdw6VhyKExm03PAMRn2ZxnPtm58pV0dBVPdhHE=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
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/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
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/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matoous/godox v0.0.0-20190910121045-032ad8106c86/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/monopole/mdrip v1.0.0/go.mod h1:N1/ppRG9CaPeUKAUHZ3dUlfOT81lTpKZLkyhCvTETwM=
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
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.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/securego/gosec v0.0.0-20190912120752-140048b2a218/go.mod h1:q6oYAujd2qyeU4cJqIri4LBIgdHXGvxWHZ1E29HNFRE=
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.3/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc=
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5 h1:SW/0nsKCUaozCUtZTakri5laocGx/5bkDSSLrFUsa5s=
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911230505-6bfd74cf029c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
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.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
sigs.k8s.io/kustomize/api v0.2.0 h1:e++6JpysnnlUbHmFrv6jvfF5rFlgQ103bS1DO7r5bWA=
sigs.k8s.io/kustomize/api v0.2.0/go.mod h1:zVtMg179jW1gr74jo9fc2Ac9dLYLTZZThc3DDb9lDW4=
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0 h1:otg4dLFc03c3gzl+2CV8GPGcd1kk8wjXwD+UhhcCn5I=
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0/go.mod h1:bl/gVJgYYhJZCZdYU2BfnaKYAlqFkgbJEkpl302jEss=
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=
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=

View File

@@ -0,0 +1,23 @@
package httpclient
import (
"net/http"
"time"
"github.com/gomodule/redigo/redis"
"github.com/gregjones/httpcache"
rediscache "github.com/gregjones/httpcache/redis"
)
func FromCache(header http.Header) bool {
return header.Get(httpcache.XFromCache) != ""
}
func NewClient(conn redis.Conn) *http.Client {
etagCache := rediscache.NewWithClient(conn)
tr := httpcache.NewTransport(etagCache)
return &http.Client{
Transport: tr,
Timeout: 10 * time.Second,
}
}

View File

@@ -0,0 +1,298 @@
package index
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"time"
es "github.com/elastic/go-elasticsearch/v6"
"github.com/elastic/go-elasticsearch/v6/esapi"
)
const IndexConfig = `
{
"mappings": {
"_doc": {
"properties": {
"repositoryUrl": {
"type": "keyword"
},
"filePath": {
"type": "keyword"
},
"defaultBranch": {
"type": "keyword"
},
"document": {
"type": "text"
},
"creationTime": {
"type": "date"
},
"kinds": {
"type": "text"
},
"identifiers": {
"type": "text"
},
"values": {
"type": "text"
}
}
}
}
}`
// TODO(damienr74) Split index into reader and writer?
type index struct {
ctx context.Context
client *es.Client
name string
}
func newIndex(ctx context.Context, name string) (*index, error) {
client, err := es.NewDefaultClient()
if err != nil {
return nil, err
}
return &index{
ctx: ctx,
client: client,
name: name,
}, nil
}
type readerFunc func(io.Reader) error
func ignoreResponseBody(_ io.Reader) error {
return nil
}
// checks that elastic returned successfully. If it has not, it will read the
// body and return it in an error message.
//
// Otherwise, it will use the readerFunc to read the body. This function is a
// mechanism for getting relevant data from the response only if it was successful.
func (idx *index) responseErrorOrNil(info string, res *esapi.Response,
err error, reader readerFunc) error {
messageStart := fmt.Sprintf("index %s error: %s", idx.name, info)
if err != nil || res == nil {
return fmt.Errorf("%s: %v", messageStart, err)
}
defer res.Body.Close()
if res.IsError() {
return fmt.Errorf("%s: %s", messageStart, res.String())
}
if reader != nil {
err = reader(res.Body)
if err != nil {
return fmt.Errorf("%s: %v", messageStart, err)
}
}
return nil
}
func byteJoin(bts ...interface{}) []byte {
ret := make([][]byte, len(bts))
for i, v := range bts {
switch bt := v.(type) {
case []byte:
ret[i] = bt
case string:
ret[i] = []byte(bt)
default:
ret[i] = []byte(fmt.Sprintf("%v", bt))
}
}
return bytes.Join(ret, []byte(` `))
}
// Update the elasticsearch index mappings. (describes how to index/search for the documents).
func (idx *index) UpdateMapping(mappings []byte) error {
request := byteJoin(`{ "mappings":`, mappings, `}`)
op := idx.client.Indices.PutMapping
res, err := op(
bytes.NewReader(request),
op.WithContext(idx.ctx),
op.WithIndex(idx.name),
op.WithIncludeTypeName(true),
op.WithPretty(),
)
return idx.responseErrorOrNil(
fmt.Sprintf("could not update index mappings '%s'", request),
res, err, ignoreResponseBody)
}
// Update the elasticsearch index settings. (describes default parameters and
// some analyzer definitions, etc.)
func (idx *index) UpdateSetting(settings []byte) error {
request := byteJoin(`{ "settings": `, settings, `}`)
op := idx.client.Indices.PutSettings
res, err := op(
bytes.NewReader(request),
op.WithContext(idx.ctx),
op.WithIndex(idx.name),
op.WithPretty(),
)
return idx.responseErrorOrNil(
fmt.Sprintf("could not update index settings '%s'", request),
res, err, ignoreResponseBody)
}
// Create an index providing the config for both the mappings and the settings.
func (idx *index) CreateIndex(config []byte) error {
op := idx.client.Indices.Create
res, err := op(
idx.name,
op.WithBody(bytes.NewReader(config)),
op.WithContext(idx.ctx),
op.WithHuman(),
op.WithPretty(),
)
return idx.responseErrorOrNil(
fmt.Sprintf("could not create index with config '%s'", config),
res, err, ignoreResponseBody)
}
// Delete an index.
func (idx *index) DeleteIndex() error {
res, err := idx.client.Indices.Delete(
[]string{idx.name},
)
return idx.responseErrorOrNil("could not delete index",
res, err, ignoreResponseBody)
}
// Insert or update the document by ID.
func (idx *index) Put(uniqueID string, doc interface{}) (string, error) {
body, err := json.Marshal(doc)
if err != nil {
return "", err
}
req := esapi.IndexRequest{
Index: idx.name,
Body: bytes.NewReader(body),
DocumentID: uniqueID,
}
res, err := req.Do(idx.ctx, idx.client)
var id string
readId := func(reader io.Reader) error {
type InsertResult struct {
ID string `json:"_id,omitempty"`
}
var ir InsertResult
data, err := ioutil.ReadAll(reader)
if err != nil {
return err
}
err = json.Unmarshal(data, &ir)
if err != nil {
return err
}
id = ir.ID
return nil
}
// populates the id field.
err = idx.responseErrorOrNil("could not insert document",
res, err, readId)
return id, err
}
type scrollUpdater func(string, readerFunc) error
// Update the scroll for iteration. If no scroll exists, create one.
func (idx *index) scrollUpdater(query []byte, batchSize int,
timeout time.Duration) scrollUpdater {
return func(scrollID string, reader readerFunc) error {
var res *esapi.Response
var err error
if scrollID == "" {
search := idx.client.Search
res, err = search(
search.WithContext(idx.ctx),
search.WithIndex(idx.name),
search.WithBody(bytes.NewBuffer(query)),
search.WithScroll(timeout),
search.WithSize(batchSize),
)
} else {
scroll := idx.client.Scroll
res, err = scroll(
scroll.WithContext(idx.ctx),
scroll.WithScroll(timeout),
scroll.WithScrollID(scrollID),
)
}
return idx.responseErrorOrNil(
fmt.Sprintf("could not scroll for query %s", query),
res, err, reader)
}
}
// Simple search options. Size is the number of elements to return, From is the
// rank of the results according to the query. Used as a simple (stateless)
// pagination technique.
type SearchOptions struct {
Size int
From int
}
// Search for a query (json query dsl) with some options, and use the reader func
// to extract the response.
func (idx *index) Search(query []byte, opts SearchOptions,
responseReader readerFunc) error {
op := idx.client.Search
res, err := op(
op.WithContext(idx.ctx),
op.WithIndex(idx.name),
op.WithBody(bytes.NewBuffer(query)),
op.WithTrackTotalHits(true),
op.WithSize(opts.Size),
op.WithFrom(opts.From),
op.WithPretty(),
)
return idx.responseErrorOrNil(
fmt.Sprintf("could not complete search query %v", query),
res, err, responseReader)
}
// Delete an element from elasticsearch by Id.
func (idx *index) Delete(id string) error {
op := idx.client.Delete
res, err := op(
idx.name,
id,
op.WithContext(idx.ctx),
op.WithPretty(),
)
return idx.responseErrorOrNil(
fmt.Sprintf("could not delete id(%s) from index(%s)", id, idx.name),
res, err, ignoreResponseBody)
}

View File

@@ -0,0 +1,355 @@
package index
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"strings"
"time"
"sigs.k8s.io/kustomize/api/internal/crawl/doc"
)
const (
AggregationKeyword = "aggs"
)
// Redefinition of Hits structure. Must match the json string of
// KustomizeResult.Hits.Hits. Declared as a convenience for iteration.
type KustomizeHits []struct {
ID string `json:"id"`
Document doc.KustomizationDocument `json:"result"`
}
type KustomizeResult struct {
ScrollID *string `json:"-"`
Hits *struct {
Total int `json:"total"`
Hits []struct {
ID string `json:"id"`
Document doc.KustomizationDocument `json:"result"`
} `json:"hits"`
} `json:"hits,omitempty"`
Aggregations *struct {
Timeseries *struct {
Buckets []struct {
Key string `json:"key"`
Count int `json:"count"`
} `json:"buckets"`
} `json:"timeseries,omitempty"`
Kinds *struct {
OtherCount int `json:"otherResults"`
Buckets []struct {
Key string `json:"key"`
Count int `json:"count"`
} `json:"buckets"`
} `json:"kinds,omitempty"`
} `json:"aggregations,omitempty"`
}
// Elasticsearch has some sometimes inconsistent labels, and some pretty ugly label choices.
// However, the structure seems reasonable, so I wanted to use it if possible. This method
// needs two copies of the types to make the json strings different. The Copies must be the
// exact same type/structure, so the types must be declared inline. Go will check that these
// are convertible at compile time, and converting at runtime is a noop.
type ElasticKustomizeResult struct {
ScrollID *string `json:"_scroll_id,omitempty"`
Hits *struct {
Total int `json:"total"`
Hits []struct {
ID string `json:"_id"`
Document doc.KustomizationDocument `json:"_source"`
} `json:"hits"`
} `json:"hits,omitempty"`
Aggregations *struct {
Timeseries *struct {
Buckets []struct {
Key string `json:"key_as_string"`
Count int `json:"doc_count"`
}
} `json:"timeseries,omitempty"`
Kinds *struct {
OtherCount int `json:"sum_other_doc_count"`
Buckets []struct {
Key string `json:"key"`
Count int `json:"doc_count"`
}
} `json:"kinds,omitempty"`
} `json:"aggregations,omitempty"`
}
type KustomizeIndex struct {
*index
}
// Create index reference to the index containing the kustomize documents.
func NewKustomizeIndex(ctx context.Context) (*KustomizeIndex, error) {
idx, err := newIndex(ctx, "kustomize")
if err != nil {
return nil, err
}
indicesExistsOp := idx.client.Indices.Exists
resp, err := indicesExistsOp([]string{"kustomize"},
indicesExistsOp.WithContext(idx.ctx),
indicesExistsOp.WithPretty())
if err != nil {
return nil, err
}
if resp.StatusCode == 200 {
fmt.Printf("The kustomize index already exists\n")
} else {
fmt.Printf("Creating the kustomize index\n")
if err := idx.CreateIndex([]byte(IndexConfig)); err != nil {
return nil, err
}
}
return &KustomizeIndex{idx}, nil
}
// Return a timeseries of kustomization file counts.
func TimeseriesAggregation() (string, map[string]interface{}) {
return "timeseries", map[string]interface{}{
"date_histogram": map[string]interface{}{
"field": "creationTime",
"interval": "day",
/// XXX Only return values with counts, otherwise
// every day is added to the output...
// This matters if ever a zero valued time would
// be stored in the creationTime field... it would
// return >600k entries (for every day since year 0).
// IDK why this is default, but I would not want this
// to happen...
"min_doc_count": 1,
},
}
}
// Return aggregation of results based off of their kinds.
func KindAggregation(maxBuckets int) (string, map[string]interface{}) {
if maxBuckets < 1 {
maxBuckets = 1
}
return "kinds", map[string]interface{}{
"terms": map[string]interface{}{
"field": "kinds.keyword",
"size": maxBuckets,
},
}
}
// The multi_match search type in elasticsearch will check each field according
// to their respective analyzers for the identifier.
func multiMatch(query string) map[string]interface{} {
return map[string]interface{}{
"multi_match": map[string]interface{}{
"type": "cross_fields",
"fields": []string{
"values.keyword^3",
"identifiers.keyword^3",
"values.ngram",
"identifiers.ngram",
// TODO(damienr74) remove document with default
// analyzer. It does not handle special (=,: etc)
// characters properly, and matches with false
// positives. document.whitespace does not exist
// yet, but should use the whitespace analyzer.
"document",
"document.whitespace",
},
"query": query,
},
}
}
// Build an elasticsearch query from a user query.
func BuildQuery(query string) map[string]interface{} {
queryTokens := strings.Fields(query)
if len(queryTokens) == 0 {
return map[string]interface{}{
"size": 0,
}
}
mustMatch := make([]map[string]interface{}, len(queryTokens))
for i, tok := range queryTokens {
if strings.HasPrefix(strings.ToLower(tok), "kind=") {
mustMatch[i] = map[string]interface{}{
"term": map[string]interface{}{
"kinds.keyword": tok[5:],
},
}
continue
}
mustMatch[i] = multiMatch(tok)
}
structuredQuery := map[string]interface{}{
"query": map[string]interface{}{
"bool": map[string]interface{}{
"must": mustMatch,
},
},
}
return structuredQuery
}
// Iterator based off of the way bufio.Scanner works.
//
// Example:
// for it.Next() {
// for _, doc := range it.Value().Hits {
// // Handle KustomizationDocument.
// }
// }
//
// if err := it.Err(); err != nil {
// // Handle err.
// }
type KustomizeIterator struct {
update scrollUpdater
err error
// Matches the return definition of elasticsearch search results. The
// scroll ID is practically a database cursor.
scrollImpl KustomizeResult
}
// Get the next batch of results. Note that this returns multiple results that
// can be iterated.
func (it *KustomizeIterator) Next() bool {
reader := func(reader io.Reader) error {
data, err := ioutil.ReadAll(reader)
if err != nil {
return fmt.Errorf("could not read from body: %v", err)
}
var scrollInput ElasticKustomizeResult
err = json.Unmarshal(data, &scrollInput)
if err != nil {
return fmt.Errorf("cloud not marshal %s into %T: %v",
data, scrollInput, err)
}
it.scrollImpl = KustomizeResult(scrollInput)
return nil
}
if it.err == nil {
fmt.Printf("updating scroll: %s\n", *it.scrollImpl.ScrollID)
it.err = it.update(*it.scrollImpl.ScrollID, reader)
}
// if there is no error and the array is not empty, then Value is
// obligated to return a valid result.
return it.err == nil &&
it.scrollImpl.Hits != nil &&
len(it.scrollImpl.Hits.Hits) > 0
}
// Get the value from this batch of iterations.
func (it *KustomizeIterator) Value() KustomizeResult {
return it.scrollImpl
}
// Check if any errors have occured.
func (it *KustomizeIterator) Err() error {
return it.err
}
// Create an iterator over query. Iterate in chunks of batchSize, each batch
// should take no longer than timeout to read (otherwise, elasticsearch will
// delete the context).
//
// XXX Important to set a reasonable amount of time to read the documents. If
// a lot of processing must be done, consider loading everything in memory
// before doing it so that, a short timeout period can be set. Scrolling creates
// a consistent DB context, so this can be costly.
//
// Scrolling is also not meant to be used for real time purposes. If you need
// results quickly, consider using the From: field in SearchOptions and a normal
// search. This will not guarantee that the values will not change but is more
// suitable for lower latencies/long execution timeouts.
func (ki *KustomizeIndex) IterateQuery(query []byte, batchSize int,
timeout time.Duration) *KustomizeIterator {
emptyScroll := ""
return &KustomizeIterator{
update: ki.scrollUpdater(query, batchSize, timeout),
scrollImpl: KustomizeResult{
ScrollID: &emptyScroll,
},
}
}
// type specific Put for inserting structured kustomization documents.
func (ki *KustomizeIndex) Put(id string, doc *doc.KustomizationDocument) (string, error) {
id, err := ki.index.Put(id, doc)
if err != nil {
return id, fmt.Errorf("could not insert in elastic: %v", err)
}
return id, nil
}
// Kustomize search options: What metrics should be returned? Kind Aggregation,
// TimeseriesAggregation, etc. Also embedds the SearchOptions field to specify
// the position in the sorted list of results and the number of results to return.
type KustomizeSearchOptions struct {
SearchOptions
KindAggregation bool
TimeseriesAggregation bool
}
// Search the index with the given query string. Returns a structured result and possible
// aggregates.
func (ki *KustomizeIndex) Search(query string,
opts KustomizeSearchOptions) (*KustomizeResult, error) {
aggMap := make(map[string]interface{})
if opts.KindAggregation {
k, kAgg := KindAggregation(15)
aggMap[k] = kAgg
}
if opts.TimeseriesAggregation {
t, tAgg := TimeseriesAggregation()
aggMap[t] = tAgg
}
esQuery := BuildQuery(query)
if len(aggMap) > 0 {
esQuery[AggregationKeyword] = aggMap
}
data, err := json.Marshal(&esQuery)
if err != nil {
return nil, fmt.Errorf("failed to format query %s", query)
}
fmt.Printf("formated query: %s\n", data)
var kr ElasticKustomizeResult
err = ki.index.Search(data, opts.SearchOptions, func(results io.Reader) error {
data, err = ioutil.ReadAll(results)
if err != nil {
return fmt.Errorf("could not read results from search: %v", err)
}
if err = json.Unmarshal(data, &kr); err != nil {
return fmt.Errorf("could not parse results from search: %v", err)
}
return nil
})
res := KustomizeResult(kr)
return &res, err
}

View File

@@ -0,0 +1,72 @@
package index
import (
"reflect"
"testing"
)
func TestBuildQuery(t *testing.T) {
testCases := []struct {
query string
result map[string]interface{}
}{
{
query: " \t\n\r",
result: map[string]interface{}{"size": 0},
},
{
query: "\tidentifier1 identifier2\nidentifier3\r",
result: map[string]interface{}{
"query": map[string]interface{}{
"bool": map[string]interface{}{
"must": []map[string]interface{}{
multiMatch("identifier1"),
multiMatch("identifier2"),
multiMatch("identifier3"),
},
},
},
},
},
{
query: "kind=Kustomization",
result: map[string]interface{}{
"query": map[string]interface{}{
"bool": map[string]interface{}{
"must": []map[string]interface{}{
{
"term": map[string]interface{}{
"kinds.keyword": "Kustomization",
},
},
},
},
},
},
},
{
query: "kind=Kustomization identifier2",
result: map[string]interface{}{
"query": map[string]interface{}{
"bool": map[string]interface{}{
"must": []map[string]interface{}{
{
"term": map[string]interface{}{
"kinds.keyword": "Kustomization",
},
},
multiMatch("identifier2"),
},
},
},
},
},
}
for _, tc := range testCases {
result := BuildQuery(tc.query)
if !reflect.DeepEqual(tc.result, result) {
t.Errorf("Expected %#v to match %#v", result, tc.result)
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,2 @@
node_modules
dist

View File

@@ -0,0 +1,2 @@
node_modules
dist

2
api/internal/crawl/ui/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules
dist

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