Compare commits

..

129 Commits

Author SHA1 Message Date
Phillip Wittrock
e465ee9e9a update go.mod for release 2020-05-07 13:51:47 -07:00
Phillip Wittrock
1008933b21 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-05-07 13:51:33 -07:00
Kubernetes Prow Robot
3dfc76b769 Merge pull request #2436 from pwittrock/exec
Refactor function runtimes into a single package
2020-05-07 13:33:42 -07:00
Phillip Wittrock
dab0f3cf22 Add documentation and simplify framework 2020-05-07 12:51:02 -07:00
Kubernetes Prow Robot
8d5fc2f281 Merge pull request #2397 from sunny0826/docs
Update zh docs
2020-05-07 10:43:09 -07:00
Kubernetes Prow Robot
9b26390d92 Merge pull request #2433 from webwurst/patch-2
Fix typo
2020-05-06 13:33:08 -07:00
Kubernetes Prow Robot
099b6ff460 Merge pull request #2441 from phanimarupaka/DoNotModifyCM
Do not modify CM and Secrets Generator
2020-05-06 12:21:09 -07:00
Phani Teja Marupaka
7d600c1c5a Do not modify CM and Secrets Generator 2020-05-06 09:38:33 -07:00
Phillip Wittrock
259624ac07 run e2e tests in GitHub Actions CI 2020-05-06 08:59:54 -07:00
Phillip Wittrock
d16d1f81b0 add starlark e2e tests 2020-05-06 08:59:54 -07:00
Phillip Wittrock
b3ff51b9bc Add warnings for exec functions 2020-05-06 08:59:54 -07:00
Phillip Wittrock
c9f50745ff e2e tests for functions 2020-05-06 08:59:54 -07:00
Phillip Wittrock
95f0a44fc0 Enable exec functions (Alpha) 2020-05-06 08:59:54 -07:00
Phillip Wittrock
174b2ed62e Refactor starlark runtime ontop of runtimeutil 2020-05-06 08:16:14 -07:00
Phillip Wittrock
594c48d19a Refactor container function runtime into its own package 2020-05-06 08:16:14 -07:00
Phillip Wittrock
096ad8c95c Exec function implementation 2020-05-06 08:16:14 -07:00
Phillip Wittrock
fd70213ca2 Create shared functions filter library 2020-05-06 08:16:14 -07:00
Jeff Regan
212ec66e91 Merge pull request #2440 from kubernetes-sigs/pwittrock-patch-1
Create go.yml
2020-05-06 07:58:45 -07:00
Phillip Wittrock
02235007e1 Create go.yml 2020-05-06 07:52:18 -07:00
Tobias Bradtke
2ce66c0a85 Fix typo 2020-05-03 01:23:46 +02:00
Kubernetes Prow Robot
f7909fad71 Merge pull request #2429 from pwittrock/framework
Better support for writing functions in go
2020-05-01 12:06:29 -07:00
jregan
4246f79874 update go.mod for release 2020-04-30 17:04:01 -07:00
jregan
a28dd97871 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-04-30 16:36:10 -07:00
Kubernetes Prow Robot
5e00158b00 Merge pull request #2430 from monopole/versionBump
kyaml 0.1.8, cmd/config 0.1.7
2020-04-30 16:06:06 -07:00
Jeffrey Regan
218880cdd9 kyaml 0.1.8, cmd/config 0.1.7 2020-04-30 15:51:49 -07:00
Jeff Regan
31e5ab1e6d Merge pull request #2428 from thephw/update-exampe-tests-link-in-docs
Update link to travis pre-commit hook in docs
2020-04-30 15:44:47 -07:00
Jeff Regan
a9e998d27d Merge pull request #2424 from monopole/fixErrorMessage
Improve config failure error message.
2020-04-30 15:41:04 -07:00
Phillip Wittrock
b579bf2b03 Better support for writing functions in go 2020-04-30 13:33:56 -07:00
Patrick Wiseman
cae8fd0013 Update link to travis pre-commit hook in docs 2020-04-30 12:03:47 -04:00
jregan
de9f80c41b Improve config failure error message. 2020-04-29 19:42:56 -07:00
Jeff Regan
085b64cde4 Merge pull request #2423 from justinsb/fix_mergefilter_docs
Fix godocs on MergeFilter
2020-04-29 15:36:29 -07:00
Jeff Regan
0abad97de3 Merge pull request #2419 from phanimarupaka/SettersNameForSubst
Simplify substitutions
2020-04-29 12:46:39 -07:00
Phani Teja Marupaka
a2f2cc6a85 Create setter with marker as name for subst 2020-04-29 12:20:38 -07:00
Justin SB
028811eb97 Fix godocs on MergeFilter
It was copy-pasted from GrepFilter
2020-04-29 14:13:15 -04:00
Jeff Regan
d67c03af12 Merge pull request #2421 from Shell32-Natsu/2392-codeClean
Remove inventory transformer
2020-04-29 11:03:54 -07:00
Jeff Regan
c95a89c1f1 Merge pull request #2422 from muenchdo/test/merge-env-from
add test to demonstrate kubernetes-sigs/kustomize#1835
2020-04-29 10:54:16 -07:00
Dominik Münch
499246a4a4 add test to demonstrate kubernetes-sigs/kustomize#1835 2020-04-29 09:27:23 +02:00
Jeff Regan
bd3d413ffd Merge pull request #2420 from ian-howell/replaceVars-panic
Fix a panic when doing variable replacement.
2020-04-28 11:25:42 -07:00
Donny Xia
9195f05bba Update document 2020-04-28 17:36:44 +00:00
Donny Xia
63f7495e88 Remove inventory transformer #2392 2020-04-28 17:36:44 +00:00
guoxudong
6994e41eb3 fix 2020-04-28 14:17:03 +08:00
Ian Howell
1d46edccb5 Fix a panic when doing variable replacement.
This fixes an issue where Kustomize expects a []string, but gets a slice
of something else instead, which causing a runtime panic.

This fixes 2241
2020-04-27 15:54:04 -05:00
Jeff Regan
c771f24626 Merge pull request #2389 from webwurst/patch-1
Fix formating of example
2020-04-27 10:44:33 -07:00
Kubernetes Prow Robot
3e6b5918a2 Merge pull request #2418 from monopole/annotationTransformer
Remove MapTransformer.
2020-04-25 14:59:28 -07:00
jregan
7feb873ef6 Remove MapTransformer. 2020-04-25 13:27:01 -07:00
Jeff Regan
3c72a93c21 Merge pull request #2417 from monopole/fixBuild
Disable flaky git diff checks.
2020-04-25 12:52:23 -07:00
jregan
f07cae82b0 Disable flakey git diff checks. 2020-04-25 12:37:10 -07:00
Jeff Regan
2b48c4b08d Merge pull request #2416 from aodinokov/finalize_helm_v3_support
Finalize helm v3 support
2020-04-25 12:34:01 -07:00
Alexey Odinokov
855b2c3171 Polishing helmV3 related changes
Enabled HelmV3 related test,
Made sure HelmV2 and HelmV3 UT pass,
Pinned Chart version as per TODO note
Removed accomplished TODOs
2020-04-25 04:58:14 +00:00
Jeff Regan
82f20da46e Merge pull request #2414 from monopole/migrateLabelTransformer
Migrate label transformer
2020-04-24 18:12:44 -07:00
jregan
72c7db39f2 Migrate the label transformer. 2020-04-24 17:39:52 -07:00
jregan
c852bb00f2 Special treatment for core apiVersions. 2020-04-24 15:40:44 -07:00
Jeff Regan
a5db82e3a0 Merge pull request #2411 from monopole/moarTests
More GVK tests before GV treatment changes.
2020-04-24 15:35:22 -07:00
jregan
0636368c8c More GVK tests before GV treatment changes. 2020-04-24 15:10:59 -07:00
Jeff Regan
3516bf1bb1 Merge pull request #2409 from monopole/apiVersionExtractions
Isolate apiVersion parsing.
2020-04-24 14:17:28 -07:00
jregan
d5ba8533fa Isolate apiVersion parsing. 2020-04-24 13:56:41 -07:00
Alexey Odinokov
40db077d3d updated test for helmV3 2020-04-24 05:51:23 +00:00
Alexey Odinokov
d4d95012c7 Added needed calls for HelmV3 2020-04-24 05:14:08 +00:00
Kubernetes Prow Robot
99b15004dd Merge pull request #2406 from monopole/upgradeTests
Bug hunting; add more tests high and low.
2020-04-23 17:14:07 -07:00
jregan
c777a3805d Bug hunting; add more tests high and low. 2020-04-23 16:52:12 -07:00
Kubernetes Prow Robot
52facc13ed Merge pull request #2404 from monopole/goVersion14
Adjust scripts to allow either Go v.13 or Go v1.14
2020-04-22 19:09:45 -07:00
jregan
53e7c87604 Adjust scripts to be go v1.14 compatible 2020-04-22 18:34:15 -07:00
Kubernetes Prow Robot
8618613325 Merge pull request #2402 from pwittrock/validation-spec
release kyaml and cmd/config
2020-04-22 15:19:44 -07:00
Jeff Regan
59b1662b92 Merge pull request #2401 from monopole/fixBuild
Fix broken Makefiles.
2020-04-22 13:43:58 -07:00
jregan
efeb26c634 Fix broken Makefiles. 2020-04-22 13:30:39 -07:00
Jeff Regan
e287d68d0f Merge pull request #2403 from monopole/removeGuess
Remove time guessing.
2020-04-22 13:30:09 -07:00
jregan
3786db2dba Remove time guessing. 2020-04-22 13:20:10 -07:00
Phillip Wittrock
46316198cb release kyaml and cmd/config 2020-04-22 13:08:19 -07:00
Phillip Wittrock
8de94ba96c update go.mod for release 2020-04-22 12:11:42 -07:00
Phillip Wittrock
deb55a9f15 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-04-22 12:11:19 -07:00
Kubernetes Prow Robot
243394002f Merge pull request #2391 from pwittrock/validation-spec
Allow declaratively running starlark functions
2020-04-22 09:27:53 -07:00
guoxudong
60f3ca6f70 update:docs/zh/README.md 2020-04-22 17:00:16 +08:00
guoxudong
5e9619d3fb update:docs/zh/fields.md 2020-04-22 16:44:50 +08:00
Jeff Regan
b9053655db Merge pull request #2395 from monopole/checkForProwNotTravis
Check for prow, not travis, in CI test.
2020-04-21 21:19:45 -07:00
jregan
1f35b37712 Check for prow, not travis, in CI test. 2020-04-21 21:09:09 -07:00
Kubernetes Prow Robot
d7484aacad Merge pull request #2394 from monopole/installHelm3
Start adding helm3 to ChartInflator and its coverage.
2020-04-21 21:03:51 -07:00
jregan
98414e8bf6 Start adding helm3 to ChartInflator and its coverage. 2020-04-21 20:45:19 -07:00
Phillip Wittrock
c1f2dd3688 Allow declaratively running starlark functions 2020-04-21 14:51:15 -07:00
Jeff Regan
982b9e77d4 Merge pull request #2372 from phanimarupaka/GitDiffForUpdate
Print file difference like git diff
2020-04-21 11:16:36 -07:00
Kubernetes Prow Robot
c75a47322b Merge pull request #2388 from monopole/removeStaticCling
Remove static cling in plugin development flow.
2020-04-20 12:13:40 -07:00
Jeff Regan
bb05d19a5a Merge pull request #2383 from novalagung/master
Issue #2381 - bug fix for unmodified `found` variable
2020-04-20 10:41:20 -07:00
Tobias Bradtke
8c18970b56 Fix formating of example 2020-04-20 18:33:22 +02:00
jregan
2c615d78a2 Remove static cling in plugin development flow. 2020-04-20 08:29:01 -07:00
Jeff Regan
45a9805656 Merge pull request #2387 from monopole/splitOutCompilerUtils
Add compiler utils test.
2020-04-19 17:56:38 -07:00
jregan
504029281a Add compiler utils test. 2020-04-19 17:44:01 -07:00
Jeff Regan
2d699f93fc Merge pull request #2385 from monopole/pluginLoader
Plugin flow doc improvements and a new option for the plugin loader
2020-04-18 19:45:16 -07:00
jregan
1653a70693 Plugin loader improvements. 2020-04-18 18:29:10 -07:00
Kubernetes Prow Robot
4667372114 Merge pull request #2384 from pwittrock/validation-spec
Enable functions to defer failures
2020-04-17 21:43:35 -07:00
Phillip Wittrock
6d9702561a Enable functions to defer failures
so multiple validators can be run sequentially without shortcircuiting the pipeline
2020-04-17 20:27:58 -07:00
novalagung
4533e43009 Issue #2381 - bug fix for unmodified found variable 2020-04-18 04:06:54 +07:00
Kubernetes Prow Robot
c7b00733c1 Merge pull request #2382 from pwittrock/release
release cmd/config and kyaml
2020-04-17 11:03:07 -07:00
Phillip Wittrock
b023570fae release cmd/config and kyaml 2020-04-17 09:51:59 -07:00
Phillip Wittrock
bf721a3fdd update go.mod for release 2020-04-17 09:51:31 -07:00
Phillip Wittrock
7d968fa80e Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-04-17 09:51:26 -07:00
Phani Teja Marupaka
014db05f9c Print file difference like git diff 2020-04-16 12:31:00 -07:00
Phillip Wittrock
fd6207b1c3 update go.mod for release 2020-04-02 11:31:23 -07:00
Phillip Wittrock
cbe85f482d Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-04-02 11:31:14 -07:00
Phillip Wittrock
a1c7330331 update go.mod for release 2020-03-28 08:34:06 -07:00
Phillip Wittrock
08ceab40dd Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-03-28 08:34:01 -07:00
Phillip Wittrock
f8fb00cb13 update go.mod for release 2020-03-23 12:08:52 -07:00
Phillip Wittrock
8a1c841420 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-03-23 12:08:31 -07:00
Phillip Wittrock
730d1f2473 update go.mod for release 2020-03-06 09:29:07 -08:00
Phillip Wittrock
29d1a37858 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-03-06 09:29:02 -08:00
Phillip Wittrock
c467f48bf2 update go.mod for release 2020-02-27 12:21:10 -08:00
Phillip Wittrock
054f56ceaf Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.1 2020-02-27 12:21:05 -08:00
Phillip Wittrock
d5a107074d update go.mod for release 2020-02-21 10:18:34 -08:00
Phillip Wittrock
d8bd6db880 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-02-21 10:18:15 -08:00
Phillip Wittrock
d27135e3a3 update go.mod for release 2020-02-19 15:00:57 -08:00
Phillip Wittrock
282b1fa49a Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-02-19 15:00:38 -08:00
Phillip Wittrock
584eb236fd update go.mod for release 2020-02-05 17:46:10 -08:00
Phillip Wittrock
31a193f60f Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-02-05 17:46:04 -08:00
Phillip Wittrock
bf17b244e5 update go.mod for release 2020-01-16 16:24:55 -08:00
Phillip Wittrock
8338299529 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-16 16:24:35 -08:00
Phillip Wittrock
7adf7eb271 update go.mod for release 2020-01-15 18:10:02 -08:00
Phillip Wittrock
06b091b175 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-15 18:09:55 -08:00
Phillip Wittrock
bf03669e94 update go.mod for release 2020-01-13 12:49:07 -08:00
Phillip Wittrock
7f52c814a8 release cmd/config
Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0
2020-01-13 12:47:01 -08:00
Phillip Wittrock
d5aac922d9 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-10 16:38:39 -08:00
Phillip Wittrock
9a62d866f3 update go.mod for release 2020-01-09 17:02:34 -08:00
Phillip Wittrock
b2812838bf release cmd/config 0.0.6
Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0
2020-01-09 17:02:08 -08:00
Phillip Wittrock
e59e477702 update go.mod for release 2020-01-03 10:01:16 -08:00
Phillip Wittrock
4264ad0ad4 release cmd/config 0.0.5 Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2020-01-03 10:01:06 -08:00
Phillip Wittrock
2554cd00eb update go.mod for release 2020-01-02 09:02:06 -08:00
Phillip Wittrock
383244cd63 Release cmd/config 0.0.4
Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0
2020-01-02 09:01:56 -08:00
Phillip Wittrock
b9da33afd4 update go.mod for release 2019-12-20 09:44:25 -08:00
Phillip Wittrock
23e339b86c Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2019-12-20 09:44:21 -08:00
Phillip Wittrock
9555009df8 update go.mod for release 2019-12-16 14:21:02 -08:00
Phillip Wittrock
91a10c560c Merge remote-tracking branch 'upstream/master' into release-cmd/config-v0.0 2019-12-16 14:20:58 -08:00
Phillip Wittrock
780cb19c4d drop replace 2019-12-13 13:50:17 -08:00
130 changed files with 6369 additions and 5277 deletions

28
.github/workflows/go.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: Go
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build
run: ./travis/kyaml-pre-commit.sh
env:
KUSTOMIZE_DOCKER_E2E: true

View File

