mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-17 11:58:18 +00:00
Compare commits
245 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
633c43a672 | ||
|
|
0833693372 | ||
|
|
77c07ba96e | ||
|
|
6847bb7924 | ||
|
|
f0deaf707d | ||
|
|
e113944027 | ||
|
|
1cf9131ae2 | ||
|
|
da142a8e97 | ||
|
|
6c81e3b95f | ||
|
|
a0089a2521 | ||
|
|
11768f6232 | ||
|
|
675c17737f | ||
|
|
735a93d000 | ||
|
|
67d2c2ed4a | ||
|
|
f931e15653 | ||
|
|
34169174a8 | ||
|
|
ebf33964c7 | ||
|
|
38a5e12d66 | ||
|
|
04ab218fa0 | ||
|
|
950c353f90 | ||
|
|
aff09b1108 | ||
|
|
6da691f874 | ||
|
|
22c99aa535 | ||
|
|
5fa209acfa | ||
|
|
d72879e109 | ||
|
|
337f3631ff | ||
|
|
b3993dc874 | ||
|
|
11c04dd6c4 | ||
|
|
b29e449d4a | ||
|
|
430f2f84fb | ||
|
|
52c6b5755b | ||
|
|
958bc63293 | ||
|
|
94ed0fe515 | ||
|
|
bb8233ceff | ||
|
|
6221bed190 | ||
|
|
1ffeb181e7 | ||
|
|
759ba1cbf4 | ||
|
|
12f2c41273 | ||
|
|
2174741376 | ||
|
|
31dd8fc5b1 | ||
|
|
77f4811779 | ||
|
|
7050a45134 | ||
|
|
3b64f1e102 | ||
|
|
6c4c79e2cc | ||
|
|
3975ebc21a | ||
|
|
ec95e5f97e | ||
|
|
72b1a4bc5c | ||
|
|
16447efca3 | ||
|
|
524d593c5c | ||
|
|
3b644474c4 | ||
|
|
42e6ced2b0 | ||
|
|
f018370628 | ||
|
|
c9a8bc1121 | ||
|
|
8c7cbb12dd | ||
|
|
b02f7775c5 | ||
|
|
f9a0e671b7 | ||
|
|
70fb22cad6 | ||
|
|
2ae00db6a9 | ||
|
|
f9577ab540 | ||
|
|
6a2786a5c4 | ||
|
|
924aa6fb29 | ||
|
|
e2cd44f9d8 | ||
|
|
017c4ae0aa | ||
|
|
7b2baad390 | ||
|
|
bc2d69f4f9 | ||
|
|
e913a71fad | ||
|
|
7406dda074 | ||
|
|
0b4df3d414 | ||
|
|
7d38916d63 | ||
|
|
79d1abe573 | ||
|
|
9563052094 | ||
|
|
f881c19bb6 | ||
|
|
8d7b5f82c4 | ||
|
|
7554406c61 | ||
|
|
cf17050170 | ||
|
|
3857a67701 | ||
|
|
10665c6fc9 | ||
|
|
e0a09f4755 | ||
|
|
31c6a55747 | ||
|
|
8332a70d19 | ||
|
|
7fe2338acd | ||
|
|
43d4dbc07a | ||
|
|
f0cf4579d2 | ||
|
|
68ba37f139 | ||
|
|
bf73633cda | ||
|
|
55f8828ba1 | ||
|
|
0e1307dccf | ||
|
|
4471b75912 | ||
|
|
75c6204337 | ||
|
|
1b7171ac9e | ||
|
|
5193d6b4a8 | ||
|
|
6a834b6262 | ||
|
|
083d3cbb65 | ||
|
|
e68411b71e | ||
|
|
664774576c | ||
|
|
37e97084f9 | ||
|
|
de4d8b7dfa | ||
|
|
7f97108686 | ||
|
|
71f069cf95 | ||
|
|
3dbe732cb5 | ||
|
|
e5aea4423b | ||
|
|
100f05260e | ||
|
|
02f9329747 | ||
|
|
b6abd7600c | ||
|
|
2e7093e67f | ||
|
|
3b3a272d27 | ||
|
|
36115a7fa3 | ||
|
|
4d9d54e2c7 | ||
|
|
88aec95628 | ||
|
|
e30401489d | ||
|
|
58bc4b14a2 | ||
|
|
2824c28e08 | ||
|
|
d7cbb95d9c | ||
|
|
e771ec1169 | ||
|
|
9e5374e725 | ||
|
|
4569a09d54 | ||
|
|
25d3ad7522 | ||
|
|
77e18724db | ||
|
|
12d1771bb3 | ||
|
|
a78aa22399 | ||
|
|
05a91893bf | ||
|
|
8d420ec3f7 | ||
|
|
838a766d12 | ||
|
|
50d79e4d3e | ||
|
|
4d2d450f6e | ||
|
|
fdc46fb0b1 | ||
|
|
92ac9b5a0e | ||
|
|
857a9df70f | ||
|
|
969f4f28fa | ||
|
|
58aa45c50a | ||
|
|
5715f4bab4 | ||
|
|
c8502c78f5 | ||
|
|
909de5c94a | ||
|
|
2eaeb83ec3 | ||
|
|
03b9c2a3a3 | ||
|
|
59b98727ec | ||
|
|
5851f96524 | ||
|
|
08be3f061e | ||
|
|
5906aaba19 | ||
|
|
4b6f180d0c | ||
|
|
7f22f187f8 | ||
|
|
fa3a64e352 | ||
|
|
82f2cf9124 | ||
|
|
276693cf0e | ||
|
|
0197c019cc | ||
|
|
9576a81787 | ||
|
|
ff4a1c0b4f | ||
|
|
7dd28b1fd9 | ||
|
|
b754557418 | ||
|
|
f305c0d791 | ||
|
|
3fdaa2e903 | ||
|
|
964c74fb46 | ||
|
|
f14988ff80 | ||
|
|
f1adbfdbff | ||
|
|
072bf992b0 | ||
|
|
2d0d09e178 | ||
|
|
564b0d6827 | ||
|
|
187415430f | ||
|
|
afac2fb46a | ||
|
|
20fd433f75 | ||
|
|
1e3824057b | ||
|
|
5edae84a9e | ||
|
|
9432671887 | ||
|
|
8fda0f87ab | ||
|
|
08bc8637c8 | ||
|
|
9645f397ef | ||
|
|
ed9f716361 | ||
|
|
9986b65326 | ||
|
|
94dab9ddc4 | ||
|
|
81f246ed60 | ||
|
|
30ed50eb27 | ||
|
|
4325401fe7 | ||
|
|
65af5c13f1 | ||
|
|
9674fd12b2 | ||
|
|
2377902a0b | ||
|
|
1dbde0b085 | ||
|
|
5920563bbd | ||
|
|
23201c27f0 | ||
|
|
d4c7131f8f | ||
|
|
d2b189874b | ||
|
|
98a38eb290 | ||
|
|
aa729229e2 | ||
|
|
afbc1b0401 | ||
|
|
3305be9589 | ||
|
|
36772aac89 | ||
|
|
7755d6cac2 | ||
|
|
6f82073d4b | ||
|
|
2a3f09a2f0 | ||
|
|
6392e6629f | ||
|
|
c25ed7f7bc | ||
|
|
918247d2cc | ||
|
|
0c260ef804 | ||
|
|
2a06a174e8 | ||
|
|
54e8a014bc | ||
|
|
5b67b580f2 | ||
|
|
6a67183ed7 | ||
|
|
a38befdaa1 | ||
|
|
0312cdf677 | ||
|
|
991ffbbdfc | ||
|
|
bbd29d9dc1 | ||
|
|
28953e03a0 | ||
|
|
37489ec2e9 | ||
|
|
636ab874eb | ||
|
|
90d16c2377 | ||
|
|
5d24dda28a | ||
|
|
dec5109e31 | ||
|
|
cc8690381c | ||
|
|
f5f95e3692 | ||
|
|
809d5b1fe2 | ||
|
|
38b4365ab3 | ||
|
|
d865300fdb | ||
|
|
e2677cdc8a | ||
|
|
ea00134776 | ||
|
|
ad3cd47c25 | ||
|
|
a1dcf3386b | ||
|
|
e7ecceb0c2 | ||
|
|
50c40eb80c | ||
|
|
398ceb0a92 | ||
|
|
b7be630924 | ||
|
|
f557841e54 | ||
|
|
9fc24634a2 | ||
|
|
0617a283a0 | ||
|
|
f616e30a38 | ||
|
|
50b197f329 | ||
|
|
6fd0330b80 | ||
|
|
8127b09d12 | ||
|
|
09ab2bb5c0 | ||
|
|
54ac4e73e7 | ||
|
|
d4ad7f80e0 | ||
|
|
623e21d1c0 | ||
|
|
0c88c43c67 | ||
|
|
c6d8bcb01b | ||
|
|
5285e6101f | ||
|
|
2fb69db685 | ||
|
|
730597b77e | ||
|
|
d488d9804d | ||
|
|
f98bc42cbb | ||
|
|
d7b9f64c5a | ||
|
|
785291af62 | ||
|
|
4f05482e00 | ||
|
|
3c3f85e623 | ||
|
|
40bb81142b | ||
|
|
46e8fd7065 | ||
|
|
4e7610a44d | ||
|
|
5a3c6553fc |
@@ -8,11 +8,13 @@ go:
|
||||
before_install:
|
||||
- source ./bin/consider-early-travis-exit.sh
|
||||
- sudo apt-get install tree
|
||||
- go get -u github.com/opennota/check/cmd/varcheck
|
||||
- go get -u github.com/golang/lint/golint
|
||||
- go get -u golang.org/x/tools/cmd/goimports
|
||||
- go get -u github.com/onsi/ginkgo/ginkgo
|
||||
- go get -u github.com/monopole/mdrip
|
||||
- go get -u github.com/fzipp/gocyclo
|
||||
- go get -u gopkg.in/alecthomas/gometalinter.v2 && gometalinter.v2 --install
|
||||
|
||||
# Install must be set to prevent default `go get` to run.
|
||||
# The dependencies have already been vendored by `dep` so
|
||||
|
||||
291
Gopkg.lock
generated
291
Gopkg.lock
generated
@@ -2,120 +2,351 @@
|
||||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8e47871087b94913898333f37af26732faaab30cdb41571136cf7aec9921dae7"
|
||||
name = "github.com/PuerkitoBio/purell"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:331a419049c2be691e5ba1d24342fc77c7e767a80c666a18fd8a9f7b82419c1c"
|
||||
name = "github.com/PuerkitoBio/urlesc"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9299ad32dcec0f92ad06773f73426bd46a21efa96f6a8138c287bb185933e77e"
|
||||
name = "github.com/aws/aws-sdk-go"
|
||||
packages = [
|
||||
"aws",
|
||||
"aws/awserr",
|
||||
"aws/awsutil",
|
||||
"aws/client",
|
||||
"aws/client/metadata",
|
||||
"aws/corehandlers",
|
||||
"aws/credentials",
|
||||
"aws/credentials/ec2rolecreds",
|
||||
"aws/credentials/endpointcreds",
|
||||
"aws/credentials/stscreds",
|
||||
"aws/csm",
|
||||
"aws/defaults",
|
||||
"aws/ec2metadata",
|
||||
"aws/endpoints",
|
||||
"aws/request",
|
||||
"aws/session",
|
||||
"aws/signer/v4",
|
||||
"internal/sdkio",
|
||||
"internal/sdkrand",
|
||||
"internal/sdkuri",
|
||||
"internal/shareddefaults",
|
||||
"private/protocol",
|
||||
"private/protocol/eventstream",
|
||||
"private/protocol/eventstream/eventstreamapi",
|
||||
"private/protocol/query",
|
||||
"private/protocol/query/queryutil",
|
||||
"private/protocol/rest",
|
||||
"private/protocol/restxml",
|
||||
"private/protocol/xml/xmlutil",
|
||||
"service/s3",
|
||||
"service/sts",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "daed0c76021ea9c4e659e3ec80bcd2d657297100"
|
||||
version = "v1.15.12"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:98e84060475ed245c3b355042afd43a74aa7d32efe50658f4f995977916f9fc3"
|
||||
name = "github.com/bgentry/go-netrc"
|
||||
packages = ["netrc"]
|
||||
pruneopts = ""
|
||||
revision = "9fd32a8b3d3d3f9d43c341bfe098430e07609480"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b"
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
pruneopts = ""
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:971e9ba63a417c5f1f83ab358677bc59e96ff04285f26c6646ff089fb60b15e8"
|
||||
name = "github.com/emicklei/go-restful"
|
||||
packages = [
|
||||
".",
|
||||
"log",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "3658237ded108b4134956c1b3050349d93e7b895"
|
||||
version = "v2.7.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:dcefbadf4534c5ecac8573698fba6e6e601157bfa8f96aafe29df31ae582ef2a"
|
||||
name = "github.com/evanphx/json-patch"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "afac545df32f2287a079e2dfb7ba2745a643747e"
|
||||
version = "v3.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b13707423743d41665fd23f0c36b2f37bb49c30e94adb813319c44188a51ba22"
|
||||
name = "github.com/ghodss/yaml"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:858b7fe7b0f4bc7ef9953926828f2816ea52d01a88d72d1c45bc8c108f23c356"
|
||||
name = "github.com/go-ini/ini"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "358ee7663966325963d4e8b2e1fbd570c5195153"
|
||||
version = "v1.38.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e116a4866bffeec941056a1fcfd37e520fad1ee60e4e3579719f19a43c392e10"
|
||||
name = "github.com/go-openapi/jsonpointer"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:3830527ef0f4f9b268d9286661c0f52f9115f8aefd9f45ee7352516f93489ac9"
|
||||
name = "github.com/go-openapi/jsonreference"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:238a056875c4b053b4b29984765ee335bf8c539fdf17e527fd9b7aa72521c8dd"
|
||||
name = "github.com/go-openapi/spec"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "bcff419492eeeb01f76e77d2ebc714dc97b607f5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:7b067ca8b94982960860d18c42e29f15bbd0e8d9ae8145a83a218296e75393cf"
|
||||
name = "github.com/go-openapi/swag"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "811b1089cde9dad18d4d0c2d09fbdbf28dbd27a5"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0a3f6a0c68ab8f3d455f8892295503b179e571b7fefe47cc6c556405d1f83411"
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"sortkeys"
|
||||
"sortkeys",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:107b233e45174dbab5b1324201d092ea9448e58243ab9f039e4c0f332e121e3a"
|
||||
name = "github.com/golang/glog"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f958a1c137db276e52f0b50efee41a1a389dcdded59a69711f3e872757dab34b"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/timestamp"
|
||||
"ptypes/timestamp",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:754f77e9c839b24778a4b64422236d38515301d2baeb63113aa3edc42e6af692"
|
||||
name = "github.com/google/gofuzz"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2a131706ff80636629ab6373f2944569b8252ecc018cda8040931b05d32e3c16"
|
||||
name = "github.com/googleapis/gnostic"
|
||||
packages = [
|
||||
"OpenAPIv2",
|
||||
"compiler",
|
||||
"extensions"
|
||||
"extensions",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:f5d25fd7bdda08e39e01193ef94a1ebf7547b1b931bcdec785d08050598f306c"
|
||||
name = "github.com/hashicorp/go-cleanhttp"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "d5fe4b57a186c716b0e00b8c301cbd9b4182694d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:fd15b3f6aac9d0fe68c6e38922282e0d2e88cd77b927ac3dd842e363645522c0"
|
||||
name = "github.com/hashicorp/go-getter"
|
||||
packages = [
|
||||
".",
|
||||
"helper/url",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "4bda8fa99001c61db3cad96b421d4c12a81f256d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2cf6c60c74eacadd31652674364af55c8d54a86b8ea193548f1c37f8c9af8f9c"
|
||||
name = "github.com/hashicorp/go-safetemp"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "b1a1dbde6fdc11e3ae79efd9039009e22d4ae240"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:139bdc2c89779b8ff8b1150be28f889b0ed964e6da96f32cbc9035bd4642881c"
|
||||
name = "github.com/hashicorp/go-version"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "270f2f71b1ee587f3b609f00f422b76a6b28f348"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6f49eae0c1e5dab1dafafee34b207aeb7a42303105960944828c2079b92fc88e"
|
||||
name = "github.com/jmespath/go-jmespath"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "0b12d6b5"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9eab2325abbed0ebcee9d44bb3660a69d5d10e42d5ac4a0e77f7a6ea22bfce88"
|
||||
name = "github.com/json-iterator/go"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
|
||||
version = "1.1.3"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d9e483f4b9e306facf126bd90b02d512bd22ea4471e1568867e32221a8abbb16"
|
||||
name = "github.com/mailru/easyjson"
|
||||
packages = [
|
||||
"buffer",
|
||||
"jlexer",
|
||||
"jwriter",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:83854f6b1d2ce047b69657e3a87ba7602f4c5505e8bdfd02ab857db8e983bde1"
|
||||
name = "github.com/mitchellh/go-homedir"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "58046073cbffe2f25d425fe1331102f55cf719de"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:51c98e2c9a8d0a724a69f46421876af14e12132cb02f1d0e144785d752247162"
|
||||
name = "github.com/mitchellh/go-testing-interface"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "a61a99592b77c9ba629d254a693acffaeb4b7e28"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0c0ff2a89c1bb0d01887e1dac043ad7efbf3ec77482ef058ac423d13497e16fd"
|
||||
name = "github.com/modern-go/concurrent"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
|
||||
version = "1.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:420f9231f816eeca3ff5aab070caac3ed7f27e4d37ded96ce9de3d7a7a2e31ad"
|
||||
name = "github.com/modern-go/reflect2"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
|
||||
version = "1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:74c32990510c9f188556aa17600313e867d1d06f5a9db244056a95d144ec34ce"
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:8e243c568f36b09031ec18dff5f7d2769dcf5ca4d624ea511c8e3197dc3d352d"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ee723e6a1962a196eeba1b24f82af61a4f60f8821d7aa96d48e787f8337bcffc"
|
||||
name = "github.com/ulikunitz/xz"
|
||||
packages = [
|
||||
".",
|
||||
"internal/hash",
|
||||
"internal/xlog",
|
||||
"lzma",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "0c6b41e72360850ca4f98dc341fd999726ea007f"
|
||||
version = "v0.5.4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:9e548233d0dc00e74be262e54a9d1bbe7e4c19e5951083520261740e37daeb02"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"http/httpguts",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna"
|
||||
"idna",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "2491c5de3490fced2f6cff376127c667efeed857"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
@@ -131,25 +362,32 @@
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable"
|
||||
"unicode/rangetable",
|
||||
"width",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:75fb3fcfc73a8c723efde7777b40e8e8ff9babf30d8c56160d01beffea8a95a6"
|
||||
name = "gopkg.in/inf.v0"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
|
||||
version = "v0.9.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:663df6da5560210fc39194a0a2c4fceba09ead717c330f1174bb15597cf18ce8"
|
||||
name = "k8s.io/api"
|
||||
packages = [
|
||||
"admissionregistration/v1alpha1",
|
||||
@@ -179,12 +417,14 @@
|
||||
"settings/v1alpha1",
|
||||
"storage/v1",
|
||||
"storage/v1alpha1",
|
||||
"storage/v1beta1"
|
||||
"storage/v1beta1",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "53d615ae3f440f957cb9989d989d597f047262d9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:bcb2285bb525712de7903a5d254c2789df65c8b58d2cfac5a26d950ad94c2079"
|
||||
name = "k8s.io/apimachinery"
|
||||
packages = [
|
||||
"pkg/api/resource",
|
||||
@@ -218,31 +458,52 @@
|
||||
"pkg/util/yaml",
|
||||
"pkg/watch",
|
||||
"third_party/forked/golang/json",
|
||||
"third_party/forked/golang/reflect"
|
||||
"third_party/forked/golang/reflect",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "13b73596e4b63e03203e86f6d9c7bcc1b937c62f"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:071cc2f032b701b9dba26568e040940f26931a49e3a3985f3375f17f7f6d9c5f"
|
||||
name = "k8s.io/client-go"
|
||||
packages = ["kubernetes/scheme"]
|
||||
pruneopts = ""
|
||||
revision = "23781f4d6632d88e869066eaebb743857aa1ef9b"
|
||||
version = "v7.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:386c5d69077ce740614e8309ddf107dde91a5db25d3d779143f452fb4fbdfd1e"
|
||||
name = "k8s.io/kube-openapi"
|
||||
packages = ["pkg/util/proto"]
|
||||
packages = [
|
||||
"pkg/common",
|
||||
"pkg/util/proto",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "b3f03f55328800731ce03a164b80973014ecd455"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "k8s.io/utils"
|
||||
packages = ["exec"]
|
||||
revision = "258e2a2fa64568210fbd6267cf1d8fd87c3cb86e"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "e966d7880a29cf5669060d6564407f0f4c164e93eb844c22efec383383af2d3e"
|
||||
input-imports = [
|
||||
"github.com/evanphx/json-patch",
|
||||
"github.com/ghodss/yaml",
|
||||
"github.com/golang/glog",
|
||||
"github.com/hashicorp/go-getter",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/spf13/cobra",
|
||||
"k8s.io/api/core/v1",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
||||
"k8s.io/apimachinery/pkg/runtime",
|
||||
"k8s.io/apimachinery/pkg/runtime/schema",
|
||||
"k8s.io/apimachinery/pkg/util/mergepatch",
|
||||
"k8s.io/apimachinery/pkg/util/sets",
|
||||
"k8s.io/apimachinery/pkg/util/strategicpatch",
|
||||
"k8s.io/apimachinery/pkg/util/validation",
|
||||
"k8s.io/apimachinery/pkg/util/yaml",
|
||||
"k8s.io/client-go/kubernetes/scheme",
|
||||
"k8s.io/kube-openapi/pkg/common",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
||||
10
Gopkg.toml
10
Gopkg.toml
@@ -49,6 +49,14 @@
|
||||
name = "k8s.io/client-go"
|
||||
version = "7.0.0"
|
||||
|
||||
[[constraint]]
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "k8s.io/utils"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/go-openapi/spec"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-getter"
|
||||
|
||||
@@ -127,7 +127,7 @@ This tool is sponsored by [sig-cli] ([KEP]).
|
||||
[examples]: examples/README.md
|
||||
[imageBase]: docs/base.jpg
|
||||
[imageOverlay]: docs/overlay.jpg
|
||||
[install]: INSTALL.md
|
||||
[install]: docs/INSTALL.md
|
||||
[kubernetes style]: docs/glossary.md#kubernetes-style-object
|
||||
[kustomization]: docs/glossary.md#kustomization
|
||||
[overlay]: docs/glossary.md#overlay
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Make sure, we run in the root of the repo and
|
||||
# therefore run the tests on all packages
|
||||
@@ -36,14 +37,29 @@ function testGoCyclo {
|
||||
diff <(echo -n) <(go_dirs | xargs -0 gocyclo -over 15)
|
||||
}
|
||||
|
||||
function testGoImports {
|
||||
diff -u <(echo -n) <(go_dirs | xargs -0 goimports -l)
|
||||
}
|
||||
|
||||
function testGoLint {
|
||||
diff -u <(echo -n) <(go_dirs | xargs -0 golint --min_confidence 0.85 )
|
||||
}
|
||||
|
||||
# Not using the 'goimports' check because it reports hyphens in imported
|
||||
# package names as errors, and we vendor in packages that have
|
||||
# hyphens in their names.
|
||||
function testGoMetalinter {
|
||||
diff -u <(echo -n) <(go_dirs | xargs -0 gometalinter.v2 --disable-all --deadline 5m \
|
||||
--enable=misspell \
|
||||
--enable=structcheck \
|
||||
--enable=deadcode \
|
||||
--enable=varcheck \
|
||||
--enable=goconst \
|
||||
--enable=unparam \
|
||||
--enable=ineffassign \
|
||||
--enable=nakedret \
|
||||
--enable=interfacer \
|
||||
--enable=misspell \
|
||||
--line-length=170 --enable=lll \
|
||||
--dupl-threshold=400 --enable=dupl)
|
||||
}
|
||||
|
||||
function testGoVet {
|
||||
go vet -all ./...
|
||||
}
|
||||
@@ -57,7 +73,7 @@ function testExamples {
|
||||
}
|
||||
|
||||
runTest testGoFmt
|
||||
runTest testGoImports
|
||||
runTest testGoMetalinter
|
||||
runTest testGoLint
|
||||
runTest testGoVet
|
||||
runTest testGoCyclo
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
[releases page]: https://github.com/kubernetes-sigs/kustomize/releases
|
||||
[`container-builder-local`]: https://github.com/GoogleCloudPlatform/container-builder-local
|
||||
[Google Container Builder]: https://cloud.google.com/container-builder
|
||||
[`cloud-build-local`]: https://github.com/GoogleCloudPlatform/cloud-build-local
|
||||
[Google Cloud Build]: https://cloud.google.com/cloud-build
|
||||
|
||||
Scripts and configuration files for publishing a
|
||||
`kustomize` release on the [releases page].
|
||||
|
||||
### Build a release locally
|
||||
|
||||
Install [`container-builder-local`], then run
|
||||
Install [`cloud-build-local`], then run
|
||||
|
||||
```
|
||||
container-builder-local \
|
||||
cloud-build-local \
|
||||
--config=build/cloudbuild_local.yaml \
|
||||
--dryrun=false --write-workspace=/tmp/w .
|
||||
```
|
||||
@@ -41,5 +41,5 @@ Push the tag upstream:
|
||||
git push upstream $version
|
||||
```
|
||||
|
||||
The new tag will trigger a job in [Google Container
|
||||
Builder] to put a new release on the [releases page].
|
||||
The new tag will trigger a job in [Google Cloud
|
||||
Build] to put a new release on the [releases page].
|
||||
|
||||
@@ -56,4 +56,4 @@ case $key in
|
||||
esac
|
||||
done
|
||||
|
||||
/goreleaser release --config=build/goreleaser.yml --rm-dist --skip-validate ${SNAPSHOT}
|
||||
/goreleaser release --config=build/goreleaser.yaml --rm-dist --skip-validate ${SNAPSHOT}
|
||||
|
||||
@@ -4,7 +4,7 @@ project_name: kustomize
|
||||
builds:
|
||||
- main: ./kustomize.go
|
||||
binary: kustomize
|
||||
ldflags: -s -X github.com/kubernetes-sigs/kustomize/version.kustomizeVersion={{.Version}} -X github.com/kubernetes-sigs/kustomize/version.gitCommit={{.Commit}} -X github.com/kubernetes-sigs/kustomize/version.buildDate={{.Date}}
|
||||
ldflags: -s -X github.com/kubernetes-sigs/kustomize/pkg/commands.kustomizeVersion={{.Version}} -X github.com/kubernetes-sigs/kustomize/pkg/commands.gitCommit={{.Commit}} -X github.com/kubernetes-sigs/kustomize/pkg/commands.buildDate={{.Date}}
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
project_name: kustomize
|
||||
builds:
|
||||
- main: ./kustomize.go
|
||||
binary: kustomize
|
||||
ldflags: -s -X github.com/kubernetes-sigs/kustomize/version.kustomizeVersion={{.Version}} -X github.com/kubernetes-sigs/kustomize/version.gitCommit={{.Commit}} -X github.com/kubernetes-sigs/kustomize/version.buildDate={{.Date}}
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
archive:
|
||||
format: binary
|
||||
snapshot:
|
||||
name_template: "master"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- Merge pull request
|
||||
- Merge branch
|
||||
release:
|
||||
github:
|
||||
owner: kubernetes-sigs
|
||||
name: kustomize
|
||||
@@ -1,6 +0,0 @@
|
||||
[Kubernetes Community Code of Conduct]: https://git.k8s.io/community/code-of-conduct.md
|
||||
|
||||
# Code of Conduct
|
||||
|
||||
This project has adopted the
|
||||
[Kubernetes Community Code of Conduct].
|
||||
4
docs/CODE_OF_CONDUCT.md
Normal file
4
docs/CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Kustomize Community Code of Conduct
|
||||
|
||||
Kustomize contributers expected to adhere to
|
||||
the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
@@ -3,7 +3,13 @@
|
||||
|
||||
## Installation
|
||||
|
||||
Download a binary from the [release page].
|
||||
On macOS, you can install kustomize with Homebrew package
|
||||
manager:
|
||||
|
||||
brew install kustomize
|
||||
|
||||
For all operating systems, download a binary from the
|
||||
[release page].
|
||||
|
||||
Or try this to grab the latest official release
|
||||
using the command line:
|
||||
23
docs/README.md
Normal file
23
docs/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Kustomize docs
|
||||
|
||||
* [installation instructions](INSTALL.md)
|
||||
|
||||
* [kustomization.yaml](kustomization.yaml) - Example of a
|
||||
[kustomization](glossary.md#kustomization)
|
||||
with explanations of each field.
|
||||
|
||||
* [workflow](workflows.md) - Some steps one might take in using
|
||||
bespoke and off-the-shelf configurations.
|
||||
|
||||
* [glossary](glossary.md) - An attempt to disambiguiate terminology.
|
||||
|
||||
* [eschewed features](eschewedFeatures.md) - Why certain features are (currently)
|
||||
not supported in Kustomize.
|
||||
|
||||
* [contributing guidelines](CONTRIBUTING.md) - Please read before sending a PR.
|
||||
|
||||
* [code of conduct](CODE_OF_CONDUCT.md)
|
||||
|
||||
|
||||
|
||||
|
||||
15
docs/SECURITY_CONTACTS
Normal file
15
docs/SECURITY_CONTACTS
Normal file
@@ -0,0 +1,15 @@
|
||||
# Defined below are the security contacts for this repo.
|
||||
#
|
||||
# They are the contact point for the Product Security Team to reach out
|
||||
# to for triaging and handling of incoming issues.
|
||||
#
|
||||
# The below names agree to abide by the
|
||||
# [Embargo Policy](https://github.com/kubernetes/sig-release/blob/master/security-release-process-documentation/security-release-process.md#embargo-policy)
|
||||
# and will be removed and replaced if they violate that agreement.
|
||||
#
|
||||
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||
|
||||
monopole
|
||||
Liujingfang1
|
||||
pwittrock
|
||||
80
docs/eschewedFeatures.md
Normal file
80
docs/eschewedFeatures.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Eschewed Features
|
||||
|
||||
## Removal directives
|
||||
|
||||
`kustomize` supports configurations that can be reasoned about as
|
||||
_compositions_ or _mixins_ - concepts that are widely accepted as
|
||||
a best practice in various programming languages.
|
||||
|
||||
To this end, `kustomize` offers various _addition_ directives. One
|
||||
can add labels, annotations, patches, resources and bases.
|
||||
Corresponding _removal_ directives are not offered.
|
||||
|
||||
Removal semantics would introduce many possibilities for
|
||||
inconsistency, and the need to add code to detect, report and
|
||||
reject it. It would also allow, and possibly encourage,
|
||||
unnecessarily complex configuration layouts.
|
||||
|
||||
When faced with a situation where removal is desirable, it's
|
||||
always possible to remove things from a base like labels and
|
||||
annotations, and/or split multi-resource manifests into individual
|
||||
resource files - then add things back as desired via the
|
||||
[kustomization].
|
||||
|
||||
If the underlying base is outside of one's control, an [OTS
|
||||
workflow] is the recommended best practice. Fork the base, remove
|
||||
what you don't want and commit it to your private fork, then use
|
||||
kustomize on your fork. As often as desired, use _git rebase_ to
|
||||
capture improvements from the upstream base.
|
||||
|
||||
## Build-time side effects from CLI args or env variables
|
||||
|
||||
`kustomize` supports the best practice of storing one's
|
||||
entire configuration in a version control system.
|
||||
|
||||
Changing `kustomize build` configuration output as a result
|
||||
of additional arguments or flags to `build`, or by
|
||||
consulting shell environment variable values in `build`
|
||||
code, would violate that goal.
|
||||
|
||||
`kustomize` insteads offers [kustomization] file `edit`
|
||||
commands. Like any shell command, they can accept
|
||||
environment variable arguments.
|
||||
|
||||
For example, to set the tag used on an image to match an
|
||||
environment variable, run
|
||||
|
||||
```
|
||||
kustomize edit set imagetag nginx:$MY_NGINX_VERSION
|
||||
```
|
||||
|
||||
as part of some encapsulating work flow executed before
|
||||
`kustomize build`.
|
||||
|
||||
|
||||
## Globs in kustomization files
|
||||
|
||||
`kustomize` supports the best practice of storing one's
|
||||
entire configuration in a version control system.
|
||||
|
||||
Globbing the local file system for files not explicitly
|
||||
declared in the [kustomization] file at `kustomize build` time
|
||||
would violate that goal.
|
||||
|
||||
Allowing globbing in a kustomization file would also introduce
|
||||
the same problems as allowing globbing in [java import]
|
||||
declarations or BUILD/Makefile dependency rules.
|
||||
|
||||
`kustomize` will instead provide kustomization file editting
|
||||
commands that accept globbed arguments, expand them at _edit
|
||||
time_ relative to the local file system, and store the resulting
|
||||
explicit names into the kustomization file.
|
||||
|
||||
In this way the resources, patches and bases used at _build time_
|
||||
remain explicitly declared in version control.
|
||||
|
||||
|
||||
[base]: glossary.md#base
|
||||
[kustomization]: glossary.md#kustomization
|
||||
[OTS workflow]: workflows.md#off-the-shelf-configuration
|
||||
[java import]: https://www.codebyamir.com/blog/pitfalls-java-import-wildcards
|
||||
@@ -285,7 +285,7 @@ The _target_ is the argument to `kustomize build`, e.g.:
|
||||
> kustomize build $target
|
||||
> ```
|
||||
|
||||
`$target` must be a path to a directory that
|
||||
`$target` must be a path or a url to a directory that
|
||||
immediately contains a [kustomization].
|
||||
|
||||
The target contains, or refers to, all the information
|
||||
|
||||
@@ -82,11 +82,30 @@ secretGenerator:
|
||||
tls.crt: "cat secret/tls.cert"
|
||||
tls.key: "cat secret/tls.key"
|
||||
type: "kubernetes.io/tls"
|
||||
- name: downloaded_secret
|
||||
# timeoutSeconds specifies the number of seconds to
|
||||
# wait for the commands below. It defaults to 5 seconds.
|
||||
timeoutSeconds: 30
|
||||
commands:
|
||||
username: "curl -s https://path/to/secrets/username.yaml"
|
||||
password: "curl -s https://path/to/secrets/password.yaml"
|
||||
type: Opaque
|
||||
- name: env_file_secret
|
||||
# envCommand is similar to command but outputs lines of key=val pairs
|
||||
# i.e. a Docker .env file or a .ini file.
|
||||
# you can only specify one envCommand per secret.
|
||||
envCommand: printf \"DB_USERNAME=admin\nDB_PASSWORD=somepw\"
|
||||
type: Opaque
|
||||
|
||||
# Each entry in this list should resolve to a directory
|
||||
# containing a kustomization file, else the
|
||||
# customization fails.
|
||||
#
|
||||
# The entry could be a relative path pointing to a local directory
|
||||
# or a url pointing to a directory in a remote repo.
|
||||
# The url should follow hashicorp/go-getter URL format
|
||||
# https://github.com/hashicorp/go-getter#url-format
|
||||
#
|
||||
# The presence of this field means this file (the file
|
||||
# you a reading) is an _overlay_ that further
|
||||
# customizes information coming from these _bases_.
|
||||
@@ -97,6 +116,9 @@ secretGenerator:
|
||||
# etc. that differ from the common base).
|
||||
bases:
|
||||
- ../../base
|
||||
- github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6
|
||||
- github.com/Liujingfang1/mysql
|
||||
- github.com/Liujingfang1/kustomize//examples/helloWorld?ref=test-branch
|
||||
|
||||
# Each entry in this list should resolve to
|
||||
# a partial or complete resource definition file.
|
||||
@@ -115,3 +137,83 @@ patches:
|
||||
- service_port_8888.yaml
|
||||
- deployment_increase_replicas.yaml
|
||||
- deployment_increase_memory.yaml
|
||||
|
||||
|
||||
# Each entry in this list should be a relative path to
|
||||
# a file for custom resource definition(CRD).
|
||||
#
|
||||
# The presence of this field is to allow kustomize be
|
||||
# aware of CRDs and apply proper
|
||||
# transformation for any objects in those types.
|
||||
#
|
||||
# Typical use case: A CRD object refers to a ConfigMap object.
|
||||
# In kustomization, the ConfigMap object name may change by adding namePrefix or hashing
|
||||
# The name reference for this ConfigMap object in CRD object need to be
|
||||
# updated with namePrefix or hashing in the same way.
|
||||
crds:
|
||||
- crds/typeA.yaml
|
||||
- crds/typeB.yaml
|
||||
|
||||
# Vars are used to insert values from resources that cannot be referenced
|
||||
# otherwise. For example if you need to pass a Service's name to the arguments
|
||||
# or environment variables of a program but without hard coding the actual name
|
||||
# of the Service you'd insert `$(MY_SERVICE_NAME)` into the value field of the
|
||||
# env var or into the command or args of the container as shown here:
|
||||
# ```
|
||||
# containers:
|
||||
# - image: myimage
|
||||
# command: ["start", "--host", "$(MY_SERVICE_NAME)"]
|
||||
# env:
|
||||
# - name: SECRET_TOKEN
|
||||
# value: $(SOME_SECRET_NAME)
|
||||
# ```
|
||||
#
|
||||
# Then you'll add an entry to `vars:` like shown below with the same name
|
||||
# and a reference to the resource from which to pull the field's value.
|
||||
# The actual field's path is optional and by default it will use
|
||||
# `metadata.name`. Currently only string type fields are supported, no integers
|
||||
# or booleans, etc. Also array access is currently not possible. For example getting
|
||||
# the image field of container number 2 inside of a pod can currently not be done.
|
||||
#
|
||||
# Not every location of a variable is supported. To see a complete list of locations
|
||||
# see the file [refvars.go](https://github.com/kubernetes-sigs/kustomize/blob/master/pkg/transformers/refvars.go#L20).
|
||||
#
|
||||
# An example of a situation where you'd not use vars is when you'd like to set a
|
||||
# pod's `serviceAccountName`. In that case you would just reference the ServiceAccount
|
||||
# by name and Kustomize will resolve it to the eventual name while building the manifests.
|
||||
vars:
|
||||
- name: SOME_SECRET_NAME
|
||||
objref:
|
||||
kind: Secret
|
||||
name: my-secret
|
||||
apiVersion: v1
|
||||
- name: MY_SERVICE_NAME
|
||||
objref:
|
||||
kind: Service
|
||||
name: my-service
|
||||
apiVersion: v1
|
||||
fieldref:
|
||||
fieldpath: metadata.name
|
||||
- name: ANOTHER_DEPLOYMENTS_POD_RESTART_POLICY
|
||||
objref:
|
||||
kind: Deployment
|
||||
name: my-deployment
|
||||
apiVersion: apps/v1
|
||||
fieldref:
|
||||
fieldpath: spec.template.spec.restartPolicy
|
||||
|
||||
# ImageTags modify the tags for images without creating patches.
|
||||
# E.g. Given this fragment of a Deployment:
|
||||
# ```
|
||||
# containers:
|
||||
# - name: myapp
|
||||
# image: mycontainerregistry/myimage:v0
|
||||
# - name: nginxapp
|
||||
# image: nginx:1.7.9
|
||||
#```
|
||||
# one can change the tag of myimage to v1 and the tag of nginx to 1.8.0 with the following:
|
||||
imageTags:
|
||||
- name: mycontainerregistry/myimage
|
||||
newTag: v1
|
||||
- name: nginx
|
||||
newTag: 1.8.0
|
||||
|
||||
@@ -21,14 +21,17 @@ use and maintain a configuration.
|
||||
|
||||
## Bespoke configuration
|
||||
|
||||
In this workflow, all configuration files are owned by
|
||||
the user. No content is incorporated from version
|
||||
In this workflow, all configuration (resource YAML) files
|
||||
are owned by the user. No content is incorporated from version
|
||||
control repositories owned by others.
|
||||
|
||||
![bespoke config workflow image][workflowBespoke]
|
||||
|
||||
#### 1) create a directory in version control
|
||||
|
||||
Speculate some overall cluster application called _ldap_;
|
||||
we want to keep its configuration in its own repo.
|
||||
|
||||
> ```
|
||||
> git init ~/ldap
|
||||
> ```
|
||||
|
||||
@@ -23,11 +23,20 @@ go get github.com/kubernetes-sigs/kustomize
|
||||
* [springboot](springboot/README.md) - Create a Spring Boot
|
||||
application production configuration from scratch.
|
||||
|
||||
* [configGeneration](configGeneration.md) -
|
||||
* [combineConfigs](combineConfigs.md) -
|
||||
Mixing configuration data from different owners
|
||||
(e.g. devops/SRE and developers).
|
||||
|
||||
* [configGenerations](configGeneration.md) -
|
||||
Rolling update when ConfigMapGenerator changes
|
||||
|
||||
* [breakfast](breakfast.md) - Customize breakfast for
|
||||
Alice and Bob.
|
||||
|
||||
* [container args](wordpress/README.md) - Injecting k8s runtime data into container arguments (e.g. to point wordpress to a SQL service).
|
||||
|
||||
* [image tags](imageTags.md) - Updating image tags without applying a patch.
|
||||
|
||||
* [multibases](multibases/README.md) - Composing three variants (dev, staging, production) with a common base.
|
||||
|
||||
* [remote target](remoteBuild.md) - Building a kustomization from a github URL
|
||||
|
||||
298
examples/combineConfigs.md
Normal file
298
examples/combineConfigs.md
Normal file
@@ -0,0 +1,298 @@
|
||||
[overlay]: ../docs/glossary.md#overlay
|
||||
[target]: ../docs/glossary.md#target
|
||||
|
||||
# Demo: combining config data from devops and developers
|
||||
|
||||
Scenario: you have a Java-based server storefront in
|
||||
production that various internal development teams
|
||||
(signups, checkout, search, etc.) contribute to.
|
||||
|
||||
The server runs in different environments:
|
||||
_development_, _testing_, _staging_ and _production_,
|
||||
accepting configuration parameters from java property
|
||||
files.
|
||||
|
||||
Using one big properties file for each environment is
|
||||
difficult to manage. The files change frequently, and
|
||||
have to be changed by devops exclusively because
|
||||
|
||||
1. the files must at least partially agree on certain
|
||||
values that devops cares about and that developers
|
||||
ignore and
|
||||
1. because the production
|
||||
properties contain sensitive data like production
|
||||
database credentials.
|
||||
|
||||
## Property sharding
|
||||
|
||||
With some study, we notice that the properties are
|
||||
separable into categories.
|
||||
|
||||
### Common properties
|
||||
|
||||
E.g. internationalization data, static data like
|
||||
physical constants, location of external services, etc.
|
||||
|
||||
_Things that are the same regardless of environment._
|
||||
|
||||
Only one set of values is needed.
|
||||
|
||||
Place them in a file called
|
||||
|
||||
* `common.properties`
|
||||
|
||||
(relative location defined below).
|
||||
|
||||
### Plumbing properties
|
||||
|
||||
E.g. serving location of static content (HTML, CSS,
|
||||
javascript), location of product and customer database
|
||||
tables, ports expected by load balancers, log sinks,
|
||||
etc.
|
||||
|
||||
_The different values for these properties are
|
||||
precisely what sets the environments apart._
|
||||
|
||||
Devops or SRE will want full control over the values
|
||||
used in production. Testing will have fixed
|
||||
databases supporting testing. Developers will want
|
||||
to do whatever they want to try scenarios under
|
||||
development.
|
||||
|
||||
Places these values in
|
||||
|
||||
* `development/plumbing.properties`
|
||||
* `staging/plumbing.properties`
|
||||
* `production/plumbing.properties`
|
||||
|
||||
|
||||
### Secret properties
|
||||
|
||||
E.g. location of actual user tables, database
|
||||
credentials, decryption keys, etc.
|
||||
|
||||
_Things that are a subset of devops controls, that
|
||||
nobody else has (or should want) access to._
|
||||
|
||||
Places these values in
|
||||
|
||||
* `development/secret.properties`
|
||||
* `staging/secret.properties`
|
||||
* `production/secret.properties`
|
||||
|
||||
[kubernetes secret]: https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/
|
||||
|
||||
and control access to them with (for example) unix file
|
||||
owner and mode bits, or better yet, put them in
|
||||
a server dedicated to storing password protected
|
||||
secrets, and use a field called `secretGenerator`
|
||||
in your _kustomization_ to create a kubernetes
|
||||
secret holding them (not covering that here).
|
||||
|
||||
<!--
|
||||
secretGenerator:
|
||||
- name: app-tls
|
||||
commands:
|
||||
tls.crt: "cat tls.cert"
|
||||
tls.key: "cat tls.key"
|
||||
type: "kubernetes.io/tls"
|
||||
EOF
|
||||
-->
|
||||
|
||||
## A mixin approach to management
|
||||
|
||||
The way to create _n_ cluster environments that share
|
||||
some common information is to create _n_ overlays of a
|
||||
common base.
|
||||
|
||||
For the rest of this example, we'll do _n==2_, just
|
||||
_development_ and _production_, since adding more
|
||||
environments follows the same pattern.
|
||||
|
||||
A cluster environment is created by
|
||||
running `kustomize build` on a [target] that happens to
|
||||
be an [overlay].
|
||||
|
||||
[helloworld]: helloWorld/README.md
|
||||
|
||||
The following example will do that, but will focus on
|
||||
configMap construction, and not worry about how to
|
||||
connect the configMaps to deployments (that is covered
|
||||
in the [helloworld] example).
|
||||
|
||||
|
||||
All files - including the shared property files
|
||||
discussed above - will be created in a directory tree
|
||||
that is consistent with the base vs overlay file layout
|
||||
defined in the [helloworld] demo.
|
||||
|
||||
It will all live in this work directory:
|
||||
|
||||
<!-- @makeWorkplace @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
### Create the base
|
||||
|
||||
<!-- kubectl create configmap BOB --dry-run -o yaml --from-file db. -->
|
||||
|
||||
Make a place to put the base configuration:
|
||||
|
||||
<!-- @baseDir @test -->
|
||||
```
|
||||
mkdir -p $DEMO_HOME/base
|
||||
```
|
||||
|
||||
Make the data for the base. This direction by
|
||||
definition should hold resources common to all
|
||||
environments. Here we're only defining a java
|
||||
properties file, and a `kustomization` file that
|
||||
references it.
|
||||
|
||||
<!-- @baseKustomization @test -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/base/common.properties
|
||||
color=blue
|
||||
height=10m
|
||||
EOF
|
||||
|
||||
cat <<EOF >$DEMO_HOME/base/kustomization.yaml
|
||||
configMapGenerator:
|
||||
- name: my-configmap
|
||||
files:
|
||||
- common.properties
|
||||
EOF
|
||||
```
|
||||
|
||||
|
||||
### Create and use the overlay for _development_
|
||||
|
||||
Make an abbreviation for the parent of the overlay
|
||||
directories:
|
||||
|
||||
<!-- @overlays @test -->
|
||||
```
|
||||
OVERLAYS=$DEMO_HOME/overlays
|
||||
```
|
||||
|
||||
Create the files that define the _development_ overlay:
|
||||
|
||||
<!-- @developmentFiles @test -->
|
||||
```
|
||||
mkdir -p $OVERLAYS/development
|
||||
|
||||
cat <<EOF >$OVERLAYS/development/plumbing.properties
|
||||
port=30000
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/development/secret.properties
|
||||
dbpassword=mothersMaidenName
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/development/kustomization.yaml
|
||||
bases:
|
||||
- ../../base
|
||||
namePrefix: dev-
|
||||
configMapGenerator:
|
||||
- name: my-configmap
|
||||
behavior: merge
|
||||
files:
|
||||
- plumbing.properties
|
||||
- secret.properties
|
||||
EOF
|
||||
```
|
||||
|
||||
One can now generate the configMaps for development:
|
||||
|
||||
<!-- @runDev @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/development
|
||||
```
|
||||
|
||||
#### Check the ConfigMap name
|
||||
|
||||
The name of the generated `ConfigMap` is visible in this
|
||||
output.
|
||||
|
||||
The name should be something like `dev-my-configmap-b5m75ck895`:
|
||||
|
||||
* `"dev-"` comes from the `namePrefix` field,
|
||||
* `"my-configmap"` comes from the `configMapGenerator/name` field,
|
||||
* `"-b5m75ck895"` comes from a deterministic hash that `kustomize`
|
||||
computes from the contents of the configMap.
|
||||
|
||||
The hash suffix is critical. If the configMap content
|
||||
changes, so does the configMap name, along with all
|
||||
references to that name that appear in the YAML output
|
||||
from `kustomize`.
|
||||
|
||||
The name change means deployments will do a rolling
|
||||
restart to get new data if this YAML is applied to the
|
||||
cluster using a command like
|
||||
|
||||
> ```
|
||||
> kustomize build $OVERLAYS/development | kubectl apply -f -
|
||||
> ```
|
||||
|
||||
A deployment has no means to automatically know when or
|
||||
if a configMap in use by the deployment changes.
|
||||
|
||||
If one changes a configMap without changing its name
|
||||
and all references to that name, one must imperatively
|
||||
restart the cluster to pick up the change.
|
||||
|
||||
The best practice is to treat configMaps as immutable.
|
||||
|
||||
Instead of editing configMaps, modify your declarative
|
||||
specification of the cluster's desired state to
|
||||
point deployments to _new_ configMaps with _new_ names.
|
||||
`kustomize` makes this easy with its
|
||||
`configMapGenerator` directive and associated naming
|
||||
controls. A GC process in the k8s master eventually
|
||||
deletes unused configMaps.
|
||||
|
||||
|
||||
### Create and use the overlay for _production_
|
||||
|
||||
Next, create the files for the _production_ overlay:
|
||||
|
||||
|
||||
<!-- @productionFiles @test -->
|
||||
```
|
||||
mkdir -p $OVERLAYS/production
|
||||
|
||||
cat <<EOF >$OVERLAYS/production/plumbing.properties
|
||||
port=8080
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/production/secret.properties
|
||||
dbpassword=thisShouldProbablyBeInASecretInstead
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/production/kustomization.yaml
|
||||
bases:
|
||||
- ../../base
|
||||
namePrefix: prod-
|
||||
configMapGenerator:
|
||||
- name: my-configmap
|
||||
behavior: merge
|
||||
files:
|
||||
- plumbing.properties
|
||||
- secret.properties
|
||||
EOF
|
||||
```
|
||||
|
||||
One can now generate the configMaps for production:
|
||||
|
||||
<!-- @runProd @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/production
|
||||
```
|
||||
|
||||
A CICD process could apply this directly to
|
||||
the cluser using:
|
||||
|
||||
> ```
|
||||
> kustomize build $OVERLAYS/production | kubectl apply -f -
|
||||
> ```
|
||||
@@ -1,298 +1,208 @@
|
||||
[overlay]: ../docs/glossary.md#overlay
|
||||
[target]: ../docs/glossary.md#target
|
||||
[patch]: ../../docs/glossary.md#patch
|
||||
[resource]: ../../docs/glossary.md#resource
|
||||
[variant]: ../../docs/glossary.md#variant
|
||||
|
||||
# Demo: combining config data from devops and developers
|
||||
## ConfigMap generation and rolling updates
|
||||
|
||||
Scenario: you have a Java-based server storefront in
|
||||
production that various internal development teams
|
||||
(signups, checkout, search, etc.) contribute to.
|
||||
Kustomize provides two ways of adding ConfigMap in one `kustomization`, either by declaring ConfigMap as a [resource] or declaring ConfigMap from a ConfigMapGenerator. The formats inside `kustomization.yaml` are
|
||||
|
||||
The server runs in different environments:
|
||||
_development_, _testing_, _staging_ and _production_,
|
||||
accepting configuration parameters from java property
|
||||
files.
|
||||
> ```
|
||||
> # declare ConfigMap as a resource
|
||||
> resources:
|
||||
> - configmap.yaml
|
||||
>
|
||||
> # declare ConfigMap from a ConfigMapGenerator
|
||||
> configMapGenerator:
|
||||
> - name: a-configmap
|
||||
> files:
|
||||
> - configs/configfile
|
||||
> - configs/another_configfile
|
||||
> ```
|
||||
|
||||
Using one big properties file for each environment is
|
||||
difficult to manage. The files change frequently, and
|
||||
have to be changed by devops exclusively because
|
||||
The ConfigMaps declared as [resource] are treated the same way as other resources. Kustomize doesn't append any hash to the ConfigMap name. The ConfigMap declared from a ConfigMapGenerator is treated differently. A hash is appended to the name and any change in the ConfigMap will trigger a rolling update.
|
||||
|
||||
1. the files must at least partially agree on certain
|
||||
values that devops cares about and that developers
|
||||
ignore and
|
||||
1. because the production
|
||||
properties contain sensitive data like production
|
||||
database credentials.
|
||||
In this demo, the same [hello_world](helloWorld/README.md) is used while the ConfigMap declared as [resources] is replaced by a ConfigMap declared from a ConfigmapGenerator. The change in this ConfigMap will result in a hash change and a rolling update.
|
||||
|
||||
## Property sharding
|
||||
### Establish base and staging
|
||||
|
||||
With some study, we notice that the properties are
|
||||
separable into categories.
|
||||
|
||||
### Common properties
|
||||
|
||||
E.g. internationalization data, static data like
|
||||
physical constants, location of external services, etc.
|
||||
|
||||
_Things that are the same regardless of environment._
|
||||
|
||||
Only one set of values is needed.
|
||||
|
||||
Place them in a file called
|
||||
|
||||
* `common.properties`
|
||||
|
||||
(relative location defined below).
|
||||
|
||||
### Plumbing properties
|
||||
|
||||
E.g. serving location of static content (HTML, CSS,
|
||||
javascript), location of product and customer database
|
||||
tables, ports expected by load balancers, log sinks,
|
||||
etc.
|
||||
|
||||
_The different values for these properties are
|
||||
precisely what sets the environments apart._
|
||||
|
||||
Devops or SRE will want full control over the values
|
||||
used in production. Testing will have fixed
|
||||
databases supporting testing. Developers will want
|
||||
to do whatever they want to try scenarios under
|
||||
development.
|
||||
|
||||
Places these values in
|
||||
|
||||
* `development/plumbing.properties`
|
||||
* `staging/plumbing.properties`
|
||||
* `production/plumbing.properties`
|
||||
|
||||
|
||||
### Secret properties
|
||||
|
||||
E.g. location of actual user tables, database
|
||||
credentials, decryption keys, etc.
|
||||
|
||||
_Things that are a subset of devops controls, that
|
||||
nobody else has (or should want) access to._
|
||||
|
||||
Places these values in
|
||||
|
||||
* `development/secret.properties`
|
||||
* `staging/secret.properties`
|
||||
* `production/secret.properties`
|
||||
|
||||
[kubernetes secret]: https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/
|
||||
|
||||
and control access to them with (for example) unix file
|
||||
owner and mode bits, or better yet, put them in
|
||||
a server dedicated to storing password protected
|
||||
secrets, and use a field called `secretGenerator`
|
||||
in your _kustomization_ to create a kubernetes
|
||||
secret holding them (not covering that here).
|
||||
|
||||
<!--
|
||||
secretGenerator:
|
||||
- name: app-tls
|
||||
commands:
|
||||
tls.crt: "cat tls.cert"
|
||||
tls.key: "cat tls.key"
|
||||
type: "kubernetes.io/tls"
|
||||
EOF
|
||||
-->
|
||||
|
||||
## A mixin approach to management
|
||||
|
||||
The way to create _n_ cluster environments that share
|
||||
some common information is to create _n_ overlays of a
|
||||
common base.
|
||||
|
||||
For the rest of this example, we'll do _n==2_, just
|
||||
_development_ and _production_, since adding more
|
||||
environments follows the same pattern.
|
||||
|
||||
A cluster environment is created by
|
||||
running `kustomize build` on a [target] that happens to
|
||||
be an [overlay].
|
||||
|
||||
[helloworld]: helloworld.md
|
||||
|
||||
The following example will do that, but will focus on
|
||||
configMap construction, and not worry about how to
|
||||
connect the configMaps to deployments (that is covered
|
||||
in the [helloworld] example).
|
||||
|
||||
|
||||
All files - including the shared property files
|
||||
discussed above - will be created in a directory tree
|
||||
that is consistent with the base vs overlay file layout
|
||||
defined in the [helloworld] demo.
|
||||
|
||||
It will all live in this work directory:
|
||||
|
||||
<!-- @makeWorkplace @test -->
|
||||
Establish the base with a configMapGenerator
|
||||
<!-- @establishBase @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
### Create the base
|
||||
BASE=$DEMO_HOME/base
|
||||
mkdir -p $BASE
|
||||
|
||||
<!-- kubectl create configmap BOB --dry-run -o yaml --from-file db. -->
|
||||
curl -s -o "$BASE/#1.yaml" "https://raw.githubusercontent.com\
|
||||
/kubernetes-sigs/kustomize\
|
||||
/master/examples/helloWorld\
|
||||
/{deployment,service}.yaml"
|
||||
|
||||
Make a place to put the base configuration:
|
||||
|
||||
<!-- @baseDir @test -->
|
||||
```
|
||||
mkdir -p $DEMO_HOME/base
|
||||
```
|
||||
|
||||
Make the data for the base. This direction by
|
||||
definition should hold resources common to all
|
||||
environments. Here we're only defining a java
|
||||
properties file, and a `kustomization` file that
|
||||
references it.
|
||||
|
||||
<!-- @baseKustomization @test -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/base/common.properties
|
||||
color=blue
|
||||
height=10m
|
||||
EOF
|
||||
|
||||
cat <<EOF >$DEMO_HOME/base/kustomization.yaml
|
||||
configMapGenerator:
|
||||
- name: my-configmap
|
||||
files:
|
||||
- common.properties
|
||||
cat <<'EOF' >$BASE/kustomization.yaml
|
||||
commonLabels:
|
||||
app: hello
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
configMapGenerator:
|
||||
- name: the-map
|
||||
literals:
|
||||
- altGreeting=Good Morning!
|
||||
- enableRisky="false"
|
||||
EOF
|
||||
```
|
||||
|
||||
|
||||
### Create and use the overlay for _development_
|
||||
|
||||
Make an abbreviation for the parent of the overlay
|
||||
directories:
|
||||
|
||||
<!-- @overlays @test -->
|
||||
Establish the staging with a patch applied to the ConfigMap
|
||||
<!-- @establishStaging @test -->
|
||||
```
|
||||
OVERLAYS=$DEMO_HOME/overlays
|
||||
```
|
||||
mkdir -p $OVERLAYS/staging
|
||||
|
||||
Create the files that define the _development_ overlay:
|
||||
|
||||
<!-- @developmentFiles @test -->
|
||||
```
|
||||
mkdir -p $OVERLAYS/development
|
||||
|
||||
cat <<EOF >$OVERLAYS/development/plumbing.properties
|
||||
port=30000
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/development/secret.properties
|
||||
dbpassword=mothersMaidenName
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/development/kustomization.yaml
|
||||
cat <<'EOF' >$OVERLAYS/staging/kustomization.yaml
|
||||
namePrefix: staging-
|
||||
commonLabels:
|
||||
variant: staging
|
||||
org: acmeCorporation
|
||||
commonAnnotations:
|
||||
note: Hello, I am staging!
|
||||
bases:
|
||||
- ../../base
|
||||
namePrefix: dev-
|
||||
configMapGenerator:
|
||||
- name: my-configmap
|
||||
behavior: merge
|
||||
files:
|
||||
- plumbing.properties
|
||||
- secret.properties
|
||||
patches:
|
||||
- map.yaml
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/staging/map.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: the-map
|
||||
data:
|
||||
altGreeting: "Have a pineapple!"
|
||||
enableRisky: "true"
|
||||
EOF
|
||||
```
|
||||
|
||||
One can now generate the configMaps for development:
|
||||
### Review
|
||||
|
||||
<!-- @runDev @test -->
|
||||
The _hello-world_ deployment running in this cluster is
|
||||
configured with data from a configMap.
|
||||
|
||||
The deployment refers to this map by name:
|
||||
|
||||
|
||||
<!-- @showDeployment @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/development
|
||||
grep -C 2 configMapKeyRef $BASE/deployment.yaml
|
||||
```
|
||||
|
||||
#### Check the ConfigMap name
|
||||
Changing the data held by a live configMap in a cluster
|
||||
is considered bad practice. Deployments have no means
|
||||
to know that the configMaps they refer to have
|
||||
changed, so such updates have no effect.
|
||||
|
||||
The name of the generated `ConfigMap` is visible in this
|
||||
output.
|
||||
The recommended way to change a deployment's
|
||||
configuration is to
|
||||
|
||||
The name should be something like `dev-my-configmap-b5m75ck895`:
|
||||
1. create a new configMap with a new name,
|
||||
1. patch the _deployment_, modifying the name value of
|
||||
the appropriate `configMapKeyRef` field.
|
||||
|
||||
* `"dev-"` comes from the `namePrefix` field,
|
||||
* `"my-configmap"` comes from the `configMapGenerator/name` field,
|
||||
* `"-b5m75ck895"` comes from a deterministic hash that `kustomize`
|
||||
computes from the contents of the configMap.
|
||||
This latter change initiates rolling update to the pods
|
||||
in the deployment. The older configMap, when no longer
|
||||
referenced by any other resource, is eventually [garbage
|
||||
collected](https://github.com/kubernetes-sigs/kustomize/issues/242).
|
||||
|
||||
The hash suffix is critical. If the configMap content
|
||||
changes, so does the configMap name, along with all
|
||||
references to that name that appear in the YAML output
|
||||
from `kustomize`.
|
||||
### How this works with kustomize
|
||||
|
||||
The name change means deployments will do a rolling
|
||||
restart to get new data if this YAML is applied to the
|
||||
cluster using a command like
|
||||
The _staging_ [variant] here has a configMap [patch]:
|
||||
|
||||
> ```
|
||||
> kustomize build $OVERLAYS/development | kubectl apply -f -
|
||||
> ```
|
||||
|
||||
A deployment has no means to automatically know when or
|
||||
if a configMap in use by the deployment changes.
|
||||
|
||||
If one changes a configMap without changing its name
|
||||
and all references to that name, one must imperatively
|
||||
restart the cluster to pick up the change.
|
||||
|
||||
The best practice is to treat configMaps as immutable.
|
||||
|
||||
Instead of editing configMaps, modify your declarative
|
||||
specification of the cluster's desired state to
|
||||
point deployments to _new_ configMaps with _new_ names.
|
||||
`kustomize` makes this easy with its
|
||||
`configMapGenerator` directive and associated naming
|
||||
controls. A GC process in the k8s master eventually
|
||||
deletes unused configMaps.
|
||||
|
||||
|
||||
### Create and use the overlay for _production_
|
||||
|
||||
Next, create the files for the _production_ overlay:
|
||||
|
||||
|
||||
<!-- @productionFiles @test -->
|
||||
<!-- @showMapPatch @test -->
|
||||
```
|
||||
mkdir -p $OVERLAYS/production
|
||||
|
||||
cat <<EOF >$OVERLAYS/production/plumbing.properties
|
||||
port=8080
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/production/secret.properties
|
||||
dbpassword=thisShouldProbablyBeInASecretInstead
|
||||
EOF
|
||||
|
||||
cat <<EOF >$OVERLAYS/production/kustomization.yaml
|
||||
bases:
|
||||
- ../../base
|
||||
namePrefix: prod-
|
||||
configMapGenerator:
|
||||
- name: my-configmap
|
||||
behavior: merge
|
||||
files:
|
||||
- plumbing.properties
|
||||
- secret.properties
|
||||
EOF
|
||||
cat $OVERLAYS/staging/map.yaml
|
||||
```
|
||||
|
||||
One can now generate the configMaps for production:
|
||||
This patch is by definition a named but not necessarily
|
||||
complete resource spec intended to modify a complete
|
||||
resource spec.
|
||||
|
||||
<!-- @runProd @test -->
|
||||
The ConfigMap it modifies is declared from a configMapGenerator.
|
||||
|
||||
<!-- @showMapBase @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/production
|
||||
grep -C 4 configMapGenerator $BASE/kustomization.yaml
|
||||
```
|
||||
|
||||
A CICD process could apply this directly to
|
||||
the cluser using:
|
||||
For a patch to work, the names in the `metadata/name`
|
||||
fields must match.
|
||||
|
||||
> ```
|
||||
> kustomize build $OVERLAYS/production | kubectl apply -f -
|
||||
> ```
|
||||
However, the name values specified in the file are
|
||||
_not_ what gets used in the cluster. By design,
|
||||
kustomize modifies names of ConfigMaps declared from ConfigMapGenerator. To see the names
|
||||
ultimately used in the cluster, just run kustomize:
|
||||
|
||||
<!-- @grepStagingName @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging |\
|
||||
grep -B 8 -A 1 staging-the-map
|
||||
```
|
||||
|
||||
The configMap name is prefixed by _staging-_, per the
|
||||
`namePrefix` field in
|
||||
`$OVERLAYS/staging/kustomization.yaml`.
|
||||
|
||||
The suffix to the configMap name is generated from a
|
||||
hash of the maps content - in this case the name suffix
|
||||
is _hhhhkfmgmk_:
|
||||
|
||||
<!-- @grepStagingHash @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging | grep hhhhkfmgmk
|
||||
```
|
||||
|
||||
Now modify the map patch, to change the greeting
|
||||
the server will use:
|
||||
|
||||
<!-- @changeMap @test -->
|
||||
```
|
||||
sed -i 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml
|
||||
```
|
||||
|
||||
See the new greeting:
|
||||
|
||||
```
|
||||
kustomize build $OVERLAYS/staging |\
|
||||
grep -B 2 -A 3 kiwi
|
||||
```
|
||||
|
||||
Run kustomize again to see the new configMap names:
|
||||
|
||||
<!-- @grepStagingName @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging |\
|
||||
grep -B 8 -A 1 staging-the-map
|
||||
```
|
||||
|
||||
Confirm that the change in configMap content resulted
|
||||
in three new names ending in _khk45ktkd9_ - one in the
|
||||
configMap name itself, and two in the deployment that
|
||||
uses the map:
|
||||
|
||||
<!-- @countHashes @test -->
|
||||
```
|
||||
test 3 == \
|
||||
$(kustomize build $OVERLAYS/staging | grep khk45ktkd9 | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
Applying these resources to the cluster will result in
|
||||
a rolling update of the deployments pods, retargetting
|
||||
them from the _hhhhkfmgmk_ maps to the _khk45ktkd9_
|
||||
maps. The system will later garbage collect the
|
||||
unused maps.
|
||||
|
||||
## Rollback
|
||||
|
||||
To rollback, one would undo whatever edits were made to
|
||||
the configuation in source control, then rerun kustomize
|
||||
on the reverted configuration and apply it to the
|
||||
cluster.
|
||||
|
||||
@@ -112,6 +112,12 @@ sed -i 's/app: hello/app: my-hello/' \
|
||||
$BASE/kustomization.yaml
|
||||
```
|
||||
|
||||
On a Mac, use:
|
||||
```
|
||||
sed -i '' $pattern $file
|
||||
```
|
||||
to get in-place editing.
|
||||
|
||||
See the effect:
|
||||
<!-- @checkLabel @test -->
|
||||
```
|
||||
@@ -309,122 +315,3 @@ To deploy, pipe the above commands to kubectl apply:
|
||||
> kustomize build $OVERLAYS/production |\
|
||||
> kubectl apply -f -
|
||||
> ```
|
||||
|
||||
## Rolling updates
|
||||
|
||||
### Review
|
||||
|
||||
The _hello-world_ deployment running in this cluster is
|
||||
configured with data from a configMap.
|
||||
|
||||
The deployment refers to this map by name:
|
||||
|
||||
|
||||
<!-- @showDeployment @test -->
|
||||
```
|
||||
grep -C 2 configMapKeyRef $DEMO_HOME/base/deployment.yaml
|
||||
```
|
||||
|
||||
Changing the data held by a live configMap in a cluster
|
||||
is considered bad practice. Deployments have no means
|
||||
to know that the configMaps they refer to have
|
||||
changed, so such updates have no effect.
|
||||
|
||||
The recommended way to change a deployment's
|
||||
configuration is to
|
||||
|
||||
1. create a new configMap with a new name,
|
||||
1. patch the _deployment_, modifying the name value of
|
||||
the appropriate `configMapKeyRef` field.
|
||||
|
||||
This latter change initiates rolling update to the pods
|
||||
in the deployment. The older configMap, when no longer
|
||||
referenced by any other resource, is eventually garbage
|
||||
collected.
|
||||
|
||||
### How this works with kustomize
|
||||
|
||||
The _staging_ [variant] here has a configMap [patch]:
|
||||
|
||||
<!-- @showMapPatch @test -->
|
||||
```
|
||||
cat $OVERLAYS/staging/map.yaml
|
||||
```
|
||||
|
||||
This patch is by definition a named but not necessarily
|
||||
complete resource spec intended to modify a complete
|
||||
resource spec.
|
||||
|
||||
The resource it modifies is here:
|
||||
|
||||
<!-- @showMapBase @test -->
|
||||
```
|
||||
cat $DEMO_HOME/base/configMap.yaml
|
||||
```
|
||||
|
||||
For a patch to work, the names in the `metadata/name`
|
||||
fields must match.
|
||||
|
||||
However, the name values specified in the file are
|
||||
_not_ what gets used in the cluster. By design,
|
||||
kustomize modifies these names. To see the names
|
||||
ultimately used in the cluster, just run kustomize:
|
||||
|
||||
<!-- @grepStagingName @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging |\
|
||||
grep -B 8 -A 1 staging-the-map
|
||||
```
|
||||
|
||||
The configMap name is prefixed by _staging-_, per the
|
||||
`namePrefix` field in
|
||||
`$OVERLAYS/staging/kustomization.yaml`.
|
||||
|
||||
The suffix to the configMap name is generated from a
|
||||
hash of the maps content - in this case the name suffix
|
||||
is _hhhhkfmgmk_:
|
||||
|
||||
<!-- @grepStagingHash @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging | grep hhhhkfmgmk
|
||||
```
|
||||
|
||||
Now modify the map patch, to change the greeting
|
||||
the server will use:
|
||||
|
||||
<!-- @changeMap @test -->
|
||||
```
|
||||
sed -i 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml
|
||||
```
|
||||
|
||||
Run kustomize again to see the new names:
|
||||
|
||||
<!-- @grepStagingName @test -->
|
||||
```
|
||||
kustomize build $OVERLAYS/staging |\
|
||||
grep -B 8 -A 1 staging-the-map
|
||||
```
|
||||
|
||||
Confirm that the change in configMap content resulted
|
||||
in three new names ending in _khk45ktkd9_ - one in the
|
||||
configMap name itself, and two in the deployment that
|
||||
uses the map:
|
||||
|
||||
<!-- @countHashes @test -->
|
||||
```
|
||||
test 3 == \
|
||||
$(kustomize build $OVERLAYS/staging | grep khk45ktkd9 | wc -l)
|
||||
```
|
||||
|
||||
Applying these resources to the cluster will result in
|
||||
a rolling update of the deployments pods, retargetting
|
||||
them from the _hhhhkfmgmk_ maps to the _khk45ktkd9_
|
||||
maps. The system will later garbage collect the
|
||||
unused maps.
|
||||
|
||||
## Rollback
|
||||
|
||||
To rollback, one would undo whatever edits were made to
|
||||
the configuation in source control, then rerun kustomize
|
||||
on the reverted configuration and apply it to the
|
||||
cluster.
|
||||
|
||||
@@ -5,5 +5,5 @@ commonLabels:
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- configMap.yaml
|
||||
- service.yaml
|
||||
- configMap.yaml
|
||||
|
||||
75
examples/imageTags.md
Normal file
75
examples/imageTags.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Demo: change image tags
|
||||
|
||||
|
||||
Define a place to work:
|
||||
|
||||
<!-- @makeWorkplace @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
Make a `kustomization` containing a pod resource
|
||||
|
||||
<!-- @createKustomization @test -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- pod.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
Declare the pod resource
|
||||
|
||||
<!-- @createDeployment @test -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/pod.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: myapp-pod
|
||||
labels:
|
||||
app: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp-container
|
||||
image: busybox:1.29.0
|
||||
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
|
||||
initContainers:
|
||||
- name: init-mydb
|
||||
image: busybox:1.29.0
|
||||
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
|
||||
EOF
|
||||
```
|
||||
|
||||
The `myapp-pod` resource declares an initContainer and a container, both use the image `busybox:1.29.0`.
|
||||
The tag `1.29.0` can be changed by adding `imageTags` in `kustomization.yaml`.
|
||||
|
||||
|
||||
Add `imageTags`:
|
||||
<!-- @addImageTags @test -->
|
||||
```
|
||||
cd $DEMO_HOME
|
||||
kustomize edit set imagetag busybox:1.29.1
|
||||
```
|
||||
|
||||
The `kustomization.yaml` will be added following `imageTags`.
|
||||
> ```
|
||||
> imageTags:
|
||||
> - name: busybox
|
||||
> newTag: 1.29.1
|
||||
> ```
|
||||
|
||||
Now build this `kustomization`
|
||||
<!-- @kustomizeBuild @test -->
|
||||
```
|
||||
kustomize build $DEMO_HOME
|
||||
```
|
||||
|
||||
Confirm that this replaces _both_ busybox tags:
|
||||
|
||||
<!-- @confirmTags @test -->
|
||||
```
|
||||
test 2 == \
|
||||
$(kustomize build $DEMO_HOME | grep busybox:1.29.1 | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
127
examples/multibases/README.md
Normal file
127
examples/multibases/README.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Demo: multibases with a common base
|
||||
|
||||
`kustomize` encourages defining multiple variants - e.g. dev, staging and prod, as overlays on a common base.
|
||||
|
||||
It's possible to create an additional overlay to compose these variants together - just declare the overlays as the bases of a new kustomization.
|
||||
|
||||
This is also a means to apply a common label or annotation across the variants, if for some reason the base isn't under your control. It also allows one to define a left-most namePrefix across the variants - something that cannot be done by modifying the common base.
|
||||
|
||||
The following demonstrates this using a base that's just one pod.
|
||||
|
||||
Define a place to work:
|
||||
|
||||
<!-- @makeWorkplace @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
Define a common base:
|
||||
<!-- @makeBase @test -->
|
||||
```
|
||||
BASE=$DEMO_HOME/base
|
||||
mkdir $BASE
|
||||
|
||||
cat <<EOF >$BASE/kustomization.yaml
|
||||
resources:
|
||||
- pod.yaml
|
||||
EOF
|
||||
|
||||
cat <<EOF >$BASE/pod.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: myapp-pod
|
||||
labels:
|
||||
app: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
EOF
|
||||
```
|
||||
|
||||
Define a dev variant overlaying base:
|
||||
<!-- @makeDev @test -->
|
||||
```
|
||||
DEV=$DEMO_HOME/dev
|
||||
mkdir $DEV
|
||||
|
||||
cat <<EOF >$DEV/kustomization.yaml
|
||||
bases:
|
||||
- ./../base
|
||||
namePrefix: dev-
|
||||
EOF
|
||||
```
|
||||
|
||||
Define a staging variant overlaying base:
|
||||
<!-- @makeStaging @test -->
|
||||
```
|
||||
STAG=$DEMO_HOME/staging
|
||||
mkdir $STAG
|
||||
|
||||
cat <<EOF >$STAG/kustomization.yaml
|
||||
bases:
|
||||
- ./../base
|
||||
namePrefix: stag-
|
||||
EOF
|
||||
```
|
||||
|
||||
Define a production variant overlaying base:
|
||||
<!-- @makeProd @test -->
|
||||
```
|
||||
PROD=$DEMO_HOME/production
|
||||
mkdir $PROD
|
||||
|
||||
cat <<EOF >$PROD/kustomization.yaml
|
||||
bases:
|
||||
- ./../base
|
||||
namePrefix: prod-
|
||||
EOF
|
||||
```
|
||||
|
||||
Then define a _Kustomization_ composing three variants together:
|
||||
<!-- @makeTopLayer @test -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
bases:
|
||||
- ./dev
|
||||
- ./staging
|
||||
- ./production
|
||||
|
||||
namePrefix: cluster-a-
|
||||
EOF
|
||||
```
|
||||
|
||||
Now the workspace has following directories
|
||||
> ```
|
||||
> .
|
||||
> ├── base
|
||||
> │ ├── kustomization.yaml
|
||||
> │ └── pod.yaml
|
||||
> ├── dev
|
||||
> │ └── kustomization.yaml
|
||||
> ├── kustomization.yaml
|
||||
> ├── production
|
||||
> │ └── kustomization.yaml
|
||||
> └── staging
|
||||
> └── kustomization.yaml
|
||||
> ```
|
||||
|
||||
Confirm that the `kustomize build` output contains three pod objects from dev, staing and production variants.
|
||||
|
||||
<!-- @confirmVariants @test -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep cluster-a-dev-myapp-pod | wc -l); \
|
||||
echo $?
|
||||
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep cluster-a-stag-myapp-pod | wc -l); \
|
||||
echo $?
|
||||
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep cluster-a-prod-myapp-pod | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
Similarly to adding different `namePrefix` in different variants, one can also add different `namespace` and compose those variants in
|
||||
one _kustomization_. For more details, take a look at [multi-namespaces](multi-namespace.md).
|
||||
2
examples/multibases/base/kustomization.yaml
Normal file
2
examples/multibases/base/kustomization.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
resources:
|
||||
- pod.yaml
|
||||
10
examples/multibases/base/pod.yaml
Normal file
10
examples/multibases/base/pod.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: myapp-pod
|
||||
labels:
|
||||
app: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
4
examples/multibases/dev/kustomization.yaml
Normal file
4
examples/multibases/dev/kustomization.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
bases:
|
||||
- ./../base
|
||||
|
||||
namePrefix: dev-
|
||||
6
examples/multibases/kustomization.yaml
Normal file
6
examples/multibases/kustomization.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
bases:
|
||||
- ./dev
|
||||
- ./staging
|
||||
- ./production
|
||||
|
||||
namePrefix: cluster-a-
|
||||
115
examples/multibases/multi-namespace.md
Normal file
115
examples/multibases/multi-namespace.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Demo: multi namespaces with a common base
|
||||
|
||||
`kustomize` supports defining multiple variants with different namespace, as overlays on a common base.
|
||||
|
||||
It's possible to create an additional overlay to compose these variants together - just declare the overlays as the bases of a new kustomization. The following demonstrates this using a base that's just one pod.
|
||||
|
||||
Define a place to work:
|
||||
|
||||
<!-- @makeWorkplace @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
```
|
||||
|
||||
Define a common base:
|
||||
<!-- @makeBase @test -->
|
||||
```
|
||||
BASE=$DEMO_HOME/base
|
||||
mkdir $BASE
|
||||
|
||||
cat <<EOF >$BASE/kustomization.yaml
|
||||
resources:
|
||||
- pod.yaml
|
||||
EOF
|
||||
|
||||
cat <<EOF >$BASE/pod.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: myapp-pod
|
||||
labels:
|
||||
app: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.7.9
|
||||
EOF
|
||||
```
|
||||
|
||||
Define a variant in namespace-a overlaying base:
|
||||
<!-- @makeNamespaceA @test -->
|
||||
```
|
||||
NSA=$DEMO_HOME/namespace-a
|
||||
mkdir $NSA
|
||||
|
||||
cat <<EOF >$NSA/kustomization.yaml
|
||||
bases:
|
||||
- ./../base
|
||||
resources:
|
||||
- namespace.yaml
|
||||
namespace: namespace-a
|
||||
EOF
|
||||
|
||||
cat <<EOF >$NSA/namespace.yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: namespace-a
|
||||
EOF
|
||||
```
|
||||
|
||||
Define a variant in namespace-b overlaying base:
|
||||
<!-- @makeNamespaceB @test -->
|
||||
```
|
||||
NSB=$DEMO_HOME/namespace-b
|
||||
mkdir $NSB
|
||||
|
||||
cat <<EOF >$NSB/kustomization.yaml
|
||||
bases:
|
||||
- ./../base
|
||||
resources:
|
||||
- namespace.yaml
|
||||
namespace: namespace-b
|
||||
EOF
|
||||
|
||||
cat <<EOF >$NSB/namespace.yaml
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: namespace-b
|
||||
EOF
|
||||
```
|
||||
|
||||
Then define a _Kustomization_ composing two variants together:
|
||||
<!-- @makeTopLayer @test -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
bases:
|
||||
- ./namespace-a
|
||||
- ./namespace-b
|
||||
EOF
|
||||
```
|
||||
|
||||
Now the workspace has following directories
|
||||
> ```
|
||||
> .
|
||||
> ├── base
|
||||
> │ ├── kustomization.yaml
|
||||
> │ └── pod.yaml
|
||||
> ├── kustomization.yaml
|
||||
> ├── namespace-a
|
||||
> │ ├── kustomization.yaml
|
||||
> │ └── namespace.yaml
|
||||
> └── namespace-b
|
||||
> ├── kustomization.yaml
|
||||
> └── namespace.yaml
|
||||
> ```
|
||||
|
||||
Confirm that the `kustomize build` output contains two pod objects from namespace-a and namespace-b.
|
||||
|
||||
<!-- @confirmVariants @test -->
|
||||
```
|
||||
test 2 == \
|
||||
$(kustomize build $DEMO_HOME| grep -B 4 "namespace: namespace-[ab]" | grep "name: myapp-pod" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
4
examples/multibases/production/kustomization.yaml
Normal file
4
examples/multibases/production/kustomization.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
bases:
|
||||
- ./../base
|
||||
|
||||
namePrefix: prod-
|
||||
4
examples/multibases/staging/kustomization.yaml
Normal file
4
examples/multibases/staging/kustomization.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
bases:
|
||||
- ./../base
|
||||
|
||||
namePrefix: staging-
|
||||
58
examples/remoteBuild.md
Normal file
58
examples/remoteBuild.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# remote targets
|
||||
|
||||
`kustomize build` can be run against a url. The effect is the same as cloing the repo, checking out the specified ref,
|
||||
then running `kustomize build` against the desired directory in the local copy.
|
||||
|
||||
Take `github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6` as an example.
|
||||
According to [multibases](multibases/README.md) demo, this kustomization contains three Pod objects with names as
|
||||
`cluster-a-dev-myapp-pod`, `cluster-a-stag-myapp-pod`, `cluster-a-prod-myapp-pod`.
|
||||
Running `kustomize build` against the url gives the same output.
|
||||
|
||||
<!-- @remoteBuild @test -->
|
||||
```
|
||||
target=github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6
|
||||
test 3 == \
|
||||
$(kustomize build $target | grep cluster-a-.*-myapp-pod | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
A base can also be specified as a URL:
|
||||
|
||||
<!-- @createOverlay @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
bases:
|
||||
- github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6
|
||||
namePrefix: remote-
|
||||
EOF
|
||||
```
|
||||
Running `kustomize build $DEMO_HOME` and confirm the output contains three Pods and all have `remote-` prefix.
|
||||
<!-- @remoteBases @test -->
|
||||
```
|
||||
test 3 == \
|
||||
$(kustomize build $DEMO_HOME | grep remote-.*-myapp-pod | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
|
||||
## URL format
|
||||
The url should follow
|
||||
[hashicorp/go-getter URL format](https://github.com/hashicorp/go-getter#url-format).
|
||||
Here are some example urls pointing to Github repos following this convention.
|
||||
|
||||
- a repo with a root level kustomization.yaml
|
||||
|
||||
`github.com/Liujingfang1/mysql`
|
||||
- a repo with a root level kustomization.yaml on branch test
|
||||
|
||||
`github.com/Liujingfang1/mysql?ref=test`
|
||||
- a subdirectory in a repo on version v1.0.6
|
||||
|
||||
`github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6`
|
||||
- a subdirectory in a repo on branch repoUrl2
|
||||
|
||||
`github.com/Liujingfang1/kustomize//examples/helloWorld?ref=repoUrl2`
|
||||
- a subdirectory in a repo on commit `7050a45134e9848fca214ad7e7007e96e5042c03`
|
||||
|
||||
`github.com/Liujingfang1/kustomize//examples/helloWorld?ref=7050a45134e9848fca214ad7e7007e96e5042c03`
|
||||
@@ -4,7 +4,7 @@ metadata:
|
||||
name: demo-configmap
|
||||
data:
|
||||
application.properties: |
|
||||
app.name=Staging Kinflate Demo
|
||||
app.name=Production Kinflate Demo
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.datasource.url=jdbc:mysql://<production_db_ip>:3306/db_example
|
||||
spring.datasource.username=root
|
||||
|
||||
@@ -53,6 +53,8 @@ bases:
|
||||
- wordpress
|
||||
- mysql
|
||||
namePrefix: demo-
|
||||
patches:
|
||||
- patch.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
@@ -65,7 +67,7 @@ In the new kustomization, apply a patch for wordpress deployment. The patch does
|
||||
```
|
||||
CONTENT="https://raw.githubusercontent.com\
|
||||
/kubernetes-sigs/kustomize\
|
||||
/master/examples/patch.yaml"
|
||||
/master/examples/wordpress"
|
||||
|
||||
curl -s -o "$DEMO_HOME/#1.yaml" \
|
||||
"$CONTENT/{patch}.yaml"
|
||||
@@ -89,7 +91,7 @@ The patch has following content
|
||||
> - name: wordpress
|
||||
> env:
|
||||
> - name: WORDPRESS_DB_HOST
|
||||
> value: mysql
|
||||
> value: $(MYSQL_SERVICE)
|
||||
> - name: WORDPRESS_DB_PASSWORD
|
||||
> valueFrom:
|
||||
> secretKeyRef:
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
- name: wordpress
|
||||
env:
|
||||
- name: WORDPRESS_DB_HOST
|
||||
value: mysql
|
||||
value: $(MYSQL_SERVICE)
|
||||
- name: WORDPRESS_DB_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
|
||||
@@ -26,7 +26,10 @@ import (
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/crds"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
@@ -42,12 +45,13 @@ import (
|
||||
// https://github.com/kubernetes-sigs/kustomize/blob/master/docs/glossary.md#target
|
||||
type Application struct {
|
||||
kustomization *types.Kustomization
|
||||
loader loader.Loader
|
||||
ldr loader.Loader
|
||||
fSys fs.FileSystem
|
||||
}
|
||||
|
||||
// NewApplication returns a new instance of Application primed with a Loader.
|
||||
func NewApplication(loader loader.Loader) (*Application, error) {
|
||||
content, err := loader.Load(constants.KustomizationFileName)
|
||||
func NewApplication(ldr loader.Loader, fSys fs.FileSystem) (*Application, error) {
|
||||
content, err := ldr.Load(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -57,7 +61,8 @@ func NewApplication(loader loader.Loader) (*Application, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Application{kustomization: &m, loader: loader}, nil
|
||||
|
||||
return &Application{kustomization: &m, ldr: ldr, fSys: fSys}, nil
|
||||
}
|
||||
|
||||
func unmarshal(y []byte, o interface{}) error {
|
||||
@@ -81,24 +86,14 @@ func (a *Application) MakeCustomizedResMap() (resmap.ResMap, error) {
|
||||
return a.resolveRefsToGeneratedResources(m)
|
||||
}
|
||||
|
||||
// MakeUncustomizedResMap purports to create a ResMap without customization.
|
||||
// The Resources in the returned ResMap include all resources mentioned
|
||||
// in the kustomization file and transitively reachable via its Bases,
|
||||
// and all generated secrets and configMaps.
|
||||
// Meant for use in generating a diff against customized resources.
|
||||
// TODO: See https://github.com/kubernetes-sigs/kustomize/issues/85
|
||||
func (a *Application) MakeUncustomizedResMap() (resmap.ResMap, error) {
|
||||
m, err := a.loadResMapFromBasesAndResources()
|
||||
// resolveRefsToGeneratedResources fixes all name references.
|
||||
func (a *Application) resolveRefsToGeneratedResources(m resmap.ResMap) (resmap.ResMap, error) {
|
||||
err := transformers.NewNameHashTransformer().Transform(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.resolveRefsToGeneratedResources(m)
|
||||
}
|
||||
|
||||
// resolveRefsToGeneratedResources fixes all name references.
|
||||
func (a *Application) resolveRefsToGeneratedResources(m resmap.ResMap) (resmap.ResMap, error) {
|
||||
r := []transformers.Transformer{transformers.NewNameHashTransformer()}
|
||||
|
||||
var r []transformers.Transformer
|
||||
t, err := transformers.NewDefaultingNameReferenceTransformer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -127,14 +122,21 @@ func (a *Application) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
errs := &interror.KustomizationErrors{}
|
||||
result, err := a.loadResMapFromBasesAndResources()
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "rawResources"))
|
||||
errs.Append(errors.Wrap(err, "loadResMapFromBasesAndResources"))
|
||||
}
|
||||
|
||||
cms, err := resmap.NewResMapFromConfigMapArgs(a.loader, a.kustomization.ConfigMapGenerator)
|
||||
err = crds.RegisterCRDs(a.ldr, a.kustomization.Crds)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "RegisterCRDs"))
|
||||
}
|
||||
cms, err := resmap.NewResMapFromConfigMapArgs(
|
||||
configmapandsecret.NewConfigMapFactory(a.fSys, a.ldr),
|
||||
a.kustomization.ConfigMapGenerator)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "NewResMapFromConfigMapArgs"))
|
||||
}
|
||||
secrets, err := resmap.NewResMapFromSecretArgs(a.loader.Root(), a.kustomization.SecretGenerator)
|
||||
secrets, err := resmap.NewResMapFromSecretArgs(
|
||||
configmapandsecret.NewSecretFactory(a.fSys, a.ldr.Root()),
|
||||
a.kustomization.SecretGenerator)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "NewResMapFromSecretArgs"))
|
||||
}
|
||||
@@ -148,7 +150,7 @@ func (a *Application) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patches, err := resmap.NewResourceSliceFromPatches(a.loader, a.kustomization.Patches)
|
||||
patches, err := resmap.NewResourceSliceFromPatches(a.ldr, a.kustomization.Patches)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "NewResourceSliceFromPatches"))
|
||||
}
|
||||
@@ -156,11 +158,20 @@ func (a *Application) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
if len(errs.Get()) > 0 {
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
var r []transformers.Transformer
|
||||
t, err := a.newTransformer(patches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = t.Transform(result)
|
||||
r = append(r, t)
|
||||
t, err = transformers.NewImageTagTransformer(a.kustomization.ImageTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = append(r, t)
|
||||
|
||||
err = transformers.NewMultiTransformer(r).Transform(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -170,7 +181,7 @@ func (a *Application) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
// Gets Bases and Resources as advertised.
|
||||
func (a *Application) loadResMapFromBasesAndResources() (resmap.ResMap, error) {
|
||||
bases, errs := a.loadCustomizedBases()
|
||||
resources, err := resmap.NewResMapFromFiles(a.loader, a.kustomization.Resources)
|
||||
resources, err := resmap.NewResMapFromFiles(a.ldr, a.kustomization.Resources)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "rawResources failed to read Resources"))
|
||||
}
|
||||
@@ -183,15 +194,15 @@ func (a *Application) loadResMapFromBasesAndResources() (resmap.ResMap, error) {
|
||||
// Loop through the Bases of this kustomization recursively loading resources.
|
||||
// Combine into one ResMap, demanding unique Ids for each resource.
|
||||
func (a *Application) loadCustomizedBases() (resmap.ResMap, *interror.KustomizationErrors) {
|
||||
list := []resmap.ResMap{}
|
||||
var list []resmap.ResMap
|
||||
errs := &interror.KustomizationErrors{}
|
||||
for _, path := range a.kustomization.Bases {
|
||||
loader, err := a.loader.New(path)
|
||||
ldr, err := a.ldr.New(path)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "couldn't make loader for "+path))
|
||||
errs.Append(errors.Wrap(err, "couldn't make ldr for "+path))
|
||||
continue
|
||||
}
|
||||
app, err := NewApplication(loader)
|
||||
app, err := NewApplication(ldr, a.fSys)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "couldn't make app for "+path))
|
||||
continue
|
||||
@@ -201,6 +212,7 @@ func (a *Application) loadCustomizedBases() (resmap.ResMap, *interror.Kustomizat
|
||||
errs.Append(errors.Wrap(err, "SemiResources"))
|
||||
continue
|
||||
}
|
||||
ldr.Cleanup()
|
||||
list = append(list, resMap)
|
||||
}
|
||||
result, err := resmap.MergeWithoutOverride(list...)
|
||||
@@ -214,12 +226,12 @@ func (a *Application) loadBasesAsFlatList() ([]*Application, error) {
|
||||
var result []*Application
|
||||
errs := &interror.KustomizationErrors{}
|
||||
for _, path := range a.kustomization.Bases {
|
||||
loader, err := a.loader.New(path)
|
||||
ldr, err := a.ldr.New(path)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
continue
|
||||
}
|
||||
a, err := NewApplication(loader)
|
||||
a, err := NewApplication(ldr, a.fSys)
|
||||
if err != nil {
|
||||
errs.Append(err)
|
||||
continue
|
||||
@@ -234,7 +246,7 @@ func (a *Application) loadBasesAsFlatList() ([]*Application, error) {
|
||||
|
||||
// newTransformer makes a Transformer that does everything except resolve generated names.
|
||||
func (a *Application) newTransformer(patches []*resource.Resource) (transformers.Transformer, error) {
|
||||
r := []transformers.Transformer{}
|
||||
var r []transformers.Transformer
|
||||
t, err := transformers.NewPatchTransformer(patches)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -267,7 +279,7 @@ func (a *Application) resolveRefVars(m resmap.ResMap) (map[string]string, error)
|
||||
}
|
||||
for _, v := range vars {
|
||||
id := resource.NewResId(v.ObjRef.GroupVersionKind(), v.ObjRef.Name)
|
||||
if r, found := m[id]; found {
|
||||
if r, found := m.DemandOneMatchForId(id); found {
|
||||
s, err := r.GetFieldValue(v.FieldRef.FieldPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to resolve referred var: %+v", v)
|
||||
@@ -282,7 +294,7 @@ func (a *Application) resolveRefVars(m resmap.ResMap) (map[string]string, error)
|
||||
|
||||
// getAllVars returns all the "environment" style Var instances defined in the app.
|
||||
func (a *Application) getAllVars() ([]types.Var, error) {
|
||||
result := []types.Var{}
|
||||
var result []types.Var
|
||||
errs := &interror.KustomizationErrors{}
|
||||
|
||||
bases, err := a.loadBasesAsFlatList()
|
||||
@@ -297,6 +309,7 @@ func (a *Application) getAllVars() ([]types.Var, error) {
|
||||
errs.Append(err)
|
||||
continue
|
||||
}
|
||||
b.ldr.Cleanup()
|
||||
result = append(result, vars...)
|
||||
}
|
||||
for _, v := range a.kustomization.Vars {
|
||||
|
||||
@@ -18,11 +18,12 @@ package app
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/internal/loadertest"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
@@ -53,6 +54,14 @@ secretGenerator:
|
||||
DB_USERNAME: "printf admin"
|
||||
DB_PASSWORD: "printf somepw"
|
||||
type: Opaque
|
||||
`
|
||||
kustomizationContent2 = `
|
||||
secretGenerator:
|
||||
- name: secret
|
||||
timeoutSeconds: 1
|
||||
commands:
|
||||
USER: "sleep 2"
|
||||
type: Opaque
|
||||
`
|
||||
deploymentContent = `apiVersion: apps/v1
|
||||
metadata:
|
||||
@@ -67,31 +76,30 @@ metadata:
|
||||
)
|
||||
|
||||
func makeLoader1(t *testing.T) loader.Loader {
|
||||
loader := loadertest.NewFakeLoader("/testpath")
|
||||
err := loader.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContent1))
|
||||
ldr := loadertest.NewFakeLoader("/testpath")
|
||||
err := ldr.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContent1))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake loader.")
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
err = loader.AddFile("/testpath/deployment.yaml", []byte(deploymentContent))
|
||||
err = ldr.AddFile("/testpath/deployment.yaml", []byte(deploymentContent))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake loader.")
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
err = loader.AddFile("/testpath/namespace.yaml", []byte(namespaceContent))
|
||||
err = ldr.AddFile("/testpath/namespace.yaml", []byte(namespaceContent))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake loader.")
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
return loader
|
||||
return ldr
|
||||
}
|
||||
|
||||
var deploy = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
|
||||
var cmap = schema.GroupVersionKind{Version: "v1", Kind: "ConfigMap"}
|
||||
var secret = schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
|
||||
var ns = schema.GroupVersionKind{Version: "v1", Kind: "Namespace"}
|
||||
var svc = schema.GroupVersionKind{Version: "v1", Kind: "Service"}
|
||||
|
||||
func TestResources1(t *testing.T) {
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(deploy, "dply1"): resource.NewResourceFromMap(
|
||||
resource.NewResIdWithPrefixNamespace(deploy, "dply1", "foo-", "ns1"): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@@ -123,7 +131,7 @@ func TestResources1(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
resource.NewResId(cmap, "literalConfigMap"): resource.NewResourceFromMap(
|
||||
resource.NewResIdWithPrefixNamespace(cmap, "literalConfigMap", "foo-", "ns1"): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
@@ -142,8 +150,8 @@ func TestResources1(t *testing.T) {
|
||||
"DB_USERNAME": "admin",
|
||||
"DB_PASSWORD": "somepw",
|
||||
},
|
||||
}),
|
||||
resource.NewResId(secret, "secret"): resource.NewResourceFromMap(
|
||||
}).SetBehavior(resource.BehaviorCreate),
|
||||
resource.NewResIdWithPrefixNamespace(secret, "secret", "foo-", "ns1"): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
@@ -163,8 +171,8 @@ func TestResources1(t *testing.T) {
|
||||
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
|
||||
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
||||
},
|
||||
}),
|
||||
resource.NewResId(ns, "ns1"): resource.NewResourceFromMap(
|
||||
}).SetBehavior(resource.BehaviorCreate),
|
||||
resource.NewResIdWithPrefixNamespace(ns, "ns1", "foo-", ""): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Namespace",
|
||||
@@ -180,7 +188,9 @@ func TestResources1(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
l := makeLoader1(t)
|
||||
app, err := NewApplication(l)
|
||||
fakeFs := fs.MakeFakeFS()
|
||||
fakeFs.Mkdir("/")
|
||||
app, err := NewApplication(l, fakeFs)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected construction error %v", err)
|
||||
}
|
||||
@@ -195,146 +205,44 @@ func TestResources1(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawResources1(t *testing.T) {
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(deploy, "dply1"): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "dply1",
|
||||
},
|
||||
}),
|
||||
resource.NewResId(ns, "ns1"): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Namespace",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "ns1",
|
||||
},
|
||||
}),
|
||||
func TestResourceNotFound(t *testing.T) {
|
||||
l := loadertest.NewFakeLoader("/testpath")
|
||||
err := l.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContent1))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
l := makeLoader1(t)
|
||||
app, err := NewApplication(l)
|
||||
fakeFs := fs.MakeFakeFS()
|
||||
fakeFs.Mkdir("/")
|
||||
app, err := NewApplication(l, fakeFs)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected construction error %v", err)
|
||||
}
|
||||
actual, err := app.MakeUncustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected RawResources error %v", err)
|
||||
_, err = app.MakeCustomizedResMap()
|
||||
if err == nil {
|
||||
t.Fatalf("Didn't get the expected error for an unknown resource")
|
||||
}
|
||||
|
||||
if err := expected.ErrorIfNotEqual(actual); err != nil {
|
||||
t.Fatalf("unexpected inequality: %v", err)
|
||||
if !strings.Contains(err.Error(), `cannot read file "/testpath/deployment.yaml"`) {
|
||||
t.Fatalf("Unpexpected error message %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
kustomizationContentBase = `
|
||||
namePrefix: foo-
|
||||
commonLabels:
|
||||
app: banana
|
||||
resources:
|
||||
- deployment.yaml
|
||||
`
|
||||
kustomizationContentOverlay = `
|
||||
commonLabels:
|
||||
env: staging
|
||||
resources:
|
||||
- service.yaml
|
||||
bases:
|
||||
- base
|
||||
`
|
||||
serviceContent = `apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: svc
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
`
|
||||
)
|
||||
|
||||
func makeLoader2(t *testing.T) loader.Loader {
|
||||
loader := loadertest.NewFakeLoader("/testpath")
|
||||
err := loader.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContentOverlay))
|
||||
func TestSecretTimeout(t *testing.T) {
|
||||
l := loadertest.NewFakeLoader("/testpath")
|
||||
err := l.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContent2))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
err = loader.AddFile("/testpath/service.yaml", []byte(serviceContent))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake loader.")
|
||||
}
|
||||
err = loader.AddDirectory("/testpath/base", os.ModeDir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake loader.")
|
||||
}
|
||||
err = loader.AddFile("/testpath/base/"+constants.KustomizationFileName, []byte(kustomizationContentBase))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake loader.")
|
||||
}
|
||||
err = loader.AddFile("/testpath/base/deployment.yaml", []byte(deploymentContent))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake loader.")
|
||||
}
|
||||
return loader
|
||||
}
|
||||
|
||||
// TODO: This test covers incorrect behavior; it should not pass.
|
||||
// It asks for raw resources. The Service resource is returned in raw form,
|
||||
// but the resources in the base are modified to have the banana label,
|
||||
// the 'foo' name prefix, etc. This method exists only to support the
|
||||
// diff command comparing customized to non-customized resources;
|
||||
// perhaps it's not worth supporting the command.
|
||||
func TestRawResources2(t *testing.T) {
|
||||
expected := resmap.ResMap{
|
||||
resource.NewResId(deploy, "dply1"): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "foo-dply1",
|
||||
"labels": map[string]interface{}{
|
||||
"app": "banana",
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app": "banana",
|
||||
},
|
||||
},
|
||||
"template": map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"labels": map[string]interface{}{
|
||||
"app": "banana",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
resource.NewResId(svc, "svc"): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Service",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "svc",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"type": "LoadBalancer",
|
||||
},
|
||||
}),
|
||||
}
|
||||
l := makeLoader2(t)
|
||||
app, err := NewApplication(l)
|
||||
fakeFs := fs.MakeFakeFS()
|
||||
fakeFs.Mkdir("/")
|
||||
app, err := NewApplication(l, fakeFs)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected construction error %v", err)
|
||||
}
|
||||
actual, err := app.MakeUncustomizedResMap()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected RawResources error %v", err)
|
||||
_, err = app.MakeCustomizedResMap()
|
||||
if err == nil {
|
||||
t.Fatalf("Didn't get the expected error for an unknown resource")
|
||||
}
|
||||
|
||||
if err := expected.ErrorIfNotEqual(actual); err != nil {
|
||||
t.Fatalf("unexpected inequality: %v", err)
|
||||
if !strings.Contains(err.Error(), "killed") {
|
||||
t.Fatalf("Unpexpected error message %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
98
pkg/commands/addbase.go
Normal file
98
pkg/commands/addbase.go
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
type addBaseOptions struct {
|
||||
baseDirectoryPaths string
|
||||
}
|
||||
|
||||
// newCmdAddBase adds the file path of the kustomize base to the kustomization file.
|
||||
func newCmdAddBase(fsys fs.FileSystem) *cobra.Command {
|
||||
var o addBaseOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "base",
|
||||
Short: "Adds one or more bases to the kustomization.yaml in current directory",
|
||||
Example: `
|
||||
add base {filepath1},{filepath2}`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = o.Complete(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunAddBase(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates addBase command.
|
||||
func (o *addBaseOptions) Validate(args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("must specify a base directory")
|
||||
}
|
||||
o.baseDirectoryPaths = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complete completes addBase command.
|
||||
func (o *addBaseOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunAddBase runs addBase command (do real work).
|
||||
func (o *addBaseOptions) RunAddBase(fsys fs.FileSystem) error {
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := mf.read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// split directory paths
|
||||
paths := strings.Split(o.baseDirectoryPaths, ",")
|
||||
for _, path := range paths {
|
||||
if !fsys.Exists(path) {
|
||||
return errors.New(path + " does not exist")
|
||||
}
|
||||
if stringInSlice(path, m.Bases) {
|
||||
return fmt.Errorf("base %s already in kustomization file", path)
|
||||
}
|
||||
m.Bases = append(m.Bases, path)
|
||||
|
||||
}
|
||||
|
||||
return mf.write(m)
|
||||
}
|
||||
100
pkg/commands/addbase_test.go
Normal file
100
pkg/commands/addbase_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
baseDirectoryPaths = "my/path/to/wonderful/base,other/path/to/even/more/wonderful/base"
|
||||
)
|
||||
|
||||
func TestAddBaseHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
bases := strings.Split(baseDirectoryPaths, ",")
|
||||
for _, base := range bases {
|
||||
fakeFS.Mkdir(base)
|
||||
}
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdAddBase(fakeFS)
|
||||
args := []string{baseDirectoryPaths}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
|
||||
for _, base := range bases {
|
||||
if !strings.Contains(string(content), base) {
|
||||
t.Errorf("expected base name in kustomization")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddBaseAlreadyThere(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
// Create fake directories
|
||||
bases := strings.Split(baseDirectoryPaths, ",")
|
||||
for _, base := range bases {
|
||||
fakeFS.Mkdir(base)
|
||||
}
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdAddBase(fakeFS)
|
||||
args := []string{baseDirectoryPaths}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected cmd error: %v", err)
|
||||
}
|
||||
// adding an existing base should return an error
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err == nil {
|
||||
t.Errorf("expected already there problem")
|
||||
}
|
||||
var expectedErrors []string
|
||||
for _, base := range bases {
|
||||
msg := "base " + base + " already in kustomization file"
|
||||
expectedErrors = append(expectedErrors, msg)
|
||||
if !stringInSlice(msg, expectedErrors) {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAddBaseNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdAddBase(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "must specify a base directory" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
172
pkg/commands/addmetadata.go
Normal file
172
pkg/commands/addmetadata.go
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/validate"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// KindOfAdd is the kind of metadata being added: label or annotation
|
||||
type KindOfAdd int
|
||||
|
||||
const (
|
||||
annotation KindOfAdd = iota
|
||||
label
|
||||
)
|
||||
|
||||
func (k KindOfAdd) String() string {
|
||||
kinds := [...]string{
|
||||
"annotation",
|
||||
"label",
|
||||
}
|
||||
if k < 0 || k > 1 {
|
||||
return "Unknown metadatakind"
|
||||
}
|
||||
return kinds[k]
|
||||
}
|
||||
|
||||
type addMetadataOptions struct {
|
||||
metadata map[string]string
|
||||
}
|
||||
|
||||
// newCmdAddAnnotation adds one or more commonAnnotations to the kustomization file.
|
||||
func newCmdAddAnnotation(fsys fs.FileSystem) *cobra.Command {
|
||||
var o addMetadataOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "annotation",
|
||||
Short: "Adds one or more commonAnnotations to the kustomization.yaml in current directory",
|
||||
Example: `
|
||||
add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.ValidateAndParse(args, annotation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunAddAnnotation(fsys, annotation)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// newCmdAddLabel adds one or more commonLabels to the kustomization file.
|
||||
func newCmdAddLabel(fsys fs.FileSystem) *cobra.Command {
|
||||
var o addMetadataOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "label",
|
||||
Short: "Adds one or more commonLabels to the kustomization.yaml in current directory",
|
||||
Example: `
|
||||
add label {labelKey1:labelValue1},{labelKey2:labelValue2}`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.ValidateAndParse(args, label)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunAddLabel(fsys, label)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// ValidateAndParse validates addLabel and addAnnotation commands and parses them into o.metadata
|
||||
func (o *addMetadataOptions) ValidateAndParse(args []string, k KindOfAdd) error {
|
||||
o.metadata = make(map[string]string)
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("must specify %s", k)
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("%ss must be comma-separated, with no spaces. See help text for example", k)
|
||||
}
|
||||
inputs := strings.Split(args[0], ",")
|
||||
for _, input := range inputs {
|
||||
switch k {
|
||||
case label:
|
||||
valid, err := validate.IsValidLabel(input)
|
||||
if !valid {
|
||||
return err
|
||||
}
|
||||
case annotation:
|
||||
valid, err := validate.IsValidAnnotation(input)
|
||||
if !valid {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown metadata kind %s", k)
|
||||
}
|
||||
//parse annotation keys and values into metadata
|
||||
kv := strings.Split(input, ":")
|
||||
o.metadata[kv[0]] = kv[1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunAddAnnotation runs addAnnotation command, doing the real work.
|
||||
func (o *addMetadataOptions) RunAddAnnotation(fsys fs.FileSystem, k KindOfAdd) error {
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := mf.read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.CommonAnnotations == nil {
|
||||
m.CommonAnnotations = make(map[string]string)
|
||||
}
|
||||
|
||||
for key, value := range o.metadata {
|
||||
if k == annotation {
|
||||
if _, ok := m.CommonAnnotations[key]; ok {
|
||||
return fmt.Errorf("%s %s already in kustomization file", k, key)
|
||||
}
|
||||
m.CommonAnnotations[key] = value
|
||||
}
|
||||
}
|
||||
return mf.write(m)
|
||||
}
|
||||
|
||||
// RunAddLabel runs addLabel command, doing the real work.
|
||||
func (o *addMetadataOptions) RunAddLabel(fsys fs.FileSystem, k KindOfAdd) error {
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := mf.read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.CommonLabels == nil {
|
||||
m.CommonLabels = make(map[string]string)
|
||||
}
|
||||
|
||||
for key, value := range o.metadata {
|
||||
if _, ok := m.CommonLabels[key]; ok {
|
||||
return fmt.Errorf("%s %s already in kustomization file", k, key)
|
||||
}
|
||||
m.CommonLabels[key] = value
|
||||
}
|
||||
return mf.write(m)
|
||||
}
|
||||
190
pkg/commands/addmetadata_test.go
Normal file
190
pkg/commands/addmetadata_test.go
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func TestParseValidateInput(t *testing.T) {
|
||||
var testcases = []struct {
|
||||
input string
|
||||
valid bool
|
||||
name string
|
||||
expectedData map[string]string
|
||||
kind KindOfAdd
|
||||
}{
|
||||
{
|
||||
input: "otters:cute",
|
||||
valid: true,
|
||||
name: "Adds single input",
|
||||
expectedData: map[string]string{
|
||||
"otters": "cute",
|
||||
},
|
||||
kind: label,
|
||||
},
|
||||
{
|
||||
input: "owls:great,unicorns:magical",
|
||||
valid: true,
|
||||
name: "Adds two items",
|
||||
expectedData: map[string]string{
|
||||
"owls": "great",
|
||||
"unicorns": "magical",
|
||||
},
|
||||
kind: label,
|
||||
},
|
||||
{
|
||||
input: "123:45",
|
||||
valid: true,
|
||||
name: "Numeric input is allowed",
|
||||
expectedData: map[string]string{
|
||||
"123": "45",
|
||||
},
|
||||
kind: annotation,
|
||||
},
|
||||
{
|
||||
input: " ",
|
||||
valid: false,
|
||||
name: "Empty space input",
|
||||
expectedData: nil,
|
||||
kind: annotation,
|
||||
},
|
||||
}
|
||||
var o addMetadataOptions
|
||||
for _, tc := range testcases {
|
||||
args := []string{tc.input}
|
||||
err := o.ValidateAndParse(args, tc.kind)
|
||||
if err != nil && tc.valid {
|
||||
t.Errorf("for test case %s, unexpected cmd error: %v", tc.name, err)
|
||||
}
|
||||
if err == nil && !tc.valid {
|
||||
t.Errorf("unexpected error: expected invalid format error for test case %v", tc.name)
|
||||
}
|
||||
//o.metadata should be the same as expectedData
|
||||
if tc.valid {
|
||||
if !reflect.DeepEqual(o.metadata, tc.expectedData) {
|
||||
t.Errorf("unexpected error: for test case %s, unexpected data was added", tc.name)
|
||||
}
|
||||
} else {
|
||||
if len(o.metadata) != 0 {
|
||||
t.Errorf("unexpected error: for test case %s, expected no data to be added", tc.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAddAnnotation(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
var o addMetadataOptions
|
||||
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
|
||||
|
||||
err := o.RunAddAnnotation(fakeFS, annotation)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
// adding the same test input should not work
|
||||
err = o.RunAddAnnotation(fakeFS, annotation)
|
||||
if err == nil {
|
||||
t.Errorf("expected already in kustomization file error")
|
||||
}
|
||||
// adding new annotations should work
|
||||
o.metadata = map[string]string{"new": "annotation"}
|
||||
err = o.RunAddAnnotation(fakeFS, annotation)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
cmd := newCmdAddAnnotation(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error but error is %v", err)
|
||||
}
|
||||
if err != nil && err.Error() != "must specify annotation" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
func TestAddAnnotationMultipleArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
cmd := newCmdAddAnnotation(fakeFS)
|
||||
args := []string{"this:annotation", "has:spaces"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err == nil {
|
||||
t.Errorf("expected an error but error is %v", err)
|
||||
}
|
||||
if err != nil && err.Error() != "annotations must be comma-separated, with no spaces. See help text for example" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAddLabel(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
var o addMetadataOptions
|
||||
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
|
||||
|
||||
err := o.RunAddLabel(fakeFS, label)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
// adding the same test input should not work
|
||||
err = o.RunAddLabel(fakeFS, label)
|
||||
if err == nil {
|
||||
t.Errorf("expected already in kustomization file error")
|
||||
}
|
||||
// adding new labels should work
|
||||
o.metadata = map[string]string{"new": "label"}
|
||||
err = o.RunAddLabel(fakeFS, label)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdAddLabel(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error but error is: %v", err)
|
||||
}
|
||||
if err != nil && err.Error() != "must specify label" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelMultipleArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
cmd := newCmdAddLabel(fakeFS)
|
||||
args := []string{"this:input", "has:spaces"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err == nil {
|
||||
t.Errorf("expected an error but error is: %v", err)
|
||||
}
|
||||
if err != nil && err.Error() != "labels must be comma-separated, with no spaces. See help text for example" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,7 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -28,11 +27,11 @@ import (
|
||||
)
|
||||
|
||||
type addPatchOptions struct {
|
||||
patchFilePath string
|
||||
patchFilePaths []string
|
||||
}
|
||||
|
||||
// newCmdAddPatch adds the name of a file containing a patch to the kustomization file.
|
||||
func newCmdAddPatch(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
func newCmdAddPatch(fsys fs.FileSystem) *cobra.Command {
|
||||
var o addPatchOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -49,7 +48,7 @@ func newCmdAddPatch(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunAddPatch(out, errOut, fsys)
|
||||
return o.RunAddPatch(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
@@ -57,10 +56,10 @@ func newCmdAddPatch(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
|
||||
// Validate validates addPatch command.
|
||||
func (o *addPatchOptions) Validate(args []string) error {
|
||||
if len(args) != 1 {
|
||||
if len(args) == 0 {
|
||||
return errors.New("must specify a patch file")
|
||||
}
|
||||
o.patchFilePath = args[0]
|
||||
o.patchFilePaths = args
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -70,11 +69,14 @@ func (o *addPatchOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// RunAddPatch runs addPatch command (do real work).
|
||||
func (o *addPatchOptions) RunAddPatch(out, errOut io.Writer, fsys fs.FileSystem) error {
|
||||
_, err := fsys.Stat(o.patchFilePath)
|
||||
func (o *addPatchOptions) RunAddPatch(fsys fs.FileSystem) error {
|
||||
patches, err := globPatterns(fsys, o.patchFilePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(patches) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
@@ -86,11 +88,13 @@ func (o *addPatchOptions) RunAddPatch(out, errOut io.Writer, fsys fs.FileSystem)
|
||||
return err
|
||||
}
|
||||
|
||||
if stringInSlice(o.patchFilePath, m.Patches) {
|
||||
return fmt.Errorf("patch %s already in kustomization file", o.patchFilePath)
|
||||
for _, patch := range patches {
|
||||
if stringInSlice(patch, m.Patches) {
|
||||
log.Printf("patch %s already in kustomization file", patch)
|
||||
continue
|
||||
}
|
||||
m.Patches = append(m.Patches, patch)
|
||||
}
|
||||
|
||||
m.Patches = append(m.Patches, o.patchFilePath)
|
||||
|
||||
return mf.write(m)
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ limitations under the License.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"strings"
|
||||
@@ -36,13 +34,13 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
)
|
||||
|
||||
func TestAddPatchHappyPath(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
||||
fakeFS.WriteFile(patchFileName+"another", []byte(patchFileContent))
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdAddPatch(buf, os.Stderr, fakeFS)
|
||||
args := []string{patchFileName}
|
||||
cmd := newCmdAddPatch(fakeFS)
|
||||
args := []string{patchFileName + "*"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
@@ -54,36 +52,34 @@ func TestAddPatchHappyPath(t *testing.T) {
|
||||
if !strings.Contains(string(content), patchFileName) {
|
||||
t.Errorf("expected patch name in kustomization")
|
||||
}
|
||||
if !strings.Contains(string(content), patchFileName+"another") {
|
||||
t.Errorf("expected patch name in kustomization")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPatchAlreadyThere(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdAddPatch(buf, os.Stderr, fakeFS)
|
||||
cmd := newCmdAddPatch(fakeFS)
|
||||
args := []string{patchFileName}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected cmd error: %v", err)
|
||||
}
|
||||
|
||||
// adding an existing patch should return an error
|
||||
// adding an existing patch shouldn't return an error
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err == nil {
|
||||
t.Errorf("expected already there problem")
|
||||
}
|
||||
if err.Error() != "patch "+patchFileName+" already in kustomization file" {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPatchNoArgs(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdAddPatch(buf, os.Stderr, fakeFS)
|
||||
cmd := newCmdAddPatch(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
|
||||
@@ -18,8 +18,7 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -28,11 +27,11 @@ import (
|
||||
)
|
||||
|
||||
type addResourceOptions struct {
|
||||
resourceFilePath string
|
||||
resourceFilePaths []string
|
||||
}
|
||||
|
||||
// newCmdAddResource adds the name of a file containing a resource to the kustomization file.
|
||||
func newCmdAddResource(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
func newCmdAddResource(fsys fs.FileSystem) *cobra.Command {
|
||||
var o addResourceOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -49,7 +48,7 @@ func newCmdAddResource(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunAddResource(out, errOut, fsys)
|
||||
return o.RunAddResource(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
@@ -57,10 +56,10 @@ func newCmdAddResource(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command
|
||||
|
||||
// Validate validates addResource command.
|
||||
func (o *addResourceOptions) Validate(args []string) error {
|
||||
if len(args) != 1 {
|
||||
if len(args) == 0 {
|
||||
return errors.New("must specify a resource file")
|
||||
}
|
||||
o.resourceFilePath = args[0]
|
||||
o.resourceFilePaths = args
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -70,11 +69,14 @@ func (o *addResourceOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// RunAddResource runs addResource command (do real work).
|
||||
func (o *addResourceOptions) RunAddResource(out, errOut io.Writer, fsys fs.FileSystem) error {
|
||||
_, err := fsys.Stat(o.resourceFilePath)
|
||||
func (o *addResourceOptions) RunAddResource(fsys fs.FileSystem) error {
|
||||
resources, err := globPatterns(fsys, o.resourceFilePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resources) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
@@ -86,11 +88,13 @@ func (o *addResourceOptions) RunAddResource(out, errOut io.Writer, fsys fs.FileS
|
||||
return err
|
||||
}
|
||||
|
||||
if stringInSlice(o.resourceFilePath, m.Resources) {
|
||||
return fmt.Errorf("resource %s already in kustomization file", o.resourceFilePath)
|
||||
for _, resource := range resources {
|
||||
if stringInSlice(resource, m.Resources) {
|
||||
log.Printf("resource %s already in kustomization file", resource)
|
||||
continue
|
||||
}
|
||||
m.Resources = append(m.Resources, resource)
|
||||
}
|
||||
|
||||
m.Resources = append(m.Resources, o.resourceFilePath)
|
||||
|
||||
return mf.write(m)
|
||||
}
|
||||
|
||||
@@ -17,11 +17,8 @@ limitations under the License.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
@@ -52,13 +49,13 @@ secretGenerator: []
|
||||
)
|
||||
|
||||
func TestAddResourceHappyPath(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||
fakeFS.WriteFile(resourceFileName+"another", []byte(resourceFileContent))
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdAddResource(buf, os.Stderr, fakeFS)
|
||||
args := []string{resourceFileName}
|
||||
cmd := newCmdAddResource(fakeFS)
|
||||
args := []string{resourceFileName + "*"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
@@ -70,36 +67,34 @@ func TestAddResourceHappyPath(t *testing.T) {
|
||||
if !strings.Contains(string(content), resourceFileName) {
|
||||
t.Errorf("expected resource name in kustomization")
|
||||
}
|
||||
if !strings.Contains(string(content), resourceFileName+"another") {
|
||||
t.Errorf("expected resource name in kustomization")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddResourceAlreadyThere(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdAddResource(buf, os.Stderr, fakeFS)
|
||||
cmd := newCmdAddResource(fakeFS)
|
||||
args := []string{resourceFileName}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected cmd error: %v", err)
|
||||
}
|
||||
|
||||
// adding an existing resource should return an error
|
||||
// adding an existing resource doesn't return an error
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err == nil {
|
||||
t.Errorf("expected already there problem")
|
||||
}
|
||||
if err.Error() != "resource "+resourceFileName+" already in kustomization file" {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error :%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddResourceNoArgs(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdAddResource(buf, os.Stderr, fakeFS)
|
||||
cmd := newCmdAddResource(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
|
||||
@@ -18,7 +18,6 @@ package commands
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -32,25 +31,45 @@ import (
|
||||
|
||||
type buildOptions struct {
|
||||
kustomizationPath string
|
||||
outputPath string
|
||||
}
|
||||
|
||||
var examples = `
|
||||
Use the file somedir/kustomization.yaml to generate a set of api resources:
|
||||
build somedir
|
||||
|
||||
Use a url pointing to a remote directory/kustomization.yaml to generate a set of api resources:
|
||||
build url
|
||||
The url should follow hashicorp/go-getter URL format described in
|
||||
https://github.com/hashicorp/go-getter#url-format
|
||||
|
||||
url examples:
|
||||
github.com/kubernetes-sigs/kustomize//examples/multibases?ref=v1.0.6
|
||||
github.com/Liujingfang1/mysql
|
||||
github.com/Liujingfang1/kustomize//examples/helloWorld?ref=repoUrl2
|
||||
`
|
||||
|
||||
// newCmdBuild creates a new build command.
|
||||
func newCmdBuild(out, errOut io.Writer, fs fs.FileSystem) *cobra.Command {
|
||||
func newCmdBuild(out io.Writer, fs fs.FileSystem) *cobra.Command {
|
||||
var o buildOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "build [path]",
|
||||
Short: "Print current configuration per contents of " + constants.KustomizationFileName,
|
||||
Example: "Use the file somedir/" + constants.KustomizationFileName +
|
||||
" to generate a set of api resources:\nbuild somedir/",
|
||||
Use: "build [path]",
|
||||
Short: "Print current configuration per contents of " + constants.KustomizationFileName,
|
||||
Example: examples,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunBuild(out, errOut, fs)
|
||||
return o.RunBuild(out, fs)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(
|
||||
&o.outputPath,
|
||||
"output", "o", "",
|
||||
"If specified, write the build output to this path.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -68,20 +87,14 @@ func (o *buildOptions) Validate(args []string) error {
|
||||
}
|
||||
|
||||
// RunBuild runs build command.
|
||||
func (o *buildOptions) RunBuild(out, errOut io.Writer, fs fs.FileSystem) error {
|
||||
l := loader.Init([]loader.SchemeLoader{loader.NewFileLoader(fs)})
|
||||
|
||||
absPath, err := filepath.Abs(o.kustomizationPath)
|
||||
func (o *buildOptions) RunBuild(out io.Writer, fSys fs.FileSystem) error {
|
||||
rootLoader, err := loader.NewLoader(o.kustomizationPath, "", fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rootLoader.Cleanup()
|
||||
|
||||
rootLoader, err := l.New(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
application, err := app.NewApplication(rootLoader)
|
||||
application, err := app.NewApplication(rootLoader, fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -97,6 +110,10 @@ func (o *buildOptions) RunBuild(out, errOut io.Writer, fs fs.FileSystem) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.outputPath != "" {
|
||||
return fSys.WriteFile(o.outputPath, res)
|
||||
}
|
||||
_, err = out.Write(res)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func TestBuildValidate(t *testing.T) {
|
||||
func TestBuild(t *testing.T) {
|
||||
const updateEnvVar = "UPDATE_KUSTOMIZE_EXPECTED_DATA"
|
||||
updateKustomizeExpected := os.Getenv(updateEnvVar) == "true"
|
||||
fs := fs.MakeRealFS()
|
||||
fSys := fs.MakeRealFS()
|
||||
|
||||
testcases := sets.NewString()
|
||||
filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
||||
@@ -104,12 +104,12 @@ func TestBuild(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testcaseName := range testcases.List() {
|
||||
t.Run(testcaseName, func(t *testing.T) { runBuildTestCase(t, testcaseName, updateKustomizeExpected, fs) })
|
||||
t.Run(testcaseName, func(t *testing.T) { runBuildTestCase(t, testcaseName, updateKustomizeExpected, fSys) })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func runBuildTestCase(t *testing.T, testcaseName string, updateKustomizeExpected bool, fs fs.FileSystem) {
|
||||
func runBuildTestCase(t *testing.T, testcaseName string, updateKustomizeExpected bool, fSys fs.FileSystem) {
|
||||
name := testcaseName
|
||||
testcase := buildTestCase{}
|
||||
testcaseDir := filepath.Join("testdata", "testcase-"+name)
|
||||
@@ -125,7 +125,7 @@ func runBuildTestCase(t *testing.T, testcaseName string, updateKustomizeExpected
|
||||
kustomizationPath: testcase.Filename,
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err = ops.RunBuild(buf, os.Stderr, fs)
|
||||
err = ops.RunBuild(buf, fSys)
|
||||
switch {
|
||||
case err != nil && len(testcase.ExpectedError) == 0:
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
|
||||
@@ -18,10 +18,12 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
// dataConfig encapsulates the options for add configmap/Secret commands.
|
||||
type dataConfig struct {
|
||||
// cMapFlagsAndArgs encapsulates the options for add configmap commands.
|
||||
type cMapFlagsAndArgs struct {
|
||||
// Name of configMap/Secret (required)
|
||||
Name string
|
||||
// FileSources to derive the configMap/Secret from (optional)
|
||||
@@ -34,7 +36,7 @@ type dataConfig struct {
|
||||
}
|
||||
|
||||
// Validate validates required fields are set to support structured generation.
|
||||
func (a *dataConfig) Validate(args []string) error {
|
||||
func (a *cMapFlagsAndArgs) Validate(args []string) error {
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("name must be specified once")
|
||||
}
|
||||
@@ -48,3 +50,12 @@ func (a *dataConfig) Validate(args []string) error {
|
||||
// TODO: Should we check if the path exists? if it's valid, if it's within the same (sub-)directory?
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *cMapFlagsAndArgs) ExpandFileSource(fSys fs.FileSystem) error {
|
||||
result, err := globPatterns(fSys, a.FileSources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.FileSources = result
|
||||
return nil
|
||||
}
|
||||
@@ -17,11 +17,14 @@ limitations under the License.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func TestDataConfigValidation_NoName(t *testing.T) {
|
||||
config := dataConfig{}
|
||||
config := cMapFlagsAndArgs{}
|
||||
|
||||
if config.Validate([]string{}) == nil {
|
||||
t.Fatal("Validation should fail if no name is specified")
|
||||
@@ -29,7 +32,7 @@ func TestDataConfigValidation_NoName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
|
||||
config := dataConfig{}
|
||||
config := cMapFlagsAndArgs{}
|
||||
|
||||
if config.Validate([]string{"name", "othername"}) == nil {
|
||||
t.Fatal("Validation should fail if more than one name is specified")
|
||||
@@ -39,12 +42,12 @@ func TestDataConfigValidation_MoreThanOneName(t *testing.T) {
|
||||
func TestDataConfigValidation_Flags(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config dataConfig
|
||||
config cMapFlagsAndArgs
|
||||
shouldFail bool
|
||||
}{
|
||||
{
|
||||
name: "env-file-source and literal are both set",
|
||||
config: dataConfig{
|
||||
config: cMapFlagsAndArgs{
|
||||
LiteralSources: []string{"one", "two"},
|
||||
EnvFileSource: "three",
|
||||
},
|
||||
@@ -52,7 +55,7 @@ func TestDataConfigValidation_Flags(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "env-file-source and from-file are both set",
|
||||
config: dataConfig{
|
||||
config: cMapFlagsAndArgs{
|
||||
FileSources: []string{"one", "two"},
|
||||
EnvFileSource: "three",
|
||||
},
|
||||
@@ -60,12 +63,12 @@ func TestDataConfigValidation_Flags(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "we don't have any option set",
|
||||
config: dataConfig{},
|
||||
config: cMapFlagsAndArgs{},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
name: "we have from-file and literal ",
|
||||
config: dataConfig{
|
||||
config: cMapFlagsAndArgs{
|
||||
LiteralSources: []string{"one", "two"},
|
||||
FileSources: []string{"three", "four"},
|
||||
},
|
||||
@@ -81,3 +84,21 @@ func TestDataConfigValidation_Flags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandFileSource(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.Create("dir/config1")
|
||||
fakeFS.Create("dir/config2")
|
||||
fakeFS.Create("dir/reademe")
|
||||
config := cMapFlagsAndArgs{
|
||||
FileSources: []string{"dir/config*"},
|
||||
}
|
||||
config.ExpandFileSource(fakeFS)
|
||||
expected := []string{
|
||||
"dir/config1",
|
||||
"dir/config2",
|
||||
}
|
||||
if !reflect.DeepEqual(config.FileSources, expected) {
|
||||
t.Fatalf("FileSources is not correctly expanded: %v", config.FileSources)
|
||||
}
|
||||
}
|
||||
@@ -19,18 +19,16 @@ package commands
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// NewDefaultCommand returns the default (aka root) command for kustomize command.
|
||||
func NewDefaultCommand() *cobra.Command {
|
||||
fsys := fs.MakeRealFS()
|
||||
stdOut, stdErr := os.Stdout, os.Stderr
|
||||
stdOut := os.Stdout
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: "kustomize",
|
||||
@@ -43,10 +41,10 @@ See https://github.com/kubernetes-sigs/kustomize
|
||||
}
|
||||
|
||||
c.AddCommand(
|
||||
newCmdBuild(stdOut, stdErr, fsys),
|
||||
newCmdDiff(stdOut, stdErr, fsys),
|
||||
newCmdEdit(stdOut, stdErr, fsys),
|
||||
version.NewCmdVersion(stdOut),
|
||||
// TODO: Make consistent API for newCmd* functions.
|
||||
newCmdBuild(stdOut, fsys),
|
||||
newCmdEdit(fsys),
|
||||
newCmdVersion(stdOut),
|
||||
)
|
||||
c.PersistentFlags().AddGoFlagSet(flag.CommandLine)
|
||||
|
||||
@@ -57,7 +55,7 @@ See https://github.com/kubernetes-sigs/kustomize
|
||||
}
|
||||
|
||||
// newCmdEdit returns an instance of 'edit' subcommand.
|
||||
func newCmdEdit(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
func newCmdEdit(fsys fs.FileSystem) *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "edit",
|
||||
Short: "Edits a kustomization file",
|
||||
@@ -72,17 +70,17 @@ func newCmdEdit(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
c.AddCommand(
|
||||
newCmdAdd(stdOut, stdErr, fsys),
|
||||
newCmdSet(stdOut, stdErr, fsys),
|
||||
newCmdAdd(fsys),
|
||||
newCmdSet(fsys),
|
||||
)
|
||||
return c
|
||||
}
|
||||
|
||||
// newAddCommand returns an instance of 'add' subcommand.
|
||||
func newCmdAdd(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
func newCmdAdd(fsys fs.FileSystem) *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "Adds configmap/resource/patch to the kustomization file.",
|
||||
Short: "Adds configmap/resource/patch/base to the kustomization file.",
|
||||
Long: "",
|
||||
Example: `
|
||||
# Adds a configmap to the kustomization file
|
||||
@@ -93,19 +91,32 @@ func newCmdAdd(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
|
||||
# Adds a patch to the kustomization
|
||||
kustomize edit add patch <filepath>
|
||||
|
||||
# Adds one or more base directories to the kustomization
|
||||
kustomize edit add base <filepath>
|
||||
kustomize edit add base <filepath1>,<filepath2>,<filepath3>
|
||||
|
||||
# Adds one or more commonLabels to the kustomization
|
||||
kustomize edit add label {labelKey1:labelValue1},{labelKey2:labelValue2}
|
||||
|
||||
# Adds one or more commonAnnotations to the kustomization
|
||||
kustomize edit add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}
|
||||
`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
}
|
||||
c.AddCommand(
|
||||
newCmdAddResource(stdOut, stdErr, fsys),
|
||||
newCmdAddPatch(stdOut, stdErr, fsys),
|
||||
newCmdAddConfigMap(stdErr, fsys),
|
||||
newCmdAddResource(fsys),
|
||||
newCmdAddPatch(fsys),
|
||||
newCmdAddConfigMap(fsys),
|
||||
newCmdAddBase(fsys),
|
||||
newCmdAddLabel(fsys),
|
||||
newCmdAddAnnotation(fsys),
|
||||
)
|
||||
return c
|
||||
}
|
||||
|
||||
// newSetCommand returns an instance of 'set' subcommand.
|
||||
func newCmdSet(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
func newCmdSet(fsys fs.FileSystem) *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "set",
|
||||
Short: "Sets the value of different fields in kustomization file.",
|
||||
@@ -118,7 +129,9 @@ func newCmdSet(stdOut, stdErr io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
}
|
||||
|
||||
c.AddCommand(
|
||||
newCmdSetNamePrefix(stdOut, stdErr, fsys),
|
||||
newCmdSetNamePrefix(fsys),
|
||||
newCmdSetNamespace(fsys),
|
||||
newCmdSetImageTag(fsys),
|
||||
)
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -18,18 +18,18 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
func newCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
var config dataConfig
|
||||
func newCmdAddConfigMap(fSys fs.FileSystem) *cobra.Command {
|
||||
var flagsAndArgs cMapFlagsAndArgs
|
||||
cmd := &cobra.Command{
|
||||
Use: "configmap NAME [--from-file=[key=]source] [--from-literal=key1=value1]",
|
||||
Short: "Adds a configmap to the kustomization file.",
|
||||
@@ -45,45 +45,55 @@ func newCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
kustomize edit add configmap my-configmap --from-env-file=env/path.env
|
||||
`,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
err := config.Validate(args)
|
||||
err := flagsAndArgs.ExpandFileSource(fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load in the kustomization file.
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
err = flagsAndArgs.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := mf.read()
|
||||
// Load the kustomization file.
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add the config map to the kustomization file.
|
||||
err = addConfigMap(m, config)
|
||||
kustomization, err := mf.read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Add the flagsAndArgs map to the kustomization file.
|
||||
err = addConfigMap(
|
||||
kustomization, flagsAndArgs,
|
||||
configmapandsecret.NewConfigMapFactory(
|
||||
fSys, loader.NewFileLoader(fSys)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write out the kustomization file with added configmap.
|
||||
return mf.write(m)
|
||||
return mf.write(kustomization)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringSliceVar(
|
||||
&config.FileSources,
|
||||
&flagsAndArgs.FileSources,
|
||||
"from-file",
|
||||
[]string{},
|
||||
"Key file can be specified using its file path, in which case file basename will be used as configmap key, or optionally with a key and file path, in which case the given key will be used. Specifying a directory will iterate each named file in the directory whose basename is a valid configmap key.")
|
||||
"Key file can be specified using its file path, in which case file basename will be used as configmap "+
|
||||
"key, or optionally with a key and file path, in which case the given key will be used. Specifying a "+
|
||||
"directory will iterate each named file in the directory whose basename is a valid configmap key.")
|
||||
cmd.Flags().StringArrayVar(
|
||||
&config.LiteralSources,
|
||||
&flagsAndArgs.LiteralSources,
|
||||
"from-literal",
|
||||
[]string{},
|
||||
"Specify a key and literal value to insert in configmap (i.e. mykey=somevalue)")
|
||||
cmd.Flags().StringVar(
|
||||
&config.EnvFileSource,
|
||||
&flagsAndArgs.EnvFileSource,
|
||||
"from-env-file",
|
||||
"",
|
||||
"Specify the path to a file to read lines of key=val pairs to create a configmap (i.e. a Docker .env file).")
|
||||
@@ -91,27 +101,27 @@ func newCmdAddConfigMap(errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
// addConfigMap updates a configmap within a kustomization file, using the data in config.
|
||||
// Note: error may leave kustomization file in an undefined state. Suggest passing a copy
|
||||
// of kustomization file.
|
||||
func addConfigMap(m *types.Kustomization, config dataConfig) error {
|
||||
cm := getOrCreateConfigMap(m, config.Name)
|
||||
|
||||
err := mergeData(&cm.DataSources, config)
|
||||
// addConfigMap adds a configmap to a kustomization file.
|
||||
// Note: error may leave kustomization file in an undefined state.
|
||||
// Suggest passing a copy of kustomization file.
|
||||
func addConfigMap(
|
||||
k *types.Kustomization,
|
||||
flagsAndArgs cMapFlagsAndArgs,
|
||||
factory *configmapandsecret.ConfigMapFactory) error {
|
||||
cmArgs := makeConfigMapArgs(k, flagsAndArgs.Name)
|
||||
err := mergeFlagsIntoCmArgs(&cmArgs.DataSources, flagsAndArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate by trying to create corev1.configmap.
|
||||
_, _, err = configmapandsecret.MakeConfigmapAndGenerateName(*cm)
|
||||
_, _, err = factory.MakeUnstructAndGenerateName(cmArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getOrCreateConfigMap(m *types.Kustomization, name string) *types.ConfigMapArgs {
|
||||
func makeConfigMapArgs(m *types.Kustomization, name string) *types.ConfigMapArgs {
|
||||
for i, v := range m.ConfigMapGenerator {
|
||||
if name == v.Name {
|
||||
return &m.ConfigMapGenerator[i]
|
||||
@@ -123,13 +133,12 @@ func getOrCreateConfigMap(m *types.Kustomization, name string) *types.ConfigMapA
|
||||
return &m.ConfigMapGenerator[len(m.ConfigMapGenerator)-1]
|
||||
}
|
||||
|
||||
func mergeData(src *types.DataSources, config dataConfig) error {
|
||||
src.LiteralSources = append(src.LiteralSources, config.LiteralSources...)
|
||||
src.FileSources = append(src.FileSources, config.FileSources...)
|
||||
if src.EnvSource != "" && src.EnvSource != config.EnvFileSource {
|
||||
return fmt.Errorf("updating existing env source '%s' not allowed.", src.EnvSource)
|
||||
func mergeFlagsIntoCmArgs(src *types.DataSources, flags cMapFlagsAndArgs) error {
|
||||
src.LiteralSources = append(src.LiteralSources, flags.LiteralSources...)
|
||||
src.FileSources = append(src.FileSources, flags.FileSources...)
|
||||
if src.EnvSource != "" && src.EnvSource != flags.EnvFileSource {
|
||||
return fmt.Errorf("updating existing env source '%s' not allowed", src.EnvSource)
|
||||
}
|
||||
src.EnvSource = config.EnvFileSource
|
||||
|
||||
src.EnvSource = flags.EnvFileSource
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ import (
|
||||
)
|
||||
|
||||
func TestNewAddConfigMapIsNotNil(t *testing.T) {
|
||||
if newCmdAddConfigMap(nil, fs.MakeFakeFS()) == nil {
|
||||
if newCmdAddConfigMap(fs.MakeFakeFS()) == nil {
|
||||
t.Fatal("newCmdAddConfigMap shouldn't be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrCreateConfigMap(t *testing.T) {
|
||||
func TestMakeConfigMapArgs(t *testing.T) {
|
||||
cmName := "test-config-name"
|
||||
|
||||
kustomization := &types.Kustomization{
|
||||
@@ -39,24 +39,24 @@ func TestGetOrCreateConfigMap(t *testing.T) {
|
||||
if len(kustomization.ConfigMapGenerator) != 0 {
|
||||
t.Fatal("Initial kustomization should not have any configmaps")
|
||||
}
|
||||
cm := getOrCreateConfigMap(kustomization, cmName)
|
||||
args := makeConfigMapArgs(kustomization, cmName)
|
||||
|
||||
if cm == nil {
|
||||
t.Fatalf("ConfigMap should always be non-nil")
|
||||
if args == nil {
|
||||
t.Fatalf("args should always be non-nil")
|
||||
}
|
||||
|
||||
if len(kustomization.ConfigMapGenerator) != 1 {
|
||||
t.Fatalf("Kustomization should have newly created configmap")
|
||||
}
|
||||
|
||||
if &kustomization.ConfigMapGenerator[len(kustomization.ConfigMapGenerator)-1] != cm {
|
||||
t.Fatalf("Pointer address for newly inserted configmap should be same")
|
||||
if &kustomization.ConfigMapGenerator[len(kustomization.ConfigMapGenerator)-1] != args {
|
||||
t.Fatalf("Pointer address for newly inserted configmap generator should be same")
|
||||
}
|
||||
|
||||
existingCM := getOrCreateConfigMap(kustomization, cmName)
|
||||
args2 := makeConfigMapArgs(kustomization, cmName)
|
||||
|
||||
if existingCM != cm {
|
||||
t.Fatalf("should have returned an existing cm with name: %v", cmName)
|
||||
if args2 != args {
|
||||
t.Fatalf("should have returned an existing args with name: %v", cmName)
|
||||
}
|
||||
|
||||
if len(kustomization.ConfigMapGenerator) != 1 {
|
||||
@@ -64,10 +64,10 @@ func TestGetOrCreateConfigMap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeData_LiteralSources(t *testing.T) {
|
||||
func TestMergeFlagsIntoCmArgs_LiteralSources(t *testing.T) {
|
||||
ds := &types.DataSources{}
|
||||
|
||||
err := mergeData(ds, dataConfig{LiteralSources: []string{"k1=v1"}})
|
||||
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k1=v1"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge initial literal source should not return error")
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func TestMergeData_LiteralSources(t *testing.T) {
|
||||
t.Fatalf("Initial literal source should have been added")
|
||||
}
|
||||
|
||||
err = mergeData(ds, dataConfig{LiteralSources: []string{"k2=v2"}})
|
||||
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{LiteralSources: []string{"k2=v2"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge second literal source should not return error")
|
||||
}
|
||||
@@ -86,10 +86,10 @@ func TestMergeData_LiteralSources(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeData_FileSources(t *testing.T) {
|
||||
func TestMergeFlagsIntoCmArgs_FileSources(t *testing.T) {
|
||||
ds := &types.DataSources{}
|
||||
|
||||
err := mergeData(ds, dataConfig{FileSources: []string{"file1"}})
|
||||
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file1"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge initial file source should not return error")
|
||||
}
|
||||
@@ -98,7 +98,7 @@ func TestMergeData_FileSources(t *testing.T) {
|
||||
t.Fatalf("Initial file source should have been added")
|
||||
}
|
||||
|
||||
err = mergeData(ds, dataConfig{FileSources: []string{"file2"}})
|
||||
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{FileSources: []string{"file2"}})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge second file source should not return error")
|
||||
}
|
||||
@@ -108,12 +108,12 @@ func TestMergeData_FileSources(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeData_EnvSource(t *testing.T) {
|
||||
func TestMergeFlagsIntoCmArgs_EnvSource(t *testing.T) {
|
||||
envFileName := "env1"
|
||||
envFileName2 := "env2"
|
||||
ds := &types.DataSources{}
|
||||
|
||||
err := mergeData(ds, dataConfig{EnvFileSource: envFileName})
|
||||
err := mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName})
|
||||
if err != nil {
|
||||
t.Fatalf("Merge initial env source should not return error")
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func TestMergeData_EnvSource(t *testing.T) {
|
||||
t.Fatalf("Initial env source filename should have been added")
|
||||
}
|
||||
|
||||
err = mergeData(ds, dataConfig{EnvFileSource: envFileName2})
|
||||
err = mergeFlagsIntoCmArgs(ds, cMapFlagsAndArgs{EnvFileSource: envFileName2})
|
||||
if err == nil {
|
||||
t.Fatalf("Updating env source should return an error")
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/app"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/diff"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
)
|
||||
|
||||
type diffOptions struct {
|
||||
kustomizationPath string
|
||||
}
|
||||
|
||||
// newCmdDiff makes the diff command.
|
||||
func newCmdDiff(out, errOut io.Writer, fs fs.FileSystem) *cobra.Command {
|
||||
var o diffOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "diff [path]",
|
||||
Short: "diff between customized resources and uncustomized resources",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunDiff(out, errOut, fs)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates diff command.
|
||||
func (o *diffOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
return errors.New("specify one path to " + constants.KustomizationFileName)
|
||||
}
|
||||
if len(args) == 0 {
|
||||
o.kustomizationPath = "./"
|
||||
return nil
|
||||
}
|
||||
o.kustomizationPath = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunDiff gets the differences between Application.MakeCustomizedResMap() and Application.MakeUncustomizedResMap().
|
||||
func (o *diffOptions) RunDiff(out, errOut io.Writer, fs fs.FileSystem) error {
|
||||
|
||||
l := loader.Init([]loader.SchemeLoader{loader.NewFileLoader(fs)})
|
||||
|
||||
absPath, err := filepath.Abs(o.kustomizationPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rootLoader, err := l.New(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
application, err := app.NewApplication(rootLoader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
transformedResources, err := application.MakeCustomizedResMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawResources, err := application.MakeUncustomizedResMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return diff.RunDiff(rawResources, transformedResources, out, errOut)
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
type DiffTestCase struct {
|
||||
Description string `yaml:"description"`
|
||||
Args []string `yaml:"args"`
|
||||
Filename string `yaml:"filename"`
|
||||
// path to the file that contains the expected output
|
||||
ExpectedDiff string `yaml:"expectedDiff"`
|
||||
ExpectedError string `yaml:"expectedError"`
|
||||
}
|
||||
|
||||
func TestDiff(t *testing.T) {
|
||||
const updateEnvVar = "UPDATE_KUSTOMIZE_EXPECTED_DATA"
|
||||
updateKustomizeExpected := os.Getenv(updateEnvVar) == "true"
|
||||
|
||||
noopDir, _ := regexp.Compile(`/tmp/noop-[0-9]*/`)
|
||||
transformedDir, _ := regexp.Compile(`/tmp/transformed-[0-9]*/`)
|
||||
timestamp, _ := regexp.Compile(`[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9].[0-9]* [+-]{1}[0-9]{4}`)
|
||||
|
||||
fs := fs.MakeRealFS()
|
||||
|
||||
testcases := sets.NewString()
|
||||
filepath.Walk("testdata", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path == "testdata" {
|
||||
return nil
|
||||
}
|
||||
name := filepath.Base(path)
|
||||
if info.IsDir() {
|
||||
if strings.HasPrefix(name, "testcase-") {
|
||||
testcases.Insert(strings.TrimPrefix(name, "testcase-"))
|
||||
}
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// sanity check that we found the right folder
|
||||
if !testcases.Has("simple") {
|
||||
t.Fatalf("Error locating testcases")
|
||||
}
|
||||
|
||||
for _, testcaseName := range testcases.List() {
|
||||
t.Run(testcaseName, func(t *testing.T) {
|
||||
runDiffTestCase(t, testcaseName, updateKustomizeExpected, fs,
|
||||
noopDir, transformedDir, timestamp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runDiffTestCase(t *testing.T, testcaseName string, updateKustomizeExpected bool, fs fs.FileSystem,
|
||||
noopDir, transformedDir, timestamp *regexp.Regexp) {
|
||||
name := testcaseName
|
||||
testcase := DiffTestCase{}
|
||||
testcaseDir := filepath.Join("testdata", "testcase-"+name)
|
||||
testcaseData, err := ioutil.ReadFile(filepath.Join(testcaseDir, "test.yaml"))
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", name, err)
|
||||
}
|
||||
if err := yaml.Unmarshal(testcaseData, &testcase); err != nil {
|
||||
t.Fatalf("%s: %v", name, err)
|
||||
}
|
||||
|
||||
diffOps := &diffOptions{
|
||||
kustomizationPath: testcase.Filename,
|
||||
}
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
err = diffOps.RunDiff(buf, os.Stderr, fs)
|
||||
switch {
|
||||
case err != nil && len(testcase.ExpectedError) == 0:
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
case err != nil && len(testcase.ExpectedError) != 0:
|
||||
if !strings.Contains(err.Error(), testcase.ExpectedError) {
|
||||
t.Errorf("expected error to contain %q but got: %v", testcase.ExpectedError, err)
|
||||
}
|
||||
return
|
||||
case err == nil && len(testcase.ExpectedError) != 0:
|
||||
t.Errorf("unexpected no error")
|
||||
}
|
||||
|
||||
actualString := string(buf.Bytes())
|
||||
actualString = noopDir.ReplaceAllString(actualString, "/tmp/noop/")
|
||||
actualString = transformedDir.ReplaceAllString(actualString, "/tmp/transformed/")
|
||||
actualString = timestamp.ReplaceAllString(actualString, "YYYY-MM-DD HH:MM:SS")
|
||||
actualBytes := []byte(actualString)
|
||||
if !updateKustomizeExpected {
|
||||
expectedBytes, err := ioutil.ReadFile(testcase.ExpectedDiff)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(actualBytes, expectedBytes) {
|
||||
t.Errorf("%s\ndoesn't equal expected:\n%s\n", actualBytes, expectedBytes)
|
||||
}
|
||||
} else {
|
||||
ioutil.WriteFile(testcase.ExpectedDiff, actualBytes, 0644)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const kustomizationTemplate = `
|
||||
namePrefix: some-prefix
|
||||
# Labels to add to all objects and selectors.
|
||||
# These labels would also be used to form the selector for apply --prune
|
||||
# Named differently than “labels” to avoid confusion with metadata for this object
|
||||
commonLabels:
|
||||
app: helloworld
|
||||
commonAnnotations:
|
||||
note: This is an example annotation
|
||||
resources: []
|
||||
#- service.yaml
|
||||
#- ../some-dir/
|
||||
# There could also be configmaps in Base, which would make these overlays
|
||||
configMapGenerator: []
|
||||
# There could be secrets in Base, if just using a fork/rebase workflow
|
||||
secretGenerator: []
|
||||
`
|
||||
|
||||
type initOptions struct {
|
||||
}
|
||||
|
||||
// NewCmdInit makes the init command.
|
||||
func newCmdInit(out, errOut io.Writer, fs fs.FileSystem) *cobra.Command {
|
||||
var o initOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Creates a file called \"" + constants.KustomizationFileName + "\" in the current directory",
|
||||
Long: "Creates a file called \"" +
|
||||
constants.KustomizationFileName + "\" in the current directory with example values.",
|
||||
Example: `init`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = o.Complete(cmd, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunInit(out, errOut, fs)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates init command.
|
||||
func (o *initOptions) Validate(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
return errors.New("The init command takes no arguments.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Complete completes init command.
|
||||
func (o *initOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunInit writes a kustomization file.
|
||||
func (o *initOptions) RunInit(out, errOut io.Writer, fs fs.FileSystem) error {
|
||||
if _, err := fs.Stat(constants.KustomizationFileName); err == nil {
|
||||
return fmt.Errorf("%q already exists", constants.KustomizationFileName)
|
||||
}
|
||||
return fs.WriteFile(constants.KustomizationFileName, []byte(kustomizationTemplate))
|
||||
}
|
||||
@@ -17,9 +17,13 @@ limitations under the License.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
@@ -30,12 +34,49 @@ import (
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
type kustomizationFile struct {
|
||||
path string
|
||||
fsys fs.FileSystem
|
||||
var (
|
||||
// These field names are the exact kustomization fields
|
||||
kustomizationFields = []string{
|
||||
"APIVersion",
|
||||
"Kind",
|
||||
"Resources",
|
||||
"Bases",
|
||||
"NamePrefix",
|
||||
"Namespace",
|
||||
"Crds",
|
||||
"CommonLabels",
|
||||
"CommonAnnotations",
|
||||
"Patches",
|
||||
"ConfigMapGenerator",
|
||||
"SecretGenerator",
|
||||
"Vars",
|
||||
"ImageTags",
|
||||
}
|
||||
)
|
||||
|
||||
// commentedField records the comment associated with a kustomization field
|
||||
// field has to be a recognized kustomization field
|
||||
// comment can be empty
|
||||
type commentedField struct {
|
||||
field string
|
||||
comment []byte
|
||||
}
|
||||
|
||||
func newKustomizationFile(mPath string, fsys fs.FileSystem) (*kustomizationFile, error) {
|
||||
func (cf *commentedField) appendComment(comment []byte) {
|
||||
cf.comment = append(cf.comment, comment...)
|
||||
}
|
||||
|
||||
func squash(x [][]byte) []byte {
|
||||
return bytes.Join(x, []byte(``))
|
||||
}
|
||||
|
||||
type kustomizationFile struct {
|
||||
path string
|
||||
fsys fs.FileSystem
|
||||
originalFields []*commentedField
|
||||
}
|
||||
|
||||
func newKustomizationFile(mPath string, fsys fs.FileSystem) (*kustomizationFile, error) { // nolint
|
||||
mf := &kustomizationFile{path: mPath, fsys: fsys}
|
||||
err := mf.validate()
|
||||
if err != nil {
|
||||
@@ -45,37 +86,39 @@ func newKustomizationFile(mPath string, fsys fs.FileSystem) (*kustomizationFile,
|
||||
}
|
||||
|
||||
func (mf *kustomizationFile) validate() error {
|
||||
f, err := mf.fsys.Stat(mf.path)
|
||||
if err != nil {
|
||||
if !mf.fsys.Exists(mf.path) {
|
||||
errorMsg := fmt.Sprintf("Missing kustomization file '%s'.\n", mf.path)
|
||||
merr := interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
||||
return merr
|
||||
}
|
||||
if f.IsDir() {
|
||||
if mf.fsys.IsDir(mf.path) {
|
||||
mf.path = path.Join(mf.path, constants.KustomizationFileName)
|
||||
_, err = mf.fsys.Stat(mf.path)
|
||||
if err != nil {
|
||||
if !mf.fsys.Exists(mf.path) {
|
||||
errorMsg := fmt.Sprintf("Missing kustomization file '%s'.\n", mf.path)
|
||||
merr := interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
||||
return merr
|
||||
}
|
||||
} else {
|
||||
if !strings.HasSuffix(mf.path, constants.KustomizationFileName) {
|
||||
errorMsg := fmt.Sprintf("Kustomization file path (%s) should have %s suffix\n", mf.path, constants.KustomizationFileSuffix)
|
||||
merr := interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
||||
return merr
|
||||
errorMsg := fmt.Sprintf("Kustomization file path (%s) should have %s suffix\n",
|
||||
mf.path, constants.KustomizationFileSuffix)
|
||||
return interror.KustomizationError{KustomizationPath: mf.path, ErrorMsg: errorMsg}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mf *kustomizationFile) read() (*types.Kustomization, error) {
|
||||
bytes, err := mf.fsys.ReadFile(mf.path)
|
||||
data, err := mf.fsys.ReadFile(mf.path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var kustomization types.Kustomization
|
||||
err = yaml.Unmarshal(bytes, &kustomization)
|
||||
err = yaml.Unmarshal(data, &kustomization)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = mf.parseCommentedFields(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -84,14 +127,13 @@ func (mf *kustomizationFile) read() (*types.Kustomization, error) {
|
||||
|
||||
func (mf *kustomizationFile) write(kustomization *types.Kustomization) error {
|
||||
if kustomization == nil {
|
||||
return errors.New("util: kustomization file arg is nil.")
|
||||
return errors.New("util: kustomization file arg is nil")
|
||||
}
|
||||
bytes, err := yaml.Marshal(kustomization)
|
||||
data, err := mf.marshal(kustomization)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mf.fsys.WriteFile(mf.path, bytes)
|
||||
return mf.fsys.WriteFile(mf.path, data)
|
||||
}
|
||||
|
||||
func stringInSlice(str string, list []string) bool {
|
||||
@@ -102,3 +144,106 @@ func stringInSlice(str string, list []string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (mf *kustomizationFile) parseCommentedFields(content []byte) error {
|
||||
buffer := bytes.NewBuffer(content)
|
||||
var comments [][]byte
|
||||
|
||||
line, err := buffer.ReadBytes('\n')
|
||||
for err == nil {
|
||||
if isCommentOrBlankLine(line) {
|
||||
comments = append(comments, line)
|
||||
} else {
|
||||
matched, field := findMatchedField(line)
|
||||
if matched {
|
||||
mf.originalFields = append(mf.originalFields, &commentedField{field: field, comment: squash(comments)})
|
||||
comments = [][]byte{}
|
||||
} else if len(comments) > 0 {
|
||||
mf.originalFields[len(mf.originalFields)-1].appendComment(squash(comments))
|
||||
comments = [][]byte{}
|
||||
}
|
||||
}
|
||||
line, err = buffer.ReadBytes('\n')
|
||||
}
|
||||
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mf *kustomizationFile) marshal(kustomization *types.Kustomization) ([]byte, error) {
|
||||
var output []byte
|
||||
for _, comment := range mf.originalFields {
|
||||
output = append(output, comment.comment...)
|
||||
content, err := marshalField(comment.field, kustomization)
|
||||
if err != nil {
|
||||
return content, err
|
||||
}
|
||||
output = append(output, content...)
|
||||
}
|
||||
for _, field := range kustomizationFields {
|
||||
if mf.hasField(field) {
|
||||
continue
|
||||
}
|
||||
content, err := marshalField(field, kustomization)
|
||||
if err != nil {
|
||||
return content, nil
|
||||
}
|
||||
output = append(output, content...)
|
||||
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (mf *kustomizationFile) hasField(name string) bool {
|
||||
for _, n := range mf.originalFields {
|
||||
if n.field == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
isCommentOrBlankLine determines if a line is a comment or blank line
|
||||
Return true for following lines
|
||||
# This line is a comment
|
||||
# This line is also a comment with several leading white spaces
|
||||
|
||||
(The line above is a blank line)
|
||||
*/
|
||||
func isCommentOrBlankLine(line []byte) bool {
|
||||
s := bytes.TrimRight(bytes.TrimLeft(line, " "), "\n")
|
||||
return len(s) == 0 || bytes.HasPrefix(s, []byte(`#`))
|
||||
}
|
||||
|
||||
func findMatchedField(line []byte) (bool, string) {
|
||||
for _, field := range kustomizationFields {
|
||||
// (?i) is for case insensitive regexp matching
|
||||
r := regexp.MustCompile("^(" + "(?i)" + field + "):")
|
||||
if r.Match(line) {
|
||||
return true, field
|
||||
}
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// marshalField marshal a given field of a kustomization object into yaml format.
|
||||
// If the field wasn't in the original kustomization.yaml file or wasn't added,
|
||||
// an empty []byte is returned.
|
||||
func marshalField(field string, kustomization *types.Kustomization) ([]byte, error) {
|
||||
r := reflect.ValueOf(*kustomization)
|
||||
v := r.FieldByName(strings.Title(field))
|
||||
|
||||
if !v.IsValid() || v.Len() == 0 {
|
||||
return []byte{}, nil
|
||||
}
|
||||
|
||||
k := &types.Kustomization{}
|
||||
kr := reflect.ValueOf(k)
|
||||
kv := kr.Elem().FieldByName(strings.Title(field))
|
||||
kv.Set(v)
|
||||
|
||||
return yaml.Marshal(k)
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestEmptyFile(t *testing.T) {
|
||||
func TestNewNotExist(t *testing.T) {
|
||||
badSuffix := "foo.bar"
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.Mkdir(".", 0644)
|
||||
fakeFS.Mkdir(".")
|
||||
fakeFS.Create(badSuffix)
|
||||
_, err := newKustomizationFile(constants.KustomizationFileName, fakeFS)
|
||||
if err == nil {
|
||||
@@ -88,3 +88,140 @@ func TestNewNotExist(t *testing.T) {
|
||||
t.Fatalf("expect an error contains %q, but got %v", contained, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreserveComments(t *testing.T) {
|
||||
kustomizationContentWithComments := []byte(
|
||||
`# shem qing some comments
|
||||
# This is some comment we should preserve
|
||||
# don't delete it
|
||||
resources:
|
||||
- pod.yaml
|
||||
- service.yaml
|
||||
# something you may want to keep
|
||||
vars:
|
||||
- fieldref:
|
||||
fieldPath: metadata.name
|
||||
name: MY_SERVICE_NAME
|
||||
objref:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
name: my-service
|
||||
bases:
|
||||
- ../namespaces
|
||||
# some descriptions for the patches
|
||||
patches:
|
||||
- service.yaml
|
||||
- pod.yaml
|
||||
`)
|
||||
fsys := fs.MakeFakeFS()
|
||||
fsys.Create(constants.KustomizationFileName)
|
||||
fsys.WriteFile(constants.KustomizationFileName, kustomizationContentWithComments)
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
kustomization, err := mf.read()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
if err = mf.write(kustomization); err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
bytes, _ := fsys.ReadFile(mf.path)
|
||||
|
||||
if !reflect.DeepEqual(kustomizationContentWithComments, bytes) {
|
||||
t.Fatal("written kustomization with comments is not the same as original one")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreserveCommentsWithAdjust(t *testing.T) {
|
||||
kustomizationContentWithComments := []byte(`
|
||||
|
||||
|
||||
|
||||
# shem qing some comments
|
||||
# This is some comment we should preserve
|
||||
# don't delete it
|
||||
resources:
|
||||
- pod.yaml
|
||||
# See which field this comment goes into
|
||||
- service.yaml
|
||||
|
||||
APIVersion: v1beta1
|
||||
kind: kustomization.yaml
|
||||
|
||||
# something you may want to keep
|
||||
vars:
|
||||
- fieldref:
|
||||
fieldPath: metadata.name
|
||||
name: MY_SERVICE_NAME
|
||||
objref:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
name: my-service
|
||||
|
||||
BASES:
|
||||
- ../namespaces
|
||||
|
||||
# some descriptions for the patches
|
||||
|
||||
patches:
|
||||
- service.yaml
|
||||
- pod.yaml
|
||||
`)
|
||||
|
||||
expected := []byte(`
|
||||
|
||||
|
||||
|
||||
# shem qing some comments
|
||||
# This is some comment we should preserve
|
||||
# don't delete it
|
||||
# See which field this comment goes into
|
||||
resources:
|
||||
- pod.yaml
|
||||
- service.yaml
|
||||
|
||||
apiVersion: v1beta1
|
||||
kind: kustomization.yaml
|
||||
|
||||
# something you may want to keep
|
||||
vars:
|
||||
- fieldref:
|
||||
fieldPath: metadata.name
|
||||
name: MY_SERVICE_NAME
|
||||
objref:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
name: my-service
|
||||
|
||||
bases:
|
||||
- ../namespaces
|
||||
|
||||
# some descriptions for the patches
|
||||
|
||||
patches:
|
||||
- service.yaml
|
||||
- pod.yaml
|
||||
`)
|
||||
fsys := fs.MakeFakeFS()
|
||||
fsys.Create(constants.KustomizationFileName)
|
||||
fsys.WriteFile(constants.KustomizationFileName, kustomizationContentWithComments)
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
|
||||
kustomization, err := mf.read()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
if err = mf.write(kustomization); err != nil {
|
||||
t.Fatalf("Unexpected Error: %v", err)
|
||||
}
|
||||
bytes, _ := fsys.ReadFile(mf.path)
|
||||
|
||||
if !reflect.DeepEqual(expected, bytes) {
|
||||
t.Fatal("written kustomization with comments is not the same as original one\n", string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -31,7 +30,7 @@ type setNamePrefixOptions struct {
|
||||
}
|
||||
|
||||
// newCmdSetNamePrefix sets the value of the namePrefix field in the kustomization.
|
||||
func newCmdSetNamePrefix(out, errOut io.Writer, fsys fs.FileSystem) *cobra.Command {
|
||||
func newCmdSetNamePrefix(fsys fs.FileSystem) *cobra.Command {
|
||||
var o setNamePrefixOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -52,7 +51,7 @@ and overwrite the value with "acme-" if the field does exist.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunSetNamePrefix(out, errOut, fsys)
|
||||
return o.RunSetNamePrefix(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
@@ -74,7 +73,7 @@ func (o *setNamePrefixOptions) Complete(cmd *cobra.Command, args []string) error
|
||||
}
|
||||
|
||||
// RunSetNamePrefix runs setNamePrefix command (does real work).
|
||||
func (o *setNamePrefixOptions) RunSetNamePrefix(out, errOut io.Writer, fsys fs.FileSystem) error {
|
||||
func (o *setNamePrefixOptions) RunSetNamePrefix(fsys fs.FileSystem) error {
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -17,8 +17,6 @@ limitations under the License.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"strings"
|
||||
@@ -32,11 +30,10 @@ const (
|
||||
)
|
||||
|
||||
func TestSetNamePrefixHappyPath(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdSetNamePrefix(buf, os.Stderr, fakeFS)
|
||||
cmd := newCmdSetNamePrefix(fakeFS)
|
||||
args := []string{goodPrefixValue}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
@@ -52,10 +49,9 @@ func TestSetNamePrefixHappyPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetNamePrefixNoArgs(t *testing.T) {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdSetNamePrefix(buf, os.Stderr, fakeFS)
|
||||
cmd := newCmdSetNamePrefix(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
|
||||
113
pkg/commands/setimagetag.go
Normal file
113
pkg/commands/setimagetag.go
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"sort"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
type setImageTagOptions struct {
|
||||
imageTagMap map[string]string
|
||||
}
|
||||
|
||||
var pattern = regexp.MustCompile("^(.*):([a-zA-Z0-9._-]*)$")
|
||||
|
||||
// newCmdSetImageTag sets the new tags for images in the kustomization.
|
||||
func newCmdSetImageTag(fsys fs.FileSystem) *cobra.Command {
|
||||
var o setImageTagOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "imagetag",
|
||||
Short: "Sets images and their new tags in the kustomization file",
|
||||
Example: `
|
||||
The command
|
||||
set imagetag nginx:1.8.0 my-app:latest
|
||||
will add
|
||||
|
||||
imageTags:
|
||||
- name: nginx
|
||||
newTag: 1.8.0
|
||||
- name: my-app
|
||||
newTag: latest
|
||||
|
||||
to the kustomization file if it doesn't exist,
|
||||
and overwrite the previous newTag if the image name exists.
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunSetImageTags(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates setImageTag command.
|
||||
func (o *setImageTagOptions) Validate(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("no image and newTag specified")
|
||||
}
|
||||
o.imageTagMap = make(map[string]string)
|
||||
for _, arg := range args {
|
||||
imagetag := pattern.FindStringSubmatch(arg)
|
||||
if len(imagetag) != 3 {
|
||||
return errors.New("invalid format of imagetag, must specify it as <image>:<newtag>")
|
||||
}
|
||||
o.imageTagMap[imagetag[1]] = imagetag[2]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunSetImageTags runs setImageTags command (does real work).
|
||||
func (o *setImageTagOptions) RunSetImageTags(fsys fs.FileSystem) error {
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := mf.read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
imageTagMap := map[string]string{}
|
||||
for _, it := range m.ImageTags {
|
||||
imageTagMap[it.Name] = it.NewTag
|
||||
}
|
||||
for key, value := range o.imageTagMap {
|
||||
imageTagMap[key] = value
|
||||
}
|
||||
var imageTags []types.ImageTag
|
||||
for key, value := range imageTagMap {
|
||||
imageTags = append(imageTags, types.ImageTag{Name: key, NewTag: value})
|
||||
}
|
||||
sort.Slice(imageTags, func(i, j int) bool {
|
||||
return imageTags[i].Name < imageTags[j].Name
|
||||
})
|
||||
|
||||
m.ImageTags = imageTags
|
||||
|
||||
return mf.write(m)
|
||||
}
|
||||
99
pkg/commands/setimagetag_test.go
Normal file
99
pkg/commands/setimagetag_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func TestSetImageTagsHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdSetImageTag(fakeFS)
|
||||
args := []string{"image1:tag1", "image2:tag2", "localhost:5000/operator:1.0.0"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
expected := []byte(`
|
||||
imageTags:
|
||||
- name: image1
|
||||
newTag: tag1
|
||||
- name: image2
|
||||
newTag: tag2
|
||||
- name: localhost:5000/operator
|
||||
newTag: 1.0.0
|
||||
`)
|
||||
if !strings.Contains(string(content), string(expected)) {
|
||||
t.Errorf("expected imageTags in kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetImageTagsOverride(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdSetImageTag(fakeFS)
|
||||
args := []string{"image1:tag1", "image2:tag1"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
args = []string{"image2:tag2", "image3:tag3"}
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
expected := []byte(`
|
||||
imageTags:
|
||||
- name: image1
|
||||
newTag: tag1
|
||||
- name: image2
|
||||
newTag: tag2
|
||||
- name: image3
|
||||
newTag: tag3
|
||||
`)
|
||||
if !strings.Contains(string(content), string(expected)) {
|
||||
t.Errorf("expected imageTags in kustomization file %s", string(content))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetImageTagsNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdSetImageTag(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "no image and newTag specified" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
83
pkg/commands/setnamespace.go
Normal file
83
pkg/commands/setnamespace.go
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
type setNamespaceOptions struct {
|
||||
namespace string
|
||||
}
|
||||
|
||||
// newCmdSetNamespace sets the value of the namespace field in the kustomization.
|
||||
func newCmdSetNamespace(fsys fs.FileSystem) *cobra.Command {
|
||||
var o setNamespaceOptions
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "namespace",
|
||||
Short: "Sets the value of the namespace field in the kustomization file",
|
||||
Example: `
|
||||
The command
|
||||
set namespace staging
|
||||
will add the field "namespace: staging" to the kustomization file if it doesn't exist,
|
||||
and overwrite the value with "staging" if the field does exist.
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunSetNamespace(fsys)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates setNamespace command.
|
||||
func (o *setNamespaceOptions) Validate(args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.New("must specify exactly one namespace value")
|
||||
}
|
||||
ns := args[0]
|
||||
if errs := validation.IsDNS1123Label(ns); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid namespace name: %s", ns, strings.Join(errs, ";"))
|
||||
}
|
||||
o.namespace = ns
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunSetNamespace runs setNamespace command (does real work).
|
||||
func (o *setNamespaceOptions) RunSetNamespace(fsys fs.FileSystem) error {
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := mf.read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Namespace = o.namespace
|
||||
return mf.write(m)
|
||||
}
|
||||
103
pkg/commands/setnamespace_test.go
Normal file
103
pkg/commands/setnamespace_test.go
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
goodNamespaceValue = "staging"
|
||||
)
|
||||
|
||||
func TestSetNamespaceHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdSetNamespace(fakeFS)
|
||||
args := []string{goodNamespaceValue}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
expected := []byte(fmt.Sprintf("namespace: %s", goodNamespaceValue))
|
||||
if !strings.Contains(string(content), string(expected)) {
|
||||
t.Errorf("expected namespace in kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNamespaceOverride(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdSetNamespace(fakeFS)
|
||||
args := []string{goodNamespaceValue}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
args = []string{"newnamespace"}
|
||||
err = cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
}
|
||||
content, err := fakeFS.ReadFile(constants.KustomizationFileName)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error: %v", err)
|
||||
}
|
||||
expected := []byte("namespace: newnamespace")
|
||||
if !strings.Contains(string(content), string(expected)) {
|
||||
t.Errorf("expected namespace in kustomization file %s", string(content))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNamespaceNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
|
||||
cmd := newCmdSetNamespace(fakeFS)
|
||||
err := cmd.Execute()
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "must specify exactly one namespace value" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetNamespaceInvalid(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdSetNamespace(fakeFS)
|
||||
args := []string{"/badnamespace/"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "is not a valid namespace name") {
|
||||
t.Errorf("unexpected error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,33 @@ diff -u -N /tmp/noop/apps_v1beta2_Deployment_nginx.yaml /tmp/transformed/apps_v1
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
diff -u -N /tmp/noop/networking.k8s.io_v1_NetworkPolicy_nginx.yaml /tmp/transformed/networking.k8s.io_v1_NetworkPolicy_nginx.yaml
|
||||
--- /tmp/noop/networking.k8s.io_v1_NetworkPolicy_nginx.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/networking.k8s.io_v1_NetworkPolicy_nginx.yaml YYYY-MM-DD HH:MM:SS
|
||||
@@ -1,13 +1,21 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
- name: nginx
|
||||
+ annotations:
|
||||
+ note: This is a test annotation
|
||||
+ labels:
|
||||
+ app: mynginx
|
||||
+ org: example.com
|
||||
+ team: foo
|
||||
+ name: team-foo-nginx
|
||||
spec:
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
- app: nginx
|
||||
+ app: mynginx
|
||||
+ org: example.com
|
||||
+ team: foo
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- key: app
|
||||
diff -u -N /tmp/noop/v1_Service_nginx.yaml /tmp/transformed/v1_Service_nginx.yaml
|
||||
--- /tmp/noop/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/v1_Service_nginx.yaml YYYY-MM-DD HH:MM:SS
|
||||
|
||||
@@ -44,3 +44,28 @@ spec:
|
||||
containers:
|
||||
- image: nginx
|
||||
name: nginx
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
annotations:
|
||||
note: This is a test annotation
|
||||
labels:
|
||||
app: mynginx
|
||||
org: example.com
|
||||
team: foo
|
||||
name: team-foo-nginx
|
||||
spec:
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: mynginx
|
||||
org: example.com
|
||||
team: foo
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- key: app
|
||||
operator: In
|
||||
values:
|
||||
- test
|
||||
|
||||
@@ -6,5 +6,6 @@ commonLabels:
|
||||
commonAnnotations:
|
||||
note: This is a test annotation
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- resources/deployment.yaml
|
||||
- resources/networkpolicy.yaml
|
||||
- resources/service.yaml
|
||||
|
||||
13
pkg/commands/testdata/testcase-base-only/in/resources/networkpolicy.yaml
vendored
Normal file
13
pkg/commands/testdata/testcase-base-only/in/resources/networkpolicy.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
podSelector:
|
||||
matchExpressions:
|
||||
- {key: app, operator: In, values: [test]}
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
6
pkg/commands/testdata/testcase-crds/crd/bee.yaml
vendored
Normal file
6
pkg/commands/testdata/testcase-crds/crd/bee.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1beta1
|
||||
kind: Bee
|
||||
metadata:
|
||||
name: bee
|
||||
spec:
|
||||
action: fly
|
||||
9
pkg/commands/testdata/testcase-crds/crd/kustomization.yaml
vendored
Normal file
9
pkg/commands/testdata/testcase-crds/crd/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
crds:
|
||||
- mycrd.json
|
||||
|
||||
resources:
|
||||
- secret.yaml
|
||||
- mykind.yaml
|
||||
- bee.yaml
|
||||
|
||||
namePrefix: test-
|
||||
170
pkg/commands/testdata/testcase-crds/crd/mycrd.json
vendored
Normal file
170
pkg/commands/testdata/testcase-crds/crd/mycrd.json
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
{
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.Bee": {
|
||||
"Schema": {
|
||||
"description": "Bee",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||
},
|
||||
"spec": {
|
||||
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dependencies": [
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec",
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||
]
|
||||
},
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.BeeList": {
|
||||
"Schema": {
|
||||
"required": [
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.Bee"
|
||||
}
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dependencies": [
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.Bee",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"
|
||||
]
|
||||
},
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference": {
|
||||
"Schema": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dependencies": []
|
||||
},
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec": {
|
||||
"Schema": {
|
||||
"description": "BeeSpec defines the desired state of Bee"
|
||||
},
|
||||
"Dependencies": []
|
||||
},
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus": {
|
||||
"Schema": {
|
||||
"description": "BeeStatus defines the observed state of Bee"
|
||||
},
|
||||
"Dependencies": []
|
||||
},
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.MyKind": {
|
||||
"Schema": {
|
||||
"description": "MyKind",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||
},
|
||||
"spec": {
|
||||
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dependencies": [
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec",
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||
]
|
||||
},
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindList": {
|
||||
"Schema": {
|
||||
"required": [
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKind"
|
||||
}
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"$ref": "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dependencies": [
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.MyKind",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"
|
||||
]
|
||||
},
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec": {
|
||||
"Schema": {
|
||||
"description": "MyKindSpec defines the desired state of MyKind",
|
||||
"properties": {
|
||||
"beeRef": {
|
||||
"x-kubernetes-object-ref-api-version": "v1beta1",
|
||||
"x-kubernetes-object-ref-kind": "Bee",
|
||||
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference"
|
||||
},
|
||||
"secretRef": {
|
||||
"description": "If defined, we use this secret for configuring the MYSQL_ROOT_PASSWORD If it is not set we generate a secret dynamically",
|
||||
"x-kubernetes-object-ref-api-version": "v1",
|
||||
"x-kubernetes-object-ref-kind": "Secret",
|
||||
"$ref": "k8s.io/api/core/v1.LocalObjectReference"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Dependencies": [
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference",
|
||||
"k8s.io/api/core/v1.LocalObjectReference"
|
||||
]
|
||||
},
|
||||
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus": {
|
||||
"Schema": {
|
||||
"description": "MyKindStatus defines the observed state of MyKind"
|
||||
},
|
||||
"Dependencies": []
|
||||
}
|
||||
}
|
||||
9
pkg/commands/testdata/testcase-crds/crd/mykind.yaml
vendored
Normal file
9
pkg/commands/testdata/testcase-crds/crd/mykind.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: jingfang.example.com/v1beta1
|
||||
kind: MyKind
|
||||
metadata:
|
||||
name: mykind
|
||||
spec:
|
||||
secretRef:
|
||||
name: crdsecret
|
||||
beeRef:
|
||||
name: bee
|
||||
6
pkg/commands/testdata/testcase-crds/crd/secret.yaml
vendored
Normal file
6
pkg/commands/testdata/testcase-crds/crd/secret.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: crdsecret
|
||||
data:
|
||||
PATH: YmJiYmJiYmIK
|
||||
36
pkg/commands/testdata/testcase-crds/expected.diff
vendored
Normal file
36
pkg/commands/testdata/testcase-crds/expected.diff
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
diff -u -N /tmp/noop/jingfang.example.com_v1beta1_MyKind_mykind.yaml /tmp/transformed/jingfang.example.com_v1beta1_MyKind_mykind.yaml
|
||||
--- /tmp/noop/jingfang.example.com_v1beta1_MyKind_mykind.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/jingfang.example.com_v1beta1_MyKind_mykind.yaml YYYY-MM-DD HH:MM:SS
|
||||
@@ -1,9 +1,9 @@
|
||||
apiVersion: jingfang.example.com/v1beta1
|
||||
kind: MyKind
|
||||
metadata:
|
||||
- name: mykind
|
||||
+ name: test-mykind
|
||||
spec:
|
||||
beeRef:
|
||||
- name: bee
|
||||
+ name: test-bee
|
||||
secretRef:
|
||||
- name: crdsecret
|
||||
+ name: test-crdsecret
|
||||
diff -u -N /tmp/noop/v1beta1_Bee_bee.yaml /tmp/transformed/v1beta1_Bee_bee.yaml
|
||||
--- /tmp/noop/v1beta1_Bee_bee.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/v1beta1_Bee_bee.yaml YYYY-MM-DD HH:MM:SS
|
||||
@@ -1,6 +1,6 @@
|
||||
apiVersion: v1beta1
|
||||
kind: Bee
|
||||
metadata:
|
||||
- name: bee
|
||||
+ name: test-bee
|
||||
spec:
|
||||
action: fly
|
||||
diff -u -N /tmp/noop/v1_Secret_crdsecret.yaml /tmp/transformed/v1_Secret_crdsecret.yaml
|
||||
--- /tmp/noop/v1_Secret_crdsecret.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/v1_Secret_crdsecret.yaml YYYY-MM-DD HH:MM:SS
|
||||
@@ -3,4 +3,4 @@
|
||||
PATH: YmJiYmJiYmIK
|
||||
kind: Secret
|
||||
metadata:
|
||||
- name: crdsecret
|
||||
+ name: test-crdsecret
|
||||
23
pkg/commands/testdata/testcase-crds/expected.yaml
vendored
Normal file
23
pkg/commands/testdata/testcase-crds/expected.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: v1
|
||||
data:
|
||||
PATH: YmJiYmJiYmIK
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: test-crdsecret
|
||||
---
|
||||
apiVersion: v1beta1
|
||||
kind: Bee
|
||||
metadata:
|
||||
name: test-bee
|
||||
spec:
|
||||
action: fly
|
||||
---
|
||||
apiVersion: jingfang.example.com/v1beta1
|
||||
kind: MyKind
|
||||
metadata:
|
||||
name: test-mykind
|
||||
spec:
|
||||
beeRef:
|
||||
name: test-bee
|
||||
secretRef:
|
||||
name: test-crdsecret
|
||||
5
pkg/commands/testdata/testcase-crds/test.yaml
vendored
Normal file
5
pkg/commands/testdata/testcase-crds/test.yaml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
description: name reference in CRDs
|
||||
args: []
|
||||
filename: testdata/testcase-crds/crd
|
||||
expectedStdout: testdata/testcase-crds/expected.yaml
|
||||
expectedDiff: testdata/testcase-crds/expected.diff
|
||||
@@ -2,8 +2,8 @@ namePrefix: staging-
|
||||
commonLabels:
|
||||
env: staging
|
||||
patches:
|
||||
- deployment-patch1.yaml
|
||||
- deployment-patch2.yaml
|
||||
- patches/deployment-patch1.yaml
|
||||
- patches/deployment-patch2.yaml
|
||||
bases:
|
||||
- ../package/
|
||||
configMapGenerator:
|
||||
|
||||
@@ -48,7 +48,7 @@ diff -u -N /tmp/noop/extensions_v1beta1_Deployment_mungebot.yaml /tmp/transforme
|
||||
- name: foo
|
||||
value: bar
|
||||
- image: nginx
|
||||
+ image: nginx:1.7.9
|
||||
+ image: nginx:1.8.0
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@@ -111,7 +111,7 @@ spec:
|
||||
name: test-infra-app-tls-6hkmhf2224
|
||||
- name: foo
|
||||
value: bar
|
||||
image: nginx:1.7.9
|
||||
image: nginx:1.8.0
|
||||
name: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
description: simple
|
||||
args: []
|
||||
filename: ../examples/simple/instances/exampleinstance/
|
||||
filename: ../examplelayout/simple/instances/exampleinstance/
|
||||
expectedStdout: testdata/testcase-simple/expected.yaml
|
||||
expectedDiff: testdata/testcase-simple/expected.diff
|
||||
|
||||
@@ -41,6 +41,33 @@ diff -u -N /tmp/noop/apps_v1beta1_StatefulSet_cockroachdb.yaml /tmp/transformed/
|
||||
terminationGracePeriodSeconds: 60
|
||||
volumes:
|
||||
- name: datadir
|
||||
diff -u -N /tmp/noop/batch_v1beta1_CronJob_cronjob-example.yaml /tmp/transformed/batch_v1beta1_CronJob_cronjob-example.yaml
|
||||
--- /tmp/noop/batch_v1beta1_CronJob_cronjob-example.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/batch_v1beta1_CronJob_cronjob-example.yaml YYYY-MM-DD HH:MM:SS
|
||||
@@ -1,7 +1,7 @@
|
||||
apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
- name: base-cronjob-example
|
||||
+ name: dev-base-cronjob-example
|
||||
spec:
|
||||
concurrencyPolicy: Forbid
|
||||
jobTemplate:
|
||||
@@ -11,11 +11,11 @@
|
||||
containers:
|
||||
- command:
|
||||
- echo
|
||||
- - base-cockroachdb
|
||||
- - base-test-config-map-259876d7fg
|
||||
+ - dev-base-cockroachdb
|
||||
+ - dev-base-test-config-map-b2g2dmd64b
|
||||
env:
|
||||
- name: CDB_PUBLIC_SVC
|
||||
- value: base-cockroachdb-public
|
||||
+ value: dev-base-cockroachdb-public
|
||||
image: cockroachdb/cockroach:v1.1.5
|
||||
name: cronjob-example
|
||||
schedule: '*/1 * * * *'
|
||||
diff -u -N /tmp/noop/policy_v1beta1_PodDisruptionBudget_cockroachdb-budget.yaml /tmp/transformed/policy_v1beta1_PodDisruptionBudget_cockroachdb-budget.yaml
|
||||
--- /tmp/noop/policy_v1beta1_PodDisruptionBudget_cockroachdb-budget.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/policy_v1beta1_PodDisruptionBudget_cockroachdb-budget.yaml YYYY-MM-DD HH:MM:SS
|
||||
@@ -115,6 +142,15 @@ diff -u -N /tmp/noop/rbac.authorization.k8s.io_v1beta1_Role_cockroachdb.yaml /tm
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
diff -u -N /tmp/noop/v1_ConfigMap_test-config-map.yaml /tmp/transformed/v1_ConfigMap_test-config-map.yaml
|
||||
--- /tmp/noop/v1_ConfigMap_test-config-map.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/v1_ConfigMap_test-config-map.yaml YYYY-MM-DD HH:MM:SS
|
||||
@@ -5,4 +5,4 @@
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
- name: base-test-config-map-259876d7fg
|
||||
+ name: dev-base-test-config-map-b2g2dmd64b
|
||||
diff -u -N /tmp/noop/v1_ServiceAccount_cockroachdb.yaml /tmp/transformed/v1_ServiceAccount_cockroachdb.yaml
|
||||
--- /tmp/noop/v1_ServiceAccount_cockroachdb.yaml YYYY-MM-DD HH:MM:SS
|
||||
+++ /tmp/transformed/v1_ServiceAccount_cockroachdb.yaml YYYY-MM-DD HH:MM:SS
|
||||
|
||||
@@ -1,4 +1,81 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: Role
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
rules:
|
||||
- apiGroups:
|
||||
- certificates.k8s.io
|
||||
resources:
|
||||
- certificatesigningrequests
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: dev-base-cockroachdb
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: dev-base-cockroachdb
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: dev-base-cockroachdb
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: dev-base-cockroachdb
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: v1
|
||||
data:
|
||||
baz: qux
|
||||
foo: bar
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: dev-base-test-config-map-b2g2dmd64b
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
@@ -38,13 +115,6 @@ spec:
|
||||
selector:
|
||||
app: cockroachdb
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
---
|
||||
apiVersion: apps/v1beta1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
@@ -131,6 +201,28 @@ spec:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: dev-base-cronjob-example
|
||||
spec:
|
||||
concurrencyPolicy: Forbid
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- echo
|
||||
- dev-base-cockroachdb
|
||||
- dev-base-test-config-map-b2g2dmd64b
|
||||
env:
|
||||
- name: CDB_PUBLIC_SVC
|
||||
value: dev-base-cockroachdb-public
|
||||
image: cockroachdb/cockroach:v1.1.5
|
||||
name: cronjob-example
|
||||
schedule: '*/1 * * * *'
|
||||
---
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
@@ -142,64 +234,3 @@ spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cockroachdb
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
rules:
|
||||
- apiGroups:
|
||||
- certificates.k8s.io
|
||||
resources:
|
||||
- certificatesigningrequests
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: dev-base-cockroachdb
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: dev-base-cockroachdb
|
||||
namespace: default
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: Role
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: dev-base-cockroachdb
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: dev-base-cockroachdb
|
||||
namespace: default
|
||||
|
||||
21
pkg/commands/testdata/testcase-variable-ref/in/package/cronjob.yaml
vendored
Normal file
21
pkg/commands/testdata/testcase-variable-ref/in/package/cronjob.yaml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
apiVersion: batch/v1beta1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: cronjob-example
|
||||
spec:
|
||||
schedule: "*/1 * * * *"
|
||||
concurrencyPolicy: Forbid
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: cronjob-example
|
||||
image: cockroachdb/cockroach:v1.1.5
|
||||
command:
|
||||
- echo
|
||||
- "$(CDB_STATEFULSET_NAME)"
|
||||
- "$(TEST_CONFIG_MAP)"
|
||||
env:
|
||||
- name: CDB_PUBLIC_SVC
|
||||
value: "$(CDB_PUBLIC_SVC)"
|
||||
@@ -1,6 +1,12 @@
|
||||
namePrefix: base-
|
||||
resources:
|
||||
- cockroachdb-statefulset-secure.yaml
|
||||
- cronjob.yaml
|
||||
configMapGenerator:
|
||||
- name: test-config-map
|
||||
literals:
|
||||
- foo=bar
|
||||
- baz=qux
|
||||
vars:
|
||||
- name: CDB_PUBLIC_SVC
|
||||
objref:
|
||||
@@ -24,3 +30,10 @@ vars:
|
||||
fieldref:
|
||||
fieldpath: metadata.name
|
||||
|
||||
- name: TEST_CONFIG_MAP
|
||||
objref:
|
||||
kind: ConfigMap
|
||||
name: test-config-map
|
||||
apiVersion: v1
|
||||
fieldref:
|
||||
fieldpath: metadata.name
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,6 +14,26 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package temp provides an interface to handle temporary files and
|
||||
// directories.
|
||||
package temp // import "k8s.io/utils/temp"
|
||||
package commands
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func globPatterns(fsys fs.FileSystem, patterns []string) ([]string, error) {
|
||||
var result []string
|
||||
for _, pattern := range patterns {
|
||||
files, err := fsys.Glob(pattern)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(files) == 0 {
|
||||
log.Printf("%s has no match", pattern)
|
||||
continue
|
||||
}
|
||||
result = append(result, files...)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package version
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -32,8 +32,8 @@ var (
|
||||
buildDate = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
)
|
||||
|
||||
// Version represens kustomize version.
|
||||
type Version struct {
|
||||
// version returns the version of kustomize.
|
||||
type version struct {
|
||||
// KustomizeVersion is a kustomize binary version.
|
||||
KustomizeVersion string `json:"kustomizeVersion"`
|
||||
// GitCommit is a git commit
|
||||
@@ -46,9 +46,9 @@ type Version struct {
|
||||
GoArch string `json:"goArch"`
|
||||
}
|
||||
|
||||
// GetVersion returns version.
|
||||
func GetVersion() Version {
|
||||
return Version{
|
||||
// getVersion returns version.
|
||||
func getVersion() version {
|
||||
return version{
|
||||
kustomizeVersion,
|
||||
gitCommit,
|
||||
buildDate,
|
||||
@@ -58,18 +58,18 @@ func GetVersion() Version {
|
||||
}
|
||||
|
||||
// Print prints version.
|
||||
func (v Version) Print(w io.Writer) {
|
||||
func (v version) Print(w io.Writer) {
|
||||
fmt.Fprintf(w, "Version: %+v\n", v)
|
||||
}
|
||||
|
||||
// NewCmdVersion makes version command.
|
||||
func NewCmdVersion(w io.Writer) *cobra.Command {
|
||||
func newCmdVersion(w io.Writer) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Prints the kustomize version",
|
||||
Example: `kustomize version`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
GetVersion().Print(w)
|
||||
getVersion().Print(w)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package configmapandsecret generates configmaps and secrets per generator rules.
|
||||
package configmapandsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
cutil "github.com/kubernetes-sigs/kustomize/pkg/configmapandsecret/util"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/hash"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// MakeConfigmapAndGenerateName makes a configmap and returns the configmap and the name appended with a hash.
|
||||
func MakeConfigmapAndGenerateName(cm types.ConfigMapArgs) (*unstructured.Unstructured, string, error) {
|
||||
corev1CM, err := makeConfigMap(cm)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
h, err := hash.ConfigMapHash(corev1CM)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
nameWithHash := fmt.Sprintf("%s-%s", corev1CM.GetName(), h)
|
||||
unstructuredCM, err := objectToUnstructured(corev1CM)
|
||||
return unstructuredCM, nameWithHash, err
|
||||
}
|
||||
|
||||
// MakeSecretAndGenerateName returns a secret with the name appended with a hash.
|
||||
func MakeSecretAndGenerateName(secret types.SecretArgs, path string) (*unstructured.Unstructured, string, error) {
|
||||
corev1Secret, err := makeSecret(secret, path)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
h, err := hash.SecretHash(corev1Secret)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
nameWithHash := fmt.Sprintf("%s-%s", secret.Name, h)
|
||||
unstructuredCM, err := objectToUnstructured(corev1Secret)
|
||||
return unstructuredCM, nameWithHash, err
|
||||
}
|
||||
|
||||
func objectToUnstructured(in runtime.Object) (*unstructured.Unstructured, error) {
|
||||
marshaled, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out unstructured.Unstructured
|
||||
err = out.UnmarshalJSON(marshaled)
|
||||
return &out, err
|
||||
}
|
||||
|
||||
func makeConfigMap(cm types.ConfigMapArgs) (*corev1.ConfigMap, error) {
|
||||
corev1cm := &corev1.ConfigMap{}
|
||||
corev1cm.APIVersion = "v1"
|
||||
corev1cm.Kind = "ConfigMap"
|
||||
corev1cm.Name = cm.Name
|
||||
corev1cm.Data = map[string]string{}
|
||||
|
||||
if cm.EnvSource != "" {
|
||||
if err := cutil.HandleConfigMapFromEnvFileSource(corev1cm, cm.EnvSource); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if cm.FileSources != nil {
|
||||
if err := cutil.HandleConfigMapFromFileSources(corev1cm, cm.FileSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if cm.LiteralSources != nil {
|
||||
if err := cutil.HandleConfigMapFromLiteralSources(corev1cm, cm.LiteralSources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return corev1cm, nil
|
||||
}
|
||||
|
||||
func makeSecret(secret types.SecretArgs, path string) (*corev1.Secret, error) {
|
||||
corev1secret := &corev1.Secret{}
|
||||
corev1secret.APIVersion = "v1"
|
||||
corev1secret.Kind = "Secret"
|
||||
corev1secret.Name = secret.Name
|
||||
corev1secret.Type = corev1.SecretType(secret.Type)
|
||||
if corev1secret.Type == "" {
|
||||
corev1secret.Type = corev1.SecretTypeOpaque
|
||||
}
|
||||
corev1secret.Data = map[string][]byte{}
|
||||
|
||||
for k, v := range secret.Commands {
|
||||
out, err := createSecretKey(path, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
corev1secret.Data[k] = out
|
||||
}
|
||||
|
||||
return corev1secret, nil
|
||||
}
|
||||
|
||||
func createSecretKey(wd string, command string) ([]byte, error) {
|
||||
fi, err := os.Stat(wd)
|
||||
if err != nil || !fi.IsDir() {
|
||||
wd = filepath.Dir(wd)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", command)
|
||||
cmd.Dir = wd
|
||||
|
||||
return cmd.Output()
|
||||
}
|
||||
216
pkg/configmapandsecret/configmapfactory.go
Normal file
216
pkg/configmapandsecret/configmapfactory.go
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package configmapandsecret generates configmaps and secrets per generator rules.
|
||||
package configmapandsecret
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/hash"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/api/core/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
// ConfigMapFactory makes ConfigMaps.
|
||||
type ConfigMapFactory struct {
|
||||
fSys fs.FileSystem
|
||||
ldr loader.Loader
|
||||
}
|
||||
|
||||
// NewConfigMapFactory returns a new ConfigMapFactory.
|
||||
func NewConfigMapFactory(
|
||||
fSys fs.FileSystem, l loader.Loader) *ConfigMapFactory {
|
||||
return &ConfigMapFactory{fSys: fSys, ldr: l}
|
||||
}
|
||||
|
||||
// MakeUnstructAndGenerateName returns an configmap and the name appended with a hash.
|
||||
func (f *ConfigMapFactory) MakeUnstructAndGenerateName(
|
||||
args *types.ConfigMapArgs) (*unstructured.Unstructured, string, error) {
|
||||
cm, err := f.MakeConfigMap(args)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
h, err := hash.ConfigMapHash(cm)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
nameWithHash := fmt.Sprintf("%s-%s", cm.GetName(), h)
|
||||
unstructuredCM, err := objectToUnstructured(cm)
|
||||
return unstructuredCM, nameWithHash, err
|
||||
}
|
||||
|
||||
func objectToUnstructured(in runtime.Object) (*unstructured.Unstructured, error) {
|
||||
marshaled, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out unstructured.Unstructured
|
||||
err = out.UnmarshalJSON(marshaled)
|
||||
return &out, err
|
||||
}
|
||||
|
||||
func (f *ConfigMapFactory) makeFreshConfigMap(
|
||||
args *types.ConfigMapArgs) *corev1.ConfigMap {
|
||||
cm := &corev1.ConfigMap{}
|
||||
cm.APIVersion = "v1"
|
||||
cm.Kind = "ConfigMap"
|
||||
cm.Name = args.Name
|
||||
cm.Data = map[string]string{}
|
||||
return cm
|
||||
}
|
||||
|
||||
// MakeConfigMap returns a new ConfigMap, or nil and an error.
|
||||
func (f *ConfigMapFactory) MakeConfigMap(
|
||||
args *types.ConfigMapArgs) (*corev1.ConfigMap, error) {
|
||||
var all []kvPair
|
||||
var err error
|
||||
cm := f.makeFreshConfigMap(args)
|
||||
|
||||
pairs, err := keyValuesFromEnvFile(f.ldr, args.EnvSource)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf(
|
||||
"env source file: %s",
|
||||
args.EnvSource))
|
||||
}
|
||||
all = append(all, pairs...)
|
||||
|
||||
pairs, err = keyValuesFromLiteralSources(args.LiteralSources)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf(
|
||||
"literal sources %v", args.LiteralSources))
|
||||
}
|
||||
all = append(all, pairs...)
|
||||
|
||||
pairs, err = keyValuesFromFileSources(f.ldr, args.FileSources)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf(
|
||||
"file sources: %v", args.FileSources))
|
||||
}
|
||||
all = append(all, pairs...)
|
||||
|
||||
for _, kv := range all {
|
||||
err = addKvToConfigMap(cm, kv.key, kv.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func keyValuesFromLiteralSources(sources []string) ([]kvPair, error) {
|
||||
var kvs []kvPair
|
||||
for _, s := range sources {
|
||||
k, v, err := parseLiteralSource(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kvs = append(kvs, kvPair{key: k, value: v})
|
||||
}
|
||||
return kvs, nil
|
||||
}
|
||||
|
||||
func keyValuesFromFileSources(ldr loader.Loader, sources []string) ([]kvPair, error) {
|
||||
var kvs []kvPair
|
||||
for _, s := range sources {
|
||||
k, fPath, err := parseFileSource(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content, err := ldr.Load(fPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kvs = append(kvs, kvPair{key: k, value: string(content)})
|
||||
}
|
||||
return kvs, nil
|
||||
}
|
||||
|
||||
func keyValuesFromEnvFile(l loader.Loader, path string) ([]kvPair, error) {
|
||||
if path == "" {
|
||||
return nil, nil
|
||||
}
|
||||
content, err := l.Load(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keyValuesFromLines(content)
|
||||
}
|
||||
|
||||
// addKvToConfigMap adds the given key and data to the given config map.
|
||||
// Error if key invalid, or already exists.
|
||||
func addKvToConfigMap(configMap *v1.ConfigMap, keyName, data string) error {
|
||||
// Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys.
|
||||
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid key name for a ConfigMap: %s", keyName, strings.Join(errs, ";"))
|
||||
}
|
||||
if _, entryExists := configMap.Data[keyName]; entryExists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists: %v", keyName, configMap.Data)
|
||||
}
|
||||
configMap.Data[keyName] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseFileSource parses the source given.
|
||||
//
|
||||
// Acceptable formats include:
|
||||
// 1. source-path: the basename will become the key name
|
||||
// 2. source-name=source-path: the source-name will become the key name and
|
||||
// source-path is the path to the key file.
|
||||
//
|
||||
// Key names cannot include '='.
|
||||
func parseFileSource(source string) (keyName, filePath string, err error) {
|
||||
numSeparators := strings.Count(source, "=")
|
||||
switch {
|
||||
case numSeparators == 0:
|
||||
return path.Base(source), source, nil
|
||||
case numSeparators == 1 && strings.HasPrefix(source, "="):
|
||||
return "", "", fmt.Errorf("key name for file path %v missing", strings.TrimPrefix(source, "="))
|
||||
case numSeparators == 1 && strings.HasSuffix(source, "="):
|
||||
return "", "", fmt.Errorf("file path for key name %v missing", strings.TrimSuffix(source, "="))
|
||||
case numSeparators > 1:
|
||||
return "", "", errors.New("key names or file paths cannot contain '='")
|
||||
default:
|
||||
components := strings.Split(source, "=")
|
||||
return components[0], components[1], nil
|
||||
}
|
||||
}
|
||||
|
||||
// parseLiteralSource parses the source key=val pair into its component pieces.
|
||||
// This functionality is distinguished from strings.SplitN(source, "=", 2) since
|
||||
// it returns an error in the case of empty keys, values, or a missing equals sign.
|
||||
func parseLiteralSource(source string) (keyName, value string, err error) {
|
||||
// leading equal is invalid
|
||||
if strings.Index(source, "=") == 0 {
|
||||
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
|
||||
}
|
||||
// split after the first equal (so values can have the = character)
|
||||
items := strings.SplitN(source, "=", 2)
|
||||
if len(items) != 2 {
|
||||
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
|
||||
}
|
||||
return items[0], strings.Trim(items[1], "\"'"), nil
|
||||
}
|
||||
@@ -17,10 +17,11 @@ limitations under the License.
|
||||
package configmapandsecret
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -89,41 +90,8 @@ func makeLiteralConfigMap(name string) *corev1.ConfigMap {
|
||||
Data: map[string]string{
|
||||
"a": "x",
|
||||
"b": "y",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestSecret(name string) *corev1.Secret {
|
||||
return &corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Secret",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"DB_USERNAME": []byte("admin"),
|
||||
"DB_PASSWORD": []byte("somepw"),
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
}
|
||||
}
|
||||
|
||||
func makeUnstructuredSecret(name string) *unstructured.Unstructured {
|
||||
return &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": name,
|
||||
"creationTimestamp": nil,
|
||||
},
|
||||
"type": string(corev1.SecretTypeOpaque),
|
||||
"data": map[string]interface{}{
|
||||
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
|
||||
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
||||
},
|
||||
"c": "Hello World",
|
||||
"d": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -141,7 +109,7 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
input: types.ConfigMapArgs{
|
||||
Name: "envConfigMap",
|
||||
DataSources: types.DataSources{
|
||||
EnvSource: "../examples/simple/instances/exampleinstance/configmap/app.env",
|
||||
EnvSource: "../examplelayout/simple/instances/exampleinstance/configmap/app.env",
|
||||
},
|
||||
},
|
||||
expected: makeEnvConfigMap("envConfigMap"),
|
||||
@@ -151,7 +119,7 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
input: types.ConfigMapArgs{
|
||||
Name: "fileConfigMap",
|
||||
DataSources: types.DataSources{
|
||||
FileSources: []string{"../examples/simple/instances/exampleinstance/configmap/app-init.ini"},
|
||||
FileSources: []string{"../examplelayout/simple/instances/exampleinstance/configmap/app-init.ini"},
|
||||
},
|
||||
},
|
||||
expected: makeFileConfigMap("fileConfigMap"),
|
||||
@@ -161,15 +129,18 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
input: types.ConfigMapArgs{
|
||||
Name: "literalConfigMap",
|
||||
DataSources: types.DataSources{
|
||||
LiteralSources: []string{"a=x", "b=y"},
|
||||
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||
},
|
||||
},
|
||||
expected: makeLiteralConfigMap("literalConfigMap"),
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: all tests should use a FakeFs
|
||||
fSys := fs.MakeRealFS()
|
||||
f := NewConfigMapFactory(fSys, loader.NewFileLoader(fSys))
|
||||
for _, tc := range testCases {
|
||||
cm, err := makeConfigMap(tc.input)
|
||||
cm, err := f.MakeConfigMap(&tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
@@ -179,39 +150,6 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConstructSecret(t *testing.T) {
|
||||
secret := types.SecretArgs{
|
||||
Name: "secret",
|
||||
Commands: map[string]string{
|
||||
"DB_USERNAME": "printf admin",
|
||||
"DB_PASSWORD": "printf somepw",
|
||||
},
|
||||
Type: "Opaque",
|
||||
}
|
||||
cm, err := makeSecret(secret, ".")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
expected := makeTestSecret("secret")
|
||||
if !reflect.DeepEqual(*cm, *expected) {
|
||||
t.Fatalf("%#v\ndoesn't match expected:\n%#v", *cm, *expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailConstructSecret(t *testing.T) {
|
||||
secret := types.SecretArgs{
|
||||
Name: "secret",
|
||||
Commands: map[string]string{
|
||||
"FAILURE": "false", // This will fail.
|
||||
},
|
||||
Type: "Opaque",
|
||||
}
|
||||
_, err := makeSecret(secret, ".")
|
||||
if err == nil {
|
||||
t.Fatalf("Expected failure.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjectConvertToUnstructured(t *testing.T) {
|
||||
type testCase struct {
|
||||
description string
|
||||
@@ -234,10 +172,10 @@ func TestObjectConvertToUnstructured(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
actual, err := objectToUnstructured(tc.input)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
t.Fatalf("%s: unexpected error: %v", tc.description, err)
|
||||
}
|
||||
if !reflect.DeepEqual(actual, tc.expected) {
|
||||
t.Fatalf("%#v\ndoesn't match expected\n%#v\n", actual, tc.expected)
|
||||
t.Fatalf("%s: %#v\ndoesn't match expected\n%#v\n", tc.description, actual, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package resmap
|
||||
package configmapandsecret
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -28,14 +28,14 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
||||
// kvPair represents a key value pair.
|
||||
type kvPair struct {
|
||||
key string
|
||||
value string
|
||||
}
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
||||
// keyValuesFromLines parses given content in to a list of key-value pairs.
|
||||
func keyValuesFromLines(content []byte) ([]kvPair, error) {
|
||||
var kvs []kvPair
|
||||
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package resmap
|
||||
package configmapandsecret
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
143
pkg/configmapandsecret/secretfactory.go
Normal file
143
pkg/configmapandsecret/secretfactory.go
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package configmapandsecret
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultCommandTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
// SecretFactory makes Secrets.
|
||||
type SecretFactory struct {
|
||||
fSys fs.FileSystem
|
||||
wd string
|
||||
}
|
||||
|
||||
// NewSecretFactory returns a new SecretFactory.
|
||||
func NewSecretFactory(fSys fs.FileSystem, wd string) *SecretFactory {
|
||||
return &SecretFactory{fSys: fSys, wd: wd}
|
||||
}
|
||||
|
||||
func (f *SecretFactory) makeFreshSecret(args *types.SecretArgs) *corev1.Secret {
|
||||
s := &corev1.Secret{}
|
||||
s.APIVersion = "v1"
|
||||
s.Kind = "Secret"
|
||||
s.Name = args.Name
|
||||
s.Type = corev1.SecretType(args.Type)
|
||||
if s.Type == "" {
|
||||
s.Type = corev1.SecretTypeOpaque
|
||||
}
|
||||
s.Data = map[string][]byte{}
|
||||
return s
|
||||
}
|
||||
|
||||
// MakeSecret returns a new secret.
|
||||
func (f *SecretFactory) MakeSecret(args *types.SecretArgs) (*corev1.Secret, error) {
|
||||
var all []kvPair
|
||||
var err error
|
||||
s := f.makeFreshSecret(args)
|
||||
|
||||
timeout := defaultCommandTimeout
|
||||
if args.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*args.TimeoutSeconds) * time.Second
|
||||
}
|
||||
|
||||
pairs, err := f.keyValuesFromEnvFileCommand(args.EnvCommand, timeout)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf(
|
||||
"env source file: %s",
|
||||
args.EnvCommand))
|
||||
}
|
||||
all = append(all, pairs...)
|
||||
|
||||
pairs, err = f.keyValuesFromCommands(args.Commands, timeout)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf(
|
||||
"commands %v", args.Commands))
|
||||
}
|
||||
all = append(all, pairs...)
|
||||
|
||||
for _, kv := range all {
|
||||
err = addKvToSecret(s, kv.key, kv.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func addKvToSecret(secret *corev1.Secret, keyName, data string) error {
|
||||
// Note, the rules for SecretKeys keys are the exact same as the ones for ConfigMap.
|
||||
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid key name for a Secret: %s", keyName, strings.Join(errs, ";"))
|
||||
}
|
||||
if _, entryExists := secret.Data[keyName]; entryExists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists", keyName)
|
||||
}
|
||||
secret.Data[keyName] = []byte(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *SecretFactory) keyValuesFromEnvFileCommand(cmd string, timeout time.Duration) ([]kvPair, error) {
|
||||
content, err := f.createSecretKey(cmd, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keyValuesFromLines(content)
|
||||
}
|
||||
|
||||
func (f *SecretFactory) keyValuesFromCommands(sources map[string]string, timeout time.Duration) ([]kvPair, error) {
|
||||
var kvs []kvPair
|
||||
for k, cmd := range sources {
|
||||
content, err := f.createSecretKey(cmd, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kvs = append(kvs, kvPair{key: k, value: string(content)})
|
||||
}
|
||||
return kvs, nil
|
||||
}
|
||||
|
||||
// Run a command, return its output as the secret.
|
||||
func (f *SecretFactory) createSecretKey(command string, timeout time.Duration) ([]byte, error) {
|
||||
if !f.fSys.IsDir(f.wd) {
|
||||
f.wd = filepath.Dir(f.wd)
|
||||
if !f.fSys.IsDir(f.wd) {
|
||||
return nil, errors.New("not a directory: " + f.wd)
|
||||
}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", command)
|
||||
cmd.Dir = f.wd
|
||||
return cmd.Output()
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
// HandleConfigMapFromLiteralSources adds the specified literal source
|
||||
// information into the provided configMap.
|
||||
func HandleConfigMapFromLiteralSources(configMap *v1.ConfigMap, literalSources []string) error {
|
||||
for _, literalSource := range literalSources {
|
||||
keyName, value, err := ParseLiteralSource(literalSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = addKeyFromLiteralToConfigMap(configMap, keyName, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleConfigMapFromFileSources adds the specified file source information
|
||||
// into the provided configMap
|
||||
func HandleConfigMapFromFileSources(configMap *v1.ConfigMap, fileSources []string) error {
|
||||
for _, fileSource := range fileSources {
|
||||
keyName, filePath, err := ParseFileSource(fileSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
if strings.Contains(fileSource, "=") {
|
||||
return fmt.Errorf("cannot give a key name for a directory path.")
|
||||
}
|
||||
fileList, err := ioutil.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing files in %s: %v", filePath, err)
|
||||
}
|
||||
for _, item := range fileList {
|
||||
itemPath := path.Join(filePath, item.Name())
|
||||
if item.Mode().IsRegular() {
|
||||
keyName = item.Name()
|
||||
err = addKeyFromFileToConfigMap(configMap, keyName, itemPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := addKeyFromFileToConfigMap(configMap, keyName, filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleConfigMapFromEnvFileSource adds the specified env file source information
|
||||
// into the provided configMap
|
||||
func HandleConfigMapFromEnvFileSource(configMap *v1.ConfigMap, envFileSource string) error {
|
||||
info, err := os.Stat(envFileSource)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", envFileSource, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", envFileSource, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
return fmt.Errorf("env config file cannot be a directory")
|
||||
}
|
||||
|
||||
return addFromEnvFile(envFileSource, func(key, value string) error {
|
||||
return addKeyFromLiteralToConfigMap(configMap, key, value)
|
||||
})
|
||||
}
|
||||
|
||||
// addKeyFromFileToConfigMap adds a key with the given name to a ConfigMap, populating
|
||||
// the value with the content of the given file path, or returns an error.
|
||||
func addKeyFromFileToConfigMap(configMap *v1.ConfigMap, keyName, filePath string) error {
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return addKeyFromLiteralToConfigMap(configMap, keyName, string(data))
|
||||
}
|
||||
|
||||
// addKeyFromLiteralToConfigMap adds the given key and data to the given config map,
|
||||
// returning an error if the key is not valid or if the key already exists.
|
||||
func addKeyFromLiteralToConfigMap(configMap *v1.ConfigMap, keyName, data string) error {
|
||||
// Note, the rules for ConfigMap keys are the exact same as the ones for SecretKeys.
|
||||
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid key name for a ConfigMap: %s", keyName, strings.Join(errs, ";"))
|
||||
}
|
||||
if _, entryExists := configMap.Data[keyName]; entryExists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists: %v.", keyName, configMap.Data)
|
||||
}
|
||||
configMap.Data[keyName] = data
|
||||
return nil
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
var utf8bom = []byte{0xEF, 0xBB, 0xBF}
|
||||
|
||||
// processEnvFileLine returns a blank key if the line is empty or a comment.
|
||||
// The value will be retrieved from the environment if necessary.
|
||||
func processEnvFileLine(line []byte, filePath string,
|
||||
currentLine int) (key, value string, err error) {
|
||||
|
||||
if !utf8.Valid(line) {
|
||||
return ``, ``, fmt.Errorf("env file %s contains invalid utf8 bytes at line %d: %v",
|
||||
filePath, currentLine+1, line)
|
||||
}
|
||||
|
||||
// We trim UTF8 BOM from the first line of the file but no others
|
||||
if currentLine == 0 {
|
||||
line = bytes.TrimPrefix(line, utf8bom)
|
||||
}
|
||||
|
||||
// trim the line from all leading whitespace first
|
||||
line = bytes.TrimLeftFunc(line, unicode.IsSpace)
|
||||
|
||||
// If the line is empty or a comment, we return a blank key/value pair.
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
return ``, ``, nil
|
||||
}
|
||||
|
||||
data := strings.SplitN(string(line), "=", 2)
|
||||
key = data[0]
|
||||
if errs := validation.IsEnvVarName(key); len(errs) != 0 {
|
||||
return ``, ``, fmt.Errorf("%q is not a valid key name: %s", key, strings.Join(errs, ";"))
|
||||
}
|
||||
|
||||
if len(data) == 2 {
|
||||
value = data[1]
|
||||
} else {
|
||||
// No value (no `=` in the line) is a signal to obtain the value
|
||||
// from the environment.
|
||||
value = os.Getenv(key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// addFromEnvFile processes an env file allows a generic addTo to handle the
|
||||
// collection of key value pairs or returns an error.
|
||||
func addFromEnvFile(filePath string, addTo func(key, value string) error) error {
|
||||
f, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
currentLine := 0
|
||||
for scanner.Scan() {
|
||||
// Process the current line, retrieving a key/value pair if
|
||||
// possible.
|
||||
scannedBytes := scanner.Bytes()
|
||||
key, value, err := processEnvFileLine(scannedBytes, filePath, currentLine)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentLine++
|
||||
|
||||
if len(key) == 0 {
|
||||
// no key means line was empty or a comment
|
||||
continue
|
||||
}
|
||||
|
||||
if err = addTo(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
// HandleFromLiteralSources adds the specified literal source information into the provided secret
|
||||
func HandleFromLiteralSources(secret *v1.Secret, literalSources []string) error {
|
||||
for _, literalSource := range literalSources {
|
||||
keyName, value, err := ParseLiteralSource(literalSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = addKeyFromLiteralToSecret(secret, keyName, []byte(value)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleFromFileSources adds the specified file source information into the provided secret
|
||||
func HandleFromFileSources(secret *v1.Secret, fileSources []string) error {
|
||||
for _, fileSource := range fileSources {
|
||||
keyName, filePath, err := ParseFileSource(fileSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", filePath, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
if strings.Contains(fileSource, "=") {
|
||||
return fmt.Errorf("cannot give a key name for a directory path.")
|
||||
}
|
||||
fileList, err := ioutil.ReadDir(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listing files in %s: %v", filePath, err)
|
||||
}
|
||||
for _, item := range fileList {
|
||||
itemPath := path.Join(filePath, item.Name())
|
||||
if item.Mode().IsRegular() {
|
||||
keyName = item.Name()
|
||||
if err = addKeyFromFileToSecret(secret, keyName, itemPath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := addKeyFromFileToSecret(secret, keyName, filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandleFromEnvFileSource adds the specified env file source information
|
||||
// into the provided secret
|
||||
func HandleFromEnvFileSource(secret *v1.Secret, envFileSource string) error {
|
||||
info, err := os.Stat(envFileSource)
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *os.PathError:
|
||||
return fmt.Errorf("error reading %s: %v", envFileSource, err.Err)
|
||||
default:
|
||||
return fmt.Errorf("error reading %s: %v", envFileSource, err)
|
||||
}
|
||||
}
|
||||
if info.IsDir() {
|
||||
return fmt.Errorf("env secret file cannot be a directory")
|
||||
}
|
||||
|
||||
return addFromEnvFile(envFileSource, func(key, value string) error {
|
||||
return addKeyFromLiteralToSecret(secret, key, []byte(value))
|
||||
})
|
||||
}
|
||||
|
||||
func addKeyFromFileToSecret(secret *v1.Secret, keyName, filePath string) error {
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return addKeyFromLiteralToSecret(secret, keyName, data)
|
||||
}
|
||||
|
||||
func addKeyFromLiteralToSecret(secret *v1.Secret, keyName string, data []byte) error {
|
||||
if errs := validation.IsConfigMapKey(keyName); len(errs) != 0 {
|
||||
return fmt.Errorf("%q is not a valid key name for a Secret: %s", keyName, strings.Join(errs, ";"))
|
||||
}
|
||||
|
||||
if _, entryExists := secret.Data[keyName]; entryExists {
|
||||
return fmt.Errorf("cannot add key %s, another key by that name already exists: %v.", keyName, secret.Data)
|
||||
}
|
||||
secret.Data[keyName] = data
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user