mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 10:30:59 +00:00
Compare commits
175 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58492e2d83 | ||
|
|
a9f44aa259 | ||
|
|
b17d7fbbfd | ||
|
|
d78e77fb92 | ||
|
|
64fdb8d760 | ||
|
|
a377ec1dde | ||
|
|
87e9964091 | ||
|
|
60d8334fed | ||
|
|
71f5105a86 | ||
|
|
09a62efe88 | ||
|
|
6dbf4b5b60 | ||
|
|
5401bd367b | ||
|
|
f7def79764 | ||
|
|
cda909a609 | ||
|
|
6f61cadf9f | ||
|
|
1c616b1962 | ||
|
|
849343fb41 | ||
|
|
8810027d89 | ||
|
|
548ea8a9fb | ||
|
|
81b5cf65d6 | ||
|
|
dc9ae64646 | ||
|
|
829cb2baa3 | ||
|
|
7b301446fa | ||
|
|
4a297fa138 | ||
|
|
7811d9f2bc | ||
|
|
21ff81b758 | ||
|
|
485bbddd3e | ||
|
|
6dc80293a6 | ||
|
|
b649ad5c69 | ||
|
|
d782abb214 | ||
|
|
95f5becee1 | ||
|
|
694d809f33 | ||
|
|
df3c3c3357 | ||
|
|
99e770b05a | ||
|
|
340cb2b385 | ||
|
|
cdbd83a645 | ||
|
|
fab2a5a5d7 | ||
|
|
8da2f37181 | ||
|
|
c906a0c16e | ||
|
|
93618166b6 | ||
|
|
babfb3faa9 | ||
|
|
cedf215445 | ||
|
|
51a4907f89 | ||
|
|
6457162383 | ||
|
|
7f0e9e3a6a | ||
|
|
95cf508b2b | ||
|
|
4a565ffdb8 | ||
|
|
17f1779a48 | ||
|
|
a76cb0b008 | ||
|
|
9700bc3298 | ||
|
|
ce31dac24f | ||
|
|
a81b2e32e0 | ||
|
|
b713d5a1cc | ||
|
|
e11ba17248 | ||
|
|
3d9d4bd36f | ||
|
|
6a3e3c3a71 | ||
|
|
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 | ||
|
|
187415430f | ||
|
|
afac2fb46a | ||
|
|
20fd433f75 | ||
|
|
1e3824057b |
@@ -3,7 +3,7 @@ language: go
|
||||
go:
|
||||
- 1.10.x
|
||||
|
||||
# go_import_path: k8s.io/kubectl
|
||||
go_import_path: sigs.k8s.io/kustomize
|
||||
|
||||
before_install:
|
||||
- source ./bin/consider-early-travis-exit.sh
|
||||
|
||||
248
Gopkg.lock
generated
248
Gopkg.lock
generated
@@ -2,175 +2,359 @@
|
||||
|
||||
|
||||
[[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"
|
||||
"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]]
|
||||
digest = "1:10e1dfc240bcd0fce14366215fd2b070e79d9b9460b27382db4423ad74204f2b"
|
||||
name = "github.com/krishicks/yaml-patch"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "83cc9ac50becbbfafb86a89167f3bc5372e8e530"
|
||||
version = "v0.0.10"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d9e483f4b9e306facf126bd90b02d512bd22ea4471e1568867e32221a8abbb16"
|
||||
name = "github.com/mailru/easyjson"
|
||||
packages = [
|
||||
"buffer",
|
||||
"jlexer",
|
||||
"jwriter"
|
||||
"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",
|
||||
@@ -187,25 +371,31 @@
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable",
|
||||
"width"
|
||||
"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",
|
||||
@@ -235,17 +425,24 @@
|
||||
"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/equality",
|
||||
"pkg/api/meta",
|
||||
"pkg/api/resource",
|
||||
"pkg/api/validation",
|
||||
"pkg/apis/meta/v1",
|
||||
"pkg/apis/meta/v1/unstructured",
|
||||
"pkg/apis/meta/v1/validation",
|
||||
"pkg/apis/meta/v1beta1",
|
||||
"pkg/conversion",
|
||||
"pkg/conversion/queryparams",
|
||||
"pkg/fields",
|
||||
@@ -274,28 +471,57 @@
|
||||
"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/common",
|
||||
"pkg/util/proto"
|
||||
"pkg/util/proto",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "b3f03f55328800731ce03a164b80973014ecd455"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "74d444cd05ac6f803960180ec8ccfd5a4358077f7c79a5218a243554cb599274"
|
||||
input-imports = [
|
||||
"github.com/evanphx/json-patch",
|
||||
"github.com/ghodss/yaml",
|
||||
"github.com/golang/glog",
|
||||
"github.com/hashicorp/go-getter",
|
||||
"github.com/krishicks/yaml-patch",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/spf13/cobra",
|
||||
"gopkg.in/yaml.v2",
|
||||
"k8s.io/api/core/v1",
|
||||
"k8s.io/apimachinery/pkg/api/validation",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/validation",
|
||||
"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/validation/field",
|
||||
"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
|
||||
|
||||
12
Gopkg.toml
12
Gopkg.toml
@@ -49,10 +49,18 @@
|
||||
name = "k8s.io/client-go"
|
||||
version = "7.0.0"
|
||||
|
||||
[[constraint]]
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "k8s.io/utils"
|
||||
|
||||
[[constraint]]
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/go-openapi/spec"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/hashicorp/go-getter"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/krishicks/yaml-patch"
|
||||
version = "0.0.10"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -41,14 +41,14 @@ 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 \
|
||||
# Disabling 'goimports' because it reports hyphens in imported package \
|
||||
# names as errors, and we have to vendor them in regardless. \
|
||||
# --enable=goimports \
|
||||
--enable=varcheck \
|
||||
--enable=goconst \
|
||||
--enable=unparam \
|
||||
@@ -60,7 +60,6 @@ function testGoMetalinter {
|
||||
--dupl-threshold=400 --enable=dupl)
|
||||
}
|
||||
|
||||
|
||||
function testGoVet {
|
||||
go vet -all ./...
|
||||
}
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -23,10 +23,10 @@ set -x
|
||||
# - Use /go as the default GOPATH because this is what the image uses
|
||||
# - Link our current directory (containing the source code) to the package location in the GOPATH
|
||||
|
||||
OWNER="kubernetes-sigs"
|
||||
OWNER="sigs.k8s.io"
|
||||
REPO="kustomize"
|
||||
|
||||
GO_PKG_OWNER=$GOPATH/src/github.com/$OWNER
|
||||
GO_PKG_OWNER=$GOPATH/src/$OWNER
|
||||
GO_PKG_PATH=$GO_PKG_OWNER/$REPO
|
||||
|
||||
mkdir -p $GO_PKG_OWNER
|
||||
|
||||
@@ -4,7 +4,7 @@ project_name: kustomize
|
||||
builds:
|
||||
- main: ./kustomize.go
|
||||
binary: kustomize
|
||||
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}}
|
||||
ldflags: -s -X sigs.k8s.io/kustomize/pkg/commands.kustomizeVersion={{.Version}} -X sigs.k8s.io/kustomize/pkg/commands.gitCommit={{.Commit}} -X sigs.k8s.io/kustomize/pkg/commands.buildDate={{.Date}}
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
|
||||
@@ -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).
|
||||
@@ -11,12 +11,12 @@
|
||||
can be considered.
|
||||
1. Submit an issue describing your proposed change to
|
||||
the repo in question.
|
||||
1. The [repo owners](OWNERS) will respond to your issue
|
||||
1. The [repo owners](../OWNERS) will respond to your issue
|
||||
promptly.
|
||||
1. Fork the repo, develop and test your code.
|
||||
See the [github workflow guide].
|
||||
1. For _new features_, provide a markdown-based demo following
|
||||
the pattern established in the [examples](examples) directory.
|
||||
the pattern established in the [examples](../examples) directory.
|
||||
Run `bin/pre-commit.sh` to test your demo.
|
||||
1. Submit a pull request.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
[DAM]: #declarative-application-management
|
||||
[JSON]: https://www.json.org/
|
||||
[JSONPatch]: https://tools.ietf.org/html/rfc6902
|
||||
[Resource]: #resource
|
||||
[YAML]: http://www.yaml.org/start.html
|
||||
[application]: #application
|
||||
@@ -15,16 +16,19 @@
|
||||
[kubernetes]: #kubernetes
|
||||
[kustomize]: #kustomize
|
||||
[kustomization]: #kustomization
|
||||
[off-the-shelf]: #off-the-shelf
|
||||
[off-the-shelf]: #off-the-shelf-configuration
|
||||
[overlay]: #overlay
|
||||
[overlays]: #overlay
|
||||
[patch]: #patch
|
||||
[patches]: #patch
|
||||
[patchJson6902]: #patchJson6902
|
||||
[patchesJson6902]: #patchjson6902
|
||||
[proposal]: https://github.com/kubernetes/community/pull/1629
|
||||
[rebase]: https://git-scm.com/docs/git-rebase
|
||||
[resource]: #resource
|
||||
[resources]: #resource
|
||||
[rpm]: https://en.wikipedia.org/wiki/Rpm_(software)
|
||||
[strategic merge]: https://github.com/kubernetes/community/blob/master/contributors/devel/strategic-merge-patch.md
|
||||
[target]: #target
|
||||
[variant]: #variant
|
||||
[variants]: #variant
|
||||
@@ -254,6 +258,15 @@ dependencies. Since any resource file can be used as a
|
||||
patch, one cannot reliably distinguish a resource from
|
||||
a patch just by looking at the file's [YAML].
|
||||
|
||||
## patchJson6902
|
||||
A _patchJson6902_ refers to a kubernetes object and a [JSONPatch]
|
||||
that can patch the object. A [JSONPatch] contains a list of operations to change the object's field directly.
|
||||
This is different from [patch], which is
|
||||
applied to a target kubernetes object by [strategic merge].
|
||||
|
||||
_patchesJson6902_ is a field in kustomization. It contains a list of
|
||||
_patchJson6902_.
|
||||
|
||||
## resource
|
||||
|
||||
A _resource_, in the context of kustomize, is a path to
|
||||
@@ -285,7 +298,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
|
||||
|
||||
@@ -83,15 +83,29 @@ secretGenerator:
|
||||
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_.
|
||||
@@ -102,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.
|
||||
@@ -121,6 +138,41 @@ patches:
|
||||
- deployment_increase_replicas.yaml
|
||||
- deployment_increase_memory.yaml
|
||||
|
||||
# Each entry in this list should resolve to
|
||||
# a kubernetes object and a JSON patch that will be applied
|
||||
# to the object.
|
||||
# The JSON patch is documented at https://tools.ietf.org/html/rfc6902
|
||||
#
|
||||
# target field points to a kubernetes object within the same kustomization
|
||||
# by the object's group, version, kind, name and namespace.
|
||||
# path field is a relative file path of a JSON patch file.
|
||||
# The content in this patch file can be either in JSON format as
|
||||
#
|
||||
# [
|
||||
# {"op": "add", "path": "/some/new/path", "value": "value"},
|
||||
# {"op": "replace", "path": "/some/existing/path", "value": "new value"}
|
||||
# ]
|
||||
#
|
||||
# or in YAML format as
|
||||
#
|
||||
# - op: add
|
||||
# path: /some/new/path
|
||||
# value: value
|
||||
# - op:replace
|
||||
# path: /some/existing/path
|
||||
# value: new value
|
||||
#
|
||||
patchesJson6902
|
||||
- target:
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: my-deployment
|
||||
path: add_init_container.yaml
|
||||
- target:
|
||||
version: v1
|
||||
kind: Service
|
||||
name: my-service
|
||||
path: add_service_annotation.yaml
|
||||
|
||||
# Each entry in this list should be a relative path to
|
||||
# a file for custom resource definition(CRD).
|
||||
@@ -195,8 +247,12 @@ vars:
|
||||
# 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:
|
||||
#
|
||||
# It also supports digests. If digest is present newTag is ignored.
|
||||
imageTags:
|
||||
- name: mycontainerregistry/myimage
|
||||
newTag: v1
|
||||
- name: nginx
|
||||
newTag: 1.8.0
|
||||
- name: alpine
|
||||
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[OTS]: glossary.md#off-the-shelf
|
||||
[OTS]: glossary.md#off-the-shelf-configuration
|
||||
[apply]: glossary.md#apply
|
||||
[applying]: glossary.md#apply
|
||||
[base]: glossary.md#base
|
||||
[fork]: https://guides.github.com/activities/forking/
|
||||
[variants]: glossary.md#variant
|
||||
[kustomization]: glossary.md#kustomization
|
||||
[off-the-shelf]: glossary.md#off-the-shelf
|
||||
[off-the-shelf]: glossary.md#off-the-shelf-configuration
|
||||
[overlays]: glossary.md#overlay
|
||||
[patch]: glossary.md#patch
|
||||
[patches]: glossary.md#patch
|
||||
@@ -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
|
||||
> ```
|
||||
|
||||
@@ -7,7 +7,7 @@ tests, and should work with HEAD
|
||||
|
||||
<!-- @installkustomize @test -->
|
||||
```
|
||||
go get github.com/kubernetes-sigs/kustomize
|
||||
go get sigs.k8s.io/kustomize
|
||||
```
|
||||
|
||||
* [hello world](helloWorld/README.md) - Deploy multiple
|
||||
@@ -36,3 +36,9 @@ go get github.com/kubernetes-sigs/kustomize
|
||||
* [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
|
||||
|
||||
* [json patch](jsonpatch.md) - Apply a json patch in a kustomization
|
||||
|
||||
@@ -73,7 +73,7 @@ commonLabels:
|
||||
who: alice
|
||||
bases:
|
||||
- ../../base
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- temperature.yaml
|
||||
EOF
|
||||
|
||||
@@ -96,7 +96,7 @@ commonLabels:
|
||||
who: bob
|
||||
bases:
|
||||
- ../../base
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- topping.yaml
|
||||
EOF
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[patch]: ../../docs/glossary.md#patch
|
||||
[resource]: ../../docs/glossary.md#resource
|
||||
[variant]: ../../docs/glossary.md#variant
|
||||
[patch]: ../docs/glossary.md#patch
|
||||
[resource]: ../docs/glossary.md#resource
|
||||
[variant]: ../docs/glossary.md#variant
|
||||
|
||||
## ConfigMap generation and rolling updates
|
||||
|
||||
@@ -67,7 +67,7 @@ commonAnnotations:
|
||||
note: Hello, I am staging!
|
||||
bases:
|
||||
- ../../base
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- map.yaml
|
||||
EOF
|
||||
|
||||
@@ -109,8 +109,8 @@ configuration is to
|
||||
|
||||
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.
|
||||
referenced by any other resource, is eventually [garbage
|
||||
collected](https://github.com/kubernetes-sigs/kustomize/issues/242).
|
||||
|
||||
### How this works with kustomize
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ commonAnnotations:
|
||||
note: Hello, I am staging!
|
||||
bases:
|
||||
- ../../base
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- map.yaml
|
||||
EOF
|
||||
```
|
||||
@@ -197,7 +197,7 @@ commonAnnotations:
|
||||
note: Hello, I am production!
|
||||
bases:
|
||||
- ../../base
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- deployment.yaml
|
||||
EOF
|
||||
```
|
||||
|
||||
77
examples/jsonpatch.md
Normal file
77
examples/jsonpatch.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# Demo: applying a json patch
|
||||
|
||||
A kustomization file supports customizing resources via [JSON patches](https://tools.ietf.org/html/rfc6902).
|
||||
|
||||
The example below modifies an `Ingress` object with such a patch.
|
||||
|
||||
Make a `kustomization` containing an ingress resource.
|
||||
|
||||
<!-- @createIngress @test -->
|
||||
```
|
||||
DEMO_HOME=$(mktemp -d)
|
||||
|
||||
cat <<EOF >$DEMO_HOME/kustomization.yaml
|
||||
resources:
|
||||
- ingress.yaml
|
||||
EOF
|
||||
|
||||
cat <<EOF >$DEMO_HOME/ingress.yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: my-ingress
|
||||
spec:
|
||||
rules:
|
||||
- host: foo.bar.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: my-api
|
||||
servicePort: 80
|
||||
EOF
|
||||
```
|
||||
|
||||
Declare a JSON patch file to update two fields of the Ingress object:
|
||||
|
||||
- change host from `foo.bar.com` to `foo.bar.io`
|
||||
- change servicePort from `80` to `8080`
|
||||
|
||||
<!-- @addJsonPatch @test -->
|
||||
```
|
||||
cat <<EOF >$DEMO_HOME/ingress_patch.json
|
||||
[
|
||||
{"op": "replace", "path": "/spec/rules/0/host", "value": "foo.bar.io"},
|
||||
{"op": "replace", "path": "/spec/rules/0/http/paths/0/backend/servicePort", "value": 8080}
|
||||
]
|
||||
EOF
|
||||
```
|
||||
|
||||
Apply the patch by adding _patchesJson6902_ field in kustomization.yaml
|
||||
|
||||
<!-- @applyJsonPatch @test -->
|
||||
```
|
||||
cat <<EOF >>$DEMO_HOME/kustomization.yaml
|
||||
patchesJson6902:
|
||||
- target:
|
||||
group: extensions
|
||||
version: v1beta1
|
||||
kind: Ingress
|
||||
name: my-ingress
|
||||
path: ingress_patch.json
|
||||
EOF
|
||||
```
|
||||
|
||||
Running `kustomize build $DEMO_HOME`, in the ourput confirm that host has been updated correctly.
|
||||
<!-- @confirmHost @test -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "host: foo.bar.io" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
Running `kustomize build $DEMO_HOME`, in the ourput confirm that the servicePort has been updated correctly.
|
||||
<!-- @confirmServicePort @test -->
|
||||
```
|
||||
test 1 == \
|
||||
$(kustomize build $DEMO_HOME | grep "servicePort: 8080" | wc -l); \
|
||||
echo $?
|
||||
```
|
||||
@@ -1,5 +1,5 @@
|
||||
bases:
|
||||
- ../../base
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- deployment.yaml
|
||||
namePrefix: production-
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
bases:
|
||||
- ../../base
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- deployment.yaml
|
||||
nameprefix: staging-
|
||||
configMapGenerator:
|
||||
|
||||
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, staging 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-
|
||||
@@ -150,7 +150,7 @@ Off the shelf MySQL uses `emptyDir` type volume, which
|
||||
gets wiped away if the MySQL Pod is recreated, and that
|
||||
is certainly not desirable for production
|
||||
environment. So we want to use Persistent Disk in
|
||||
production. kustomize lets you apply `patches` to the
|
||||
production. kustomize lets you apply `patchesStrategicMerge` to the
|
||||
resources.
|
||||
|
||||
<!-- @createPatchFile @test -->
|
||||
@@ -176,7 +176,7 @@ Add the patch file to `kustomization.yaml`:
|
||||
<!-- @specifyPatch @test -->
|
||||
```
|
||||
cat <<'EOF' >> $DEMO_HOME/kustomization.yaml
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- persistent-disk.yaml
|
||||
EOF
|
||||
```
|
||||
@@ -188,7 +188,7 @@ Lets break this down:
|
||||
in deployment.yaml
|
||||
|
||||
- Then we added `persistent-disk.yaml` to list of
|
||||
`patches` in `kustomization.yaml`. `kustomize build`
|
||||
`patchesStrategicMerge` in `kustomization.yaml`. `kustomize build`
|
||||
will apply this patch to the deployment resource with
|
||||
the name `mysql` as defined in the patch.
|
||||
|
||||
|
||||
69
examples/remoteBuild.md
Normal file
69
examples/remoteBuild.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# 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 $?
|
||||
```
|
||||
|
||||
Overlays can be remote as well:
|
||||
|
||||
<!-- @remoteOverlayBuild @test -->
|
||||
|
||||
```
|
||||
target=github.com/kubernetes-sigs/kustomize//examples/multibases/dev/?ref=v1.0.6
|
||||
test 1 == \
|
||||
$(kustomize build $target | grep cluster-a-dev-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`
|
||||
@@ -291,7 +291,7 @@ kustomize edit add patch healthcheck_patch.yaml
|
||||
`kustomization.yaml` should have patches field:
|
||||
|
||||
> ```
|
||||
> patches:
|
||||
> patchesStrategicMerge:
|
||||
> - patch.yaml
|
||||
> - memorylimit_patch.yaml
|
||||
> - healthcheck_patch.yaml
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
bases:
|
||||
- ../../base
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- patch.yaml
|
||||
- healthcheck_patch.yaml
|
||||
- memorylimit_patch.yaml
|
||||
|
||||
@@ -53,6 +53,8 @@ bases:
|
||||
- wordpress
|
||||
- mysql
|
||||
namePrefix: demo-
|
||||
patchesStrategicMerge:
|
||||
- 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"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
bases:
|
||||
- wordpress
|
||||
- mysql
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- patch.yaml
|
||||
namePrefix: demo-
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/commands"
|
||||
"sigs.k8s.io/kustomize/pkg/commands"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -26,17 +26,19 @@ 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"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/pkg/configmapandsecret"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/crds"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
interror "sigs.k8s.io/kustomize/pkg/internal/error"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/patch"
|
||||
patchtransformer "sigs.k8s.io/kustomize/pkg/patch/transformer"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
// Application implements the guts of the kustomize 'build' command.
|
||||
@@ -61,7 +63,6 @@ func NewApplication(ldr loader.Loader, fSys fs.FileSystem) (*Application, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Application{kustomization: &m, ldr: ldr, fSys: fSys}, nil
|
||||
}
|
||||
|
||||
@@ -86,20 +87,6 @@ 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()
|
||||
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) {
|
||||
err := transformers.NewNameHashTransformer().Transform(m)
|
||||
@@ -138,7 +125,7 @@ func (a *Application) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "loadResMapFromBasesAndResources"))
|
||||
}
|
||||
err = crds.RegisterCRDs(a.ldr, a.kustomization.CRDs)
|
||||
err = crds.RegisterCRDs(a.ldr, a.kustomization.Crds)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "RegisterCRDs"))
|
||||
}
|
||||
@@ -164,7 +151,8 @@ func (a *Application) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patches, err := resmap.NewResourceSliceFromPatches(a.ldr, a.kustomization.Patches)
|
||||
a.kustomization.PatchesStrategicMerge = patch.Append(a.kustomization.PatchesStrategicMerge, a.kustomization.Patches...)
|
||||
patches, err := resmap.NewResourceSliceFromPatches(a.ldr, a.kustomization.PatchesStrategicMerge)
|
||||
if err != nil {
|
||||
errs.Append(errors.Wrap(err, "NewResourceSliceFromPatches"))
|
||||
}
|
||||
@@ -179,6 +167,11 @@ func (a *Application) loadCustomizedResMap() (resmap.ResMap, error) {
|
||||
return nil, err
|
||||
}
|
||||
r = append(r, t)
|
||||
t, err = patchtransformer.NewPatchJson6902Factory(a.ldr).MakePatchJson6902Transformer(a.kustomization.PatchesJson6902)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r = append(r, t)
|
||||
t, err = transformers.NewImageTagTransformer(a.kustomization.ImageTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -226,6 +219,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...)
|
||||
@@ -292,7 +286,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)
|
||||
@@ -322,6 +316,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 {
|
||||
|
||||
@@ -19,16 +19,17 @@ package app
|
||||
import (
|
||||
"encoding/base64"
|
||||
"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"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resource"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -53,6 +54,21 @@ secretGenerator:
|
||||
DB_USERNAME: "printf admin"
|
||||
DB_PASSWORD: "printf somepw"
|
||||
type: Opaque
|
||||
patchesJson6902:
|
||||
- target:
|
||||
group: apps
|
||||
version: v1
|
||||
kind: Deployment
|
||||
name: dply1
|
||||
path: jsonpatch.json
|
||||
`
|
||||
kustomizationContent2 = `
|
||||
secretGenerator:
|
||||
- name: secret
|
||||
timeoutSeconds: 1
|
||||
commands:
|
||||
USER: "sleep 2"
|
||||
type: Opaque
|
||||
`
|
||||
deploymentContent = `apiVersion: apps/v1
|
||||
metadata:
|
||||
@@ -64,6 +80,9 @@ kind: Namespace
|
||||
metadata:
|
||||
name: ns1
|
||||
`
|
||||
jsonpatchContent = `[
|
||||
{"op": "add", "path": "/spec/replica", "value": "3"}
|
||||
]`
|
||||
)
|
||||
|
||||
func makeLoader1(t *testing.T) loader.Loader {
|
||||
@@ -80,6 +99,10 @@ func makeLoader1(t *testing.T) loader.Loader {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
err = ldr.AddFile("/testpath/jsonpatch.json", []byte(jsonpatchContent))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
return ldr
|
||||
}
|
||||
|
||||
@@ -87,11 +110,10 @@ var deploy = schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deploy
|
||||
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",
|
||||
@@ -106,6 +128,7 @@ func TestResources1(t *testing.T) {
|
||||
},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"replica": "3",
|
||||
"selector": map[string]interface{}{
|
||||
"matchLabels": map[string]interface{}{
|
||||
"app": "nginx",
|
||||
@@ -123,7 +146,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",
|
||||
@@ -143,7 +166,7 @@ func TestResources1(t *testing.T) {
|
||||
"DB_PASSWORD": "somepw",
|
||||
},
|
||||
}).SetBehavior(resource.BehaviorCreate),
|
||||
resource.NewResId(secret, "secret"): resource.NewResourceFromMap(
|
||||
resource.NewResIdWithPrefixNamespace(secret, "secret", "foo-", "ns1"): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
@@ -164,7 +187,7 @@ func TestResources1(t *testing.T) {
|
||||
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
||||
},
|
||||
}).SetBehavior(resource.BehaviorCreate),
|
||||
resource.NewResId(ns, "ns1"): resource.NewResourceFromMap(
|
||||
resource.NewResIdWithPrefixNamespace(ns, "ns1", "foo-", ""): resource.NewResourceFromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Namespace",
|
||||
@@ -197,146 +220,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, fs.MakeFakeFS())
|
||||
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 {
|
||||
ldr := loadertest.NewFakeLoader("/testpath")
|
||||
err := ldr.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContentOverlay))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = ldr.AddFile("/testpath/service.yaml", []byte(serviceContent))
|
||||
func TestSecretTimeout(t *testing.T) {
|
||||
l := loadertest.NewFakeLoader("/testpath")
|
||||
err := l.AddFile("/testpath/"+constants.KustomizationFileName, []byte(kustomizationContent2))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
err = ldr.AddDirectory("/testpath/base")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
err = ldr.AddFile("/testpath/base/"+constants.KustomizationFileName, []byte(kustomizationContentBase))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
err = ldr.AddFile("/testpath/base/deployment.yaml", []byte(deploymentContent))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to setup fake ldr.")
|
||||
}
|
||||
return ldr
|
||||
}
|
||||
|
||||
// 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, fs.MakeFakeFS())
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
type addBaseOptions struct {
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
175
pkg/commands/addmetadata.go
Normal file
175
pkg/commands/addmetadata.go
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
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/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
"sigs.k8s.io/kustomize/pkg/validators"
|
||||
)
|
||||
|
||||
// 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
|
||||
mapValidator validators.MapValidatorFunc
|
||||
kind kindOfAdd
|
||||
}
|
||||
|
||||
// newCmdAddAnnotation adds one or more commonAnnotations to the kustomization file.
|
||||
func newCmdAddAnnotation(fSys fs.FileSystem, v validators.MapValidatorFunc) *cobra.Command {
|
||||
var o addMetadataOptions
|
||||
o.kind = annotation
|
||||
o.mapValidator = v
|
||||
cmd := &cobra.Command{
|
||||
Use: "annotation",
|
||||
Short: "Adds one or more commonAnnotations to " + constants.KustomizationFileName,
|
||||
Example: `
|
||||
add annotation {annotationKey1:annotationValue1},{annotationKey2:annotationValue2}`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return o.runE(args, fSys, o.addAnnotations)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// newCmdAddLabel adds one or more commonLabels to the kustomization file.
|
||||
func newCmdAddLabel(fSys fs.FileSystem, v validators.MapValidatorFunc) *cobra.Command {
|
||||
var o addMetadataOptions
|
||||
o.kind = label
|
||||
o.mapValidator = v
|
||||
cmd := &cobra.Command{
|
||||
Use: "label",
|
||||
Short: "Adds one or more commonLabels to " + constants.KustomizationFileName,
|
||||
Example: `
|
||||
add label {labelKey1:labelValue1},{labelKey2:labelValue2}`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return o.runE(args, fSys, o.addLabels)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) runE(
|
||||
args []string, fSys fs.FileSystem, adder func(*types.Kustomization) error) error {
|
||||
err := o.validateAndParse(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kf, err := newKustomizationFile(constants.KustomizationFileName, fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := kf.read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = adder(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return kf.write(m)
|
||||
}
|
||||
|
||||
// validateAndParse validates `add` commands and parses them into o.metadata
|
||||
func (o *addMetadataOptions) validateAndParse(args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("must specify %s", o.kind)
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return fmt.Errorf("%ss must be comma-separated, with no spaces", o.kind)
|
||||
}
|
||||
m, err := o.convertToMap(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = o.mapValidator(m); err != nil {
|
||||
return err
|
||||
}
|
||||
o.metadata = m
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) convertToMap(arg string) (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
inputs := strings.Split(arg, ",")
|
||||
for _, input := range inputs {
|
||||
kv := strings.Split(input, ":")
|
||||
if len(kv[0]) < 1 {
|
||||
return nil, o.makeError(input, "empty key")
|
||||
}
|
||||
if len(kv) > 2 {
|
||||
return nil, o.makeError(input, "too many colons")
|
||||
}
|
||||
if len(kv) > 1 {
|
||||
result[kv[0]] = kv[1]
|
||||
} else {
|
||||
result[kv[0]] = ""
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) addAnnotations(m *types.Kustomization) error {
|
||||
if m.CommonAnnotations == nil {
|
||||
m.CommonAnnotations = make(map[string]string)
|
||||
}
|
||||
return o.writeToMap(m.CommonAnnotations, annotation)
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) addLabels(m *types.Kustomization) error {
|
||||
if m.CommonLabels == nil {
|
||||
m.CommonLabels = make(map[string]string)
|
||||
}
|
||||
return o.writeToMap(m.CommonLabels, label)
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) writeToMap(m map[string]string, kind kindOfAdd) error {
|
||||
for k, v := range o.metadata {
|
||||
if _, ok := m[k]; ok {
|
||||
return fmt.Errorf("%s %s already in kustomization file", kind, k)
|
||||
}
|
||||
m[k] = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *addMetadataOptions) makeError(input string, message string) error {
|
||||
return fmt.Errorf("invalid %s: %s (%s)", o.kind, input, message)
|
||||
}
|
||||
273
pkg/commands/addmetadata_test.go
Normal file
273
pkg/commands/addmetadata_test.go
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
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"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
"sigs.k8s.io/kustomize/pkg/validators"
|
||||
)
|
||||
|
||||
func makeKustomization(t *testing.T) *types.Kustomization {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
kf, err := newKustomizationFile(constants.KustomizationFileName, fakeFS)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected new error %v", err)
|
||||
}
|
||||
m, err := kf.read()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected read error %v", err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func TestRunAddAnnotation(t *testing.T) {
|
||||
var o addMetadataOptions
|
||||
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
|
||||
|
||||
m := makeKustomization(t)
|
||||
err := o.addAnnotations(m)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
// adding the same test input should not work
|
||||
err = o.addAnnotations(m)
|
||||
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.addAnnotations(m)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
err := cmd.Execute()
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "must specify annotation" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationInvalidFormat(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeSadMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"whatever:whatever"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != validators.SAD {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationManyArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"k1:v1,k2:v2,k3:v3,k4:v5"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationNoKey(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{":nokey"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "invalid annotation: :nokey (empty key)" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationTooManyColons(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"key:v1:v2"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "invalid annotation: key:v1:v2 (too many colons)" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationNoValue(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"no:,value"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddAnnotationMultipleArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddAnnotation(fakeFS, v.Validator)
|
||||
args := []string{"this:annotation", "has:spaces"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "annotations must be comma-separated, with no spaces" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAddLabel(t *testing.T) {
|
||||
var o addMetadataOptions
|
||||
o.metadata = map[string]string{"owls": "cute", "otters": "adorable"}
|
||||
|
||||
m := makeKustomization(t)
|
||||
err := o.addLabels(m)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
// adding the same test input should not work
|
||||
err = o.addLabels(m)
|
||||
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.addLabels(m)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: could not write to kustomization file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelNoArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
err := cmd.Execute()
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "must specify label" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelInvalidFormat(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeSadMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{"exclamation!:point"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != validators.SAD {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelNoKey(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{":nokey"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "invalid label: :nokey (empty key)" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelTooManyColons(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{"key:v1:v2"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "invalid label: key:v1:v2 (too many colons)" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelNoValue(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{"no,value:"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyCall()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLabelMultipleArgs(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
v := validators.MakeHappyMapValidator(t)
|
||||
cmd := newCmdAddLabel(fakeFS, v.Validator)
|
||||
args := []string{"this:input", "has:spaces"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
v.VerifyNoCall()
|
||||
if err == nil {
|
||||
t.Errorf("expected an error")
|
||||
}
|
||||
if err.Error() != "labels must be comma-separated, with no spaces" {
|
||||
t.Errorf("incorrect error: %v", err.Error())
|
||||
}
|
||||
}
|
||||
@@ -18,16 +18,17 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/patch"
|
||||
)
|
||||
|
||||
type addPatchOptions struct {
|
||||
patchFilePath string
|
||||
patchFilePaths []string
|
||||
}
|
||||
|
||||
// newCmdAddPatch adds the name of a file containing a patch to the kustomization file.
|
||||
@@ -56,10 +57,10 @@ func newCmdAddPatch(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,8 +71,12 @@ func (o *addPatchOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// RunAddPatch runs addPatch command (do real work).
|
||||
func (o *addPatchOptions) RunAddPatch(fsys fs.FileSystem) error {
|
||||
if !fsys.Exists(o.patchFilePath) {
|
||||
return errors.New(o.patchFilePath + " doesn't exist")
|
||||
patches, err := globPatterns(fsys, o.patchFilePaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(patches) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
mf, err := newKustomizationFile(constants.KustomizationFileName, fsys)
|
||||
@@ -84,11 +89,13 @@ func (o *addPatchOptions) RunAddPatch(fsys fs.FileSystem) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if stringInSlice(o.patchFilePath, m.Patches) {
|
||||
return fmt.Errorf("patch %s already in kustomization file", o.patchFilePath)
|
||||
for _, p := range patches {
|
||||
if patch.Exist(m.PatchesStrategicMerge, p) || stringInSlice(p, m.Patches) {
|
||||
log.Printf("patch %s already in kustomization file", p)
|
||||
continue
|
||||
}
|
||||
m.PatchesStrategicMerge = patch.Append(m.PatchesStrategicMerge, p)
|
||||
}
|
||||
|
||||
m.Patches = append(m.Patches, o.patchFilePath)
|
||||
|
||||
return mf.write(m)
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,10 +36,11 @@ sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
func TestAddPatchHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(patchFileName, []byte(patchFileContent))
|
||||
fakeFS.WriteFile(patchFileName+"another", []byte(patchFileContent))
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdAddPatch(fakeFS)
|
||||
args := []string{patchFileName}
|
||||
args := []string{patchFileName + "*"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
@@ -51,6 +52,9 @@ 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) {
|
||||
@@ -65,13 +69,10 @@ func TestAddPatchAlreadyThere(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,16 +18,16 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
type addResourceOptions struct {
|
||||
resourceFilePath string
|
||||
resourceFilePaths []string
|
||||
}
|
||||
|
||||
// newCmdAddResource adds the name of a file containing a resource to the kustomization file.
|
||||
@@ -56,10 +56,10 @@ func newCmdAddResource(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,9 +70,14 @@ func (o *addResourceOptions) Complete(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// RunAddResource runs addResource command (do real work).
|
||||
func (o *addResourceOptions) RunAddResource(fsys fs.FileSystem) error {
|
||||
if !fsys.Exists(o.resourceFilePath) {
|
||||
return errors.New(o.resourceFilePath + " does not exist")
|
||||
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 {
|
||||
return err
|
||||
@@ -83,11 +88,13 @@ func (o *addResourceOptions) RunAddResource(fsys fs.FileSystem) error {
|
||||
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,12 +17,11 @@ limitations under the License.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -52,10 +51,11 @@ secretGenerator: []
|
||||
func TestAddResourceHappyPath(t *testing.T) {
|
||||
fakeFS := fs.MakeFakeFS()
|
||||
fakeFS.WriteFile(resourceFileName, []byte(resourceFileContent))
|
||||
fakeFS.WriteFile(resourceFileName+"another", []byte(resourceFileContent))
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdAddResource(fakeFS)
|
||||
args := []string{resourceFileName}
|
||||
args := []string{resourceFileName + "*"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
@@ -67,6 +67,9 @@ 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) {
|
||||
@@ -81,13 +84,10 @@ func TestAddResourceAlreadyThere(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,31 +18,45 @@ package commands
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/app"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/app"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
)
|
||||
|
||||
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:
|
||||
sigs.k8s.io/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 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)
|
||||
@@ -52,6 +66,10 @@ func newCmdBuild(out io.Writer, fs fs.FileSystem) *cobra.Command {
|
||||
return o.RunBuild(out, fs)
|
||||
},
|
||||
}
|
||||
cmd.Flags().StringVarP(
|
||||
&o.outputPath,
|
||||
"output", "o", "",
|
||||
"If specified, write the build output to this path.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -70,17 +88,11 @@ func (o *buildOptions) Validate(args []string) error {
|
||||
|
||||
// RunBuild runs build command.
|
||||
func (o *buildOptions) RunBuild(out io.Writer, fSys fs.FileSystem) error {
|
||||
l := loader.NewLoader(loader.NewFileLoader(fSys))
|
||||
|
||||
absPath, err := filepath.Abs(o.kustomizationPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rootLoader, err := l.New(absPath)
|
||||
rootLoader, err := loader.NewLoader(o.kustomizationPath, "", fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rootLoader.Cleanup()
|
||||
|
||||
application, err := app.NewApplication(rootLoader, fSys)
|
||||
if err != nil {
|
||||
@@ -98,6 +110,10 @@ func (o *buildOptions) RunBuild(out io.Writer, fSys fs.FileSystem) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.outputPath != "" {
|
||||
return fSys.WriteFile(o.outputPath, res)
|
||||
}
|
||||
_, err = out.Write(res)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ import (
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
type buildTestCase struct {
|
||||
|
||||
@@ -18,6 +18,8 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
// cMapFlagsAndArgs encapsulates the options for add configmap commands.
|
||||
@@ -48,3 +50,12 @@ func (a *cMapFlagsAndArgs) 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,7 +17,10 @@ limitations under the License.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func TestDataConfigValidation_NoName(t *testing.T) {
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,15 @@ import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/validators"
|
||||
)
|
||||
|
||||
// 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",
|
||||
@@ -36,14 +37,13 @@ func NewDefaultCommand() *cobra.Command {
|
||||
Long: `
|
||||
kustomize manages declarative configuration of Kubernetes.
|
||||
|
||||
See https://github.com/kubernetes-sigs/kustomize
|
||||
See https://sigs.k8s.io/kustomize
|
||||
`,
|
||||
}
|
||||
|
||||
c.AddCommand(
|
||||
// TODO: Make consistent API for newCmd* functions.
|
||||
newCmdBuild(stdOut, fsys),
|
||||
newCmdDiff(stdOut, stdErr, fsys),
|
||||
newCmdEdit(fsys),
|
||||
newCmdVersion(stdOut),
|
||||
)
|
||||
@@ -96,6 +96,12 @@ func newCmdAdd(fsys fs.FileSystem) *cobra.Command {
|
||||
# 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),
|
||||
}
|
||||
@@ -104,6 +110,8 @@ func newCmdAdd(fsys fs.FileSystem) *cobra.Command {
|
||||
newCmdAddPatch(fsys),
|
||||
newCmdAddConfigMap(fsys),
|
||||
newCmdAddBase(fsys),
|
||||
newCmdAddLabel(fsys, validators.MakeLabelValidator()),
|
||||
newCmdAddAnnotation(fsys, validators.MakeAnnotationValidator()),
|
||||
)
|
||||
return c
|
||||
}
|
||||
@@ -123,6 +131,7 @@ func newCmdSet(fsys fs.FileSystem) *cobra.Command {
|
||||
|
||||
c.AddCommand(
|
||||
newCmdSetNamePrefix(fsys),
|
||||
newCmdSetNamespace(fsys),
|
||||
newCmdSetImageTag(fsys),
|
||||
)
|
||||
return c
|
||||
|
||||
@@ -21,11 +21,11 @@ import (
|
||||
|
||||
"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"
|
||||
"sigs.k8s.io/kustomize/pkg/configmapandsecret"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
func newCmdAddConfigMap(fSys fs.FileSystem) *cobra.Command {
|
||||
@@ -45,7 +45,12 @@ func newCmdAddConfigMap(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 := flagsAndArgs.Validate(args)
|
||||
err := flagsAndArgs.ExpandFileSource(fSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = flagsAndArgs.Validate(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -65,7 +70,7 @@ func newCmdAddConfigMap(fSys fs.FileSystem) *cobra.Command {
|
||||
err = addConfigMap(
|
||||
kustomization, flagsAndArgs,
|
||||
configmapandsecret.NewConfigMapFactory(
|
||||
fSys, loader.NewLoader(loader.NewFileLoader(fSys))))
|
||||
fSys, loader.NewFileLoader(fSys)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ package commands
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
func TestNewAddConfigMapIsNotNil(t *testing.T) {
|
||||
|
||||
@@ -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(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return o.RunDiff(out, errOut, fs)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Validate validates diff command.
|
||||
func (o *diffOptions) Validate(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, fSys fs.FileSystem) error {
|
||||
|
||||
l := loader.NewLoader(loader.NewFileLoader(fSys))
|
||||
|
||||
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, fSys)
|
||||
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,131 +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"
|
||||
|
||||
tempDir := regexp.QuoteMeta(filepath.Clean(os.TempDir()))
|
||||
noopDir, _ := regexp.Compile(tempDir + `/noop-[0-9]*/`)
|
||||
transformedDir, _ := regexp.Compile(tempDir + `/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}`)
|
||||
|
||||
fSys := 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, fSys,
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,22 +17,64 @@ limitations under the License.
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
interror "github.com/kubernetes-sigs/kustomize/pkg/internal/error"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// These field names are the exact kustomization fields
|
||||
kustomizationFields = []string{
|
||||
"APIVersion",
|
||||
"Kind",
|
||||
"Resources",
|
||||
"Bases",
|
||||
"NamePrefix",
|
||||
"Namespace",
|
||||
"Crds",
|
||||
"CommonLabels",
|
||||
"CommonAnnotations",
|
||||
"Patches",
|
||||
"PatchesStrategicMerge",
|
||||
"PatchesJson6902",
|
||||
"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 (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
|
||||
path string
|
||||
fsys fs.FileSystem
|
||||
originalFields []*commentedField
|
||||
}
|
||||
|
||||
func newKustomizationFile(mPath string, fsys fs.FileSystem) (*kustomizationFile, error) { // nolint
|
||||
@@ -46,34 +88,33 @@ func newKustomizationFile(mPath string, fsys fs.FileSystem) (*kustomizationFile,
|
||||
|
||||
func (mf *kustomizationFile) validate() error {
|
||||
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
|
||||
return fmt.Errorf("Missing kustomization file '%s'.\n", mf.path)
|
||||
}
|
||||
if mf.fsys.IsDir(mf.path) {
|
||||
mf.path = path.Join(mf.path, constants.KustomizationFileName)
|
||||
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
|
||||
return fmt.Errorf("Missing kustomization file '%s'.\n", mf.path)
|
||||
}
|
||||
} else {
|
||||
if !strings.HasSuffix(mf.path, constants.KustomizationFileName) {
|
||||
errorMsg := fmt.Sprintf("Kustomization file path (%s) should have %s suffix\n",
|
||||
return fmt.Errorf("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,12 +125,11 @@ func (mf *kustomizationFile) write(kustomization *types.Kustomization) error {
|
||||
if kustomization == 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 {
|
||||
@@ -100,3 +140,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)
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
func TestWriteAndRead(t *testing.T) {
|
||||
@@ -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
|
||||
patchesStrategicMerge:
|
||||
- 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
|
||||
|
||||
patchesStrategicMerge:
|
||||
- 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
|
||||
|
||||
patchesStrategicMerge:
|
||||
- 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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
type setNamePrefixOptions struct {
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -18,30 +18,33 @@ package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
"sort"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
type setImageTagOptions struct {
|
||||
imageTagMap map[string]string
|
||||
imageTagMap map[string]types.ImageTag
|
||||
}
|
||||
|
||||
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",
|
||||
Short: "Sets images and their new tags or digests in the kustomization file",
|
||||
Example: `
|
||||
The command
|
||||
set imagetag nginx:1.8.0 my-app:latest
|
||||
set imagetag nginx:1.8.0 my-app:latest alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||
will add
|
||||
|
||||
imageTags:
|
||||
@@ -49,9 +52,11 @@ imageTags:
|
||||
newTag: 1.8.0
|
||||
- name: my-app
|
||||
newTag: latest
|
||||
- name: alpine
|
||||
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||
|
||||
to the kustomization file if it doesn't exist,
|
||||
and overwrite the previous newTag if the image name exists.
|
||||
and overwrite the previous ones if the image tag exists.
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := o.Validate(args)
|
||||
@@ -67,15 +72,28 @@ and overwrite the previous newTag if the image name exists.
|
||||
// Validate validates setImageTag command.
|
||||
func (o *setImageTagOptions) Validate(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("No image and newTag specified.")
|
||||
return errors.New("no image specified")
|
||||
}
|
||||
o.imageTagMap = make(map[string]string)
|
||||
|
||||
o.imageTagMap = make(map[string]types.ImageTag)
|
||||
|
||||
for _, arg := range args {
|
||||
imagetag := strings.Split(arg, ":")
|
||||
if len(imagetag) != 2 {
|
||||
return errors.New("Invalid format of imagetag, must specify it as <image>:<newtag>")
|
||||
if s := strings.Split(arg, "@"); len(s) > 1 {
|
||||
o.imageTagMap[s[0]] = types.ImageTag{
|
||||
Name: s[0],
|
||||
Digest: s[1],
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
s := pattern.FindStringSubmatch(arg)
|
||||
if len(s) != 3 {
|
||||
return errors.New("invalid format of imagetag, must specify it as <image>:<newtag> or <image>@<digest>")
|
||||
}
|
||||
o.imageTagMap[s[1]] = types.ImageTag{
|
||||
Name: s[1],
|
||||
NewTag: s[2],
|
||||
}
|
||||
o.imageTagMap[imagetag[0]] = imagetag[1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -90,16 +108,18 @@ func (o *setImageTagOptions) RunSetImageTags(fsys fs.FileSystem) error {
|
||||
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
|
||||
if _, ok := o.imageTagMap[it.Name]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
o.imageTagMap[it.Name] = it
|
||||
}
|
||||
|
||||
var imageTags []types.ImageTag
|
||||
for key, value := range imageTagMap {
|
||||
imageTags = append(imageTags, types.ImageTag{Name: key, NewTag: value})
|
||||
for _, v := range o.imageTagMap {
|
||||
imageTags = append(imageTags, v)
|
||||
}
|
||||
sort.Slice(imageTags, func(i, j int) bool {
|
||||
return imageTags[i].Name < imageTags[j].Name
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/constants"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
func TestSetImageTagsHappyPath(t *testing.T) {
|
||||
@@ -29,7 +29,8 @@ func TestSetImageTagsHappyPath(t *testing.T) {
|
||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||
|
||||
cmd := newCmdSetImageTag(fakeFS)
|
||||
args := []string{"image1:tag1", "image2:tag2"}
|
||||
args := []string{"image1:tag1", "image2:tag2", "localhost:5000/operator:1.0.0",
|
||||
"foo.bar.baz:5000/one/two@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"}
|
||||
err := cmd.RunE(cmd, args)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected cmd error: %v", err)
|
||||
@@ -40,10 +41,14 @@ func TestSetImageTagsHappyPath(t *testing.T) {
|
||||
}
|
||||
expected := []byte(`
|
||||
imageTags:
|
||||
- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||
name: foo.bar.baz:5000/one/two
|
||||
- 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")
|
||||
@@ -91,7 +96,7 @@ func TestSetImageTagsNoArgs(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Errorf("expected error: %v", err)
|
||||
}
|
||||
if err.Error() != "No image and newTag specified." {
|
||||
if err.Error() != "no image 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/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
)
|
||||
|
||||
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"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/constants"
|
||||
"sigs.k8s.io/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())
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,6 @@ commonLabels:
|
||||
commonAnnotations:
|
||||
note: This is a test annotation
|
||||
resources:
|
||||
- resources/*.yaml
|
||||
- resources/deployment.yaml
|
||||
- resources/networkpolicy.yaml
|
||||
- resources/service.yaml
|
||||
|
||||
4
pkg/commands/testdata/testcase-multibases-conflict/base/kustomization.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-conflict/base/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
resources:
|
||||
- serviceaccount.yaml
|
||||
- rolebinding.yaml
|
||||
namePrefix: base-
|
||||
11
pkg/commands/testdata/testcase-multibases-conflict/base/rolebinding.yaml
vendored
Normal file
11
pkg/commands/testdata/testcase-multibases-conflict/base/rolebinding.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: serviceaccount
|
||||
4
pkg/commands/testdata/testcase-multibases-conflict/base/serviceaccount.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-conflict/base/serviceaccount.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: serviceaccount
|
||||
3
pkg/commands/testdata/testcase-multibases-conflict/combined/kustomization.yaml
vendored
Normal file
3
pkg/commands/testdata/testcase-multibases-conflict/combined/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
bases:
|
||||
- ../overlays/a
|
||||
- ../overlays/b
|
||||
7
pkg/commands/testdata/testcase-multibases-conflict/overlays/a/kustomization.yaml
vendored
Normal file
7
pkg/commands/testdata/testcase-multibases-conflict/overlays/a/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
bases:
|
||||
- ../../base/
|
||||
|
||||
namePrefix: a-
|
||||
|
||||
resources:
|
||||
- serviceaccount.yaml
|
||||
4
pkg/commands/testdata/testcase-multibases-conflict/overlays/a/serviceaccount.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-conflict/overlays/a/serviceaccount.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: serviceaccount
|
||||
4
pkg/commands/testdata/testcase-multibases-conflict/overlays/b/kustomization.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-conflict/overlays/b/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
bases:
|
||||
- ../../base/
|
||||
|
||||
namePrefix: b-
|
||||
4
pkg/commands/testdata/testcase-multibases-conflict/test.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-conflict/test.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
description: multibases with name reference
|
||||
args: []
|
||||
filename: testdata/testcase-multibases-conflict/combined
|
||||
expectedError: detected conflicts when resolving name references serviceaccount
|
||||
4
pkg/commands/testdata/testcase-multibases-nonconflict/base/kustomization.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-nonconflict/base/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
resources:
|
||||
- serviceaccount.yaml
|
||||
- rolebinding.yaml
|
||||
namePrefix: base-
|
||||
11
pkg/commands/testdata/testcase-multibases-nonconflict/base/rolebinding.yaml
vendored
Normal file
11
pkg/commands/testdata/testcase-multibases-nonconflict/base/rolebinding.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: serviceaccount
|
||||
4
pkg/commands/testdata/testcase-multibases-nonconflict/base/serviceaccount.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-nonconflict/base/serviceaccount.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: serviceaccount
|
||||
3
pkg/commands/testdata/testcase-multibases-nonconflict/combined/kustomization.yaml
vendored
Normal file
3
pkg/commands/testdata/testcase-multibases-nonconflict/combined/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
bases:
|
||||
- ../overlays/a
|
||||
- ../overlays/b
|
||||
33
pkg/commands/testdata/testcase-multibases-nonconflict/expected.yaml
vendored
Normal file
33
pkg/commands/testdata/testcase-multibases-nonconflict/expected.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: a-base-serviceaccount
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: b-base-serviceaccount
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: a-base-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: a-base-serviceaccount
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: b-base-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: b-base-serviceaccount
|
||||
4
pkg/commands/testdata/testcase-multibases-nonconflict/overlays/a/kustomization.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-nonconflict/overlays/a/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
bases:
|
||||
- ../../base/
|
||||
|
||||
namePrefix: a-
|
||||
4
pkg/commands/testdata/testcase-multibases-nonconflict/overlays/b/kustomization.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-nonconflict/overlays/b/kustomization.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
bases:
|
||||
- ../../base/
|
||||
|
||||
namePrefix: b-
|
||||
4
pkg/commands/testdata/testcase-multibases-nonconflict/test.yaml
vendored
Normal file
4
pkg/commands/testdata/testcase-multibases-nonconflict/test.yaml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
description: multibases with name reference
|
||||
args: []
|
||||
filename: testdata/testcase-multibases-nonconflict/combined
|
||||
expectedStdout: testdata/testcase-multibases-nonconflict/expected.yaml
|
||||
@@ -1,7 +1,7 @@
|
||||
namePrefix: staging-
|
||||
commonLabels:
|
||||
env: staging
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- deployment-patch2.yaml
|
||||
- deployment-patch1.yaml
|
||||
bases:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namePrefix: staging-
|
||||
commonLabels:
|
||||
env: staging
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- patches/deployment-patch1.yaml
|
||||
- patches/deployment-patch2.yaml
|
||||
bases:
|
||||
|
||||
@@ -2,7 +2,7 @@ namePrefix: staging-
|
||||
commonLabels:
|
||||
env: staging
|
||||
team: override-foo
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- deployment.yaml
|
||||
bases:
|
||||
- ../package/
|
||||
|
||||
@@ -78,16 +78,10 @@ metadata:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
prometheus.io/path: _status/vars
|
||||
prometheus.io/port: "8080"
|
||||
prometheus.io/scrape: "true"
|
||||
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb
|
||||
name: dev-base-cockroachdb-public
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- name: grpc
|
||||
port: 26257
|
||||
@@ -101,10 +95,16 @@ spec:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
annotations:
|
||||
prometheus.io/path: _status/vars
|
||||
prometheus.io/port: "8080"
|
||||
prometheus.io/scrape: "true"
|
||||
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
|
||||
labels:
|
||||
app: cockroachdb
|
||||
name: dev-base-cockroachdb-public
|
||||
name: dev-base-cockroachdb
|
||||
spec:
|
||||
clusterIP: None
|
||||
ports:
|
||||
- name: grpc
|
||||
port: 26257
|
||||
|
||||
39
pkg/commands/util.go
Normal file
39
pkg/commands/util.go
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
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 (
|
||||
"log"
|
||||
|
||||
"sigs.k8s.io/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
|
||||
}
|
||||
@@ -23,16 +23,16 @@ import (
|
||||
"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"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/hash"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
// ConfigMapFactory makes ConfigMaps.
|
||||
@@ -212,6 +212,5 @@ func parseLiteralSource(source string) (keyName, value string, err error) {
|
||||
if len(items) != 2 {
|
||||
return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
|
||||
}
|
||||
|
||||
return items[0], items[1], nil
|
||||
return items[0], strings.Trim(items[1], "\"'"), nil
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ import (
|
||||
"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"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
func makeEnvConfigMap(name string) *corev1.ConfigMap {
|
||||
@@ -90,6 +90,8 @@ func makeLiteralConfigMap(name string) *corev1.ConfigMap {
|
||||
Data: map[string]string{
|
||||
"a": "x",
|
||||
"b": "y",
|
||||
"c": "Hello World",
|
||||
"d": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -127,7 +129,7 @@ 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"),
|
||||
@@ -136,8 +138,7 @@ func TestConstructConfigMap(t *testing.T) {
|
||||
|
||||
// TODO: all tests should use a FakeFs
|
||||
fSys := fs.MakeRealFS()
|
||||
f := NewConfigMapFactory(fSys,
|
||||
loader.NewLoader(loader.NewFileLoader(fSys)))
|
||||
f := NewConfigMapFactory(fSys, loader.NewFileLoader(fSys))
|
||||
for _, tc := range testCases {
|
||||
cm, err := f.MakeConfigMap(&tc.input)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,15 +1,38 @@
|
||||
/*
|
||||
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"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultCommandTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
// SecretFactory makes Secrets.
|
||||
@@ -23,8 +46,7 @@ func NewSecretFactory(fSys fs.FileSystem, wd string) *SecretFactory {
|
||||
return &SecretFactory{fSys: fSys, wd: wd}
|
||||
}
|
||||
|
||||
// MakeSecret returns a new secret.
|
||||
func (f *SecretFactory) MakeSecret(args types.SecretArgs) (*corev1.Secret, error) {
|
||||
func (f *SecretFactory) makeFreshSecret(args *types.SecretArgs) *corev1.Secret {
|
||||
s := &corev1.Secret{}
|
||||
s.APIVersion = "v1"
|
||||
s.Kind = "Secret"
|
||||
@@ -34,25 +56,86 @@ func (f *SecretFactory) MakeSecret(args types.SecretArgs) (*corev1.Secret, error
|
||||
s.Type = corev1.SecretTypeOpaque
|
||||
}
|
||||
s.Data = map[string][]byte{}
|
||||
for k, v := range args.Commands {
|
||||
out, err := f.createSecretKey(v)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "createSecretKey")
|
||||
}
|
||||
s.Data[k] = out
|
||||
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) ([]byte, error) {
|
||||
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(), 5*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "sh", "-c", command)
|
||||
cmd.Dir = f.wd
|
||||
|
||||
@@ -23,10 +23,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||
)
|
||||
|
||||
type pathConfigs struct {
|
||||
|
||||
@@ -21,10 +21,10 @@ import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/internal/loadertest"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/loader"
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/transformers"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/kustomize/pkg/internal/loadertest"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package diff
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// directory represents a new temp directory and lets one create files in it.
|
||||
type directory struct {
|
||||
n string
|
||||
}
|
||||
|
||||
// newDirectory makes a directory instance holding a new temp directory on disk.
|
||||
// The directory name is the given prefix following by a random string.
|
||||
func newDirectory(prefix string) (*directory, error) {
|
||||
name, err := ioutil.TempDir("", prefix+"-")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &directory{n: name}, nil
|
||||
}
|
||||
|
||||
// newFile creates a new file in the directory.
|
||||
func (d *directory) newFile(name string) (*os.File, error) {
|
||||
return os.OpenFile(filepath.Join(d.n, name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
|
||||
}
|
||||
|
||||
// delete removes the directory recursively.
|
||||
func (d *directory) delete() error {
|
||||
return os.RemoveAll(d.n)
|
||||
}
|
||||
|
||||
// name is the name of the directory.
|
||||
func (d *directory) name() string {
|
||||
return d.n
|
||||
}
|
||||
@@ -1,58 +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 diff
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/exec"
|
||||
)
|
||||
|
||||
// program wraps the system diff program.
|
||||
// If specified, the value of KUBERNETES_EXTERNAL_DIFF environment variable
|
||||
// will be used instead of simply `diff(1)`.
|
||||
type program struct {
|
||||
stdout io.Writer
|
||||
stderr io.Writer
|
||||
}
|
||||
|
||||
func newProgram(out, errOut io.Writer) *program {
|
||||
return &program{
|
||||
stdout: out,
|
||||
stderr: errOut,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *program) makeCommand(args ...string) exec.Cmd {
|
||||
diff := "diff"
|
||||
if envDiff := os.Getenv("KUBERNETES_EXTERNAL_DIFF"); envDiff != "" {
|
||||
diff = envDiff
|
||||
} else {
|
||||
args = append([]string{"-u", "-N"}, args...)
|
||||
}
|
||||
cmd := exec.New().Command(diff, args...)
|
||||
cmd.SetStdout(d.stdout)
|
||||
cmd.SetStderr(d.stderr)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// run runs the detected diff program. `from` and `to` are the directory to diff.
|
||||
func (d *program) run(from, to string) error {
|
||||
d.makeCommand(from, to).Run() // Ignore diff return code
|
||||
return nil
|
||||
}
|
||||
@@ -1,83 +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 diff runs system `diff` to compare resource collections.
|
||||
package diff
|
||||
|
||||
import (
|
||||
"github.com/ghodss/yaml"
|
||||
|
||||
"io"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||
)
|
||||
|
||||
// RunDiff runs system diff program to compare two Maps.
|
||||
func RunDiff(raw, transformed resmap.ResMap, out, errOut io.Writer) (err error) {
|
||||
transformedDir, err := writeYamlToNewDir(transformed, "transformed")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = transformedDir.delete()
|
||||
}()
|
||||
|
||||
noopDir, err := writeYamlToNewDir(raw, "noop")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
err = noopDir.delete()
|
||||
}()
|
||||
|
||||
return newProgram(out, errOut).run(noopDir.name(), transformedDir.name())
|
||||
}
|
||||
|
||||
// writeYamlToNewDir writes each obj in ResMap to a file in a new directory.
|
||||
// The directory's name will begin with the given prefix.
|
||||
// Each file is named with GroupVersionKindName.
|
||||
func writeYamlToNewDir(in resmap.ResMap, prefix string) (*directory, error) {
|
||||
dir, err := newDirectory(prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for id, obj := range in {
|
||||
f, err := dir.newFile(id.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = write(obj, f)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// Write the object as YAML.
|
||||
func write(obj interface{}, w io.Writer) error {
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
data, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
return err
|
||||
}
|
||||
@@ -7,7 +7,7 @@ commonAnnotations:
|
||||
note: This is a test annotation
|
||||
bases:
|
||||
- ../../package/
|
||||
patches:
|
||||
patchesStrategicMerge:
|
||||
- deployment/deployment.yaml
|
||||
configMapGenerator:
|
||||
- name: app-env
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/kubernetes-sigs/kustomize/pkg/exec"
|
||||
"sigs.k8s.io/kustomize/pkg/exec"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
|
||||
@@ -18,6 +18,8 @@ package fs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
)
|
||||
|
||||
var _ FileSystem = &FakeFS{}
|
||||
@@ -60,6 +62,18 @@ func (fs *FakeFS) Exists(name string) bool {
|
||||
return found
|
||||
}
|
||||
|
||||
// Glob returns the list of matching files
|
||||
func (fs *FakeFS) Glob(pattern string) ([]string, error) {
|
||||
var result []string
|
||||
for p := range fs.m {
|
||||
if fs.pathMatch(p, pattern) {
|
||||
result = append(result, p)
|
||||
}
|
||||
}
|
||||
sort.Strings(result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// IsDir returns true if the file exists and is a directory.
|
||||
func (fs *FakeFS) IsDir(name string) bool {
|
||||
f, found := fs.m[name]
|
||||
@@ -77,18 +91,6 @@ func (fs *FakeFS) ReadFile(name string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("cannot read file %q", name)
|
||||
}
|
||||
|
||||
// ReadFiles looks through all files in the fake filesystem
|
||||
// and find the matching files and then read content from all of them
|
||||
func (fs *FakeFS) ReadFiles(name string) (map[string][]byte, error) {
|
||||
result := map[string][]byte{}
|
||||
for p, f := range fs.m {
|
||||
if fs.pathMatch(p, name) {
|
||||
result[p] = f.content
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// WriteFile always succeeds and does nothing.
|
||||
func (fs *FakeFS) WriteFile(name string, c []byte) error {
|
||||
ff := &FakeFile{}
|
||||
@@ -98,8 +100,6 @@ func (fs *FakeFS) WriteFile(name string, c []byte) error {
|
||||
}
|
||||
|
||||
func (fs *FakeFS) pathMatch(path, pattern string) bool {
|
||||
if path == pattern {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
match, _ := filepath.Match(pattern, path)
|
||||
return match
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user