@@ -116,7 +116,6 @@ _builtinplugins = \
ConfigMapGenerator.go \
HashTransformer.go \
ImageTagTransformer.go \
InventoryTransformer.go \
LabelTransformer.go \
LegacyOrderTransformer.go \
NamespaceTransformer.go \
@@ -141,7 +140,6 @@ $(pGen)/AnnotationsTransformer.go: $(pSrc)/annotationstransformer/AnnotationsTra
$(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
@@ -188,8 +186,14 @@ lint-kustomize: install-tools $(builtinplugins)
cd pluginator; \
$(MYGOBIN)/golangci-lint-kustomize -c ../.golangci-kustomize.yml run ./...
# Used to add non-default compilation flags when experimenting with
# plugin-to-api compatibility checks.
.PHONY: build-kustomize-api
build-kustomize-api: $(builtinplugins)
cd api; go build ./...
.PHONY: test-unit-kustomize-api
test-unit-kustomize-api: $(builtinplugins)
test-unit-kustomize-api: build-kustomize-api
cd api; go test ./...
.PHONY: test-unit-kustomize-plugins
@@ -212,10 +216,6 @@ test-unit-cmd-all:
test-go-mod:
./travis/check-go-mod.sh
.PHONY:
test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
./hack/testExamplesAgainstKustomize.sh HEAD
.PHONY:
test-examples-e2e-kustomize: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
( \
@@ -226,13 +226,17 @@ test-examples-e2e-kustomize: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
./hack/testExamplesE2EAgainstKustomize.sh .; \
)
.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; \
GO111MODULE=on go get sigs.k8s.io/kustomize/kustomize/v3@v3.5.4; \
./hack/testExamplesAgainstKustomize.sh latest; \
echo "Reinstalling kustomize from HEAD."; \
cd kustomize; go install .; \
@@ -255,21 +259,38 @@ $(MYGOBIN)/kubeval:
)
# 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:
# 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 binaries.
$(MYGOBIN)/helmV2:
( \
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); \
tgzFile=helm-v2.13.1-linux-amd64.tar.gz; \
wget https://storage.googleapis.com/kubernetes-helm/$$tgzFile; \
tar -xvzf $$tgzFile; \
mv linux-amd64/helm $(MYGOBIN)/helmV2; \
rm -rf $$d \
)
# Helm V3 differs from helm V2; downloading it to provide coverage for the
# chart inflator plugin under helm v3.
$(MYGOBIN)/helmV3:
( \
set -e; \
d=$(shell mktemp -d); cd $$d; \
tgzFile=helm-v3.2.0-rc.1-linux-amd64.tar.gz; \
wget https://get.helm.sh/$$tgzFile; \
tar -xvzf $$tgzFile; \
mv linux-amd64/helm $(MYGOBIN)/helmV3; \
rm -rf $$d \
)
# Default version of helm is v2 for the time being.
$(MYGOBIN)/helm: $(MYGOBIN)/helmV2
ln -s $(MYGOBIN)/helmV2 $(MYGOBIN)/helm
$(MYGOBIN)/kind:
( \
set -e; \
@@ -288,6 +309,7 @@ clean: kustomize-external-go-plugin-clean
rm -f $(MYGOBIN)/kustomize
rm -f $(MYGOBIN)/golangci-lint-kustomize
# Nuke the site from orbit. It's the only way to be sure.
.PHONY: nuke
nuke: clean
sudo rm -rf $(shell go env GOPATH)/pkg/mod/sigs.k8s.io
go clean --modcache

View File

@@ -6,7 +6,6 @@ package builtins
import (
"sigs.k8s.io/kustomize/api/filters/annotations"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
@@ -16,10 +15,6 @@ import (
type AnnotationsTransformerPlugin struct {
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
// YAMLSupport can be set to true to use the kyaml filter instead of the
// kunstruct transformer
YAMLSupport bool `json:"yamlSupport,omitempty" yaml:"yamlSupport,omitempty"`
}
func (p *AnnotationsTransformerPlugin) Config(
@@ -30,27 +25,16 @@ func (p *AnnotationsTransformerPlugin) Config(
}
func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
if p.YAMLSupport {
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(annotations.Filter{
Annotations: p.Annotations,
FsSlice: p.FieldSpecs,
}, r.Kunstructured)
if err != nil {
return err
}
}
return nil
} else {
t, err := transform.NewMapTransformer(
p.FieldSpecs,
p.Annotations,
)
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(annotations.Filter{
Annotations: p.Annotations,
FsSlice: p.FieldSpecs,
}, r.Kunstructured)
if err != nil {
return err
}
return t.Transform(m)
}
return nil
}
func NewAnnotationsTransformerPlugin() resmap.TransformerPlugin {

View File

@@ -1,125 +0,0 @@
// Code generated by pluginator on InventoryTransformer; DO NOT EDIT.
// pluginator {unknown 1970-01-01T00:00:00Z }
package builtins
import (
"fmt"
"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 {
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"`
}
func (p *InventoryTransformerPlugin) Config(
h *resmap.PluginHelpers, c []byte) (err error) {
p.h = h
err = yaml.Unmarshal(c, p)
if err != nil {
return err
}
if p.Policy == "" {
p.Policy = types.GarbageIgnore.String()
}
if p.Policy != types.GarbageCollect.String() &&
p.Policy != types.GarbageIgnore.String() {
return fmt.Errorf(
"unrecognized garbagePolicy '%s'", p.Policy)
}
return nil
}
// Transform generates an inventory object from the input ResMap.
// This ConfigMap supports the pruning command in
// the client side tool proposed here:
// https://github.com/kubernetes/enhancements/pull/810
//
// The inventory data is written to the ConfigMap's
// annotations, rather than to the key-value pairs in
// the ConfigMap's data field, since
// 1. Keys in a ConfigMap's data field are too
// constrained for this purpose.
// 2. Using annotations allow any object to be used,
// not just a ConfigMap, should some other object
// (e.g. some App object) become more desirable
// for this purpose.
func (p *InventoryTransformerPlugin) Transform(m resmap.ResMap) error {
inv, h, err := makeInventory(m)
if err != nil {
return err
}
args := types.ConfigMapArgs{}
args.Name = p.Name
args.Namespace = p.Namespace
args.Options = &types.GeneratorOptions{
Annotations: map[string]string{inventory.HashAnnotation: h},
}
err = inv.UpdateAnnotations(args.Options.Annotations)
if err != nil {
return err
}
cm, err := p.h.ResmapFactory().RF().MakeConfigMap(
kv.NewLoader(p.h.Loader(), p.h.Validator()), &args)
if err != nil {
return err
}
if p.Policy == types.GarbageCollect.String() {
for _, byeBye := range m.AllIds() {
m.Remove(byeBye)
}
}
return m.Append(cm)
}
func makeInventory(m resmap.ResMap) (
inv *inventory.Inventory, hash string, err error) {
inv = inventory.NewInventory()
var keys []string
for _, r := range m.Resources() {
ns := r.GetNamespace()
item := resid.NewResIdWithNamespace(r.GetGvk(), r.GetName(), ns)
if _, ok := inv.Current[item]; ok {
return nil, "", fmt.Errorf(
"item '%v' already in inventory", item)
}
inv.Current[item], err = computeRefs(r, m)
if err != nil {
return nil, "", err
}
keys = append(keys, item.String())
}
h, err := hasher.SortArrayAndComputeHash(keys)
return inv, h, err
}
func computeRefs(
r *resource.Resource, m resmap.ResMap) (refs []resid.ResId, err error) {
for _, refid := range r.GetRefBy() {
ref, err := m.GetByCurrentId(refid)
if err != nil {
return nil, err
}
refs = append(
refs,
resid.NewResIdWithNamespace(
ref.GetGvk(), ref.GetName(), ref.GetNamespace()))
}
return
}
func NewInventoryTransformerPlugin() resmap.TransformerPlugin {
return &InventoryTransformerPlugin{}
}

View File

@@ -4,9 +4,10 @@
package builtins
import (
"sigs.k8s.io/kustomize/api/filters/labels"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/transform"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/filtersutil"
"sigs.k8s.io/yaml"
)
@@ -24,14 +25,16 @@ func (p *LabelTransformerPlugin) Config(
}
func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error {
t, err := transform.NewMapTransformer(
p.FieldSpecs,
p.Labels,
)
if err != nil {
return err
for _, r := range m.Resources() {
err := filtersutil.ApplyToJSON(labels.Filter{
Labels: p.Labels,
FsSlice: p.FieldSpecs,
}, r.Kunstructured)
if err != nil {
return err
}
}
return t.Transform(m)
return nil
}
func NewLabelTransformerPlugin() resmap.TransformerPlugin {

View File

@@ -124,12 +124,7 @@ func isMatchGVK(fs types.FieldSpec, obj *yaml.RNode) (bool, error) {
}
// parse the group and version from the apiVersion field
var group, version string
parts := strings.SplitN(meta.APIVersion, "/", 2)
group = parts[0]
if len(parts) > 1 {
version = parts[1]
}
group, version := parseGV(meta.APIVersion)
if fs.Group != "" && fs.Group != group {
// group doesn't match

View File

@@ -4,9 +4,6 @@
package fsslice
import (
"strings"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -59,20 +56,3 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
}
return obj, nil
}
// GetGVK parses the metadata into a GVK
func GetGVK(meta yaml.ResourceMeta) resid.Gvk {
// parse the group and version from the apiVersion field
var group, version string
parts := strings.SplitN(meta.APIVersion, "/", 2)
group = parts[0]
if len(parts) > 1 {
version = parts[1]
}
return resid.Gvk{
Group: group,
Version: version,
Kind: meta.Kind,
}
}

View File

@@ -10,7 +10,6 @@ import (
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/filters/fsslice"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -281,6 +280,52 @@ kind: Bar
a:
b:
- c: {d: e}
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "group v1",
fsSlice: `
- path: a/b
group: v1
create: true
kind: Bar
`,
input: `
apiVersion: v1
kind: Bar
`,
expected: `
apiVersion: v1
kind: Bar
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
CreateKind: yaml.ScalarNode,
},
},
{
name: "version v1",
fsSlice: `
- path: a/b
version: v1
create: true
kind: Bar
`,
input: `
apiVersion: v1
kind: Bar
`,
expected: `
apiVersion: v1
kind: Bar
a:
b: e
`,
filter: fsslice.Filter{
SetValue: fsslice.SetScalar("e"),
@@ -332,23 +377,3 @@ func TestFilter_Filter(t *testing.T) {
})
}
}
func TestGetGVK(t *testing.T) {
obj, err := yaml.Parse(`
apiVersion: apps/v1
kind: Deployment
`)
if !assert.NoError(t, err) {
t.FailNow()
}
meta, err := obj.GetMeta()
if !assert.NoError(t, err) {
t.FailNow()
}
gvk := fsslice.GetGVK(meta)
expected := resid.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}
if !assert.Equal(t, expected, gvk) {
t.FailNow()
}
}

View File

@@ -0,0 +1,49 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fsslice
import (
"strings"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Return true for 'v' followed by a 1 or 2, and don't look at rest.
// I.e. 'v1', 'v1beta1', 'v2', would return true.
func looksLikeACoreApiVersion(s string) bool {
if len(s) < 2 {
return false
}
if s[0:1] != "v" {
return false
}
return s[1:2] == "1" || s[1:2] == "2"
}
// parseGV parses apiVersion field into group and version.
func parseGV(apiVersion string) (group, version string) {
// parse the group and version from the apiVersion field
parts := strings.SplitN(apiVersion, "/", 2)
group = parts[0]
if len(parts) > 1 {
version = parts[1]
}
// Special case the original "apiVersion" of what
// we now call the "core" (empty) group.
if version == "" && looksLikeACoreApiVersion(group) {
version = group
group = ""
}
return
}
// GetGVK parses the metadata into a GVK
func GetGVK(meta yaml.ResourceMeta) resid.Gvk {
group, version := parseGV(meta.APIVersion)
return resid.Gvk{
Group: group,
Version: version,
Kind: meta.Kind,
}
}

View File

@@ -0,0 +1,156 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package fsslice
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestParseGV(t *testing.T) {
testCases := map[string]struct {
input string
expectedGroup string
expectedVersion string
}{
"empty": {
input: "",
expectedGroup: "",
expectedVersion: "",
},
"certSigning": {
input: "certificates.k8s.io/v1beta1",
expectedGroup: "certificates.k8s.io",
expectedVersion: "v1beta1",
},
"extensions": {
input: "extensions/v1beta1",
expectedGroup: "extensions",
expectedVersion: "v1beta1",
},
"normal": {
input: "apps/v1",
expectedGroup: "apps",
expectedVersion: "v1",
},
"justApps": {
input: "apps",
expectedGroup: "apps",
expectedVersion: "",
},
"coreV1": {
input: "v1",
expectedGroup: "",
expectedVersion: "v1",
},
"coreV2": {
input: "v2",
expectedGroup: "",
expectedVersion: "v2",
},
"coreV2Beta1": {
input: "v2beta1",
expectedGroup: "",
expectedVersion: "v2beta1",
},
}
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
group, version := parseGV(tc.input)
if !assert.Equal(t, tc.expectedGroup, group) {
t.FailNow()
}
if !assert.Equal(t, tc.expectedVersion, version) {
t.FailNow()
}
})
}
}
func TestGetGVK(t *testing.T) {
testCases := map[string]struct {
input string
expected resid.Gvk
parseError string
metaError string
}{
"empty": {
input: `
`,
parseError: "EOF",
},
"junk": {
input: `
congress: effective
`,
metaError: "missing Resource metadata",
},
"normal": {
input: `
apiVersion: apps/v1
kind: Deployment
`,
expected: resid.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"},
},
"apiVersionOnlyWithSlash": {
input: `
apiVersion: apps/v1
`,
expected: resid.Gvk{Group: "apps", Version: "v1", Kind: ""},
},
"apiVersionOnlyNoSlash1": {
input: `
apiVersion: apps
`,
expected: resid.Gvk{Group: "apps", Version: "", Kind: ""},
},
"apiVersionOnlyNoSlash2": {
input: `
apiVersion: v1
`,
expected: resid.Gvk{Group: "", Version: "v1", Kind: ""},
},
}
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
obj, err := yaml.Parse(tc.input)
if len(tc.parseError) != 0 {
if err == nil {
t.Error("expected parse error")
return
}
if !strings.Contains(err.Error(), tc.parseError) {
t.Errorf("expected parse err '%s', got '%v'", tc.parseError, err)
}
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
meta, err := obj.GetMeta()
if len(tc.metaError) != 0 {
if err == nil {
t.Error("expected meta error")
return
}
if !strings.Contains(err.Error(), tc.metaError) {
t.Errorf("expected meta err '%s', got '%v'", tc.metaError, err)
}
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
gvk := GetGVK(meta)
if !assert.Equal(t, tc.expected, gvk) {
t.FailNow()
}
})
}
}

View File

@@ -8,19 +8,16 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/resid"
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
"sigs.k8s.io/kustomize/api/types"
)
var labelsFs = builtinconfig.MakeDefaultConfig().CommonLabels
func TestLabels_Filter(t *testing.T) {
testCases := map[string]struct {
input string
expectedOutput string
filter Filter
fsSlice types.FsSlice
}{
"add": {
input: `
@@ -45,12 +42,20 @@ metadata:
clown: emmett kelley
dragon: smaug
`,
filter: Filter{Labels: labelMap{
"clown": "emmett kelley",
"auto": "ford",
"dragon": "smaug",
"bean": "cannellini",
}},
filter: Filter{
Labels: labelMap{
"clown": "emmett kelley",
"auto": "ford",
"dragon": "smaug",
"bean": "cannellini",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
},
},
},
"update": {
input: `
@@ -73,13 +78,21 @@ metadata:
bean: cannellini
clown: emmett kelley
`,
filter: Filter{Labels: labelMap{
"clown": "emmett kelley",
"hero": "superman",
"fiend": "luthor",
"bean": "cannellini",
}},
filter: Filter{
Labels: labelMap{
"clown": "emmett kelley",
"hero": "superman",
"fiend": "luthor",
"bean": "cannellini",
}, FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
},
},
},
"data-fieldspecs": {
input: `
apiVersion: example.com/v1
@@ -113,13 +126,168 @@ a:
b:
sleater: kinney
`,
filter: Filter{Labels: labelMap{
"sleater": "kinney",
}},
fsSlice: []types.FieldSpec{
{
Path: "a/b",
CreateIfNotPresent: true,
filter: Filter{
Labels: labelMap{
"sleater": "kinney",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
{
Path: "a/b",
CreateIfNotPresent: true,
},
},
},
},
"fieldSpecWithKind": {
input: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
---
apiVersion: example.com/v2
kind: Bar
metadata:
name: instance
`,
expectedOutput: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
labels:
cheese: cheddar
---
apiVersion: example.com/v2
kind: Bar
metadata:
name: instance
labels:
cheese: cheddar
a:
b:
cheese: cheddar
`,
filter: Filter{
Labels: labelMap{
"cheese": "cheddar",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
{
Gvk: resid.Gvk{
Kind: "Bar",
},
Path: "a/b",
CreateIfNotPresent: true,
},
},
},
},
"fieldSpecWithVersion": {
input: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
---
apiVersion: example.com/v2
kind: Bar
metadata:
name: instance
`,
expectedOutput: `
apiVersion: example.com/v1
kind: Foo
metadata:
name: instance
labels:
cheese: cheddar
a:
b:
cheese: cheddar
---
apiVersion: example.com/v2
kind: Bar
metadata:
name: instance
labels:
cheese: cheddar
`,
filter: Filter{
Labels: labelMap{
"cheese": "cheddar",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
{
Gvk: resid.Gvk{
Version: "v1",
},
Path: "a/b",
CreateIfNotPresent: true,
},
},
},
},
"fieldSpecWithVersionInConfigButNoGroupInData": {
input: `
apiVersion: v1
kind: Foo
metadata:
name: instance
---
apiVersion: v2
kind: Bar
metadata:
name: instance
`,
expectedOutput: `
apiVersion: v1
kind: Foo
metadata:
name: instance
labels:
cheese: cheddar
a:
b:
cheese: cheddar
---
apiVersion: v2
kind: Bar
metadata:
name: instance
labels:
cheese: cheddar
`,
filter: Filter{
Labels: labelMap{
"cheese": "cheddar",
},
FsSlice: []types.FieldSpec{
{
Path: "metadata/labels",
CreateIfNotPresent: true,
},
{
Gvk: resid.Gvk{
Version: "v1",
},
Path: "a/b",
CreateIfNotPresent: true,
},
},
},
},
@@ -127,11 +295,9 @@ a:
for tn, tc := range testCases {
t.Run(tn, func(t *testing.T) {
filter := tc.filter
filter.FsSlice = append(labelsFs, tc.fsSlice...)
if !assert.Equal(t,
strings.TrimSpace(tc.expectedOutput),
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, filter))) {
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, tc.filter))) {
t.FailNow()
}
})

View File

@@ -297,6 +297,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
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/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
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=
@@ -422,6 +423,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/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/sys v0.0.0-20191002063906-3421d5a6bb1c h1:Vco5b+cuG5NNfORVxZy6bYZQ7rsigisU1WQFkvQ0L5E=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/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=
@@ -506,13 +508,6 @@ mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphD
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
mvdan.cc/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 v1.0.11 h1:Yb+6DDt9+aR2AvQApvUaKS/ugteeG4MPyoFeUHiPOjk=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize/kyaml v0.1.1 h1:nGUNYINljZNmlAS8uoobUv/wx/s3Pg8dNxYo+W7uYh0=
sigs.k8s.io/kustomize/kyaml v0.1.1/go.mod h1:/NdPPfrperSCGjm55cwEro1loBVtbtVIXSb7FguK6uk=
sigs.k8s.io/kustomize/kyaml v0.1.2 h1:l12+QGl+ETUHhP8/bZAi6TknU7H194fXL/9b2gUxZFY=
sigs.k8s.io/kustomize/kyaml v0.1.3 h1:zbeHVTMCQPtWgjIH/YYJZC45mm7coTdw2TblyJ79BrY=
sigs.k8s.io/kustomize/kyaml v0.1.3/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
sigs.k8s.io/kustomize/kyaml v0.1.5 h1:NicBWYTwkuOfVyZDbNkfSBSCwSgin4uirkedtyZltIc=
sigs.k8s.io/kustomize/kyaml v0.1.5/go.mod h1:461i94nj0h0ylJ6w83jLkR4SqqVhn1iY6fjD0JSTQeE=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=

View File

@@ -40,7 +40,11 @@ func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
case []interface{}:
var xs []interface{}
for _, a := range in.([]interface{}) {
xs = append(xs, expansion2.Expand(a.(string), rv.mappingFunc))
x, ok := a.(string)
if !ok {
return nil, fmt.Errorf("expected array of strings, found %v", in)
}
xs = append(xs, expansion2.Expand(x, rv.mappingFunc))
}
return xs, nil
case map[string]interface{}:
@@ -49,7 +53,7 @@ func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
for k, v := range inMap {
s, ok := v.(string)
if !ok {
// This field not contain a $(VAR) since it is not
// This field can not contain a $(VAR) since it is not
// of string type. For instance .spec.replicas: 3 in
// a Deployment object
xs[k] = v
@@ -64,7 +68,7 @@ func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
case interface{}:
s, ok := in.(string)
if !ok {
// This field not contain a $(VAR) since it is not of string type.
// This field can not contain a $(VAR) since it is not of string type.
return in, nil
}
// This field can potentially contain a $(VAR) since it is

View File

@@ -29,6 +29,7 @@ func TestRefVarTransformer(t *testing.T) {
description string
given given
expected expected
errMessage string
}{
{
description: "var replacement in map[string]",
@@ -111,6 +112,27 @@ func TestRefVarTransformer(t *testing.T) {
unused: []string{"BAR"},
},
},
{
description: "var replacement panic in map[string]",
given: given{
varMap: map[string]interface{}{},
fs: []types.FieldSpec{
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/slice"},
},
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{}{
"slice": []interface{}{5}, // noticeably *not* a []string
}}).ResMap(),
},
errMessage: "expected array of strings, found [5]",
},
}
for _, tc := range testCases {
@@ -122,16 +144,23 @@ func TestRefVarTransformer(t *testing.T) {
err := tr.Transform(tc.given.res)
// assert
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if tc.errMessage != "" {
if err == nil {
t.Fatalf("missing expected error %v", tc.errMessage)
} else if err.Error() != tc.errMessage {
t.Fatalf("actual error doesn't match expected error: \nACTUAL: %v\nEXPECTED: %v", err.Error(), tc.errMessage)
}
} else {
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)
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

@@ -13,21 +13,20 @@ func _() {
_ = x[ConfigMapGenerator-2]
_ = x[HashTransformer-3]
_ = x[ImageTagTransformer-4]
_ = x[InventoryTransformer-5]
_ = x[LabelTransformer-6]
_ = x[LegacyOrderTransformer-7]
_ = x[NamespaceTransformer-8]
_ = x[PatchJson6902Transformer-9]
_ = x[PatchStrategicMergeTransformer-10]
_ = x[PatchTransformer-11]
_ = x[PrefixSuffixTransformer-12]
_ = x[ReplicaCountTransformer-13]
_ = x[SecretGenerator-14]
_ = x[LabelTransformer-5]
_ = x[LegacyOrderTransformer-6]
_ = x[NamespaceTransformer-7]
_ = x[PatchJson6902Transformer-8]
_ = x[PatchStrategicMergeTransformer-9]
_ = x[PatchTransformer-10]
_ = x[PrefixSuffixTransformer-11]
_ = x[ReplicaCountTransformer-12]
_ = x[SecretGenerator-13]
}
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorHashTransformerImageTagTransformerInventoryTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerReplicaCountTransformerSecretGenerator"
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorHashTransformerImageTagTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerReplicaCountTransformerSecretGenerator"
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 62, 81, 101, 117, 139, 159, 183, 213, 229, 252, 275, 290}
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 62, 81, 97, 119, 139, 163, 193, 209, 232, 255, 270}
func (i BuiltinPluginType) String() string {
if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) {

View File

@@ -17,7 +17,6 @@ const (
ConfigMapGenerator
HashTransformer
ImageTagTransformer
InventoryTransformer
LabelTransformer
LegacyOrderTransformer
NamespaceTransformer
@@ -63,7 +62,6 @@ var TransformerFactories = map[BuiltinPluginType]func() resmap.TransformerPlugin
AnnotationsTransformer: builtins.NewAnnotationsTransformerPlugin,
HashTransformer: builtins.NewHashTransformerPlugin,
ImageTagTransformer: builtins.NewImageTagTransformerPlugin,
InventoryTransformer: builtins.NewInventoryTransformerPlugin,
LabelTransformer: builtins.NewLabelTransformerPlugin,
LegacyOrderTransformer: builtins.NewLegacyOrderTransformerPlugin,
NamespaceTransformer: builtins.NewNamespaceTransformerPlugin,

View File

@@ -6,143 +6,85 @@ package compiler
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/konfig"
)
// Compiler creates Go plugin object files.
//
// Source code is read from
// ${srcRoot}/${g}/${v}/${k}.go
//
// Object code is written to
// ${objRoot}/${g}/${v}/${k}.so
type Compiler struct {
srcRoot string
objRoot string
}
// DeterminePluginSrcRoot guesses where the user
// has her ${g}/${v}/$lower(${k})/${k}.go files.
func DeterminePluginSrcRoot(fSys filesys.FileSystem) (string, error) {
return konfig.FirstDirThatExistsElseError(
"source directory", fSys, []konfig.NotedFunc{
{
Note: "relative to unit test",
F: func() string {
return filepath.Clean(
filepath.Join(
os.Getenv("PWD"),
"..", "..",
konfig.RelPluginHome))
},
},
{
Note: "relative to unit test (internal pkg)",
F: func() string {
return filepath.Clean(
filepath.Join(
os.Getenv("PWD"),
"..", "..", "..", "..",
konfig.RelPluginHome))
},
},
{
Note: "relative to api package",
F: func() string {
return filepath.Clean(
filepath.Join(
os.Getenv("PWD"),
"..", "..", "..",
konfig.RelPluginHome))
},
},
{
Note: "old style $GOPATH",
F: func() string {
return filepath.Join(
os.Getenv("GOPATH"),
"src", konfig.DomainName,
konfig.ProgramName, konfig.RelPluginHome)
},
},
{
Note: "HOME with literal 'gopath'",
F: func() string {
return filepath.Join(
konfig.HomeDir(), "gopath",
"src", konfig.DomainName,
konfig.ProgramName, konfig.RelPluginHome)
},
},
{
Note: "home directory",
F: func() string {
return filepath.Join(
konfig.HomeDir(), konfig.DomainName,
konfig.ProgramName, konfig.RelPluginHome)
},
},
})
// pluginRoot is where the user
// has her ${g}/${v}/$lower(${k})/${k}.go files.
pluginRoot string
// Where compilation happens.
workDir string
// Used as the root file name for src and object.
rawKind string
// Capture compiler output.
stderr bytes.Buffer
// Capture compiler output.
stdout bytes.Buffer
}
// NewCompiler returns a new compiler instance.
func NewCompiler(srcRoot, objRoot string) *Compiler {
return &Compiler{srcRoot: srcRoot, objRoot: objRoot}
func NewCompiler(root string) *Compiler {
return &Compiler{pluginRoot: root}
}
// ObjRoot is root of compilation target tree.
func (b *Compiler) ObjRoot() string {
return b.objRoot
// Set GVK converts g,v,k tuples to file path components.
func (b *Compiler) SetGVK(g, v, k string) {
b.rawKind = k
b.workDir = filepath.Join(b.pluginRoot, g, v, strings.ToLower(k))
}
// SrcRoot is where to find src.
func (b *Compiler) SrcRoot() string {
return b.srcRoot
func (b *Compiler) srcPath() string {
return filepath.Join(b.workDir, b.rawKind+".go")
}
func goBin() string {
return filepath.Join(runtime.GOROOT(), "bin", "go")
func (b *Compiler) objFile() string {
return b.rawKind + ".so"
}
// Compile reads ${srcRoot}/${g}/${v}/${k}.go
// and writes ${objRoot}/${g}/${v}/${k}.so
func (b *Compiler) Compile(g, v, k string) error {
lowK := strings.ToLower(k)
objDir := filepath.Join(b.objRoot, g, v, lowK)
objFile := filepath.Join(objDir, k) + ".so"
if RecentFileExists(objFile) {
// Skip rebuilding it.
// Absolute path to the compiler output (the .so file).
func (b *Compiler) ObjPath() string {
return filepath.Join(b.workDir, b.objFile())
}
// Cleanup provides a hook to delete the .so file.
// Ignore errors.
func (b *Compiler) Cleanup() {
_ = os.Remove(b.ObjPath())
}
// Compile changes its working directory to
// ${pluginRoot}/${g}/${v}/$lower(${k} and places
// object code next to source code.
func (b *Compiler) Compile() error {
if FileYoungerThan(b.ObjPath(), 8*time.Second) {
// Skip rebuilding it, to save time in a plugin test file
// that has many distinct calls to make a harness and compile
// the plugin (only the first compile will happen).
// Make it a short time to avoid tricking someone who's actively
// developing a plugin.
return nil
}
err := os.MkdirAll(objDir, os.ModePerm)
if err != nil {
return err
}
srcFile := filepath.Join(b.srcRoot, g, v, lowK, k) + ".go"
if !FileExists(srcFile) {
// Handy for tests of lone plugins.
s := k + ".go"
if !FileExists(s) {
return fmt.Errorf(
"cannot find source at '%s' or '%s'", srcFile, s)
}
srcFile = s
if !FileExists(b.srcPath()) {
return fmt.Errorf("cannot find source at '%s'", b.srcPath())
}
// If you use an IDE, make sure it's go build and test flags
// match those used below. Same goes for Makefile targets.
commands := []string{
"build",
// "-trimpath", This flag used to make it better, now it makes it worse,
// see https://github.com/golang/go/issues/31354
"-buildmode",
"plugin",
"-o", objFile, srcFile,
"-o", b.objFile(),
}
goBin := goBin()
if !FileExists(goBin) {
@@ -150,34 +92,26 @@ func (b *Compiler) Compile(g, v, k string) error {
"cannot find go compiler %s", goBin)
}
cmd := exec.Command(goBin, commands...)
var stderr bytes.Buffer
cmd.Stderr = &stderr
b.stderr.Reset()
cmd.Stderr = &b.stderr
b.stdout.Reset()
cmd.Stdout = &b.stdout
cmd.Env = os.Environ()
cmd.Dir = b.workDir
if err := cmd.Run(); err != nil {
b.report()
return errors.Wrapf(
err, "cannot compile %s:\nSTDERR\n%s\n", srcFile, stderr.String())
err, "cannot compile %s:\nSTDERR\n%s\n",
b.srcPath(), b.stderr.String())
}
return nil
}
// True if file less than 3 minutes old, i.e. not
// accidentally left over from some earlier build.
func RecentFileExists(path string) bool {
fi, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
age := time.Since(fi.ModTime())
return age.Minutes() < 3
}
func FileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
func (b *Compiler) report() {
log.Println("stdout: -------")
log.Println(b.stdout.String())
log.Println("----------------")
log.Println("stderr: -------")
log.Println(b.stderr.String())
log.Println("----------------")
}

View File

@@ -4,8 +4,6 @@
package compiler_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@@ -15,53 +13,46 @@ import (
// Regression coverage over compiler behavior.
func TestCompiler(t *testing.T) {
configRoot, err := ioutil.TempDir("", "kustomize-compiler-test")
if err != nil {
t.Errorf("failed to make temp dir: %v", err)
}
srcRoot, err := DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
if err != nil {
t.Error(err)
}
c := NewCompiler(srcRoot, configRoot)
if configRoot != c.ObjRoot() {
t.Errorf("unexpected objRoot %s", c.ObjRoot())
}
c := NewCompiler(srcRoot)
c.SetGVK("someteam.example.com", "v1", "DatePrefixer")
expectObj := filepath.Join(
c.ObjRoot(),
"someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
if FileExists(expectObj) {
t.Errorf("obj file should not exist yet: %s", expectObj)
srcRoot, "someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
if expectObj != c.ObjPath() {
t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath())
}
err = c.Compile("someteam.example.com", "v1", "DatePrefixer")
err = c.Compile()
if err != nil {
t.Error(err)
}
if !RecentFileExists(expectObj) {
if !FileExists(expectObj) {
t.Errorf("didn't find expected obj file %s", expectObj)
}
c.Cleanup()
if FileExists(expectObj) {
t.Errorf("obj file '%s' should be gone", expectObj)
}
c.SetGVK("builtin", "", "SecretGenerator")
expectObj = filepath.Join(
c.ObjRoot(),
srcRoot,
"builtin", "", "secretgenerator", "SecretGenerator.so")
if FileExists(expectObj) {
t.Errorf("obj file should not exist yet: %s", expectObj)
if expectObj != c.ObjPath() {
t.Errorf("Expected '%s', got '%s'", expectObj, c.ObjPath())
}
err = c.Compile("builtin", "", "SecretGenerator")
err = c.Compile()
if err != nil {
t.Error(err)
}
if !RecentFileExists(expectObj) {
if !FileExists(expectObj) {
t.Errorf("didn't find expected obj file %s", expectObj)
}
err = os.RemoveAll(c.ObjRoot())
if err != nil {
t.Errorf(
"removing temp dir: %s %v", c.ObjRoot(), err)
}
c.Cleanup()
if FileExists(expectObj) {
t.Errorf("cleanup failed; still see: %s", expectObj)
t.Errorf("obj file '%s' should be gone", expectObj)
}
}

View File

@@ -0,0 +1,115 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package compiler
import (
"os"
"path/filepath"
"runtime"
"time"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/konfig"
)
func goBin() string {
return filepath.Join(runtime.GOROOT(), "bin", "go")
}
// DeterminePluginSrcRoot guesses where the user
// has her ${g}/${v}/$lower(${k})/${k}.go files.
func DeterminePluginSrcRoot(fSys filesys.FileSystem) (string, error) {
return konfig.FirstDirThatExistsElseError(
"source directory", fSys, []konfig.NotedFunc{
{
Note: "relative to unit test",
F: func() string {
return filepath.Clean(
filepath.Join(
os.Getenv("PWD"),
"..", "..",
konfig.RelPluginHome))
},
},
{
Note: "relative to unit test (internal pkg)",
F: func() string {
return filepath.Clean(
filepath.Join(
os.Getenv("PWD"),
"..", "..", "..", "..",
konfig.RelPluginHome))
},
},
{
Note: "relative to api package",
F: func() string {
return filepath.Clean(
filepath.Join(
os.Getenv("PWD"),
"..", "..", "..",
konfig.RelPluginHome))
},
},
{
Note: "old style $GOPATH",
F: func() string {
return filepath.Join(
os.Getenv("GOPATH"),
"src", konfig.DomainName,
konfig.ProgramName, konfig.RelPluginHome)
},
},
{
Note: "HOME with literal 'gopath'",
F: func() string {
return filepath.Join(
konfig.HomeDir(), "gopath",
"src", konfig.DomainName,
konfig.ProgramName, konfig.RelPluginHome)
},
},
{
Note: "home directory",
F: func() string {
return filepath.Join(
konfig.HomeDir(), konfig.DomainName,
konfig.ProgramName, konfig.RelPluginHome)
},
},
})
}
// FileYoungerThan returns true if the file both exists and has an
// age is <= the Duration argument.
func FileYoungerThan(path string, d time.Duration) bool {
fi, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
return time.Since(fi.ModTime()) <= d
}
// FileModifiedAfter returns true if the file both exists and was
// modified after the given time..
func FileModifiedAfter(path string, t time.Time) bool {
fi, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
return false
}
}
return fi.ModTime().After(t)
}
func FileExists(path string) bool {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}

View File

@@ -0,0 +1,26 @@
// Copyright 2020 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package compiler
import (
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
)
func TestDeterminePluginSrcRoot(t *testing.T) {
actual, err := DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
if err != nil {
t.Error(err)
}
if !filepath.IsAbs(actual) {
t.Errorf("expected absolute path, but got '%s'", actual)
}
expectedSuffix := filepath.Join("sigs.k8s.io", "kustomize", "plugin")
if !strings.HasSuffix(actual, expectedSuffix) {
t.Errorf("expected suffix '%s' in '%s'", expectedSuffix, actual)
}
}

View File

@@ -94,7 +94,7 @@ TO GENERATE CODE
cd $repo/plugin/builtin
go generate ./...
See travis/pre-commit.sh for canonical way
See travis/kyaml-pre-commit.sh for canonical way
to execute the above.
This creates

View File

@@ -22,6 +22,7 @@ import (
"sigs.k8s.io/kustomize/api/types"
)
// Loader loads plugins using a file loader (a different loader).
type Loader struct {
pc *types.PluginConfig
rf *resmap.Factory
@@ -107,17 +108,35 @@ func isBuiltinPlugin(res *resource.Resource) bool {
}
func (l *Loader) loadAndConfigurePlugin(
ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (c resmap.Configurable, err error) {
ldr ifc.Loader,
v ifc.Validator,
res *resource.Resource) (c resmap.Configurable, err error) {
if isBuiltinPlugin(res) {
// Instead of looking for and loading a .so file, just
// instantiate the plugin from a generated factory
// function (see "pluginator"). Being able to do this
// is what makes a plugin "builtin".
c, err = l.makeBuiltinPlugin(res.GetGvk())
} else if l.pc.PluginRestrictions == types.PluginRestrictionsNone {
c, err = l.loadPlugin(res.OrgId())
switch l.pc.BpLoadingOptions {
case types.BploLoadFromFileSys:
c, err = l.loadPlugin(res.OrgId())
case types.BploUseStaticallyLinked:
// Instead of looking for and loading a .so file,
// instantiate the plugin from a generated factory
// function (see "pluginator"). Being able to do this
// is what makes a plugin "builtin".
c, err = l.makeBuiltinPlugin(res.GetGvk())
default:
err = fmt.Errorf(
"unknown plugin loader behavior specified: %v",
l.pc.BpLoadingOptions)
}
} else {
err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind)
switch l.pc.PluginRestrictions {
case types.PluginRestrictionsNone:
c, err = l.loadPlugin(res.OrgId())
case types.PluginRestrictionsBuiltinsOnly:
err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind)
default:
err = fmt.Errorf(
"unknown plugin restriction specified: %v",
l.pc.PluginRestrictions)
}
}
if err != nil {
return nil, err

View File

@@ -15,6 +15,7 @@ import (
"sigs.k8s.io/kustomize/api/resource"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
"sigs.k8s.io/kustomize/api/types"
)
const (
@@ -58,22 +59,26 @@ func TestLoader(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c, err := konfig.EnabledPluginConfig()
if err != nil {
t.Fatal(err)
}
pLdr := NewLoader(c, rmF)
if pLdr == nil {
t.Fatal("expect non-nil loader")
}
m, err := rmF.NewResMapFromBytes([]byte(
generatorConfigs, err := rmF.NewResMapFromBytes([]byte(
someServiceGenerator + "---\n" + secretGenerator))
if err != nil {
t.Fatal(err)
}
_, err = pLdr.LoadGenerators(
fLdr, valtest_test.MakeFakeValidator(), m)
if err != nil {
t.Fatal(err)
for _, behavior := range []types.BuiltinPluginLoadingOptions{
types.BploUseStaticallyLinked,
types.BploLoadFromFileSys} {
c, err := konfig.EnabledPluginConfig(behavior)
if err != nil {
t.Fatal(err)
}
pLdr := NewLoader(c, rmF)
if pLdr == nil {
t.Fatal("expect non-nil loader")
}
_, err = pLdr.LoadGenerators(
fLdr, valtest_test.MakeFakeValidator(), generatorConfigs)
if err != nil {
t.Fatal(err)
}
}
}

View File

@@ -106,15 +106,10 @@ func unmarshal(y []byte, o interface{}) error {
// MakeCustomizedResMap creates a fully customized ResMap
// per the instructions contained in its kustomiztion instance.
func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
return kt.makeCustomizedResMap(types.GarbageIgnore)
return kt.makeCustomizedResMap()
}
func (kt *KustTarget) MakePruneConfigMap() (resmap.ResMap, error) {
return kt.makeCustomizedResMap(types.GarbageCollect)
}
func (kt *KustTarget) makeCustomizedResMap(
garbagePolicy types.GarbagePolicy) (resmap.ResMap, error) {
func (kt *KustTarget) makeCustomizedResMap() (resmap.ResMap, error) {
ra, err := kt.AccumulateTarget()
if err != nil {
return nil, err
@@ -141,11 +136,6 @@ func (kt *KustTarget) makeCustomizedResMap(
return nil, err
}
err = kt.computeInventory(ra, garbagePolicy)
if err != nil {
return nil, err
}
return ra.ResMap(), nil
}
@@ -159,35 +149,6 @@ func (kt *KustTarget) addHashesToNames(
return ra.Transform(p)
}
func (kt *KustTarget) computeInventory(
ra *accumulator.ResAccumulator, garbagePolicy types.GarbagePolicy) error {
inv := kt.kustomization.Inventory
if inv == nil {
return nil
}
if inv.Type != "ConfigMap" {
return fmt.Errorf("don't know how to do that")
}
if inv.ConfigMap.Namespace != kt.kustomization.Namespace {
return fmt.Errorf("namespace mismatch")
}
var c struct {
Policy string
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
}
c.Name = inv.ConfigMap.Name
c.Namespace = inv.ConfigMap.Namespace
c.Policy = garbagePolicy.String()
p := builtins.NewInventoryTransformerPlugin()
err := kt.configureBuiltinPlugin(p, c, builtinhelpers.InventoryTransformer)
if err != nil {
return err
}
return ra.Transform(p)
}
// AccumulateTarget returns a new ResAccumulator,
// holding customized resources and the data/rules used
// to do so. The name back references and vars are
@@ -364,7 +325,8 @@ func (kt *KustTarget) configureBuiltinPlugin(
}
err = p.Config(resmap.NewPluginHelpers(kt.ldr, kt.validator, kt.rFactory), y)
if err != nil {
return errors.Wrapf(err, "builtin %s config: %v", bpt, y)
return errors.Wrapf(
err, "trouble configuring builtin %s with config: `\n%s`", bpt, string(y))
}
return nil
}

View File

@@ -7,7 +7,6 @@ import (
"encoding/base64"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
"sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/resmap"
@@ -19,8 +18,7 @@ import (
// high level tests.
func TestMakeCustomizedResMap(t *testing.T) {
fSys := filesys.MakeFsInMemory()
th := kusttest_test.MakeHarnessWithFs(t, fSys)
th := kusttest_test.MakeHarness(t)
th.WriteK("/whatever", `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
@@ -168,7 +166,7 @@ metadata:
}
actual, err := makeKustTargetWithRf(
t, fSys, "/whatever", resFactory).MakeCustomizedResMap()
t, th.GetFSys(), "/whatever", resFactory).MakeCustomizedResMap()
if err != nil {
t.Fatalf("unexpected Resources error %v", err)
}

View File

@@ -1,12 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inventory
const (
// Annotation that contains the inventory content.
ContentAnnotation = "kustomize.config.k8s.io/Inventory"
// Annotation for inventory content hash.
HashAnnotation = "kustomize.config.k8s.io/InventoryHash"
)

View File

@@ -1,235 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inventory
import (
"encoding/json"
"sigs.k8s.io/kustomize/api/resid"
)
//Refs is a reference map. Each key is the id
//of a k8s resource, and each value is a list of
//object ids that refer back to the object in the
//key.
//For example, the key could correspond to a
//ConfigMap, and the list of values might include
//several different Deployments that get data from
//that ConfigMap (and thus refer to it).
//References are important in inventory management
//because one may not delete an object before all
//objects referencing it have been removed.
type Refs map[resid.ResId][]resid.ResId
func NewRefs() Refs {
return Refs{}
}
// Merge merges a Refs into an existing Refs
func (rf Refs) Merge(b Refs) Refs {
for key, value := range b {
_, ok := rf[key]
if ok {
rf[key] = append(rf[key], value...)
} else {
rf[key] = value
}
}
return rf
}
// removeIfContains removes the reference relationship
// a --> b
// from the Refs if it exists
func (rf Refs) RemoveIfContains(a, b resid.ResId) {
refs, ok := rf[a]
if !ok {
return
}
for i, ref := range refs {
if ref.Equals(b) {
rf[a] = append(refs[:i], refs[i+1:]...)
break
}
}
}
//Inventory is a an object intended for
//serialization into the annotations of a so-called
//apply-root object (a ConfigMap, an Application,
//etc.) living in the cluster. This apply-root
//object is written as part of an apply operation as
//a means to record overall cluster state changes.
//At the end of a successful apply, the "current"
//field in Inventory will be a map whose keys all
//correspond to an object in the cluster, and
//"previous" will be the previous such set (an empty
//set on the first apply).
//An Inventory allows the Prune method to work.
type Inventory struct {
Current Refs `json:"current,omitempty"`
Previous Refs `json:"previous,omitempty"`
}
// NewInventory returns an Inventory object
func NewInventory() *Inventory {
return &Inventory{
Current: NewRefs(),
Previous: NewRefs(),
}
}
// UpdateCurrent updates the Inventory given a
// new current Refs
// The existing Current refs is merged into
// the Previous refs
func (a *Inventory) UpdateCurrent(curref Refs) *Inventory {
if len(a.Previous) > 0 {
a.Previous.Merge(a.Current)
} else {
a.Previous = a.Current
}
a.Current = curref
return a
}
func (a *Inventory) removeNewlyOrphanedItemsFromPrevious() []resid.ResId {
var results []resid.ResId
for item, refs := range a.Previous {
if _, ok := a.Current[item]; ok {
delete(a.Previous, item)
continue
}
var newRefs []resid.ResId
toDelete := true
for _, ref := range refs {
if _, ok := a.Current[ref]; ok {
toDelete = false
newRefs = append(newRefs, ref)
}
}
if toDelete {
results = append(results, item)
delete(a.Previous, item)
} else {
a.Previous[item] = newRefs
}
}
return results
}
func (a *Inventory) removeOrphanedItemsFromPreviousThatAreNotInCurrent() []resid.ResId {
var results []resid.ResId
for item, refs := range a.Previous {
if _, ok := a.Current[item]; ok {
continue
}
if len(refs) == 0 {
results = append(results, item)
delete(a.Previous, item)
}
}
return results
}
func (a *Inventory) removeOrphanedItemsFromPreviousThatAreInCurrent() {
//Remove references from Previous that are already in Current refs
for item, refs := range a.Current {
for _, ref := range refs {
a.Previous.RemoveIfContains(item, ref)
}
}
//Remove items from Previous that are already in Current refs
for item, refs := range a.Previous {
if len(refs) == 0 {
if _, ok := a.Current[item]; ok {
delete(a.Previous, item)
}
}
}
}
// Prune computes the diff of Current refs and Previous refs
// and returns a list of Items that can be pruned.
// An item that can be pruned shows up only in Previous refs.
// Prune also updates the Previous refs with those items removed
func (a *Inventory) Prune() []resid.ResId {
a.removeOrphanedItemsFromPreviousThatAreInCurrent()
// These are candidates for deletion from the cluster.
removable1 := a.removeOrphanedItemsFromPreviousThatAreNotInCurrent()
removable2 := a.removeNewlyOrphanedItemsFromPrevious()
return append(removable1, removable2...)
}
// inventory is the internal type used for serialization
type inventory struct {
Current map[string][]resid.ResId `json:"current,omitempty"`
Previous map[string][]resid.ResId `json:"previous,omitempty"`
}
func (a *Inventory) toInternalType() inventory {
prev := map[string][]resid.ResId{}
curr := map[string][]resid.ResId{}
for id, refs := range a.Current {
curr[id.String()] = refs
}
for id, refs := range a.Previous {
prev[id.String()] = refs
}
return inventory{
Current: curr,
Previous: prev,
}
}
func (a *Inventory) fromInternalType(i *inventory) {
for s, refs := range i.Previous {
a.Previous[resid.FromString(s)] = refs
}
for s, refs := range i.Current {
a.Current[resid.FromString(s)] = refs
}
}
func (a *Inventory) marshal() ([]byte, error) {
return json.Marshal(a.toInternalType())
}
func (a *Inventory) unMarshal(data []byte) error {
inv := &inventory{
Current: map[string][]resid.ResId{},
Previous: map[string][]resid.ResId{},
}
err := json.Unmarshal(data, inv)
if err != nil {
return err
}
a.fromInternalType(inv)
return nil
}
// UpdateAnnotations update the annotation map
func (a *Inventory) UpdateAnnotations(annot map[string]string) error {
data, err := a.marshal()
if err != nil {
return err
}
annot[ContentAnnotation] = string(data)
return nil
}
// LoadFromAnnotation loads the Inventory date from the annotation map
func (a *Inventory) LoadFromAnnotation(annot map[string]string) error {
value, ok := annot[ContentAnnotation]
if ok {
return a.unMarshal([]byte(value))
}
return nil
}

View File

@@ -1,60 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package inventory_test
import (
"testing"
. "sigs.k8s.io/kustomize/api/inventory"
"sigs.k8s.io/kustomize/api/resid"
)
func makeRefs() (Refs, Refs) {
a := resid.FromString("G1_V1_K1|ns1|nm1")
b := resid.FromString("G2_V2_K2|ns2|nm2")
c := resid.FromString("G3_V3_K3|ns3|nm3")
current := NewRefs()
current[a] = []resid.ResId{b, c}
current[b] = []resid.ResId{}
current[c] = []resid.ResId{}
newRefs := NewRefs()
newRefs[a] = []resid.ResId{b}
newRefs[b] = []resid.ResId{}
return current, newRefs
}
func TestInventory(t *testing.T) {
inventory := NewInventory()
curref, _ := makeRefs()
inventory.UpdateCurrent(curref)
if len(inventory.Current) != 3 {
t.Fatalf("not getting the correct inventory %v", inventory)
}
curref, newref := makeRefs()
inventory.UpdateCurrent(curref)
if len(inventory.Current) != 3 {
t.Fatalf("not getting the corrent inventory %v", inventory)
}
if len(inventory.Previous) != 3 {
t.Fatalf("not getting the corrent inventory %v", inventory)
}
items := inventory.Prune()
if len(items) != 0 {
t.Fatalf("not getting the corrent items %v", items)
}
if len(inventory.Previous) != 0 {
t.Fatalf("not getting the corrent inventory %v", inventory)
}
inventory.UpdateCurrent(newref)
items = inventory.Prune()
if len(items) != 1 {
t.Fatalf("not getting the corrent items %v", items)
}
if len(inventory.Previous) != 0 {
t.Fatalf("not getting the corrent inventory %v", inventory.Previous)
}
}

View File

@@ -35,37 +35,46 @@ const (
// Domain from which kustomize code is imported, for locating
// plugin source code under $GOPATH when GOPATH is defined.
DomainName = "sigs.k8s.io"
// Injected into plugin paths when plugins are disabled.
// Provides a clue in flows that shouldn't happen.
NoPluginHomeSentinal = "/No/non-builtin/plugins!"
)
func EnabledPluginConfig() (*types.PluginConfig, error) {
func EnabledPluginConfig(b types.BuiltinPluginLoadingOptions) (*types.PluginConfig, error) {
dir, err := DefaultAbsPluginHome(filesys.MakeFsOnDisk())
if err != nil {
return nil, err
}
return MakePluginConfig(types.PluginRestrictionsNone, dir), nil
return MakePluginConfig(types.PluginRestrictionsNone, b, dir), nil
}
func DisabledPluginConfig() *types.PluginConfig {
return MakePluginConfig(
types.PluginRestrictionsBuiltinsOnly, NoPluginHomeSentinal)
types.PluginRestrictionsBuiltinsOnly,
types.BploUseStaticallyLinked,
NoPluginHomeSentinal)
}
func MakePluginConfig(
pr types.PluginRestrictions, home string) *types.PluginConfig {
pr types.PluginRestrictions,
b types.BuiltinPluginLoadingOptions,
home string) *types.PluginConfig {
return &types.PluginConfig{
PluginRestrictions: pr,
AbsPluginHome: home,
BpLoadingOptions: b,
}
}
// Use an obviously erroneous path, in case it's accidentally used.
const NoPluginHomeSentinal = "/no/non-builtin/plugins!"
type NotedFunc struct {
Note string
F func() string
}
// DefaultAbsPluginHome returns the absolute path in the given file
// system to first directory that looks like a good candidate for
// the home of kustomize plugins.
func DefaultAbsPluginHome(fSys filesys.FileSystem) (string, error) {
return FirstDirThatExistsElseError(
"plugin home directory", fSys, []NotedFunc{

View File

@@ -154,9 +154,8 @@ LEGUME=chickpea
`)
th.WriteF("/app/overlay/configmap/dummy.txt",
`Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
`)
th.WriteF("/app/overlay/deployment/deployment.yaml", `
apiVersion: apps/v1
@@ -293,11 +292,8 @@ metadata:
---
apiVersion: v1
data:
nonsense: |
Lorem ipsum dolor sit amet, consectetur
adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
nonsense: "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit, sed do eiusmod
tempor\nincididunt ut labore et dolore magna aliqua. \n"
kind: ConfigMap
metadata:
annotations:
@@ -306,6 +302,6 @@ metadata:
app: mungebot
org: kubernetes
repo: test-infra
name: test-infra-app-config-hh272bg5d4
name: test-infra-app-config-f462h769f9
`)
}

View File

@@ -71,11 +71,7 @@ func (b *Kustomizer) Run(path string) (resmap.ResMap, error) {
return nil, err
}
var m resmap.ResMap
if b.options.DoPrune {
m, err = kt.MakePruneConfigMap()
} else {
m, err = kt.MakeCustomizedResMap()
}
m, err = kt.MakeCustomizedResMap()
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,110 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func makeCommonFileForMergeEnvFromTest(th kusttest_test.Harness) {
th.WriteF("/app/deployment.yaml", `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: image1
envFrom:
- configMapRef:
name: some-config
- configMapRef:
name: more-config
`)
}
// When patching, `envFrom` should merge the list instead of replacing it.
func TestMergeEnvFrom(t *testing.T) {
th := kusttest_test.MakeHarness(t)
makeCommonFileForMergeEnvFromTest(th)
th.WriteK("/app", `
resources:
- deployment.yaml
patchesStrategicMerge:
- |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
envFrom:
- configMapRef:
name: another-config
`)
m := th.Run("/app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- envFrom:
- configMapRef:
name: another-config
image: image1
name: nginx
`)
}
func TestMergeEnvFromViaJsonInline(t *testing.T) {
th := kusttest_test.MakeHarness(t)
makeCommonFileForMergeEnvFromTest(th)
th.WriteK("app", `
resources:
- deployment.yaml
patches:
- target:
kind: Deployment
name: nginx
patch: |-
- op: add
path: /spec/template/spec/containers/0/envFrom/-
value:
configMapRef:
name: another-config
`)
m := th.Run("app", th.MakeDefaultOptions())
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- envFrom:
- configMapRef:
name: some-config
- configMapRef:
name: more-config
- configMapRef:
name: another-config
image: image1
name: nginx
`)
}

View File

@@ -1,163 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package krusty_test
import (
"testing"
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
)
func TestPruneConfigMap(t *testing.T) {
th := kusttest_test.MakeHarness(t)
th.WriteK("/app/base", `
resources:
- deployment.yaml
- service.yaml
- secret.yaml
inventory:
type: ConfigMap
configMap:
name: haha
namespace: default
namePrefix: my-
namespace: default
`)
th.WriteF("/app/base/deployment.yaml", `
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: mysql
labels:
app: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
emptyDir: {}
`)
th.WriteF("/app/base/service.yaml", `
apiVersion: v1
kind: Service
metadata:
name: mmmysql
labels:
app: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
`)
th.WriteF("/app/base/secret.yaml", `
apiVersion: v1
kind: Secret
metadata:
name: pass
type: Opaque
data:
# Default password is "admin".
password: YWRtaW4=
username: jingfang
`)
m := th.Run("/app/base", th.MakeDefaultOptions())
//nolint
th.AssertActualEqualsExpected(m, `
apiVersion: apps/v1beta2
kind: Deployment
metadata:
labels:
app: mysql
name: my-mysql
namespace: default
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: my-pass
image: mysql:5.6
name: mysql
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- mountPath: /var/lib/mysql
name: mysql-persistent-storage
volumes:
- emptyDir: {}
name: mysql-persistent-storage
---
apiVersion: v1
kind: Service
metadata:
labels:
app: mysql
name: my-mmmysql
namespace: default
spec:
ports:
- port: 3306
selector:
app: mysql
---
apiVersion: v1
data:
password: YWRtaW4=
username: jingfang
kind: Secret
metadata:
name: my-pass
namespace: default
type: Opaque
---
apiVersion: v1
kind: ConfigMap
metadata:
annotations:
kustomize.config.k8s.io/Inventory: '{"current":{"apps_v1beta2_Deployment|default|my-mysql":null,"~G_v1_Secret|default|my-pass":[{"group":"apps","version":"v1beta2","kind":"Deployment","name":"my-mysql","namespace":"default"}],"~G_v1_Service|default|my-mmmysql":null}}'
kustomize.config.k8s.io/InventoryHash: kd67f7ht8t
name: haha
namespace: default
`)
}

View File

@@ -9,7 +9,6 @@ import (
"fmt"
"os"
"path"
"regexp"
"strings"
"unicode"
"unicode/utf8"
@@ -86,17 +85,11 @@ func (kvl *loader) keyValuesFromFileSources(sources []string) ([]types.Pair, err
if err != nil {
return nil, err
}
kvs = append(kvs, types.Pair{Key: k, Value: trimTrailingSpacesInLines(string(content))})
kvs = append(kvs, types.Pair{Key: k, Value: string(content)})
}
return kvs, nil
}
// trimTrailingSpacesInLines takes string with multiple lines and trims the trailing white spaces and tabs from each line.
func trimTrailingSpacesInLines(str string) string {
re := regexp.MustCompile(`[ \t]*\n`)
return re.ReplaceAllString(str, "\n")
}
func (kvl *loader) keyValuesFromEnvFiles(paths []string) ([]types.Pair, error) {
var kvs []types.Pair
for _, p := range paths {

View File

@@ -95,12 +95,3 @@ func TestKeyValuesFromFileSources(t *testing.T) {
}
}
}
func TestTrimTrailingSpacesInLines(t *testing.T) {
input := "\"fooKey\": \"fooValue\" \t\n \t\t \n\t\"barKey\": \"barValue\""
expected := "\"fooKey\": \"fooValue\"\n\n\t\"barKey\": \"barValue\""
res := trimTrailingSpacesInLines(input)
if !reflect.DeepEqual(res, expected) {
t.Errorf("Trim trailing spaces in lines should succeed, got: %s exptected: %s", res, expected)
}
}

View File

@@ -70,7 +70,7 @@ func (th Harness) MakeOptionsPluginsDisabled() krusty.Options {
// Enables use of non-builtin plugins.
func (th Harness) MakeOptionsPluginsEnabled() krusty.Options {
c, err := konfig.EnabledPluginConfig()
c, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
if err != nil {
if strings.Contains(err.Error(), "unable to find plugin root") {
th.t.Log(

View File

@@ -19,38 +19,50 @@ import (
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// HarnessEnhanced manages a full plugin environment for tests.
type HarnessEnhanced struct {
// An instance of *testing.T, and a filesystem (likely in-memory)
// for loading test data - plugin config, resources to transform, etc.
Harness
// plugintestEnv holds the plugin compiler and data needed to
// create compilation sub-processes.
pte *pluginTestEnv
rf *resmap.Factory
// rf creates Resources from byte streams.
rf *resmap.Factory
// A file loader using the Harness.fSys to read test data.
ldr ifc.Loader
pl *pLdr.Loader
// A plugin loader that loads plugins from a (real) file system.
pl *pLdr.Loader
}
func MakeEnhancedHarness(t *testing.T) *HarnessEnhanced {
pte := newPluginTestEnv(t).set()
pc, err := konfig.EnabledPluginConfig()
pc, err := konfig.EnabledPluginConfig(types.BploLoadFromFileSys)
if err != nil {
t.Fatal(err)
}
fSys := filesys.MakeFsInMemory()
rf := resmap.NewFactory(
resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()),
transformer.NewFactoryImpl())
result := &HarnessEnhanced{
Harness: Harness{t: t, fSys: fSys},
Harness: MakeHarness(t),
pte: pte,
rf: rf,
pl: pLdr.NewLoader(pc, rf)}
// Point the file loader to the root ('/') of the in-memory file system.
result.ResetLoaderRoot(filesys.Separator)
return result
@@ -60,21 +72,23 @@ func (th *HarnessEnhanced) Reset() {
th.pte.reset()
}
func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced {
return th.BuildGoPlugin(konfig.BuiltinPluginPackage, "", k)
}
func (th *HarnessEnhanced) BuildGoPlugin(g, v, k string) *HarnessEnhanced {
th.pte.buildGoPlugin(g, v, k)
th.pte.prepareGoPlugin(g, v, k)
return th
}
func (th *HarnessEnhanced) PrepExecPlugin(g, v, k string) *HarnessEnhanced {
th.pte.prepExecPlugin(g, v, k)
return th
}
func (th *HarnessEnhanced) PrepBuiltin(k string) *HarnessEnhanced {
th.pte.buildGoPlugin(konfig.BuiltinPluginPackage, "", k)
th.pte.prepareExecPlugin(g, v, k)
return th
}
// ResetLoaderRoot interprets its argument as an absolute directory path.
// It creates the directory, and creates the harness's file loader
// rooted in that directory.
func (th *HarnessEnhanced) ResetLoaderRoot(root string) {
if err := th.fSys.Mkdir(root); err != nil {
th.t.Fatal(err)
@@ -137,7 +151,6 @@ func toggleYamlSupportField(config string, yamlSupport bool) (string, error) {
Reader: bytes.NewBufferString(config),
Writer: &out,
}
err := kio.Pipeline{
Inputs: []kio.Reader{&rw},
Filters: []kio.Filter{

View File

@@ -4,11 +4,7 @@
package kusttest_test
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"sigs.k8s.io/kustomize/api/filesys"
@@ -16,17 +12,14 @@ import (
"sigs.k8s.io/kustomize/api/konfig"
)
// pluginTestEnv manages plugins for tests.
// It manages a Go plugin compiler,
// makes and removes a temporary working directory,
// and sets/resets shell env vars as needed.
// pluginTestEnv manages compiling plugins for tests.
// It manages a Go plugin compiler, and sets/resets shell env vars as needed.
type pluginTestEnv struct {
t *testing.T
compiler *compiler.Compiler
srcRoot string
workDir string
oldXdg string
wasSet bool
t *testing.T
compiler *compiler.Compiler
pluginRoot string
oldXdg string
wasSet bool
}
// newPluginTestEnv returns a new instance of pluginTestEnv.
@@ -39,76 +32,44 @@ func newPluginTestEnv(t *testing.T) *pluginTestEnv {
// plugin code - this FileSystem has nothing to do with
// the FileSystem used for loading config yaml in the tests.
func (x *pluginTestEnv) set() *pluginTestEnv {
x.createWorkDir()
var err error
x.srcRoot, err = compiler.DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
x.pluginRoot, err = compiler.DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
if err != nil {
x.t.Error(err)
}
x.compiler = compiler.NewCompiler(x.srcRoot, x.workDir)
x.compiler = compiler.NewCompiler(x.pluginRoot)
x.setEnv()
return x
}
// reset restores the environment to pre-test state.
func (x *pluginTestEnv) reset() {
// Calling Cleanup forces recompilation in a test file with multiple
// calls to MakeEnhancedHarness - so leaving it out. Your .gitignore
// should ignore .so files anyway.
// x.compiler.Cleanup()
x.resetEnv()
x.removeWorkDir()
}
// buildGoPlugin compiles a Go plugin, leaving the newly
// created object code in the right place - a temporary
// working directory pointed to by KustomizePluginHomeEnv.
// This avoids overwriting anything the user/developer has
// otherwise created.
func (x *pluginTestEnv) buildGoPlugin(g, v, k string) {
err := x.compiler.Compile(g, v, k)
// prepareGoPlugin compiles a Go plugin, leaving the newly
// created object code alongside the src code.
func (x *pluginTestEnv) prepareGoPlugin(g, v, k string) {
x.compiler.SetGVK(g, v, k)
err := x.compiler.Compile()
if err != nil {
x.t.Errorf("compile failed: %v", err)
}
}
// prepExecPlugin copies an exec plugin from it's
// home in the discovered srcRoot to the same temp
// directory where Go plugin object code is placed.
// Kustomize (and its tests) expect to find plugins
// (Go or Exec) in the same spot, and since the test
// framework is compiling Go plugins to a temp dir,
// it must likewise copy Exec plugins to that same
// temp dir.
func (x *pluginTestEnv) prepExecPlugin(g, v, k string) {
lowK := strings.ToLower(k)
src := filepath.Join(x.srcRoot, g, v, lowK, k)
tmp := filepath.Join(x.workDir, g, v, lowK, k)
if err := os.MkdirAll(filepath.Dir(tmp), 0755); err != nil {
x.t.Errorf("error making directory: %s", filepath.Dir(tmp))
}
cmd := exec.Command("cp", src, tmp)
cmd.Env = os.Environ()
if err := cmd.Run(); err != nil {
x.t.Errorf("error copying %s to %s: %v", src, tmp, err)
}
}
func (x *pluginTestEnv) createWorkDir() {
var err error
x.workDir, err = ioutil.TempDir("", "kustomize-plugin-tests")
if err != nil {
x.t.Errorf("failed to make work dir: %v", err)
}
}
func (x *pluginTestEnv) removeWorkDir() {
err := os.RemoveAll(x.workDir)
if err != nil {
x.t.Errorf(
"removing work dir: %s %v", x.workDir, err)
}
func (x *pluginTestEnv) prepareExecPlugin(_, _, _ string) {
// Do nothing. At one point this method
// copied the exec plugin directory to a temp dir
// and ran it from there. Left as a hook.
}
func (x *pluginTestEnv) setEnv() {
x.oldXdg, x.wasSet = os.LookupEnv(konfig.KustomizePluginHomeEnv)
os.Setenv(konfig.KustomizePluginHomeEnv, x.workDir)
os.Setenv(konfig.KustomizePluginHomeEnv, x.pluginRoot)
}
func (x *pluginTestEnv) resetEnv() {

View File

@@ -1,64 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform
import (
"errors"
"fmt"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/types"
)
// mapTransformer applies a string->string map to fieldSpecs.
type mapTransformer struct {
m map[string]string
fieldSpecs []types.FieldSpec
}
var _ resmap.Transformer = &mapTransformer{}
// NewMapTransformer construct a mapTransformer.
func NewMapTransformer(
pc []types.FieldSpec, m map[string]string) (resmap.Transformer, error) {
if m == nil {
return newNoOpTransformer(), nil
}
if pc == nil {
return nil, errors.New("fieldSpecs is not expected to be nil")
}
return &mapTransformer{fieldSpecs: pc, m: m}, nil
}
// Transform apply each <key, value> pair in the mapTransformer to the
// fields specified in mapTransformer.
func (o *mapTransformer) Transform(m resmap.ResMap) error {
for _, r := range m.Resources() {
for _, path := range o.fieldSpecs {
if !r.OrgId().IsSelected(&path.Gvk) {
continue
}
err := MutateField(
r.Map(), path.PathSlice(),
path.CreateIfNotPresent, o.addMap)
if err != nil {
return err
}
}
}
return nil
}
func (o *mapTransformer) addMap(in interface{}) (interface{}, error) {
m, ok := in.(map[string]interface{})
if in == nil {
m = map[string]interface{}{}
} else if !ok {
return nil, fmt.Errorf("%#v is expected to be %T", in, m)
}
for k, v := range o.m {
m[k] = v
}
return m, nil
}

View File

@@ -1,570 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform_test
import (
"testing"
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
"sigs.k8s.io/kustomize/api/resource"
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
. "sigs.k8s.io/kustomize/api/transform"
)
var resourceFactory = resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
func TestLabelsRun(t *testing.T) {
m := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
},
}).
Add(map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"old-label": "old-value",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
},
"spec": map[string]interface{}{
"ports": []interface{}{
map[string]interface{}{
"name": "port1",
"port": "12345",
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": map[string]interface{}{
"name": "job1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": map[string]interface{}{
"name": "job2",
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"old-label": "old-value",
},
},
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1beta1",
"kind": "CronJob",
"metadata": map[string]interface{}{
"name": "cronjob1",
},
"spec": map[string]interface{}{
"schedule": "* 23 * * *",
"jobTemplate": map[string]interface{}{
"spec": map[string]interface{}{
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1beta1",
"kind": "CronJob",
"metadata": map[string]interface{}{
"name": "cronjob2",
},
"spec": map[string]interface{}{
"schedule": "* 23 * * *",
"jobTemplate": map[string]interface{}{
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"old-label": "old-value",
},
},
"template": map[string]interface{}{
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
},
},
}).ResMap()
expected := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
}).
Add(map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"old-label": "old-value",
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"ports": []interface{}{
map[string]interface{}{
"name": "port1",
"port": "12345",
},
},
"selector": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": map[string]interface{}{
"name": "job1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1",
"kind": "Job",
"metadata": map[string]interface{}{
"name": "job2",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
"old-label": "old-value",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1beta1",
"kind": "CronJob",
"metadata": map[string]interface{}{
"name": "cronjob1",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"schedule": "* 23 * * *",
"jobTemplate": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "batch/v1beta1",
"kind": "CronJob",
"metadata": map[string]interface{}{
"name": "cronjob2",
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"schedule": "* 23 * * *",
"jobTemplate": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"selector": map[string]interface{}{
"matchLabels": map[string]interface{}{
"old-label": "old-value",
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"label-key1": "label-value1",
"label-key2": "label-value2",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
},
},
}).ResMap()
lt, err := NewMapTransformer(
builtinconfig.MakeDefaultConfig().CommonLabels,
map[string]string{"label-key1": "label-value1", "label-key2": "label-value2"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = lt.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}
func TestAnnotationsRun(t *testing.T) {
m := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
},
}).
Add(map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"old-label": "old-value",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
},
"spec": map[string]interface{}{
"ports": []interface{}{
map[string]interface{}{
"name": "port1",
"port": "12345",
},
},
},
}).ResMap()
expected := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
},
}).
Add(map[string]interface{}{
"group": "apps",
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "deploy1",
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
"labels": map[string]interface{}{
"old-label": "old-value",
},
},
"spec": map[string]interface{}{
"containers": []interface{}{
map[string]interface{}{
"name": "nginx",
"image": "nginx:1.7.9",
},
},
},
},
},
}).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "Service",
"metadata": map[string]interface{}{
"name": "svc1",
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
},
"spec": map[string]interface{}{
"ports": []interface{}{
map[string]interface{}{
"name": "port1",
"port": "12345",
},
},
},
}).ResMap()
at, err := NewMapTransformer(
builtinconfig.MakeDefaultConfig().CommonAnnotations,
map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = at.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}
func TestAnnotationsRunWithNullValue(t *testing.T) {
m := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
"annotations": nil,
},
}).ResMap()
expected := resmaptest_test.NewRmBuilder(t, resourceFactory).
Add(map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "cm1",
"annotations": map[string]interface{}{
"anno-key1": "anno-value1",
"anno-key2": "anno-value2",
},
},
}).ResMap()
at, err := NewMapTransformer(
builtinconfig.MakeDefaultConfig().CommonAnnotations,
map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = at.Transform(m)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err = expected.ErrorIfNotEqualLists(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err)
}
}

View File

@@ -1,21 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package transform
import "sigs.k8s.io/kustomize/api/resmap"
// noOpTransformer contains a no-op transformer.
type noOpTransformer struct{}
var _ resmap.Transformer = &noOpTransformer{}
// newNoOpTransformer constructs a noOpTransformer.
func newNoOpTransformer() resmap.Transformer {
return &noOpTransformer{}
}
// Transform does nothing.
func (o *noOpTransformer) Transform(_ resmap.ResMap) error {
return nil
}

View File

@@ -1,12 +0,0 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package types
//go:generate stringer -type=GarbagePolicy
type GarbagePolicy int
const (
GarbageIgnore GarbagePolicy = iota + 1
GarbageCollect
)

View File

@@ -1,25 +0,0 @@
// Code generated by "stringer -type=GarbagePolicy"; DO NOT EDIT.
package types
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[GarbageIgnore-1]
_ = x[GarbageCollect-2]
}
const _GarbagePolicy_name = "GarbageIgnoreGarbageCollect"
var _GarbagePolicy_index = [...]uint8{0, 13, 27}
func (i GarbagePolicy) String() string {
i -= 1
if i < 0 || i >= GarbagePolicy(len(_GarbagePolicy_index)-1) {
return "GarbagePolicy(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _GarbagePolicy_name[_GarbagePolicy_index[i]:_GarbagePolicy_index[i+1]]
}

View File

@@ -10,22 +10,23 @@ type PluginConfig struct {
// containing the fields 'apiVersion' and 'kind', e.g.
// apiVersion: apps/v1
// kind: Deployment
// When kustomize reads a plugin configuration file (as as result
// of seeing the file name in the 'generators:' or 'transformers:'
// field in a kustomization file), it must then locate the plugin
// code (Go plugin or exec plugin).
// Every kustomize plugin (its code, its tests, supporting data
// kustomize reads plugin configuration data from a file path
// specified in the 'generators:' or 'transformers:' field of a
// kustomization file. kustomize must then use this data to both
// locate the plugin and configure it.
// Every kustomize plugin (its code, its tests, its supporting data
// files, etc.) must be housed in its own directory at
// ${AbsPluginHome}/${pluginApiVersion}/LOWERCASE(${pluginKind})
// where
// - ${AbsPluginHome} is an absolute path, defined below.
// - ${pluginApiVersion} is taken from the plugin config file.
// - ${pluginKind} is taken from the plugin config file.
// The value of AbsPluginHome can be any absolute path, but might
// default to $XDG_CONFIG_HOME/kustomize/plugin.
// The value of AbsPluginHome can be any absolute path.
AbsPluginHome string
// PluginRestrictions defines the plugin restriction state.
// See type for more information.
// PluginRestrictions distinguishes plugin restrictions.
PluginRestrictions PluginRestrictions
// BpLoadingOptions distinguishes builtin plugin behaviors.
BpLoadingOptions BuiltinPluginLoadingOptions
}

View File

@@ -26,3 +26,18 @@ const (
// No restrictions, do whatever you want.
PluginRestrictionsNone
)
// BuiltinPluginLoadingOptions distinguish ways in which builtin plugins are used.
//go:generate stringer -type=BuiltinPluginLoadingOptions
type BuiltinPluginLoadingOptions int
const (
BploUndefined BuiltinPluginLoadingOptions = iota
// Desired in production use for performance.
BploUseStaticallyLinked
// Desired in testing and development cycles where it's undesirable
// to generate static code.
BploLoadFromFileSys
)

View File

@@ -19,7 +19,7 @@ linters:
# - gochecknoinits
# - goconst
# - gocritic
- gocyclo
# - gocyclo
- gofmt
- goimports
# - golint

View File

@@ -32,7 +32,7 @@ lint:
$(GOBIN)/golangci-lint run ./...
test:
go test -cover ./...
go test -v -timeout 45m -cover ./...
vet:
go vet ./...

View File

@@ -77,8 +77,7 @@ An example using `v1/List` as input:
apiVersion: v1
kind: List
items:
spec:
- apiVersion: foo-corp.com/v1
- apiVersion: foo-corp.com/v1
kind: FulfillmentCenter
metadata:
name: staging

View File

@@ -6,11 +6,9 @@ require (
github.com/go-errors/errors v1.0.1
github.com/olekukonko/tablewriter v0.0.4
github.com/posener/complete/v2 v2.0.1-alpha.12
github.com/spf13/cobra v0.0.5
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.4.0
k8s.io/apimachinery v0.17.0
sigs.k8s.io/kustomize/kyaml v0.0.0
sigs.k8s.io/kustomize/kyaml v0.1.9
)
replace sigs.k8s.io/kustomize/kyaml v0.0.0 => ../../kyaml

View File

@@ -1,6 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/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/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
@@ -8,19 +10,29 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
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/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/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
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/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
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/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
@@ -28,8 +40,12 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
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-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
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=
@@ -45,18 +61,31 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp
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/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/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/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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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/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/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/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-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/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
@@ -66,10 +95,15 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/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/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/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=
@@ -82,6 +116,7 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
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/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -91,7 +126,9 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/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/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -101,6 +138,7 @@ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGV
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.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=
@@ -109,50 +147,84 @@ github.com/posener/complete/v2 v2.0.1-alpha.12 h1:0wvkuDfHb5vSZlNBYgpEH4XQHpF46M
github.com/posener/complete/v2 v2.0.1-alpha.12/go.mod h1://JlL91cS2JV7rOl6LVHrRqBXoBUecJu3ILQPgbJiMQ=
github.com/posener/script v1.0.4 h1:nSuXW5ZdmFnQIueLB2s0qvs4oNsUloM1Zydzh75v42w=
github.com/posener/script v1.0.4/go.mod h1:Rg3ijooqulo05aGLyGsHoLmIOUzHUVK19WVgrYBPU/E=
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/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d h1:K6eOUihrFLdZjZnA4XlRp864fmWXv9YTIk7VPLhRacA=
github.com/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
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/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
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/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/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/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/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.2.3-0.20181224173747-660f15d67dbb/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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
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.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
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/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-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-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-20190311183353-d8887717615a/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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 h1:rjwSpXsdiK0dV8/Naq3kAw9ymfAeJIyd0upUIElB+lI=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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/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/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-20181205085412-a5c9d58dba9a/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -160,9 +232,18 @@ golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fq
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
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=
@@ -170,7 +251,9 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
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/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=
@@ -179,11 +262,14 @@ gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/apimachinery v0.17.0 h1:xRBnuie9rXcPxUkDizUsGvPf1cnlZCFu210op7J7LJo=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
sigs.k8s.io/kustomize/kyaml v0.1.9 h1:bNgeeRwTtYay3sQVkBajTuPH5WeyuXXE8sPsdOGBOog=
sigs.k8s.io/kustomize/kyaml v0.1.9/go.mod h1:mPmeBSRy0LTMv6fSrYSoi2yIFNZVouGKDsTekE5kdhs=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -4,6 +4,7 @@
package commands
import (
"regexp"
"strings"
"github.com/spf13/cobra"
@@ -17,20 +18,19 @@ import (
func NewCreateSubstitutionRunner(parent string) *CreateSubstitutionRunner {
r := &CreateSubstitutionRunner{}
cs := &cobra.Command{
Use: "create-subst DIR NAME VALUE",
Args: cobra.ExactArgs(3),
Use: "create-subst DIR NAME",
Args: cobra.ExactArgs(2),
PreRunE: r.preRunE,
RunE: r.runE,
}
cs.Flags().StringVar(&r.CreateSubstitution.FieldName, "field", "",
"name of the field to set -- e.g. --field port")
"name of the field to set -- e.g. --field image")
cs.Flags().StringVar(&r.CreateSubstitution.FieldValue, "field-value", "",
"value of the field to create substitution for -- e.g. --field-value nginx:0.1.0")
cs.Flags().StringVar(&r.CreateSubstitution.Pattern, "pattern", "",
"substitution pattern")
cs.Flags().StringSliceVar(&r.Values, "value", []string{""},
"substitution values for the pattern. format is PATTERN_MARKER=SETTER_NAME"+
"where PATTERN_MARKER is the pattern substring to replace, and SETTER_NAME is the"+
"setter from which to take the replacement value.")
`substitution pattern -- e.g. --pattern \${my-image-setter}:\${my-tag-setter}`)
_ = cs.MarkFlagRequired("pattern")
_ = cs.MarkFlagRequired("field-value")
fixDocs(parent, cs)
r.Command = cs
return r
@@ -54,7 +54,6 @@ func (r *CreateSubstitutionRunner) runE(c *cobra.Command, args []string) error {
func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) error {
var err error
r.CreateSubstitution.Name = args[1]
r.CreateSubstitution.FieldValue = args[2]
if err != nil {
return err
}
@@ -64,16 +63,20 @@ func (r *CreateSubstitutionRunner) preRunE(c *cobra.Command, args []string) erro
return err
}
// parse the marker values
for i := range r.Values {
parts := strings.SplitN(r.Values[i], "=", 2)
if len(parts) < 2 {
return errors.Errorf("values must be specified as PATTERN_MARKER=SETTER_NAME")
}
ref := setters2.DefinitionsPrefix + setters2.SetterDefinitionPrefix + parts[1]
// extract setter name tokens from pattern enclosed in ${}
re := regexp.MustCompile(`\$\{([^}]*)\}`)
markers := re.FindAll([]byte(r.CreateSubstitution.Pattern), -1)
if len(markers) == 0 {
return errors.Errorf("unable to find setter names in pattern, " +
"setter names must be enclosed in ${}")
}
for _, marker := range markers {
ref := setters2.DefinitionsPrefix + setters2.SetterDefinitionPrefix +
strings.TrimSuffix(strings.TrimPrefix(string(marker), "${"), "}")
r.CreateSubstitution.Values = append(
r.CreateSubstitution.Values,
setters2.Value{Marker: parts[0], Ref: ref},
setters2.Value{Marker: string(marker), Ref: ref},
)
}

View File

@@ -29,8 +29,7 @@ func TestCreateSubstitutionCommand(t *testing.T) {
{
name: "substitution replicas",
args: []string{
"image", "nginx:1.7.9", "--pattern", "IMAGE:TAG",
"--value", "IMAGE=image", "--value", "TAG=tag"},
"my-image-subst", "--field-value", "nginx:1.7.9", "--pattern", "${my-image-setter}:${my-tag-setter}"},
input: `
apiVersion: apps/v1
kind: Deployment
@@ -51,15 +50,15 @@ apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.image:
io.k8s.cli.setters.my-image-setter:
x-k8s-cli:
setter:
name: image
name: my-image-setter
value: "nginx"
io.k8s.cli.setters.tag:
io.k8s.cli.setters.my-tag-setter:
x-k8s-cli:
setter:
name: tag
name: my-tag-setter
value: "1.7.9"
`,
expectedOpenAPI: `
@@ -67,26 +66,26 @@ apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.image:
io.k8s.cli.setters.my-image-setter:
x-k8s-cli:
setter:
name: image
name: my-image-setter
value: "nginx"
io.k8s.cli.setters.tag:
io.k8s.cli.setters.my-tag-setter:
x-k8s-cli:
setter:
name: tag
name: my-tag-setter
value: "1.7.9"
io.k8s.cli.substitutions.image:
io.k8s.cli.substitutions.my-image-subst:
x-k8s-cli:
substitution:
name: image
pattern: IMAGE:TAG
name: my-image-subst
pattern: ${my-image-setter}:${my-tag-setter}
values:
- marker: IMAGE
ref: '#/definitions/io.k8s.cli.setters.image'
- marker: TAG
ref: '#/definitions/io.k8s.cli.setters.tag'
- marker: ${my-image-setter}
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
- marker: ${my-tag-setter}
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
`,
expectedResources: `
apiVersion: apps/v1
@@ -99,7 +98,7 @@ spec:
spec:
containers:
- name: nginx
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
image: nginx:1.7.9 # {"$ref":"#/definitions/io.k8s.cli.substitutions.my-image-subst"}
- name: sidecar
image: sidecar:1.7.9
`,
@@ -107,8 +106,7 @@ spec:
{
name: "substitution and create setters 1",
args: []string{
"image", "something/nginx::1.7.9/nginxotherthing", "--pattern", "something/IMAGE::TAG/nginxotherthing",
"--value", "IMAGE=image", "--value", "TAG=tag"},
"my-image-subst", "--field-value", "something/nginx::1.7.9/nginxotherthing", "--pattern", "something/${my-image-setter}::${my-tag-setter}/nginxotherthing"},
input: `
apiVersion: apps/v1
kind: Deployment
@@ -133,26 +131,26 @@ apiVersion: v1alpha1
kind: Example
openAPI:
definitions:
io.k8s.cli.setters.image:
io.k8s.cli.setters.my-image-setter:
x-k8s-cli:
setter:
name: image
name: my-image-setter
value: nginx
io.k8s.cli.setters.tag:
io.k8s.cli.setters.my-tag-setter:
x-k8s-cli:
setter:
name: tag
name: my-tag-setter
value: 1.7.9
io.k8s.cli.substitutions.image:
io.k8s.cli.substitutions.my-image-subst:
x-k8s-cli:
substitution:
name: image
pattern: something/IMAGE::TAG/nginxotherthing
name: my-image-subst
pattern: something/${my-image-setter}::${my-tag-setter}/nginxotherthing
values:
- marker: IMAGE
ref: '#/definitions/io.k8s.cli.setters.image'
- marker: TAG
ref: '#/definitions/io.k8s.cli.setters.tag'
- marker: ${my-image-setter}
ref: '#/definitions/io.k8s.cli.setters.my-image-setter'
- marker: ${my-tag-setter}
ref: '#/definitions/io.k8s.cli.setters.my-tag-setter'
`,
expectedResources: `
apiVersion: apps/v1
@@ -165,7 +163,7 @@ spec:
spec:
containers:
- name: nginx
image: something/nginx::1.7.9/nginxotherthing # {"$ref":"#/definitions/io.k8s.cli.substitutions.image"}
image: something/nginx::1.7.9/nginxotherthing # {"$ref":"#/definitions/io.k8s.cli.substitutions.my-image-subst"}
- name: sidecar
image: sidecar:1.7.9
`,

View File

@@ -0,0 +1,661 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package e2e
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRunE2e(t *testing.T) {
binDir, err := ioutil.TempDir("", "kustomize-test-")
if !assert.NoError(t, err) {
t.FailNow()
}
//defer os.RemoveAll(binDir)
build(t, binDir)
tests := []struct {
name string
args func(string) []string
files func(string) map[string]string
expectedFiles func(string) map[string]string
expectedErr string
skipIfFalseEnv string
}{
{
name: "exec_function_no_args",
args: func(d string) []string {
return []string{
"--enable-exec", "--exec-path", filepath.Join(d, "e2econtainerconfig"),
}
},
files: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-string-value: ''
a-int-value: '0'
a-bool-value: 'false'
`,
}
},
},
{
name: "exec_function_args",
args: func(d string) []string {
return []string{
"--enable-exec", "--exec-path", filepath.Join(d, "e2econtainerconfig"),
"--", "stringValue=a", "intValue=1", "boolValue=true",
}
},
files: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-string-value: 'a'
a-int-value: '1'
a-bool-value: 'true'
`,
}
},
},
{
name: "exec_function_config",
args: func(d string) []string {
return []string{"--enable-exec"}
},
files: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": fmt.Sprintf(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: "%s"
`, filepath.Join(d, "e2econtainerconfig")),
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": fmt.Sprintf(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: "%s"
a-string-value: ''
a-int-value: '0'
a-bool-value: 'false'
`, filepath.Join(d, "e2econtainerconfig"))}
},
},
//
// Starklark function tests
//
{
name: "exec_function_config",
args: func(d string) []string {
return []string{"--enable-exec"}
},
files: func(d string) map[string]string {
return map[string]string{
"config.yaml": fmt.Sprintf(`
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: "%s"
data:
stringValue: a
intValue: 2
boolValue: true
`, filepath.Join(d, "e2econtainerconfig")),
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"config.yaml": fmt.Sprintf(`
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: "%s"
a-string-value: 'a'
a-int-value: '2'
a-bool-value: 'true'
data:
stringValue: a
intValue: 2
boolValue: true
`, filepath.Join(d, "e2econtainerconfig")),
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-string-value: 'a'
a-int-value: '2'
a-bool-value: 'true'
`,
}
},
},
{
//
// NOTE: Do not change the expected value of this test. It is to ensure that
// exec functions are off by default when run from the CLI.
// exec functions execute arbitrary code outside of a sandbox environment.
//
name: "exec_function_config_disabled",
args: func(d string) []string { return []string{} },
files: func(d string) map[string]string {
return map[string]string{
"config.yaml": fmt.Sprintf(`
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: "%s"
data:
stringValue: a
intValue: 2
boolValue: true
`, filepath.Join(d, "e2econtainerconfig")),
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"config.yaml": fmt.Sprintf(`
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
exec:
path: "%s"
data:
stringValue: a
intValue: 2
boolValue: true
`, filepath.Join(d, "e2econtainerconfig")),
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
},
{
name: "exec_function_no_enable",
expectedErr: "must specify --enable-exec with --exec-path",
args: func(d string) []string {
return []string{"--exec-path", filepath.Join(d, "e2econtainerconfig")}
},
files: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-string-value: ''
a-int-value: '0'
a-bool-value: 'false'
`,
}
},
},
//
// Container
//
{
name: "container_function_no_args",
skipIfFalseEnv: "KUSTOMIZE_DOCKER_E2E",
args: func(d string) []string {
return []string{"--image", "gcr.io/kustomize-functions/e2econtainerconfig"}
},
files: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-string-value: ''
a-int-value: '0'
a-bool-value: 'false'
`,
}
},
},
{
name: "container_function_args",
skipIfFalseEnv: "KUSTOMIZE_DOCKER_E2E",
args: func(d string) []string {
return []string{
"--image", "gcr.io/kustomize-functions/e2econtainerconfig",
"--", "stringValue=a", "intValue=1", "boolValue=true",
}
},
files: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-string-value: 'a'
a-int-value: '1'
a-bool-value: 'true'
`,
}
},
},
{
name: "container_function_config",
skipIfFalseEnv: "KUSTOMIZE_DOCKER_E2E",
args: func(d string) []string { return []string{} },
files: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: "gcr.io/kustomize-functions/e2econtainerconfig"
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: "gcr.io/kustomize-functions/e2econtainerconfig"
a-string-value: ''
a-int-value: '0'
a-bool-value: 'false'
`}
},
},
{
name: "container_function_config",
skipIfFalseEnv: "KUSTOMIZE_DOCKER_E2E",
args: func(d string) []string { return []string{} },
files: func(d string) map[string]string {
return map[string]string{
"config.yaml": `
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: "gcr.io/kustomize-functions/e2econtainerconfig"
data:
stringValue: a
intValue: 2
boolValue: true
`,
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"config.yaml": `
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
container:
image: "gcr.io/kustomize-functions/e2econtainerconfig"
a-string-value: 'a'
a-int-value: '2'
a-bool-value: 'true'
data:
stringValue: a
intValue: 2
boolValue: true
`,
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-string-value: 'a'
a-int-value: '2'
a-bool-value: 'true'
`,
}
},
},
{
name: "starlark_function_config",
args: func(d string) []string { return []string{"--enable-star"} },
files: func(d string) map[string]string {
return map[string]string{
"script.star": `
# set the foo annotation on each resource
def run(r, fc):
for resource in r:
resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"]
resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"]
resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"]
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"])
`,
"config.yaml": `
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
config.kubernetes.io/function: |
starlark:
path: script.star
name: fn
data:
boolValue: true
intValue: 2
stringValue: a
`,
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"config.yaml": `
apiVersion: example.com/v1alpha1
kind: Input
metadata:
name: foo
annotations:
a-bool-value: true
a-int-value: 2
a-string-value: a
config.kubernetes.io/function: |
starlark:
path: script.star
name: fn
data:
boolValue: true
intValue: 2
stringValue: a
`,
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-bool-value: true
a-int-value: 2
a-string-value: a
`,
}
},
},
{
name: "starlark_function_path",
args: func(d string) []string {
return []string{
"--enable-star", "--star-path", "script.star",
"--", "stringValue=a", "intValue=2", "boolValue=true",
}
},
files: func(d string) map[string]string {
return map[string]string{
"script.star": `
# set the foo annotation on each resource
def run(r, fc):
for resource in r:
resource["metadata"]["annotations"]["a-string-value"] = fc["data"]["stringValue"]
resource["metadata"]["annotations"]["a-int-value"] = fc["data"]["intValue"]
resource["metadata"]["annotations"]["a-bool-value"] = fc["data"]["boolValue"]
run(ctx.resource_list["items"], ctx.resource_list["functionConfig"])
`,
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
}
},
expectedFiles: func(d string) map[string]string {
return map[string]string{
"deployment.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
annotations:
a-bool-value: true
a-int-value: 2
a-string-value: a
`,
}
},
},
}
for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
if tt.skipIfFalseEnv != "" && os.Getenv(tt.skipIfFalseEnv) == "false" {
t.Skip()
}
dir, err := ioutil.TempDir("", "kustomize-test-data-")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.RemoveAll(dir)
os.Chdir(dir)
// write the input
for path, data := range tt.files(binDir) {
err := ioutil.WriteFile(path, []byte(data), 0600)
if !assert.NoError(t, err) {
t.FailNow()
}
}
args := append([]string{"run", "."}, tt.args(binDir)...)
cmd := exec.Command(filepath.Join(binDir, "kyaml"), args...)
cmd.Dir = dir
var stdErr, stdOut bytes.Buffer
cmd.Stdout = &stdOut
cmd.Stderr = &stdErr
cmd.Env = os.Environ()
err = cmd.Run()
if tt.expectedErr != "" {
if !assert.Contains(t, stdErr.String(), tt.expectedErr) {
t.FailNow()
}
return
}
if !assert.NoError(t, err, stdErr.String()) {
t.FailNow()
}
for path, data := range tt.expectedFiles(binDir) {
b, err := ioutil.ReadFile(path)
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, strings.TrimSpace(data), strings.TrimSpace(string(b))) {
t.FailNow()
}
}
})
}
}
func build(t *testing.T, binDir string) {
build := exec.Command("go", "build", "-o",
filepath.Join(binDir, "e2econtainerconfig"))
build.Dir = "e2econtainerconfig"
build.Stdout = os.Stdout
build.Stderr = os.Stderr
if !assert.NoError(t, build.Run()) {
t.FailNow()
}
build = exec.Command("go", "build", "-o", filepath.Join(binDir, "kyaml"))
build.Dir = filepath.Join("..", "..", "..")
build.Stdout = os.Stdout
build.Stderr = os.Stderr
if !assert.NoError(t, build.Run()) {
t.FailNow()
}
if os.Getenv("KUSTOMIZE_DOCKER_E2E") == "false" {
return
}
build = exec.Command(
"docker", "build", ".", "-t", "gcr.io/kustomize-functions/e2econtainerconfig")
build.Dir = "e2econtainerconfig"
build.Stdout = os.Stdout
build.Stderr = os.Stderr
if !assert.NoError(t, build.Run()) {
t.FailNow()
}
}

View File

@@ -0,0 +1,12 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.13-stretch
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY . .
RUN go build -v -o /usr/local/bin/function ./
FROM alpine:latest
COPY --from=0 /usr/local/bin/function /usr/local/bin/function
CMD ["function"]

View File

@@ -0,0 +1,13 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
.PHONY: generate license fix vet fmt test build tidy
GOBIN := $(shell go env GOPATH)/bin
test:
go test
image:
docker build . -t gcr.io/kustomize-functions/e2econtainerconfig
docker push gcr.io/kustomize-functions/e2econtainerconfig

View File

@@ -0,0 +1,9 @@
// Package main contains a function to be used for e2e testing.
//
// The function is written using the framework, and parses the ResourceList.functionConfig
// into a go struct.
//
// The function will set 3 annotations on each resource.
//
// See https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
package main

View File

@@ -0,0 +1,6 @@
module sigs.k8s.io/kustomize/cmd/config/internal/commands/e2e/e2econtainerconfig
go 1.14
require sigs.k8s.io/kustomize/kyaml v0.1.9-0.20200501190629-f7909fad7167

View File

@@ -0,0 +1,183 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/PuerkitoBio/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-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/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/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
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-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/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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/dustmop/soup v1.1.2-0.20190516214245-38228baa104e/go.mod h1:CgNC6SGbT+Xb8wGGvzilttZL1mc5sQ/5KkcxsZttMIk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
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.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
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/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/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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
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/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
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/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/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu//Vk=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/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/qri-io/starlib v0.4.2-0.20200213133954-ff2e8cd5ef8d/go.mod h1:7DPO4domFU579Ga6E61sB9VFNaniPVwJP5C4bBCu3wA=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
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/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
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/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
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.starlark.net v0.0.0-20190528202925-30ae18b8564f/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
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/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-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-20190311183353-d8887717615a/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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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/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/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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2 h1:XZx7nhd5GMaZpmDaEHFVafUZC7ya0fuo7cSJ3UCKYmM=
gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/kustomize/kyaml v0.1.9-0.20200501190629-f7909fad7167 h1:138Q3rZVU5mLBF6dFekNuP6D4ZeF4mndI54RBJ8kK8c=
sigs.k8s.io/kustomize/kyaml v0.1.9-0.20200501190629-f7909fad7167/go.mod h1:I4OFZ1vTPdteiqqCBwW3DI0swPzxBpd99y9CHN5IMUU=

View File

@@ -0,0 +1,57 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"fmt"
"os"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Data contains the items
type Data struct {
StringValue string `yaml:"stringValue,omitempty"`
IntValue int `yaml:"intValue,omitempty"`
BoolValue bool `yaml:"boolValue,omitempty"`
}
// Example defines the ResourceList.functionConfig schema.
type Example struct {
// Data contains configuration data for the Example
// Nest values under Data so that the function can accept a ConfigMap as its
// functionConfig (`run` generates a ConfigMap for the functionConfig when run with --)
Data Data `yaml:"data,omitempty"`
}
func main() {
functionConfig := &Example{}
cmd := framework.Command(functionConfig, func(items []*yaml.RNode) ([]*yaml.RNode, error) {
for i := range items {
if err := items[i].PipeE(yaml.SetAnnotation("a-string-value",
functionConfig.Data.StringValue)); err != nil {
return nil, err
}
if err := items[i].PipeE(yaml.SetAnnotation("a-int-value",
fmt.Sprintf("%v", functionConfig.Data.IntValue))); err != nil {
return nil, err
}
if err := items[i].PipeE(yaml.SetAnnotation("a-bool-value",
fmt.Sprintf("%v", functionConfig.Data.BoolValue))); err != nil {
return nil, err
}
}
return items, nil
})
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}

View File

@@ -11,7 +11,7 @@ import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/cmd/config/internal/generateddocs/commands"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio/filters"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/runfn"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -41,15 +41,17 @@ func GetRunFnRunner(name string) *RunFnRunner {
r.Command.Flags().StringVar(
&r.Image, "image", "",
"run this image as a function instead of discovering them.")
// NOTE: exec plugins execute arbitrary code -- never change the default value of this flag!!!
r.Command.Flags().BoolVar(
&r.EnableStar, "enable-star", false, "enable support for starlark functions.")
r.Command.Flags().MarkHidden("enable-star")
&r.EnableExec, "enable-exec", false /*do not change!*/, "enable support for exec functions -- note: exec functions run arbitrary code -- do not use for untrusted configs!!! (Alpha)")
r.Command.Flags().StringVar(
&r.StarPath, "star-path", "", "run a starlark script as a function.")
r.Command.Flags().MarkHidden("star-path")
&r.ExecPath, "exec-path", "", "run an executable as a function. (Alpha)")
r.Command.Flags().BoolVar(
&r.EnableStar, "enable-star", false, "enable support for starlark functions. (Alpha)")
r.Command.Flags().StringVar(
&r.StarName, "star-name", "", "name of starlark program.")
r.Command.Flags().MarkHidden("star-name")
&r.StarPath, "star-path", "", "run a starlark script as a function. (Alpha)")
r.Command.Flags().StringVar(
&r.StarName, "star-name", "", "name of starlark program. (Alpha)")
r.Command.Flags().StringVar(
&r.ResultsDir, "results-dir", "", "write function results to this dir")
@@ -79,6 +81,8 @@ type RunFnRunner struct {
EnableStar bool
StarPath string
StarName string
EnableExec bool
ExecPath string
RunFns runfn.RunFns
ResultsDir string
Network bool
@@ -94,14 +98,13 @@ func (r *RunFnRunner) runE(c *cobra.Command, args []string) error {
// Functions to run.
func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []string) (
[]*yaml.RNode, error) {
if r.Image == "" && r.StarPath == "" {
if r.Image == "" && r.StarPath == "" && r.ExecPath == "" {
return nil, nil
}
var fn *yaml.RNode
var err error
// if image isn't specified, then Functions is empty
if r.Image != "" {
// create the function spec to set as an annotation
fn, err = yaml.Parse(`container: {}`)
@@ -143,8 +146,19 @@ func (r *RunFnRunner) getContainerFunctions(c *cobra.Command, args, dataItems []
if err != nil {
return nil, err
}
} else {
return nil, nil
} else if r.EnableExec && r.ExecPath != "" {
// create the function spec to set as an annotation
fn, err = yaml.Parse(`exec: {}`)
if err != nil {
return nil, err
}
err = fn.PipeE(
yaml.Lookup("exec"),
yaml.SetField("path", yaml.NewScalarRNode(r.ExecPath)))
if err != nil {
return nil, err
}
}
// create the function config
@@ -208,21 +222,25 @@ data: {}
return []*yaml.RNode{rc}, nil
}
func toStorageMounts(mounts []string) []filters.StorageMount {
var sms []filters.StorageMount
func toStorageMounts(mounts []string) []runtimeutil.StorageMount {
var sms []runtimeutil.StorageMount
for _, mount := range mounts {
sms = append(sms, filters.StringToStorageMount(mount))
sms = append(sms, runtimeutil.StringToStorageMount(mount))
}
return sms
}
func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
if r.EnableStar != (r.StarPath != "") {
return errors.Errorf("must specify --star-path with --enable-star")
if !r.EnableStar && r.StarPath != "" {
return errors.Errorf("must specify --enable-star with --star-path")
}
if !r.EnableExec && r.ExecPath != "" {
return errors.Errorf("must specify --enable-exec with --exec-path")
}
if c.ArgsLenAtDash() >= 0 && r.Image == "" &&
!(r.EnableStar && r.StarPath != "") {
!(r.EnableStar && r.StarPath != "") && !(r.EnableExec && r.ExecPath != "") {
return errors.Errorf("must specify --image")
}
@@ -270,6 +288,7 @@ func (r *RunFnRunner) preRunE(c *cobra.Command, args []string) error {
Network: r.Network,
NetworkName: r.NetworkName,
EnableStarlark: r.EnableStar,
EnableExec: r.EnableExec,
StorageMounts: storageMounts,
ResultsDir: r.ResultsDir,
}

View File

@@ -183,7 +183,7 @@ apiVersion: v1
"--star-name", "foo",
"--", "Foo", "g=h"},
path: "dir",
err: "must specify --star-path with --enable-star",
err: "must specify --enable-star with --star-path",
},
{
name: "image-star-not-enabled",
@@ -193,7 +193,17 @@ apiVersion: v1
"--star-name", "foo",
"--", "Foo", "g=h"},
path: "dir",
err: "must specify --star-path with --enable-star",
err: "must specify --enable-star with --star-path",
},
{
name: "star-enabled",
args: []string{"run", "dir", "--enable-star"},
path: "dir",
expectedStruct: &runfn.RunFns{
Path: "dir",
NetworkName: "bridge",
EnableStarlark: true,
},
},
{
name: "function paths",

View File

@@ -308,8 +308,7 @@ An example using ` + "`" + `v1/List` + "`" + ` as input:
apiVersion: v1
kind: List
items:
spec:
- apiVersion: foo-corp.com/v1
- apiVersion: foo-corp.com/v1
kind: FulfillmentCenter
metadata:
name: staging

View File

@@ -5,7 +5,7 @@
GOBIN := $(shell go env GOPATH)/bin
all: generate license fix vet fmt test lint tidy
all: generate license fix vet fmt test tidy
fix:
go fix ./...
@@ -33,4 +33,3 @@ test:
vet:
go vet ./...

View File

@@ -47,5 +47,4 @@ Call stack when running `kustomize build`, with links to code.
* The following steps must be done last, not as part of the recursion implicit in AccumulateTarget.
* [addHashesToNames](https://github.com/kubernetes-sigs/kustomize/blob/c7d78970fb86782dbdded3a93944b774f826071f/api/internal/target/kusttarget.go#L153)
* [FixBackReferences](https://github.com/kubernetes-sigs/kustomize/blob/c7d78970fb86782dbdded3a93944b774f826071f/api/internal/accumulator/resaccumulator.go#L160): Given that names have changed (prefixs/suffixes added), fix all the back references to those names.
* [ResolveVars](https://github.com/kubernetes-sigs/kustomize/blob/c7d78970fb86782dbdded3a93944b774f826071f/api/internal/accumulator/resaccumulator.go#L141)
* [computeInventory](https://github.com/kubernetes-sigs/kustomize/blob/c7d78970fb86782dbdded3a93944b774f826071f/api/internal/target/kusttarget.go#L163)
* [ResolveVars](https://github.com/kubernetes-sigs/kustomize/blob/c7d78970fb86782dbdded3a93944b774f826071f/api/internal/accumulator/resaccumulator.go#L141)

View File

@@ -1,4 +1,8 @@
# inventory directive in kustomization.yaml
# [DEPRECATED] inventory directive in kustomization.yaml
**Inventory object has been deprecated in Kustomize. Please use [kpt live apply](https://googlecontainertools.github.io/kpt/reference/live/apply/).**
---
New in v2.1.0, a kustomization file may have an `inventory` field:
```yaml

View File

@@ -19,6 +19,16 @@
## 发行说明
* [kustomize/3.2.2](/../../releases/tag/kustomize%2Fv3.2.2) - 基于 kustomize Go API [3.3.0](../v3.3.0.md) 的 `kustomize` CLI。
* [API 3.3.0](../v3.3.0.md) - kustomize Go API 的首个 release该 release 不包含 `kustomize` CLI且之后 CLI 和 API 将独立发布。
* [kustomize/3.2.1](/../../releases/tag/kustomize%2Fv3.2.1) - `kustomize` CLI 基于 kustomize Go API release [3.2.0](../v3.2.0.md) 发布的补丁。
* [3.2.0](../v3.2.0.md)
* [3.1.1](../v3.1.0.md)
* [3.1](../v3.1.0.md) - 2019年7月下旬扩展 patches 和改进的资源匹配。
* [3.0](../v3.0.0.md) - 2019年6月下旬插件开发者发布。

View File

@@ -1,6 +1,18 @@
# Kustomization 文件字段
介绍 [kustomization](../glossary.md#kustomization) 文件中各字段的含义。
[field-name-namespace]: ../plugins/builtins.md#field-name-namespace
[field-name-images]: ../plugins/builtins.md#field-name-images
[field-names-namePrefix-nameSuffix]: ../plugins/builtins.md#field-names-namePrefix-nameSuffix
[field-name-patches]: ../plugins/builtins.md#field-name-patches
[field-name-patchesStrategicMerge]: ../plugins/builtins.md#field-name-patchesStrategicMerge
[field-name-patchesJson6902]: ../plugins/builtins.md#field-name-patchesJson6902
[field-name-replicas]: ../plugins/builtins.md#field-name-replicas
[field-name-secretGenerator]: ../plugins/builtins.md#field-name-secretGenerator
[field-name-commonLabels]: ../plugins/builtins.md#field-name-commonLabels
[field-name-commonAnnotations]: ../plugins/builtins.md#field-name-commonAnnotations
[field-name-configMapGenerator]: ../plugins/builtins.md#field-name-configMapGenerator
介绍 [kustomization.yaml](../glossary.md#kustomization) 配置文件中各字段的含义。
## Resources
@@ -8,36 +20,36 @@
| 字段 | 类型 | 说明 |
| --- | --- | --- |
|[resources](#resources) | list | 包含 k8s API 对象的文件,或其他包含 kustomizations 文件的目录。 |
|[CRDs](#crds)| list | CDR 文件,允许在资源列表中指定自定义资源。 |
|[resources](#resources) | list | 包含 k8s API 对象的文件,或其他包含 `kustomization.yaml` 文件的目录。 |
|[CRDs](#crds)| list | CDR 文件,允许在资源列表中指定自定义资源。 |
## Generators
生成可定制的对象
资源生成器
| 字段 | 类型 | 说明 |
| --- | --- | --- |
|[configMapGenerator](#configmapgenerator)| list | 列表中的每个条目都将创建一个 ConfigMap 它是n个 ConfigMap 的生成器)。 |
|[secretGenerator](#secretgenerator)| list | 列表中的每个条目都将创建一个 Secret 资源它是nsecrets 的生成器)。 |
|[configMapGenerator](#configmapgenerator)| list | 列表中的每个条目都将生成一个 ConfigMap 合计可以生成 n 个 ConfigMap。 |
|[secretGenerator](#secretgenerator)| list | 列表中的每个条目都将生成一个 Secret(合计可以生成 n Secrets。 |
|[generatorOptions](#generatoroptions)| string | generatorOptions 可以修改所有 ConfigMapGenerator 和 SecretGenerator 的行为。 |
|[generators](#generators)| list | [插件](../plugins)配置文件。 |
## Transformers
可用的转换。
资源字段转换
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| [commonLabels](#commonlabels) | string | 为所有资源和 selectors 增加 Labels 。 |
| [commonAnnotations](#commonannotations) | string | 为所有资源增加 Annotations 。 |
| [images](#images) | list | 修改镜像的名称、tag 或 image digest ,而无需使用 patches 。 |
| [images](#images) | list | 无需使用 patches即可修改镜像的名称、tag 或 image digest。 |
| [inventory](#inventory) | struct | 用于生成一个包含清单信息的对象。 |
| [namespace](#namespace) | string | 为所有 resources 添加 namespace 。 |
| [namePrefix](#nameprefix) | string | 该字段的值将添加在所有资源的名称之前。 |
| [nameSuffix](#namesuffix) | string | 该字段的值将添加在所有资源的名称后面。 |
| [namePrefix](#nameprefix) | string | 所有资源的名称添加前缀。 |
| [nameSuffix](#namesuffix) | string | 所有资源的名称添加后缀。 |
| [replicas](#replicas) | list | 修改资源的副本数。 |
| [patchesStrategicMerge](#patchesstrategicmerge) | list | 此列表中的每个条目都应可以解析为部分或完整的资源定义文件。 |
| [patchesJson6902](#patchesjson6902) | list | 列表中的每个条目都应可以解析为 kubernetes 对象和将应用于该对象的 JSON patch 。 |
| [patchesJson6902](#patchesjson6902) | list | 列表中的每个条目都应可以解析为 Kubernetes 对象和将应用于该对象的 JSON patch 。 |
| [transformers](#transformers) | list | [插件](../plugins)配置文件。 |
## Meta
@@ -46,7 +58,7 @@
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| [vars](#vars) | string | 获取一个对象中的字段插入到另外的对象中。 |
| [vars](#vars) | string | 一个对象中的字段插入另一个对象中。 |
| [apiVersion](#apiversion) | string | [k8s metadata] 字段。 |
| [kind](#kind) | string | [k8s metadata] 字段。 |
@@ -67,59 +79,26 @@ apiVersion: kustomize.config.k8s.io/v1beta1
### commonLabels
为所有资源和 selectors 增加 Labels
```
commonLabels:
someName: someValue
owner: alice
app: bingo
```
详见 [field-name-commonLabels]。
### commonAnnotations
为所有资源增加 Annotations ,和 labels 一样是 key:value 的键值对。
```
commonAnnotations:
oncallPager: 800-555-1212
```
详见 [field-name-commonAnnotations].
### configMapGenerator
列表中的每个条目都将创建一个 ConfigMap 它是n个 ConfigMap 的生成器)。
下面的示例创建了两个 ConfigMaps
- 一个具有给定文件的名称和内容
- 另一个包含 key/value 键值对数据
每个 configMapGenerator 项都可以使用 `behavior: [create|replace|merge]` 参数。
允许 overlay 从父级修改或替换现有的 configMap。
```
configMapGenerator:
- name: myJavaServerProps
files:
- application.properties
- more.properties
- name: myJavaServerEnvVars
literals:
- JAVA_HOME=/opt/java/jdk
- JAVA_TOOL_OPTIONS=-agentlib:hprof
```
详见 [field-name-configMapGenerator].
### crds
此列表中的每个条目都应该是自定义资源定义CRD文件的相对路径。
该字段的存在是为了让 kustomize 知道用户自定义的 CRD ,并对这些类型中的对象应用适当的转换。
该字段的存在是为了让 kustomize 识别用户自定义的 CRD ,并对这些类型中的对象应用适当的转换。
典型用例CRD 引用 ConfigMap 对象
在 kustomization 中ConfigMap 对象名称可能会通过 namePrefixnameSuffix 或 hashing 来更改 CRD 对象中 ConfigMap 对象的名称,
引用时需要以相同的方式使用 namePrefix 、 nameSuffix 或 hashing 来进行更新。
在 kustomization 中ConfigMap 对象名称可能会通过 `namePrefix``nameSuffix``hashing` 来更改 CRD 对象中 ConfigMap 对象的名称,
引用时需要以相同的方式使用 `namePrefix``nameSuffix``hashing` 来进行更新。
Annotations 可以放入 openAPI 的定义中:
@@ -140,7 +119,7 @@ crds:
### generatorOptions
generatorOptions 修改所有 [ConfigMapGenerator](#configmapgenerator) 和 [SecretGenerator](#secretgenerator) 的行为。
generatorOptions 可以修改所有 [ConfigMapGenerator](#configmapgenerator) 和 [SecretGenerator](#secretgenerator) 的行为。
```
generatorOptions:
@@ -166,41 +145,7 @@ generators:
### images
修改镜像的名称、tag 或 image digest ,而无需使用 patches 。例如,对于这种 kubernetes Deployment 片段:
```
containers:
- name: mypostgresdb
image: postgres:8
- name: nginxapp
image: nginx:1.7.9
- name: myapp
image: my-demo-app:latest
- name: alpine-app
image: alpine:3.7
```
可以通过以下方式更改 `image`
- `postgres:8` to `my-registry/my-postgres:v1`,
- nginx tag `1.7.9` to `1.8.0`,
- image name `my-demo-app` to `my-app`,
- alpine's tag `3.7` to a digest value
可以在 *kustomization* 中添加以下内容:
```
images:
- name: postgres
newName: my-registry/my-postgres
newTag: v1
- name: nginx
newTag: 1.8.0
- name: my-demo-app
newName: my-app
- name: alpine
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
```
详见 [field-name-images]。
### inventory
@@ -217,120 +162,31 @@ kind: Kustomization
### namespace
为所有 resources 添加 namespace
```
namespace: my-namespace
```
详见 [field-name-namespace]
### namePrefix
该字段的值将添加在所有资源的名称之前,例如 将资源名称 `wordpress` 变为 `alices-wordpress`
```
namePrefix: alices-
```
详见 [field-names-namePrefix-nameSuffix]
### nameSuffix
该字段的值将添加在所有资源的名称后面,例如 将资源名称 `wordpress` 变为 `wordpress-v2`
详见 [field-names-namePrefix-nameSuffix]
如果资源类型为 ConfigMap 或 Secret ,则在哈希值之前附加后缀。
### patches
```
nameSuffix: -v2
```
详见 [field-name-patches]。
### patchesStrategicMerge
此列表中的每个条目都应可以解析为部分或完整的资源定义文件
这些(也可能是部分的)资源文件中的 name 必须与已经通过 `resources` 加载的 name 字段匹配,或者通过 `bases` 中的 name 字段匹配。这些条目将用于 _patch_(修改)已知资源。
推荐使用小的 patches例如修改内存的 request/limit更改 ConfigMap 中的 env 变量等小的 patches 易于维护和查看,并且易于在 overlays 中混合使用。
```
patchesStrategicMerge:
- service_port_8888.yaml
- deployment_increase_replicas.yaml
- deployment_increase_memory.yaml
```
详见 [field-name-patchesStrategicMerge]
### patchesJson6902
patchesJson6902 列表中的每个条目都应可以解析为 kubernetes 对象和将应用于该对象的 JSON patch
JSON patch 的文档地址https://tools.ietf.org/html/rfc6902
目标字段指向的 kubernetes 对象的 group、 version、 kind、 name 和 namespace 在同一 kustomization 内 path 字段内容是 JSON patch 文件的相对路径。
patch 文件中的内容可以如下这种 JSON 格式:
```
[
{"op": "add", "path": "/some/new/path", "value": "value"},
{"op": "replace", "path": "/some/existing/path", "value": "new value"}
]
```
也可以使用 YAML 格式表示:
```
- op: add
path: /some/new/path
value: value
- op: replace
path: /some/existing/path
value: new value
```
```
patchesJson6902:
- target:
version: v1
kind: Deployment
name: my-deployment
path: add_init_container.yaml
- target:
version: v1
kind: Service
name: my-service
path: add_service_annotation.yaml
```
详见 [field-name-patchesJson6902]。
### replicas
修改资源的副本数
例如:对于如下 kubernetes Deployment 片段:
```
kind: Deployment
metadata:
name: deployment-name
spec:
replicas: 3
```
在 kustomization 中添加以下内容将副本数更改为5
```
replicas:
- name: deployment-name
count: 5
```
该字段内容为列表,所以可以同时修改许多资源。
#### Limitation
由于这个声明无法设置 `kind:` 或 `group:` 它将匹配任何可以匹配名称的 `group` 和 `kind` ,并且它是以下之一:
- `Deployment`
- `ReplicationController`
- `ReplicaSet`
- `StatefulSet`
对于更复杂的用例,请使用 patch 。
详见 [field-name-replicas]
### resources
@@ -348,8 +204,7 @@ resource:
将以深度优先的顺序读取和处理资源。
文件应包含 YAML 格式的 k8s 资源。一个资源描述文件可以含有多个由(“---”)分隔的资源。
文件应包含 YAML 格式的 k8s 资源。一个资源描述文件可以含有多个由(`---`)分隔的资源。
应该包含 `resources` 字段的 kustomization 文件的指定文件目录的相对路径。
[hashicorp URL]: https://github.com/hashicorp/go-getter#url-format
@@ -358,31 +213,11 @@ resource:
### secretGenerator
此列表中的每个条目都将创建一个 Secret 资源它是n个 secrets 的生成器)
```
secretGenerator:
- name: app-tls
files:
- secret/tls.cert
- secret/tls.key
type: "kubernetes.io/tls"
- name: app-tls-namespaced
# you can define a namespace to generate secret in, defaults to: "default"
namespace: apps
files:
- tls.crt=catsecret/tls.cert
- tls.key=secret/tls.key
type: "kubernetes.io/tls"
- name: env_file_secret
envs:
- env.txt
type: Opaque
```
详见 [field-name-secretGenerator]
### vars
Vars 用于从一个 resource 字段中获取文本,并将该文本插入指定位置 - 反射功能。
Vars 用于从一个 resource 字段中获取,并将该插入指定位置 - 反射功能。
例如,假设需要在容器的 command 中指定了 Service 对象的名称,并在容器的 env 中指定了 Secret 对象的名称来确保以下内容可以正常工作:

View File

@@ -5,7 +5,7 @@ English | [简体中文](zh/README.md)
To run these examples, your `$PATH` must contain `kustomize`.
See the [installation instructions](../docs/INSTALL.md).
These examples are [tested](../travis/pre-commit.sh)
These examples are [tested](../travis/kyaml-pre-commit.sh)
to work with the latest _released_ version of kustomize.
Basic Usage

View File

@@ -4,7 +4,7 @@
这些示例默认 `kustomize` 在您的 `$PATH` 中。
这些示例通过了 [pre-commit](../../travis/pre-commit.sh) 测试,并且应该与 HEAD 一起使用。
这些示例通过了 [pre-commit](../../travis/kyaml-pre-commit.sh) 测试,并且应该与 HEAD 一起使用。
```
go get sigs.k8s.io/kustomize/v3/cmd/kustomize

View File

@@ -21,8 +21,8 @@ generate:
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
(which $(GOBIN)/addlicense || go get github.com/google/addlicense)
$(GOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)

View File

@@ -109,6 +109,7 @@ func getGroupKinds(in []*yaml.RNode) ([]metav1.GroupKind, error) {
found := false
for _, gk := range groupKinds {
if gk.Group == gvk.Group && gk.Kind == gvk.Kind {
found = true
break
}
}

View File

@@ -21,8 +21,8 @@ generate:
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
(which $(GOBIN)/addlicense || go get github.com/google/addlicense)
$(GOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)

View File

@@ -21,8 +21,8 @@ generate:
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
(which $(GOBIN)/addlicense || go get github.com/google/addlicense)
$(GOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)

View File

@@ -3,9 +3,11 @@
.PHONY: license image
GOBIN := $(shell go env GOPATH)/bin
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
(which $(GOBIN)/addlicense || go get github.com/google/addlicense)
$(GOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
all: license

View File

@@ -21,8 +21,8 @@ generate:
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
(which $(GOBIN)/addlicense || go get github.com/google/addlicense)
$(GOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)

View File

@@ -21,8 +21,8 @@ generate:
(cd image && GOBIN=$(GOBIN) go generate ./...)
license:
(which $(GOPATH)/bin/addlicense || go get github.com/google/addlicense)
$(GOPATH)/bin/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
(which $(GOBIN)/addlicense || go get github.com/google/addlicense)
$(GOBIN)/addlicense -y 2019 -c "The Kubernetes Authors." -f LICENSE_TEMPLATE .
tidy:
(cd image && go mod tidy)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env sh
#!/bin/bash
set -e
# Builds or removes Go plugin object code.
@@ -40,8 +40,8 @@ function removePlugin {
}
goPlugins=$(
find $root -name "*.go" |
grep -v builtin/ |
find $root -name "*.go" |
grep -v builtin/ |
xargs grep -l "var KustomizePlugin")
for p in $goPlugins; do

15
hack/shellHelpers.sh Normal file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
# TODO: Make the code ignorant of the CI environment "brand name".
# We used to run CI tests on travis, and disabled certain tests
# when running there. Now we run on Prow, so look for that.
# https://github.com/kubernetes/test-infra/blob/master/prow/jobs.md
# Might be useful to eschew using the brand name of the CI environment
# (replace "travis" with "RemoteCI" or something - not just switch to "prow").
function onLinuxAndNotOnRemoteCI {
[[ ("linux" == "$(go env GOOS)") && (-z ${PROW_JOB_ID+x}) ]] && return
false
}

View File

@@ -9,10 +9,8 @@ set -o pipefail
version=$1
function onLinuxAndNotOnTravis {
[[ ("linux" == "$(go env GOOS)") && (-z ${TRAVIS+x}) ]] && return
false
}
# All hack scripts should run from top level.
. hack/shellHelpers.sh
# TODO: change the label?
# We test against the latest release, and HEAD, and presumably
@@ -22,13 +20,12 @@ mdrip --mode test \
--label testAgainstLatestRelease examples
# TODO: make work for non-linux
if onLinuxAndNotOnTravis; then
echo "On linux, and not on travis, so running the notravis example tests."
if onLinuxAndNotOnRemoteCI; then
echo "On linux, and not on remote CI. Running expensive tests."
# Requires helm.
make $(go env GOPATH)/bin/helm
mdrip --mode test \
--label helmtest examples/chart.md
mdrip --mode test --label helmtest examples/chart.md
fi
echo "Example tests passed against ${version}."

View File

@@ -14,21 +14,19 @@ set -o pipefail
rcAccumulator=0
function onLinuxAndNotOnTravis {
[[ ("linux" == "$(go env GOOS)") && (-z ${TRAVIS+x}) ]] && return
false
}
# All hack scripts should run from top level.
. hack/shellHelpers.sh
function runTest {
local file=$1
local code=0
if grep -q "// +build notravis" "$file"; then
if onLinuxAndNotOnTravis; then
go test -v -tags=notravis $file
if onLinuxAndNotOnRemoteCI; then
go test -v -tags=notravis $file
code=$?
else
# TODO: make work for non-linux
echo "Not on linux or on travis; skipping $file"
echo "Not on linux or on remote CI; skipping $file"
fi
else
go test -v $file
@@ -49,8 +47,10 @@ function scanDir {
popd >& /dev/null
}
if onLinuxAndNotOnTravis; then
if onLinuxAndNotOnRemoteCI; then
# Some of these tests have special deps.
make $(go env GOPATH)/bin/helmV2
make $(go env GOPATH)/bin/helmV3
make $(go env GOPATH)/bin/helm
make $(go env GOPATH)/bin/kubeval
fi
@@ -60,7 +60,7 @@ for goMod in $(find ./plugin -name 'go.mod'); do
if [[ "$d" == "./plugin/someteam.example.com/v1/gogetter" ]]; then
echo "Skipping broken $d"
else
scanDir $d
scanDir $d
fi
done

View File

@@ -16,6 +16,7 @@ import (
"sigs.k8s.io/kustomize/api/krusty"
"sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/yaml"
)
@@ -76,7 +77,6 @@ func NewCmdBuild(out io.Writer) *cobra.Command {
addFlagLoadRestrictor(cmd.Flags())
addFlagEnablePlugins(cmd.Flags())
addFlagReorderOutput(cmd.Flags())
cmd.AddCommand(NewCmdBuildPrune(out))
return cmd
}
@@ -104,10 +104,9 @@ func (o *Options) makeOptions() *krusty.Options {
opts := &krusty.Options{
DoLegacyResourceSort: o.outOrder == legacy,
LoadRestrictions: getFlagLoadRestrictorValue(),
DoPrune: false,
}
if isFlagEnablePluginsSet() {
c, err := konfig.EnabledPluginConfig()
c, err := konfig.EnabledPluginConfig(types.BploUseStaticallyLinked)
if err != nil {
log.Fatal(err)
}
@@ -128,18 +127,6 @@ func (o *Options) RunBuild(out io.Writer) error {
return o.emitResources(out, fSys, m)
}
func (o *Options) RunBuildPrune(out io.Writer) error {
fSys := filesys.MakeFsOnDisk()
opts := o.makeOptions()
opts.DoPrune = true
k := krusty.MakeKustomizer(fSys, opts)
m, err := k.Run(o.kustomizationPath)
if err != nil {
return err
}
return o.emitResources(out, fSys, m)
}
func (o *Options) emitResources(
out io.Writer, fSys filesys.FileSystem, m resmap.ResMap) error {
if o.outputPath != "" && fSys.IsDir(o.outputPath) {
@@ -156,24 +143,6 @@ func (o *Options) emitResources(
return err
}
func NewCmdBuildPrune(out io.Writer) *cobra.Command {
var o Options
cmd := &cobra.Command{
Use: "alpha-inventory [path]",
Short: "Print the inventory object which contains a list of all other objects",
Example: examples,
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
err := o.Validate(args)
if err != nil {
return err
}
return o.RunBuildPrune(out)
},
}
return cmd
}
func writeIndividualFiles(
fSys filesys.FileSystem, folderPath string, m resmap.ResMap) error {
byNamespace := m.GroupedByCurrentNamespace()

View File

@@ -121,17 +121,23 @@ func Diff(sourceDir, destDir string) (sets.String, error) {
return diff, err
}
if !bytes.Equal(b1, b2) {
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(string(b1), string(b2), false)
fmt.Println(dmp.DiffPrettyText(diffs))
fmt.Println(PrettyFileDiff(string(b1), string(b2)))
diff.Insert(f)
}
}
// return the differing files
return diff, nil
}
// PrettyFileDiff takes the content of two files and returns the pretty diff
func PrettyFileDiff(s1, s2 string) string {
dmp := diffmatchpatch.New()
wSrc, wDst, warray := dmp.DiffLinesToRunes(s1, s2)
diffs := dmp.DiffMainRunes(wSrc, wDst, false)
diffs = dmp.DiffCharsToLines(diffs, warray)
return dmp.DiffPrettyText(diffs)
}
// SyncFile copies file from src file path to a dst file path by replacement
// deletes dst file if src file doesn't exist
func SyncFile(src, dst string) error {

View File

@@ -261,3 +261,25 @@ func TestSyncFileNoSrcFile(t *testing.T) {
assert.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "no such file or directory"))
}
func TestPrettyFileDiff(t *testing.T) {
s1 := `apiVersion: someversion/v1alpha2
kind: ContainerCluster
metadata:
clusterName: "some_cluster"
name: asm-cluster
namespace: "PROJECT_ID" # {"$ref":"#/definitions/io.k8s.cli.setters.gcloud.core.project"}`
s2 := `apiVersion: someversion/v1alpha2
kind: ContainerCluster
metadata:
clusterName: "some_cluster"
name: asm-cluster
namespace: "some_project" # {"$ref":"#/definitions/io.k8s.cli.setters.gcloud.core.project"}`
expectedLine1 := `[31m namespace: "PROJECT_ID" # {"$ref":"#/definitions/io.k8s.cli.setters.gcloud.core.project"}`
expectedLine2 := `[32m namespace: "some_project" # {"$ref":"#/definitions/io.k8s.cli.setters.gcloud.core.project"}`
assert.Contains(t, PrettyFileDiff(s1, s2), expectedLine1)
assert.Contains(t, PrettyFileDiff(s1, s2), expectedLine2)
}

127
kyaml/fn/framework/doc.go Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package framework contains a framework for writing functions in go. The function spec
// is defined at: https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
//
// Examples
//
// Example function implementation using framework.ResourceList with functionConfig
//
// type Spec struct {
// Value string `yaml:"value,omitempty"`
// }
// type Example struct {
// Spec Spec `yaml:"spec,omitempty"`
// }
// functionConfig := &Example{}
//
// rl := framework.ResourceList{FunctionConfig: functionConfig}
// if err := rl.Read(); err != nil { return err }
//
// for i := range rl.Items {
// // modify the items...
// }
// if err := rl.Write(); err != nil { return err }
//
// Example function implementation using framework.Command with flags
//
// var value string
// cmd := framework.Command(nil, func(items []*yaml.RNode) ([]*yaml.RNode, error) {
// for i := range items {
// // modify the items...
// }
// return items, nil
// })
// cmd.Flags().StringVar(&value, "value", "", "annotation value")
// if err := cmd.Execute(); err != nil { return err }
//
// Architecture
//
// Functions modify a slice of resources (ResourceList.items) which are read as input and written
// as output. The function itself may be configured through a functionConfig
// (ResourceList.functionConfig).
//
// Example Function Input:
//
// kind: ResourceList
// items:
// - kind: Deployment
// ...
// - kind: Service
// ....
// functionConfig:
// kind: Example
// spec:
// value: foo
//
// The functionConfig may be specified declaratively and run with
//
// config run DIR/
//
// Declarative function declaration:
//
// kind: Example
// metadata:
// annotations:
// # run the function by creating this container and providing this
// # Example as the functionConfig
// config.kubernetes.io/function: |
// image: image/containing/fuction:impl
// spec:
// value: foo
//
// The framework takes care of serializing and deserializing the ResourceList.
//
// Generated ResourceList.functionConfig -- ConfigMaps
//
// Functions may also be specified imperatively and run using:
//
// config run DIR/ --image image/containing/fuction:impl -- value=foo
//
// When run imperatively, a ConfigMap is generated for the functionConfig, and the command
// arguments are set as ConfigMap data entries.
//
// kind: ConfigMap
// data:
// value: foo
//
// To write a function that can be run imperatively on the commandline, have it take a
// ConfigMap as its functionConfig.
//
// Mutator and Generator Functions
//
// Functions may add, delete or modify resources by modifying the items slice.
// When using framework.Command this is done through returning the new items slice.
// When using framework.ResourceList this is done through modifying ResourceList.Items in place.
//
// Validator Functions
//
// A function may validate resources by providing a Result.
// When using framework.Command this is done through returning a framework.Result as an error.
// WHen using framework.ResourceList this is done through setting ResourceList.Result.
//
// Configuring Functions
//
// Functions may be configured through a functionConfig (i.e. a client side custom resource),
// or through flags (which the framework parses from a ConfigMap provided as input).
//
// When using framework.Command, any flags registered on the cobra.Command will be parsed
// from the functionConfig input if they are defined as functionConfig.data entries.
//
// When using framework.ResourceList, any flags set on the ResourceList.Flags will be
// parsed from the functionConfig input if they are defined as functionConfig.data entries.
//
// Functions may also access environment variables set by the caller.
//
// Building a container image for the function
//
// The go program must be built into a container to be run as a function. The framework
// can be used to generate a Dockerfile to build the function container.
//
// # create the ./Dockerfile for the container
// $ go run ./main.go gen ./
//
// # build the function's container
// $ docker build . -t gcr.io/my-project/my-image:my-version
package framework

View File

@@ -0,0 +1,12 @@
# Copyright 2019 The Kubernetes Authors.
# SPDX-License-Identifier: Apache-2.0
FROM golang:1.13-stretch
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY . .
RUN go build -v -o /usr/local/bin/function ./
FROM alpine:latest
COPY --from=0 /usr/local/bin/function /usr/local/bin/function
CMD ["function"]

View File

@@ -0,0 +1,9 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package main contains an example using the the framework.
//
// To generate the Dockerfile for the function image run:
//
// $ go run ./main.go .
package main

View File

@@ -0,0 +1,29 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package main
import (
"os"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func main() {
var value string
cmd := framework.Command(nil, func(items []*yaml.RNode) ([]*yaml.RNode, error) {
for i := range items {
// set the annotation on each resource item
if err := items[i].PipeE(yaml.SetAnnotation("value", value)); err != nil {
return nil, err
}
}
return items, nil
})
cmd.Flags().StringVar(&value, "value", "", "annotation value")
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}

View File

@@ -0,0 +1,529 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package framework_test
import (
"bytes"
"fmt"
"github.com/spf13/pflag"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
const service = "Service"
// ExampleResourceList_modify implements a function that sets an annotation on each resource.
// The annotation value is configured via a flag value parsed from ResourceList.functionConfig.data
func ExampleResourceList_modify() {
// for testing purposes only -- normally read from stdin when Executing
input := bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
# items are provided as nodes
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
- apiVersion: v1
kind: Service
metadata:
name: foo
# functionConfig is parsed into flags by framework.Command
functionConfig:
apiVersion: v1
kind: ConfigMap
data:
value: baz
`)
// configure the annotation value using a flag parsed from
// ResourceList.functionConfig.data.value
fs := pflag.NewFlagSet("tests", pflag.ContinueOnError)
value := fs.String("value", "", "annotation value")
rl := framework.ResourceList{
Flags: fs,
Reader: input, // for testing only
}
if err := rl.Read(); err != nil {
panic(err)
}
for i := range rl.Items {
// set the annotation on each resource item
if err := rl.Items[i].PipeE(yaml.SetAnnotation("value", *value)); err != nil {
panic(err)
}
}
if err := rl.Write(); err != nil {
panic(err)
}
// Output:
// apiVersion: config.kubernetes.io/v1alpha1
// kind: ResourceList
// items:
// - apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: foo
// annotations:
// value: 'baz'
// - apiVersion: v1
// kind: Service
// metadata:
// name: foo
// annotations:
// value: 'baz'
// functionConfig:
// apiVersion: v1
// kind: ConfigMap
// data:
// value: baz
}
// ExampleCommand_modify implements a function that sets an annotation on each resource.
// The annotation value is configured via a flag value parsed from
// ResourceList.functionConfig.data
func ExampleCommand_modify() {
// configure the annotation value using a flag parsed from
// ResourceList.functionConfig.data.value
var value string
cmd := framework.Command(nil, func(items []*yaml.RNode) ([]*yaml.RNode, error) {
for i := range items {
// set the annotation on each resource item
if err := items[i].PipeE(yaml.SetAnnotation("value", value)); err != nil {
return nil, err
}
}
return items, nil
})
cmd.Flags().StringVar(&value, "value", "", "annotation value")
// for testing purposes only -- normally read from stdin when Executing
cmd.SetIn(bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
# items are provided as nodes
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
- apiVersion: v1
kind: Service
metadata:
name: foo
# functionConfig is parsed into flags by framework.Command
functionConfig:
apiVersion: v1
kind: ConfigMap
data:
value: baz
`))
// run the command
if err := cmd.Execute(); err != nil {
panic(err)
}
// Output:
// apiVersion: config.kubernetes.io/v1alpha1
// kind: ResourceList
// items:
// - apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: foo
// annotations:
// value: 'baz'
// - apiVersion: v1
// kind: Service
// metadata:
// name: foo
// annotations:
// value: 'baz'
// functionConfig:
// apiVersion: v1
// kind: ConfigMap
// data:
// value: baz
}
// ExampleCommand_generateReplace generates a resource from a functionConfig.
// If the resource already exist s, it replaces the resource with a new copy.
func ExampleCommand_generateReplace() {
// function API definition which will be parsed from the ResourceList.functionConfig
// read from stdin
type Spec struct {
Name string `yaml:"name,omitempty"`
}
type ExampleServiceGenerator struct {
Spec Spec `yaml:"spec,omitempty"`
}
functionConfig := &ExampleServiceGenerator{}
// function implementation -- generate a Service resource
cmd := framework.Command(functionConfig, func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
var newNodes []*yaml.RNode
for i := range nodes {
meta, err := nodes[i].GetMeta()
if err != nil {
return nil, err
}
// something we already generated, remove it from the list so we regenerate it
if meta.Name == functionConfig.Spec.Name &&
meta.Kind == service &&
meta.APIVersion == "v1" {
continue
}
newNodes = append(newNodes, nodes[i])
}
// generate the resource
n, err := yaml.Parse(fmt.Sprintf(`apiVersion: v1
kind: Service
metadata:
name: %s
`, functionConfig.Spec.Name))
if err != nil {
return nil, err
}
return append(newNodes, n), nil
})
// for testing purposes only -- normally read from stdin when Executing
cmd.SetIn(bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
# items are provided as nodes
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
# functionConfig is parsed into flags by framework.Command
functionConfig:
apiVersion: example.com/v1alpha1
kind: ExampleServiceGenerator
spec:
name: bar
`))
// run the command
if err := cmd.Execute(); err != nil {
panic(err)
}
// Output:
// apiVersion: config.kubernetes.io/v1alpha1
// kind: ResourceList
// items:
// - apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: foo
// - apiVersion: v1
// kind: Service
// metadata:
// name: bar
// functionConfig:
// apiVersion: example.com/v1alpha1
// kind: ExampleServiceGenerator
// spec:
// name: bar
}
// ExampleResourceList_generateReplace generates a resource from a functionConfig.
// If the resource already exist s, it replaces the resource with a new copy.
func ExampleResourceList_generateReplace() {
input := bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
# items are provided as nodes
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
# functionConfig is parsed into flags by framework.Command
functionConfig:
apiVersion: example.com/v1alpha1
kind: ExampleServiceGenerator
spec:
name: bar
`)
// function API definition which will be parsed from the ResourceList.functionConfig
// read from stdin
type Spec struct {
Name string `yaml:"name,omitempty"`
}
type ExampleServiceGenerator struct {
Spec Spec `yaml:"spec,omitempty"`
}
functionConfig := &ExampleServiceGenerator{}
rl := framework.ResourceList{
FunctionConfig: functionConfig,
Reader: input, // for testing only
}
if err := rl.Read(); err != nil {
panic(err)
}
// remove the last generated resource
var newNodes []*yaml.RNode
for i := range rl.Items {
meta, err := rl.Items[i].GetMeta()
if err != nil {
panic(err)
}
// something we already generated, remove it from the list so we regenerate it
if meta.Name == functionConfig.Spec.Name &&
meta.Kind == service &&
meta.APIVersion == "v1" {
continue
}
newNodes = append(newNodes, rl.Items[i])
}
rl.Items = newNodes
// generate the resource again
n, err := yaml.Parse(fmt.Sprintf(`apiVersion: v1
kind: Service
metadata:
name: %s
`, functionConfig.Spec.Name))
if err != nil {
panic(err)
}
rl.Items = append(rl.Items, n)
if err := rl.Write(); err != nil {
panic(err)
}
// Output:
// apiVersion: config.kubernetes.io/v1alpha1
// kind: ResourceList
// items:
// - apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: foo
// - apiVersion: v1
// kind: Service
// metadata:
// name: bar
// functionConfig:
// apiVersion: example.com/v1alpha1
// kind: ExampleServiceGenerator
// spec:
// name: bar
}
// ExampleCommand_generateUpdate generates a resource, updating the previously generated
// copy rather than replacing it.
//
// Note: This will keep manual edits to the previously generated copy.
func ExampleCommand_generateUpdate() {
// function API definition which will be parsed from the ResourceList.functionConfig
// read from stdin
type Spec struct {
Name string `yaml:"name,omitempty"`
Annotations map[string]string `yaml:"annotations,omitempty"`
}
type ExampleServiceGenerator struct {
Spec Spec `yaml:"spec,omitempty"`
}
functionConfig := &ExampleServiceGenerator{}
// function implementation -- generate or update a Service resource
cmd := framework.Command(functionConfig, func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
var found bool
for i := range nodes {
meta, err := nodes[i].GetMeta()
if err != nil {
return nil, err
}
// something we already generated, reconcile it to make sure it matches what
// is specified by the functionConfig
if meta.Name == functionConfig.Spec.Name &&
meta.Kind == service &&
meta.APIVersion == "v1" {
// set some values
for k, v := range functionConfig.Spec.Annotations {
err := nodes[i].PipeE(yaml.SetAnnotation(k, v))
if err != nil {
return nil, err
}
}
found = true
break
}
}
if found {
return nodes, nil
}
// generate the resource if not found
n, err := yaml.Parse(fmt.Sprintf(`apiVersion: v1
kind: Service
metadata:
name: %s
`, functionConfig.Spec.Name))
for k, v := range functionConfig.Spec.Annotations {
err := n.PipeE(yaml.SetAnnotation(k, v))
if err != nil {
return nil, err
}
}
nodes = append(nodes, n)
if err != nil {
return nil, err
}
return nodes, nil
})
// for testing purposes only -- normally read from stdin when Executing
cmd.SetIn(bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
# items are provided as nodes
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
- apiVersion: v1
kind: Service
metadata:
name: bar
# functionConfig is parsed into flags by framework.Command
functionConfig:
apiVersion: example.com/v1alpha1
kind: ExampleServiceGenerator
spec:
name: bar
annotations:
a: b
`))
// run the command
if err := cmd.Execute(); err != nil {
panic(err)
}
// Output:
// apiVersion: config.kubernetes.io/v1alpha1
// kind: ResourceList
// items:
// - apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: foo
// - apiVersion: v1
// kind: Service
// metadata:
// name: bar
// annotations:
// a: 'b'
// functionConfig:
// apiVersion: example.com/v1alpha1
// kind: ExampleServiceGenerator
// spec:
// name: bar
// annotations:
// a: b
}
// ExampleCommand_validate validates that all Deployment resources have the replicas field set.
// If any Deployments do not contain spec.replicas, then the function will return results
// which will be set on ResourceList.results
func ExampleCommand_validate() {
cmd := framework.Command(nil, func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
// validation results
var validationResults []framework.Item
// validate that each Deployment resource has spec.replicas set
for i := range nodes {
// only check Deployment resources
meta, err := nodes[i].GetMeta()
if err != nil {
return nil, err
}
if meta.Kind != "Deployment" {
continue
}
// lookup replicas field
r, err := nodes[i].Pipe(yaml.Lookup("spec", "replicas"))
if err != nil {
return nil, err
}
// check replicas not specified
if r != nil {
continue
}
validationResults = append(validationResults, framework.Item{
Severity: framework.Error,
Message: "missing replicas",
ResourceRef: meta,
Field: framework.Field{
Path: "spec.field",
SuggestedValue: "1",
},
})
}
// framework will only consider results an error if it has at least 1 item
return nodes, framework.Result{
Name: "replicas-validator",
Items: validationResults,
}
})
// for testing purposes only -- normally read from stdin when Executing
cmd.SetIn(bytes.NewBufferString(`
apiVersion: config.kubernetes.io/v1alpha1
kind: ResourceList
# items are provided as nodes
items:
- apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`))
// run the command
if err := cmd.Execute(); err != nil {
// normally exit 1 here
}
// Output:
// apiVersion: config.kubernetes.io/v1alpha1
// kind: ResourceList
// items:
// - apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: foo
// results:
// name: replicas-validator
// items:
// - message: missing replicas
// severity: error
// resourceRef:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: foo
// field:
// path: spec.field
// suggestedValue: "1"
}

View File

@@ -0,0 +1,246 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package framework
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// ResourceList reads the function input and writes the function output.
//
// Adheres to the spec: https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md
type ResourceList struct {
// FunctionConfig is the ResourceList.functionConfig input value. If FunctionConfig
// is set to a value such as a struct or map[string]interface{} before ResourceList.Read()
// is called, then the functionConfig will be parsed into that value.
// If it is nil, the functionConfig will be set to a map[string]interface{}
// before it is parsed.
//
// e.g. given the function input:
//
// kind: ResourceList
// functionConfig:
// kind: Example
// spec:
// foo: var
//
// FunctionConfig will contain the Example unmarshalled into its value.
FunctionConfig interface{}
// Items is the ResourceList.items input and output value. Items will be set by
// ResourceList.Read() and written by ResourceList.Write().
//
// e.g. given the function input:
//
// kind: ResourceList
// items:
// - kind: Deployment
// ...
// - kind: Service
// ...
//
// Items will be a slice containing the Deployment and Service resources
Items []*yaml.RNode
// Result is ResourceList.result output value. Result will be written by
// ResourceList.Write()
Result *Result
// Flags are an optional set of flags to parse the ResourceList.functionConfig.data.
// If non-nil, ResourceList.Read() will set the flag value for each flag name matching
// a ResourceList.functionConfig.data map entry.
//
// e.g. given the function input:
//
// kind: ResourceList
// functionConfig:
// data:
// foo: bar
// a: b
//
// The flags --a=b and --foo=bar will be set in Flags.
Flags *pflag.FlagSet
// Reader is used to read the function input (ResourceList).
// Defaults to os.Stdin.
Reader io.Reader
// Writer is used to write the function output (ResourceList)
// Defaults to os.Stdout.
Writer io.Writer
// rw reads function input and writes function output
rw *kio.ByteReadWriter
}
// Read reads the ResourceList
func (r *ResourceList) Read() error {
if r.Reader == nil {
r.Reader = os.Stdin
}
if r.Writer == nil {
r.Writer = os.Stdout
}
r.rw = &kio.ByteReadWriter{
Reader: r.Reader,
Writer: r.Writer,
KeepReaderAnnotations: true,
}
var err error
r.Items, err = r.rw.Read()
if err != nil {
return errors.Wrap(err)
}
// parse the functionConfig
return func() error {
if r.rw.FunctionConfig == nil {
// no function config exists
return nil
}
if r.FunctionConfig == nil {
// set directly from r.rw
r.FunctionConfig = r.rw.FunctionConfig
} else {
// unmarshal the functionConfig into the provided value
err := yaml.Unmarshal([]byte(r.rw.FunctionConfig.MustString()), r.FunctionConfig)
if err != nil {
return errors.Wrap(err)
}
}
// set the functionConfig values as flags so they are easy to access
if r.Flags == nil || !r.Flags.HasFlags() {
return nil
}
// flags are always set from the "data" field
data, err := r.rw.FunctionConfig.Pipe(yaml.Lookup("data"))
if err != nil || data == nil {
return err
}
return data.VisitFields(func(node *yaml.MapNode) error {
f := r.Flags.Lookup(node.Key.YNode().Value)
if f == nil {
return nil
}
return f.Value.Set(node.Value.YNode().Value)
})
}()
}
// Write writes the ResourceList
func (r *ResourceList) Write() error {
// set the ResourceList.results for validating functions
if r.Result != nil {
if len(r.Result.Items) > 0 {
b, err := yaml.Marshal(r.Result)
if err != nil {
return errors.Wrap(err)
}
y, err := yaml.Parse(string(b))
if err != nil {
return errors.Wrap(err)
}
r.rw.Results = y
}
}
// write the results
return r.rw.Write(r.Items)
}
// Command returns a cobra.Command to run a function.
//
// The cobra.Command will use a ResourceList to Read() the input, run the provided function,
// and Write() the output.
//
// If functionConfig is non-nil, the ResourceList.functionConfig will be unmarshalled into it.
//
// The returned cobra.Command will have a "gen" subcommand which can be used to generate
// a Dockerfile to build the function into a container image
//
// go run main.go gen DIR/
func Command(functionConfig interface{}, function Function) cobra.Command {
cmd := cobra.Command{}
AddGenerateDockerfile(&cmd)
cmd.RunE = func(cmd *cobra.Command, args []string) error {
err := execute(function, functionConfig, cmd)
if err != nil {
fmt.Fprintf(cmd.ErrOrStderr(), "%v", err)
}
return err
}
cmd.SilenceErrors = true
cmd.SilenceUsage = true
return cmd
}
// AddGenerateDockerfile adds a "gen" subcommand to create a Dockerfile for building
// the function as a container.
func AddGenerateDockerfile(cmd *cobra.Command) {
gen := &cobra.Command{
Use: "gen",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return ioutil.WriteFile(filepath.Join(args[0], "Dockerfile"), []byte(`FROM golang:1.13-stretch
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY . .
RUN go build -v -o /usr/local/bin/function ./
FROM alpine:latest
COPY --from=0 /usr/local/bin/function /usr/local/bin/function
CMD ["function"]
`), 0600)
},
}
cmd.AddCommand(gen)
}
func execute(function Function, functionConfig interface{}, cmd *cobra.Command) error {
rl := ResourceList{
FunctionConfig: functionConfig,
Flags: cmd.Flags(),
Writer: cmd.OutOrStdout(),
Reader: cmd.InOrStdin(),
}
if err := rl.Read(); err != nil {
return err
}
// run the function implementation
var err error
rl.Items, err = function(rl.Items)
// set the ResourceList.results for validating functions
var result *Result
if err != nil {
if val, ok := err.(Result); ok {
rl.Result = &val
} else {
return errors.Wrap(err)
}
}
if err := rl.Write(); err != nil {
return err
}
if result != nil && result.ExitCode() != 0 {
return errors.Wrap(err)
}
return nil
}

View File

@@ -0,0 +1,53 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package framework_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/fn/framework"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestCommand_dockerfile(t *testing.T) {
d, err := ioutil.TempDir("", "kustomize")
if !assert.NoError(t, err) {
t.FailNow()
}
defer os.RemoveAll(d)
// create a function
cmd := framework.Command(nil, func(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
return nil, nil
})
// generate the Dockerfile
cmd.SetArgs([]string{"gen", d})
if !assert.NoError(t, cmd.Execute()) {
t.FailNow()
}
b, err := ioutil.ReadFile(filepath.Join(d, "Dockerfile"))
if !assert.NoError(t, err) {
t.FailNow()
}
expected := `FROM golang:1.13-stretch
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY . .
RUN go build -v -o /usr/local/bin/function ./
FROM alpine:latest
COPY --from=0 /usr/local/bin/function /usr/local/bin/function
CMD ["function"]
`
if !assert.Equal(t, expected, string(b)) {
t.FailNow()
}
}

View File

@@ -0,0 +1,92 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package framework
import (
"strings"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Function defines a function which mutates or validates a collection of configuration
// To create a structured validation result, return a Result as the error.
type Function func(nodes []*yaml.RNode) ([]*yaml.RNode, error)
// Result defines a function result which will be set on the emitted ResourceList
type Result struct {
// Name is the name of the function creating the result
Name string `yaml:"name,omitempty"`
// Items are the individual results
Items []Item `yaml:"items,omitempty"`
}
// Severity indicates the severity of the result
type Severity string
const (
// Error indicates the result is an error. Will cause the function to exit non-0.
Error Severity = "error"
// Warning indicates the result is a warning
Warning Severity = "warning"
// Info indicates the result is an informative message
Info Severity = "info"
)
// Item defines a validation result
type Item struct {
// Message is a human readable message
Message string `yaml:"message,omitempty"`
// Severity is the severity of the
Severity Severity `yaml:"severity,omitempty"`
// ResourceRef is a reference to a resource
ResourceRef yaml.ResourceMeta `yaml:"resourceRef,omitempty"`
Field Field `yaml:"field,omitempty"`
File File `yaml:"file,omitempty"`
}
// File references a file containing a resource
type File struct {
// Path is relative path to the file containing the resource
Path string `yaml:"path,omitempty"`
// Index is the index into the file containing the resource
// (i.e. if there are multiple resources in a single file)
Index int `yaml:"index,omitempty"`
}
// Field references a field in a resource
type Field struct {
// Path is the field path
Path string `yaml:"path,omitempty"`
// CurrentValue is the current field value
CurrentValue string `yaml:"currentValue,omitempty"`
// SuggestedValue is the suggested field value
SuggestedValue string `yaml:"suggestedValue,omitempty"`
}
// Error implement error
func (e Result) Error() string {
var msgs []string
for _, i := range e.Items {
msgs = append(msgs, i.Message)
}
return strings.Join(msgs, "\n\n")
}
// ExitCode provides the exit code based on the result
func (e Result) ExitCode() int {
for _, i := range e.Items {
if i.Severity == Error {
return 1
}
}
return 0
}

View File

@@ -0,0 +1,202 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package container
import (
"fmt"
"os"
"strings"
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// Filter filters Resources using a container image.
// The container must start a process that reads the list of
// input Resources from stdin, reads the Configuration from the env
// API_CONFIG, and writes the filtered Resources to stdout.
// If there is a error or validation failure, the process must exit
// non-zero.
// The full set of environment variables from the parent process
// are passed to the container.
//
// Function Scoping:
// Filter applies the function only to Resources to which it is scoped.
//
// Resources are scoped to a function if any of the following are true:
// - the Resource were read from the same directory as the function config
// - the Resource were read from a subdirectory of the function config directory
// - the function config is in a directory named "functions" and
// they were read from a subdirectory of "functions" parent
// - the function config doesn't have a path annotation (considered globally scoped)
// - the Filter has GlobalScope == true
//
// In Scope Examples:
//
// Example 1: deployment.yaml and service.yaml in function.yaml scope
// same directory as the function config directory
// .
// ├── function.yaml
// ├── deployment.yaml
// └── service.yaml
//
// Example 2: apps/deployment.yaml and apps/service.yaml in function.yaml scope
// subdirectory of the function config directory
// .
// ├── function.yaml
// └── apps
//    ├── deployment.yaml
//    └── service.yaml
//
// Example 3: apps/deployment.yaml and apps/service.yaml in functions/function.yaml scope
// function config is in a directory named "functions"
// .
// ├── functions
// │   └── function.yaml
// └── apps
//    ├── deployment.yaml
//    └── service.yaml
//
// Out of Scope Examples:
//
// Example 1: apps/deployment.yaml and apps/service.yaml NOT in stuff/function.yaml scope
// .
// ├── stuff
// │   └── function.yaml
// └── apps
//    ├── deployment.yaml
//    └── service.yaml
//
// Example 2: apps/deployment.yaml and apps/service.yaml NOT in stuff/functions/function.yaml scope
// .
// ├── stuff
// │   └── functions
// │    └── function.yaml
// └── apps
//    ├── deployment.yaml
//    └── service.yaml
//
// Default Paths:
// Resources emitted by functions will have default path applied as annotations
// if none is present.
// The default path will be the function-dir/ (or parent directory in the case of "functions")
// + function-file-name/ + namespace/ + kind_name.yaml
//
// Example 1: Given a function in fn.yaml that produces a Deployment name foo and a Service named bar
// dir
// └── fn.yaml
//
// Would default newly generated Resources to:
//
// dir
// ├── fn.yaml
// └── fn
//    ├── deployment_foo.yaml
//    └── service_bar.yaml
//
// Example 2: Given a function in functions/fn.yaml that produces a Deployment name foo and a Service named bar
// dir
// └── fn.yaml
//
// Would default newly generated Resources to:
//
// dir
// ├── functions
// │   └── fn.yaml
// └── fn
//    ├── deployment_foo.yaml
//    └── service_bar.yaml
//
// Example 3: Given a function in fn.yaml that produces a Deployment name foo, namespace baz and a Service named bar namespace baz
// dir
// └── fn.yaml
//
// Would default newly generated Resources to:
//
// dir
// ├── fn.yaml
// └── fn
// └── baz
//    ├── deployment_foo.yaml
//    └── service_bar.yaml
type Filter struct {
// Image is the container image to use to create a container.
Image string `yaml:"image,omitempty"`
// Network is the container network to use.
Network string `yaml:"network,omitempty"`
// StorageMounts is a list of storage options that the container will have mounted.
StorageMounts []runtimeutil.StorageMount `yaml:"mounts,omitempty"`
Exec runtimeexec.Filter
}
func (c Filter) String() string {
if c.Exec.DeferFailure {
return fmt.Sprintf("%s deferFailure: %v", c.Image, c.Exec.DeferFailure)
}
return c.Image
}
func (c Filter) GetExit() error {
return c.Exec.GetExit()
}
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
c.setupExec()
return c.Exec.Filter(nodes)
}
func (c *Filter) setupExec() {
// don't init 2x
if c.Exec.Path != "" {
return
}
path, args := c.getCommand()
c.Exec.Path = path
c.Exec.Args = args
}
// getArgs returns the command + args to run to spawn the container
func (c *Filter) getCommand() (string, []string) {
// run the container using docker. this is simpler than using the docker
// libraries, and ensures things like auth work the same as if the container
// was run from the cli.
network := "none"
if c.Network != "" {
network = c.Network
}
args := []string{"run",
"--rm", // delete the container afterward
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR", // attach stdin, stdout, stderr
"--network", network,
// added security options
"--user", "nobody", // run as nobody
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
// note: don't make fs readonly because things like heredoc rely on writing tmp files
}
// TODO(joncwong): Allow StorageMount fields to have default values.
for _, storageMount := range c.StorageMounts {
args = append(args, "--mount", storageMount.String())
}
os.Setenv("LOG_TO_STDERR", "true")
os.Setenv("STRUCTURED_RESULTS", "true")
// export the local environment vars to the container
for _, pair := range os.Environ() {
args = append(args, "-e", strings.Split(pair, "=")[0])
}
a := append(args, c.Image)
return "docker", a
}

View File

@@ -0,0 +1,203 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package container
import (
"bytes"
"fmt"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestFilter_setupExec(t *testing.T) {
var tests = []struct {
name string
functionConfig string
expectedArgs []string
instance Filter
}{
{
name: "command",
functionConfig: `apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
expectedArgs: []string{
"run",
"--rm",
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
"--network", "none",
"--user", "nobody",
"--security-opt=no-new-privileges",
},
instance: Filter{Image: "example.com:version"},
},
{
name: "network",
functionConfig: `apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
expectedArgs: []string{
"run",
"--rm",
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
"--network", "test-1",
"--user", "nobody",
"--security-opt=no-new-privileges",
},
instance: Filter{Image: "example.com:version", Network: "test-1"},
},
{
name: "storage_mounts",
functionConfig: `apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`,
expectedArgs: []string{
"run",
"--rm",
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
"--network", "none",
"--user", "nobody",
"--security-opt=no-new-privileges",
"--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "bind", "/mount/path", "/local/"),
"--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "volume", "myvol", "/local/"),
"--mount", fmt.Sprintf("type=%s,src=%s,dst=%s:ro", "tmpfs", "", "/local/"),
},
instance: Filter{
Image: "example.com:version",
StorageMounts: []runtimeutil.StorageMount{
{MountType: "bind", Src: "/mount/path", DstPath: "/local/"},
{MountType: "volume", Src: "myvol", DstPath: "/local/"},
{MountType: "tmpfs", Src: "", DstPath: "/local/"},
},
},
},
}
for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
cfg, err := yaml.Parse(tt.functionConfig)
if !assert.NoError(t, err) {
t.FailNow()
}
tt.instance.Exec.FunctionConfig = cfg
os.Setenv("KYAML_TEST", "FOO")
tt.instance.setupExec()
// configure expected env
for _, e := range os.Environ() {
// the process env
tt.expectedArgs = append(tt.expectedArgs, "-e", strings.Split(e, "=")[0])
}
tt.expectedArgs = append(tt.expectedArgs, tt.instance.Image)
if !assert.Equal(t, "docker", tt.instance.Exec.Path) {
t.FailNow()
}
if !assert.Equal(t, tt.expectedArgs, tt.instance.Exec.Args) {
t.FailNow()
}
})
}
}
func TestFilter_Filter(t *testing.T) {
cfg, err := yaml.Parse(`apiVersion: apps/v1
kind: Deployment
metadata:
name: foo
`)
if !assert.NoError(t, err) {
return
}
input, err := (&kio.ByteReader{Reader: bytes.NewBufferString(`
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-foo
---
apiVersion: v1
kind: Service
metadata:
name: service-foo
`)}).Read()
if !assert.NoError(t, err) {
return
}
instance := Filter{}
instance.Exec.FunctionConfig = cfg
instance.Exec.Path = "sed"
instance.Exec.Args = []string{"s/Deployment/StatefulSet/g"}
output, err := instance.Filter(input)
if !assert.NoError(t, err) {
t.FailNow()
}
b := &bytes.Buffer{}
err = kio.ByteWriter{Writer: b, KeepReaderAnnotations: true}.Write(output)
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, `apiVersion: apps/v1
kind: StatefulSet
metadata:
name: deployment-foo
annotations:
config.kubernetes.io/index: '0'
config.kubernetes.io/path: 'statefulset_deployment-foo.yaml'
---
apiVersion: v1
kind: Service
metadata:
name: service-foo
annotations:
config.kubernetes.io/index: '1'
config.kubernetes.io/path: 'service_service-foo.yaml'
`, b.String()) {
t.FailNow()
}
}
func TestFilter_String(t *testing.T) {
instance := Filter{Image: "foo"}
if !assert.Equal(t, "foo", instance.String()) {
t.FailNow()
}
instance.Exec.DeferFailure = true
if !assert.Equal(t, "foo deferFailure: true", instance.String()) {
t.FailNow()
}
}
func TestFilter_ExitCode(t *testing.T) {
instance := Filter{}
instance.Exec.Path = "/not/real/command"
instance.Exec.DeferFailure = true
_, err := instance.Filter(nil)
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.EqualError(t, instance.GetExit(), "fork/exec /not/real/command: no such file or directory") {
t.FailNow()
}
}

View File

@@ -0,0 +1,5 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package exec contains the exec function implementation.
package exec

View File

@@ -0,0 +1,34 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package exec
import (
"io"
"os/exec"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
type Filter struct {
// Path is the path to the executable to run
Path string `yaml:"path,omitempty"`
// Args are the arguments to the executable
Args []string `yaml:"args,omitempty"`
runtimeutil.FunctionFilter
}
func (c *Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
c.FunctionFilter.Run = c.Run
return c.FunctionFilter.Filter(nodes)
}
func (c *Filter) Run(reader io.Reader, writer io.Writer) error {
cmd := exec.Command(c.Path, c.Args...)
cmd.Stdin = reader
cmd.Stdout = writer
return cmd.Run()
}

View File

@@ -0,0 +1,112 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package exec_test
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestFunctionFilter_Filter(t *testing.T) {
var tests = []struct {
name string
input []string
functionConfig string
expectedOutput []string
expectedError string
instance exec.Filter
}{
{
name: "exec_sed",
input: []string{
`apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-foo`,
`apiVersion: v1
kind: Service
metadata:
name: service-foo`,
},
expectedOutput: []string{
`apiVersion: apps/v1
kind: StatefulSet
metadata:
name: deployment-foo
annotations:
config.kubernetes.io/path: 'statefulset_deployment-foo.yaml'
`,
`apiVersion: v1
kind: Service
metadata:
name: service-foo
annotations:
config.kubernetes.io/path: 'service_service-foo.yaml'
`,
},
expectedError: "",
instance: exec.Filter{
Path: "sed",
Args: []string{"s/Deployment/StatefulSet/g"},
},
},
}
for i := range tests {
tt := tests[i]
t.Run(tt.name, func(t *testing.T) {
// initialize the inputs for the FunctionFilter
var inputs []*yaml.RNode
for i := range tt.input {
node, err := yaml.Parse(tt.input[i])
if !assert.NoError(t, err) {
t.FailNow()
}
inputs = append(inputs, node)
}
if tt.functionConfig != "" {
fc, err := yaml.Parse(tt.functionConfig)
if !assert.NoError(t, err) {
t.FailNow()
}
tt.instance.FunctionConfig = fc
}
// run the function
output, err := tt.instance.Filter(inputs)
// check for errors
if tt.expectedError != "" {
if !assert.EqualError(t, err, tt.expectedError) {
t.FailNow()
}
return
}
if !assert.NoError(t, err) {
t.FailNow()
}
// verify the output
var actual []string
for i := range output {
s, err := output[i].String()
if !assert.NoError(t, err) {
t.FailNow()
}
actual = append(actual, strings.TrimSpace(s))
}
var expected []string
for i := range tt.expectedOutput {
expected = append(expected, strings.TrimSpace(tt.expectedOutput[i]))
}
if !assert.Equal(t, expected, actual) {
t.FailNow()
}
})
}
}

View File

@@ -0,0 +1,5 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
// Package runtimeutil contains libraries for implementing function runtimes.
package runtimeutil

View File

@@ -1,9 +1,12 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package filters
package runtimeutil
import (
"fmt"
"strings"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
@@ -19,16 +22,25 @@ type FunctionSpec struct {
// Network is the name of the network to use from a container
Network string `json:"network,omitempty" yaml:"network,omitempty"`
DeferFailure bool `json:"deferFailure,omitempty" yaml:"deferFailure,omitempty"`
// Container is the spec for running a function as a container
Container ContainerSpec `json:"container,omitempty" yaml:"container,omitempty"`
// Starlark is the spec for running a function as a starlark script
Starlark StarlarkSpec `json:"starlark,omitempty" yaml:"starlark,omitempty"`
// ExecSpec is the spec for running a function as an executable
Exec ExecSpec `json:"exec,omitempty" yaml:"exec,omitempty"`
// Mounts are the storage or directories to mount into the container
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
}
type ExecSpec struct {
Path string `json:"path,omitempty" yaml:"path,omitempty"`
}
// ContainerSpec defines a spec for running a function as a container
type ContainerSpec struct {
// Image is the container image to run
@@ -70,6 +82,10 @@ type StorageMount struct {
DstPath string `json:"dst,omitempty" yaml:"dst,omitempty"`
}
func (s *StorageMount) String() string {
return fmt.Sprintf("type=%s,src=%s,dst=%s:ro", s.MountType, s.Src, s.DstPath)
}
// GetFunctionSpec returns the FunctionSpec for a resource. Returns
// nil if the resource does not have a FunctionSpec.
//
@@ -117,3 +133,52 @@ func getFunctionSpecFromAnnotation(n *yaml.RNode, meta yaml.ResourceMeta) *Funct
_ = yaml.Unmarshal([]byte(s), &fs)
return &fs
}
func StringToStorageMount(s string) StorageMount {
m := make(map[string]string)
options := strings.Split(s, ",")
for _, option := range options {
keyVal := strings.SplitN(option, "=", 2)
m[keyVal[0]] = keyVal[1]
}
var sm StorageMount
for key, value := range m {
switch {
case key == "type":
sm.MountType = value
case key == "src":
sm.Src = value
case key == "dst":
sm.DstPath = value
}
}
return sm
}
// IsReconcilerFilter filters Resources based on whether or not they are Reconciler Resource.
// Resources with an apiVersion starting with '*.gcr.io', 'gcr.io' or 'docker.io' are considered
// Reconciler Resources.
type IsReconcilerFilter struct {
// ExcludeReconcilers if set to true, then Reconcilers will be excluded -- e.g.
// Resources with a reconcile container through the apiVersion (gcr.io prefix) or
// through the annotations
ExcludeReconcilers bool `yaml:"excludeReconcilers,omitempty"`
// IncludeNonReconcilers if set to true, the NonReconciler will be included.
IncludeNonReconcilers bool `yaml:"includeNonReconcilers,omitempty"`
}
// Filter implements kio.Filter
func (c *IsReconcilerFilter) Filter(inputs []*yaml.RNode) ([]*yaml.RNode, error) {
var out []*yaml.RNode
for i := range inputs {
isFnResource := GetFunctionSpec(inputs[i]) != nil
if isFnResource && !c.ExcludeReconcilers {
out = append(out, inputs[i])
}
if !isFnResource && c.IncludeNonReconcilers {
out = append(out, inputs[i])
}
}
return out, nil
}

View File

@@ -0,0 +1,198 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package runtimeutil
import (
"bytes"
"io"
"io/ioutil"
"path"
"strings"
"sigs.k8s.io/kustomize/kyaml/errors"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
// FunctionFilter wraps another filter to be invoked in the context of a function.
// FunctionFilter manages scoping the function, deferring failures, and saving results
// to files.
type FunctionFilter struct {
// Run implements the function.
Run func(reader io.Reader, writer io.Writer) error
// FunctionConfig is passed to the function through ResourceList.functionConfig.
FunctionConfig *yaml.RNode `yaml:"functionConfig,omitempty"`
// GlobalScope explicitly scopes the function to all input resources rather than only those
// resources scoped to it by path.
GlobalScope bool
// ResultsFile is the file to write function ResourceList.results to.
// If unset, results will not be written.
ResultsFile string
// DeferFailure will cause the Filter to return a nil error even if Run returns an error.
// The Run error will be available through GetExit().
DeferFailure bool
// results saves the results emitted from Run
results *yaml.RNode
// exit saves the error returned from Run
exit error
}
// GetExit returns the error from Run
func (c FunctionFilter) GetExit() error {
return c.exit
}
// functionsDirectoryName is keyword directory name for functions scoped 1 directory higher
const functionsDirectoryName = "functions"
// getFunctionScope returns the path of the directory containing the function config,
// or its parent directory if the base directory is named "functions"
func (c *FunctionFilter) getFunctionScope() (string, error) {
m, err := c.FunctionConfig.GetMeta()
if err != nil {
return "", errors.Wrap(err)
}
p, found := m.Annotations[kioutil.PathAnnotation]
if !found {
return "", nil
}
functionDir := path.Clean(path.Dir(p))
if path.Base(functionDir) == functionsDirectoryName {
// the scope of functions in a directory called "functions" is 1 level higher
// this is similar to how the golang "internal" directory scoping works
functionDir = path.Dir(functionDir)
}
return functionDir, nil
}
// scope partitions the input nodes into 2 slices. The first slice contains only Resources
// which are scoped under dir, and the second slice contains the Resources which are not.
func (c *FunctionFilter) scope(dir string, nodes []*yaml.RNode) ([]*yaml.RNode, []*yaml.RNode, error) {
// scope container filtered Resources to Resources under that directory
var input, saved []*yaml.RNode
if c.GlobalScope {
return nodes, nil, nil
}
// global function
if dir == "" || dir == "." {
return nodes, nil, nil
}
// identify Resources read from directories under the function configuration
for i := range nodes {
m, err := nodes[i].GetMeta()
if err != nil {
return nil, nil, err
}
p, found := m.Annotations[kioutil.PathAnnotation]
if !found {
// this Resource isn't scoped under the function -- don't know where it came from
// consider it out of scope
saved = append(saved, nodes[i])
continue
}
resourceDir := path.Clean(path.Dir(p))
if path.Base(resourceDir) == functionsDirectoryName {
// Functions in the `functions` directory are scoped to
// themselves, and should see themselves as input
resourceDir = path.Dir(resourceDir)
}
if !strings.HasPrefix(resourceDir, dir) {
// this Resource doesn't fall under the function scope if it
// isn't in a subdirectory of where the function lives
saved = append(saved, nodes[i])
continue
}
// this input is scoped under the function
input = append(input, nodes[i])
}
return input, saved, nil
}
func (c *FunctionFilter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
in := &bytes.Buffer{}
out := &bytes.Buffer{}
// only process Resources scoped to this function, save the others
functionDir, err := c.getFunctionScope()
if err != nil {
return nil, err
}
input, saved, err := c.scope(functionDir, nodes)
if err != nil {
return nil, err
}
// write the input
err = kio.ByteWriter{
WrappingAPIVersion: kio.ResourceListAPIVersion,
WrappingKind: kio.ResourceListKind,
Writer: in,
KeepReaderAnnotations: true,
FunctionConfig: c.FunctionConfig}.Write(input)
if err != nil {
return nil, err
}
// capture the command stdout for the return value
r := &kio.ByteReader{Reader: out}
// don't exit immediately if the function fails -- write out the validation
c.exit = c.Run(in, out)
output, err := r.Read()
if err != nil {
return nil, err
}
if err := c.doResults(r); err != nil {
return nil, err
}
if c.exit != nil && !c.DeferFailure {
return append(output, saved...), c.exit
}
// annotate any generated Resources with a path and index if they don't already have one
if err := kioutil.DefaultPathAnnotation(functionDir, output); err != nil {
return nil, err
}
// emit both the Resources output from the function, and the out-of-scope Resources
// which were not provided to the function
return append(output, saved...), nil
}
func (c *FunctionFilter) doResults(r *kio.ByteReader) error {
// Write the results to a file if configured to do so
if c.ResultsFile != "" && r.Results != nil {
results, err := r.Results.String()
if err != nil {
return err
}
err = ioutil.WriteFile(c.ResultsFile, []byte(results), 0600)
if err != nil {
return err
}
}
if r.Results != nil {
c.results = r.Results
}
return nil
}

File diff suppressed because it is too large Load Diff

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