mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-29 09:40:49 +00:00
Compare commits
1386 Commits
v1.0.11
...
release-ap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bae6418ca2 | ||
|
|
1db0248748 | ||
|
|
387afef19b | ||
|
|
9e47e585d4 | ||
|
|
1f8696865f | ||
|
|
87b6a4d6bc | ||
|
|
5fe0b9e1b2 | ||
|
|
9a25cb96b7 | ||
|
|
0f81cf52e7 | ||
|
|
eb75c039e5 | ||
|
|
0f0c6618a0 | ||
|
|
97f2f33460 | ||
|
|
382981fb43 | ||
|
|
519dacc10f | ||
|
|
02c0c2692f | ||
|
|
6df4efd145 | ||
|
|
4dab6268da | ||
|
|
4150026ec7 | ||
|
|
10cd82ce31 | ||
|
|
d0ce4fcf15 | ||
|
|
d54ff23560 | ||
|
|
8e4609a850 | ||
|
|
eabb476461 | ||
|
|
74255f6bad | ||
|
|
3dfe62fe55 | ||
|
|
ad9b869ddb | ||
|
|
5c4c19bf19 | ||
|
|
1f86a0ca5d | ||
|
|
db630a9d07 | ||
|
|
388dd13a5f | ||
|
|
67c9d469c4 | ||
|
|
28a55bbd9c | ||
|
|
bdacb941ab | ||
|
|
d87ad523de | ||
|
|
ec36993d42 | ||
|
|
6b5aebac22 | ||
|
|
883714e2e5 | ||
|
|
8b0f4bf714 | ||
|
|
e83b97ea1f | ||
|
|
48dfcf5a3e | ||
|
|
cb4498594b | ||
|
|
b0bd4b5410 | ||
|
|
dbf8a0fde4 | ||
|
|
3db1111f8e | ||
|
|
e482ffa3f9 | ||
|
|
2b1749778f | ||
|
|
6af51a1bfe | ||
|
|
e0b46acf2f | ||
|
|
2e33a69388 | ||
|
|
018698ec85 | ||
|
|
0029a8ce32 | ||
|
|
04d5494246 | ||
|
|
41a8bd208d | ||
|
|
6bb470dbd0 | ||
|
|
9fa0391ce9 | ||
|
|
bda22d08cc | ||
|
|
09faaa1b2c | ||
|
|
9a65634df6 | ||
|
|
300383959d | ||
|
|
ab17d8dd74 | ||
|
|
c5ba2ced3b | ||
|
|
4103580834 | ||
|
|
d36e3f015d | ||
|
|
975e071394 | ||
|
|
6542af600d | ||
|
|
9e6e928725 | ||
|
|
ad24ba2234 | ||
|
|
c7fd1dee8b | ||
|
|
606eb8a74c | ||
|
|
2340c98f6a | ||
|
|
a7607c20e3 | ||
|
|
0b5cf74945 | ||
|
|
0687699d27 | ||
|
|
357126fc4e | ||
|
|
0aeb53a2b3 | ||
|
|
efd7c8e3f7 | ||
|
|
152ee44b92 | ||
|
|
588297f1f9 | ||
|
|
6b81ae9a93 | ||
|
|
077c7b2d20 | ||
|
|
1a4330a7cb | ||
|
|
d08690a6aa | ||
|
|
ba925e833d | ||
|
|
4c15c42447 | ||
|
|
690e01c2ba | ||
|
|
133d8bff5e | ||
|
|
079c3307c1 | ||
|
|
e0070f7ec5 | ||
|
|
a45eca7e22 | ||
|
|
79c5f8a977 | ||
|
|
69d1699963 | ||
|
|
b6b8f4396f | ||
|
|
9ca5284e4f | ||
|
|
335292fe8a | ||
|
|
cf9edbfb3c | ||
|
|
364a2342df | ||
|
|
3301913178 | ||
|
|
ac3c53557e | ||
|
|
8bf98dd255 | ||
|
|
f2368201d9 | ||
|
|
f44574cf43 | ||
|
|
3054d69dd9 | ||
|
|
739c2a5bac | ||
|
|
a780bd6465 | ||
|
|
4716cb026f | ||
|
|
a186144a78 | ||
|
|
2ea4762d0f | ||
|
|
eb268fa722 | ||
|
|
3b3a1309e4 | ||
|
|
72db6dde9e | ||
|
|
6d30bc5c35 | ||
|
|
773629e544 | ||
|
|
cd50bf4e1e | ||
|
|
ff59e9b52f | ||
|
|
9a954d4f0b | ||
|
|
5c63c20390 | ||
|
|
a84f8d65db | ||
|
|
6e2335ec3d | ||
|
|
4cf6630fc0 | ||
|
|
d239d52217 | ||
|
|
c260105212 | ||
|
|
44cdf98a47 | ||
|
|
92390eabe4 | ||
|
|
7b8fa51ec5 | ||
|
|
af8e17a1ed | ||
|
|
e2eeb90639 | ||
|
|
1704977a4b | ||
|
|
7050c6a7b6 | ||
|
|
02f9b98b5a | ||
|
|
ce7ebe3299 | ||
|
|
3928ada0e5 | ||
|
|
0a8faced8f | ||
|
|
3c06debf98 | ||
|
|
fcee91eafd | ||
|
|
b0b3a705f4 | ||
|
|
67c8fbcc3c | ||
|
|
9b50b78ec8 | ||
|
|
943a1b0195 | ||
|
|
f77143cd34 | ||
|
|
b7d2ba2376 | ||
|
|
c28a0eb83d | ||
|
|
f7e5b5138b | ||
|
|
61149cbf21 | ||
|
|
310d516030 | ||
|
|
a9e3fe155b | ||
|
|
24837bad40 | ||
|
|
9686cc9861 | ||
|
|
a4784ee5ec | ||
|
|
3063560e77 | ||
|
|
bff0604bee | ||
|
|
9f8faa7d7e | ||
|
|
335077eade | ||
|
|
491baa74cb | ||
|
|
2f2d078669 | ||
|
|
b7bcb90057 | ||
|
|
35dc15b16b | ||
|
|
83f70877c8 | ||
|
|
98cd31b820 | ||
|
|
5416ae7365 | ||
|
|
644f2ddcdc | ||
|
|
46524d3b6a | ||
|
|
4f014d0262 | ||
|
|
0cf2057fc5 | ||
|
|
3f08e1546c | ||
|
|
10619fb0f7 | ||
|
|
c88e8cc057 | ||
|
|
327a3f5300 | ||
|
|
fddde81f9c | ||
|
|
22d07ed37d | ||
|
|
dee1c425da | ||
|
|
951d15bf17 | ||
|
|
0f82d2932c | ||
|
|
e2d7a06e9f | ||
|
|
286b9c1aed | ||
|
|
f54d4a5837 | ||
|
|
d9031fb2c9 | ||
|
|
3af5a8afea | ||
|
|
e2fd33c54a | ||
|
|
c90e0a4080 | ||
|
|
5de000ee3d | ||
|
|
c28b82510c | ||
|
|
fda3ba8af9 | ||
|
|
fd1356e5d8 | ||
|
|
a62f1364fe | ||
|
|
d1240bcc63 | ||
|
|
1c24fe7d16 | ||
|
|
e5c8b5ec8f | ||
|
|
180429774a | ||
|
|
586bba0b31 | ||
|
|
2ce138ab3a | ||
|
|
5e99ad000e | ||
|
|
0f0e740c21 | ||
|
|
33600189bc | ||
|
|
07d2500ee3 | ||
|
|
de6eb14867 | ||
|
|
85b71a31e3 | ||
|
|
16e7638220 | ||
|
|
04c23b2085 | ||
|
|
24db94dd0d | ||
|
|
5f862ba17c | ||
|
|
18ba3ee91b | ||
|
|
4e9d42fae7 | ||
|
|
52e57dab7f | ||
|
|
ba464a5e11 | ||
|
|
2734085fb0 | ||
|
|
d21ff7cfe6 | ||
|
|
3a15f450a9 | ||
|
|
bb77e7491a | ||
|
|
aa82240b4c | ||
|
|
cac7b46ebd | ||
|
|
367d0e042c | ||
|
|
d851305c33 | ||
|
|
0c52bd71ba | ||
|
|
41a008e9a3 | ||
|
|
2fadb4dd59 | ||
|
|
a88ee3f93c | ||
|
|
237848a80b | ||
|
|
9e3b837093 | ||
|
|
c4eca908ac | ||
|
|
72d9b4cbca | ||
|
|
19d94110b1 | ||
|
|
1756765dbc | ||
|
|
b306f8511c | ||
|
|
9778f867b5 | ||
|
|
a69ebf2b11 | ||
|
|
cf9c81f908 | ||
|
|
b95164b9a8 | ||
|
|
0551338958 | ||
|
|
40b7ad23ea | ||
|
|
ce66ceeed6 | ||
|
|
4e45af6265 | ||
|
|
07a9454215 | ||
|
|
9f5a936236 | ||
|
|
e6770e5f1e | ||
|
|
705b4ab212 | ||
|
|
2cb964ab8e | ||
|
|
949b10bf93 | ||
|
|
cc4341c546 | ||
|
|
d0caea0ce1 | ||
|
|
f2ac5a2d0d | ||
|
|
78d14d0d75 | ||
|
|
d5034af5ca | ||
|
|
40ed9e6a44 | ||
|
|
c1d20546ec | ||
|
|
3cf6b8ec4d | ||
|
|
3aee7a9081 | ||
|
|
abefa2b155 | ||
|
|
5d800f0b0a | ||
|
|
4eb2d5bcc2 | ||
|
|
988af1ff61 | ||
|
|
1617183ea4 | ||
|
|
ee72746481 | ||
|
|
c9e7dc3bfa | ||
|
|
07e0e46ac7 | ||
|
|
404d2d631a | ||
|
|
baa0296a12 | ||
|
|
0f665ac153 | ||
|
|
14b0a65091 | ||
|
|
2d58f8b81c | ||
|
|
9a43ca53cc | ||
|
|
5372fc6f6c | ||
|
|
86bc344057 | ||
|
|
a014f7d414 | ||
|
|
9a94bcb854 | ||
|
|
07634ef098 | ||
|
|
995f88d60c | ||
|
|
5caba59073 | ||
|
|
334a64676f | ||
|
|
08963ba503 | ||
|
|
326fb689be | ||
|
|
970ce67c34 | ||
|
|
98d1893057 | ||
|
|
d89b448c74 | ||
|
|
17bf9d325b | ||
|
|
a99aff1d1c | ||
|
|
a694ac7b63 | ||
|
|
b5b11ef6e9 | ||
|
|
fa1af6f51e | ||
|
|
9288dec02a | ||
|
|
1a45dd0b4f | ||
|
|
592c5acf5a | ||
|
|
ac9424fa3e | ||
|
|
79fbe7c4cc | ||
|
|
f69d526fa3 | ||
|
|
07a95a60f6 | ||
|
|
032b385711 | ||
|
|
810629596a | ||
|
|
b82a8fd316 | ||
|
|
2d0c22d6a4 | ||
|
|
aa342deff7 | ||
|
|
10786ec0a7 | ||
|
|
7c7056877b | ||
|
|
e8933d9789 | ||
|
|
9d7b65446f | ||
|
|
7a0946a922 | ||
|
|
def4f04572 | ||
|
|
2f2408f1cd | ||
|
|
3b9bcc48a0 | ||
|
|
d0429ff43b | ||
|
|
33deefc307 | ||
|
|
9b3de82b45 | ||
|
|
d217074fbf | ||
|
|
1d90ba7c7b | ||
|
|
eeeb4c36a1 | ||
|
|
b1faa989f4 | ||
|
|
d8250c9ee2 | ||
|
|
c950046659 | ||
|
|
0c32691e9e | ||
|
|
88b1d62740 | ||
|
|
aec8206695 | ||
|
|
20c2b53a46 | ||
|
|
274b5c3b4e | ||
|
|
b1fdaa2311 | ||
|
|
a3103f1e62 | ||
|
|
74ed0b30e5 | ||
|
|
b5d5e70bdc | ||
|
|
2e82985380 | ||
|
|
55941f5769 | ||
|
|
32be1cf4c2 | ||
|
|
2050afdeb4 | ||
|
|
7e71009283 | ||
|
|
72d26c6ad5 | ||
|
|
e011f3be4f | ||
|
|
f725bfc165 | ||
|
|
94ac55f17b | ||
|
|
dd5b3c1e2e | ||
|
|
e898c5221b | ||
|
|
1237ae43b4 | ||
|
|
281f932814 | ||
|
|
cd0187e948 | ||
|
|
9516880042 | ||
|
|
4cb883863f | ||
|
|
9e226001e3 | ||
|
|
9ee35c9afb | ||
|
|
e455acc14b | ||
|
|
6a3c2b2893 | ||
|
|
f59d7998d2 | ||
|
|
77b63f96d1 | ||
|
|
6fcb78403f | ||
|
|
f87edc8c67 | ||
|
|
6a4150d199 | ||
|
|
143c5dd21d | ||
|
|
ed920afb2e | ||
|
|
2677f4c4e7 | ||
|
|
a081534938 | ||
|
|
4ebad27d7a | ||
|
|
716a7307b2 | ||
|
|
ed91bce275 | ||
|
|
c2d6f09ef3 | ||
|
|
119ff5af73 | ||
|
|
2e7ad48b44 | ||
|
|
6ead3b7b1f | ||
|
|
31262cccbe | ||
|
|
93cedbaa51 | ||
|
|
6e13acfac3 | ||
|
|
2e6dd481e0 | ||
|
|
a66808a10d | ||
|
|
a4e1ba0593 | ||
|
|
73660af10c | ||
|
|
84519c236b | ||
|
|
aedb362565 | ||
|
|
6918931728 | ||
|
|
3f1b2bb744 | ||
|
|
33ad02a6b4 | ||
|
|
bfd6e086de | ||
|
|
a9f58383d8 | ||
|
|
aabbbf05ef | ||
|
|
40c613d0cd | ||
|
|
eca5b8796f | ||
|
|
aa2bf7ed08 | ||
|
|
351df67e39 | ||
|
|
8a8698ccdd | ||
|
|
66fa2de073 | ||
|
|
3ace96d7a4 | ||
|
|
2b44ba200f | ||
|
|
4b67a6de12 | ||
|
|
33bd221a98 | ||
|
|
594a06d35b | ||
|
|
e541ff3999 | ||
|
|
9ea184c04a | ||
|
|
993993c6cd | ||
|
|
35b39763dd | ||
|
|
2c1dda5436 | ||
|
|
653123975c | ||
|
|
fb8b314a29 | ||
|
|
5cf3f4e275 | ||
|
|
766500508c | ||
|
|
423a8a6e0d | ||
|
|
7783a76b8f | ||
|
|
2b6a406dc7 | ||
|
|
bc303c4629 | ||
|
|
00360f381c | ||
|
|
fa834f9541 | ||
|
|
a2767cab2a | ||
|
|
24c173a49b | ||
|
|
d3d4908f95 | ||
|
|
be1d5478dc | ||
|
|
d3022ccd65 | ||
|
|
fe45157b26 | ||
|
|
df779fd720 | ||
|
|
e0d388c6f7 | ||
|
|
62edcae233 | ||
|
|
ac6918d70f | ||
|
|
ca41674df3 | ||
|
|
c02b4f3a11 | ||
|
|
64341a81fa | ||
|
|
fe8ba8e44b | ||
|
|
54f1952195 | ||
|
|
44b62a8ebc | ||
|
|
8e9c08ea61 | ||
|
|
c464fb0a81 | ||
|
|
9481e3fba6 | ||
|
|
0e5206a251 | ||
|
|
96c5b4aa3e | ||
|
|
6c44da52a2 | ||
|
|
694cf23df8 | ||
|
|
e66656aa7f | ||
|
|
eaae7af5fe | ||
|
|
2de052ecd8 | ||
|
|
6cf8b9e2b8 | ||
|
|
f9fe138114 | ||
|
|
78c9729252 | ||
|
|
2a2a889c37 | ||
|
|
34287e511f | ||
|
|
e6fffc8ba4 | ||
|
|
86f221611e | ||
|
|
b4d6e89fa2 | ||
|
|
adbb6228a5 | ||
|
|
5937bd0259 | ||
|
|
eeafd43513 | ||
|
|
a68f95b65f | ||
|
|
ed3c29be12 | ||
|
|
3d2e956b19 | ||
|
|
dd9d1f95e9 | ||
|
|
a279c08f7d | ||
|
|
a798109161 | ||
|
|
5dfa929906 | ||
|
|
e904f612f3 | ||
|
|
bafd6b5423 | ||
|
|
963913f9ef | ||
|
|
46905588ac | ||
|
|
5426888df4 | ||
|
|
35481ec6d9 | ||
|
|
6c92c30e94 | ||
|
|
02f6b3ec98 | ||
|
|
a9848f2738 | ||
|
|
b4038a6cd2 | ||
|
|
95f3303493 | ||
|
|
2faf4a491b | ||
|
|
e646bba1ff | ||
|
|
99a21b0a3c | ||
|
|
e7a22b6bc5 | ||
|
|
d783bbc0bc | ||
|
|
b7405f3872 | ||
|
|
abc419b5f9 | ||
|
|
336378b114 | ||
|
|
29959551da | ||
|
|
fc78917191 | ||
|
|
ffd95ef5a9 | ||
|
|
230090d790 | ||
|
|
8fa3861ba3 | ||
|
|
69c90e3427 | ||
|
|
5a73f345fd | ||
|
|
0e62d759f0 | ||
|
|
b2967d2f77 | ||
|
|
c23039c07a | ||
|
|
5747c417c4 | ||
|
|
8c53d77111 | ||
|
|
01667cabde | ||
|
|
f649b62629 | ||
|
|
3a4d025b5c | ||
|
|
c2cc93a009 | ||
|
|
af29855802 | ||
|
|
99eb08eb1e | ||
|
|
d3f8c0d87f | ||
|
|
0bec7b996b | ||
|
|
dd5674fe6b | ||
|
|
33159c26df | ||
|
|
afc7dbebe5 | ||
|
|
f363acf839 | ||
|
|
96d5a7401d | ||
|
|
403fa20546 | ||
|
|
ba4d7ddca8 | ||
|
|
5116e2f210 | ||
|
|
9e0f198227 | ||
|
|
30b378a924 | ||
|
|
3a843f1eca | ||
|
|
9b40f8ab47 | ||
|
|
dc6dcd8150 | ||
|
|
3cb6c7f1f4 | ||
|
|
7632839bc8 | ||
|
|
c3ea109b59 | ||
|
|
579995dc8a | ||
|
|
b43bd5440d | ||
|
|
c4d899f7f3 | ||
|
|
7998ee7036 | ||
|
|
878960d7b1 | ||
|
|
ed0cfc685b | ||
|
|
b0a7345123 | ||
|
|
580963ea76 | ||
|
|
0707deae95 | ||
|
|
fb44880b8c | ||
|
|
e5ebca6604 | ||
|
|
f5fc9acb84 | ||
|
|
28d1bad3cb | ||
|
|
6f74419628 | ||
|
|
8121467c1e | ||
|
|
a85f297f31 | ||
|
|
76a7816aeb | ||
|
|
7872405379 | ||
|
|
6c17a3409f | ||
|
|
f1dbab9dee | ||
|
|
bfafbbf47f | ||
|
|
08d7c35da7 | ||
|
|
f12704f6c1 | ||
|
|
0edab60b30 | ||
|
|
3c05e2d664 | ||
|
|
aa2313c282 | ||
|
|
eeed1954fb | ||
|
|
cd00ce7ab1 | ||
|
|
145d07363f | ||
|
|
33fff655db | ||
|
|
31ab347da2 | ||
|
|
7a48b2ba8e | ||
|
|
876f2a8236 | ||
|
|
095333ffb1 | ||
|
|
0d8d9e2f2b | ||
|
|
9bff2e8883 | ||
|
|
120ba6b870 | ||
|
|
483188ba89 | ||
|
|
672bda0c9c | ||
|
|
49b32473ca | ||
|
|
08400d77a6 | ||
|
|
c912baeb3a | ||
|
|
433733eb0e | ||
|
|
f996ac82c7 | ||
|
|
efcb7cc5a5 | ||
|
|
bf7b57537b | ||
|
|
6b597f8711 | ||
|
|
088739900f | ||
|
|
3bf13f83d3 | ||
|
|
c64a72f1f9 | ||
|
|
8b60b456ac | ||
|
|
e0bac6ad19 | ||
|
|
d841d1bb36 | ||
|
|
0d87cd6ba1 | ||
|
|
28ad36b02c | ||
|
|
cad8a7bd3f | ||
|
|
60a990d660 | ||
|
|
cb3751cea6 | ||
|
|
5ad012e6d9 | ||
|
|
8a454de8f9 | ||
|
|
57b18b7caa | ||
|
|
701d2c9597 | ||
|
|
e7e844bc95 | ||
|
|
0fe95a2f74 | ||
|
|
eb93d8c389 | ||
|
|
8b373ab587 | ||
|
|
c352003f3e | ||
|
|
79d0de7000 | ||
|
|
a32d5ce7ab | ||
|
|
5de0673db1 | ||
|
|
c2b0b6f781 | ||
|
|
116b37813a | ||
|
|
27f0d29734 | ||
|
|
f62af4ebf3 | ||
|
|
faa6d0fd0a | ||
|
|
0554da9d6e | ||
|
|
fa1fd9fbd7 | ||
|
|
3dffc30e83 | ||
|
|
2126b6cf23 | ||
|
|
2b052fdd55 | ||
|
|
58faa762cb | ||
|
|
349cfff1cb | ||
|
|
558be8b923 | ||
|
|
233b3613ae | ||
|
|
615a41d6be | ||
|
|
0ceefcf39d | ||
|
|
16ae64a722 | ||
|
|
3f239fb4a5 | ||
|
|
a60d99fdc9 | ||
|
|
dd0334536b | ||
|
|
3cef37bdb2 | ||
|
|
ac27e94dff | ||
|
|
0877aa7e0b | ||
|
|
07e5a544fe | ||
|
|
60c04a5f33 | ||
|
|
b9b9fb1dd2 | ||
|
|
e1233a0fbc | ||
|
|
cc8203032c | ||
|
|
7117961234 | ||
|
|
d410252cf8 | ||
|
|
4235c57657 | ||
|
|
e34c1ce192 | ||
|
|
4d399ad89c | ||
|
|
9d6ab24388 | ||
|
|
ee9f35d451 | ||
|
|
45c11ec733 | ||
|
|
0519df4ad5 | ||
|
|
55585d8da5 | ||
|
|
b8b49c3124 | ||
|
|
a41471d895 | ||
|
|
877e9ecf64 | ||
|
|
150985bb9c | ||
|
|
039f7669df | ||
|
|
6caa042b05 | ||
|
|
cc0fffc67b | ||
|
|
50d40ef941 | ||
|
|
69d40bd740 | ||
|
|
4272611593 | ||
|
|
74f5e74b89 | ||
|
|
2bba0a6aa3 | ||
|
|
762d3143eb | ||
|
|
7f22e25dfe | ||
|
|
41c162a65f | ||
|
|
ca521946a5 | ||
|
|
b0e53d2b39 | ||
|
|
5c93722db8 | ||
|
|
d34c82c905 | ||
|
|
f11d083b0a | ||
|
|
f1a5a7703c | ||
|
|
9cc2c90a4b | ||
|
|
bc31fa9120 | ||
|
|
7a67645558 | ||
|
|
b0f59358d9 | ||
|
|
0e6c7d8af7 | ||
|
|
9c20085ca9 | ||
|
|
d48a52055a | ||
|
|
dc433e12fb | ||
|
|
1740ca6a16 | ||
|
|
2ae8ca1d63 | ||
|
|
674cd89ac9 | ||
|
|
6ed70add4a | ||
|
|
ae5ebccec7 | ||
|
|
19c8e23425 | ||
|
|
b878cd050d | ||
|
|
a7df00c07a | ||
|
|
3127f1adc6 | ||
|
|
a722cca80a | ||
|
|
0ffd78eab6 | ||
|
|
694c868048 | ||
|
|
2da2006e2a | ||
|
|
0bc83ca065 | ||
|
|
ab2643ef14 | ||
|
|
297812ec11 | ||
|
|
158f754f18 | ||
|
|
da3504105e | ||
|
|
d3f50695b4 | ||
|
|
5a9a6ab0f6 | ||
|
|
b86e78b7a9 | ||
|
|
b1cdf581d0 | ||
|
|
8bf20527be | ||
|
|
3eedc40595 | ||
|
|
93db0ef3e9 | ||
|
|
6922dbbc70 | ||
|
|
f1b9b27a15 | ||
|
|
a755558beb | ||
|
|
b8423d0f5f | ||
|
|
42ef4fbcc1 | ||
|
|
69c11780eb | ||
|
|
c925b43090 | ||
|
|
a5b97cbd9b | ||
|
|
bcb844663f | ||
|
|
0905ee293c | ||
|
|
3325852aab | ||
|
|
c437d99c5f | ||
|
|
cacafc63e8 | ||
|
|
b08f3383b8 | ||
|
|
2eccf67b1c | ||
|
|
293c8bef54 | ||
|
|
00c7ae0542 | ||
|
|
cd656faadf | ||
|
|
056b95ffa9 | ||
|
|
d211df1e73 | ||
|
|
934e036b99 | ||
|
|
9fc86f92fa | ||
|
|
49c6bd4141 | ||
|
|
24011cf2a5 | ||
|
|
83b284dfde | ||
|
|
7c9181317f | ||
|
|
01b410fe9c | ||
|
|
56ac98468d | ||
|
|
658ebeaa21 | ||
|
|
59aa898533 | ||
|
|
c88f87cee2 | ||
|
|
cc663bb08c | ||
|
|
63d647df18 | ||
|
|
e3a46cb6ce | ||
|
|
4556eb3a0c | ||
|
|
26ed9b7c58 | ||
|
|
8ff0b5423d | ||
|
|
0fbced95a8 | ||
|
|
66b816aabc | ||
|
|
af67c893d8 | ||
|
|
71f44d646f | ||
|
|
9edecffcc8 | ||
|
|
d2c93065d5 | ||
|
|
7dd02c1766 | ||
|
|
93a97950e7 | ||
|
|
f17698a8ea | ||
|
|
2cb9f81bab | ||
|
|
ed03818e20 | ||
|
|
cc531af665 | ||
|
|
0dbe78149d | ||
|
|
4bc31f4b2a | ||
|
|
a5253adb9c | ||
|
|
ae3700a193 | ||
|
|
a56604154d | ||
|
|
000f81b21c | ||
|
|
a9583fc6ec | ||
|
|
57eecd7497 | ||
|
|
6803cc4788 | ||
|
|
2796e54540 | ||
|
|
06c23b7742 | ||
|
|
7ce6181bce | ||
|
|
ec31bcbe62 | ||
|
|
0b555e1b2c | ||
|
|
ed21e77fb1 | ||
|
|
3f8b1fe05b | ||
|
|
8d4b6452d4 | ||
|
|
05e3dead7b | ||
|
|
3a01a63a01 | ||
|
|
624aa5290e | ||
|
|
8d9897d5a5 | ||
|
|
16a9975e84 | ||
|
|
755dd3d024 | ||
|
|
ba49fd4c18 | ||
|
|
08b6f6f4e4 | ||
|
|
3128b25236 | ||
|
|
5e054c9d31 | ||
|
|
4bb4a85037 | ||
|
|
b24d813f0f | ||
|
|
7e12918f75 | ||
|
|
11bb176a3f | ||
|
|
fcc3082231 | ||
|
|
49d94f5318 | ||
|
|
fa23026b80 | ||
|
|
0fa2d9c32c | ||
|
|
15a77fd2bb | ||
|
|
9c36ac28fa | ||
|
|
e1e622d985 | ||
|
|
3e86ebc3cf | ||
|
|
6d309b52a5 | ||
|
|
52faa01ecf | ||
|
|
a79c888e0c | ||
|
|
449175e3a6 | ||
|
|
2fce1a6d25 | ||
|
|
798b61c8ef | ||
|
|
84efd367d2 | ||
|
|
d9b0c4c84c | ||
|
|
c9300edead | ||
|
|
4502e8fffb | ||
|
|
51d82bece3 | ||
|
|
0e4f9acb6e | ||
|
|
aa2d8b20cd | ||
|
|
c63ebbdfc4 | ||
|
|
c094780aae | ||
|
|
4162dbc2d8 | ||
|
|
c250f75d1d | ||
|
|
af57fc3ece | ||
|
|
985abd1456 | ||
|
|
0375137296 | ||
|
|
e4956c5500 | ||
|
|
53377cdddc | ||
|
|
81c98c855f | ||
|
|
115a0bc560 | ||
|
|
2744e058b6 | ||
|
|
b6139f74de | ||
|
|
d925939795 | ||
|
|
d4842ebd90 | ||
|
|
5000a2e503 | ||
|
|
109988d105 | ||
|
|
b07bea40f7 | ||
|
|
e287f615f4 | ||
|
|
d2103dbf39 | ||
|
|
7a54d998d4 | ||
|
|
3168b2a1ed | ||
|
|
e0d2fa5701 | ||
|
|
4b4c799129 | ||
|
|
b2c8752211 | ||
|
|
4e9436eb80 | ||
|
|
8c133ef048 | ||
|
|
142879ec30 | ||
|
|
c4f79eff51 | ||
|
|
1dd448e65c | ||
|
|
ab3fed06c7 | ||
|
|
b4dbac1b84 | ||
|
|
e1b59c93de | ||
|
|
0adfd2751e | ||
|
|
fd2248e7c2 | ||
|
|
dd75392d98 | ||
|
|
af2b101fe2 | ||
|
|
62cef3de98 | ||
|
|
03e518f0ea | ||
|
|
7765bdd967 | ||
|
|
cd19d4262b | ||
|
|
4812ddff9f | ||
|
|
df52b51f67 | ||
|
|
a2e4f6cf68 | ||
|
|
ee728d58f5 | ||
|
|
6be6ade6d7 | ||
|
|
d4305ab9da | ||
|
|
ca478016c9 | ||
|
|
a7a2589e81 | ||
|
|
02e4f7305d | ||
|
|
f777ba8aa9 | ||
|
|
7e9eaf41c9 | ||
|
|
bb9b3163ee | ||
|
|
e6d1de0d72 | ||
|
|
e2a660c787 | ||
|
|
c9a5c03eaa | ||
|
|
c2eda0a172 | ||
|
|
c470982ce5 | ||
|
|
68f6b0be6e | ||
|
|
02f379536c | ||
|
|
e239d5f909 | ||
|
|
47c965481f | ||
|
|
3af0f9776f | ||
|
|
c116a28e67 | ||
|
|
6a10654618 | ||
|
|
a42b0bd574 | ||
|
|
404884e295 | ||
|
|
e17d303392 | ||
|
|
e4205c125c | ||
|
|
fdee15e523 | ||
|
|
c9d903cc36 | ||
|
|
e5a0a12ffd | ||
|
|
78cdff6d09 | ||
|
|
a64baed428 | ||
|
|
d8f3bffe63 | ||
|
|
a09b42b364 | ||
|
|
fe67bcdb8b | ||
|
|
7dc1eae40f | ||
|
|
e13896496e | ||
|
|
f864c912ad | ||
|
|
b28aaae66b | ||
|
|
fb872be04a | ||
|
|
8f413f523c | ||
|
|
89243aed37 | ||
|
|
f212deab4d | ||
|
|
79906d73d0 | ||
|
|
d4e3cd31a4 | ||
|
|
f621543d9c | ||
|
|
e801b3a75d | ||
|
|
5f93266e2c | ||
|
|
9b6f8f0c74 | ||
|
|
a352ff3923 | ||
|
|
812ae77257 | ||
|
|
b4efc833c7 | ||
|
|
5e33ac4a09 | ||
|
|
72f565d55d | ||
|
|
b92ee25696 | ||
|
|
a2d4423630 | ||
|
|
897d434673 | ||
|
|
0df5883853 | ||
|
|
84c5e44345 | ||
|
|
aafc23a615 | ||
|
|
49bd56d012 | ||
|
|
45901219b7 | ||
|
|
6ba6f305cc | ||
|
|
5653ae69e4 | ||
|
|
31534fe47d | ||
|
|
3a85fcd365 | ||
|
|
f9c631e9ee | ||
|
|
621bb7c6c5 | ||
|
|
9590eaf342 | ||
|
|
939de0cdbe | ||
|
|
c8be17c91f | ||
|
|
c6476d16e7 | ||
|
|
14668f794d | ||
|
|
efcf8757b0 | ||
|
|
ac9f2ded6e | ||
|
|
c836de5ca8 | ||
|
|
2aa7e30aff | ||
|
|
c724cb7178 | ||
|
|
858c7493df | ||
|
|
8b433b0ff9 | ||
|
|
5614649d14 | ||
|
|
9a4cb6c991 | ||
|
|
2a090e9118 | ||
|
|
7fa02ce5b3 | ||
|
|
44ac9a9f44 | ||
|
|
95e4cc1aec | ||
|
|
fa4dc14c97 | ||
|
|
69f59bfb2a | ||
|
|
eb2bdc3105 | ||
|
|
e079c20ceb | ||
|
|
27324c8236 | ||
|
|
2e71a3b862 | ||
|
|
06acd3caa9 | ||
|
|
4df576869f | ||
|
|
61d46c26b8 | ||
|
|
8eee69bd8f | ||
|
|
e4159d9411 | ||
|
|
7ab4d284ee | ||
|
|
3e6ee23a17 | ||
|
|
faaf600276 | ||
|
|
ca6228b526 | ||
|
|
16924d7913 | ||
|
|
540e4023da | ||
|
|
2ecb2e3c80 | ||
|
|
2675bf4b73 | ||
|
|
01df12cf3c | ||
|
|
7295a9b32e | ||
|
|
607eb13a52 | ||
|
|
2d70526eab | ||
|
|
e29261033f | ||
|
|
9390860288 | ||
|
|
c6764ab31f | ||
|
|
2825888ffd | ||
|
|
34e8de3fc8 | ||
|
|
86f0f9a435 | ||
|
|
03ad8efcba | ||
|
|
bcc7412ef2 | ||
|
|
c1e2b27c60 | ||
|
|
8c5d4128e0 | ||
|
|
f2295acfdd | ||
|
|
529db0493b | ||
|
|
a8c476f7c0 | ||
|
|
f38d0c690c | ||
|
|
e42933ec54 | ||
|
|
ad7ca6977b | ||
|
|
0045d7b716 | ||
|
|
64bd069290 | ||
|
|
70def86613 | ||
|
|
cff5349426 | ||
|
|
54d1c557b2 | ||
|
|
a889f97fd1 | ||
|
|
9ffe20a18b | ||
|
|
9bd456c6df | ||
|
|
a43dffdeda | ||
|
|
61cf67fb95 | ||
|
|
09f2157a92 | ||
|
|
ca4aea173c | ||
|
|
76d370a8f2 | ||
|
|
f4364eb990 | ||
|
|
865348695f | ||
|
|
2ec8189c1c | ||
|
|
a5dfc65440 | ||
|
|
45302f0790 | ||
|
|
1afc6c775b | ||
|
|
ca4d5ed42b | ||
|
|
cd9572e0bb | ||
|
|
ac3ea4d6f3 | ||
|
|
030824b196 | ||
|
|
cfb0c5efad | ||
|
|
b67d713bc0 | ||
|
|
0ac48f60a5 | ||
|
|
445f739234 | ||
|
|
fc8063f752 | ||
|
|
d5abe39d53 | ||
|
|
520acc7d97 | ||
|
|
ae0510f648 | ||
|
|
0f50be877c | ||
|
|
5b18c4de0c | ||
|
|
72fd31fd20 | ||
|
|
256ffdb932 | ||
|
|
8991bcb399 | ||
|
|
185ae510e8 | ||
|
|
40303cb329 | ||
|
|
38ec207609 | ||
|
|
1545e07dd6 | ||
|
|
f123380917 | ||
|
|
b4fc1e4357 | ||
|
|
76a3179868 | ||
|
|
c9bf70fd4b | ||
|
|
9a85071085 | ||
|
|
a6f41bb96d | ||
|
|
3f2acc90aa | ||
|
|
aba9f7d1e5 | ||
|
|
3b8c5ee96d | ||
|
|
a5bb5479fb | ||
|
|
3c58c9d132 | ||
|
|
1b1f91580e | ||
|
|
644dc4b9a7 | ||
|
|
96707645e2 | ||
|
|
b878e5f10d | ||
|
|
a914570240 | ||
|
|
b3d2ab29e9 | ||
|
|
3ff5c793e3 | ||
|
|
c752660aa6 | ||
|
|
efded10e26 | ||
|
|
8767495b5a | ||
|
|
403ede788c | ||
|
|
c444f93eb5 | ||
|
|
ed146f656e | ||
|
|
bcb697eb0b | ||
|
|
3ac66049c7 | ||
|
|
7a1a231041 | ||
|
|
748c88c276 | ||
|
|
6f4b104c9e | ||
|
|
867201a075 | ||
|
|
2545ea1019 | ||
|
|
5be42092af | ||
|
|
50c076eb3f | ||
|
|
fb9e00bf33 | ||
|
|
b9007fcc29 | ||
|
|
f6e01cfda7 | ||
|
|
9203478a8a | ||
|
|
28cb6daec7 | ||
|
|
e191ff53dd | ||
|
|
177297c0ef | ||
|
|
e5d730e1fe | ||
|
|
ba43ecbcb7 | ||
|
|
ee68a9c450 | ||
|
|
175c754f61 | ||
|
|
e8eed838b5 | ||
|
|
e9a3f9f5f6 | ||
|
|
826affb8dd | ||
|
|
4937b1c75e | ||
|
|
ffc16d51e0 | ||
|
|
1623f1e4c0 | ||
|
|
b32e041bfe | ||
|
|
38029d1836 | ||
|
|
b2dd74ab97 | ||
|
|
16fe7ced6a | ||
|
|
cb4af7a9d4 | ||
|
|
7493732176 | ||
|
|
2cf8371add | ||
|
|
a575c24a24 | ||
|
|
9e8d06e7ce | ||
|
|
4f1a2350ce | ||
|
|
cefb64b6a9 | ||
|
|
440d036176 | ||
|
|
53f0deec8f | ||
|
|
3c495e3b23 | ||
|
|
deaf0779a1 | ||
|
|
fd7a353df6 | ||
|
|
927b497feb | ||
|
|
237c54f47e | ||
|
|
8c23db47a7 | ||
|
|
7971ac1cb8 | ||
|
|
2490e605c3 | ||
|
|
21a0cba43e | ||
|
|
42d9287985 | ||
|
|
4848987a1f | ||
|
|
53a22cbe9b | ||
|
|
c3700e0c88 | ||
|
|
58d9a51040 | ||
|
|
8f395ad86f | ||
|
|
99391157ec | ||
|
|
99406d4412 | ||
|
|
c1dea6676f | ||
|
|
afa4664511 | ||
|
|
267eec5509 | ||
|
|
9764eb2f83 | ||
|
|
9a12b55139 | ||
|
|
f8cffef977 | ||
|
|
822420e4ab | ||
|
|
b60fca05bd | ||
|
|
1a35071672 | ||
|
|
bfc3655bad | ||
|
|
2c0c0c9497 | ||
|
|
46bd38e89d | ||
|
|
9fc4d388ce | ||
|
|
2965134f89 | ||
|
|
3a7c8a03f4 | ||
|
|
449b1b68e0 | ||
|
|
dd59eb38d0 | ||
|
|
a8465c95e1 | ||
|
|
df2f67b191 | ||
|
|
7764dee59d | ||
|
|
6465a36176 | ||
|
|
103c1b3a4f | ||
|
|
29cbec37b8 | ||
|
|
2c9e4507a7 | ||
|
|
d5d5c076a7 | ||
|
|
f09bbff35c | ||
|
|
2627e2507b | ||
|
|
1bd7afe6e7 | ||
|
|
e6c1b14108 | ||
|
|
7130e3ff1d | ||
|
|
abf538d80d | ||
|
|
f311ba8d4f | ||
|
|
3e85c4589b | ||
|
|
d0cf047381 | ||
|
|
31091a8df2 | ||
|
|
e207ae4c01 | ||
|
|
3011f18047 | ||
|
|
388d5c2d7c | ||
|
|
5c4719651e | ||
|
|
f850ca63f4 | ||
|
|
65886f1258 | ||
|
|
942e36e19f | ||
|
|
7b82154c4c | ||
|
|
284efc709c | ||
|
|
56965a0046 | ||
|
|
18f6328284 | ||
|
|
e7be999bc9 | ||
|
|
c06b95077d | ||
|
|
c4da063934 | ||
|
|
62d3200e4f | ||
|
|
9a419824ae | ||
|
|
a94eab0398 | ||
|
|
01b8ab8524 | ||
|
|
fa552d7773 | ||
|
|
7acbd4d3e0 | ||
|
|
9811123e2e | ||
|
|
f7cd44be42 | ||
|
|
9a4692e6ee | ||
|
|
3d0e29075d | ||
|
|
0f571b9120 | ||
|
|
3a44508d6f | ||
|
|
5294355c98 | ||
|
|
559efd6477 | ||
|
|
a59577c08d | ||
|
|
4f429d6b86 | ||
|
|
5e7ddc8616 | ||
|
|
a6f6514412 | ||
|
|
31ee38b1a1 | ||
|
|
46c7d6d39a | ||
|
|
78cbff16ef | ||
|
|
28cefb3bd1 | ||
|
|
e666630d36 | ||
|
|
ed2ad860c6 | ||
|
|
a341c24b2a | ||
|
|
0101d6e393 | ||
|
|
e429d8ca10 | ||
|
|
45ba785641 | ||
|
|
ea3d5e68db | ||
|
|
eb75203926 | ||
|
|
1303ea3969 | ||
|
|
bb69e9e70b | ||
|
|
16d1b20ed6 | ||
|
|
b0c3cd75e1 | ||
|
|
f4eef1dc0b | ||
|
|
76c6655520 | ||
|
|
d5c8734555 | ||
|
|
62ee138173 | ||
|
|
ff6cd3ca55 | ||
|
|
852e7ed5aa | ||
|
|
b7e8042a02 | ||
|
|
6bfd7cff72 | ||
|
|
9d77cbea8c | ||
|
|
8bbe147c14 | ||
|
|
b67179e951 | ||
|
|
47237aa7a2 | ||
|
|
5e6c06fb61 | ||
|
|
901455eb0b | ||
|
|
f8c80b7335 | ||
|
|
8db82d27e9 | ||
|
|
1eab47b63f | ||
|
|
c4656b71e5 | ||
|
|
711d3d3515 | ||
|
|
0488f570cb | ||
|
|
0e459ebac8 | ||
|
|
1d65f24b04 | ||
|
|
70719a8f65 | ||
|
|
773c1f2199 | ||
|
|
bf1c801a5e | ||
|
|
e1420b408c | ||
|
|
88a7471039 | ||
|
|
3c58cf0bf0 | ||
|
|
77eebb89fd | ||
|
|
d4d993a53c | ||
|
|
ef3b0672c5 | ||
|
|
0f30c09cbf | ||
|
|
6f670a8f38 | ||
|
|
8c93f7ba74 | ||
|
|
7d3735b19e | ||
|
|
f5f8e49fa3 | ||
|
|
1382d87d7f | ||
|
|
e65b45f969 | ||
|
|
d72b16235a | ||
|
|
3118ccfd05 | ||
|
|
fdba7df3c1 | ||
|
|
02d753027a | ||
|
|
1a43759ac3 | ||
|
|
7574f07be3 | ||
|
|
48717f3f30 | ||
|
|
74d3e92b55 | ||
|
|
f66024b1c1 | ||
|
|
bf4e09a400 | ||
|
|
d968c0b4b1 | ||
|
|
9837b5b429 | ||
|
|
1a03dcabde | ||
|
|
6fb11493ad | ||
|
|
1f063d6712 | ||
|
|
cebcd8a44d | ||
|
|
ce7e5ee2c3 | ||
|
|
242b9209d8 | ||
|
|
92bd809bc8 | ||
|
|
ccc4461827 | ||
|
|
9de524da7d | ||
|
|
7c8db24656 | ||
|
|
d720e9ef49 | ||
|
|
9e69b9dcc4 | ||
|
|
4f7b0c1a21 | ||
|
|
fc5c7264cf | ||
|
|
ede407e6a2 | ||
|
|
e41ca934ac | ||
|
|
0184d5b697 | ||
|
|
e14ebc0adf | ||
|
|
e905704b0c | ||
|
|
f8060f3575 | ||
|
|
120e7b5744 | ||
|
|
b15b20467c | ||
|
|
1d005d47b5 | ||
|
|
d8585334cc | ||
|
|
6444981796 | ||
|
|
713c06354f | ||
|
|
5e2c947cf8 | ||
|
|
92ede0d3c9 | ||
|
|
84057436d6 | ||
|
|
bf18cf2d9e | ||
|
|
7913e5f5bc | ||
|
|
f550540318 | ||
|
|
1d263d24dd | ||
|
|
199763dec8 | ||
|
|
093801479c | ||
|
|
cdcc0052a6 | ||
|
|
b6d4101808 | ||
|
|
83f4fa2190 | ||
|
|
35daae1715 | ||
|
|
1997606372 | ||
|
|
209b115b7c | ||
|
|
93515517b8 | ||
|
|
8c2bff2c91 | ||
|
|
00e9657025 | ||
|
|
31691f0330 | ||
|
|
bb74a42e04 | ||
|
|
731a2a683e | ||
|
|
95fd0c5530 | ||
|
|
79d357b460 | ||
|
|
3ddc20f72c | ||
|
|
4179b8e6c8 | ||
|
|
da23b6fce1 | ||
|
|
feb0502cb4 | ||
|
|
af8a169619 | ||
|
|
68ca28879d | ||
|
|
ad400cd13d | ||
|
|
90b863d124 | ||
|
|
9b7ddd6684 | ||
|
|
bcb939c19d | ||
|
|
e2102dec3c | ||
|
|
2ef16dce90 | ||
|
|
1d9a20b391 | ||
|
|
d953eca630 | ||
|
|
6651e488d6 | ||
|
|
fd3cd47562 | ||
|
|
dcb5682594 | ||
|
|
0bd2a1e232 | ||
|
|
4d77c9f940 | ||
|
|
c461f1f766 | ||
|
|
fbcae2b770 | ||
|
|
c21dfefbdf | ||
|
|
a0c22b8216 | ||
|
|
f7a59178a8 | ||
|
|
028724df08 | ||
|
|
51bbf57e95 | ||
|
|
3e4ec3a12c | ||
|
|
a9dff35a24 | ||
|
|
1cf8156c0c | ||
|
|
f1b8fdec7f | ||
|
|
62d096e57d | ||
|
|
e49bd3ab1d | ||
|
|
1edfdea5e8 | ||
|
|
d141b2421c | ||
|
|
244b3a2c59 | ||
|
|
2e6bdd4041 | ||
|
|
077d554b76 | ||
|
|
4e058f8ece | ||
|
|
d4b90c8f4e | ||
|
|
fed8195eb2 | ||
|
|
b22e43a4a7 | ||
|
|
2c1be17fe7 | ||
|
|
56ce6b8ba4 | ||
|
|
78bac973f7 | ||
|
|
bfd61a7605 | ||
|
|
c40e3d12e8 | ||
|
|
7568531118 | ||
|
|
87411590c5 | ||
|
|
d4170797ae | ||
|
|
6616b25d66 | ||
|
|
6d56c1750f | ||
|
|
4e2c4b94e3 | ||
|
|
0be9815d27 | ||
|
|
f7c34ccb52 | ||
|
|
549290c447 | ||
|
|
2fa4a34589 | ||
|
|
14af70d148 | ||
|
|
6dd599a983 | ||
|
|
176ad74a1c | ||
|
|
38f0ca9f03 | ||
|
|
4d60f9229b | ||
|
|
ea1dd08a8c | ||
|
|
73624da253 | ||
|
|
78a2884b79 | ||
|
|
e24968c679 | ||
|
|
60dc3aa09d | ||
|
|
94be867a54 | ||
|
|
b9ab948ef2 | ||
|
|
a5c6938c65 | ||
|
|
5d0c7aa6a9 | ||
|
|
032fffe111 | ||
|
|
1b726b26cd | ||
|
|
50a8b27854 | ||
|
|
aeb2adbcfb | ||
|
|
746c7b0b5b | ||
|
|
93ad371400 | ||
|
|
d98afdc229 | ||
|
|
80f3afc1ff | ||
|
|
b16a7364fd | ||
|
|
4b543169c8 | ||
|
|
b7e1f8da72 | ||
|
|
97507a92a3 | ||
|
|
a838b85426 | ||
|
|
92fc368ede | ||
|
|
8c994725cb | ||
|
|
20b13a03e0 | ||
|
|
9dcbee1d48 | ||
|
|
77ac84c468 | ||
|
|
8b76799dd9 | ||
|
|
bab0421c6c | ||
|
|
95203c58c4 | ||
|
|
ad7c90b904 | ||
|
|
4583c4a9de | ||
|
|
428cef54c1 | ||
|
|
037f898f81 | ||
|
|
541754df8d | ||
|
|
1cd99ab68e | ||
|
|
6f566d7a38 | ||
|
|
4b25963c93 | ||
|
|
900152f724 | ||
|
|
048c1dde97 | ||
|
|
949fd51463 | ||
|
|
dd17174b35 | ||
|
|
0d14e89549 | ||
|
|
64372a786b | ||
|
|
819b2e99d0 | ||
|
|
d8e703d0f5 | ||
|
|
47a04f2648 | ||
|
|
4af125fa2d | ||
|
|
0665371590 | ||
|
|
ecb83c6ae1 | ||
|
|
c4264daf6a | ||
|
|
243cbae411 | ||
|
|
186dd20ad6 | ||
|
|
d4ba22191a | ||
|
|
29694e5b6a | ||
|
|
a99f415f36 | ||
|
|
54d6cf7087 | ||
|
|
e487e494f9 | ||
|
|
11a19906b9 | ||
|
|
65100e13b3 | ||
|
|
3b52fd5019 | ||
|
|
20e37eaf65 | ||
|
|
b6b2fb9c62 | ||
|
|
b971e6a1da | ||
|
|
087c4976b6 | ||
|
|
d0e4db74b7 | ||
|
|
f7414fec08 | ||
|
|
8cecccbc88 | ||
|
|
441f45e1cc | ||
|
|
48e8a3aec3 | ||
|
|
3fe07888ce | ||
|
|
77b44f570a | ||
|
|
352ec69556 | ||
|
|
bd83773a1e | ||
|
|
bf8b435457 | ||
|
|
e9b19281b2 | ||
|
|
986c85e728 | ||
|
|
8e72931a8b | ||
|
|
aeda4172e4 | ||
|
|
d40f52e953 | ||
|
|
3e47a2c0a7 | ||
|
|
3b9cd6bedd | ||
|
|
0759136d3f | ||
|
|
f0f8aad2bb | ||
|
|
259cecd4b8 | ||
|
|
38873aa0fa | ||
|
|
506c4a330d | ||
|
|
63e4e5ccaa | ||
|
|
cbcc976828 | ||
|
|
1ce1b82f6f | ||
|
|
b92e9ab075 | ||
|
|
cfdae37ef5 | ||
|
|
108b3e497b | ||
|
|
bff228815f | ||
|
|
252cf3723c | ||
|
|
5b88179406 | ||
|
|
6ad5d9f55b | ||
|
|
8a8331bf57 | ||
|
|
931f43f8d7 | ||
|
|
3c1e52bf94 | ||
|
|
a62d15e746 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,7 +4,9 @@
|
|||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
kustomize
|
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
|
||||||
# Test binary, build with `go test -c`
|
# Test binary, build with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|||||||
48
.travis.yml
48
.travis.yml
@@ -1,34 +1,42 @@
|
|||||||
language: go
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
# TODO: Speed up the slowness of the osx travis runs
|
||||||
|
# Maybe cache brew installs?
|
||||||
|
#
|
||||||
|
# TODO: Uncomment when some gets the tests running on Windows.
|
||||||
|
# - windows
|
||||||
|
|
||||||
go:
|
addons:
|
||||||
- 1.11.x
|
apt:
|
||||||
|
packages:
|
||||||
go_import_path: sigs.k8s.io/kustomize
|
- tree
|
||||||
|
homebrew:
|
||||||
# Maybe, maybe not.
|
packages:
|
||||||
# sudo: false
|
- tree
|
||||||
|
update: true
|
||||||
|
|
||||||
# Only clone the most recent commit.
|
# Only clone the most recent commit.
|
||||||
git:
|
git:
|
||||||
depth: 1
|
depth: 1
|
||||||
|
|
||||||
env:
|
language: go
|
||||||
- GOLANGCI_RELEASE="v1.10.2"
|
|
||||||
|
go:
|
||||||
|
- "1.13"
|
||||||
|
|
||||||
|
go_import_path: sigs.k8s.io/kustomize
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- source ./bin/consider-early-travis-exit.sh
|
- source ./travis/consider-early-travis-exit.sh
|
||||||
- sudo apt-get install tree
|
|
||||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $GOPATH/bin ${GOLANGCI_RELEASE}
|
|
||||||
- go get -u github.com/monopole/mdrip
|
|
||||||
|
|
||||||
# Install must be set to prevent default `go get` to run.
|
# Skip the install process; let pre-commit.sh do it.
|
||||||
# The dependencies have already been vendored by `dep` so
|
install: true
|
||||||
# we don't need to fetch them.
|
|
||||||
install:
|
|
||||||
-
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./bin/pre-commit.sh
|
- ./travis/verify-deps.sh
|
||||||
|
- ./travis/pre-commit.sh
|
||||||
|
- ./travis/kyaml-pre-commit.sh
|
||||||
|
|
||||||
# TBD. Suppressing for now.
|
# TBD. Suppressing for now.
|
||||||
notifications:
|
notifications:
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ _As contributors and maintainers of this project, and in the interest of fosteri
|
|||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
|
Dev guides:
|
||||||
|
|
||||||
|
- [Mac](docs/macDevGuide.md)
|
||||||
|
|
||||||
We have full documentation on how to get started contributing here:
|
We have full documentation on how to get started contributing here:
|
||||||
|
|
||||||
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
|
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
|
||||||
|
|||||||
388
Gopkg.lock
generated
388
Gopkg.lock
generated
@@ -1,388 +0,0 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
|
||||||
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:d8ebbd207f3d3266d4423ce4860c9f3794956306ded6c7ba312ecc69cdfbf04c"
|
|
||||||
name = "github.com/PuerkitoBio/purell"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "0bcb03f4b4d0a9428594752bd2a3b9aa0a9d4bd4"
|
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:8098cd40cd09879efbf12e33bcd51ead4a66006ac802cd563a66c4f3373b9727"
|
|
||||||
name = "github.com/PuerkitoBio/urlesc"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "de5bf2ad457846296e2031421a34e2568e304e35"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
|
|
||||||
name = "github.com/davecgh/go-spew"
|
|
||||||
packages = ["spew"]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:f8e6f07329067bc182633dcb19a3df53ce5d454b551e1b5a1cac2163748648d9"
|
|
||||||
name = "github.com/emicklei/go-restful"
|
|
||||||
packages = [
|
|
||||||
".",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "3658237ded108b4134956c1b3050349d93e7b895"
|
|
||||||
version = "v2.7.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:ad32dc29f37281bacb5dcedff17c9461dc1739dc8a5f63a71ab491c6e92edf8d"
|
|
||||||
name = "github.com/evanphx/json-patch"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "afac545df32f2287a079e2dfb7ba2745a643747e"
|
|
||||||
version = "v3.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756"
|
|
||||||
name = "github.com/ghodss/yaml"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "0ca9ea5df5451ffdf184b4428c902747c2c11cd7"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:260f7ebefc63024c8dfe2c9f1a2935a89fa4213637a1f522f592f80c001cc441"
|
|
||||||
name = "github.com/go-openapi/jsonpointer"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "3a0015ad55fa9873f41605d3e8f28cd279c32ab2"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:98abd61947ff5c7c6fcfec5473d02a4821ed3a2dd99a4fbfdb7925b0dd745546"
|
|
||||||
name = "github.com/go-openapi/jsonreference"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "3fb327e6747da3043567ee86abd02bb6376b6be2"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:e95b560c49fb849a61957a5fb3346ce23b3f67426e00e01179e5396cabc9a12c"
|
|
||||||
name = "github.com/go-openapi/spec"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "bcff419492eeeb01f76e77d2ebc714dc97b607f5"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:a610c604eb06f0be4b0fc667388b7a221155d77d7f9089f70ac142a4a9daf014"
|
|
||||||
name = "github.com/go-openapi/swag"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "811b1089cde9dad18d4d0c2d09fbdbf28dbd27a5"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:1b3dd24f14a5280710fc7a3aa2480b6e4d20fdfc905841de9a3aa2aa2f1d4ee9"
|
|
||||||
name = "github.com/gogo/protobuf"
|
|
||||||
packages = [
|
|
||||||
"proto",
|
|
||||||
"sortkeys",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
|
||||||
version = "v1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:e2b86e41f3d669fc36b50d31d32d22c8ac656c75aa5ea89717ce7177e134ff2a"
|
|
||||||
name = "github.com/golang/glog"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:03e14cff610a8a58b774e36bd337fa979482be86aab01be81fb8bbd6d0f07fc8"
|
|
||||||
name = "github.com/golang/protobuf"
|
|
||||||
packages = [
|
|
||||||
"proto",
|
|
||||||
"ptypes",
|
|
||||||
"ptypes/any",
|
|
||||||
"ptypes/duration",
|
|
||||||
"ptypes/timestamp",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
|
||||||
version = "v1.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc"
|
|
||||||
name = "github.com/google/gofuzz"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:3d7c1446fc5c710351b246c0dc6700fae843ca27f5294d0bd9f68bab2a810c44"
|
|
||||||
name = "github.com/googleapis/gnostic"
|
|
||||||
packages = [
|
|
||||||
"OpenAPIv2",
|
|
||||||
"compiler",
|
|
||||||
"extensions",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "ee43cbb60db7bd22502942cccbc39059117352ab"
|
|
||||||
version = "v0.1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:406338ad39ab2e37b7f4452906442a3dbf0eb3379dd1f06aafb5c07e769a5fbb"
|
|
||||||
name = "github.com/inconshreveable/mousetrap"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
|
||||||
version = "v1.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:42c47ace7ccb114261ef7e0d418d274921514ab50a3bf6bdb9e51c3dde8ce13d"
|
|
||||||
name = "github.com/json-iterator/go"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "ca39e5af3ece67bbcda3d0f4f56a8e24d9f2dad4"
|
|
||||||
version = "1.1.3"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:ada518b8c338e10e0afa443d84671476d3bd1d926e13713938088e8ddbee1a3e"
|
|
||||||
name = "github.com/mailru/easyjson"
|
|
||||||
packages = [
|
|
||||||
"buffer",
|
|
||||||
"jlexer",
|
|
||||||
"jwriter",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "3fdea8d05856a0c8df22ed4bc71b3219245e4485"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:2f42fa12d6911c7b7659738758631bec870b7e9b4c6be5444f963cdcfccc191f"
|
|
||||||
name = "github.com/modern-go/concurrent"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94"
|
|
||||||
version = "1.0.3"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:314a5881fab303a80d6d2e35a77000f2224bb50f09ef63a9aa4c1f9eaef985d8"
|
|
||||||
name = "github.com/modern-go/reflect2"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "1df9eeb2bb81f327b96228865c5687bc2194af3f"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121"
|
|
||||||
name = "github.com/pkg/errors"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
|
||||||
version = "v0.8.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:0f156dbd01b40676bdcbc64e51535c09b50f83c9cca5faef3090f82f18bda3c2"
|
|
||||||
name = "github.com/spf13/cobra"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
|
||||||
version = "v0.0.2"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:15e5c398fbd9d2c439b635a08ac161b13d04f0c2aa587fe256b65dc0c3efe8b7"
|
|
||||||
name = "github.com/spf13/pflag"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
|
||||||
version = "v1.0.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:d1a6ebe75268a41b6fbb1d43947cf8688d8580423b7484fa5ae608beef6df24d"
|
|
||||||
name = "golang.org/x/net"
|
|
||||||
packages = [
|
|
||||||
"http2",
|
|
||||||
"http2/hpack",
|
|
||||||
"idna",
|
|
||||||
"lex/httplex",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "1c05540f6879653db88113bc4a2b70aec4bd491f"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:e33513a825fcd765e97b5de639a2f7547542d1a8245df0cef18e1fd390b778a9"
|
|
||||||
name = "golang.org/x/text"
|
|
||||||
packages = [
|
|
||||||
"collate",
|
|
||||||
"collate/build",
|
|
||||||
"internal/colltab",
|
|
||||||
"internal/gen",
|
|
||||||
"internal/tag",
|
|
||||||
"internal/triegen",
|
|
||||||
"internal/ucd",
|
|
||||||
"language",
|
|
||||||
"secure/bidirule",
|
|
||||||
"transform",
|
|
||||||
"unicode/bidi",
|
|
||||||
"unicode/cldr",
|
|
||||||
"unicode/norm",
|
|
||||||
"unicode/rangetable",
|
|
||||||
"width",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
|
||||||
version = "v0.3.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a"
|
|
||||||
name = "gopkg.in/inf.v0"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "d2d2541c53f18d2a059457998ce2876cc8e67cbf"
|
|
||||||
version = "v0.9.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:7c95b35057a0ff2e19f707173cc1a947fa43a6eb5c4d300d196ece0334046082"
|
|
||||||
name = "gopkg.in/yaml.v2"
|
|
||||||
packages = ["."]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
|
||||||
version = "v2.2.1"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:d895c7c24a0dd1ed2ecd061fd88dfea9e1e84d6f280ed859942a2d1aabee10ec"
|
|
||||||
name = "k8s.io/api"
|
|
||||||
packages = [
|
|
||||||
"admissionregistration/v1alpha1",
|
|
||||||
"admissionregistration/v1beta1",
|
|
||||||
"apps/v1",
|
|
||||||
"apps/v1beta1",
|
|
||||||
"apps/v1beta2",
|
|
||||||
"authentication/v1",
|
|
||||||
"authentication/v1beta1",
|
|
||||||
"authorization/v1",
|
|
||||||
"authorization/v1beta1",
|
|
||||||
"autoscaling/v1",
|
|
||||||
"autoscaling/v2beta1",
|
|
||||||
"batch/v1",
|
|
||||||
"batch/v1beta1",
|
|
||||||
"batch/v2alpha1",
|
|
||||||
"certificates/v1beta1",
|
|
||||||
"core/v1",
|
|
||||||
"events/v1beta1",
|
|
||||||
"extensions/v1beta1",
|
|
||||||
"networking/v1",
|
|
||||||
"policy/v1beta1",
|
|
||||||
"rbac/v1",
|
|
||||||
"rbac/v1alpha1",
|
|
||||||
"rbac/v1beta1",
|
|
||||||
"scheduling/v1alpha1",
|
|
||||||
"settings/v1alpha1",
|
|
||||||
"storage/v1",
|
|
||||||
"storage/v1alpha1",
|
|
||||||
"storage/v1beta1",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "53d615ae3f440f957cb9989d989d597f047262d9"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:dff69dd9d9fc681ae077ce5a409aca3c24894d09102ab0395ca7972f6ec01811"
|
|
||||||
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",
|
|
||||||
"pkg/labels",
|
|
||||||
"pkg/runtime",
|
|
||||||
"pkg/runtime/schema",
|
|
||||||
"pkg/runtime/serializer",
|
|
||||||
"pkg/runtime/serializer/json",
|
|
||||||
"pkg/runtime/serializer/protobuf",
|
|
||||||
"pkg/runtime/serializer/recognizer",
|
|
||||||
"pkg/runtime/serializer/versioning",
|
|
||||||
"pkg/selection",
|
|
||||||
"pkg/types",
|
|
||||||
"pkg/util/errors",
|
|
||||||
"pkg/util/framer",
|
|
||||||
"pkg/util/intstr",
|
|
||||||
"pkg/util/json",
|
|
||||||
"pkg/util/mergepatch",
|
|
||||||
"pkg/util/net",
|
|
||||||
"pkg/util/runtime",
|
|
||||||
"pkg/util/sets",
|
|
||||||
"pkg/util/strategicpatch",
|
|
||||||
"pkg/util/validation",
|
|
||||||
"pkg/util/validation/field",
|
|
||||||
"pkg/util/wait",
|
|
||||||
"pkg/util/yaml",
|
|
||||||
"pkg/watch",
|
|
||||||
"third_party/forked/golang/json",
|
|
||||||
"third_party/forked/golang/reflect",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "13b73596e4b63e03203e86f6d9c7bcc1b937c62f"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
digest = "1:ae9ced9ef7b8eb2794a4f80bc3af9d2bc38ec7d60337367bad9a655c1d641458"
|
|
||||||
name = "k8s.io/client-go"
|
|
||||||
packages = ["kubernetes/scheme"]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "23781f4d6632d88e869066eaebb743857aa1ef9b"
|
|
||||||
version = "v7.0.0"
|
|
||||||
|
|
||||||
[[projects]]
|
|
||||||
branch = "master"
|
|
||||||
digest = "1:f4fb3421360af5c51070bfe0c1c7467f8809fa70e278e129f068f5106b5c8a65"
|
|
||||||
name = "k8s.io/kube-openapi"
|
|
||||||
packages = [
|
|
||||||
"pkg/common",
|
|
||||||
"pkg/util/proto",
|
|
||||||
]
|
|
||||||
pruneopts = "NUT"
|
|
||||||
revision = "b3f03f55328800731ce03a164b80973014ecd455"
|
|
||||||
|
|
||||||
[solve-meta]
|
|
||||||
analyzer-name = "dep"
|
|
||||||
analyzer-version = 1
|
|
||||||
input-imports = [
|
|
||||||
"github.com/evanphx/json-patch",
|
|
||||||
"github.com/ghodss/yaml",
|
|
||||||
"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/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
|
|
||||||
58
Gopkg.toml
58
Gopkg.toml
@@ -1,58 +0,0 @@
|
|||||||
# Gopkg.toml example
|
|
||||||
#
|
|
||||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
|
||||||
# for detailed Gopkg.toml documentation.
|
|
||||||
#
|
|
||||||
# required = ["github.com/user/thing/cmd/thing"]
|
|
||||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project"
|
|
||||||
# version = "1.0.0"
|
|
||||||
#
|
|
||||||
# [[constraint]]
|
|
||||||
# name = "github.com/user/project2"
|
|
||||||
# branch = "dev"
|
|
||||||
# source = "github.com/myfork/project2"
|
|
||||||
#
|
|
||||||
# [[override]]
|
|
||||||
# name = "github.com/x/y"
|
|
||||||
# version = "2.4.0"
|
|
||||||
|
|
||||||
# prune out unused content from vendor
|
|
||||||
[prune]
|
|
||||||
go-tests = true
|
|
||||||
non-go = true
|
|
||||||
unused-packages = true
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/evanphx/json-patch"
|
|
||||||
version = "3.0.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/ghodss/yaml"
|
|
||||||
version = "1.0.0"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/spf13/cobra"
|
|
||||||
version = "0.0.2"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "k8s.io/api"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
branch = "master"
|
|
||||||
name = "k8s.io/apimachinery"
|
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "k8s.io/client-go"
|
|
||||||
version = "7.0.0"
|
|
||||||
|
|
||||||
[[override]]
|
|
||||||
branch = "master"
|
|
||||||
name = "k8s.io/utils"
|
|
||||||
|
|
||||||
[[override]]
|
|
||||||
branch = "master"
|
|
||||||
name = "github.com/go-openapi/spec"
|
|
||||||
145
Makefile
Normal file
145
Makefile
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# This Makefile is (and must be) used by
|
||||||
|
# travis/pre-commit.sh to qualify pull requests.
|
||||||
|
#
|
||||||
|
# That script generates all the code that needs
|
||||||
|
# to be generated, and runs all the tests.
|
||||||
|
#
|
||||||
|
# Functionality in that script is gradually moving here.
|
||||||
|
|
||||||
|
MYGOBIN := $(shell go env GOPATH)/bin
|
||||||
|
PATH := $(PATH):$(MYGOBIN)
|
||||||
|
SHELL := env PATH=$(PATH) /bin/bash
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: pre-commit
|
||||||
|
|
||||||
|
# The pre-commit.sh script generates, lints and tests.
|
||||||
|
# It uses this makefile. For more clarity, would like
|
||||||
|
# to stop that - any scripts invoked by targets here
|
||||||
|
# shouldn't "call back" to the makefile.
|
||||||
|
.PHONY: pre-commit
|
||||||
|
pre-commit:
|
||||||
|
./travis/pre-commit.sh
|
||||||
|
|
||||||
|
# Version pinned by api/go.mod
|
||||||
|
$(MYGOBIN)/golangci-lint:
|
||||||
|
cd api; \
|
||||||
|
go install github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||||
|
|
||||||
|
# Version pinned by api/go.mod
|
||||||
|
$(MYGOBIN)/mdrip:
|
||||||
|
cd api; \
|
||||||
|
go install github.com/monopole/mdrip
|
||||||
|
|
||||||
|
# Version pinned by api/go.mod
|
||||||
|
$(MYGOBIN)/stringer:
|
||||||
|
cd api; \
|
||||||
|
go install golang.org/x/tools/cmd/stringer
|
||||||
|
|
||||||
|
# Version pinned by api/go.mod
|
||||||
|
$(MYGOBIN)/goimports:
|
||||||
|
cd api; \
|
||||||
|
go install golang.org/x/tools/cmd/goimports
|
||||||
|
|
||||||
|
# TODO: need a new release of the API, followed by a new pluginator.
|
||||||
|
# pluginator v1.1.0 is too old for the code currently needed in the API.
|
||||||
|
# Can release a new one at any time, just haven't done so.
|
||||||
|
# When one has been released,
|
||||||
|
# - uncomment the pluginator line in './api/internal/tools/tools.go'
|
||||||
|
# - pin the version tag in './api/go.mod' to match the new release
|
||||||
|
# - change the following to 'cd api; go install sigs.k8s.io/kustomize/pluginator'
|
||||||
|
$(MYGOBIN)/pluginator:
|
||||||
|
cd pluginator; \
|
||||||
|
go install .
|
||||||
|
|
||||||
|
.PHONY: install-tools
|
||||||
|
install-tools: \
|
||||||
|
$(MYGOBIN)/goimports \
|
||||||
|
$(MYGOBIN)/golangci-lint \
|
||||||
|
$(MYGOBIN)/mdrip \
|
||||||
|
$(MYGOBIN)/pluginator \
|
||||||
|
$(MYGOBIN)/stringer
|
||||||
|
|
||||||
|
# Builtin plugins are generated code.
|
||||||
|
# Add new items here to create new builtins.
|
||||||
|
builtinplugins = \
|
||||||
|
api/builtins/annotationstransformer.go \
|
||||||
|
api/builtins/configmapgenerator.go \
|
||||||
|
api/builtins/hashtransformer.go \
|
||||||
|
api/builtins/imagetagtransformer.go \
|
||||||
|
api/builtins/inventorytransformer.go \
|
||||||
|
api/builtins/labeltransformer.go \
|
||||||
|
api/builtins/legacyordertransformer.go \
|
||||||
|
api/builtins/namespacetransformer.go \
|
||||||
|
api/builtins/patchjson6902transformer.go \
|
||||||
|
api/builtins/patchstrategicmergetransformer.go \
|
||||||
|
api/builtins/patchtransformer.go \
|
||||||
|
api/builtins/prefixsuffixtransformer.go \
|
||||||
|
api/builtins/replicacounttransformer.go \
|
||||||
|
api/builtins/secretgenerator.go
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: install-tools $(builtinplugins)
|
||||||
|
cd api; $(MYGOBIN)/golangci-lint run ./...
|
||||||
|
cd kustomize; $(MYGOBIN)/golangci-lint run ./...
|
||||||
|
cd pluginator; $(MYGOBIN)/golangci-lint run ./...
|
||||||
|
|
||||||
|
api/builtins/%.go: $(MYGOBIN)/pluginator
|
||||||
|
@echo "generating $*"; \
|
||||||
|
cd plugin/builtin/$*; \
|
||||||
|
go generate .; \
|
||||||
|
cd ../../../api/builtins; \
|
||||||
|
$(MYGOBIN)/goimports -w $*.go
|
||||||
|
|
||||||
|
.PHONY: generate
|
||||||
|
generate: $(builtinplugins)
|
||||||
|
|
||||||
|
.PHONY: unit-test-api
|
||||||
|
unit-test-api: $(builtinplugins)
|
||||||
|
cd api; go test ./...
|
||||||
|
|
||||||
|
.PHONY: unit-test-plugins
|
||||||
|
unit-test-plugins:
|
||||||
|
./hack/runPluginUnitTests.sh
|
||||||
|
|
||||||
|
.PHONY: unit-test-kustomize
|
||||||
|
unit-test-kustomize:
|
||||||
|
cd kustomize; go test ./...
|
||||||
|
|
||||||
|
.PHONY: unit-test-all
|
||||||
|
unit-test-all: unit-test-api unit-test-kustomize unit-test-plugins
|
||||||
|
|
||||||
|
# linux only.
|
||||||
|
# This is for testing an example plugin that
|
||||||
|
# uses kubeval for validation.
|
||||||
|
# Don't want to add a hard dependence in go.mod file
|
||||||
|
# to github.com/instrumenta/kubeval.
|
||||||
|
# Instead, download the binary.
|
||||||
|
$(MYGOBIN)/kubeval:
|
||||||
|
d=$(shell mktemp -d); cd $$d; \
|
||||||
|
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz; \
|
||||||
|
tar xf kubeval-linux-amd64.tar.gz; \
|
||||||
|
mv kubeval $(MYGOBIN); \
|
||||||
|
rm -rf $$d
|
||||||
|
|
||||||
|
# linux only.
|
||||||
|
# This is for testing an example plugin that
|
||||||
|
# uses helm to inflate a chart for subsequent kustomization.
|
||||||
|
# Don't want to add a hard dependence in go.mod file
|
||||||
|
# to helm.
|
||||||
|
# Instead, download the binary.
|
||||||
|
$(MYGOBIN)/helm:
|
||||||
|
d=$(shell mktemp -d); cd $$d; \
|
||||||
|
wget https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz; \
|
||||||
|
tar -xvzf helm-v2.13.1-linux-amd64.tar.gz; \
|
||||||
|
mv linux-amd64/helm $(MYGOBIN); \
|
||||||
|
rm -rf $$d
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f $(builtinplugins)
|
||||||
|
rm -f $(MYGOBIN)/pluginator
|
||||||
|
|
||||||
|
.PHONY: nuke
|
||||||
|
nuke: clean
|
||||||
|
sudo rm -rf $(shell go env GOPATH)/pkg/mod/sigs.k8s.io
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
aliases:
|
aliases:
|
||||||
kustomize-admins:
|
kustomize-admins:
|
||||||
- grodrigues3
|
|
||||||
- monopole
|
- monopole
|
||||||
- pwittrock
|
- pwittrock
|
||||||
kustomize-maintainers:
|
kustomize-maintainers:
|
||||||
|
|||||||
67
README.md
67
README.md
@@ -7,16 +7,32 @@ untouched and usable as is.
|
|||||||
`kustomize` targets kubernetes; it understands and can
|
`kustomize` targets kubernetes; it understands and can
|
||||||
patch [kubernetes style] API objects. It's like
|
patch [kubernetes style] API objects. It's like
|
||||||
[`make`], in that what it does is declared in a file,
|
[`make`], in that what it does is declared in a file,
|
||||||
and it's like [`sed`], in that it emits editted text.
|
and it's like [`sed`], in that it emits edited text.
|
||||||
|
|
||||||
|
This tool is sponsored by [sig-cli] ([KEP]), and
|
||||||
|
inspired by [DAM].
|
||||||
|
|
||||||
This tool is sponsored by [sig-cli] ([KEP]).
|
|
||||||
|
|
||||||
[](https://travis-ci.org/kubernetes-sigs/kustomize)
|
[](https://travis-ci.org/kubernetes-sigs/kustomize)
|
||||||
[](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
|
[](https://goreportcard.com/report/github.com/kubernetes-sigs/kustomize)
|
||||||
|
|
||||||
**Installation**: Download a binary from the [release
|
Download a binary from the [release page], or see
|
||||||
page], or see these [install] notes. Then try one of
|
these [instructions](docs/INSTALL.md).
|
||||||
the tested [examples].
|
|
||||||
|
Browse the [docs](docs) or jump right into the
|
||||||
|
tested [examples](examples).
|
||||||
|
|
||||||
|
## kubectl integration
|
||||||
|
|
||||||
|
Since [v1.14][kubectl announcement] the kustomize build system has been included in kubectl.
|
||||||
|
|
||||||
|
| kubectl version | kustomize version |
|
||||||
|
|---------|--------|
|
||||||
|
| v1.16.0 | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
|
||||||
|
| v1.15.x | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
|
||||||
|
| v1.14.x | [v2.0.3](https://github.com/kubernetes-sigs/kustomize/tree/v2.0.3) |
|
||||||
|
|
||||||
|
For examples and guides for using the kubectl integration please see the [kubectl book] or the [kubernetes documentation].
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -115,29 +131,46 @@ The YAML can be directly [applied] to a cluster:
|
|||||||
> kustomize build ~/someApp/overlays/production | kubectl apply -f -
|
> kustomize build ~/someApp/overlays/production | kubectl apply -f -
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
## Community, discussion, contribution, and support
|
## Community
|
||||||
|
|
||||||
Learn how to engage with the Kubernetes community on the [community page].
|
To file bugs please read [this](docs/bugs.md).
|
||||||
|
|
||||||
You can reach the maintainers of this project at:
|
Before working on an implementation, please
|
||||||
|
|
||||||
|
* Read the [eschewed feature list].
|
||||||
|
* File an issue describing
|
||||||
|
how the new feature would behave
|
||||||
|
and label it [kind/feature].
|
||||||
|
|
||||||
|
### Other communication channels
|
||||||
|
|
||||||
- [Slack]
|
- [Slack]
|
||||||
- [Mailing List]
|
- [Mailing List]
|
||||||
|
- General kubernetes [community page]
|
||||||
|
|
||||||
### Code of conduct
|
### Code of conduct
|
||||||
|
|
||||||
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct].
|
Participation in the Kubernetes community
|
||||||
|
is governed by the [Kubernetes Code of Conduct].
|
||||||
|
|
||||||
[KEP]: https://github.com/kubernetes/community/blob/master/keps/sig-cli/0008-kustomize.md
|
|
||||||
[`make`]: https://www.gnu.org/software/make
|
[`make`]: https://www.gnu.org/software/make
|
||||||
[`sed`]: https://www.gnu.org/software/sed
|
[`sed`]: https://www.gnu.org/software/sed
|
||||||
|
[DAM]: docs/glossary.md#declarative-application-management
|
||||||
|
[KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/0008-kustomize.md
|
||||||
|
[Kubernetes Code of Conduct]: code-of-conduct.md
|
||||||
|
[Mailing List]: https://groups.google.com/forum/#!forum/kubernetes-sig-cli
|
||||||
|
[Slack]: https://kubernetes.slack.com/messages/sig-cli
|
||||||
[applied]: docs/glossary.md#apply
|
[applied]: docs/glossary.md#apply
|
||||||
[base]: docs/glossary.md#base
|
[base]: docs/glossary.md#base
|
||||||
|
[community page]: http://kubernetes.io/community/
|
||||||
[declarative configuration]: docs/glossary.md#declarative-application-management
|
[declarative configuration]: docs/glossary.md#declarative-application-management
|
||||||
[examples]: examples/README.md
|
[eschewed feature list]: docs/eschewedFeatures.md
|
||||||
[imageBase]: docs/base.jpg
|
[imageBase]: docs/images/base.jpg
|
||||||
[imageOverlay]: docs/overlay.jpg
|
[imageOverlay]: docs/images/overlay.jpg
|
||||||
[install]: docs/INSTALL.md
|
[kind/feature]: https://github.com/kubernetes-sigs/kustomize/labels/kind%2Ffeature
|
||||||
|
[kubectl announcement]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
|
||||||
|
[kubectl book]: https://kubectl.docs.kubernetes.io/pages/app_customization/introduction.html
|
||||||
|
[kubernetes documentation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
|
||||||
[kubernetes style]: docs/glossary.md#kubernetes-style-object
|
[kubernetes style]: docs/glossary.md#kubernetes-style-object
|
||||||
[kustomization]: docs/glossary.md#kustomization
|
[kustomization]: docs/glossary.md#kustomization
|
||||||
[overlay]: docs/glossary.md#overlay
|
[overlay]: docs/glossary.md#overlay
|
||||||
@@ -148,8 +181,6 @@ Participation in the Kubernetes community is governed by the [Kubernetes Code of
|
|||||||
[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md
|
[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md
|
||||||
[variant]: docs/glossary.md#variant
|
[variant]: docs/glossary.md#variant
|
||||||
[variants]: docs/glossary.md#variant
|
[variants]: docs/glossary.md#variant
|
||||||
|
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
|
||||||
|
[v2.1.0]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.1.0
|
||||||
[workflows]: docs/workflows.md
|
[workflows]: docs/workflows.md
|
||||||
[community page]: http://kubernetes.io/community/
|
|
||||||
[Kubernetes Code of Conduct]: code-of-conduct.md
|
|
||||||
[Slack]: https://kubernetes.slack.com/messages/sig-cli
|
|
||||||
[Mailing List]: https://groups.google.com/forum/#!forum/kubernetes-sig-cli
|
|
||||||
|
|||||||
39
api/builtins/annotationstransformer.go
Normal file
39
api/builtins/annotationstransformer.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Code generated by pluginator on AnnotationsTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add the given annotations to the given field specifications.
|
||||||
|
type AnnotationsTransformerPlugin struct {
|
||||||
|
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||||
|
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AnnotationsTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
p.Annotations = nil
|
||||||
|
p.FieldSpecs = nil
|
||||||
|
return yaml.Unmarshal(c, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
t, err := transform.NewMapTransformer(
|
||||||
|
p.FieldSpecs,
|
||||||
|
p.Annotations,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.Transform(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAnnotationsTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &AnnotationsTransformerPlugin{}
|
||||||
|
}
|
||||||
43
api/builtins/configmapgenerator.go
Normal file
43
api/builtins/configmapgenerator.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigMapGeneratorPlugin struct {
|
||||||
|
h *resmap.PluginHelpers
|
||||||
|
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
types.GeneratorOptions
|
||||||
|
types.ConfigMapArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConfigMapGeneratorPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, config []byte) (err error) {
|
||||||
|
p.GeneratorOptions = types.GeneratorOptions{}
|
||||||
|
p.ConfigMapArgs = types.ConfigMapArgs{}
|
||||||
|
err = yaml.Unmarshal(config, p)
|
||||||
|
if p.ConfigMapArgs.Name == "" {
|
||||||
|
p.ConfigMapArgs.Name = p.Name
|
||||||
|
}
|
||||||
|
if p.ConfigMapArgs.Namespace == "" {
|
||||||
|
p.ConfigMapArgs.Namespace = p.Namespace
|
||||||
|
}
|
||||||
|
p.h = h
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ConfigMapGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||||
|
return p.h.ResmapFactory().FromConfigMapArgs(
|
||||||
|
kv.NewLoader(p.h.Loader(), p.h.Validator()),
|
||||||
|
&p.GeneratorOptions, p.ConfigMapArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigMapGeneratorPlugin() resmap.GeneratorPlugin {
|
||||||
|
return &ConfigMapGeneratorPlugin{}
|
||||||
|
}
|
||||||
8
api/builtins/doc.go
Normal file
8
api/builtins/doc.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package builtins holds code generated from the builtin plugins.
|
||||||
|
// The "builtin" plugins are written as normal plugins and can
|
||||||
|
// be used as such, but they are also used to generate the code
|
||||||
|
// in this package so they can be statically linked to client code.
|
||||||
|
package builtins
|
||||||
39
api/builtins/hashtransformer.go
Normal file
39
api/builtins/hashtransformer.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Code generated by pluginator on HashTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HashTransformerPlugin struct {
|
||||||
|
hasher ifc.KunstructuredHasher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HashTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, config []byte) (err error) {
|
||||||
|
p.hasher = h.ResmapFactory().RF().Hasher()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform appends hash to generated resources.
|
||||||
|
func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
for _, res := range m.Resources() {
|
||||||
|
if res.NeedHashSuffix() {
|
||||||
|
h, err := p.hasher.Hash(res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHashTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &HashTransformerPlugin{}
|
||||||
|
}
|
||||||
185
api/builtins/imagetagtransformer.go
Normal file
185
api/builtins/imagetagtransformer.go
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find matching image declarations and replace
|
||||||
|
// the name, tag and/or digest.
|
||||||
|
type ImageTagTransformerPlugin struct {
|
||||||
|
ImageTag types.Image `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
|
||||||
|
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ImageTagTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
p.ImageTag = types.Image{}
|
||||||
|
p.FieldSpecs = nil
|
||||||
|
return yaml.Unmarshal(c, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ImageTagTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
for _, r := range m.Resources() {
|
||||||
|
for _, path := range p.FieldSpecs {
|
||||||
|
if !r.OrgId().IsSelected(&path.Gvk) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := transform.MutateField(
|
||||||
|
r.Map(), path.PathSlice(), false, p.mutateImage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Kept for backward compatibility
|
||||||
|
if err := p.findAndReplaceImage(r.Map()); err != nil && r.OrgId().Kind != `CustomResourceDefinition` {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ImageTagTransformerPlugin) mutateImage(in interface{}) (interface{}, error) {
|
||||||
|
original, ok := in.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("image path is not of type string but %T", in)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isImageMatched(original, p.ImageTag.Name) {
|
||||||
|
return original, nil
|
||||||
|
}
|
||||||
|
name, tag := split(original)
|
||||||
|
if p.ImageTag.NewName != "" {
|
||||||
|
name = p.ImageTag.NewName
|
||||||
|
}
|
||||||
|
if p.ImageTag.NewTag != "" {
|
||||||
|
tag = ":" + p.ImageTag.NewTag
|
||||||
|
}
|
||||||
|
if p.ImageTag.Digest != "" {
|
||||||
|
tag = "@" + p.ImageTag.Digest
|
||||||
|
}
|
||||||
|
return name + tag, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// findAndReplaceImage replaces the image name and
|
||||||
|
// tags inside one object.
|
||||||
|
// It searches the object for container session
|
||||||
|
// then loops though all images inside containers
|
||||||
|
// session, finds matched ones and update the
|
||||||
|
// image name and tag name
|
||||||
|
func (p *ImageTagTransformerPlugin) findAndReplaceImage(obj map[string]interface{}) error {
|
||||||
|
paths := []string{"containers", "initContainers"}
|
||||||
|
updated := false
|
||||||
|
for _, path := range paths {
|
||||||
|
containers, found := obj[path]
|
||||||
|
if found {
|
||||||
|
if _, err := p.updateContainers(containers); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !updated {
|
||||||
|
return p.findContainers(obj)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ImageTagTransformerPlugin) updateContainers(in interface{}) (interface{}, error) {
|
||||||
|
containers, ok := in.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"containers path is not of type []interface{} but %T", in)
|
||||||
|
}
|
||||||
|
for i := range containers {
|
||||||
|
container := containers[i].(map[string]interface{})
|
||||||
|
containerImage, found := container["image"]
|
||||||
|
if !found {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
imageName := containerImage.(string)
|
||||||
|
if isImageMatched(imageName, p.ImageTag.Name) {
|
||||||
|
newImage, err := p.mutateImage(imageName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
container["image"] = newImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return containers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ImageTagTransformerPlugin) findContainers(obj map[string]interface{}) error {
|
||||||
|
for key := range obj {
|
||||||
|
switch typedV := obj[key].(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
err := p.findAndReplaceImage(typedV)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case []interface{}:
|
||||||
|
for i := range typedV {
|
||||||
|
item := typedV[i]
|
||||||
|
typedItem, ok := item.(map[string]interface{})
|
||||||
|
if ok {
|
||||||
|
err := p.findAndReplaceImage(typedItem)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isImageMatched(s, t string) bool {
|
||||||
|
// Tag values are limited to [a-zA-Z0-9_.-].
|
||||||
|
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.-]*)?$")
|
||||||
|
return pattern.MatchString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// split separates and returns the name and tag parts
|
||||||
|
// from the image string using either colon `:` or at `@` separators.
|
||||||
|
// Note that the returned tag keeps its separator.
|
||||||
|
func split(imageName string) (name string, tag string) {
|
||||||
|
// check if image name contains a domain
|
||||||
|
// if domain is present, ignore domain and check for `:`
|
||||||
|
ic := -1
|
||||||
|
if slashIndex := strings.Index(imageName, "/"); slashIndex < 0 {
|
||||||
|
ic = strings.LastIndex(imageName, ":")
|
||||||
|
} else {
|
||||||
|
lastIc := strings.LastIndex(imageName[slashIndex:], ":")
|
||||||
|
// set ic only if `:` is present
|
||||||
|
if lastIc > 0 {
|
||||||
|
ic = slashIndex + lastIc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ia := strings.LastIndex(imageName, "@")
|
||||||
|
if ic < 0 && ia < 0 {
|
||||||
|
return imageName, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
i := ic
|
||||||
|
if ia > 0 {
|
||||||
|
i = ia
|
||||||
|
}
|
||||||
|
|
||||||
|
name = imageName[:i]
|
||||||
|
tag = imageName[i:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImageTagTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &ImageTagTransformerPlugin{}
|
||||||
|
}
|
||||||
128
api/builtins/inventorytransformer.go
Normal file
128
api/builtins/inventorytransformer.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
// Code generated by pluginator on InventoryTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/hasher"
|
||||||
|
"sigs.k8s.io/kustomize/api/inventory"
|
||||||
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type InventoryTransformerPlugin struct {
|
||||||
|
h *resmap.PluginHelpers
|
||||||
|
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
Policy string `json:"policy,omitempty" yaml:"policy,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *InventoryTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
p.h = h
|
||||||
|
err = yaml.Unmarshal(c, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if p.Policy == "" {
|
||||||
|
p.Policy = types.GarbageIgnore.String()
|
||||||
|
}
|
||||||
|
if p.Policy != types.GarbageCollect.String() &&
|
||||||
|
p.Policy != types.GarbageIgnore.String() {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"unrecognized garbagePolicy '%s'", p.Policy)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform generates an inventory object from the input ResMap.
|
||||||
|
// This ConfigMap supports the pruning command in
|
||||||
|
// the client side tool proposed here:
|
||||||
|
// https://github.com/kubernetes/enhancements/pull/810
|
||||||
|
//
|
||||||
|
// The inventory data is written to the ConfigMap's
|
||||||
|
// annotations, rather than to the key-value pairs in
|
||||||
|
// the ConfigMap's data field, since
|
||||||
|
// 1. Keys in a ConfigMap's data field are too
|
||||||
|
// constrained for this purpose.
|
||||||
|
// 2. Using annotations allow any object to be used,
|
||||||
|
// not just a ConfigMap, should some other object
|
||||||
|
// (e.g. some App object) become more desirable
|
||||||
|
// for this purpose.
|
||||||
|
func (p *InventoryTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
|
||||||
|
inv, h, err := makeInventory(m)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
args := types.ConfigMapArgs{}
|
||||||
|
args.Name = p.Name
|
||||||
|
args.Namespace = p.Namespace
|
||||||
|
opts := &types.GeneratorOptions{
|
||||||
|
Annotations: make(map[string]string),
|
||||||
|
}
|
||||||
|
opts.Annotations[inventory.HashAnnotation] = h
|
||||||
|
err = inv.UpdateAnnotations(opts.Annotations)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cm, err := p.h.ResmapFactory().RF().MakeConfigMap(
|
||||||
|
kv.NewLoader(p.h.Loader(), p.h.Validator()), opts, &args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Policy == types.GarbageCollect.String() {
|
||||||
|
for _, byeBye := range m.AllIds() {
|
||||||
|
m.Remove(byeBye)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m.Append(cm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInventory(m resmap.ResMap) (
|
||||||
|
inv *inventory.Inventory, hash string, err error) {
|
||||||
|
inv = inventory.NewInventory()
|
||||||
|
var keys []string
|
||||||
|
for _, r := range m.Resources() {
|
||||||
|
ns := r.GetNamespace()
|
||||||
|
item := resid.NewResIdWithNamespace(r.GetGvk(), r.GetName(), ns)
|
||||||
|
if _, ok := inv.Current[item]; ok {
|
||||||
|
return nil, "", fmt.Errorf(
|
||||||
|
"item '%v' already in inventory", item)
|
||||||
|
}
|
||||||
|
inv.Current[item], err = computeRefs(r, m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
keys = append(keys, item.String())
|
||||||
|
}
|
||||||
|
h, err := hasher.SortArrayAndComputeHash(keys)
|
||||||
|
return inv, h, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeRefs(
|
||||||
|
r *resource.Resource, m resmap.ResMap) (refs []resid.ResId, err error) {
|
||||||
|
for _, refid := range r.GetRefBy() {
|
||||||
|
ref, err := m.GetByCurrentId(refid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
refs = append(
|
||||||
|
refs,
|
||||||
|
resid.NewResIdWithNamespace(
|
||||||
|
ref.GetGvk(), ref.GetName(), ref.GetNamespace()))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInventoryTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &InventoryTransformerPlugin{}
|
||||||
|
}
|
||||||
39
api/builtins/labeltransformer.go
Normal file
39
api/builtins/labeltransformer.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Code generated by pluginator on LabelTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add the given labels to the given field specifications.
|
||||||
|
type LabelTransformerPlugin struct {
|
||||||
|
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||||
|
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LabelTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
p.Labels = nil
|
||||||
|
p.FieldSpecs = nil
|
||||||
|
return yaml.Unmarshal(c, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LabelTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
t, err := transform.NewMapTransformer(
|
||||||
|
p.FieldSpecs,
|
||||||
|
p.Labels,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.Transform(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLabelTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &LabelTransformerPlugin{}
|
||||||
|
}
|
||||||
46
api/builtins/legacyordertransformer.go
Normal file
46
api/builtins/legacyordertransformer.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Code generated by pluginator on LegacyOrderTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sort the resources using an ordering defined in the Gvk class.
|
||||||
|
// This puts cluster-wide basic resources with no
|
||||||
|
// dependencies (like Namespace, StorageClass, etc.)
|
||||||
|
// first, and resources with a high number of dependencies
|
||||||
|
// (like ValidatingWebhookConfiguration) last.
|
||||||
|
type LegacyOrderTransformerPlugin struct{}
|
||||||
|
|
||||||
|
// Nothing needed for configuration.
|
||||||
|
func (p *LegacyOrderTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *LegacyOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) {
|
||||||
|
resources := make([]*resource.Resource, m.Size())
|
||||||
|
ids := m.AllIds()
|
||||||
|
sort.Sort(resmap.IdSlice(ids))
|
||||||
|
for i, id := range ids {
|
||||||
|
resources[i], err = m.GetByCurrentId(id)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "expected match for sorting")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m.Clear()
|
||||||
|
for _, r := range resources {
|
||||||
|
m.Append(r)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLegacyOrderTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &LegacyOrderTransformerPlugin{}
|
||||||
|
}
|
||||||
131
api/builtins/namespacetransformer.go
Normal file
131
api/builtins/namespacetransformer.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
// Code generated by pluginator on NamespaceTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Change or set the namespace of non-cluster level resources.
|
||||||
|
type NamespaceTransformerPlugin struct {
|
||||||
|
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *NamespaceTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
p.Namespace = ""
|
||||||
|
p.FieldSpecs = nil
|
||||||
|
return yaml.Unmarshal(c, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
if len(p.Namespace) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, r := range m.Resources() {
|
||||||
|
if len(r.Map()) == 0 {
|
||||||
|
// Don't mutate empty objects?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
id := r.OrgId()
|
||||||
|
applicableFs := p.applicableFieldSpecs(id)
|
||||||
|
|
||||||
|
for _, fs := range applicableFs {
|
||||||
|
err := transform.MutateField(
|
||||||
|
r.Map(), fs.PathSlice(), fs.CreateIfNotPresent,
|
||||||
|
p.changeNamespace(r))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals)
|
||||||
|
if len(matches) != 1 {
|
||||||
|
return fmt.Errorf("namespace tranformation produces ID conflict: %#v", matches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const metaNamespace = "metadata/namespace"
|
||||||
|
|
||||||
|
// Special casing metadata.namespace since
|
||||||
|
// all objects have it, even "ClusterKind" objects
|
||||||
|
// that don't exist in a namespace (the Namespace
|
||||||
|
// object itself doesn't live in a namespace).
|
||||||
|
func (p *NamespaceTransformerPlugin) applicableFieldSpecs(id resid.ResId) []types.FieldSpec {
|
||||||
|
var res []types.FieldSpec
|
||||||
|
for _, fs := range p.FieldSpecs {
|
||||||
|
if id.IsSelected(&fs.Gvk) && (fs.Path != metaNamespace || (fs.Path == metaNamespace && id.IsNamespaceableKind())) {
|
||||||
|
res = append(res, fs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *NamespaceTransformerPlugin) changeNamespace(
|
||||||
|
referrer *resource.Resource) func(in interface{}) (interface{}, error) {
|
||||||
|
return func(in interface{}) (interface{}, error) {
|
||||||
|
switch in.(type) {
|
||||||
|
case string:
|
||||||
|
// will happen when the metadata/namespace
|
||||||
|
// value is replaced
|
||||||
|
return p.Namespace, nil
|
||||||
|
case []interface{}:
|
||||||
|
l, _ := in.([]interface{})
|
||||||
|
for idx, item := range l {
|
||||||
|
switch item.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
// Will happen when mutating the subjects
|
||||||
|
// field of ClusterRoleBinding and RoleBinding
|
||||||
|
inMap, _ := item.(map[string]interface{})
|
||||||
|
if _, ok := inMap["name"]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, ok := inMap["name"].(string)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The only case we need to force the namespace
|
||||||
|
// if for the "service account". "default" is
|
||||||
|
// kind of hardcoded here for right now.
|
||||||
|
if name != "default" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
inMap["namespace"] = p.Namespace
|
||||||
|
l[idx] = inMap
|
||||||
|
default:
|
||||||
|
// nothing to do for right now
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return in, nil
|
||||||
|
case map[string]interface{}:
|
||||||
|
// Will happen if the createField=true
|
||||||
|
// when the namespace is added to the
|
||||||
|
// object
|
||||||
|
inMap := in.(map[string]interface{})
|
||||||
|
if len(inMap) == 0 {
|
||||||
|
return p.Namespace, nil
|
||||||
|
} else {
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNamespaceTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &NamespaceTransformerPlugin{}
|
||||||
|
}
|
||||||
100
api/builtins/patchjson6902transformer.go
Normal file
100
api/builtins/patchjson6902transformer.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Code generated by pluginator on PatchJson6902Transformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
jsonpatch "github.com/evanphx/json-patch"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PatchJson6902TransformerPlugin struct {
|
||||||
|
ldr ifc.Loader
|
||||||
|
decodedPatch jsonpatch.Patch
|
||||||
|
Target types.PatchTarget `json:"target,omitempty" yaml:"target,omitempty"`
|
||||||
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PatchJson6902TransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
p.ldr = h.Loader()
|
||||||
|
err = yaml.Unmarshal(c, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if p.Target.Name == "" {
|
||||||
|
return fmt.Errorf("must specify the target name")
|
||||||
|
}
|
||||||
|
if p.Path == "" && p.JsonOp == "" {
|
||||||
|
return fmt.Errorf("empty file path and empty jsonOp")
|
||||||
|
}
|
||||||
|
if p.Path != "" {
|
||||||
|
if p.JsonOp != "" {
|
||||||
|
return fmt.Errorf("must specify a file path or jsonOp, not both")
|
||||||
|
}
|
||||||
|
rawOp, err := p.ldr.Load(p.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.JsonOp = string(rawOp)
|
||||||
|
if p.JsonOp == "" {
|
||||||
|
return fmt.Errorf("patch file '%s' empty seems to be empty", p.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.JsonOp[0] != '[' {
|
||||||
|
// if it doesn't seem to be JSON, imagine
|
||||||
|
// it is YAML, and convert to JSON.
|
||||||
|
op, err := yaml.YAMLToJSON([]byte(p.JsonOp))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.JsonOp = string(op)
|
||||||
|
}
|
||||||
|
p.decodedPatch, err = jsonpatch.DecodePatch([]byte(p.JsonOp))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "decoding %s", p.JsonOp)
|
||||||
|
}
|
||||||
|
if len(p.decodedPatch) == 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"patch appears to be empty; file=%s, JsonOp=%s", p.Path, p.JsonOp)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PatchJson6902TransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
id := resid.NewResIdWithNamespace(
|
||||||
|
resid.Gvk{
|
||||||
|
Group: p.Target.Group,
|
||||||
|
Version: p.Target.Version,
|
||||||
|
Kind: p.Target.Kind,
|
||||||
|
},
|
||||||
|
p.Target.Name,
|
||||||
|
p.Target.Namespace,
|
||||||
|
)
|
||||||
|
obj, err := m.GetById(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rawObj, err := obj.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
modifiedObj, err := p.decodedPatch.Apply(rawObj)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(
|
||||||
|
err, "failed to apply json patch '%s'", p.JsonOp)
|
||||||
|
}
|
||||||
|
return obj.UnmarshalJSON(modifiedObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPatchJson6902TransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &PatchJson6902TransformerPlugin{}
|
||||||
|
}
|
||||||
90
api/builtins/patchstrategicmergetransformer.go
Normal file
90
api/builtins/patchstrategicmergetransformer.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
// Code generated by pluginator on PatchStrategicMergeTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PatchStrategicMergeTransformerPlugin struct {
|
||||||
|
h *resmap.PluginHelpers
|
||||||
|
loadedPatches []*resource.Resource
|
||||||
|
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
|
||||||
|
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PatchStrategicMergeTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
p.h = h
|
||||||
|
err = yaml.Unmarshal(c, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(p.Paths) == 0 && p.Patches == "" {
|
||||||
|
return fmt.Errorf("empty file path and empty patch content")
|
||||||
|
}
|
||||||
|
if len(p.Paths) != 0 {
|
||||||
|
for _, onePath := range p.Paths {
|
||||||
|
res, err := p.h.ResmapFactory().RF().SliceFromBytes([]byte(onePath))
|
||||||
|
if err == nil {
|
||||||
|
p.loadedPatches = append(p.loadedPatches, res...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res, err = p.h.ResmapFactory().RF().SliceFromPatches(
|
||||||
|
p.h.Loader(), []types.PatchStrategicMerge{onePath})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.loadedPatches = append(p.loadedPatches, res...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Patches != "" {
|
||||||
|
res, err := p.h.ResmapFactory().RF().SliceFromBytes([]byte(p.Patches))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.loadedPatches = append(p.loadedPatches, res...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.loadedPatches) == 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"patch appears to be empty; files=%v, Patch=%s", p.Paths, p.Patches)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PatchStrategicMergeTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
patches, err := p.h.ResmapFactory().MergePatches(p.loadedPatches)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, patch := range patches.Resources() {
|
||||||
|
target, err := m.GetById(patch.OrgId())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = target.Patch(patch.Kunstructured)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// remove the resource from resmap
|
||||||
|
// when the patch is to $patch: delete that target
|
||||||
|
if len(target.Map()) == 0 {
|
||||||
|
err = m.Remove(target.CurId())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPatchStrategicMergeTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &PatchStrategicMergeTransformerPlugin{}
|
||||||
|
}
|
||||||
145
api/builtins/patchtransformer.go
Normal file
145
api/builtins/patchtransformer.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
// Code generated by pluginator on PatchTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
jsonpatch "github.com/evanphx/json-patch"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PatchTransformerPlugin struct {
|
||||||
|
loadedPatch *resource.Resource
|
||||||
|
decodedPatch jsonpatch.Patch
|
||||||
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||||
|
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PatchTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
err = yaml.Unmarshal(c, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if p.Patch == "" && p.Path == "" {
|
||||||
|
err = fmt.Errorf(
|
||||||
|
"must specify one of patch and path in\n%s", string(c))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.Patch != "" && p.Path != "" {
|
||||||
|
err = fmt.Errorf(
|
||||||
|
"patch and path can't be set at the same time\n%s", string(c))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var in []byte
|
||||||
|
if p.Path != "" {
|
||||||
|
in, err = h.Loader().Load(p.Path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Patch != "" {
|
||||||
|
in = []byte(p.Patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
patchSM, errSM := h.ResmapFactory().RF().FromBytes(in)
|
||||||
|
patchJson, errJson := jsonPatchFromBytes(in)
|
||||||
|
if errSM != nil && errJson != nil {
|
||||||
|
err = fmt.Errorf(
|
||||||
|
"unable to get either a Strategic Merge Patch or JSON patch 6902 from %s", p.Patch)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errSM == nil && errJson != nil {
|
||||||
|
p.loadedPatch = patchSM
|
||||||
|
}
|
||||||
|
if errJson == nil && errSM != nil {
|
||||||
|
p.decodedPatch = patchJson
|
||||||
|
}
|
||||||
|
if patchSM != nil && patchJson != nil {
|
||||||
|
err = fmt.Errorf(
|
||||||
|
"a patch can't be both a Strategic Merge Patch and JSON patch 6902 %s", p.Patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
if p.loadedPatch != nil && p.Target == nil {
|
||||||
|
target, err := m.GetById(p.loadedPatch.OrgId())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = target.Patch(p.loadedPatch.Kunstructured)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Target == nil {
|
||||||
|
return fmt.Errorf("must specify a target for patch %s", p.Patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
resources, err := m.Select(*p.Target)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, res := range resources {
|
||||||
|
if p.decodedPatch != nil {
|
||||||
|
rawObj, err := res.MarshalJSON()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
modifiedObj, err := p.decodedPatch.Apply(rawObj)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(
|
||||||
|
err, "failed to apply json patch '%s'", p.Patch)
|
||||||
|
}
|
||||||
|
err = res.UnmarshalJSON(modifiedObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.loadedPatch != nil {
|
||||||
|
patchCopy := p.loadedPatch.DeepCopy()
|
||||||
|
patchCopy.SetName(res.GetName())
|
||||||
|
patchCopy.SetNamespace(res.GetNamespace())
|
||||||
|
patchCopy.SetGvk(res.GetGvk())
|
||||||
|
err = res.Patch(patchCopy.Kunstructured)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// jsonPatchFromBytes loads a Json 6902 patch from
|
||||||
|
// a bytes input
|
||||||
|
func jsonPatchFromBytes(
|
||||||
|
in []byte) (jsonpatch.Patch, error) {
|
||||||
|
ops := string(in)
|
||||||
|
if ops == "" {
|
||||||
|
return nil, fmt.Errorf("empty json patch operations")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ops[0] != '[' {
|
||||||
|
jsonOps, err := yaml.YAMLToJSON(in)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ops = string(jsonOps)
|
||||||
|
}
|
||||||
|
return jsonpatch.DecodePatch([]byte(ops))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPatchTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &PatchTransformerPlugin{}
|
||||||
|
}
|
||||||
123
api/builtins/prefixsuffixtransformer.go
Normal file
123
api/builtins/prefixsuffixtransformer.go
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// Code generated by pluginator on PrefixSuffixTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add the given prefix and suffix to the field.
|
||||||
|
type PrefixSuffixTransformerPlugin struct {
|
||||||
|
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||||
|
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
||||||
|
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not placed in a file yet due to lack of demand.
|
||||||
|
var prefixSuffixFieldSpecsToSkip = []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{Kind: "CustomResourceDefinition"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrefixSuffixTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
p.Prefix = ""
|
||||||
|
p.Suffix = ""
|
||||||
|
p.FieldSpecs = nil
|
||||||
|
err = yaml.Unmarshal(c, p)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if p.FieldSpecs == nil {
|
||||||
|
return errors.New("fieldSpecs is not expected to be nil")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
|
||||||
|
// Even if both the Prefix and Suffix are empty we want
|
||||||
|
// to proceed with the transformation. This allows to add contextual
|
||||||
|
// information to the resources (AddNamePrefix and AddNameSuffix).
|
||||||
|
|
||||||
|
for _, r := range m.Resources() {
|
||||||
|
if p.shouldSkip(r.OrgId()) {
|
||||||
|
// Don't change the actual definition
|
||||||
|
// of a CRD.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
id := r.OrgId()
|
||||||
|
// current default configuration contains
|
||||||
|
// only one entry: "metadata/name" with no GVK
|
||||||
|
for _, path := range p.FieldSpecs {
|
||||||
|
if !id.IsSelected(&path.Gvk) {
|
||||||
|
// With the currrent default configuration,
|
||||||
|
// because no Gvk is specified, so a wild
|
||||||
|
// card
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if smellsLikeANameChange(&path) {
|
||||||
|
// "metadata/name" is the only field.
|
||||||
|
// this will add a prefix and a suffix
|
||||||
|
// to the resource even if those are
|
||||||
|
// empty
|
||||||
|
r.AddNamePrefix(p.Prefix)
|
||||||
|
r.AddNameSuffix(p.Suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the addPrefixSuffix method will not
|
||||||
|
// change the name if both the prefix and suffix
|
||||||
|
// are empty.
|
||||||
|
err := transform.MutateField(
|
||||||
|
r.Map(),
|
||||||
|
path.PathSlice(),
|
||||||
|
path.CreateIfNotPresent,
|
||||||
|
p.addPrefixSuffix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func smellsLikeANameChange(fs *types.FieldSpec) bool {
|
||||||
|
return fs.Path == "metadata/name"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrefixSuffixTransformerPlugin) shouldSkip(
|
||||||
|
id resid.ResId) bool {
|
||||||
|
for _, path := range prefixSuffixFieldSpecsToSkip {
|
||||||
|
if id.IsSelected(&path.Gvk) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrefixSuffixTransformerPlugin) addPrefixSuffix(
|
||||||
|
in interface{}) (interface{}, error) {
|
||||||
|
s, ok := in.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("%#v is expected to be %T", in, s)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s%s%s", p.Prefix, s, p.Suffix), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrefixSuffixTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &PrefixSuffixTransformerPlugin{}
|
||||||
|
}
|
||||||
89
api/builtins/replicacounttransformer.go
Normal file
89
api/builtins/replicacounttransformer.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Code generated by pluginator on ReplicaCountTransformer; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find matching replicas declarations and replace the count.
|
||||||
|
// Eases the kustomization configuration of replica changes.
|
||||||
|
type ReplicaCountTransformerPlugin struct {
|
||||||
|
Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"`
|
||||||
|
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ReplicaCountTransformerPlugin) Config(
|
||||||
|
h *resmap.PluginHelpers, c []byte) (err error) {
|
||||||
|
|
||||||
|
p.Replica = types.Replica{}
|
||||||
|
p.FieldSpecs = nil
|
||||||
|
return yaml.Unmarshal(c, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
|
found := false
|
||||||
|
for i, replicaSpec := range p.FieldSpecs {
|
||||||
|
matcher := p.createMatcher(i)
|
||||||
|
matchOriginal := m.GetMatchingResourcesByOriginalId(matcher)
|
||||||
|
matchCurrent := m.GetMatchingResourcesByCurrentId(matcher)
|
||||||
|
|
||||||
|
for _, res := range append(matchOriginal, matchCurrent...) {
|
||||||
|
found = true
|
||||||
|
err := transform.MutateField(
|
||||||
|
res.Map(), replicaSpec.PathSlice(),
|
||||||
|
replicaSpec.CreateIfNotPresent, p.addReplicas)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
gvks := make([]string, len(p.FieldSpecs))
|
||||||
|
for i, replicaSpec := range p.FieldSpecs {
|
||||||
|
gvks[i] = replicaSpec.Gvk.String()
|
||||||
|
}
|
||||||
|
return fmt.Errorf("resource with name %s does not match a config with the following GVK %v",
|
||||||
|
p.Replica.Name, gvks)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match Replica.Name and FieldSpec
|
||||||
|
func (p *ReplicaCountTransformerPlugin) createMatcher(i int) resmap.IdMatcher {
|
||||||
|
return func(r resid.ResId) bool {
|
||||||
|
return r.Name == p.Replica.Name &&
|
||||||
|
r.Gvk.IsSelected(&p.FieldSpecs[i].Gvk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ReplicaCountTransformerPlugin) addReplicas(in interface{}) (interface{}, error) {
|
||||||
|
switch m := in.(type) {
|
||||||
|
case int64:
|
||||||
|
// Was already in the field.
|
||||||
|
case map[string]interface{}:
|
||||||
|
if len(m) != 0 {
|
||||||
|
// A map was already in the replicas field, don't want to
|
||||||
|
// discard this data silently.
|
||||||
|
return nil, fmt.Errorf("%#v is expected to be %T", in, m)
|
||||||
|
}
|
||||||
|
// Just got added, default type is map, but we can return anything.
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("%#v is expected to be %T", in, m)
|
||||||
|
}
|
||||||
|
return p.Replica.Count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReplicaCountTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &ReplicaCountTransformerPlugin{}
|
||||||
|
}
|
||||||
42
api/builtins/secretgenerator.go
Normal file
42
api/builtins/secretgenerator.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
|
||||||
|
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||||
|
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SecretGeneratorPlugin struct {
|
||||||
|
h *resmap.PluginHelpers
|
||||||
|
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
|
types.GeneratorOptions
|
||||||
|
types.SecretArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SecretGeneratorPlugin) Config(h *resmap.PluginHelpers, config []byte) (err error) {
|
||||||
|
p.GeneratorOptions = types.GeneratorOptions{}
|
||||||
|
p.SecretArgs = types.SecretArgs{}
|
||||||
|
err = yaml.Unmarshal(config, p)
|
||||||
|
if p.SecretArgs.Name == "" {
|
||||||
|
p.SecretArgs.Name = p.Name
|
||||||
|
}
|
||||||
|
if p.SecretArgs.Namespace == "" {
|
||||||
|
p.SecretArgs.Namespace = p.Namespace
|
||||||
|
}
|
||||||
|
p.h = h
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SecretGeneratorPlugin) Generate() (resmap.ResMap, error) {
|
||||||
|
return p.h.ResmapFactory().FromSecretArgs(
|
||||||
|
kv.NewLoader(p.h.Loader(), p.h.Validator()),
|
||||||
|
&p.GeneratorOptions, p.SecretArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSecretGeneratorPlugin() resmap.GeneratorPlugin {
|
||||||
|
return &SecretGeneratorPlugin{}
|
||||||
|
}
|
||||||
79
api/filesys/confirmeddir.go
Normal file
79
api/filesys/confirmeddir.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfirmedDir is a clean, absolute, delinkified path
|
||||||
|
// that was confirmed to point to an existing directory.
|
||||||
|
type ConfirmedDir string
|
||||||
|
|
||||||
|
// NewTmpConfirmedDir returns a temporary dir, else error.
|
||||||
|
// The directory is cleaned, no symlinks, etc. so it's
|
||||||
|
// returned as a ConfirmedDir.
|
||||||
|
func NewTmpConfirmedDir() (ConfirmedDir, error) {
|
||||||
|
n, err := ioutil.TempDir("", "kustomize-")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// In MacOs `ioutil.TempDir` creates a directory
|
||||||
|
// with root in the `/var` folder, which is in turn
|
||||||
|
// a symlinked path to `/private/var`.
|
||||||
|
// Function `filepath.EvalSymlinks`is used to
|
||||||
|
// resolve the real absolute path.
|
||||||
|
deLinked, err := filepath.EvalSymlinks(n)
|
||||||
|
return ConfirmedDir(deLinked), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPrefix returns true if the directory argument
|
||||||
|
// is a prefix of self (d) from the point of view of
|
||||||
|
// a file system.
|
||||||
|
//
|
||||||
|
// I.e., it's true if the argument equals or contains
|
||||||
|
// self (d) in a file path sense.
|
||||||
|
//
|
||||||
|
// HasPrefix emulates the semantics of strings.HasPrefix
|
||||||
|
// such that the following are true:
|
||||||
|
//
|
||||||
|
// strings.HasPrefix("foobar", "foobar")
|
||||||
|
// strings.HasPrefix("foobar", "foo")
|
||||||
|
// strings.HasPrefix("foobar", "")
|
||||||
|
//
|
||||||
|
// d := fSys.ConfirmDir("/foo/bar")
|
||||||
|
// d.HasPrefix("/foo/bar")
|
||||||
|
// d.HasPrefix("/foo")
|
||||||
|
// d.HasPrefix("/")
|
||||||
|
//
|
||||||
|
// Not contacting a file system here to check for
|
||||||
|
// actual path existence.
|
||||||
|
//
|
||||||
|
// This is tested on linux, but will have trouble
|
||||||
|
// on other operating systems.
|
||||||
|
// TODO(monopole) Refactor when #golang/go/18358 closes.
|
||||||
|
// See also:
|
||||||
|
// https://github.com/golang/go/issues/18358
|
||||||
|
// https://github.com/golang/dep/issues/296
|
||||||
|
// https://github.com/golang/dep/blob/master/internal/fs/fs.go#L33
|
||||||
|
// https://codereview.appspot.com/5712045
|
||||||
|
func (d ConfirmedDir) HasPrefix(path ConfirmedDir) bool {
|
||||||
|
if path.String() == string(filepath.Separator) || path == d {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return strings.HasPrefix(
|
||||||
|
string(d),
|
||||||
|
string(path)+string(filepath.Separator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d ConfirmedDir) Join(path string) string {
|
||||||
|
return filepath.Join(string(d), path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d ConfirmedDir) String() string {
|
||||||
|
return string(d)
|
||||||
|
}
|
||||||
109
api/filesys/confirmeddir_test.go
Normal file
109
api/filesys/confirmeddir_test.go
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestJoin(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
err := fSys.Mkdir("/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
d, f, err := fSys.CleanedAbs("/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if f != "" {
|
||||||
|
t.Fatalf("unexpected file: %v", f)
|
||||||
|
}
|
||||||
|
if d.Join("bar") != "/foo/bar" {
|
||||||
|
t.Fatalf("expected join %s", d.Join("bar"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefix_Slash(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
d, f, err := fSys.CleanedAbs("/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if f != "" {
|
||||||
|
t.Fatalf("unexpected file: %v", f)
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/hey") {
|
||||||
|
t.Fatalf("should be false")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/") {
|
||||||
|
t.Fatalf("/ should have the prefix /")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefix_SlashFoo(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
err := fSys.Mkdir("/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
d, _, err := fSys.CleanedAbs("/foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/fo") {
|
||||||
|
t.Fatalf("/foo does not have path prefix /fo")
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/fod") {
|
||||||
|
t.Fatalf("/foo does not have path prefix /fod")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/foo") {
|
||||||
|
t.Fatalf("/foo should have prefix /foo")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefix_SlashFooBar(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
err := fSys.MkdirAll("/foo/bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
d, _, err := fSys.CleanedAbs("/foo/bar")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/fo") {
|
||||||
|
t.Fatalf("/foo/bar does not have path prefix /fo")
|
||||||
|
}
|
||||||
|
if d.HasPrefix("/foobar") {
|
||||||
|
t.Fatalf("/foo/bar does not have path prefix /foobar")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/foo/bar") {
|
||||||
|
t.Fatalf("/foo/bar should have prefix /foo/bar")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/foo") {
|
||||||
|
t.Fatalf("/foo/bar should have prefix /foo")
|
||||||
|
}
|
||||||
|
if !d.HasPrefix("/") {
|
||||||
|
t.Fatalf("/foo/bar should have prefix /")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewTempConfirmDir(t *testing.T) {
|
||||||
|
tmp, err := NewTmpConfirmedDir()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
delinked, err := filepath.EvalSymlinks(string(tmp))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if string(tmp) != delinked {
|
||||||
|
t.Fatalf("unexpected path containing symlinks")
|
||||||
|
}
|
||||||
|
}
|
||||||
41
api/filesys/file.go
Normal file
41
api/filesys/file.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ os.FileInfo = &fileInfo{}
|
||||||
|
|
||||||
|
// fileInfo implements os.FileInfo for a fileInMemory instance.
|
||||||
|
type fileInfo struct {
|
||||||
|
*fileInMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the file
|
||||||
|
func (fi *fileInfo) Name() string { return fi.name }
|
||||||
|
|
||||||
|
// Size returns the size of the file
|
||||||
|
func (fi *fileInfo) Size() int64 { return int64(len(fi.content)) }
|
||||||
|
|
||||||
|
// Mode returns the file mode
|
||||||
|
func (fi *fileInfo) Mode() os.FileMode { return 0777 }
|
||||||
|
|
||||||
|
// ModTime returns the modification time
|
||||||
|
func (fi *fileInfo) ModTime() time.Time { return time.Time{} }
|
||||||
|
|
||||||
|
// IsDir returns if it is a directory
|
||||||
|
func (fi *fileInfo) IsDir() bool { return fi.dir }
|
||||||
|
|
||||||
|
// Sys should return underlying data source, but it now returns nil
|
||||||
|
func (fi *fileInfo) Sys() interface{} { return nil }
|
||||||
|
|
||||||
|
// File groups the basic os.File methods.
|
||||||
|
type File interface {
|
||||||
|
io.ReadWriteCloser
|
||||||
|
Stat() (os.FileInfo, error)
|
||||||
|
}
|
||||||
56
api/filesys/fileinmemory.go
Normal file
56
api/filesys/fileinmemory.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ File = &fileInMemory{}
|
||||||
|
|
||||||
|
// fileInMemory implements File in-memory for tests.
|
||||||
|
type fileInMemory struct {
|
||||||
|
name string
|
||||||
|
content []byte
|
||||||
|
dir bool
|
||||||
|
open bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeDir makes a fake directory.
|
||||||
|
func makeDir(name string) *fileInMemory {
|
||||||
|
return &fileInMemory{name: name, dir: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close marks the fake file closed.
|
||||||
|
func (f *fileInMemory) Close() error {
|
||||||
|
f.open = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read never fails, and doesn't mutate p.
|
||||||
|
func (f *fileInMemory) Read(p []byte) (n int, err error) {
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write saves the contents of the argument to memory.
|
||||||
|
func (f *fileInMemory) Write(p []byte) (n int, err error) {
|
||||||
|
f.content = p
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentMatches returns true if v matches fake file's content.
|
||||||
|
func (f *fileInMemory) ContentMatches(v []byte) bool {
|
||||||
|
return bytes.Equal(v, f.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContent the content of a fake file.
|
||||||
|
func (f *fileInMemory) GetContent() []byte {
|
||||||
|
return f.content
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat returns nil.
|
||||||
|
func (f *fileInMemory) Stat() (os.FileInfo, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
27
api/filesys/fileondisk.go
Normal file
27
api/filesys/fileondisk.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ File = &fileOnDisk{}
|
||||||
|
|
||||||
|
// fileOnDisk implements File using the local filesystem.
|
||||||
|
type fileOnDisk struct {
|
||||||
|
file *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes a file.
|
||||||
|
func (f *fileOnDisk) Close() error { return f.file.Close() }
|
||||||
|
|
||||||
|
// Read reads a file's content.
|
||||||
|
func (f *fileOnDisk) Read(p []byte) (n int, err error) { return f.file.Read(p) }
|
||||||
|
|
||||||
|
// Write writes bytes to a file
|
||||||
|
func (f *fileOnDisk) Write(p []byte) (n int, err error) { return f.file.Write(p) }
|
||||||
|
|
||||||
|
// Stat returns an interface which has all the information regarding the file.
|
||||||
|
func (f *fileOnDisk) Stat() (os.FileInfo, error) { return f.file.Stat() }
|
||||||
41
api/filesys/filesystem.go
Normal file
41
api/filesys/filesystem.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package filesys provides a file system abstraction layer.
|
||||||
|
package filesys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileSystem groups basic os filesystem methods.
|
||||||
|
type FileSystem interface {
|
||||||
|
// Create a file.
|
||||||
|
Create(name string) (File, error)
|
||||||
|
// MkDir makes a directory.
|
||||||
|
Mkdir(path string) error
|
||||||
|
// MkDir makes a directory path, creating intervening directories.
|
||||||
|
MkdirAll(path string) error
|
||||||
|
// RemoveAll removes path and any children it contains.
|
||||||
|
RemoveAll(path string) error
|
||||||
|
// Open opens the named file for reading.
|
||||||
|
Open(path string) (File, error)
|
||||||
|
// IsDir returns true if the path is a directory.
|
||||||
|
IsDir(path string) bool
|
||||||
|
// CleanedAbs converts the given path into a
|
||||||
|
// directory and a file name, where the directory
|
||||||
|
// is represented as a ConfirmedDir and all that implies.
|
||||||
|
// If the entire path is a directory, the file component
|
||||||
|
// is an empty string.
|
||||||
|
CleanedAbs(path string) (ConfirmedDir, string, error)
|
||||||
|
// Exists is true if the path exists in the file system.
|
||||||
|
Exists(path string) bool
|
||||||
|
// Glob returns the list of matching files
|
||||||
|
Glob(pattern string) ([]string, error)
|
||||||
|
// ReadFile returns the contents of the file at the given path.
|
||||||
|
ReadFile(path string) ([]byte, error)
|
||||||
|
// WriteFile writes the data to a file at the given path.
|
||||||
|
WriteFile(path string, data []byte) error
|
||||||
|
// Walk walks the file system with the given WalkFunc.
|
||||||
|
Walk(path string, walkFn filepath.WalkFunc) error
|
||||||
|
}
|
||||||
223
api/filesys/fsinmemory.go
Normal file
223
api/filesys/fsinmemory.go
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ FileSystem = &fsInMemory{}
|
||||||
|
|
||||||
|
// fsInMemory implements FileSystem using a in-memory filesystem
|
||||||
|
// primarily for use in tests.
|
||||||
|
type fsInMemory struct {
|
||||||
|
m map[string]*fileInMemory
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFsInMemory returns an instance of fsInMemory with no files in it.
|
||||||
|
func MakeFsInMemory() FileSystem {
|
||||||
|
result := &fsInMemory{m: map[string]*fileInMemory{}}
|
||||||
|
result.Mkdir(separator)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
separator = string(filepath.Separator)
|
||||||
|
doubleSep = separator + separator
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create assures a fake file appears in the in-memory file system.
|
||||||
|
func (fs *fsInMemory) Create(name string) (File, error) {
|
||||||
|
f := &fileInMemory{}
|
||||||
|
f.open = true
|
||||||
|
fs.m[name] = f
|
||||||
|
return fs.m[name], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mkdir assures a fake directory appears in the in-memory file system.
|
||||||
|
func (fs *fsInMemory) Mkdir(name string) error {
|
||||||
|
fs.m[name] = makeDir(name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAll delegates to Mkdir
|
||||||
|
func (fs *fsInMemory) MkdirAll(name string) error {
|
||||||
|
return fs.Mkdir(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAll presumably does rm -r on a path.
|
||||||
|
// There's no error.
|
||||||
|
func (fs *fsInMemory) RemoveAll(name string) error {
|
||||||
|
var toRemove []string
|
||||||
|
for k := range fs.m {
|
||||||
|
if strings.HasPrefix(k, name) {
|
||||||
|
toRemove = append(toRemove, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, k := range toRemove {
|
||||||
|
delete(fs.m, k)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open returns a fake file in the open state.
|
||||||
|
func (fs *fsInMemory) Open(name string) (File, error) {
|
||||||
|
if _, found := fs.m[name]; !found {
|
||||||
|
return nil, fmt.Errorf("file %q cannot be opened", name)
|
||||||
|
}
|
||||||
|
return fs.m[name], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanedAbs cannot fail.
|
||||||
|
func (fs *fsInMemory) CleanedAbs(path string) (ConfirmedDir, string, error) {
|
||||||
|
if fs.IsDir(path) {
|
||||||
|
return ConfirmedDir(path), "", nil
|
||||||
|
}
|
||||||
|
d := filepath.Dir(path)
|
||||||
|
if d == path {
|
||||||
|
return ConfirmedDir(d), "", nil
|
||||||
|
}
|
||||||
|
return ConfirmedDir(d), filepath.Base(path), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exists returns true if file is known.
|
||||||
|
func (fs *fsInMemory) Exists(name string) bool {
|
||||||
|
_, found := fs.m[name]
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glob returns the list of matching files
|
||||||
|
func (fs *fsInMemory) 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 *fsInMemory) IsDir(name string) bool {
|
||||||
|
f, found := fs.m[name]
|
||||||
|
if found && f.dir {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(name, separator) {
|
||||||
|
name = name + separator
|
||||||
|
}
|
||||||
|
for k := range fs.m {
|
||||||
|
if strings.HasPrefix(k, name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFile always returns an empty bytes and error depending on content of m.
|
||||||
|
func (fs *fsInMemory) ReadFile(name string) ([]byte, error) {
|
||||||
|
if ff, found := fs.m[name]; found {
|
||||||
|
return ff.content, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("cannot read file %q", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile always succeeds and does nothing.
|
||||||
|
func (fs *fsInMemory) WriteFile(name string, c []byte) error {
|
||||||
|
ff := &fileInMemory{}
|
||||||
|
ff.Write(c)
|
||||||
|
fs.m[name] = ff
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk implements filepath.Walk using the fake filesystem.
|
||||||
|
func (fs *fsInMemory) Walk(path string, walkFn filepath.WalkFunc) error {
|
||||||
|
info, err := fs.lstat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = walkFn(path, info, err)
|
||||||
|
} else {
|
||||||
|
err = fs.walk(path, info, walkFn)
|
||||||
|
}
|
||||||
|
if err == filepath.SkipDir {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *fsInMemory) pathMatch(path, pattern string) bool {
|
||||||
|
match, _ := filepath.Match(pattern, path)
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *fsInMemory) lstat(path string) (*fileInfo, error) {
|
||||||
|
f, found := fs.m[path]
|
||||||
|
if !found {
|
||||||
|
return nil, os.ErrNotExist
|
||||||
|
}
|
||||||
|
return &fileInfo{f}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *fsInMemory) join(elem ...string) string {
|
||||||
|
for i, e := range elem {
|
||||||
|
if e != "" {
|
||||||
|
return strings.Replace(
|
||||||
|
strings.Join(elem[i:], separator), doubleSep, separator, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *fsInMemory) readDirNames(path string) []string {
|
||||||
|
var names []string
|
||||||
|
if !strings.HasSuffix(path, separator) {
|
||||||
|
path += separator
|
||||||
|
}
|
||||||
|
pathSegments := strings.Count(path, separator)
|
||||||
|
for name := range fs.m {
|
||||||
|
if name == path {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Count(name, separator) > pathSegments {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(name, path) {
|
||||||
|
names = append(names, filepath.Base(name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *fsInMemory) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
|
||||||
|
if !info.IsDir() {
|
||||||
|
return walkFn(path, info, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
names := fs.readDirNames(path)
|
||||||
|
if err := walkFn(path, info, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
filename := fs.join(path, name)
|
||||||
|
fileInfo, err := fs.lstat(filename)
|
||||||
|
if err != nil {
|
||||||
|
if err := walkFn(filename, fileInfo, os.ErrNotExist); err != nil && err != filepath.SkipDir {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fs.walk(filename, fileInfo, walkFn)
|
||||||
|
if err != nil {
|
||||||
|
if !fileInfo.IsDir() || err != filepath.SkipDir {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
149
api/filesys/fsinmemory_test.go
Normal file
149
api/filesys/fsinmemory_test.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExists(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
if fSys.Exists("foo") {
|
||||||
|
t.Fatalf("expected no foo")
|
||||||
|
}
|
||||||
|
fSys.Mkdir("/")
|
||||||
|
if !fSys.IsDir("/") {
|
||||||
|
t.Fatalf("expected dir at /")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsDir(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
expectedName := "my-dir"
|
||||||
|
err := fSys.Mkdir(expectedName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
shouldExist(t, fSys, expectedName)
|
||||||
|
if !fSys.IsDir(expectedName) {
|
||||||
|
t.Fatalf(expectedName + " should be a dir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldExist(t *testing.T, fSys FileSystem, name string) {
|
||||||
|
if !fSys.Exists(name) {
|
||||||
|
t.Fatalf(name + " should exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldNotExist(t *testing.T, fSys FileSystem, name string) {
|
||||||
|
if fSys.Exists(name) {
|
||||||
|
t.Fatalf(name + " should not exist")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveAll(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
fSys.WriteFile("/foo/project/file.yaml", []byte("Unused"))
|
||||||
|
fSys.WriteFile("/foo/project/subdir/file.yaml", []byte("Unused"))
|
||||||
|
fSys.WriteFile("/foo/apple/subdir/file.yaml", []byte("Unused"))
|
||||||
|
shouldExist(t, fSys, "/foo/project/file.yaml")
|
||||||
|
shouldExist(t, fSys, "/foo/project/subdir/file.yaml")
|
||||||
|
shouldExist(t, fSys, "/foo/apple/subdir/file.yaml")
|
||||||
|
err := fSys.RemoveAll("/foo/project")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
shouldNotExist(t, fSys, "/foo/project/file.yaml")
|
||||||
|
shouldNotExist(t, fSys, "/foo/project/subdir/file.yaml")
|
||||||
|
shouldExist(t, fSys, "/foo/apple/subdir/file.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsDirDeeper(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
fSys.WriteFile("/foo/project/file.yaml", []byte("Unused"))
|
||||||
|
fSys.WriteFile("/foo/project/subdir/file.yaml", []byte("Unused"))
|
||||||
|
if !fSys.IsDir("/") {
|
||||||
|
t.Fatalf("/ should be a dir")
|
||||||
|
}
|
||||||
|
if !fSys.IsDir("/foo") {
|
||||||
|
t.Fatalf("/foo should be a dir")
|
||||||
|
}
|
||||||
|
if !fSys.IsDir("/foo/project") {
|
||||||
|
t.Fatalf("/foo/project should be a dir")
|
||||||
|
}
|
||||||
|
if fSys.IsDir("/fo") {
|
||||||
|
t.Fatalf("/fo should not be a dir")
|
||||||
|
}
|
||||||
|
if fSys.IsDir("/x") {
|
||||||
|
t.Fatalf("/x should not be a dir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreate(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
f, err := fSys.Create("foo")
|
||||||
|
if f == nil {
|
||||||
|
t.Fatalf("expected file")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error")
|
||||||
|
}
|
||||||
|
shouldExist(t, fSys, "foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadFile(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
f, err := fSys.Create("foo")
|
||||||
|
if f == nil {
|
||||||
|
t.Fatalf("expected file")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error")
|
||||||
|
}
|
||||||
|
content, err := fSys.ReadFile("foo")
|
||||||
|
if len(content) != 0 {
|
||||||
|
t.Fatalf("expected no content")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteFile(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
c := []byte("heybuddy")
|
||||||
|
err := fSys.WriteFile("foo", c)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error")
|
||||||
|
}
|
||||||
|
content, err := fSys.ReadFile("foo")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected read to work: %v", err)
|
||||||
|
}
|
||||||
|
if bytes.Compare(c, content) != 0 {
|
||||||
|
t.Fatalf("incorrect content: %v", content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGlob(t *testing.T) {
|
||||||
|
fSys := MakeFsInMemory()
|
||||||
|
fSys.Create("dir/foo")
|
||||||
|
fSys.Create("dir/bar")
|
||||||
|
files, err := fSys.Glob("dir/*")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error")
|
||||||
|
}
|
||||||
|
expected := []string{
|
||||||
|
"dir/bar",
|
||||||
|
"dir/foo",
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(files, expected) {
|
||||||
|
t.Fatalf("incorrect files found by glob: %v", files)
|
||||||
|
}
|
||||||
|
}
|
||||||
114
api/filesys/fsondisk.go
Normal file
114
api/filesys/fsondisk.go
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ FileSystem = fsOnDisk{}
|
||||||
|
|
||||||
|
// fsOnDisk implements FileSystem using the local filesystem.
|
||||||
|
type fsOnDisk struct{}
|
||||||
|
|
||||||
|
// MakeFsOnDisk makes an instance of fsOnDisk.
|
||||||
|
func MakeFsOnDisk() FileSystem {
|
||||||
|
return fsOnDisk{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create delegates to os.Create.
|
||||||
|
func (fsOnDisk) Create(name string) (File, error) { return os.Create(name) }
|
||||||
|
|
||||||
|
// Mkdir delegates to os.Mkdir.
|
||||||
|
func (fsOnDisk) Mkdir(name string) error {
|
||||||
|
return os.Mkdir(name, 0777|os.ModeDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAll delegates to os.MkdirAll.
|
||||||
|
func (fsOnDisk) MkdirAll(name string) error {
|
||||||
|
return os.MkdirAll(name, 0777|os.ModeDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAll delegates to os.RemoveAll.
|
||||||
|
func (fsOnDisk) RemoveAll(name string) error {
|
||||||
|
return os.RemoveAll(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open delegates to os.Open.
|
||||||
|
func (fsOnDisk) Open(name string) (File, error) { return os.Open(name) }
|
||||||
|
|
||||||
|
// CleanedAbs converts the given path into a
|
||||||
|
// directory and a file name, where the directory
|
||||||
|
// is represented as a ConfirmedDir and all that implies.
|
||||||
|
// If the entire path is a directory, the file component
|
||||||
|
// is an empty string.
|
||||||
|
func (x fsOnDisk) CleanedAbs(
|
||||||
|
path string) (ConfirmedDir, string, error) {
|
||||||
|
absRoot, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf(
|
||||||
|
"abs path error on '%s' : %v", path, err)
|
||||||
|
}
|
||||||
|
deLinked, err := filepath.EvalSymlinks(absRoot)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", fmt.Errorf(
|
||||||
|
"evalsymlink failure on '%s' : %v", path, err)
|
||||||
|
}
|
||||||
|
if x.IsDir(deLinked) {
|
||||||
|
return ConfirmedDir(deLinked), "", nil
|
||||||
|
}
|
||||||
|
d := filepath.Dir(deLinked)
|
||||||
|
if !x.IsDir(d) {
|
||||||
|
// Programmer/assumption error.
|
||||||
|
log.Fatalf("first part of '%s' not a directory", deLinked)
|
||||||
|
}
|
||||||
|
if d == deLinked {
|
||||||
|
// Programmer/assumption error.
|
||||||
|
log.Fatalf("d '%s' should be a subset of deLinked", d)
|
||||||
|
}
|
||||||
|
f := filepath.Base(deLinked)
|
||||||
|
if filepath.Join(d, f) != deLinked {
|
||||||
|
// Programmer/assumption error.
|
||||||
|
log.Fatalf("these should be equal: '%s', '%s'",
|
||||||
|
filepath.Join(d, f), deLinked)
|
||||||
|
}
|
||||||
|
return ConfirmedDir(d), f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exists returns true if os.Stat succeeds.
|
||||||
|
func (fsOnDisk) Exists(name string) bool {
|
||||||
|
_, err := os.Stat(name)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Glob returns the list of matching files
|
||||||
|
func (fsOnDisk) Glob(pattern string) ([]string, error) {
|
||||||
|
return filepath.Glob(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDir delegates to os.Stat and FileInfo.IsDir
|
||||||
|
func (fsOnDisk) IsDir(name string) bool {
|
||||||
|
info, err := os.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFile delegates to ioutil.ReadFile.
|
||||||
|
func (fsOnDisk) ReadFile(name string) ([]byte, error) { return ioutil.ReadFile(name) }
|
||||||
|
|
||||||
|
// WriteFile delegates to ioutil.WriteFile with read/write permissions.
|
||||||
|
func (fsOnDisk) WriteFile(name string, c []byte) error {
|
||||||
|
return ioutil.WriteFile(name, c, 0666)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk delegates to filepath.Walk.
|
||||||
|
func (fsOnDisk) Walk(path string, walkFn filepath.WalkFunc) error {
|
||||||
|
return filepath.Walk(path, walkFn)
|
||||||
|
}
|
||||||
165
api/filesys/fsondisk_test.go
Normal file
165
api/filesys/fsondisk_test.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeTestDir(t *testing.T) (FileSystem, string) {
|
||||||
|
fSys := MakeFsOnDisk()
|
||||||
|
td, err := ioutil.TempDir("", "kustomize_testing_dir")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %s", err)
|
||||||
|
}
|
||||||
|
testDir, err := filepath.EvalSymlinks(td)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %s", err)
|
||||||
|
}
|
||||||
|
if !fSys.Exists(testDir) {
|
||||||
|
t.Fatalf("expected existence")
|
||||||
|
}
|
||||||
|
if !fSys.IsDir(testDir) {
|
||||||
|
t.Fatalf("expected directory")
|
||||||
|
}
|
||||||
|
return fSys, testDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCleanedAbs_1(t *testing.T) {
|
||||||
|
fSys, testDir := makeTestDir(t)
|
||||||
|
defer os.RemoveAll(testDir)
|
||||||
|
|
||||||
|
d, f, err := fSys.CleanedAbs("")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
if d.String() != wd {
|
||||||
|
t.Fatalf("unexpected d=%s", d)
|
||||||
|
}
|
||||||
|
if f != "" {
|
||||||
|
t.Fatalf("unexpected f=%s", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCleanedAbs_2(t *testing.T) {
|
||||||
|
fSys, testDir := makeTestDir(t)
|
||||||
|
defer os.RemoveAll(testDir)
|
||||||
|
|
||||||
|
d, f, err := fSys.CleanedAbs("/")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
if d != "/" {
|
||||||
|
t.Fatalf("unexpected d=%s", d)
|
||||||
|
}
|
||||||
|
if f != "" {
|
||||||
|
t.Fatalf("unexpected f=%s", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCleanedAbs_3(t *testing.T) {
|
||||||
|
fSys, testDir := makeTestDir(t)
|
||||||
|
defer os.RemoveAll(testDir)
|
||||||
|
|
||||||
|
err := fSys.WriteFile(
|
||||||
|
filepath.Join(testDir, "foo"), []byte(`foo`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d, f, err := fSys.CleanedAbs(filepath.Join(testDir, "foo"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
if d.String() != testDir {
|
||||||
|
t.Fatalf("unexpected d=%s", d)
|
||||||
|
}
|
||||||
|
if f != "foo" {
|
||||||
|
t.Fatalf("unexpected f=%s", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCleanedAbs_4(t *testing.T) {
|
||||||
|
fSys, testDir := makeTestDir(t)
|
||||||
|
defer os.RemoveAll(testDir)
|
||||||
|
|
||||||
|
err := fSys.MkdirAll(filepath.Join(testDir, "d1", "d2"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
err = fSys.WriteFile(
|
||||||
|
filepath.Join(testDir, "d1", "d2", "bar"),
|
||||||
|
[]byte(`bar`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d, f, err := fSys.CleanedAbs(
|
||||||
|
filepath.Join(testDir, "d1", "d2"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
if d.String() != filepath.Join(testDir, "d1", "d2") {
|
||||||
|
t.Fatalf("unexpected d=%s", d)
|
||||||
|
}
|
||||||
|
if f != "" {
|
||||||
|
t.Fatalf("unexpected f=%s", f)
|
||||||
|
}
|
||||||
|
|
||||||
|
d, f, err = fSys.CleanedAbs(
|
||||||
|
filepath.Join(testDir, "d1", "d2", "bar"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err=%v", err)
|
||||||
|
}
|
||||||
|
if d.String() != filepath.Join(testDir, "d1", "d2") {
|
||||||
|
t.Fatalf("unexpected d=%s", d)
|
||||||
|
}
|
||||||
|
if f != "bar" {
|
||||||
|
t.Fatalf("unexpected f=%s", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadFilesRealFS(t *testing.T) {
|
||||||
|
fSys, testDir := makeTestDir(t)
|
||||||
|
defer os.RemoveAll(testDir)
|
||||||
|
|
||||||
|
err := fSys.WriteFile(path.Join(testDir, "foo"), []byte(`foo`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %s", err)
|
||||||
|
}
|
||||||
|
if !fSys.Exists(path.Join(testDir, "foo")) {
|
||||||
|
t.Fatalf("expected foo")
|
||||||
|
}
|
||||||
|
if fSys.IsDir(path.Join(testDir, "foo")) {
|
||||||
|
t.Fatalf("expected foo not to be a directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fSys.WriteFile(path.Join(testDir, "bar"), []byte(`bar`))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := fSys.Glob(path.Join("testDir", "*"))
|
||||||
|
expected := []string{
|
||||||
|
path.Join(testDir, "bar"),
|
||||||
|
path.Join(testDir, "foo"),
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected no error")
|
||||||
|
}
|
||||||
|
if reflect.DeepEqual(files, expected) {
|
||||||
|
t.Fatalf("incorrect files found by glob: %v", files)
|
||||||
|
}
|
||||||
|
}
|
||||||
12
api/filesys/rpath.go
Normal file
12
api/filesys/rpath.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package filesys
|
||||||
|
|
||||||
|
import "path/filepath"
|
||||||
|
|
||||||
|
// RootedPath returns a rooted path, e.g. "/foo/bar" as
|
||||||
|
// opposed to "foo/bar".
|
||||||
|
func RootedPath(elem ...string) string {
|
||||||
|
return separator + filepath.Join(elem...)
|
||||||
|
}
|
||||||
18
api/go.mod
Normal file
18
api/go.mod
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
module sigs.k8s.io/kustomize/api
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/emicklei/go-restful v2.9.6+incompatible // indirect
|
||||||
|
github.com/evanphx/json-patch v4.5.0+incompatible
|
||||||
|
github.com/go-openapi/spec v0.19.4
|
||||||
|
github.com/golangci/golangci-lint v1.19.1
|
||||||
|
github.com/googleapis/gnostic v0.3.0 // indirect
|
||||||
|
github.com/monopole/mdrip v1.0.0
|
||||||
|
github.com/pkg/errors v0.8.1
|
||||||
|
golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678
|
||||||
|
gopkg.in/yaml.v2 v2.2.4
|
||||||
|
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
|
||||||
|
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0
|
||||||
|
sigs.k8s.io/yaml v1.1.0
|
||||||
|
)
|
||||||
463
api/go.sum
Normal file
463
api/go.sum
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||||
|
github.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||||
|
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||||
|
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||||
|
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||||
|
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||||
|
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||||
|
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us=
|
||||||
|
github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM=
|
||||||
|
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
|
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||||
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||||
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/docker/spdystream v0.0.0-20181023171402-6480d4af844c/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||||
|
github.com/elazarl/goproxy v0.0.0-20191011121108-aa519ddbe484/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||||
|
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
|
||||||
|
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
|
github.com/emicklei/go-restful v2.9.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1/igzWB8OmvPgoYX7w=
|
||||||
|
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||||
|
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
|
||||||
|
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db h1:GYXWx7Vr3+zv833u+8IoXbNnQY0AdXsxAgI0kX7xcwA=
|
||||||
|
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||||
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0=
|
||||||
|
github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM=
|
||||||
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
|
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||||
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
|
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||||
|
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
|
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
|
||||||
|
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||||
|
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||||
|
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||||
|
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||||
|
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||||
|
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||||
|
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||||
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
|
||||||
|
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
||||||
|
github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
|
||||||
|
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
|
||||||
|
github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
||||||
|
github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ=
|
||||||
|
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
|
||||||
|
github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg=
|
||||||
|
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
|
||||||
|
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
|
||||||
|
github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU=
|
||||||
|
github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk=
|
||||||
|
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
|
||||||
|
github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI=
|
||||||
|
github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks=
|
||||||
|
github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg=
|
||||||
|
github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc=
|
||||||
|
github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4=
|
||||||
|
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
|
||||||
|
github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA=
|
||||||
|
github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU=
|
||||||
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
|
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||||
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0=
|
||||||
|
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4=
|
||||||
|
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
|
||||||
|
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||||
|
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w=
|
||||||
|
github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0=
|
||||||
|
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw=
|
||||||
|
github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8=
|
||||||
|
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8=
|
||||||
|
github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o=
|
||||||
|
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8=
|
||||||
|
github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU=
|
||||||
|
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98 h1:0OkFarm1Zy2CjCiDKfK9XHgmc2wbDlRMD2hD8anAJHU=
|
||||||
|
github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU=
|
||||||
|
github.com/golangci/golangci-lint v1.19.1 h1:g9xL8KW7UZDCkVlgHYJMA6F4Sj/sRVa0FoCeXI+Z3iM=
|
||||||
|
github.com/golangci/golangci-lint v1.19.1/go.mod h1:2CEc4Fxx3vxDv7g8DyXkHCBF73AOzAymcJAprs2vCps=
|
||||||
|
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI=
|
||||||
|
github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU=
|
||||||
|
github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217 h1:En/tZdwhAn0JNwLuXzP3k2RVtMqMmOEK7Yu/g3tmtJE=
|
||||||
|
github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg=
|
||||||
|
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA=
|
||||||
|
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o=
|
||||||
|
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk=
|
||||||
|
github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA=
|
||||||
|
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us=
|
||||||
|
github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI=
|
||||||
|
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg=
|
||||||
|
github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4=
|
||||||
|
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys=
|
||||||
|
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ=
|
||||||
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||||
|
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||||
|
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
|
||||||
|
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||||
|
github.com/gophercloud/gophercloud v0.6.0/go.mod h1:GICNByuaEBibcjmjvI7QvYJSZEbGkcYwAR7EZK2WMqM=
|
||||||
|
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f h1:9oNbS1z4rVpbnkHBdPZU4jo9bSmrLpII768arSyMFgk=
|
||||||
|
github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
|
github.com/gorilla/mux v1.6.0 h1:UykbtMB/w5No2LmE16gINgLj+r/vbziTgaoERQv6U+0=
|
||||||
|
github.com/gorilla/mux v1.6.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/gorilla/securecookie v0.0.0-20160422134519-667fe4e3466a h1:YH0IojQwndMQdeRWdw1aPT8bkbiWaYR3WD+Zf5e09DU=
|
||||||
|
github.com/gorilla/securecookie v0.0.0-20160422134519-667fe4e3466a/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||||
|
github.com/gorilla/sessions v0.0.0-20160922145804-ca9ada445741 h1:OuuPl66BpF1q3OEkaPpp+VfzxrBBY62ATGdWqql/XX8=
|
||||||
|
github.com/gorilla/sessions v0.0.0-20160922145804-ca9ada445741/go.mod h1:+WVp8kdw6VhyKExm03PAMRn2ZxnPtm58pV0dBVPdhHE=
|
||||||
|
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw=
|
||||||
|
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||||
|
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||||
|
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
||||||
|
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
|
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
|
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
|
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||||
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
|
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||||
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/matoous/godox v0.0.0-20190910121045-032ad8106c86 h1:q6SrfsK4FojRnJ1j8+8OJzyq3g9Y1oSVyL6nYGJXXBk=
|
||||||
|
github.com/matoous/godox v0.0.0-20190910121045-032ad8106c86/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||||
|
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||||
|
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||||
|
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||||
|
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/monopole/mdrip v1.0.0 h1:RFDBa+tab6mW+gX4Ww2SZDc4kS6p01FwnLtgz64Il+I=
|
||||||
|
github.com/monopole/mdrip v1.0.0/go.mod h1:N1/ppRG9CaPeUKAUHZ3dUlfOT81lTpKZLkyhCvTETwM=
|
||||||
|
github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
|
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E=
|
||||||
|
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||||
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
|
||||||
|
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
|
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||||
|
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||||
|
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
|
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||||
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||||
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
|
||||||
|
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/securego/gosec v0.0.0-20190912120752-140048b2a218 h1:O0yPHYL49quNL4Oj2wVq+zbGMu4dAM6iLoOQtm49TrQ=
|
||||||
|
github.com/securego/gosec v0.0.0-20190912120752-140048b2a218/go.mod h1:q6oYAujd2qyeU4cJqIri4LBIgdHXGvxWHZ1E29HNFRE=
|
||||||
|
github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc=
|
||||||
|
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||||
|
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e h1:MZM7FHLqUHYI0Y/mQAt3d2aYa0SiNms/hFqC9qJYolM=
|
||||||
|
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||||
|
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc=
|
||||||
|
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
|
github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs=
|
||||||
|
github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE=
|
||||||
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||||
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
|
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||||
|
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
|
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
|
||||||
|
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||||
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
|
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec h1:AmoEvWAO3nDx1MEcMzPh+GzOOIA5Znpv6++c7bePPY0=
|
||||||
|
github.com/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
|
||||||
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbdo=
|
||||||
|
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||||
|
github.com/ultraware/whitespace v0.0.3 h1:S5BCRRB5sttNy0bSOhbpw+0mb+cHiCmWfrvxpEzuUk0=
|
||||||
|
github.com/ultraware/whitespace v0.0.3/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
||||||
|
github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||||
|
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||||
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b h1:XfVGCX+0T4WOStkaOsJRllbsiImhB2jgVBGc9L0lPGc=
|
||||||
|
golang.org/x/net v0.0.0-20190909003024-a7b16738d86b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5 h1:SW/0nsKCUaozCUtZTakri5laocGx/5bkDSSLrFUsa5s=
|
||||||
|
golang.org/x/sys v0.0.0-20190911201528-7ad0cfa0b7b5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||||
|
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190911230505-6bfd74cf029c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678 h1:rM1Udd0CgtYI3KUIhu9ROz0QCqjW+n/ODp/hH7c60Xc=
|
||||||
|
golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||||
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||||
|
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||||
|
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||||
|
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||||
|
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
|
||||||
|
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
|
||||||
|
k8s.io/utils v0.0.0-20191030222137-2b95a09bc58d/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||||
|
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
|
||||||
|
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
|
||||||
|
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
|
||||||
|
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
|
||||||
|
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f h1:Cq7MalBHYACRd6EesksG1Q8EoIAKOsiZviGKbOLIej4=
|
||||||
|
mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw=
|
||||||
|
sigs.k8s.io/kustomize/pseudo/k8s v0.0.0-20191108212413-1f86a0ca5d6c h1:t7fk+ljA3Ru4pro+/0RuOAZcODDhByL1fvIdyHLhjTY=
|
||||||
|
sigs.k8s.io/kustomize/pseudo/k8s v0.0.0-20191108212413-1f86a0ca5d6c/go.mod h1:bl/gVJgYYhJZCZdYU2BfnaKYAlqFkgbJEkpl302jEss=
|
||||||
|
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0 h1:otg4dLFc03c3gzl+2CV8GPGcd1kk8wjXwD+UhhcCn5I=
|
||||||
|
sigs.k8s.io/kustomize/pseudo/k8s v0.1.0/go.mod h1:bl/gVJgYYhJZCZdYU2BfnaKYAlqFkgbJEkpl302jEss=
|
||||||
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||||
|
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||||
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c=
|
||||||
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||||
52
api/hasher/hasher.go
Normal file
52
api/hasher/hasher.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package hasher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SortArrayAndComputeHash sorts a string array and
|
||||||
|
// returns a hash for it
|
||||||
|
func SortArrayAndComputeHash(s []string) (string, error) {
|
||||||
|
sort.Strings(s)
|
||||||
|
data, err := json.Marshal(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return Encode(Hash(string(data)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from https://github.com/kubernetes/kubernetes
|
||||||
|
// /blob/master/pkg/kubectl/util/hash/hash.go
|
||||||
|
func Encode(hex string) (string, error) {
|
||||||
|
if len(hex) < 10 {
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"input length must be at least 10")
|
||||||
|
}
|
||||||
|
enc := []rune(hex[:10])
|
||||||
|
for i := range enc {
|
||||||
|
switch enc[i] {
|
||||||
|
case '0':
|
||||||
|
enc[i] = 'g'
|
||||||
|
case '1':
|
||||||
|
enc[i] = 'h'
|
||||||
|
case '3':
|
||||||
|
enc[i] = 'k'
|
||||||
|
case 'a':
|
||||||
|
enc[i] = 'm'
|
||||||
|
case 'e':
|
||||||
|
enc[i] = 't'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(enc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the hex form of the sha256 of the argument.
|
||||||
|
func Hash(data string) string {
|
||||||
|
return fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
|
||||||
|
}
|
||||||
41
api/hasher/hasher_test.go
Normal file
41
api/hasher/hasher_test.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package hasher_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/hasher"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSortArrayAndComputeHash(t *testing.T) {
|
||||||
|
array1 := []string{"a", "b", "c", "d"}
|
||||||
|
array2 := []string{"c", "b", "d", "a"}
|
||||||
|
h1, err := SortArrayAndComputeHash(array1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
if h1 == "" {
|
||||||
|
t.Errorf("failed to hash %v", array1)
|
||||||
|
}
|
||||||
|
h2, err := SortArrayAndComputeHash(array2)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
if h2 == "" {
|
||||||
|
t.Errorf("failed to hash %v", array2)
|
||||||
|
}
|
||||||
|
if h1 != h2 {
|
||||||
|
t.Errorf("hash is not consistent with reordered list: %s %s", h1, h2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHash(t *testing.T) {
|
||||||
|
// hash the empty string to be sure that sha256 is being used
|
||||||
|
expect := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||||
|
sum := Hash("")
|
||||||
|
if expect != sum {
|
||||||
|
t.Errorf("expected hash %q but got %q", expect, sum)
|
||||||
|
}
|
||||||
|
}
|
||||||
95
api/ifc/ifc.go
Normal file
95
api/ifc/ifc.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package ifc holds miscellaneous interfaces used by kustomize.
|
||||||
|
package ifc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validator provides functions to validate annotations and labels
|
||||||
|
type Validator interface {
|
||||||
|
MakeAnnotationValidator() func(map[string]string) error
|
||||||
|
MakeAnnotationNameValidator() func([]string) error
|
||||||
|
MakeLabelValidator() func(map[string]string) error
|
||||||
|
MakeLabelNameValidator() func([]string) error
|
||||||
|
ValidateNamespace(string) []string
|
||||||
|
ErrIfInvalidKey(string) error
|
||||||
|
IsEnvVarName(k string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// KvLoader reads and validates KV pairs.
|
||||||
|
type KvLoader interface {
|
||||||
|
Validator() Validator
|
||||||
|
Load(args types.KvPairSources) (all []types.Pair, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loader interface exposes methods to read bytes.
|
||||||
|
type Loader interface {
|
||||||
|
// Root returns the root location for this Loader.
|
||||||
|
Root() string
|
||||||
|
// New returns Loader located at newRoot.
|
||||||
|
New(newRoot string) (Loader, error)
|
||||||
|
// Load returns the bytes read from the location or an error.
|
||||||
|
Load(location string) ([]byte, error)
|
||||||
|
// Cleanup cleans the loader
|
||||||
|
Cleanup() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kunstructured allows manipulation of k8s objects
|
||||||
|
// that do not have Golang structs.
|
||||||
|
type Kunstructured interface {
|
||||||
|
Map() map[string]interface{}
|
||||||
|
SetMap(map[string]interface{})
|
||||||
|
Copy() Kunstructured
|
||||||
|
GetFieldValue(string) (interface{}, error)
|
||||||
|
GetString(string) (string, error)
|
||||||
|
GetStringSlice(string) ([]string, error)
|
||||||
|
GetBool(path string) (bool, error)
|
||||||
|
GetFloat64(path string) (float64, error)
|
||||||
|
GetInt64(path string) (int64, error)
|
||||||
|
GetSlice(path string) ([]interface{}, error)
|
||||||
|
GetStringMap(path string) (map[string]string, error)
|
||||||
|
GetMap(path string) (map[string]interface{}, error)
|
||||||
|
MarshalJSON() ([]byte, error)
|
||||||
|
UnmarshalJSON([]byte) error
|
||||||
|
GetGvk() resid.Gvk
|
||||||
|
SetGvk(resid.Gvk)
|
||||||
|
GetKind() string
|
||||||
|
GetName() string
|
||||||
|
SetName(string)
|
||||||
|
SetNamespace(string)
|
||||||
|
GetLabels() map[string]string
|
||||||
|
SetLabels(map[string]string)
|
||||||
|
GetAnnotations() map[string]string
|
||||||
|
SetAnnotations(map[string]string)
|
||||||
|
MatchesLabelSelector(selector string) (bool, error)
|
||||||
|
MatchesAnnotationSelector(selector string) (bool, error)
|
||||||
|
Patch(Kunstructured) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// KunstructuredFactory makes instances of Kunstructured.
|
||||||
|
type KunstructuredFactory interface {
|
||||||
|
SliceFromBytes([]byte) ([]Kunstructured, error)
|
||||||
|
FromMap(m map[string]interface{}) Kunstructured
|
||||||
|
Hasher() KunstructuredHasher
|
||||||
|
MakeConfigMap(
|
||||||
|
kvLdr KvLoader,
|
||||||
|
options *types.GeneratorOptions,
|
||||||
|
args *types.ConfigMapArgs) (Kunstructured, error)
|
||||||
|
MakeSecret(
|
||||||
|
kvLdr KvLoader,
|
||||||
|
options *types.GeneratorOptions,
|
||||||
|
args *types.SecretArgs) (Kunstructured, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KunstructuredHasher returns a hash of the argument
|
||||||
|
// or an error.
|
||||||
|
type KunstructuredHasher interface {
|
||||||
|
Hash(Kunstructured) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See core.v1.SecretTypeOpaque
|
||||||
|
const SecretTypeOpaque = "Opaque"
|
||||||
@@ -1,24 +1,12 @@
|
|||||||
/*
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
Copyright 2018 The Kubernetes Authors.
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Package expansion provides functions find and replace $(FOO) style variables in strings.
|
// Package expansion provides functions find and replace $(FOO) style variables in strings.
|
||||||
package expansion
|
package expansion
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -36,15 +24,22 @@ func syntaxWrap(input string) string {
|
|||||||
// implements the expansion semantics defined in the expansion spec; it
|
// implements the expansion semantics defined in the expansion spec; it
|
||||||
// returns the input string wrapped in the expansion syntax if no mapping
|
// returns the input string wrapped in the expansion syntax if no mapping
|
||||||
// for the input is found.
|
// for the input is found.
|
||||||
func MappingFuncFor(context ...map[string]string) func(string) string {
|
func MappingFuncFor(
|
||||||
return func(input string) string {
|
counts map[string]int,
|
||||||
|
context ...map[string]interface{}) func(string) interface{} {
|
||||||
|
return func(input string) interface{} {
|
||||||
for _, vars := range context {
|
for _, vars := range context {
|
||||||
val, ok := vars[input]
|
val, ok := vars[input]
|
||||||
if ok {
|
if ok {
|
||||||
return val
|
counts[input]++
|
||||||
|
switch typedV := val.(type) {
|
||||||
|
case string, int64, float64, bool:
|
||||||
|
return typedV
|
||||||
|
default:
|
||||||
|
return syntaxWrap(input)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return syntaxWrap(input)
|
return syntaxWrap(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +47,7 @@ func MappingFuncFor(context ...map[string]string) func(string) string {
|
|||||||
// Expand replaces variable references in the input string according to
|
// Expand replaces variable references in the input string according to
|
||||||
// the expansion spec using the given mapping function to resolve the
|
// the expansion spec using the given mapping function to resolve the
|
||||||
// values of variables.
|
// values of variables.
|
||||||
func Expand(input string, mapping func(string) string) string {
|
func Expand(input string, mapping func(string) interface{}) interface{} {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
checkpoint := 0
|
checkpoint := 0
|
||||||
for cursor := 0; cursor < len(input); cursor++ {
|
for cursor := 0; cursor < len(input); cursor++ {
|
||||||
@@ -69,7 +64,14 @@ func Expand(input string, mapping func(string) string) string {
|
|||||||
// We were able to read a variable name correctly;
|
// We were able to read a variable name correctly;
|
||||||
// apply the mapping to the variable name and copy the
|
// apply the mapping to the variable name and copy the
|
||||||
// bytes into the buffer
|
// bytes into the buffer
|
||||||
buf.WriteString(mapping(read))
|
mapped := mapping(read)
|
||||||
|
if input == syntaxWrap(read) {
|
||||||
|
// Preserve the type of variable
|
||||||
|
return mapped
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable is used in a middle of a string
|
||||||
|
buf.WriteString(fmt.Sprintf("%v", mapped))
|
||||||
} else {
|
} else {
|
||||||
// Not a variable name; copy the read bytes into the buffer
|
// Not a variable name; copy the read bytes into the buffer
|
||||||
buf.WriteString(read)
|
buf.WriteString(read)
|
||||||
365
api/internal/accumulator/expansion/expand_test.go
Normal file
365
api/internal/accumulator/expansion/expand_test.go
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package expansion_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
|
||||||
|
)
|
||||||
|
|
||||||
|
type expected struct {
|
||||||
|
count int
|
||||||
|
edited string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapReference(t *testing.T) {
|
||||||
|
type env struct {
|
||||||
|
Name string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
envs := []env{
|
||||||
|
{
|
||||||
|
Name: "FOO",
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "ZOO",
|
||||||
|
Value: "$(FOO)-1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "BLU",
|
||||||
|
Value: "$(ZOO)-2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "INT",
|
||||||
|
Value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "ZINT",
|
||||||
|
Value: "$(INT)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "BOOL",
|
||||||
|
Value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "ZBOOL",
|
||||||
|
Value: "$(BOOL)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
declaredEnv := map[string]interface{}{
|
||||||
|
"FOO": "bar",
|
||||||
|
"ZOO": "$(FOO)-1",
|
||||||
|
"BLU": "$(ZOO)-2",
|
||||||
|
"INT": "2",
|
||||||
|
"ZINT": "$(INT)",
|
||||||
|
"BOOL": "true",
|
||||||
|
"ZBOOL": "$(BOOL)",
|
||||||
|
}
|
||||||
|
|
||||||
|
counts := make(map[string]int)
|
||||||
|
mapping := MappingFuncFor(counts, declaredEnv)
|
||||||
|
|
||||||
|
for _, env := range envs {
|
||||||
|
declaredEnv[env.Name] = Expand(fmt.Sprintf("%v", env.Value), mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedEnv := map[string]expected{
|
||||||
|
"FOO": {count: 1, edited: "bar"},
|
||||||
|
"ZOO": {count: 1, edited: "bar-1"},
|
||||||
|
"BLU": {count: 0, edited: "bar-1-2"},
|
||||||
|
"INT": {count: 1, edited: "2"},
|
||||||
|
"ZINT": {count: 0, edited: "2"},
|
||||||
|
"BOOL": {count: 1, edited: "true"},
|
||||||
|
"ZBOOL": {count: 0, edited: "true"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range expectedEnv {
|
||||||
|
if e, a := v, declaredEnv[k]; e.edited != a || e.count != counts[k] {
|
||||||
|
t.Errorf("Expected %v count=%d, got %v count=%d",
|
||||||
|
e.edited, e.count, a, counts[k])
|
||||||
|
} else {
|
||||||
|
delete(declaredEnv, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(declaredEnv) != 0 {
|
||||||
|
t.Errorf("Unexpected keys in declared env: %v", declaredEnv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMapping(t *testing.T) {
|
||||||
|
context := map[string]interface{}{
|
||||||
|
"VAR_A": "A",
|
||||||
|
"VAR_B": "B",
|
||||||
|
"VAR_C": "C",
|
||||||
|
"VAR_REF": "$(VAR_A)",
|
||||||
|
"VAR_EMPTY": "",
|
||||||
|
}
|
||||||
|
doExpansionTest(t, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMappingDual(t *testing.T) {
|
||||||
|
context := map[string]interface{}{
|
||||||
|
"VAR_A": "A",
|
||||||
|
"VAR_EMPTY": "",
|
||||||
|
}
|
||||||
|
context2 := map[string]interface{}{
|
||||||
|
"VAR_B": "B",
|
||||||
|
"VAR_C": "C",
|
||||||
|
"VAR_REF": "$(VAR_A)",
|
||||||
|
}
|
||||||
|
|
||||||
|
doExpansionTest(t, context, context2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doExpansionTest(t *testing.T, context ...map[string]interface{}) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
counts map[string]int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "whole string",
|
||||||
|
input: "$(VAR_A)",
|
||||||
|
expected: "A",
|
||||||
|
counts: map[string]int{"VAR_A": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "repeat",
|
||||||
|
input: "$(VAR_A)-$(VAR_A)",
|
||||||
|
expected: "A-A",
|
||||||
|
counts: map[string]int{"VAR_A": 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple repeats",
|
||||||
|
input: "$(VAR_A)-$(VAR_B)-$(VAR_B)-$(VAR_B)-$(VAR_A)",
|
||||||
|
expected: "A-B-B-B-A",
|
||||||
|
counts: map[string]int{"VAR_A": 2, "VAR_B": 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "beginning",
|
||||||
|
input: "$(VAR_A)-1",
|
||||||
|
expected: "A-1",
|
||||||
|
counts: map[string]int{"VAR_A": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "middle",
|
||||||
|
input: "___$(VAR_B)___",
|
||||||
|
expected: "___B___",
|
||||||
|
counts: map[string]int{"VAR_B": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "end",
|
||||||
|
input: "___$(VAR_C)",
|
||||||
|
expected: "___C",
|
||||||
|
counts: map[string]int{"VAR_C": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compound",
|
||||||
|
input: "$(VAR_A)_$(VAR_B)_$(VAR_C)",
|
||||||
|
expected: "A_B_C",
|
||||||
|
counts: map[string]int{"VAR_A": 1, "VAR_B": 1, "VAR_C": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "escape & expand",
|
||||||
|
input: "$$(VAR_B)_$(VAR_A)",
|
||||||
|
expected: "$(VAR_B)_A",
|
||||||
|
counts: map[string]int{"VAR_A": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compound escape",
|
||||||
|
input: "$$(VAR_A)_$$(VAR_B)",
|
||||||
|
expected: "$(VAR_A)_$(VAR_B)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mixed in escapes",
|
||||||
|
input: "f000-$$VAR_A",
|
||||||
|
expected: "f000-$VAR_A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "backslash escape ignored",
|
||||||
|
input: "foo\\$(VAR_C)bar",
|
||||||
|
expected: "foo\\Cbar",
|
||||||
|
counts: map[string]int{"VAR_C": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "backslash escape ignored",
|
||||||
|
input: "foo\\\\$(VAR_C)bar",
|
||||||
|
expected: "foo\\\\Cbar",
|
||||||
|
counts: map[string]int{"VAR_C": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lots of backslashes",
|
||||||
|
input: "foo\\\\\\\\$(VAR_A)bar",
|
||||||
|
expected: "foo\\\\\\\\Abar",
|
||||||
|
counts: map[string]int{"VAR_A": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested var references",
|
||||||
|
input: "$(VAR_A$(VAR_B))",
|
||||||
|
expected: "$(VAR_A$(VAR_B))",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested var references second type",
|
||||||
|
input: "$(VAR_A$(VAR_B)",
|
||||||
|
expected: "$(VAR_A$(VAR_B)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "value is a reference",
|
||||||
|
input: "$(VAR_REF)",
|
||||||
|
expected: "$(VAR_A)",
|
||||||
|
counts: map[string]int{"VAR_REF": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "value is a reference x 2",
|
||||||
|
input: "%%$(VAR_REF)--$(VAR_REF)%%",
|
||||||
|
expected: "%%$(VAR_A)--$(VAR_A)%%",
|
||||||
|
counts: map[string]int{"VAR_REF": 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty var",
|
||||||
|
input: "foo$(VAR_EMPTY)bar",
|
||||||
|
expected: "foobar",
|
||||||
|
counts: map[string]int{"VAR_EMPTY": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unterminated expression",
|
||||||
|
input: "foo$(VAR_Awhoops!",
|
||||||
|
expected: "foo$(VAR_Awhoops!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "expression without operator",
|
||||||
|
input: "f00__(VAR_A)__",
|
||||||
|
expected: "f00__(VAR_A)__",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shell special vars pass through",
|
||||||
|
input: "$?_boo_$!",
|
||||||
|
expected: "$?_boo_$!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bare operators are ignored",
|
||||||
|
input: "$VAR_A",
|
||||||
|
expected: "$VAR_A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "undefined vars are passed through",
|
||||||
|
input: "$(VAR_DNE)",
|
||||||
|
expected: "$(VAR_DNE)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple (even) operators, var undefined",
|
||||||
|
input: "$$$$$$(BIG_MONEY)",
|
||||||
|
expected: "$$$(BIG_MONEY)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple (even) operators, var defined",
|
||||||
|
input: "$$$$$$(VAR_A)",
|
||||||
|
expected: "$$$(VAR_A)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple (odd) operators, var undefined",
|
||||||
|
input: "$$$$$$$(GOOD_ODDS)",
|
||||||
|
expected: "$$$$(GOOD_ODDS)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple (odd) operators, var defined",
|
||||||
|
input: "$$$$$$$(VAR_A)",
|
||||||
|
expected: "$$$A",
|
||||||
|
counts: map[string]int{"VAR_A": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing open expression",
|
||||||
|
input: "$VAR_A)",
|
||||||
|
expected: "$VAR_A)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shell syntax ignored",
|
||||||
|
input: "${VAR_A}",
|
||||||
|
expected: "${VAR_A}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trailing incomplete expression not consumed",
|
||||||
|
input: "$(VAR_B)_______$(A",
|
||||||
|
expected: "B_______$(A",
|
||||||
|
counts: map[string]int{"VAR_B": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trailing incomplete expression, no content, is not consumed",
|
||||||
|
input: "$(VAR_C)_______$(",
|
||||||
|
expected: "C_______$(",
|
||||||
|
counts: map[string]int{"VAR_C": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "operator at end of input string is preserved",
|
||||||
|
input: "$(VAR_A)foobarzab$",
|
||||||
|
expected: "Afoobarzab$",
|
||||||
|
counts: map[string]int{"VAR_A": 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "shell escaped incomplete expr",
|
||||||
|
input: "foo-\\$(VAR_A",
|
||||||
|
expected: "foo-\\$(VAR_A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lots of $( in middle",
|
||||||
|
input: "--$($($($($--",
|
||||||
|
expected: "--$($($($($--",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lots of $( in beginning",
|
||||||
|
input: "$($($($($--foo$(",
|
||||||
|
expected: "$($($($($--foo$(",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "lots of $( at end",
|
||||||
|
input: "foo0--$($($($(",
|
||||||
|
expected: "foo0--$($($($(",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "escaped operators in variable names are not escaped",
|
||||||
|
input: "$(foo$$var)",
|
||||||
|
expected: "$(foo$$var)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "newline not expanded",
|
||||||
|
input: "\n",
|
||||||
|
expected: "\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
counts := make(map[string]int)
|
||||||
|
mapping := MappingFuncFor(counts, context...)
|
||||||
|
expanded := Expand(fmt.Sprintf("%v", tc.input), mapping)
|
||||||
|
if e, a := tc.expected, expanded; e != a {
|
||||||
|
t.Errorf("%v: expected %q, got %q", tc.name, e, a)
|
||||||
|
}
|
||||||
|
if len(counts) != len(tc.counts) {
|
||||||
|
t.Errorf("%v: len(counts)=%d != len(tc.counts)=%d",
|
||||||
|
tc.name, len(counts), len(tc.counts))
|
||||||
|
}
|
||||||
|
if len(tc.counts) > 0 {
|
||||||
|
for k, expectedCount := range tc.counts {
|
||||||
|
c, ok := counts[k]
|
||||||
|
if ok {
|
||||||
|
if c != expectedCount {
|
||||||
|
t.Errorf(
|
||||||
|
"%v: k=%s, expected count %d, got %d",
|
||||||
|
tc.name, k, expectedCount, c)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf(
|
||||||
|
"%v: k=%s, expected count %d, got zero",
|
||||||
|
tc.name, k, expectedCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
190
api/internal/accumulator/loadconfigfromcrds.go
Normal file
190
api/internal/accumulator/loadconfigfromcrds.go
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package accumulator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-openapi/spec"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"k8s.io/kube-openapi/pkg/common"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type myProperties map[string]spec.Schema
|
||||||
|
type nameToApiMap map[string]common.OpenAPIDefinition
|
||||||
|
|
||||||
|
// LoadConfigFromCRDs parse CRD schemas from paths into a TransformerConfig
|
||||||
|
func LoadConfigFromCRDs(
|
||||||
|
ldr ifc.Loader, paths []string) (*builtinconfig.TransformerConfig, error) {
|
||||||
|
tc := builtinconfig.MakeEmptyConfig()
|
||||||
|
for _, path := range paths {
|
||||||
|
content, err := ldr.Load(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
m, err := makeNameToApiMap(content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to parse open API definition from '%s'", path)
|
||||||
|
}
|
||||||
|
otherTc, err := makeConfigFromApiMap(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tc, err = tc.Merge(otherTc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNameToApiMap(content []byte) (result nameToApiMap, err error) {
|
||||||
|
if content[0] == '{' {
|
||||||
|
err = json.Unmarshal(content, &result)
|
||||||
|
} else {
|
||||||
|
err = yaml.Unmarshal(content, &result)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeConfigFromApiMap(m nameToApiMap) (*builtinconfig.TransformerConfig, error) {
|
||||||
|
result := builtinconfig.MakeEmptyConfig()
|
||||||
|
for name, api := range m {
|
||||||
|
if !looksLikeAk8sType(api.Schema.SchemaProps.Properties) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tc := builtinconfig.MakeEmptyConfig()
|
||||||
|
err := loadCrdIntoConfig(
|
||||||
|
tc, makeGvkFromTypeName(name), m, name, []string{})
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
result, err = result.Merge(tc)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Get Group and Version for CRD from the
|
||||||
|
// openAPI definition once
|
||||||
|
// "x-kubernetes-group-version-kind" is available in CRD
|
||||||
|
func makeGvkFromTypeName(n string) resid.Gvk {
|
||||||
|
names := strings.Split(n, ".")
|
||||||
|
kind := names[len(names)-1]
|
||||||
|
return resid.Gvk{Kind: kind}
|
||||||
|
}
|
||||||
|
|
||||||
|
func looksLikeAk8sType(properties myProperties) bool {
|
||||||
|
_, ok := properties["kind"]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok = properties["apiVersion"]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok = properties["metadata"]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// "x-kubernetes-annotation": ""
|
||||||
|
xAnnotation = "x-kubernetes-annotation"
|
||||||
|
|
||||||
|
// "x-kubernetes-label-selector": ""
|
||||||
|
xLabelSelector = "x-kubernetes-label-selector"
|
||||||
|
|
||||||
|
// "x-kubernetes-identity": ""
|
||||||
|
xIdentity = "x-kubernetes-identity"
|
||||||
|
|
||||||
|
// "x-kubernetes-object-ref-api-version": <apiVersion name>
|
||||||
|
xVersion = "x-kubernetes-object-ref-api-version"
|
||||||
|
|
||||||
|
// "x-kubernetes-object-ref-kind": <kind name>
|
||||||
|
xKind = "x-kubernetes-object-ref-kind"
|
||||||
|
|
||||||
|
// "x-kubernetes-object-ref-name-key": "name"
|
||||||
|
// default is "name"
|
||||||
|
xNameKey = "x-kubernetes-object-ref-name-key"
|
||||||
|
)
|
||||||
|
|
||||||
|
// loadCrdIntoConfig loads a CRD spec into a TransformerConfig
|
||||||
|
func loadCrdIntoConfig(
|
||||||
|
theConfig *builtinconfig.TransformerConfig, theGvk resid.Gvk, theMap nameToApiMap,
|
||||||
|
typeName string, path []string) (err error) {
|
||||||
|
api, ok := theMap[typeName]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for propName, property := range api.Schema.SchemaProps.Properties {
|
||||||
|
_, annotate := property.Extensions.GetString(xAnnotation)
|
||||||
|
if annotate {
|
||||||
|
err = theConfig.AddAnnotationFieldSpec(
|
||||||
|
makeFs(theGvk, append(path, propName)))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, label := property.Extensions.GetString(xLabelSelector)
|
||||||
|
if label {
|
||||||
|
err = theConfig.AddLabelFieldSpec(
|
||||||
|
makeFs(theGvk, append(path, propName)))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, identity := property.Extensions.GetString(xIdentity)
|
||||||
|
if identity {
|
||||||
|
err = theConfig.AddPrefixFieldSpec(
|
||||||
|
makeFs(theGvk, append(path, propName)))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
version, ok := property.Extensions.GetString(xVersion)
|
||||||
|
if ok {
|
||||||
|
kind, ok := property.Extensions.GetString(xKind)
|
||||||
|
if ok {
|
||||||
|
nameKey, ok := property.Extensions.GetString(xNameKey)
|
||||||
|
if !ok {
|
||||||
|
nameKey = "name"
|
||||||
|
}
|
||||||
|
err = theConfig.AddNamereferenceFieldSpec(
|
||||||
|
builtinconfig.NameBackReferences{
|
||||||
|
Gvk: resid.Gvk{Kind: kind, Version: version},
|
||||||
|
FieldSpecs: []types.FieldSpec{
|
||||||
|
makeFs(theGvk, append(path, propName, nameKey))},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if property.Ref.GetURL() != nil {
|
||||||
|
loadCrdIntoConfig(
|
||||||
|
theConfig, theGvk, theMap,
|
||||||
|
property.Ref.String(), append(path, propName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFs(in resid.Gvk, path []string) types.FieldSpec {
|
||||||
|
return types.FieldSpec{
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
Gvk: in,
|
||||||
|
Path: strings.Join(path, "/"),
|
||||||
|
}
|
||||||
|
}
|
||||||
182
api/internal/accumulator/loadconfigfromcrds_test.go
Normal file
182
api/internal/accumulator/loadconfigfromcrds_test.go
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package accumulator_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/loadertest"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This defines two CRD's: Bee and MyKind.
|
||||||
|
//
|
||||||
|
// Bee is boring, it's spec has no dependencies.
|
||||||
|
//
|
||||||
|
// MyKind, however, has a spec that contains
|
||||||
|
// a Bee and a (k8s native) Secret.
|
||||||
|
const (
|
||||||
|
crdContent = `
|
||||||
|
{
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.Bee": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "Bee",
|
||||||
|
"properties": {
|
||||||
|
"apiVersion": {
|
||||||
|
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert
|
||||||
|
recognized schemas to the latest internal value, and may reject unrecognized values.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer
|
||||||
|
this from the endpoint the client submits requests to. Cannot be updated. In CamelCase.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$ref": "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": [
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec",
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus",
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "BeeSpec defines the desired state of Bee"
|
||||||
|
},
|
||||||
|
"Dependencies": []
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "BeeStatus defines the observed state of Bee"
|
||||||
|
},
|
||||||
|
"Dependencies": []
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKind": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "MyKind",
|
||||||
|
"properties": {
|
||||||
|
"apiVersion": {
|
||||||
|
"description": "APIVersion defines the versioned schema of this representation of an object.
|
||||||
|
Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values.
|
||||||
|
More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"description": "Kind is a string value representing the REST resource this object represents.
|
||||||
|
Servers may infer this from the endpoint the client submits requests to. Cannot be updated.
|
||||||
|
In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$ref": "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": [
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec",
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus",
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "MyKindSpec defines the desired state of MyKind",
|
||||||
|
"properties": {
|
||||||
|
"beeRef": {
|
||||||
|
"x-kubernetes-object-ref-api-version": "v1beta1",
|
||||||
|
"x-kubernetes-object-ref-kind": "Bee",
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.Bee"
|
||||||
|
},
|
||||||
|
"secretRef": {
|
||||||
|
"description": "If defined, we use this secret for configuring the MYSQL_ROOT_PASSWORD
|
||||||
|
If it is not set we generate a secret dynamically",
|
||||||
|
"x-kubernetes-object-ref-api-version": "v1",
|
||||||
|
"x-kubernetes-object-ref-kind": "Secret",
|
||||||
|
"$ref": "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1.LocalObjectReference"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": [
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.Bee",
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1.LocalObjectReference"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "MyKindStatus defines the observed state of MyKind"
|
||||||
|
},
|
||||||
|
"Dependencies": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeLoader(t *testing.T) ifc.Loader {
|
||||||
|
ldr := loadertest.NewFakeLoader("/testpath")
|
||||||
|
err := ldr.AddFile("/testpath/crd.json", []byte(crdContent))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to setup fake ldr.")
|
||||||
|
}
|
||||||
|
return ldr
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadCRDs(t *testing.T) {
|
||||||
|
nbrs := []builtinconfig.NameBackReferences{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{Kind: "Secret", Version: "v1"},
|
||||||
|
FieldSpecs: []types.FieldSpec{
|
||||||
|
{
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
Gvk: resid.Gvk{Kind: "MyKind"},
|
||||||
|
Path: "spec/secretRef/name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{Kind: "Bee", Version: "v1beta1"},
|
||||||
|
FieldSpecs: []types.FieldSpec{
|
||||||
|
{
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
Gvk: resid.Gvk{Kind: "MyKind"},
|
||||||
|
Path: "spec/beeRef/name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedTc := &builtinconfig.TransformerConfig{
|
||||||
|
NameReference: nbrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
actualTc, err := LoadConfigFromCRDs(makeLoader(t), []string{"crd.json"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error:%v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actualTc, expectedTc) {
|
||||||
|
t.Fatalf("expected\n %v\n but got\n %v\n", expectedTc, actualTc)
|
||||||
|
}
|
||||||
|
}
|
||||||
274
api/internal/accumulator/namereferencetransformer.go
Normal file
274
api/internal/accumulator/namereferencetransformer.go
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package accumulator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
)
|
||||||
|
|
||||||
|
type nameReferenceTransformer struct {
|
||||||
|
backRefs []builtinconfig.NameBackReferences
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ resmap.Transformer = &nameReferenceTransformer{}
|
||||||
|
|
||||||
|
// newNameReferenceTransformer constructs a nameReferenceTransformer
|
||||||
|
// with a given slice of NameBackReferences.
|
||||||
|
func newNameReferenceTransformer(br []builtinconfig.NameBackReferences) resmap.Transformer {
|
||||||
|
if br == nil {
|
||||||
|
log.Fatal("backrefs not expected to be nil")
|
||||||
|
}
|
||||||
|
return &nameReferenceTransformer{backRefs: br}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform updates name references in resource A that
|
||||||
|
// refer to resource B, given that B's name may have
|
||||||
|
// changed.
|
||||||
|
//
|
||||||
|
// For example, a HorizontalPodAutoscaler (HPA)
|
||||||
|
// necessarily refers to a Deployment, the thing that
|
||||||
|
// the HPA scales. The Deployment name might change
|
||||||
|
// (e.g. prefix added), and the reference in the HPA
|
||||||
|
// has to be fixed.
|
||||||
|
//
|
||||||
|
// In the outer loop over the ResMap below, say we
|
||||||
|
// encounter a specific HPA. Then, in scanning backrefs,
|
||||||
|
// we encounter an entry like
|
||||||
|
//
|
||||||
|
// - kind: Deployment
|
||||||
|
// fieldSpecs:
|
||||||
|
// - kind: HorizontalPodAutoscaler
|
||||||
|
// path: spec/scaleTargetRef/name
|
||||||
|
//
|
||||||
|
// This entry says that an HPA, via its
|
||||||
|
// 'spec/scaleTargetRef/name' field, may refer to a
|
||||||
|
// Deployment. This match to HPA means we may need to
|
||||||
|
// modify the value in its 'spec/scaleTargetRef/name'
|
||||||
|
// field, by searching for the thing it refers to,
|
||||||
|
// and getting its new name.
|
||||||
|
//
|
||||||
|
// As a filter, and search optimization, we compute a
|
||||||
|
// subset of all resources that the HPA could refer to,
|
||||||
|
// by excluding objects from other namespaces, and
|
||||||
|
// excluding objects that don't have the same prefix-
|
||||||
|
// suffix mods as the HPA.
|
||||||
|
//
|
||||||
|
// We look in this subset for all Deployment objects
|
||||||
|
// with a resId that has a Name matching the field value
|
||||||
|
// present in the HPA. If no match do nothing; if more
|
||||||
|
// than one match, it's an error.
|
||||||
|
//
|
||||||
|
// We overwrite the HPA name field with the value found
|
||||||
|
// in the Deployment's name field (the name in the raw
|
||||||
|
// object - the modified name - not the unmodified name
|
||||||
|
// in the Deployment's resId).
|
||||||
|
//
|
||||||
|
// This process assumes that the name stored in a ResId
|
||||||
|
// (the ResMap key) isn't modified by name transformers.
|
||||||
|
// Name transformers should only modify the name in the
|
||||||
|
// body of the resource object (the value in the ResMap).
|
||||||
|
//
|
||||||
|
func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
|
||||||
|
// TODO: Too much looping, here and in transitive calls.
|
||||||
|
for _, referrer := range m.Resources() {
|
||||||
|
var candidates resmap.ResMap
|
||||||
|
for _, target := range o.backRefs {
|
||||||
|
for _, fSpec := range target.FieldSpecs {
|
||||||
|
if referrer.OrgId().IsSelected(&fSpec.Gvk) {
|
||||||
|
if candidates == nil {
|
||||||
|
candidates = m.SubsetThatCouldBeReferencedByResource(referrer)
|
||||||
|
}
|
||||||
|
err := transform.MutateField(
|
||||||
|
referrer.Map(),
|
||||||
|
fSpec.PathSlice(),
|
||||||
|
fSpec.CreateIfNotPresent,
|
||||||
|
o.getNewNameFunc(
|
||||||
|
// referrer could be an HPA instance,
|
||||||
|
// target could be Gvk for Deployment,
|
||||||
|
// candidate a list of resources "reachable"
|
||||||
|
// from the HPA.
|
||||||
|
referrer, target.Gvk, candidates))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// selectReferral picks the referral among a subset of candidates.
|
||||||
|
// It returns the current name and namespace of the selected candidate.
|
||||||
|
// Note that the content of the referricalCandidateSubset slice is most of the time
|
||||||
|
// identical to the referralCandidates resmap. Still in some cases, such
|
||||||
|
// as ClusterRoleBinding, the subset only contains the resources of a specific
|
||||||
|
// namespace.
|
||||||
|
func (o *nameReferenceTransformer) selectReferral(
|
||||||
|
oldName string,
|
||||||
|
referrer *resource.Resource,
|
||||||
|
target resid.Gvk,
|
||||||
|
referralCandidates resmap.ResMap,
|
||||||
|
referralCandidateSubset []*resource.Resource) (interface{}, interface{}, error) {
|
||||||
|
|
||||||
|
for _, res := range referralCandidateSubset {
|
||||||
|
id := res.OrgId()
|
||||||
|
if id.IsSelected(&target) && res.GetOriginalName() == oldName {
|
||||||
|
matches := referralCandidates.GetMatchingResourcesByOriginalId(id.Equals)
|
||||||
|
// If there's more than one match, there's no way
|
||||||
|
// to know which one to pick, so emit error.
|
||||||
|
if len(matches) > 1 {
|
||||||
|
return nil, nil, fmt.Errorf(
|
||||||
|
"multiple matches for %s:\n %v",
|
||||||
|
id, getIds(matches))
|
||||||
|
}
|
||||||
|
// In the resource, note that it is referenced
|
||||||
|
// by the referrer.
|
||||||
|
res.AppendRefBy(referrer.CurId())
|
||||||
|
// Return transformed name of the object,
|
||||||
|
// complete with prefixes, hashes, etc.
|
||||||
|
return res.GetName(), res.GetNamespace(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldName, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// utility function to replace a simple string by the new name
|
||||||
|
func (o *nameReferenceTransformer) getSimpleNameField(
|
||||||
|
oldName string,
|
||||||
|
referrer *resource.Resource,
|
||||||
|
target resid.Gvk,
|
||||||
|
referralCandidates resmap.ResMap,
|
||||||
|
referralCandidateSubset []*resource.Resource) (interface{}, error) {
|
||||||
|
|
||||||
|
newName, _, err := o.selectReferral(oldName, referrer, target,
|
||||||
|
referralCandidates, referralCandidateSubset)
|
||||||
|
|
||||||
|
return newName, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// utility function to replace name field within a map[string]interface{}
|
||||||
|
// and leverage the namespace field.
|
||||||
|
func (o *nameReferenceTransformer) getNameAndNsStruct(
|
||||||
|
inMap map[string]interface{},
|
||||||
|
referrer *resource.Resource,
|
||||||
|
target resid.Gvk,
|
||||||
|
referralCandidates resmap.ResMap) (interface{}, error) {
|
||||||
|
|
||||||
|
// Example:
|
||||||
|
if _, ok := inMap["name"]; !ok {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"%#v is expected to contain a name field", inMap)
|
||||||
|
}
|
||||||
|
oldName, ok := inMap["name"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"%#v is expected to contain a name field of type string", oldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
subset := referralCandidates.Resources()
|
||||||
|
if namespacevalue, ok := inMap["namespace"]; ok {
|
||||||
|
namespace := namespacevalue.(string)
|
||||||
|
bynamespace := referralCandidates.GroupedByOriginalNamespace()
|
||||||
|
if _, ok := bynamespace[namespace]; !ok {
|
||||||
|
return inMap, nil
|
||||||
|
}
|
||||||
|
subset = bynamespace[namespace]
|
||||||
|
}
|
||||||
|
|
||||||
|
newname, newnamespace, err := o.selectReferral(oldName, referrer, target,
|
||||||
|
referralCandidates, subset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newname == oldName) && (newnamespace == nil) {
|
||||||
|
// no candidate found.
|
||||||
|
return inMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
inMap["name"] = newname
|
||||||
|
if newnamespace != "" {
|
||||||
|
// We don't want value "" to replace value "default" since
|
||||||
|
// the empty string is handled as a wild card here not default namespace
|
||||||
|
// by kubernetes.
|
||||||
|
inMap["namespace"] = newnamespace
|
||||||
|
}
|
||||||
|
return inMap, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *nameReferenceTransformer) getNewNameFunc(
|
||||||
|
referrer *resource.Resource,
|
||||||
|
target resid.Gvk,
|
||||||
|
referralCandidates resmap.ResMap) func(in interface{}) (interface{}, error) {
|
||||||
|
return func(in interface{}) (interface{}, error) {
|
||||||
|
switch in.(type) {
|
||||||
|
case string:
|
||||||
|
oldName, _ := in.(string)
|
||||||
|
return o.getSimpleNameField(oldName, referrer, target,
|
||||||
|
referralCandidates, referralCandidates.Resources())
|
||||||
|
case map[string]interface{}:
|
||||||
|
// Kind: ValidatingWebhookConfiguration
|
||||||
|
// FieldSpec is webhooks/clientConfig/service
|
||||||
|
oldMap, _ := in.(map[string]interface{})
|
||||||
|
return o.getNameAndNsStruct(oldMap, referrer, target,
|
||||||
|
referralCandidates)
|
||||||
|
case []interface{}:
|
||||||
|
l, _ := in.([]interface{})
|
||||||
|
for idx, item := range l {
|
||||||
|
switch item.(type) {
|
||||||
|
case string:
|
||||||
|
// Kind: Role/ClusterRole
|
||||||
|
// FieldSpec is rules.resourceNames
|
||||||
|
oldName, _ := item.(string)
|
||||||
|
newName, err := o.getSimpleNameField(oldName, referrer, target,
|
||||||
|
referralCandidates, referralCandidates.Resources())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l[idx] = newName
|
||||||
|
case map[string]interface{}:
|
||||||
|
// Kind: RoleBinding/ClusterRoleBinding
|
||||||
|
// FieldSpec is subjects
|
||||||
|
// Note: The corresponding fieldSpec had been changed from
|
||||||
|
// from path: subjects/name to just path: subjects. This is
|
||||||
|
// what get mutatefield to request the mapping of the whole
|
||||||
|
// map containing namespace and name instead of just a simple
|
||||||
|
// string field containing the name
|
||||||
|
oldMap, _ := item.(map[string]interface{})
|
||||||
|
newMap, err := o.getNameAndNsStruct(oldMap, referrer, target,
|
||||||
|
referralCandidates)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l[idx] = newMap
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"%#v is expected to be either a []string or a []map[string]interface{}", in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return in, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"%#v is expected to be either a string or a []interface{}", in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIds(rs []*resource.Resource) []string {
|
||||||
|
var result []string
|
||||||
|
for _, r := range rs {
|
||||||
|
result = append(result, r.CurId().String()+"\n")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
1061
api/internal/accumulator/namereferencetransformer_test.go
Normal file
1061
api/internal/accumulator/namereferencetransformer_test.go
Normal file
File diff suppressed because it is too large
Load Diff
110
api/internal/accumulator/refvartransformer.go
Normal file
110
api/internal/accumulator/refvartransformer.go
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package accumulator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
expansion2 "sigs.k8s.io/kustomize/api/internal/accumulator/expansion"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type refVarTransformer struct {
|
||||||
|
varMap map[string]interface{}
|
||||||
|
replacementCounts map[string]int
|
||||||
|
fieldSpecs []types.FieldSpec
|
||||||
|
mappingFunc func(string) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRefVarTransformer returns a new refVarTransformer
|
||||||
|
// that replaces $(VAR) style variables with values.
|
||||||
|
// The fieldSpecs are the places to look for occurrences of $(VAR).
|
||||||
|
func newRefVarTransformer(
|
||||||
|
varMap map[string]interface{}, fs []types.FieldSpec) *refVarTransformer {
|
||||||
|
return &refVarTransformer{
|
||||||
|
varMap: varMap,
|
||||||
|
fieldSpecs: fs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replaceVars accepts as 'in' a string, or string array, which can have
|
||||||
|
// embedded instances of $VAR style variables, e.g. a container command string.
|
||||||
|
// The function returns the string with the variables expanded to their final
|
||||||
|
// values.
|
||||||
|
func (rv *refVarTransformer) replaceVars(in interface{}) (interface{}, error) {
|
||||||
|
switch vt := in.(type) {
|
||||||
|
case []interface{}:
|
||||||
|
var xs []interface{}
|
||||||
|
for _, a := range in.([]interface{}) {
|
||||||
|
xs = append(xs, expansion2.Expand(a.(string), rv.mappingFunc))
|
||||||
|
}
|
||||||
|
return xs, nil
|
||||||
|
case map[string]interface{}:
|
||||||
|
inMap := in.(map[string]interface{})
|
||||||
|
xs := make(map[string]interface{}, len(inMap))
|
||||||
|
for k, v := range inMap {
|
||||||
|
s, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
// This field not contain a $(VAR) since it is not
|
||||||
|
// of string type. For instance .spec.replicas: 3 in
|
||||||
|
// a Deployment object
|
||||||
|
xs[k] = v
|
||||||
|
} else {
|
||||||
|
// This field can potentially contains a $(VAR) since it is
|
||||||
|
// of string type. For instance .spec.replicas: $(REPLICAS)
|
||||||
|
// in a Deployment object
|
||||||
|
xs[k] = expansion2.Expand(s, rv.mappingFunc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xs, nil
|
||||||
|
case interface{}:
|
||||||
|
s, ok := in.(string)
|
||||||
|
if !ok {
|
||||||
|
// This field not contain a $(VAR) since it is not of string type.
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
// This field can potentially contain a $(VAR) since it is
|
||||||
|
// of string type.
|
||||||
|
return expansion2.Expand(s, rv.mappingFunc), nil
|
||||||
|
case nil:
|
||||||
|
return nil, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("invalid type encountered %T", vt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnusedVars returns slice of Var names that were unused
|
||||||
|
// after a Transform run.
|
||||||
|
func (rv *refVarTransformer) UnusedVars() []string {
|
||||||
|
var unused []string
|
||||||
|
for k := range rv.varMap {
|
||||||
|
_, ok := rv.replacementCounts[k]
|
||||||
|
if !ok {
|
||||||
|
unused = append(unused, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unused
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform replaces $(VAR) style variables with values.
|
||||||
|
func (rv *refVarTransformer) Transform(m resmap.ResMap) error {
|
||||||
|
rv.replacementCounts = make(map[string]int)
|
||||||
|
rv.mappingFunc = expansion2.MappingFuncFor(
|
||||||
|
rv.replacementCounts, rv.varMap)
|
||||||
|
for _, res := range m.Resources() {
|
||||||
|
for _, fieldSpec := range rv.fieldSpecs {
|
||||||
|
if res.OrgId().IsSelected(&fieldSpec.Gvk) {
|
||||||
|
if err := transform.MutateField(
|
||||||
|
res.Map(), fieldSpec.PathSlice(),
|
||||||
|
false, rv.replaceVars); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
137
api/internal/accumulator/refvartransformer_test.go
Normal file
137
api/internal/accumulator/refvartransformer_test.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package accumulator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRefVarTransformer(t *testing.T) {
|
||||||
|
type given struct {
|
||||||
|
varMap map[string]interface{}
|
||||||
|
fs []types.FieldSpec
|
||||||
|
res resmap.ResMap
|
||||||
|
}
|
||||||
|
type expected struct {
|
||||||
|
res resmap.ResMap
|
||||||
|
unused []string
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
description string
|
||||||
|
given given
|
||||||
|
expected expected
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
description: "var replacement in map[string]",
|
||||||
|
given: given{
|
||||||
|
varMap: map[string]interface{}{
|
||||||
|
"FOO": "replacementForFoo",
|
||||||
|
"BAR": "replacementForBar",
|
||||||
|
"BAZ": int64(5),
|
||||||
|
"BOO": true,
|
||||||
|
},
|
||||||
|
fs: []types.FieldSpec{
|
||||||
|
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/map"},
|
||||||
|
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/slice"},
|
||||||
|
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/interface"},
|
||||||
|
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/nil"},
|
||||||
|
{Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"}, Path: "data/num"},
|
||||||
|
},
|
||||||
|
res: resmaptest_test.NewRmBuilder(
|
||||||
|
t, resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())).
|
||||||
|
Add(map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"map": map[string]interface{}{
|
||||||
|
"item1": "$(FOO)",
|
||||||
|
"item2": "bla",
|
||||||
|
"item3": "$(BAZ)",
|
||||||
|
"item4": "$(BAZ)+$(BAZ)",
|
||||||
|
"item5": "$(BOO)",
|
||||||
|
"item6": "if $(BOO)",
|
||||||
|
"item7": 2019,
|
||||||
|
},
|
||||||
|
"slice": []interface{}{
|
||||||
|
"$(FOO)",
|
||||||
|
"bla",
|
||||||
|
"$(BAZ)",
|
||||||
|
"$(BAZ)+$(BAZ)",
|
||||||
|
"$(BOO)",
|
||||||
|
"if $(BOO)",
|
||||||
|
},
|
||||||
|
"interface": "$(FOO)",
|
||||||
|
"nil": nil,
|
||||||
|
"num": 2019,
|
||||||
|
}}).ResMap(),
|
||||||
|
},
|
||||||
|
expected: expected{
|
||||||
|
res: resmaptest_test.NewRmBuilder(
|
||||||
|
t, resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())).
|
||||||
|
Add(map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "cm1",
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"map": map[string]interface{}{
|
||||||
|
"item1": "replacementForFoo",
|
||||||
|
"item2": "bla",
|
||||||
|
"item3": int64(5),
|
||||||
|
"item4": "5+5",
|
||||||
|
"item5": true,
|
||||||
|
"item6": "if true",
|
||||||
|
"item7": 2019,
|
||||||
|
},
|
||||||
|
"slice": []interface{}{
|
||||||
|
"replacementForFoo",
|
||||||
|
"bla",
|
||||||
|
int64(5),
|
||||||
|
"5+5",
|
||||||
|
true,
|
||||||
|
"if true",
|
||||||
|
},
|
||||||
|
"interface": "replacementForFoo",
|
||||||
|
"nil": nil,
|
||||||
|
"num": 2019,
|
||||||
|
}}).ResMap(),
|
||||||
|
unused: []string{"BAR"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.description, func(t *testing.T) {
|
||||||
|
// arrange
|
||||||
|
tr := newRefVarTransformer(tc.given.varMap, tc.given.fs)
|
||||||
|
|
||||||
|
// act
|
||||||
|
err := tr.Transform(tc.given.res)
|
||||||
|
|
||||||
|
// assert
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a, e := tc.given.res, tc.expected.res
|
||||||
|
if !reflect.DeepEqual(a, e) {
|
||||||
|
err = e.ErrorIfNotEqualLists(a)
|
||||||
|
t.Fatalf("actual doesn't match expected: \nACTUAL:\n%v\nEXPECTED:\n%v\nERR: %v", a, e, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
166
api/internal/accumulator/resaccumulator.go
Normal file
166
api/internal/accumulator/resaccumulator.go
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package accumulator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResAccumulator accumulates resources and the rules
|
||||||
|
// used to customize those resources. It's a ResMap
|
||||||
|
// plus stuff needed to modify the ResMap.
|
||||||
|
type ResAccumulator struct {
|
||||||
|
resMap resmap.ResMap
|
||||||
|
tConfig *builtinconfig.TransformerConfig
|
||||||
|
varSet types.VarSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeEmptyAccumulator() *ResAccumulator {
|
||||||
|
ra := &ResAccumulator{}
|
||||||
|
ra.resMap = resmap.New()
|
||||||
|
ra.tConfig = &builtinconfig.TransformerConfig{}
|
||||||
|
ra.varSet = types.NewVarSet()
|
||||||
|
return ra
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResMap returns a copy of the internal resMap.
|
||||||
|
func (ra *ResAccumulator) ResMap() resmap.ResMap {
|
||||||
|
return ra.resMap.ShallowCopy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vars returns a copy of underlying vars.
|
||||||
|
func (ra *ResAccumulator) Vars() []types.Var {
|
||||||
|
return ra.varSet.AsSlice()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) AppendAll(
|
||||||
|
resources resmap.ResMap) error {
|
||||||
|
return ra.resMap.AppendAll(resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) AbsorbAll(
|
||||||
|
resources resmap.ResMap) error {
|
||||||
|
return ra.resMap.AbsorbAll(resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) MergeConfig(
|
||||||
|
tConfig *builtinconfig.TransformerConfig) (err error) {
|
||||||
|
ra.tConfig, err = ra.tConfig.Merge(tConfig)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) GetTransformerConfig() *builtinconfig.TransformerConfig {
|
||||||
|
return ra.tConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) MergeVars(incoming []types.Var) error {
|
||||||
|
for _, v := range incoming {
|
||||||
|
targetId := resid.NewResIdWithNamespace(v.ObjRef.GVK(), v.ObjRef.Name, v.ObjRef.Namespace)
|
||||||
|
idMatcher := targetId.GvknEquals
|
||||||
|
if targetId.Namespace != "" || !targetId.IsNamespaceableKind() {
|
||||||
|
// Preserve backward compatibility. An empty namespace means
|
||||||
|
// wildcard search on the namespace hence we still use GvknEquals
|
||||||
|
idMatcher = targetId.Equals
|
||||||
|
}
|
||||||
|
matched := ra.resMap.GetMatchingResourcesByOriginalId(idMatcher)
|
||||||
|
if len(matched) > 1 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"found %d resId matches for var %s "+
|
||||||
|
"(unable to disambiguate)",
|
||||||
|
len(matched), v)
|
||||||
|
}
|
||||||
|
if len(matched) == 1 {
|
||||||
|
matched[0].AppendRefVarName(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ra.varSet.MergeSlice(incoming)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) MergeAccumulator(other *ResAccumulator) (err error) {
|
||||||
|
err = ra.AppendAll(other.resMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ra.MergeConfig(other.tConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ra.varSet.MergeSet(other.varSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) findVarValueFromResources(v types.Var) (interface{}, error) {
|
||||||
|
for _, res := range ra.resMap.Resources() {
|
||||||
|
for _, varName := range res.GetRefVarNames() {
|
||||||
|
if varName == v.Name {
|
||||||
|
s, err := res.GetFieldValue(v.FieldRef.FieldPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"field specified in var '%v' "+
|
||||||
|
"not found in corresponding resource", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf(
|
||||||
|
"var '%v' cannot be mapped to a field "+
|
||||||
|
"in the set of known resources", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeVarReplacementMap returns a map of Var names to
|
||||||
|
// their final values. The values are strings intended
|
||||||
|
// for substitution wherever the $(var.Name) occurs.
|
||||||
|
func (ra *ResAccumulator) makeVarReplacementMap() (map[string]interface{}, error) {
|
||||||
|
result := map[string]interface{}{}
|
||||||
|
for _, v := range ra.Vars() {
|
||||||
|
s, err := ra.findVarValueFromResources(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result[v.Name] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) Transform(t resmap.Transformer) error {
|
||||||
|
return t.Transform(ra.resMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) ResolveVars() error {
|
||||||
|
replacementMap, err := ra.makeVarReplacementMap()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(replacementMap) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
t := newRefVarTransformer(
|
||||||
|
replacementMap, ra.tConfig.VarReference)
|
||||||
|
err = ra.Transform(t)
|
||||||
|
if len(t.UnusedVars()) > 0 {
|
||||||
|
log.Printf(
|
||||||
|
"well-defined vars that were never replaced: %s\n",
|
||||||
|
strings.Join(t.UnusedVars(), ","))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ra *ResAccumulator) FixBackReferences() (err error) {
|
||||||
|
if ra.tConfig.NameReference == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ra.Transform(newNameReferenceTransformer(
|
||||||
|
ra.tConfig.NameReference))
|
||||||
|
}
|
||||||
417
api/internal/accumulator/resaccumulator_test.go
Normal file
417
api/internal/accumulator/resaccumulator_test.go
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package accumulator_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeResAccumulator(t *testing.T) (*ResAccumulator, *resource.Factory) {
|
||||||
|
ra := MakeEmptyAccumulator()
|
||||||
|
err := ra.MergeConfig(builtinconfig.MakeDefaultConfig())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
rf := resource.NewFactory(
|
||||||
|
kunstruct.NewKunstructuredFactoryImpl())
|
||||||
|
err = ra.AppendAll(
|
||||||
|
resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
Add(map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "deploy1",
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"template": map[string]interface{}{
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"containers": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"command": []interface{}{
|
||||||
|
"myserver",
|
||||||
|
"--somebackendService $(SERVICE_ONE)",
|
||||||
|
"--yetAnother $(SERVICE_TWO)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}).
|
||||||
|
Add(map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Service",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "backendOne",
|
||||||
|
}}).
|
||||||
|
Add(map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Service",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "backendTwo",
|
||||||
|
}}).ResMap())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
return ra, rf
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsHappy(t *testing.T) {
|
||||||
|
ra, _ := makeResAccumulator(t)
|
||||||
|
err := ra.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_ONE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendOne"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "SERVICE_TWO",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendTwo"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.ResolveVars()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
c := getCommand(find("deploy1", ra.ResMap()))
|
||||||
|
if c != "myserver --somebackendService backendOne --yetAnother backendTwo" {
|
||||||
|
t.Fatalf("unexpected command: %s", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsOneUnused(t *testing.T) {
|
||||||
|
ra, _ := makeResAccumulator(t)
|
||||||
|
err := ra.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_ONE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendOne"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "SERVICE_UNUSED",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendTwo"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer func() {
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
}()
|
||||||
|
err = ra.ResolveVars()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
expectLog(t, buf, "well-defined vars that were never replaced: SERVICE_UNUSED")
|
||||||
|
c := getCommand(find("deploy1", ra.ResMap()))
|
||||||
|
if c != "myserver --somebackendService backendOne --yetAnother $(SERVICE_TWO)" {
|
||||||
|
t.Fatalf("unexpected command: %s", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectLog(t *testing.T, log bytes.Buffer, expect string) {
|
||||||
|
if !strings.Contains(log.String(), expect) {
|
||||||
|
t.Fatalf("expected log containing '%s', got '%s'", expect, log.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
|
||||||
|
ra, rf := makeResAccumulator(t)
|
||||||
|
|
||||||
|
rm0 := resmap.New()
|
||||||
|
err := rm0.Append(
|
||||||
|
rf.FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Service",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "backendOne",
|
||||||
|
"namespace": "fooNamespace",
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.AppendAll(rm0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ra.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_ONE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendOne",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(), "unable to disambiguate") {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNamespacedConfigMapWithDataProviderValue(
|
||||||
|
namespace string,
|
||||||
|
value string,
|
||||||
|
) map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "environment",
|
||||||
|
"namespace": namespace,
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"provider": value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeVarToNamepaceAndPath(
|
||||||
|
name string,
|
||||||
|
namespace string,
|
||||||
|
path string,
|
||||||
|
) types.Var {
|
||||||
|
return types.Var{
|
||||||
|
Name: name,
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "ConfigMap"},
|
||||||
|
Name: "environment",
|
||||||
|
Namespace: namespace,
|
||||||
|
},
|
||||||
|
FieldRef: types.FieldSelector{FieldPath: path},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarConflicts(t *testing.T) {
|
||||||
|
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
|
||||||
|
|
||||||
|
// create configmaps in foo and bar namespaces with `data.provider` values.
|
||||||
|
fooAws := makeNamespacedConfigMapWithDataProviderValue("foo", "aws")
|
||||||
|
barAws := makeNamespacedConfigMapWithDataProviderValue("bar", "aws")
|
||||||
|
barGcp := makeNamespacedConfigMapWithDataProviderValue("bar", "gcp")
|
||||||
|
|
||||||
|
// create two variables with (apparently) conflicting names that point to
|
||||||
|
// fieldpaths that could be generalized.
|
||||||
|
varFoo := makeVarToNamepaceAndPath("PROVIDER", "foo", "data.provider")
|
||||||
|
varBar := makeVarToNamepaceAndPath("PROVIDER", "bar", "data.provider")
|
||||||
|
|
||||||
|
// create accumulators holding apparently conflicting vars that are not
|
||||||
|
// actually in conflict because they point to the same concrete value.
|
||||||
|
rm0 := resmap.New()
|
||||||
|
rm0.Append(rf.FromMap(fooAws))
|
||||||
|
ac0 := MakeEmptyAccumulator()
|
||||||
|
ac0.AppendAll(rm0)
|
||||||
|
ac0.MergeVars([]types.Var{varFoo})
|
||||||
|
|
||||||
|
rm1 := resmap.New()
|
||||||
|
rm1.Append(rf.FromMap(barAws))
|
||||||
|
ac1 := MakeEmptyAccumulator()
|
||||||
|
ac1.AppendAll(rm1)
|
||||||
|
ac1.MergeVars([]types.Var{varBar})
|
||||||
|
|
||||||
|
// validate that two vars of the same name which reference the same concrete
|
||||||
|
// value do not produce a conflict.
|
||||||
|
err := ac0.MergeAccumulator(ac1)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("see bug gh-1600")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an accumulator will have an actually conflicting value with the
|
||||||
|
// two above (because it contains a variable whose name is used in the other
|
||||||
|
// accumulators AND whose concrete values are different).
|
||||||
|
rm2 := resmap.New()
|
||||||
|
rm2.Append(rf.FromMap(barGcp))
|
||||||
|
ac2 := MakeEmptyAccumulator()
|
||||||
|
ac2.AppendAll(rm2)
|
||||||
|
ac2.MergeVars([]types.Var{varBar})
|
||||||
|
err = ac1.MergeAccumulator(ac2)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("dupe vars w/ different concrete values should conflict")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsGoodResIdBadField(t *testing.T) {
|
||||||
|
ra, _ := makeResAccumulator(t)
|
||||||
|
err := ra.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_ONE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendOne"},
|
||||||
|
FieldRef: types.FieldSelector{FieldPath: "nope_nope_nope"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.ResolveVars()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(),
|
||||||
|
"not found in corresponding resource") {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsUnmappableVar(t *testing.T) {
|
||||||
|
ra, _ := makeResAccumulator(t)
|
||||||
|
err := ra.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_THREE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "doesNotExist"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra.ResolveVars()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(),
|
||||||
|
"cannot be mapped to a field in the set of known resources") {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveVarsWithNoambiguation(t *testing.T) {
|
||||||
|
ra1, rf := makeResAccumulator(t)
|
||||||
|
err := ra1.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SERVICE_ONE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendOne",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create another accumulator having a resource with different prefix
|
||||||
|
ra2 := MakeEmptyAccumulator()
|
||||||
|
|
||||||
|
m := resmaptest_test.NewRmBuilder(t, rf).
|
||||||
|
Add(map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "deploy2",
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"template": map[string]interface{}{
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"containers": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"command": []interface{}{
|
||||||
|
"myserver",
|
||||||
|
"--somebackendService $(SUB_SERVICE_ONE)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}).
|
||||||
|
Add(map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Service",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "backendOne",
|
||||||
|
}}).ResMap()
|
||||||
|
|
||||||
|
// Make it seem like this resource
|
||||||
|
// went through a prefix transformer.
|
||||||
|
r := m.GetByIndex(1)
|
||||||
|
r.AddNamePrefix("sub-")
|
||||||
|
r.SetName("sub-backendOne") // original name remains "backendOne"
|
||||||
|
|
||||||
|
err = ra2.AppendAll(m)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ra2.MergeVars([]types.Var{
|
||||||
|
{
|
||||||
|
Name: "SUB_SERVICE_ONE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Version: "v1", Kind: "Service"},
|
||||||
|
Name: "backendOne",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
err = ra1.MergeAccumulator(ra2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ra1.ResolveVars()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func find(name string, resMap resmap.ResMap) *resource.Resource {
|
||||||
|
for _, r := range resMap.Resources() {
|
||||||
|
if r.GetName() == name {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes arg is a deployment, returns the command of first container.
|
||||||
|
func getCommand(r *resource.Resource) string {
|
||||||
|
var m map[string]interface{}
|
||||||
|
var c []interface{}
|
||||||
|
m, _ = r.Map()["spec"].(map[string]interface{})
|
||||||
|
m, _ = m["template"].(map[string]interface{})
|
||||||
|
m, _ = m["spec"].(map[string]interface{})
|
||||||
|
c, _ = m["containers"].([]interface{})
|
||||||
|
m, _ = c[0].(map[string]interface{})
|
||||||
|
|
||||||
|
cmd, _ := m["command"].([]interface{})
|
||||||
|
n := make([]string, len(cmd))
|
||||||
|
for i, v := range cmd {
|
||||||
|
n[i] = v.(string)
|
||||||
|
}
|
||||||
|
return strings.Join(n, " ")
|
||||||
|
}
|
||||||
121
api/internal/git/cloner.go
Normal file
121
api/internal/git/cloner.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Cloner is a function that can clone a git repo.
|
||||||
|
type Cloner func(repoSpec *RepoSpec) error
|
||||||
|
|
||||||
|
// ClonerUsingGitExec uses a local git install, as opposed
|
||||||
|
// to say, some remote API, to obtain a local clone of
|
||||||
|
// a remote repo.
|
||||||
|
func ClonerUsingGitExec(repoSpec *RepoSpec) error {
|
||||||
|
gitProgram, err := exec.LookPath("git")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "no 'git' program on path")
|
||||||
|
}
|
||||||
|
repoSpec.Dir, err = filesys.NewTmpConfirmedDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd := exec.Command(
|
||||||
|
gitProgram,
|
||||||
|
"init",
|
||||||
|
repoSpec.Dir.String())
|
||||||
|
var out bytes.Buffer
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &out
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error initializing empty git repo: %s", out.String())
|
||||||
|
return errors.Wrapf(
|
||||||
|
err,
|
||||||
|
"trouble initializing empty git repo in %s",
|
||||||
|
repoSpec.Dir.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command(
|
||||||
|
gitProgram,
|
||||||
|
"remote",
|
||||||
|
"add",
|
||||||
|
"origin",
|
||||||
|
repoSpec.CloneSpec())
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &out
|
||||||
|
cmd.Dir = repoSpec.Dir.String()
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error setting git remote: %s", out.String())
|
||||||
|
return errors.Wrapf(
|
||||||
|
err,
|
||||||
|
"trouble adding remote %s",
|
||||||
|
repoSpec.CloneSpec())
|
||||||
|
}
|
||||||
|
if repoSpec.Ref == "" {
|
||||||
|
repoSpec.Ref = "master"
|
||||||
|
}
|
||||||
|
cmd = exec.Command(
|
||||||
|
gitProgram,
|
||||||
|
"fetch",
|
||||||
|
"--depth=1",
|
||||||
|
"origin",
|
||||||
|
repoSpec.Ref)
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &out
|
||||||
|
cmd.Dir = repoSpec.Dir.String()
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error performing git fetch: %s", out.String())
|
||||||
|
return errors.Wrapf(err, "trouble fetching %s", repoSpec.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command(
|
||||||
|
gitProgram,
|
||||||
|
"reset",
|
||||||
|
"--hard",
|
||||||
|
"FETCH_HEAD")
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Stderr = &out
|
||||||
|
cmd.Dir = repoSpec.Dir.String()
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error performing git reset: %s", out.String())
|
||||||
|
return errors.Wrapf(
|
||||||
|
err, "trouble hard resetting empty repository to %s", repoSpec.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = exec.Command(
|
||||||
|
gitProgram,
|
||||||
|
"submodule",
|
||||||
|
"update",
|
||||||
|
"--init",
|
||||||
|
"--recursive")
|
||||||
|
cmd.Stdout = &out
|
||||||
|
cmd.Dir = repoSpec.Dir.String()
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "trouble fetching submodules for %s", repoSpec.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DoNothingCloner returns a cloner that only sets
|
||||||
|
// cloneDir field in the repoSpec. It's assumed that
|
||||||
|
// the cloneDir is associated with some fake filesystem
|
||||||
|
// used in a test.
|
||||||
|
func DoNothingCloner(dir filesys.ConfirmedDir) Cloner {
|
||||||
|
return func(rs *RepoSpec) error {
|
||||||
|
rs.Dir = dir
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
219
api/internal/git/repospec.go
Normal file
219
api/internal/git/repospec.go
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Used as a temporary non-empty occupant of the cloneDir
|
||||||
|
// field, as something distinguishable from the empty string
|
||||||
|
// in various outputs (especially tests). Not using an
|
||||||
|
// actual directory name here, as that's a temporary directory
|
||||||
|
// with a unique name that isn't created until clone time.
|
||||||
|
const notCloned = filesys.ConfirmedDir("/notCloned")
|
||||||
|
|
||||||
|
// RepoSpec specifies a git repository and a branch and path therein.
|
||||||
|
type RepoSpec struct {
|
||||||
|
// Raw, original spec, used to look for cycles.
|
||||||
|
// TODO(monopole): Drop raw, use processed fields instead.
|
||||||
|
raw string
|
||||||
|
|
||||||
|
// Host, e.g. github.com
|
||||||
|
Host string
|
||||||
|
|
||||||
|
// orgRepo name (organization/repoName),
|
||||||
|
// e.g. kubernetes-sigs/kustomize
|
||||||
|
OrgRepo string
|
||||||
|
|
||||||
|
// Dir where the orgRepo is cloned to.
|
||||||
|
Dir filesys.ConfirmedDir
|
||||||
|
|
||||||
|
// Relative path in the repository, and in the cloneDir,
|
||||||
|
// to a Kustomization.
|
||||||
|
Path string
|
||||||
|
|
||||||
|
// Branch or tag reference.
|
||||||
|
Ref string
|
||||||
|
|
||||||
|
// e.g. .git or empty in case of _git is present
|
||||||
|
GitSuffix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloneSpec returns a string suitable for "git clone {spec}".
|
||||||
|
func (x *RepoSpec) CloneSpec() string {
|
||||||
|
if isAzureHost(x.Host) || isAWSHost(x.Host) {
|
||||||
|
return x.Host + x.OrgRepo
|
||||||
|
}
|
||||||
|
return x.Host + x.OrgRepo + x.GitSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RepoSpec) CloneDir() filesys.ConfirmedDir {
|
||||||
|
return x.Dir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RepoSpec) Raw() string {
|
||||||
|
return x.raw
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RepoSpec) AbsPath() string {
|
||||||
|
return x.Dir.Join(x.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *RepoSpec) Cleaner(fSys filesys.FileSystem) func() error {
|
||||||
|
return func() error { return fSys.RemoveAll(x.Dir.String()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// From strings like git@github.com:someOrg/someRepo.git or
|
||||||
|
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
||||||
|
// the parts.
|
||||||
|
func NewRepoSpecFromUrl(n string) (*RepoSpec, error) {
|
||||||
|
if filepath.IsAbs(n) {
|
||||||
|
return nil, fmt.Errorf("uri looks like abs path: %s", n)
|
||||||
|
}
|
||||||
|
host, orgRepo, path, gitRef, gitSuffix := parseGitUrl(n)
|
||||||
|
if orgRepo == "" {
|
||||||
|
return nil, fmt.Errorf("url lacks orgRepo: %s", n)
|
||||||
|
}
|
||||||
|
if host == "" {
|
||||||
|
return nil, fmt.Errorf("url lacks host: %s", n)
|
||||||
|
}
|
||||||
|
return &RepoSpec{
|
||||||
|
raw: n, Host: host, OrgRepo: orgRepo,
|
||||||
|
Dir: notCloned, Path: path, Ref: gitRef, GitSuffix: gitSuffix}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
refQuery = "?ref="
|
||||||
|
refQueryRegex = "\\?(version|ref)="
|
||||||
|
gitSuffix = ".git"
|
||||||
|
gitDelimiter = "_git/"
|
||||||
|
)
|
||||||
|
|
||||||
|
// From strings like git@github.com:someOrg/someRepo.git or
|
||||||
|
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
||||||
|
// the parts.
|
||||||
|
func parseGitUrl(n string) (
|
||||||
|
host string, orgRepo string, path string, gitRef string, gitSuff string) {
|
||||||
|
|
||||||
|
if strings.Contains(n, gitDelimiter) {
|
||||||
|
index := strings.Index(n, gitDelimiter)
|
||||||
|
// Adding _git/ to host
|
||||||
|
host = normalizeGitHostSpec(n[:index+len(gitDelimiter)])
|
||||||
|
orgRepo = strings.Split(strings.Split(n[index+len(gitDelimiter):], "/")[0], "?")[0]
|
||||||
|
path, gitRef = peelQuery(n[index+len(gitDelimiter)+len(orgRepo):])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
host, n = parseHostSpec(n)
|
||||||
|
gitSuff = gitSuffix
|
||||||
|
if strings.Contains(n, gitSuffix) {
|
||||||
|
index := strings.Index(n, gitSuffix)
|
||||||
|
orgRepo = n[0:index]
|
||||||
|
n = n[index+len(gitSuffix):]
|
||||||
|
path, gitRef = peelQuery(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
i := strings.Index(n, "/")
|
||||||
|
if i < 1 {
|
||||||
|
return "", "", "", "", ""
|
||||||
|
}
|
||||||
|
j := strings.Index(n[i+1:], "/")
|
||||||
|
if j >= 0 {
|
||||||
|
j += i + 1
|
||||||
|
orgRepo = n[:j]
|
||||||
|
path, gitRef = peelQuery(n[j+1:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
path = ""
|
||||||
|
orgRepo, gitRef = peelQuery(n)
|
||||||
|
return host, orgRepo, path, gitRef, gitSuff
|
||||||
|
}
|
||||||
|
|
||||||
|
func peelQuery(arg string) (string, string) {
|
||||||
|
|
||||||
|
r, _ := regexp.Compile(refQueryRegex)
|
||||||
|
j := r.FindStringIndex(arg)
|
||||||
|
|
||||||
|
if len(j) > 0 {
|
||||||
|
return arg[:j[0]], arg[j[0]+len(r.FindString(arg)):]
|
||||||
|
}
|
||||||
|
return arg, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHostSpec(n string) (string, string) {
|
||||||
|
var host string
|
||||||
|
// Start accumulating the host part.
|
||||||
|
for _, p := range []string{
|
||||||
|
// Order matters here.
|
||||||
|
"git::", "gh:", "ssh://", "https://", "http://",
|
||||||
|
"git@", "github.com:", "github.com/"} {
|
||||||
|
if len(p) < len(n) && strings.ToLower(n[:len(p)]) == p {
|
||||||
|
n = n[len(p):]
|
||||||
|
host += p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if host == "git@" {
|
||||||
|
i := strings.Index(n, "/")
|
||||||
|
if i > -1 {
|
||||||
|
host += n[:i+1]
|
||||||
|
n = n[i+1:]
|
||||||
|
} else {
|
||||||
|
i = strings.Index(n, ":")
|
||||||
|
if i > -1 {
|
||||||
|
host += n[:i+1]
|
||||||
|
n = n[i+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return host, n
|
||||||
|
}
|
||||||
|
|
||||||
|
// If host is a http(s) or ssh URL, grab the domain part.
|
||||||
|
for _, p := range []string{
|
||||||
|
"ssh://", "https://", "http://"} {
|
||||||
|
if strings.HasSuffix(host, p) {
|
||||||
|
i := strings.Index(n, "/")
|
||||||
|
if i > -1 {
|
||||||
|
host = host + n[0:i+1]
|
||||||
|
n = n[i+1:]
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizeGitHostSpec(host), n
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeGitHostSpec(host string) string {
|
||||||
|
s := strings.ToLower(host)
|
||||||
|
if strings.Contains(s, "github.com") {
|
||||||
|
if strings.Contains(s, "git@") || strings.Contains(s, "ssh:") {
|
||||||
|
host = "git@github.com:"
|
||||||
|
} else {
|
||||||
|
host = "https://github.com/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(s, "git::") {
|
||||||
|
host = strings.TrimPrefix(s, "git::")
|
||||||
|
}
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
|
||||||
|
// The format of Azure repo URL is documented
|
||||||
|
// https://docs.microsoft.com/en-us/azure/devops/repos/git/clone?view=vsts&tabs=visual-studio#clone_url
|
||||||
|
func isAzureHost(host string) bool {
|
||||||
|
return strings.Contains(host, "dev.azure.com") ||
|
||||||
|
strings.Contains(host, "visualstudio.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The format of AWS repo URL is documented
|
||||||
|
// https://docs.aws.amazon.com/codecommit/latest/userguide/regions.html
|
||||||
|
func isAWSHost(host string) bool {
|
||||||
|
return strings.Contains(host, "amazonaws.com")
|
||||||
|
}
|
||||||
288
api/internal/git/repospec_test.go
Normal file
288
api/internal/git/repospec_test.go
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package git
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var orgRepos = []string{"someOrg/someRepo", "kubernetes/website"}
|
||||||
|
|
||||||
|
var pathNames = []string{"README.md", "foo/krusty.txt", ""}
|
||||||
|
|
||||||
|
var hrefArgs = []string{"someBranch", "master", "v0.1.0", ""}
|
||||||
|
|
||||||
|
var hostNamesRawAndNormalized = [][]string{
|
||||||
|
{"gh:", "gh:"},
|
||||||
|
{"GH:", "gh:"},
|
||||||
|
{"gitHub.com/", "https://github.com/"},
|
||||||
|
{"github.com:", "https://github.com/"},
|
||||||
|
{"http://github.com/", "https://github.com/"},
|
||||||
|
{"https://github.com/", "https://github.com/"},
|
||||||
|
{"hTTps://github.com/", "https://github.com/"},
|
||||||
|
{"https://git-codecommit.us-east-2.amazonaws.com/", "https://git-codecommit.us-east-2.amazonaws.com/"},
|
||||||
|
{"https://fabrikops2.visualstudio.com/", "https://fabrikops2.visualstudio.com/"},
|
||||||
|
{"ssh://git.example.com:7999/", "ssh://git.example.com:7999/"},
|
||||||
|
{"git::https://gitlab.com/", "https://gitlab.com/"},
|
||||||
|
{"git::http://git.example.com/", "http://git.example.com/"},
|
||||||
|
{"git::https://git.example.com/", "https://git.example.com/"},
|
||||||
|
{"git@github.com:", "git@github.com:"},
|
||||||
|
{"git@github.com/", "git@github.com:"},
|
||||||
|
{"git@gitlab2.sqtools.ru:10022/", "git@gitlab2.sqtools.ru:10022/"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUrl(hostFmt, orgRepo, path, href string) string {
|
||||||
|
if len(path) > 0 {
|
||||||
|
orgRepo = filepath.Join(orgRepo, path)
|
||||||
|
}
|
||||||
|
url := hostFmt + orgRepo
|
||||||
|
if href != "" {
|
||||||
|
url += refQuery + href
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRepoSpecFromUrl(t *testing.T) {
|
||||||
|
var bad [][]string
|
||||||
|
for _, tuple := range hostNamesRawAndNormalized {
|
||||||
|
hostRaw := tuple[0]
|
||||||
|
hostSpec := tuple[1]
|
||||||
|
for _, orgRepo := range orgRepos {
|
||||||
|
for _, pathName := range pathNames {
|
||||||
|
for _, hrefArg := range hrefArgs {
|
||||||
|
uri := makeUrl(hostRaw, orgRepo, pathName, hrefArg)
|
||||||
|
rs, err := NewRepoSpecFromUrl(uri)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("problem %v", err)
|
||||||
|
}
|
||||||
|
if rs.Host != hostSpec {
|
||||||
|
bad = append(bad, []string{"host", uri, rs.Host, hostSpec})
|
||||||
|
}
|
||||||
|
if rs.OrgRepo != orgRepo {
|
||||||
|
bad = append(bad, []string{"orgRepo", uri, rs.OrgRepo, orgRepo})
|
||||||
|
}
|
||||||
|
if rs.Path != pathName {
|
||||||
|
bad = append(bad, []string{"path", uri, rs.Path, pathName})
|
||||||
|
}
|
||||||
|
if rs.Ref != hrefArg {
|
||||||
|
bad = append(bad, []string{"ref", uri, rs.Ref, hrefArg})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(bad) > 0 {
|
||||||
|
for _, tuple := range bad {
|
||||||
|
fmt.Printf("\n"+
|
||||||
|
" from uri: %s\n"+
|
||||||
|
" actual %4s: %s\n"+
|
||||||
|
"expected %4s: %s\n",
|
||||||
|
tuple[1], tuple[0], tuple[2], tuple[0], tuple[3])
|
||||||
|
}
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var badData = [][]string{
|
||||||
|
{"/tmp", "uri looks like abs path"},
|
||||||
|
{"iauhsdiuashduas", "url lacks orgRepo"},
|
||||||
|
{"htxxxtp://github.com/", "url lacks host"},
|
||||||
|
{"ssh://git.example.com", "url lacks orgRepo"},
|
||||||
|
{"git::___", "url lacks orgRepo"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRepoSpecFromUrlErrors(t *testing.T) {
|
||||||
|
for _, tuple := range badData {
|
||||||
|
_, err := NewRepoSpecFromUrl(tuple[0])
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), tuple[1]) {
|
||||||
|
t.Errorf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRepoSpecFromUrl_CloneSpecs(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
input string
|
||||||
|
cloneSpec string
|
||||||
|
absPath string
|
||||||
|
ref string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo/somedir",
|
||||||
|
cloneSpec: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo",
|
||||||
|
absPath: notCloned.Join("somedir"),
|
||||||
|
ref: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo/somedir?ref=testbranch",
|
||||||
|
cloneSpec: "https://git-codecommit.us-east-2.amazonaws.com/someorg/somerepo",
|
||||||
|
absPath: notCloned.Join("somedir"),
|
||||||
|
ref: "testbranch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "https://fabrikops2.visualstudio.com/someorg/somerepo?ref=master",
|
||||||
|
cloneSpec: "https://fabrikops2.visualstudio.com/someorg/somerepo",
|
||||||
|
absPath: notCloned.String(),
|
||||||
|
ref: "master",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "http://github.com/someorg/somerepo/somedir",
|
||||||
|
cloneSpec: "https://github.com/someorg/somerepo.git",
|
||||||
|
absPath: notCloned.Join("somedir"),
|
||||||
|
ref: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "git@github.com:someorg/somerepo/somedir",
|
||||||
|
cloneSpec: "git@github.com:someorg/somerepo.git",
|
||||||
|
absPath: notCloned.Join("somedir"),
|
||||||
|
ref: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git?ref=v0.1.0",
|
||||||
|
cloneSpec: "git@gitlab2.sqtools.ru:10022/infra/kubernetes/thanos-base.git",
|
||||||
|
absPath: notCloned.String(),
|
||||||
|
ref: "v0.1.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "git@bitbucket.org:company/project.git//path?ref=branch",
|
||||||
|
cloneSpec: "git@bitbucket.org:company/project.git",
|
||||||
|
absPath: notCloned.Join("path"),
|
||||||
|
ref: "branch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "https://itfs.mycompany.com/collection/project/_git/somerepos",
|
||||||
|
cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos",
|
||||||
|
absPath: notCloned.String(),
|
||||||
|
ref: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "https://itfs.mycompany.com/collection/project/_git/somerepos?version=v1.0.0",
|
||||||
|
cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos",
|
||||||
|
absPath: notCloned.String(),
|
||||||
|
ref: "v1.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "https://itfs.mycompany.com/collection/project/_git/somerepos/somedir?version=v1.0.0",
|
||||||
|
cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos",
|
||||||
|
absPath: notCloned.Join("somedir"),
|
||||||
|
ref: "v1.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "git::https://itfs.mycompany.com/collection/project/_git/somerepos",
|
||||||
|
cloneSpec: "https://itfs.mycompany.com/collection/project/_git/somerepos",
|
||||||
|
absPath: notCloned.String(),
|
||||||
|
ref: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
rs, err := NewRepoSpecFromUrl(testcase.input)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if rs.CloneSpec() != testcase.cloneSpec {
|
||||||
|
t.Errorf("CloneSpec expected to be %v, but got %v on %s",
|
||||||
|
testcase.cloneSpec, rs.CloneSpec(), testcase.input)
|
||||||
|
}
|
||||||
|
if rs.AbsPath() != testcase.absPath {
|
||||||
|
t.Errorf("AbsPath expected to be %v, but got %v on %s",
|
||||||
|
testcase.absPath, rs.AbsPath(), testcase.input)
|
||||||
|
}
|
||||||
|
if rs.Ref != testcase.ref {
|
||||||
|
t.Errorf("ref expected to be %v, but got %v on %s",
|
||||||
|
testcase.ref, rs.Ref, testcase.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsAzureHost(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
input string
|
||||||
|
expect bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "https://git-codecommit.us-east-2.amazonaws.com",
|
||||||
|
expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "ssh://git-codecommit.us-east-2.amazonaws.com",
|
||||||
|
expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "https://fabrikops2.visualstudio.com/",
|
||||||
|
expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "https://dev.azure.com/myorg/myproject/",
|
||||||
|
expect: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
actual := isAzureHost(testcase.input)
|
||||||
|
if actual != testcase.expect {
|
||||||
|
t.Errorf("IsAzureHost: expected %v, but got %v on %s", testcase.expect, actual, testcase.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeelQuery(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
input string
|
||||||
|
expect [2]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "somerepos?ref=v1.0.0",
|
||||||
|
expect: [2]string{"somerepos", "v1.0.0"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "somerepos?version=master",
|
||||||
|
expect: [2]string{"somerepos", "master"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "somerepos",
|
||||||
|
expect: [2]string{"somerepos", ""},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
path, ref := peelQuery(testcase.input)
|
||||||
|
if path != testcase.expect[0] || ref != testcase.expect[1] {
|
||||||
|
t.Errorf("peelQuery: expected (%s, %s) got (%s, %s) on %s", testcase.expect[0], testcase.expect[1], path, ref, testcase.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsAWSHost(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
input string
|
||||||
|
expect bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "https://git-codecommit.us-east-2.amazonaws.com",
|
||||||
|
expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "ssh://git-codecommit.us-east-2.amazonaws.com",
|
||||||
|
expect: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "git@github.com:",
|
||||||
|
expect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "http://github.com/",
|
||||||
|
expect: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
actual := isAWSHost(testcase.input)
|
||||||
|
if actual != testcase.expect {
|
||||||
|
t.Errorf("IsAWSHost: expected %v, but got %v on %s", testcase.expect, actual, testcase.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
72
api/internal/k8sdeps/configmapandsecret/configmapfactory.go
Normal file
72
api/internal/k8sdeps/configmapandsecret/configmapfactory.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package configmapandsecret generates configmaps and secrets per generator rules.
|
||||||
|
package configmapandsecret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeFreshConfigMap(
|
||||||
|
args *types.ConfigMapArgs) *v1.ConfigMap {
|
||||||
|
cm := &v1.ConfigMap{}
|
||||||
|
cm.APIVersion = "v1"
|
||||||
|
cm.Kind = "ConfigMap"
|
||||||
|
cm.Name = args.Name
|
||||||
|
cm.Namespace = args.Namespace
|
||||||
|
cm.Data = map[string]string{}
|
||||||
|
return cm
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeConfigMap returns a new ConfigMap, or nil and an error.
|
||||||
|
func (f *Factory) MakeConfigMap(
|
||||||
|
args *types.ConfigMapArgs) (*v1.ConfigMap, error) {
|
||||||
|
all, err := f.kvLdr.Load(args.KvPairSources)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "loading KV pairs")
|
||||||
|
}
|
||||||
|
cm := makeFreshConfigMap(args)
|
||||||
|
for _, p := range all {
|
||||||
|
err = f.addKvToConfigMap(cm, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "trouble mapping")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f.options != nil {
|
||||||
|
cm.SetLabels(f.options.Labels)
|
||||||
|
cm.SetAnnotations(f.options.Annotations)
|
||||||
|
}
|
||||||
|
return cm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addKvToConfigMap adds the given key and data to the given config map.
|
||||||
|
// Error if key invalid, or already exists.
|
||||||
|
func (f *Factory) addKvToConfigMap(configMap *v1.ConfigMap, p types.Pair) error {
|
||||||
|
if err := f.kvLdr.Validator().ErrIfInvalidKey(p.Key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// If the configmap data contains byte sequences that are all in the UTF-8
|
||||||
|
// range, we will write it to .Data
|
||||||
|
if utf8.Valid([]byte(p.Value)) {
|
||||||
|
if _, entryExists := configMap.Data[p.Key]; entryExists {
|
||||||
|
return fmt.Errorf(keyExistsErrorMsg, p.Key, configMap.Data)
|
||||||
|
}
|
||||||
|
configMap.Data[p.Key] = p.Value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// otherwise, it's BinaryData
|
||||||
|
if configMap.BinaryData == nil {
|
||||||
|
configMap.BinaryData = map[string][]byte{}
|
||||||
|
}
|
||||||
|
if _, entryExists := configMap.BinaryData[p.Key]; entryExists {
|
||||||
|
return fmt.Errorf(keyExistsErrorMsg, p.Key, configMap.BinaryData)
|
||||||
|
}
|
||||||
|
configMap.BinaryData[p.Key] = []byte(p.Value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
158
api/internal/k8sdeps/configmapandsecret/configmapfactory_test.go
Normal file
158
api/internal/k8sdeps/configmapandsecret/configmapfactory_test.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package configmapandsecret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
|
"sigs.k8s.io/kustomize/api/loader"
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1"
|
||||||
|
metav1 "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeEnvConfigMap(name string) *corev1.ConfigMap {
|
||||||
|
return &corev1.ConfigMap{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"DB_USERNAME": "admin",
|
||||||
|
"DB_PASSWORD": "somepw",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFileConfigMap(name string) *corev1.ConfigMap {
|
||||||
|
return &corev1.ConfigMap{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"app-init.ini": `FOO=bar
|
||||||
|
BAR=baz
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
BinaryData: map[string][]byte{
|
||||||
|
"app.bin": {0xff, 0xfd},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeLiteralConfigMap(name string) *corev1.ConfigMap {
|
||||||
|
cm := &corev1.ConfigMap{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string]string{
|
||||||
|
"a": "x",
|
||||||
|
"b": "y",
|
||||||
|
"c": "Hello World",
|
||||||
|
"d": "true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cm.SetLabels(map[string]string{"foo": "bar"})
|
||||||
|
return cm
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConstructConfigMap(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
description string
|
||||||
|
input types.ConfigMapArgs
|
||||||
|
options *types.GeneratorOptions
|
||||||
|
expected *corev1.ConfigMap
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []testCase{
|
||||||
|
{
|
||||||
|
description: "construct config map from env",
|
||||||
|
input: types.ConfigMapArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "envConfigMap",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
EnvSources: []string{
|
||||||
|
filepath.Join("configmap", "app.env"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: nil,
|
||||||
|
expected: makeEnvConfigMap("envConfigMap"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "construct config map from file",
|
||||||
|
input: types.ConfigMapArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "fileConfigMap",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
FileSources: []string{
|
||||||
|
filepath.Join("configmap", "app-init.ini"),
|
||||||
|
filepath.Join("configmap", "app.bin"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: nil,
|
||||||
|
expected: makeFileConfigMap("fileConfigMap"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "construct config map from literal",
|
||||||
|
input: types.ConfigMapArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "literalConfigMap",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
LiteralSources: []string{"a=x", "b=y", "c=\"Hello World\"", "d='true'"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: &types.GeneratorOptions{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: makeLiteralConfigMap("literalConfigMap"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("configmap", "app.env"),
|
||||||
|
[]byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n"))
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("configmap", "app-init.ini"),
|
||||||
|
[]byte("FOO=bar\nBAR=baz\n"))
|
||||||
|
fSys.WriteFile(
|
||||||
|
filesys.RootedPath("configmap", "app.bin"),
|
||||||
|
[]byte{0xff, 0xfd})
|
||||||
|
kvLdr := kv.NewLoader(
|
||||||
|
loader.NewFileLoaderAtRoot(fSys),
|
||||||
|
valtest_test.MakeFakeValidator())
|
||||||
|
for _, tc := range testCases {
|
||||||
|
f := NewFactory(kvLdr, tc.options)
|
||||||
|
cm, err := f.MakeConfigMap(&tc.input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(*cm, *tc.expected) {
|
||||||
|
t.Fatalf("in testcase: %q updated:\n%#v\ndoesn't match expected:\n%#v\n", tc.description, *cm, tc.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
api/internal/k8sdeps/configmapandsecret/factory.go
Normal file
23
api/internal/k8sdeps/configmapandsecret/factory.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package configmapandsecret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Factory makes ConfigMaps and Secrets.
|
||||||
|
type Factory struct {
|
||||||
|
kvLdr ifc.KvLoader
|
||||||
|
options *types.GeneratorOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFactory returns a new factory that makes ConfigMaps and Secrets.
|
||||||
|
func NewFactory(
|
||||||
|
kvLdr ifc.KvLoader, o *types.GeneratorOptions) *Factory {
|
||||||
|
return &Factory{kvLdr: kvLdr, options: o}
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyExistsErrorMsg = "cannot add key %s, another key by that name already exists: %v"
|
||||||
58
api/internal/k8sdeps/configmapandsecret/secretfactory.go
Normal file
58
api/internal/k8sdeps/configmapandsecret/secretfactory.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package configmapandsecret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeFreshSecret(
|
||||||
|
args *types.SecretArgs) *corev1.Secret {
|
||||||
|
s := &corev1.Secret{}
|
||||||
|
s.APIVersion = "v1"
|
||||||
|
s.Kind = "Secret"
|
||||||
|
s.Name = args.Name
|
||||||
|
s.Namespace = args.Namespace
|
||||||
|
s.Type = corev1.SecretType(args.Type)
|
||||||
|
if s.Type == "" {
|
||||||
|
s.Type = corev1.SecretTypeOpaque
|
||||||
|
}
|
||||||
|
s.Data = map[string][]byte{}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSecret returns a new secret.
|
||||||
|
func (f *Factory) MakeSecret(
|
||||||
|
args *types.SecretArgs) (*corev1.Secret, error) {
|
||||||
|
all, err := f.kvLdr.Load(args.KvPairSources)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s := makeFreshSecret(args)
|
||||||
|
for _, p := range all {
|
||||||
|
err = f.addKvToSecret(s, p.Key, p.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if f.options != nil {
|
||||||
|
s.SetLabels(f.options.Labels)
|
||||||
|
s.SetAnnotations(f.options.Annotations)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Factory) addKvToSecret(secret *corev1.Secret, keyName, data string) error {
|
||||||
|
if err := f.kvLdr.Validator().ErrIfInvalidKey(keyName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, entryExists := secret.Data[keyName]; entryExists {
|
||||||
|
return fmt.Errorf(keyExistsErrorMsg, keyName, secret.Data)
|
||||||
|
}
|
||||||
|
secret.Data[keyName] = []byte(data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
143
api/internal/k8sdeps/configmapandsecret/secretfactory_test.go
Normal file
143
api/internal/k8sdeps/configmapandsecret/secretfactory_test.go
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package configmapandsecret
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
|
"sigs.k8s.io/kustomize/api/loader"
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
corev1 "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1"
|
||||||
|
metav1 "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeEnvSecret(name string) *corev1.Secret {
|
||||||
|
return &corev1.Secret{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"DB_PASSWORD": []byte("somepw"),
|
||||||
|
"DB_USERNAME": []byte("admin"),
|
||||||
|
},
|
||||||
|
Type: "Opaque",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFileSecret(name string) *corev1.Secret {
|
||||||
|
return &corev1.Secret{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"app-init.ini": []byte(`FOO=bar
|
||||||
|
BAR=baz
|
||||||
|
`),
|
||||||
|
},
|
||||||
|
Type: "Opaque",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeLiteralSecret(name string) *corev1.Secret {
|
||||||
|
s := &corev1.Secret{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "v1",
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"a": []byte("x"),
|
||||||
|
"b": []byte("y"),
|
||||||
|
},
|
||||||
|
Type: "Opaque",
|
||||||
|
}
|
||||||
|
s.SetLabels(map[string]string{"foo": "bar"})
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConstructSecret(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
description string
|
||||||
|
input types.SecretArgs
|
||||||
|
options *types.GeneratorOptions
|
||||||
|
expected *corev1.Secret
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []testCase{
|
||||||
|
{
|
||||||
|
description: "construct secret from env",
|
||||||
|
input: types.SecretArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "envSecret",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
EnvSources: []string{"secret/app.env"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: nil,
|
||||||
|
expected: makeEnvSecret("envSecret"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "construct secret from file",
|
||||||
|
input: types.SecretArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "fileSecret",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
FileSources: []string{"secret/app-init.ini"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: nil,
|
||||||
|
expected: makeFileSecret("fileSecret"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "construct secret from literal",
|
||||||
|
input: types.SecretArgs{
|
||||||
|
GeneratorArgs: types.GeneratorArgs{
|
||||||
|
Name: "literalSecret",
|
||||||
|
KvPairSources: types.KvPairSources{
|
||||||
|
LiteralSources: []string{"a=x", "b=y"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: &types.GeneratorOptions{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: makeLiteralSecret("literalSecret"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
fSys.WriteFile("/secret/app.env", []byte("DB_USERNAME=admin\nDB_PASSWORD=somepw\n"))
|
||||||
|
fSys.WriteFile("/secret/app-init.ini", []byte("FOO=bar\nBAR=baz\n"))
|
||||||
|
kvLdr := kv.NewLoader(
|
||||||
|
loader.NewFileLoaderAtRoot(fSys),
|
||||||
|
valtest_test.MakeFakeValidator())
|
||||||
|
for _, tc := range testCases {
|
||||||
|
f := NewFactory(kvLdr, tc.options)
|
||||||
|
cm, err := f.MakeSecret(&tc.input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(*cm, *tc.expected) {
|
||||||
|
t.Fatalf("in testcase: %q updated:\n%#v\ndoesn't match expected:\n%#v\n", tc.description, *cm, tc.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
api/internal/k8sdeps/transformer/factory.go
Normal file
25
api/internal/k8sdeps/transformer/factory.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package transformer provides transformer factory
|
||||||
|
package transformer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/k8sdeps/transformer/patch"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FactoryImpl makes patch transformer and name hash transformer
|
||||||
|
type FactoryImpl struct{}
|
||||||
|
|
||||||
|
// NewFactoryImpl makes a new factoryImpl instance
|
||||||
|
func NewFactoryImpl() *FactoryImpl {
|
||||||
|
return &FactoryImpl{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *FactoryImpl) MergePatches(patches []*resource.Resource,
|
||||||
|
rf *resource.Factory) (
|
||||||
|
resmap.ResMap, error) {
|
||||||
|
return patch.MergePatches(patches, rf)
|
||||||
|
}
|
||||||
221
api/internal/k8sdeps/transformer/patch/conflictdetector.go
Normal file
221
api/internal/k8sdeps/transformer/patch/conflictdetector.go
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package patch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
jsonpatch "github.com/evanphx/json-patch"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/runtime"
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/runtime/schema"
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/util/mergepatch"
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/util/strategicpatch"
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/client-go/kubernetes/scheme"
|
||||||
|
)
|
||||||
|
|
||||||
|
type conflictDetector interface {
|
||||||
|
hasConflict(patch1, patch2 *resource.Resource) (bool, error)
|
||||||
|
findConflict(conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error)
|
||||||
|
mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonMergePatch struct {
|
||||||
|
rf *resource.Factory
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ conflictDetector = &jsonMergePatch{}
|
||||||
|
|
||||||
|
func newJMPConflictDetector(rf *resource.Factory) conflictDetector {
|
||||||
|
return &jsonMergePatch{rf: rf}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jmp *jsonMergePatch) hasConflict(
|
||||||
|
patch1, patch2 *resource.Resource) (bool, error) {
|
||||||
|
return mergepatch.HasConflicts(patch1.Map(), patch2.Map())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jmp *jsonMergePatch) findConflict(
|
||||||
|
conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error) {
|
||||||
|
for i, patch := range patches {
|
||||||
|
if i == conflictingPatchIdx {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !patches[conflictingPatchIdx].OrgId().Equals(patch.OrgId()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conflict, err := mergepatch.HasConflicts(
|
||||||
|
patch.Map(),
|
||||||
|
patches[conflictingPatchIdx].Map())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if conflict {
|
||||||
|
return patch, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jmp *jsonMergePatch) mergePatches(
|
||||||
|
patch1, patch2 *resource.Resource) (*resource.Resource, error) {
|
||||||
|
baseBytes, err := json.Marshal(patch1.Map())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patchBytes, err := json.Marshal(patch2.Map())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mergedBytes, err := jsonpatch.MergeMergePatches(baseBytes, patchBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mergedMap := make(map[string]interface{})
|
||||||
|
err = json.Unmarshal(mergedBytes, &mergedMap)
|
||||||
|
return jmp.rf.FromMap(mergedMap), err
|
||||||
|
}
|
||||||
|
|
||||||
|
type strategicMergePatch struct {
|
||||||
|
lookupPatchMeta strategicpatch.LookupPatchMeta
|
||||||
|
rf *resource.Factory
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ conflictDetector = &strategicMergePatch{}
|
||||||
|
|
||||||
|
func newSMPConflictDetector(
|
||||||
|
versionedObj runtime.Object,
|
||||||
|
rf *resource.Factory) (conflictDetector, error) {
|
||||||
|
lookupPatchMeta, err := strategicpatch.NewPatchMetaFromStruct(versionedObj)
|
||||||
|
return &strategicMergePatch{lookupPatchMeta: lookupPatchMeta, rf: rf}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smp *strategicMergePatch) hasConflict(p1, p2 *resource.Resource) (bool, error) {
|
||||||
|
return strategicpatch.MergingMapsHaveConflicts(
|
||||||
|
p1.Map(), p2.Map(), smp.lookupPatchMeta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smp *strategicMergePatch) findConflict(
|
||||||
|
conflictingPatchIdx int, patches []*resource.Resource) (*resource.Resource, error) {
|
||||||
|
for i, patch := range patches {
|
||||||
|
if i == conflictingPatchIdx {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !patches[conflictingPatchIdx].OrgId().Equals(patch.OrgId()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
conflict, err := strategicpatch.MergingMapsHaveConflicts(
|
||||||
|
patch.Map(),
|
||||||
|
patches[conflictingPatchIdx].Map(),
|
||||||
|
smp.lookupPatchMeta)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if conflict {
|
||||||
|
return patch, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (smp *strategicMergePatch) mergePatches(patch1, patch2 *resource.Resource) (*resource.Resource, error) {
|
||||||
|
if hasDeleteDirectiveMarker(patch2.Map()) {
|
||||||
|
if hasDeleteDirectiveMarker(patch1.Map()) {
|
||||||
|
return nil, fmt.Errorf("cannot merge patches both containing '$patch: delete' directives")
|
||||||
|
}
|
||||||
|
patch1, patch2 = patch2, patch1
|
||||||
|
}
|
||||||
|
mergeJSONMap, err := strategicpatch.MergeStrategicMergeMapPatchUsingLookupPatchMeta(
|
||||||
|
smp.lookupPatchMeta, patch1.Map(), patch2.Map())
|
||||||
|
return smp.rf.FromMap(mergeJSONMap), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergePatches merge and index patches by OrgId.
|
||||||
|
// It errors out if there is conflict between patches.
|
||||||
|
func MergePatches(patches []*resource.Resource,
|
||||||
|
rf *resource.Factory) (resmap.ResMap, error) {
|
||||||
|
rc := resmap.New()
|
||||||
|
for ix, patch := range patches {
|
||||||
|
id := patch.OrgId()
|
||||||
|
existing := rc.GetMatchingResourcesByOriginalId(id.Equals)
|
||||||
|
if len(existing) == 0 {
|
||||||
|
rc.Append(patch)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(existing) > 1 {
|
||||||
|
return nil, fmt.Errorf("self conflict in patches")
|
||||||
|
}
|
||||||
|
|
||||||
|
versionedObj, err := scheme.Scheme.New(toSchemaGvk(id.Gvk))
|
||||||
|
if err != nil && !runtime.IsNotRegisteredError(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var cd conflictDetector
|
||||||
|
if err != nil {
|
||||||
|
cd = newJMPConflictDetector(rf)
|
||||||
|
} else {
|
||||||
|
cd, err = newSMPConflictDetector(versionedObj, rf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conflict, err := cd.hasConflict(existing[0], patch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if conflict {
|
||||||
|
conflictingPatch, err := cd.findConflict(ix, patches)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"conflict between %#v and %#v",
|
||||||
|
conflictingPatch.Map(), patch.Map())
|
||||||
|
}
|
||||||
|
merged, err := cd.mergePatches(existing[0], patch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rc.Replace(merged)
|
||||||
|
}
|
||||||
|
return rc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// toSchemaGvk converts to a schema.GroupVersionKind.
|
||||||
|
func toSchemaGvk(x resid.Gvk) schema.GroupVersionKind {
|
||||||
|
return schema.GroupVersionKind{
|
||||||
|
Group: x.Group,
|
||||||
|
Version: x.Version,
|
||||||
|
Kind: x.Kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasDeleteDirectiveMarker(patch map[string]interface{}) bool {
|
||||||
|
if v, ok := patch["$patch"]; ok && v == "delete" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, v := range patch {
|
||||||
|
switch typedV := v.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
if hasDeleteDirectiveMarker(typedV) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case []interface{}:
|
||||||
|
for _, sv := range typedV {
|
||||||
|
typedE, ok := sv.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if hasDeleteDirectiveMarker(typedE) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
35
api/internal/kusterr/yamlformaterror.go
Normal file
35
api/internal/kusterr/yamlformaterror.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package error has contextual error types.
|
||||||
|
package kusterr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// YamlFormatError represents error with yaml file name where json/yaml format error happens.
|
||||||
|
type YamlFormatError struct {
|
||||||
|
Path string
|
||||||
|
ErrorMsg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e YamlFormatError) Error() string {
|
||||||
|
return fmt.Sprintf("YAML file [%s] encounters a format error.\n%s\n", e.Path, e.ErrorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler handles YamlFormatError
|
||||||
|
func Handler(e error, path string) error {
|
||||||
|
if isYAMLSyntaxError(e) {
|
||||||
|
return YamlFormatError{
|
||||||
|
Path: path,
|
||||||
|
ErrorMsg: e.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func isYAMLSyntaxError(e error) bool {
|
||||||
|
return strings.Contains(e.Error(), "error converting YAML to JSON") || strings.Contains(e.Error(), "error unmarshaling JSON")
|
||||||
|
}
|
||||||
41
api/internal/kusterr/yamlformaterror_test.go
Normal file
41
api/internal/kusterr/yamlformaterror_test.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package kusterr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
filepath = "/path/to/whatever"
|
||||||
|
expected = "YAML file [/path/to/whatever] encounters a format error.\n" +
|
||||||
|
"error converting YAML to JSON: yaml: line 2: found character that cannot start any token\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestYamlFormatError_Error(t *testing.T) {
|
||||||
|
testErr := YamlFormatError{
|
||||||
|
Path: filepath,
|
||||||
|
ErrorMsg: "error converting YAML to JSON: yaml: line 2: found character that cannot start any token",
|
||||||
|
}
|
||||||
|
if testErr.Error() != expected {
|
||||||
|
t.Errorf("Expected : %s\n, but found : %s\n", expected, testErr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorHandler(t *testing.T) {
|
||||||
|
err := fmt.Errorf("error converting YAML to JSON: yaml: line 2: found character that cannot start any token")
|
||||||
|
testErr := Handler(err, filepath)
|
||||||
|
expectedErr := fmt.Errorf("format error message")
|
||||||
|
fmtErr := Handler(expectedErr, filepath)
|
||||||
|
if fmtErr.Error() != expectedErr.Error() {
|
||||||
|
t.Errorf("Expected returning fmt.Error, but found %T", fmtErr)
|
||||||
|
}
|
||||||
|
if _, ok := testErr.(YamlFormatError); !ok {
|
||||||
|
t.Errorf("Expected returning YamlFormatError, but found %T", testErr)
|
||||||
|
}
|
||||||
|
if testErr == nil || testErr.Error() != expected {
|
||||||
|
t.Errorf("Expected : %s\n, but found : %s\n", expected, testErr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
76
api/internal/loadertest/fakeloader.go
Normal file
76
api/internal/loadertest/fakeloader.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package loadertest holds a fake for the Loader interface.
|
||||||
|
package loadertest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/loader"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FakeLoader encapsulates the delegate Loader and the fake file system.
|
||||||
|
type FakeLoader struct {
|
||||||
|
fs filesys.FileSystem
|
||||||
|
delegate ifc.Loader
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFakeLoader returns a Loader that uses a fake filesystem.
|
||||||
|
// The loader will be restricted to root only.
|
||||||
|
// The initialDir argument should be an absolute file path.
|
||||||
|
func NewFakeLoader(initialDir string) FakeLoader {
|
||||||
|
return NewFakeLoaderWithRestrictor(
|
||||||
|
loader.RestrictionRootOnly, initialDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFakeLoaderWithRestrictor returns a Loader that
|
||||||
|
// uses a fake filesystem.
|
||||||
|
// The initialDir argument should be an absolute file path.
|
||||||
|
func NewFakeLoaderWithRestrictor(
|
||||||
|
lr loader.LoadRestrictorFunc, initialDir string) FakeLoader {
|
||||||
|
// Create fake filesystem and inject it into initial Loader.
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
fSys.Mkdir(initialDir)
|
||||||
|
ldr, err := loader.NewLoader(lr, initialDir, fSys)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to make loader: %v", err)
|
||||||
|
}
|
||||||
|
return FakeLoader{fs: fSys, delegate: ldr}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFile adds a fake file to the file system.
|
||||||
|
func (f FakeLoader) AddFile(fullFilePath string, content []byte) error {
|
||||||
|
return f.fs.WriteFile(fullFilePath, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddDirectory adds a fake directory to the file system.
|
||||||
|
func (f FakeLoader) AddDirectory(fullDirPath string) error {
|
||||||
|
return f.fs.Mkdir(fullDirPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root delegates.
|
||||||
|
func (f FakeLoader) Root() string {
|
||||||
|
return f.delegate.Root()
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new loader from a new root.
|
||||||
|
func (f FakeLoader) New(newRoot string) (ifc.Loader, error) {
|
||||||
|
l, err := f.delegate.New(newRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return FakeLoader{fs: f.fs, delegate: l}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load delegates.
|
||||||
|
func (f FakeLoader) Load(location string) ([]byte, error) {
|
||||||
|
return f.delegate.Load(location)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup delegates.
|
||||||
|
func (f FakeLoader) Cleanup() error {
|
||||||
|
return f.delegate.Cleanup()
|
||||||
|
}
|
||||||
10
api/internal/plugins/builtinconfig/doc.go
Normal file
10
api/internal/plugins/builtinconfig/doc.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package builtinconfig provides legacy methods for
|
||||||
|
// configuring builtin plugins from a common config file.
|
||||||
|
// As a user, its best to configure plugins individually
|
||||||
|
// with plugin config files specified in the `transformers:`
|
||||||
|
// or `generators:` field, than to use this legacy
|
||||||
|
// configuration technique.
|
||||||
|
package builtinconfig
|
||||||
42
api/internal/plugins/builtinconfig/loaddefaultconfig.go
Normal file
42
api/internal/plugins/builtinconfig/loaddefaultconfig.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// loadDefaultConfig returns a TranformerConfig
|
||||||
|
// object from a list of files.
|
||||||
|
func loadDefaultConfig(
|
||||||
|
ldr ifc.Loader, paths []string) (*TransformerConfig, error) {
|
||||||
|
result := &TransformerConfig{}
|
||||||
|
for _, path := range paths {
|
||||||
|
data, err := ldr.Load(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t, err := makeTransformerConfigFromBytes(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result, err = result.Merge(t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeTransformerConfigFromBytes returns a TransformerConfig object from bytes
|
||||||
|
func makeTransformerConfigFromBytes(data []byte) (*TransformerConfig, error) {
|
||||||
|
var t TransformerConfig
|
||||||
|
err := yaml.Unmarshal(data, &t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.sortFields()
|
||||||
|
return &t, nil
|
||||||
|
}
|
||||||
37
api/internal/plugins/builtinconfig/loaddefaultconfig_test.go
Normal file
37
api/internal/plugins/builtinconfig/loaddefaultconfig_test.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/loadertest"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadDefaultConfigsFromFiles(t *testing.T) {
|
||||||
|
ldr := loadertest.NewFakeLoader("/app")
|
||||||
|
ldr.AddFile("/app/config.yaml", []byte(`
|
||||||
|
namePrefix:
|
||||||
|
- path: nameprefix/path
|
||||||
|
kind: SomeKind
|
||||||
|
`))
|
||||||
|
tcfg, err := loadDefaultConfig(ldr, []string{"/app/config.yaml"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
expected := &TransformerConfig{
|
||||||
|
NamePrefix: []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{Kind: "SomeKind"},
|
||||||
|
Path: "nameprefix/path",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tcfg, expected) {
|
||||||
|
t.Fatalf("expected %v\n but go6t %v\n", expected, tcfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
93
api/internal/plugins/builtinconfig/namebackreferences.go
Normal file
93
api/internal/plugins/builtinconfig/namebackreferences.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NameBackReferences is an association between a gvk.GVK and a list
|
||||||
|
// of FieldSpec instances that could refer to it.
|
||||||
|
//
|
||||||
|
// It is used to handle name changes, and can be thought of as a
|
||||||
|
// a contact list. If you change your own contact info (name,
|
||||||
|
// phone number, etc.), you must tell your contacts or they won't
|
||||||
|
// know about the change.
|
||||||
|
//
|
||||||
|
// For example, ConfigMaps can be used by Pods and everything that
|
||||||
|
// contains a Pod; Deployment, Job, StatefulSet, etc. To change
|
||||||
|
// the name of a ConfigMap instance from 'alice' to 'bob', one
|
||||||
|
// must visit all objects that could refer to the ConfigMap, see if
|
||||||
|
// they mention 'alice', and if so, change the reference to 'bob'.
|
||||||
|
//
|
||||||
|
// The NameBackReferences instance to aid in this could look like
|
||||||
|
// {
|
||||||
|
// kind: ConfigMap
|
||||||
|
// version: v1
|
||||||
|
// FieldSpecs:
|
||||||
|
// - kind: Pod
|
||||||
|
// version: v1
|
||||||
|
// path: spec/volumes/configMap/name
|
||||||
|
// - kind: Deployment
|
||||||
|
// path: spec/template/spec/volumes/configMap/name
|
||||||
|
// - kind: Job
|
||||||
|
// path: spec/template/spec/volumes/configMap/name
|
||||||
|
// (etc.)
|
||||||
|
// }
|
||||||
|
type NameBackReferences struct {
|
||||||
|
resid.Gvk `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||||
|
FieldSpecs types.FsSlice `json:"FieldSpecs,omitempty" yaml:"FieldSpecs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n NameBackReferences) String() string {
|
||||||
|
var r []string
|
||||||
|
for _, f := range n.FieldSpecs {
|
||||||
|
r = append(r, f.String())
|
||||||
|
}
|
||||||
|
return n.Gvk.String() + ": (\n" +
|
||||||
|
strings.Join(r, "\n") + "\n)"
|
||||||
|
}
|
||||||
|
|
||||||
|
type nbrSlice []NameBackReferences
|
||||||
|
|
||||||
|
func (s nbrSlice) Len() int { return len(s) }
|
||||||
|
func (s nbrSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
func (s nbrSlice) Less(i, j int) bool {
|
||||||
|
return s[i].Gvk.IsLessThan(s[j].Gvk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s nbrSlice) mergeAll(o nbrSlice) (result nbrSlice, err error) {
|
||||||
|
result = s
|
||||||
|
for _, r := range o {
|
||||||
|
result, err = result.mergeOne(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s nbrSlice) mergeOne(other NameBackReferences) (nbrSlice, error) {
|
||||||
|
var result nbrSlice
|
||||||
|
var err error
|
||||||
|
found := false
|
||||||
|
for _, c := range s {
|
||||||
|
if c.Gvk.Equals(other.Gvk) {
|
||||||
|
c.FieldSpecs, err = c.FieldSpecs.MergeAll(other.FieldSpecs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
result = append(result, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
result = append(result, other)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMergeAll(t *testing.T) {
|
||||||
|
fsSlice1 := []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "Pod",
|
||||||
|
},
|
||||||
|
Path: "path/to/a/name",
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "Deployment",
|
||||||
|
},
|
||||||
|
Path: "another/path/to/some/name",
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fsSlice2 := []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "Job",
|
||||||
|
},
|
||||||
|
Path: "morepath/to/name",
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "StatefulSet",
|
||||||
|
},
|
||||||
|
Path: "yet/another/path/to/a/name",
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
nbrsSlice1 := nbrSlice{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
},
|
||||||
|
FieldSpecs: fsSlice1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
FieldSpecs: fsSlice2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nbrsSlice2 := nbrSlice{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
},
|
||||||
|
FieldSpecs: fsSlice1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
FieldSpecs: fsSlice2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expected := nbrSlice{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "ConfigMap",
|
||||||
|
},
|
||||||
|
FieldSpecs: fsSlice1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "Secret",
|
||||||
|
},
|
||||||
|
FieldSpecs: fsSlice2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
actual, err := nbrsSlice1.mergeAll(nbrsSlice2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("expected\n %v\n but got\n %v\n", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
148
api/internal/plugins/builtinconfig/transformerconfig.go
Normal file
148
api/internal/plugins/builtinconfig/transformerconfig.go
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig/builtinpluginconsts"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TransformerConfig holds the data needed to perform transformations.
|
||||||
|
type TransformerConfig struct {
|
||||||
|
NamePrefix types.FsSlice `json:"namePrefix,omitempty" yaml:"namePrefix,omitempty"`
|
||||||
|
NameSuffix types.FsSlice `json:"nameSuffix,omitempty" yaml:"nameSuffix,omitempty"`
|
||||||
|
NameSpace types.FsSlice `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||||
|
CommonLabels types.FsSlice `json:"commonLabels,omitempty" yaml:"commonLabels,omitempty"`
|
||||||
|
CommonAnnotations types.FsSlice `json:"commonAnnotations,omitempty" yaml:"commonAnnotations,omitempty"`
|
||||||
|
NameReference nbrSlice `json:"nameReference,omitempty" yaml:"nameReference,omitempty"`
|
||||||
|
VarReference types.FsSlice `json:"varReference,omitempty" yaml:"varReference,omitempty"`
|
||||||
|
Images types.FsSlice `json:"images,omitempty" yaml:"images,omitempty"`
|
||||||
|
Replicas types.FsSlice `json:"replicas,omitempty" yaml:"replicas,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeEmptyConfig returns an empty TransformerConfig object
|
||||||
|
func MakeEmptyConfig() *TransformerConfig {
|
||||||
|
return &TransformerConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeDefaultConfig returns a default TransformerConfig.
|
||||||
|
func MakeDefaultConfig() *TransformerConfig {
|
||||||
|
c, err := makeTransformerConfigFromBytes(
|
||||||
|
builtinpluginconsts.GetDefaultFieldSpecs())
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Unable to make default transformconfig: %v", err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeTransformerConfig returns a merger of custom config,
|
||||||
|
// if any, with default config.
|
||||||
|
func MakeTransformerConfig(
|
||||||
|
ldr ifc.Loader, paths []string) (*TransformerConfig, error) {
|
||||||
|
t1 := MakeDefaultConfig()
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return t1, nil
|
||||||
|
}
|
||||||
|
t2, err := loadDefaultConfig(ldr, paths)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return t1.Merge(t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortFields provides determinism in logging, tests, etc.
|
||||||
|
func (t *TransformerConfig) sortFields() {
|
||||||
|
sort.Sort(t.NamePrefix)
|
||||||
|
sort.Sort(t.NameSpace)
|
||||||
|
sort.Sort(t.CommonLabels)
|
||||||
|
sort.Sort(t.CommonAnnotations)
|
||||||
|
sort.Sort(t.NameReference)
|
||||||
|
sort.Sort(t.VarReference)
|
||||||
|
sort.Sort(t.Images)
|
||||||
|
sort.Sort(t.Replicas)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPrefixFieldSpec adds a FieldSpec to NamePrefix
|
||||||
|
func (t *TransformerConfig) AddPrefixFieldSpec(fs types.FieldSpec) (err error) {
|
||||||
|
t.NamePrefix, err = t.NamePrefix.MergeOne(fs)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSuffixFieldSpec adds a FieldSpec to NameSuffix
|
||||||
|
func (t *TransformerConfig) AddSuffixFieldSpec(fs types.FieldSpec) (err error) {
|
||||||
|
t.NameSuffix, err = t.NameSuffix.MergeOne(fs)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddLabelFieldSpec adds a FieldSpec to CommonLabels
|
||||||
|
func (t *TransformerConfig) AddLabelFieldSpec(fs types.FieldSpec) (err error) {
|
||||||
|
t.CommonLabels, err = t.CommonLabels.MergeOne(fs)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAnnotationFieldSpec adds a FieldSpec to CommonAnnotations
|
||||||
|
func (t *TransformerConfig) AddAnnotationFieldSpec(fs types.FieldSpec) (err error) {
|
||||||
|
t.CommonAnnotations, err = t.CommonAnnotations.MergeOne(fs)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNamereferenceFieldSpec adds a NameBackReferences to NameReference
|
||||||
|
func (t *TransformerConfig) AddNamereferenceFieldSpec(
|
||||||
|
nbrs NameBackReferences) (err error) {
|
||||||
|
t.NameReference, err = t.NameReference.mergeOne(nbrs)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges two TransformerConfigs objects into
|
||||||
|
// a new TransformerConfig object
|
||||||
|
func (t *TransformerConfig) Merge(input *TransformerConfig) (
|
||||||
|
merged *TransformerConfig, err error) {
|
||||||
|
if input == nil {
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
merged = &TransformerConfig{}
|
||||||
|
merged.NamePrefix, err = t.NamePrefix.MergeAll(input.NamePrefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.NameSuffix, err = t.NameSuffix.MergeAll(input.NameSuffix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.NameSpace, err = t.NameSpace.MergeAll(input.NameSpace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.CommonAnnotations, err = t.CommonAnnotations.MergeAll(
|
||||||
|
input.CommonAnnotations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.CommonLabels, err = t.CommonLabels.MergeAll(input.CommonLabels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.VarReference, err = t.VarReference.MergeAll(input.VarReference)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.NameReference, err = t.NameReference.mergeAll(input.NameReference)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.Images, err = t.Images.MergeAll(input.Images)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.Replicas, err = t.Replicas.MergeAll(input.Replicas)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
merged.sortFields()
|
||||||
|
return merged, nil
|
||||||
|
}
|
||||||
175
api/internal/plugins/builtinconfig/transformerconfig_test.go
Normal file
175
api/internal/plugins/builtinconfig/transformerconfig_test.go
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinconfig_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMakeDefaultConfig(t *testing.T) {
|
||||||
|
// Confirm default can be made without fatal error inside call.
|
||||||
|
_ = MakeDefaultConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddNamereferenceFieldSpec(t *testing.T) {
|
||||||
|
cfg := &TransformerConfig{}
|
||||||
|
|
||||||
|
nbrs := NameBackReferences{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "KindA",
|
||||||
|
},
|
||||||
|
FieldSpecs: []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "KindB",
|
||||||
|
},
|
||||||
|
Path: "path/to/a/field",
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cfg.AddNamereferenceFieldSpec(nbrs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.NameReference) != 1 {
|
||||||
|
t.Fatal("failed to add namereference FieldSpec")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddFieldSpecs(t *testing.T) {
|
||||||
|
cfg := &TransformerConfig{}
|
||||||
|
|
||||||
|
fieldSpec := types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Group: "GroupA", Kind: "KindB"},
|
||||||
|
Path: "path/to/a/field",
|
||||||
|
CreateIfNotPresent: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := cfg.AddPrefixFieldSpec(fieldSpec)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.NamePrefix) != 1 {
|
||||||
|
t.Fatalf("failed to add nameprefix FieldSpec")
|
||||||
|
}
|
||||||
|
err = cfg.AddSuffixFieldSpec(fieldSpec)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.NameSuffix) != 1 {
|
||||||
|
t.Fatalf("failed to add namesuffix FieldSpec")
|
||||||
|
}
|
||||||
|
err = cfg.AddLabelFieldSpec(fieldSpec)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.CommonLabels) != 1 {
|
||||||
|
t.Fatalf("failed to add nameprefix FieldSpec")
|
||||||
|
}
|
||||||
|
err = cfg.AddAnnotationFieldSpec(fieldSpec)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if len(cfg.CommonAnnotations) != 1 {
|
||||||
|
t.Fatalf("failed to add nameprefix FieldSpec")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMerge(t *testing.T) {
|
||||||
|
nameReference := []NameBackReferences{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "KindA",
|
||||||
|
},
|
||||||
|
FieldSpecs: []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "KindB",
|
||||||
|
},
|
||||||
|
Path: "path/to/a/field",
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "KindA",
|
||||||
|
},
|
||||||
|
FieldSpecs: []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{
|
||||||
|
Kind: "KindC",
|
||||||
|
},
|
||||||
|
Path: "path/to/a/field",
|
||||||
|
CreateIfNotPresent: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fieldSpecs := []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{Group: "GroupA", Kind: "KindB"},
|
||||||
|
Path: "path/to/a/field",
|
||||||
|
CreateIfNotPresent: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Gvk: resid.Gvk{Group: "GroupA", Kind: "KindC"},
|
||||||
|
Path: "path/to/a/field",
|
||||||
|
CreateIfNotPresent: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cfga := &TransformerConfig{}
|
||||||
|
cfga.AddNamereferenceFieldSpec(nameReference[0])
|
||||||
|
cfga.AddPrefixFieldSpec(fieldSpecs[0])
|
||||||
|
cfga.AddSuffixFieldSpec(fieldSpecs[0])
|
||||||
|
|
||||||
|
cfgb := &TransformerConfig{}
|
||||||
|
cfgb.AddNamereferenceFieldSpec(nameReference[1])
|
||||||
|
cfgb.AddPrefixFieldSpec(fieldSpecs[1])
|
||||||
|
cfga.AddSuffixFieldSpec(fieldSpecs[1])
|
||||||
|
|
||||||
|
actual, err := cfga.Merge(cfgb)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(actual.NamePrefix) != 2 {
|
||||||
|
t.Fatal("merge failed for namePrefix FieldSpec")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(actual.NameSuffix) != 2 {
|
||||||
|
t.Fatal("merge failed for nameSuffix FieldSpec")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(actual.NameReference) != 1 {
|
||||||
|
t.Fatal("merge failed for namereference FieldSpec")
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := &TransformerConfig{}
|
||||||
|
expected.AddNamereferenceFieldSpec(nameReference[0])
|
||||||
|
expected.AddNamereferenceFieldSpec(nameReference[1])
|
||||||
|
expected.AddPrefixFieldSpec(fieldSpecs[0])
|
||||||
|
expected.AddPrefixFieldSpec(fieldSpecs[1])
|
||||||
|
expected.AddSuffixFieldSpec(fieldSpecs[0])
|
||||||
|
expected.AddSuffixFieldSpec(fieldSpecs[1])
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, expected) {
|
||||||
|
t.Fatalf("expected: %v\n but got: %v\n", expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err = cfga.Merge(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actual, cfga) {
|
||||||
|
t.Fatalf("expected: %v\n but got: %v\n", cfga, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Code generated by "stringer -type=BuiltinPluginType"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package builtinhelpers
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Unknown-0]
|
||||||
|
_ = x[AnnotationsTransformer-1]
|
||||||
|
_ = x[ConfigMapGenerator-2]
|
||||||
|
_ = x[HashTransformer-3]
|
||||||
|
_ = x[ImageTagTransformer-4]
|
||||||
|
_ = x[InventoryTransformer-5]
|
||||||
|
_ = x[LabelTransformer-6]
|
||||||
|
_ = x[LegacyOrderTransformer-7]
|
||||||
|
_ = x[NamespaceTransformer-8]
|
||||||
|
_ = x[PatchJson6902Transformer-9]
|
||||||
|
_ = x[PatchStrategicMergeTransformer-10]
|
||||||
|
_ = x[PatchTransformer-11]
|
||||||
|
_ = x[PrefixSuffixTransformer-12]
|
||||||
|
_ = x[ReplicaCountTransformer-13]
|
||||||
|
_ = x[SecretGenerator-14]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _BuiltinPluginType_name = "UnknownAnnotationsTransformerConfigMapGeneratorHashTransformerImageTagTransformerInventoryTransformerLabelTransformerLegacyOrderTransformerNamespaceTransformerPatchJson6902TransformerPatchStrategicMergeTransformerPatchTransformerPrefixSuffixTransformerReplicaCountTransformerSecretGenerator"
|
||||||
|
|
||||||
|
var _BuiltinPluginType_index = [...]uint16{0, 7, 29, 47, 62, 81, 101, 117, 139, 159, 183, 213, 229, 252, 275, 290}
|
||||||
|
|
||||||
|
func (i BuiltinPluginType) String() string {
|
||||||
|
if i < 0 || i >= BuiltinPluginType(len(_BuiltinPluginType_index)-1) {
|
||||||
|
return "BuiltinPluginType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _BuiltinPluginType_name[_BuiltinPluginType_index[i]:_BuiltinPluginType_index[i+1]]
|
||||||
|
}
|
||||||
75
api/internal/plugins/builtinhelpers/builtins.go
Normal file
75
api/internal/plugins/builtinhelpers/builtins.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/builtins"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate stringer -type=BuiltinPluginType
|
||||||
|
type BuiltinPluginType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Unknown BuiltinPluginType = iota
|
||||||
|
AnnotationsTransformer
|
||||||
|
ConfigMapGenerator
|
||||||
|
HashTransformer
|
||||||
|
ImageTagTransformer
|
||||||
|
InventoryTransformer
|
||||||
|
LabelTransformer
|
||||||
|
LegacyOrderTransformer
|
||||||
|
NamespaceTransformer
|
||||||
|
PatchJson6902Transformer
|
||||||
|
PatchStrategicMergeTransformer
|
||||||
|
PatchTransformer
|
||||||
|
PrefixSuffixTransformer
|
||||||
|
ReplicaCountTransformer
|
||||||
|
SecretGenerator
|
||||||
|
)
|
||||||
|
|
||||||
|
var stringToBuiltinPluginTypeMap map[string]BuiltinPluginType
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
stringToBuiltinPluginTypeMap = makeStringToBuiltinPluginTypeMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeStringToBuiltinPluginTypeMap() (result map[string]BuiltinPluginType) {
|
||||||
|
result = make(map[string]BuiltinPluginType, 23)
|
||||||
|
for k := range GeneratorFactories {
|
||||||
|
result[k.String()] = k
|
||||||
|
}
|
||||||
|
for k := range TransformerFactories {
|
||||||
|
result[k.String()] = k
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBuiltinPluginType(n string) BuiltinPluginType {
|
||||||
|
result, ok := stringToBuiltinPluginTypeMap[n]
|
||||||
|
if ok {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
var GeneratorFactories = map[BuiltinPluginType]func() resmap.GeneratorPlugin{
|
||||||
|
ConfigMapGenerator: builtins.NewConfigMapGeneratorPlugin,
|
||||||
|
SecretGenerator: builtins.NewSecretGeneratorPlugin,
|
||||||
|
}
|
||||||
|
|
||||||
|
var TransformerFactories = map[BuiltinPluginType]func() resmap.TransformerPlugin{
|
||||||
|
AnnotationsTransformer: builtins.NewAnnotationsTransformerPlugin,
|
||||||
|
HashTransformer: builtins.NewHashTransformerPlugin,
|
||||||
|
ImageTagTransformer: builtins.NewImageTagTransformerPlugin,
|
||||||
|
InventoryTransformer: builtins.NewInventoryTransformerPlugin,
|
||||||
|
LabelTransformer: builtins.NewLabelTransformerPlugin,
|
||||||
|
LegacyOrderTransformer: builtins.NewLegacyOrderTransformerPlugin,
|
||||||
|
NamespaceTransformer: builtins.NewNamespaceTransformerPlugin,
|
||||||
|
PatchJson6902Transformer: builtins.NewPatchJson6902TransformerPlugin,
|
||||||
|
PatchStrategicMergeTransformer: builtins.NewPatchStrategicMergeTransformerPlugin,
|
||||||
|
PatchTransformer: builtins.NewPatchTransformerPlugin,
|
||||||
|
PrefixSuffixTransformer: builtins.NewPrefixSuffixTransformerPlugin,
|
||||||
|
ReplicaCountTransformer: builtins.NewReplicaCountTransformerPlugin,
|
||||||
|
}
|
||||||
173
api/internal/plugins/compiler/compiler.go
Normal file
173
api/internal/plugins/compiler/compiler.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compiler creates Go plugin object files.
|
||||||
|
//
|
||||||
|
// Source code is read from
|
||||||
|
// ${srcRoot}/${g}/${v}/${k}.go
|
||||||
|
//
|
||||||
|
// Object code is written to
|
||||||
|
// ${objRoot}/${g}/${v}/${k}.so
|
||||||
|
type Compiler struct {
|
||||||
|
srcRoot string
|
||||||
|
objRoot string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeterminePluginSrcRoot guesses where the user
|
||||||
|
// has her ${g}/${v}/$lower(${k})/${k}.go files.
|
||||||
|
func DeterminePluginSrcRoot(fSys filesys.FileSystem) (string, error) {
|
||||||
|
return konfig.FirstDirThatExistsElseError(
|
||||||
|
"source directory", fSys, []konfig.NotedFunc{
|
||||||
|
{
|
||||||
|
Note: "relative to unit test",
|
||||||
|
F: func() string {
|
||||||
|
return filepath.Clean(
|
||||||
|
filepath.Join(
|
||||||
|
os.Getenv("PWD"),
|
||||||
|
"..", "..", "..", "..",
|
||||||
|
konfig.RelPluginHome))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Note: "relative to api package",
|
||||||
|
F: func() string {
|
||||||
|
return filepath.Clean(
|
||||||
|
filepath.Join(
|
||||||
|
os.Getenv("PWD"),
|
||||||
|
"..", "..", "..",
|
||||||
|
konfig.RelPluginHome))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Note: "old style $GOPATH",
|
||||||
|
F: func() string {
|
||||||
|
return filepath.Join(
|
||||||
|
os.Getenv("GOPATH"),
|
||||||
|
"src", konfig.DomainName,
|
||||||
|
konfig.ProgramName, konfig.RelPluginHome)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Note: "HOME with literal 'gopath'",
|
||||||
|
F: func() string {
|
||||||
|
return filepath.Join(
|
||||||
|
konfig.HomeDir(), "gopath",
|
||||||
|
"src", konfig.DomainName,
|
||||||
|
konfig.ProgramName, konfig.RelPluginHome)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Note: "home directory",
|
||||||
|
F: func() string {
|
||||||
|
return filepath.Join(
|
||||||
|
konfig.HomeDir(), konfig.DomainName,
|
||||||
|
konfig.ProgramName, konfig.RelPluginHome)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCompiler returns a new compiler instance.
|
||||||
|
func NewCompiler(srcRoot, objRoot string) *Compiler {
|
||||||
|
return &Compiler{srcRoot: srcRoot, objRoot: objRoot}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjRoot is root of compilation target tree.
|
||||||
|
func (b *Compiler) ObjRoot() string {
|
||||||
|
return b.objRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
// SrcRoot is where to find src.
|
||||||
|
func (b *Compiler) SrcRoot() string {
|
||||||
|
return b.srcRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
func goBin() string {
|
||||||
|
return filepath.Join(runtime.GOROOT(), "bin", "go")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile reads ${srcRoot}/${g}/${v}/${k}.go
|
||||||
|
// and writes ${objRoot}/${g}/${v}/${k}.so
|
||||||
|
func (b *Compiler) Compile(g, v, k string) error {
|
||||||
|
lowK := strings.ToLower(k)
|
||||||
|
objDir := filepath.Join(b.objRoot, g, v, lowK)
|
||||||
|
objFile := filepath.Join(objDir, k) + ".so"
|
||||||
|
if RecentFileExists(objFile) {
|
||||||
|
// Skip rebuilding it.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := os.MkdirAll(objDir, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
srcFile := filepath.Join(b.srcRoot, g, v, lowK, k) + ".go"
|
||||||
|
if !FileExists(srcFile) {
|
||||||
|
// Handy for tests of lone plugins.
|
||||||
|
s := k + ".go"
|
||||||
|
if !FileExists(s) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"cannot find source at '%s' or '%s'", srcFile, s)
|
||||||
|
|
||||||
|
}
|
||||||
|
srcFile = s
|
||||||
|
}
|
||||||
|
commands := []string{
|
||||||
|
"build",
|
||||||
|
"-buildmode",
|
||||||
|
"plugin",
|
||||||
|
"-o", objFile, srcFile,
|
||||||
|
}
|
||||||
|
goBin := goBin()
|
||||||
|
if !FileExists(goBin) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"cannot find go compiler %s", goBin)
|
||||||
|
}
|
||||||
|
cmd := exec.Command(goBin, commands...)
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
cmd.Env = os.Environ()
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return errors.Wrapf(
|
||||||
|
err, "cannot compile %s:\nSTDERR\n%s\n", srcFile, stderr.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// True if file less than 3 minutes old, i.e. not
|
||||||
|
// accidentally left over from some earlier build.
|
||||||
|
func RecentFileExists(path string) bool {
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
age := time.Now().Sub(fi.ModTime())
|
||||||
|
return age.Minutes() < 3
|
||||||
|
}
|
||||||
|
|
||||||
|
func FileExists(name string) bool {
|
||||||
|
if _, err := os.Stat(name); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
67
api/internal/plugins/compiler/compiler_test.go
Normal file
67
api/internal/plugins/compiler/compiler_test.go
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package compiler_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/plugins/compiler"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Regression coverage over compiler behavior.
|
||||||
|
func TestCompiler(t *testing.T) {
|
||||||
|
configRoot, err := ioutil.TempDir("", "kustomize-compiler-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to make temp dir: %v", err)
|
||||||
|
}
|
||||||
|
srcRoot, err := DeterminePluginSrcRoot(filesys.MakeFsOnDisk())
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
c := NewCompiler(srcRoot, configRoot)
|
||||||
|
if configRoot != c.ObjRoot() {
|
||||||
|
t.Errorf("unexpected objRoot %s", c.ObjRoot())
|
||||||
|
}
|
||||||
|
|
||||||
|
expectObj := filepath.Join(
|
||||||
|
c.ObjRoot(),
|
||||||
|
"someteam.example.com", "v1", "dateprefixer", "DatePrefixer.so")
|
||||||
|
if FileExists(expectObj) {
|
||||||
|
t.Errorf("obj file should not exist yet: %s", expectObj)
|
||||||
|
}
|
||||||
|
err = c.Compile("someteam.example.com", "v1", "DatePrefixer")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !RecentFileExists(expectObj) {
|
||||||
|
t.Errorf("didn't find expected obj file %s", expectObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectObj = filepath.Join(
|
||||||
|
c.ObjRoot(),
|
||||||
|
"builtin", "", "secretgenerator", "SecretGenerator.so")
|
||||||
|
if FileExists(expectObj) {
|
||||||
|
t.Errorf("obj file should not exist yet: %s", expectObj)
|
||||||
|
}
|
||||||
|
err = c.Compile("builtin", "", "SecretGenerator")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if !RecentFileExists(expectObj) {
|
||||||
|
t.Errorf("didn't find expected obj file %s", expectObj)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.RemoveAll(c.ObjRoot())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf(
|
||||||
|
"removing temp dir: %s %v", c.ObjRoot(), err)
|
||||||
|
}
|
||||||
|
if FileExists(expectObj) {
|
||||||
|
t.Errorf("cleanup failed; still see: %s", expectObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
118
api/internal/plugins/doc.go
Normal file
118
api/internal/plugins/doc.go
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Read docs/plugins.md first for an overview of kustomize plugins.
|
||||||
|
|
||||||
|
|
||||||
|
BUILTIN PLUGIN CONFIGURATION
|
||||||
|
|
||||||
|
There are two kinds of plugins, Go plugins (shared
|
||||||
|
object library) and exec plugins (independent binary).
|
||||||
|
For performance and standardized testing reasons, all
|
||||||
|
builtin plugins are Go plugins (not exec plugins).
|
||||||
|
|
||||||
|
Using "SecretGenerator" as an example in what
|
||||||
|
follows.
|
||||||
|
|
||||||
|
The plugin config file looks like
|
||||||
|
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: SecretGenerator
|
||||||
|
metadata:
|
||||||
|
name: whatever
|
||||||
|
otherField1: whatever
|
||||||
|
otherField2: whatever
|
||||||
|
...
|
||||||
|
|
||||||
|
The apiVersion must be 'builtin'.
|
||||||
|
|
||||||
|
The kind is the CamelCase name of the plugin.
|
||||||
|
|
||||||
|
The source for a builtin plugin must be at:
|
||||||
|
|
||||||
|
repo=$GOPATH/src/sigs.k8s.io/kustomize
|
||||||
|
${repo}/plugin/builtin/LOWERCASE(${kind})/${kind}
|
||||||
|
|
||||||
|
k8s wants 'kind' values to follow CamelCase, while
|
||||||
|
Go style doesn't like but does allow such names.
|
||||||
|
|
||||||
|
The lowercased value of kind is used as the name of the
|
||||||
|
directory holding the plugin, its test, and any
|
||||||
|
optional associated files (possibly a go.mod file).
|
||||||
|
|
||||||
|
|
||||||
|
BUILTIN PLUGIN GENERATION
|
||||||
|
|
||||||
|
The `pluginator` program is a code generator that
|
||||||
|
converts kustomize generator (G) and/or
|
||||||
|
transformer (T) Go plugins to statically linkable
|
||||||
|
code.
|
||||||
|
|
||||||
|
It arises from following requirements:
|
||||||
|
|
||||||
|
* extension
|
||||||
|
kustomize does two things - generate or
|
||||||
|
transform k8s resources. Plugins let
|
||||||
|
users write their own G&T's without
|
||||||
|
having to fork kustomize and learn its
|
||||||
|
internals.
|
||||||
|
|
||||||
|
* dogfooding
|
||||||
|
A G&T extension framework one can trust
|
||||||
|
should be used by its authors to deliver
|
||||||
|
builtin G&T's.
|
||||||
|
|
||||||
|
* distribution
|
||||||
|
kustomize should be distributable via
|
||||||
|
`go get` and should run where Go
|
||||||
|
programs are expected to run.
|
||||||
|
|
||||||
|
The extension requirement led to building
|
||||||
|
a framework that accommodates writing a
|
||||||
|
G or T as either
|
||||||
|
|
||||||
|
* an 'exec' plugin (any executable file
|
||||||
|
runnable as a kustomize subprocess), or
|
||||||
|
|
||||||
|
* as a Go plugin - see
|
||||||
|
https://golang.org/pkg/plugin.
|
||||||
|
|
||||||
|
The dogfooding (and an implicit performance
|
||||||
|
requirement) requires a 'builtin' G or T to
|
||||||
|
be written as a Go plugin.
|
||||||
|
|
||||||
|
The distribution ('go get') requirement demands
|
||||||
|
conversion of Go plugins to statically linked
|
||||||
|
code, hence this program.
|
||||||
|
|
||||||
|
|
||||||
|
TO GENERATE CODE
|
||||||
|
|
||||||
|
repo=$GOPATH/src/sigs.k8s.io/kustomize
|
||||||
|
cd $repo/plugin/builtin
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
|
See travis/pre-commit.sh for canonical way
|
||||||
|
to execute the above.
|
||||||
|
|
||||||
|
This creates
|
||||||
|
|
||||||
|
$repo/api/plugins/builtins/SecretGenerator.go
|
||||||
|
|
||||||
|
etc.
|
||||||
|
|
||||||
|
Generated plugins are used in kustomize via
|
||||||
|
|
||||||
|
package whatever
|
||||||
|
import sigs.k8s.io/kustomize/api/plugins/builtins
|
||||||
|
...
|
||||||
|
g := builtin.NewSecretGenerator()
|
||||||
|
g.Config(h, k)
|
||||||
|
resources, err := g.Generate()
|
||||||
|
err = g.Transform(resources)
|
||||||
|
// Eventually emit resources.
|
||||||
|
|
||||||
|
*/
|
||||||
|
package plugins
|
||||||
270
api/internal/plugins/execplugin/execplugin.go
Normal file
270
api/internal/plugins/execplugin/execplugin.go
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package execplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
idAnnotation = "kustomize.config.k8s.io/id"
|
||||||
|
HashAnnotation = "kustomize.config.k8s.io/needs-hash"
|
||||||
|
BehaviorAnnotation = "kustomize.config.k8s.io/behavior"
|
||||||
|
tmpConfigFilePrefix = "kust-plugin-config-"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExecPlugin record the name and args of an executable
|
||||||
|
// It triggers the executable generator and transformer
|
||||||
|
type ExecPlugin struct {
|
||||||
|
// absolute path of the executable
|
||||||
|
path string
|
||||||
|
|
||||||
|
// Optional command line arguments to the executable
|
||||||
|
// pulled from specially named fields in cfg.
|
||||||
|
// This is for executables that don't want to parse YAML.
|
||||||
|
args []string
|
||||||
|
|
||||||
|
// Plugin configuration data.
|
||||||
|
cfg []byte
|
||||||
|
|
||||||
|
// PluginHelpers
|
||||||
|
h *resmap.PluginHelpers
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewExecPlugin(p string) *ExecPlugin {
|
||||||
|
return &ExecPlugin{path: p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) ErrIfNotExecutable() error {
|
||||||
|
f, err := os.Stat(p.path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if f.Mode()&0111 == 0000 {
|
||||||
|
return fmt.Errorf("unexecutable plugin at: %s", p.path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) Path() string {
|
||||||
|
return p.path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) Args() []string {
|
||||||
|
return p.args
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) Cfg() []byte {
|
||||||
|
return p.cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) Config(h *resmap.PluginHelpers, config []byte) error {
|
||||||
|
p.h = h
|
||||||
|
p.cfg = config
|
||||||
|
return p.processOptionalArgsFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
type argsConfig struct {
|
||||||
|
ArgsOneLiner string `json:"argsOneLiner,omitempty" yaml:"argsOneLiner,omitempty"`
|
||||||
|
ArgsFromFile string `json:"argsFromFile,omitempty" yaml:"argsFromFile,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) processOptionalArgsFields() error {
|
||||||
|
var c argsConfig
|
||||||
|
yaml.Unmarshal(p.cfg, &c)
|
||||||
|
if c.ArgsOneLiner != "" {
|
||||||
|
p.args = strings.Split(c.ArgsOneLiner, " ")
|
||||||
|
}
|
||||||
|
if c.ArgsFromFile != "" {
|
||||||
|
content, err := p.h.Loader().Load(c.ArgsFromFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, x := range strings.Split(string(content), "\n") {
|
||||||
|
x := strings.TrimLeft(x, " ")
|
||||||
|
if x != "" {
|
||||||
|
p.args = append(p.args, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) Generate() (resmap.ResMap, error) {
|
||||||
|
output, err := p.invokePlugin(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rm, err := p.h.ResmapFactory().NewResMapFromBytes(output)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.UpdateResourceOptions(rm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) Transform(rm resmap.ResMap) error {
|
||||||
|
// add ResIds as annotations to all objects so that we can add them back
|
||||||
|
inputRM, err := p.getResMapWithIdAnnotation(rm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode the ResMap so it can be fed to the plugin
|
||||||
|
resources, err := inputRM.AsYaml()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// invoke the plugin with resources as the input
|
||||||
|
output, err := p.invokePlugin(resources)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%v %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the original ResMap based on the output
|
||||||
|
return p.updateResMapValues(output, rm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// invokePlugin writes plugin config to a temp file, then
|
||||||
|
// passes the full temp file path as the first arg to a process
|
||||||
|
// running the plugin binary. Process output is returned.
|
||||||
|
func (p *ExecPlugin) invokePlugin(input []byte) ([]byte, error) {
|
||||||
|
f, err := ioutil.TempFile("", tmpConfigFilePrefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(
|
||||||
|
err, "creating tmp plugin config file")
|
||||||
|
}
|
||||||
|
_, err = f.Write(p.cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(
|
||||||
|
err, "writing plugin config to "+f.Name())
|
||||||
|
}
|
||||||
|
err = f.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(
|
||||||
|
err, "closing plugin config file "+f.Name())
|
||||||
|
}
|
||||||
|
cmd := exec.Command(
|
||||||
|
p.path, append([]string{f.Name()}, p.args...)...)
|
||||||
|
cmd.Env = p.getEnv()
|
||||||
|
cmd.Stdin = bytes.NewReader(input)
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if _, err := os.Stat(p.h.Loader().Root()); err == nil {
|
||||||
|
cmd.Dir = p.h.Loader().Root()
|
||||||
|
}
|
||||||
|
result, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
err, "failure in plugin configured via %s; %v",
|
||||||
|
f.Name(), err.Error())
|
||||||
|
}
|
||||||
|
return result, os.Remove(f.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ExecPlugin) getEnv() []string {
|
||||||
|
env := os.Environ()
|
||||||
|
env = append(env,
|
||||||
|
"KUSTOMIZE_PLUGIN_CONFIG_STRING="+string(p.cfg),
|
||||||
|
"KUSTOMIZE_PLUGIN_CONFIG_ROOT="+p.h.Loader().Root())
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a new copy of the given ResMap with the ResIds annotated in each Resource
|
||||||
|
func (p *ExecPlugin) getResMapWithIdAnnotation(rm resmap.ResMap) (resmap.ResMap, error) {
|
||||||
|
inputRM := rm.DeepCopy()
|
||||||
|
for _, r := range inputRM.Resources() {
|
||||||
|
idString, err := yaml.Marshal(r.CurId())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
annotations := r.GetAnnotations()
|
||||||
|
if annotations == nil {
|
||||||
|
annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
annotations[idAnnotation] = string(idString)
|
||||||
|
r.SetAnnotations(annotations)
|
||||||
|
}
|
||||||
|
return inputRM, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateResMapValues updates the Resource value in the given ResMap
|
||||||
|
// with the emitted Resource values in output.
|
||||||
|
func (p *ExecPlugin) updateResMapValues(output []byte, rm resmap.ResMap) error {
|
||||||
|
outputRM, err := p.h.ResmapFactory().NewResMapFromBytes(output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, r := range outputRM.Resources() {
|
||||||
|
// for each emitted Resource, find the matching Resource in the original ResMap
|
||||||
|
// using its id
|
||||||
|
annotations := r.GetAnnotations()
|
||||||
|
idString, ok := annotations[idAnnotation]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("the transformer %s should not remove annotation %s",
|
||||||
|
p.path, idAnnotation)
|
||||||
|
}
|
||||||
|
id := resid.ResId{}
|
||||||
|
err := yaml.Unmarshal([]byte(idString), &id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := rm.GetByCurrentId(id)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to find unique match to %s", id.String())
|
||||||
|
}
|
||||||
|
// remove the annotation set by Kustomize to track the resource
|
||||||
|
delete(annotations, idAnnotation)
|
||||||
|
if len(annotations) == 0 {
|
||||||
|
annotations = nil
|
||||||
|
}
|
||||||
|
r.SetAnnotations(annotations)
|
||||||
|
|
||||||
|
// update the ResMap resource value with the transformed object
|
||||||
|
res.Kunstructured = r.Kunstructured
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateResourceOptions updates the generator options for each resource in the
|
||||||
|
// given ResMap based on plugin provided annotations.
|
||||||
|
func (p *ExecPlugin) UpdateResourceOptions(rm resmap.ResMap) (resmap.ResMap, error) {
|
||||||
|
for _, r := range rm.Resources() {
|
||||||
|
// Disable name hashing by default and require plugin to explicitly
|
||||||
|
// request it for each resource.
|
||||||
|
annotations := r.GetAnnotations()
|
||||||
|
behavior := annotations[BehaviorAnnotation]
|
||||||
|
var needsHash bool
|
||||||
|
if val, ok := annotations[HashAnnotation]; ok {
|
||||||
|
b, err := strconv.ParseBool(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"the annotation %q contains an invalid value (%q)",
|
||||||
|
HashAnnotation, val)
|
||||||
|
}
|
||||||
|
needsHash = b
|
||||||
|
}
|
||||||
|
delete(annotations, HashAnnotation)
|
||||||
|
delete(annotations, BehaviorAnnotation)
|
||||||
|
if len(annotations) == 0 {
|
||||||
|
annotations = nil
|
||||||
|
}
|
||||||
|
r.SetAnnotations(annotations)
|
||||||
|
r.SetOptions(types.NewGenArgs(
|
||||||
|
&types.GeneratorArgs{Behavior: behavior},
|
||||||
|
&types.GeneratorOptions{DisableNameSuffixHash: !needsHash}))
|
||||||
|
}
|
||||||
|
return rm, nil
|
||||||
|
}
|
||||||
190
api/internal/plugins/execplugin/execplugin_test.go
Normal file
190
api/internal/plugins/execplugin/execplugin_test.go
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package execplugin_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/loadertest"
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/plugins/execplugin"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExecPluginConfig(t *testing.T) {
|
||||||
|
path := "/app"
|
||||||
|
rf := resmap.NewFactory(
|
||||||
|
resource.NewFactory(
|
||||||
|
kunstruct.NewKunstructuredFactoryImpl()), nil)
|
||||||
|
ldr := loadertest.NewFakeLoader(path)
|
||||||
|
v := valtest_test.MakeFakeValidator()
|
||||||
|
pluginConfig := rf.RF().FromMap(
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "someteam.example.com/v1",
|
||||||
|
"kind": "SedTransformer",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "some-random-name",
|
||||||
|
},
|
||||||
|
"argsOneLiner": "one two",
|
||||||
|
"argsFromFile": "sed-input.txt",
|
||||||
|
})
|
||||||
|
|
||||||
|
ldr.AddFile("/app/sed-input.txt", []byte(`
|
||||||
|
s/$FOO/foo/g
|
||||||
|
s/$BAR/bar/g
|
||||||
|
\ \ \
|
||||||
|
`))
|
||||||
|
|
||||||
|
p := NewExecPlugin(
|
||||||
|
loader.AbsolutePluginPath(
|
||||||
|
konfig.DisabledPluginConfig(),
|
||||||
|
pluginConfig.OrgId()))
|
||||||
|
// Not checking to see if the plugin is executable,
|
||||||
|
// because this test does not run it.
|
||||||
|
// This tests only covers sending configuration
|
||||||
|
// to the plugin wrapper object and confirming
|
||||||
|
// that it's properly prepared for execution.
|
||||||
|
|
||||||
|
yaml, err := pluginConfig.AsYAML()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
p.Config(resmap.NewPluginHelpers(ldr, v, rf), yaml)
|
||||||
|
|
||||||
|
expected := "someteam.example.com/v1/sedtransformer/SedTransformer"
|
||||||
|
if !strings.HasSuffix(p.Path(), expected) {
|
||||||
|
t.Fatalf("expected suffix '%s', got '%s'", expected, p.Path())
|
||||||
|
}
|
||||||
|
|
||||||
|
expected = `apiVersion: someteam.example.com/v1
|
||||||
|
argsFromFile: sed-input.txt
|
||||||
|
argsOneLiner: one two
|
||||||
|
kind: SedTransformer
|
||||||
|
metadata:
|
||||||
|
name: some-random-name
|
||||||
|
`
|
||||||
|
if expected != string(p.Cfg()) {
|
||||||
|
t.Fatalf("expected cfg '%s', got '%s'", expected, string(p.Cfg()))
|
||||||
|
|
||||||
|
}
|
||||||
|
if len(p.Args()) != 5 {
|
||||||
|
t.Fatalf("unexpected arg len %d, %v", len(p.Args()), p.Args())
|
||||||
|
}
|
||||||
|
if p.Args()[0] != "one" ||
|
||||||
|
p.Args()[1] != "two" ||
|
||||||
|
p.Args()[2] != "s/$FOO/foo/g" ||
|
||||||
|
p.Args()[3] != "s/$BAR/bar/g" ||
|
||||||
|
p.Args()[4] != "\\ \\ \\ " {
|
||||||
|
t.Fatalf("unexpected arg array: %v", p.Args())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeConfigMap(rf *resource.Factory, name, behavior string, hashValue *string) *resource.Resource {
|
||||||
|
r := rf.FromMap(map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{"name": name},
|
||||||
|
})
|
||||||
|
annotations := map[string]string{}
|
||||||
|
if behavior != "" {
|
||||||
|
annotations[BehaviorAnnotation] = behavior
|
||||||
|
}
|
||||||
|
if hashValue != nil {
|
||||||
|
annotations[HashAnnotation] = *hashValue
|
||||||
|
}
|
||||||
|
if len(annotations) > 0 {
|
||||||
|
r.SetAnnotations(annotations)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeConfigMapOptions(rf *resource.Factory, name, behavior string, disableHash bool) *resource.Resource {
|
||||||
|
return rf.FromMapAndOption(map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{"name": name},
|
||||||
|
}, &types.GeneratorArgs{Behavior: behavior}, &types.GeneratorOptions{DisableNameSuffixHash: disableHash})
|
||||||
|
}
|
||||||
|
|
||||||
|
func strptr(s string) *string {
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateResourceOptions(t *testing.T) {
|
||||||
|
p := NewExecPlugin("")
|
||||||
|
if err := p.ErrIfNotExecutable(); err == nil {
|
||||||
|
t.Fatalf("expected unexecutable error")
|
||||||
|
}
|
||||||
|
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
|
||||||
|
in := resmap.New()
|
||||||
|
expected := resmap.New()
|
||||||
|
cases := []struct {
|
||||||
|
behavior string
|
||||||
|
needsHash bool
|
||||||
|
hashValue *string
|
||||||
|
}{
|
||||||
|
{hashValue: strptr("false")},
|
||||||
|
{hashValue: strptr("true"), needsHash: true},
|
||||||
|
{behavior: "replace"},
|
||||||
|
{behavior: "merge"},
|
||||||
|
{behavior: "create"},
|
||||||
|
{behavior: "nonsense"},
|
||||||
|
{behavior: "merge", hashValue: strptr("false")},
|
||||||
|
{behavior: "merge", hashValue: strptr("true"), needsHash: true},
|
||||||
|
}
|
||||||
|
for i, c := range cases {
|
||||||
|
name := fmt.Sprintf("test%d", i)
|
||||||
|
in.Append(makeConfigMap(rf, name, c.behavior, c.hashValue))
|
||||||
|
expected.Append(makeConfigMapOptions(rf, name, c.behavior, !c.needsHash))
|
||||||
|
}
|
||||||
|
actual, err := p.UpdateResourceOptions(in)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err.Error())
|
||||||
|
}
|
||||||
|
for i, a := range expected.Resources() {
|
||||||
|
b := actual.GetByIndex(i)
|
||||||
|
if b == nil {
|
||||||
|
t.Fatalf("resource %d missing from processed map", i)
|
||||||
|
}
|
||||||
|
if !a.Equals(b) {
|
||||||
|
t.Errorf("expected %v got %v", a, b)
|
||||||
|
}
|
||||||
|
if a.NeedHashSuffix() != b.NeedHashSuffix() {
|
||||||
|
t.Errorf("")
|
||||||
|
}
|
||||||
|
if a.Behavior() != b.Behavior() {
|
||||||
|
t.Errorf("expected %v got %v", a.Behavior(), b.Behavior())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateResourceOptionsWithInvalidHashAnnotationValues(t *testing.T) {
|
||||||
|
p := NewExecPlugin("")
|
||||||
|
if err := p.ErrIfNotExecutable(); err == nil {
|
||||||
|
t.Fatalf("expected unexecutable error")
|
||||||
|
}
|
||||||
|
rf := resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
|
||||||
|
cases := []string{
|
||||||
|
"",
|
||||||
|
"FaLsE",
|
||||||
|
"TrUe",
|
||||||
|
"potato",
|
||||||
|
}
|
||||||
|
for i, c := range cases {
|
||||||
|
name := fmt.Sprintf("test%d", i)
|
||||||
|
in := resmap.New()
|
||||||
|
in.Append(makeConfigMap(rf, name, "", &c))
|
||||||
|
_, err := p.UpdateResourceOptions(in)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error from value %q", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
211
api/internal/plugins/loader/loader.go
Normal file
211
api/internal/plugins/loader/loader.go
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package loader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"plugin"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/execplugin"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Loader struct {
|
||||||
|
pc *types.PluginConfig
|
||||||
|
rf *resmap.Factory
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoader(
|
||||||
|
pc *types.PluginConfig, rf *resmap.Factory) *Loader {
|
||||||
|
return &Loader{pc: pc, rf: rf}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) LoadGenerators(
|
||||||
|
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Generator, error) {
|
||||||
|
var result []resmap.Generator
|
||||||
|
for _, res := range rm.Resources() {
|
||||||
|
g, err := l.LoadGenerator(ldr, v, res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, g)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) LoadGenerator(
|
||||||
|
ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (resmap.Generator, error) {
|
||||||
|
c, err := l.loadAndConfigurePlugin(ldr, v, res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
g, ok := c.(resmap.Generator)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("plugin %s not a generator", res.OrgId())
|
||||||
|
}
|
||||||
|
return g, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) LoadTransformers(
|
||||||
|
ldr ifc.Loader, v ifc.Validator, rm resmap.ResMap) ([]resmap.Transformer, error) {
|
||||||
|
var result []resmap.Transformer
|
||||||
|
for _, res := range rm.Resources() {
|
||||||
|
t, err := l.LoadTransformer(ldr, v, res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, t)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) LoadTransformer(
|
||||||
|
ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (resmap.Transformer, error) {
|
||||||
|
c, err := l.loadAndConfigurePlugin(ldr, v, res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t, ok := c.(resmap.Transformer)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("plugin %s not a transformer", res.OrgId())
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func relativePluginPath(id resid.ResId) string {
|
||||||
|
return filepath.Join(
|
||||||
|
id.Group,
|
||||||
|
id.Version,
|
||||||
|
strings.ToLower(id.Kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
func AbsolutePluginPath(pc *types.PluginConfig, id resid.ResId) string {
|
||||||
|
return filepath.Join(
|
||||||
|
pc.AbsPluginHome, relativePluginPath(id), id.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) absolutePluginPath(id resid.ResId) string {
|
||||||
|
return AbsolutePluginPath(l.pc, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBuiltinPlugin(res *resource.Resource) bool {
|
||||||
|
// TODO: the special string should appear in Group, not Version.
|
||||||
|
return res.GetGvk().Group == "" &&
|
||||||
|
res.GetGvk().Version == konfig.BuiltinPluginApiVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) loadAndConfigurePlugin(
|
||||||
|
ldr ifc.Loader, v ifc.Validator, res *resource.Resource) (c resmap.Configurable, err error) {
|
||||||
|
if isBuiltinPlugin(res) {
|
||||||
|
// Instead of looking for and loading a .so file, just
|
||||||
|
// instantiate the plugin from a generated factory
|
||||||
|
// function (see "pluginator"). Being able to do this
|
||||||
|
// is what makes a plugin "builtin".
|
||||||
|
c, err = l.makeBuiltinPlugin(res.GetGvk())
|
||||||
|
} else if l.pc.PluginRestrictions == types.PluginRestrictionsNone {
|
||||||
|
c, err = l.loadPlugin(res.OrgId())
|
||||||
|
} else {
|
||||||
|
err = types.NewErrOnlyBuiltinPluginsAllowed(res.OrgId().Kind)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
yaml, err := res.AsYAML()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "marshalling yaml from res %s", res.OrgId())
|
||||||
|
}
|
||||||
|
err = c.Config(resmap.NewPluginHelpers(ldr, v, l.rf), yaml)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
err, "plugin %s fails configuration", res.OrgId())
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) makeBuiltinPlugin(r resid.Gvk) (resmap.Configurable, error) {
|
||||||
|
bpt := builtinhelpers.GetBuiltinPluginType(r.Kind)
|
||||||
|
if f, ok := builtinhelpers.GeneratorFactories[bpt]; ok {
|
||||||
|
return f(), nil
|
||||||
|
}
|
||||||
|
if f, ok := builtinhelpers.TransformerFactories[bpt]; ok {
|
||||||
|
return f(), nil
|
||||||
|
}
|
||||||
|
return nil, errors.Errorf("unable to load builtin %s", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Loader) loadPlugin(resId resid.ResId) (resmap.Configurable, error) {
|
||||||
|
// First try to load the plugin as an executable.
|
||||||
|
p := execplugin.NewExecPlugin(l.absolutePluginPath(resId))
|
||||||
|
err := p.ErrIfNotExecutable()
|
||||||
|
if err == nil {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
// The file exists, but something else is wrong,
|
||||||
|
// likely it's not executable.
|
||||||
|
// Assume the user forgot to set the exec bit,
|
||||||
|
// and return an error, rather than adding ".so"
|
||||||
|
// to the name and attempting to load it as a Go
|
||||||
|
// plugin, which will likely fail and result
|
||||||
|
// in an obscure message.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Failing the above, try loading it as a Go plugin.
|
||||||
|
c, err := l.loadGoPlugin(resId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// registry is a means to avoid trying to load the same .so file
|
||||||
|
// into memory more than once, which results in an error.
|
||||||
|
// Each test makes its own loader, and tries to load its own plugins,
|
||||||
|
// but the loaded .so files are in shared memory, so one will get
|
||||||
|
// "this plugin already loaded" errors if the registry is maintained
|
||||||
|
// as a Loader instance variable. So make it a package variable.
|
||||||
|
var registry = make(map[string]resmap.Configurable)
|
||||||
|
|
||||||
|
func (l *Loader) loadGoPlugin(id resid.ResId) (resmap.Configurable, error) {
|
||||||
|
regId := relativePluginPath(id)
|
||||||
|
if c, ok := registry[regId]; ok {
|
||||||
|
return copyPlugin(c), nil
|
||||||
|
}
|
||||||
|
absPath := l.absolutePluginPath(id)
|
||||||
|
p, err := plugin.Open(absPath + ".so")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "plugin %s fails to load", absPath)
|
||||||
|
}
|
||||||
|
symbol, err := p.Lookup(konfig.PluginSymbol)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
err, "plugin %s doesn't have symbol %s",
|
||||||
|
regId, konfig.PluginSymbol)
|
||||||
|
}
|
||||||
|
c, ok := symbol.(resmap.Configurable)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("plugin '%s' not configurable", regId)
|
||||||
|
}
|
||||||
|
registry[regId] = c
|
||||||
|
return copyPlugin(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyPlugin(c resmap.Configurable) resmap.Configurable {
|
||||||
|
indirect := reflect.Indirect(reflect.ValueOf(c))
|
||||||
|
newIndirect := reflect.New(indirect.Type())
|
||||||
|
newIndirect.Elem().Set(reflect.ValueOf(indirect.Interface()))
|
||||||
|
newNamed := newIndirect.Interface()
|
||||||
|
return newNamed.(resmap.Configurable)
|
||||||
|
}
|
||||||
80
api/internal/plugins/loader/loader_test.go
Normal file
80
api/internal/plugins/loader/loader_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package loader_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/loadertest"
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
secretGenerator = `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: SecretGenerator
|
||||||
|
metadata:
|
||||||
|
name: secretGenerator
|
||||||
|
name: mySecret
|
||||||
|
behavior: merge
|
||||||
|
envFiles:
|
||||||
|
- a.env
|
||||||
|
- b.env
|
||||||
|
valueFiles:
|
||||||
|
- longsecret.txt
|
||||||
|
literals:
|
||||||
|
- FRUIT=apple
|
||||||
|
- VEGETABLE=carrot
|
||||||
|
`
|
||||||
|
someServiceGenerator = `
|
||||||
|
apiVersion: someteam.example.com/v1
|
||||||
|
kind: SomeServiceGenerator
|
||||||
|
metadata:
|
||||||
|
name: myServiceGenerator
|
||||||
|
service: my-service
|
||||||
|
port: "12345"
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoader(t *testing.T) {
|
||||||
|
tc := kusttest_test.NewPluginTestEnv(t).Set()
|
||||||
|
defer tc.Reset()
|
||||||
|
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"builtin", "", "SecretGenerator")
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"someteam.example.com", "v1", "SomeServiceGenerator")
|
||||||
|
|
||||||
|
rmF := resmap.NewFactory(resource.NewFactory(
|
||||||
|
kunstruct.NewKunstructuredFactoryImpl()), nil)
|
||||||
|
|
||||||
|
ldr := loadertest.NewFakeLoader("/foo")
|
||||||
|
|
||||||
|
c, err := konfig.EnabledPluginConfig()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pLdr := NewLoader(c, rmF)
|
||||||
|
if pLdr == nil {
|
||||||
|
t.Fatal("expect non-nil loader")
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := rmF.NewResMapFromBytes([]byte(
|
||||||
|
someServiceGenerator + "---\n" + secretGenerator))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = pLdr.LoadGenerators(ldr, valtest_test.MakeFakeValidator(), m)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
60
api/internal/target/accumulation_test.go
Normal file
60
api/internal/target/accumulation_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/target"
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTargetMustHaveKustomizationFile(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteF("/app/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: aService
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/deeper/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: anotherService
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTargetOrErr()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected an error")
|
||||||
|
}
|
||||||
|
if !IsMissingKustomizationFileError(err) {
|
||||||
|
t.Fatalf("unexpected error: %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResourceDirectoryMustHaveKustomizationFile(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
resources:
|
||||||
|
- base
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
backend: bungie
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected an error")
|
||||||
|
}
|
||||||
|
if !IsMissingKustomizationFileError(err) {
|
||||||
|
t.Fatalf("unexpected error: %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
313
api/internal/target/baseandoverlaymedium_test.go
Normal file
313
api/internal/target/baseandoverlaymedium_test.go
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeMediumBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
namePrefix: baseprefix-
|
||||||
|
commonLabels:
|
||||||
|
foo: bar
|
||||||
|
commonAnnotations:
|
||||||
|
baseAnno: This is a base annotation
|
||||||
|
resources:
|
||||||
|
- deployment/deployment.yaml
|
||||||
|
- service/service.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/service/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: mungebot-service
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
selector:
|
||||||
|
app: mungebot
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/deployment/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mungebot
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
env:
|
||||||
|
- name: foo
|
||||||
|
value: bar
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMediumBase(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
writeMediumBase(th)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
baseAnno: This is a base annotation
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
name: baseprefix-mungebot
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
foo: bar
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
baseAnno: This is a base annotation
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- name: foo
|
||||||
|
value: bar
|
||||||
|
image: nginx
|
||||||
|
name: nginx
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
baseAnno: This is a base annotation
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
name: baseprefix-mungebot-service
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
selector:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMediumOverlay(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
|
||||||
|
writeMediumBase(th)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
namePrefix: test-infra-
|
||||||
|
commonLabels:
|
||||||
|
app: mungebot
|
||||||
|
org: kubernetes
|
||||||
|
repo: test-infra
|
||||||
|
commonAnnotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- deployment/deployment.yaml
|
||||||
|
configMapGenerator:
|
||||||
|
- name: app-env
|
||||||
|
envs:
|
||||||
|
- configmap/db.env
|
||||||
|
- configmap/units.ini
|
||||||
|
- configmap/food.ini
|
||||||
|
- name: app-config
|
||||||
|
files:
|
||||||
|
- nonsense=configmap/dummy.txt
|
||||||
|
images:
|
||||||
|
- name: nginx
|
||||||
|
newTag: 1.8.0`)
|
||||||
|
|
||||||
|
th.WriteF("/app/overlay/configmap/db.env", `
|
||||||
|
DB_USERNAME=admin
|
||||||
|
DB_PASSWORD=somepw
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/configmap/units.ini", `
|
||||||
|
LENGTH=kilometer
|
||||||
|
ENERGY=electronvolt
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/configmap/food.ini", `
|
||||||
|
FRUIT=banana
|
||||||
|
LEGUME=chickpea
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/configmap/dummy.txt",
|
||||||
|
`Lorem ipsum dolor sit amet, consectetur
|
||||||
|
adipiscing elit, sed do eiusmod tempor
|
||||||
|
incididunt ut labore et dolore magna aliqua.
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/deployment/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: mungebot
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:1.7.9
|
||||||
|
env:
|
||||||
|
- name: FOO
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: app-env
|
||||||
|
key: somekey
|
||||||
|
- name: busybox
|
||||||
|
image: busybox
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: someConfigMap
|
||||||
|
- configMapRef:
|
||||||
|
name: app-env
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/env
|
||||||
|
name: app-env
|
||||||
|
volumes:
|
||||||
|
- configMap:
|
||||||
|
name: app-env
|
||||||
|
name: app-env
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
baseAnno: This is a base annotation
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
org: kubernetes
|
||||||
|
repo: test-infra
|
||||||
|
name: test-infra-baseprefix-mungebot
|
||||||
|
spec:
|
||||||
|
replicas: 2
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
org: kubernetes
|
||||||
|
repo: test-infra
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
baseAnno: This is a base annotation
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
org: kubernetes
|
||||||
|
repo: test-infra
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- name: FOO
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
key: somekey
|
||||||
|
name: test-infra-app-env-ffmd9b969m
|
||||||
|
- name: foo
|
||||||
|
value: bar
|
||||||
|
image: nginx:1.8.0
|
||||||
|
name: nginx
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
- envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: someConfigMap
|
||||||
|
- configMapRef:
|
||||||
|
name: test-infra-app-env-ffmd9b969m
|
||||||
|
image: busybox
|
||||||
|
name: busybox
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/env
|
||||||
|
name: app-env
|
||||||
|
volumes:
|
||||||
|
- configMap:
|
||||||
|
name: test-infra-app-env-ffmd9b969m
|
||||||
|
name: app-env
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
baseAnno: This is a base annotation
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
org: kubernetes
|
||||||
|
repo: test-infra
|
||||||
|
name: test-infra-baseprefix-mungebot-service
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
selector:
|
||||||
|
app: mungebot
|
||||||
|
foo: bar
|
||||||
|
org: kubernetes
|
||||||
|
repo: test-infra
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
DB_PASSWORD: somepw
|
||||||
|
DB_USERNAME: admin
|
||||||
|
ENERGY: electronvolt
|
||||||
|
FRUIT: banana
|
||||||
|
LEGUME: chickpea
|
||||||
|
LENGTH: kilometer
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
org: kubernetes
|
||||||
|
repo: test-infra
|
||||||
|
name: test-infra-app-env-ffmd9b969m
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
nonsense: "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit, sed do eiusmod
|
||||||
|
tempor\nincididunt ut labore et dolore magna aliqua. \n"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mungebot
|
||||||
|
org: kubernetes
|
||||||
|
repo: test-infra
|
||||||
|
name: test-infra-app-config-f462h769f9
|
||||||
|
`)
|
||||||
|
}
|
||||||
456
api/internal/target/baseandoverlaysmall_test.go
Normal file
456
api/internal/target/baseandoverlaysmall_test.go
Normal file
@@ -0,0 +1,456 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/loader"
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOrderPreserved(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/prod")
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
namePrefix: b-
|
||||||
|
resources:
|
||||||
|
- namespace.yaml
|
||||||
|
- role.yaml
|
||||||
|
- service.yaml
|
||||||
|
- deployment.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/namespace.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: myNs
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/role.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: myRole
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/deployment.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: myDep
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
namePrefix: p-
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
- service.yaml
|
||||||
|
- namespace.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/prod/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService2
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/prod/namespace.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: myNs2
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: p-b-myNs
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: p-b-myRole
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: p-b-myService
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: p-b-myDep
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: p-myService2
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: p-myNs2
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBaseInResourceList(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/prod")
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
namePrefix: b-
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
namePrefix: a-
|
||||||
|
resources:
|
||||||
|
- service.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
backend: bungie
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: b-a-myService
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
backend: bungie
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSmallBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
namePrefix: a-
|
||||||
|
commonLabels:
|
||||||
|
app: myApp
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- service.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
backend: bungie
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: myDeployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
backend: awesome
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: whatever
|
||||||
|
image: whatever
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSmallBase(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
writeSmallBase(th)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: a-myDeployment
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: myApp
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
backend: awesome
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: whatever
|
||||||
|
name: whatever
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: a-myService
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
selector:
|
||||||
|
app: myApp
|
||||||
|
backend: bungie
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSmallOverlay(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
|
||||||
|
writeSmallBase(th)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
namePrefix: b-
|
||||||
|
commonLabels:
|
||||||
|
env: prod
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- deployment/deployment.yaml
|
||||||
|
images:
|
||||||
|
- name: whatever
|
||||||
|
newTag: 1.8.0
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteF("/app/overlay/configmap/app.env", `
|
||||||
|
DB_USERNAME=admin
|
||||||
|
DB_PASSWORD=somepw
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/configmap/app-init.ini", `
|
||||||
|
FOO=bar
|
||||||
|
BAR=baz
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/deployment/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: myDeployment
|
||||||
|
spec:
|
||||||
|
replicas: 1000
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
env: prod
|
||||||
|
name: b-a-myDeployment
|
||||||
|
spec:
|
||||||
|
replicas: 1000
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: myApp
|
||||||
|
env: prod
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
backend: awesome
|
||||||
|
env: prod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: whatever:1.8.0
|
||||||
|
name: whatever
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
env: prod
|
||||||
|
name: b-a-myService
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
selector:
|
||||||
|
app: myApp
|
||||||
|
backend: bungie
|
||||||
|
env: prod
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedPatchDisAllowed(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarnessFull(
|
||||||
|
t, "/app/overlay",
|
||||||
|
loader.RestrictionRootOnly, konfig.DisabledPluginConfig())
|
||||||
|
writeSmallBase(th)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
commonLabels:
|
||||||
|
env: prod
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- ../shared/deployment-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/shared/deployment-patch.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: myDeployment
|
||||||
|
spec:
|
||||||
|
replicas: 1000
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(),
|
||||||
|
"security; file '/app/shared/deployment-patch.yaml' is not in or below '/app/overlay'") {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSharedPatchAllowed(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarnessFull(
|
||||||
|
t, "/app/overlay",
|
||||||
|
loader.RestrictionNone, konfig.DisabledPluginConfig())
|
||||||
|
writeSmallBase(th)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
commonLabels:
|
||||||
|
env: prod
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- ../shared/deployment-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/shared/deployment-patch.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: myDeployment
|
||||||
|
spec:
|
||||||
|
replicas: 1000
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
env: prod
|
||||||
|
name: a-myDeployment
|
||||||
|
spec:
|
||||||
|
replicas: 1000
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: myApp
|
||||||
|
env: prod
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
backend: awesome
|
||||||
|
env: prod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: whatever
|
||||||
|
name: whatever
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
env: prod
|
||||||
|
name: a-myService
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
selector:
|
||||||
|
app: myApp
|
||||||
|
backend: bungie
|
||||||
|
env: prod
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSmallOverlayJSONPatch(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
|
||||||
|
writeSmallBase(th)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesJson6902:
|
||||||
|
- target:
|
||||||
|
version: v1
|
||||||
|
kind: Service
|
||||||
|
name: a-myService
|
||||||
|
path: service-patch.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteF("/app/overlay/service-patch.yaml", `
|
||||||
|
- op: add
|
||||||
|
path: /spec/selector/backend
|
||||||
|
value: beagle
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: a-myDeployment
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: myApp
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
backend: awesome
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: whatever
|
||||||
|
name: whatever
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: a-myService
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 7002
|
||||||
|
selector:
|
||||||
|
app: myApp
|
||||||
|
backend: beagle
|
||||||
|
`)
|
||||||
|
}
|
||||||
199
api/internal/target/basereusenameprefix_test.go
Normal file
199
api/internal/target/basereusenameprefix_test.go
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Here is a structure of a kustomization of two components, component1
|
||||||
|
// and component2, that both use a shared postgres definition, which
|
||||||
|
// they would individually adjust. This test case checks that the name
|
||||||
|
// prefix does not cause a name reference conflict.
|
||||||
|
//
|
||||||
|
// root
|
||||||
|
// / \
|
||||||
|
// component1/overlay component2/overlay
|
||||||
|
// | |
|
||||||
|
// component1/base component2/base
|
||||||
|
// \ /
|
||||||
|
// base
|
||||||
|
//
|
||||||
|
// This is the directory layout:
|
||||||
|
//
|
||||||
|
// ├── component1
|
||||||
|
// │ ├── base
|
||||||
|
// │ │ └── kustomization.yaml
|
||||||
|
// │ └── overlay
|
||||||
|
// │ └── kustomization.yaml
|
||||||
|
// ├── component2
|
||||||
|
// │ ├── base
|
||||||
|
// │ │ └── kustomization.yaml
|
||||||
|
// │ └── overlay
|
||||||
|
// │ └── kustomization.yaml
|
||||||
|
// ├── shared
|
||||||
|
// │ ├── kustomization.yaml
|
||||||
|
// │ └── resources.yaml
|
||||||
|
// ├── kustomization.yaml
|
||||||
|
|
||||||
|
func TestBaseReuseNameConflict(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app/component1/base", `
|
||||||
|
resources:
|
||||||
|
- ../../shared
|
||||||
|
|
||||||
|
namePrefix: component1-
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/component1/overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
|
||||||
|
namePrefix: overlay-
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteK("/app/component2/base", `
|
||||||
|
resources:
|
||||||
|
- ../../shared
|
||||||
|
|
||||||
|
namePrefix: component2-
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/component2/overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
|
||||||
|
namePrefix: overlay-
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteK("/app/shared", `
|
||||||
|
resources:
|
||||||
|
- resources.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/shared/resources.yaml", `
|
||||||
|
---
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: postgres
|
||||||
|
spec:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: postgres
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels: {}
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: postgres
|
||||||
|
image: postgres
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/lib/postgresql
|
||||||
|
name: data
|
||||||
|
ports:
|
||||||
|
- name: postgres
|
||||||
|
containerPort: 5432
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: postgres
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteK("/app", `
|
||||||
|
resources:
|
||||||
|
- component1/overlay
|
||||||
|
- component2/overlay
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: overlay-component1-postgres
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: overlay-component1-postgres
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels: {}
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: postgres
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: postgres
|
||||||
|
ports:
|
||||||
|
- containerPort: 5432
|
||||||
|
name: postgres
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/lib/postgresql
|
||||||
|
name: data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: overlay-component1-postgres
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: overlay-component2-postgres
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: overlay-component2-postgres
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels: {}
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: postgres
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
name: postgres
|
||||||
|
ports:
|
||||||
|
- containerPort: 5432
|
||||||
|
name: postgres
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /var/lib/postgresql
|
||||||
|
name: data
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: overlay-component2-postgres
|
||||||
|
`)
|
||||||
|
}
|
||||||
111
api/internal/target/chartinflatorplugin_test.go
Normal file
111
api/internal/target/chartinflatorplugin_test.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// +build notravis
|
||||||
|
|
||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Disabled on travis, because don't want to install helm on travis.
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is an example of using a helm chart as a base,
|
||||||
|
// inflating it and then customizing it with a nameprefix
|
||||||
|
// applied to all its resources.
|
||||||
|
//
|
||||||
|
// The helm chart used is downloaded from
|
||||||
|
// https://github.com/helm/charts/tree/master/stable/minecraft
|
||||||
|
// with each test run, so it's a bit brittle as that
|
||||||
|
// chart could change obviously and break the test.
|
||||||
|
//
|
||||||
|
// This test requires having the helm binary on the PATH.
|
||||||
|
//
|
||||||
|
// TODO: Download and inflate the chart, and check that
|
||||||
|
// in for the test.
|
||||||
|
func TestChartInflatorPlugin(t *testing.T) {
|
||||||
|
tc := kusttest_test.NewPluginTestEnv(t).Set()
|
||||||
|
defer tc.Reset()
|
||||||
|
|
||||||
|
tc.BuildExecPlugin(
|
||||||
|
"someteam.example.com", "v1", "ChartInflator")
|
||||||
|
|
||||||
|
th := kusttest_test.NewKustTestHarnessAllowPlugins(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
generators:
|
||||||
|
- chartInflator.yaml
|
||||||
|
namePrefix: LOOOOOOOONG-
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteF("/app/chartInflator.yaml", `
|
||||||
|
apiVersion: someteam.example.com/v1
|
||||||
|
kind: ChartInflator
|
||||||
|
metadata:
|
||||||
|
name: notImportantHere
|
||||||
|
chartName: minecraft
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
chartName := regexp.MustCompile("chart: minecraft-[0-9.]+")
|
||||||
|
th.AssertActualEqualsExpectedWithTweak(m,
|
||||||
|
func(x []byte) []byte {
|
||||||
|
return chartName.ReplaceAll(x, []byte("chart: minecraft-SOMEVERSION"))
|
||||||
|
}, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
rcon-password: Q0hBTkdFTUUh
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: release-name-minecraft
|
||||||
|
chart: minecraft-SOMEVERSION
|
||||||
|
heritage: Tiller
|
||||||
|
release: release-name
|
||||||
|
name: LOOOOOOOONG-release-name-minecraft
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
volume.alpha.kubernetes.io/storage-class: default
|
||||||
|
labels:
|
||||||
|
app: release-name-minecraft
|
||||||
|
chart: minecraft-SOMEVERSION
|
||||||
|
heritage: Tiller
|
||||||
|
release: release-name
|
||||||
|
name: LOOOOOOOONG-release-name-minecraft-datadir
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: release-name-minecraft
|
||||||
|
chart: minecraft-SOMEVERSION
|
||||||
|
heritage: Tiller
|
||||||
|
release: release-name
|
||||||
|
name: LOOOOOOOONG-release-name-minecraft
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: minecraft
|
||||||
|
port: 25565
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: minecraft
|
||||||
|
selector:
|
||||||
|
app: release-name-minecraft
|
||||||
|
type: LoadBalancer
|
||||||
|
`)
|
||||||
|
}
|
||||||
557
api/internal/target/complexcomposition_test.go
Normal file
557
api/internal/target/complexcomposition_test.go
Normal file
@@ -0,0 +1,557 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const httpsService = `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: my-https-svc
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 443
|
||||||
|
protocol: TCP
|
||||||
|
name: https
|
||||||
|
selector:
|
||||||
|
app: my-app
|
||||||
|
`
|
||||||
|
|
||||||
|
func writeStatefulSetBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
resources:
|
||||||
|
- statefulset.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/statefulset.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
serviceName: my-svc
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: my-app
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: my-app
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: app
|
||||||
|
image: my-image
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- spec:
|
||||||
|
storageClassName: default
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHTTPSOverlay(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/https", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
- https-svc.yaml
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- sts-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/https/https-svc.yaml", httpsService)
|
||||||
|
th.WriteF("/app/https/sts-patch.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
serviceName: my-https-svc
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHTTPSTransformerRaw(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteF("/app/https/service/https-svc.yaml", httpsService)
|
||||||
|
th.WriteF("/app/https/transformer/transformer.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: PatchTransformer
|
||||||
|
metadata:
|
||||||
|
name: svcNameTran
|
||||||
|
target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: StatefulSet
|
||||||
|
name: my-sts
|
||||||
|
patch: |-
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
serviceName: my-https-svc
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeHTTPSTransformerBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/https/service", `
|
||||||
|
resources:
|
||||||
|
- https-svc.yaml
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/https/transformer", `
|
||||||
|
resources:
|
||||||
|
- transformer.yaml
|
||||||
|
`)
|
||||||
|
writeHTTPSTransformerRaw(th)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeConfigFromEnvOverlay(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/config", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
configMapGenerator:
|
||||||
|
- name: my-config
|
||||||
|
literals:
|
||||||
|
- MY_ENV=foo
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- sts-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/config/sts-patch.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: app
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: my-config
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeConfigFromEnvTransformerRaw(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteF("/app/config/map/generator.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: ConfigMapGenerator
|
||||||
|
metadata:
|
||||||
|
name: my-config
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
literals:
|
||||||
|
- MY_ENV=foo
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/config/transformer/transformer.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: PatchTransformer
|
||||||
|
metadata:
|
||||||
|
name: envFromConfigTrans
|
||||||
|
target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: StatefulSet
|
||||||
|
name: my-sts
|
||||||
|
patch: |-
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: app
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: my-config
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
func writeConfigFromEnvTransformerBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/config/map", `
|
||||||
|
resources:
|
||||||
|
- generator.yaml
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/config/transformer", `
|
||||||
|
resources:
|
||||||
|
- transformer.yaml
|
||||||
|
`)
|
||||||
|
writeConfigFromEnvTransformerRaw(th)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTolerationsOverlay(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/tolerations", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- sts-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/tolerations/sts-patch.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
tolerations:
|
||||||
|
- effect: NoExecute
|
||||||
|
key: node.kubernetes.io/not-ready
|
||||||
|
tolerationSeconds: 30
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTolerationsTransformerRaw(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteF("/app/tolerations/transformer.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: PatchTransformer
|
||||||
|
metadata:
|
||||||
|
name: tolTrans
|
||||||
|
target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: StatefulSet
|
||||||
|
name: my-sts
|
||||||
|
patch: |-
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
tolerations:
|
||||||
|
- effect: NoExecute
|
||||||
|
key: node.kubernetes.io/not-ready
|
||||||
|
tolerationSeconds: 30
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTolerationsTransformerBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/tolerations", `
|
||||||
|
resources:
|
||||||
|
- transformer.yaml
|
||||||
|
`)
|
||||||
|
writeTolerationsTransformerRaw(th)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeStorageOverlay(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/storage", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesJson6902:
|
||||||
|
- target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: StatefulSet
|
||||||
|
name: my-sts
|
||||||
|
path: sts-patch.json
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/storage/sts-patch.json", `
|
||||||
|
[{"op": "replace", "path": "/spec/volumeClaimTemplates/0/spec/storageClassName", "value": "my-sc"}]
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeStorageTransformerRaw(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteF("/app/storage/transformer.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: PatchTransformer
|
||||||
|
metadata:
|
||||||
|
name: storageTrans
|
||||||
|
target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: StatefulSet
|
||||||
|
name: my-sts
|
||||||
|
patch: |-
|
||||||
|
[{"op": "replace", "path": "/spec/volumeClaimTemplates/0/spec/storageClassName", "value": "my-sc"}]
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeStorageTransformerBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/storage", `
|
||||||
|
resources:
|
||||||
|
- transformer.yaml
|
||||||
|
`)
|
||||||
|
writeStorageTransformerRaw(th)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePatchingOverlays(th *kusttest_test.KustTestHarness) {
|
||||||
|
writeStorageOverlay(th)
|
||||||
|
writeConfigFromEnvOverlay(th)
|
||||||
|
writeTolerationsOverlay(th)
|
||||||
|
writeHTTPSOverlay(th)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writePatchingTransformersRaw(th *kusttest_test.KustTestHarness) {
|
||||||
|
writeStorageTransformerRaw(th)
|
||||||
|
writeConfigFromEnvTransformerRaw(th)
|
||||||
|
writeTolerationsTransformerRaw(th)
|
||||||
|
writeHTTPSTransformerRaw(th)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar to writePatchingTransformersRaw, except here the
|
||||||
|
// transformers and related artifacts are addressable as _bases_.
|
||||||
|
// They are listed in a kustomization file, and consumers of
|
||||||
|
// the plugin refer to the kustomization instead of to the local
|
||||||
|
// file in the "transformers:" field.
|
||||||
|
//
|
||||||
|
// Using bases makes the set of files relocatable with
|
||||||
|
// respect to the overlays, and avoids the need to relax load
|
||||||
|
// restrictions on file paths reaching outside the `dev` and
|
||||||
|
// `prod` kustomization roots. I.e. with bases tests can use
|
||||||
|
// NewKustTestHarness instead of NewKustTestHarnessNoLoadRestrictor.
|
||||||
|
//
|
||||||
|
// Using transformer plugins from _bases_ means the plugin config
|
||||||
|
// must be self-contained, i.e. the config may not have fields that
|
||||||
|
// refer to local files, since those files won't be present when
|
||||||
|
// the plugin is instantiated and used.
|
||||||
|
func writePatchingTransformerBases(th *kusttest_test.KustTestHarness) {
|
||||||
|
writeStorageTransformerBase(th)
|
||||||
|
writeConfigFromEnvTransformerBase(th)
|
||||||
|
writeTolerationsTransformerBase(th)
|
||||||
|
writeHTTPSTransformerBase(th)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here's a complex kustomization scenario that combines multiple overlays
|
||||||
|
// on a common base:
|
||||||
|
//
|
||||||
|
// dev prod
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// + ------- + + ------------ + ------------- +
|
||||||
|
// | | | | |
|
||||||
|
// | | | | |
|
||||||
|
// v | v v v
|
||||||
|
// storage + -----> config tolerations https
|
||||||
|
// | | | |
|
||||||
|
// | | | |
|
||||||
|
// | + --- + + --- + |
|
||||||
|
// | | | |
|
||||||
|
// | v v |
|
||||||
|
// + -----------------------> base <------------------ +
|
||||||
|
//
|
||||||
|
// The base resource is a statefulset. Each intermediate overlay manages or
|
||||||
|
// generates new resources and patches different aspects of the same base
|
||||||
|
// resource, without using any of the `namePrefix`, `nameSuffix` or `namespace`
|
||||||
|
// kustomization keywords.
|
||||||
|
//
|
||||||
|
// Intermediate overlays:
|
||||||
|
// - storage: Changes the storage class of the stateful set with a JSON patch.
|
||||||
|
// - config: Generates a config map and adds a field as an environment
|
||||||
|
// variable.
|
||||||
|
// - tolerations: Adds a new tolerations field in the spec.
|
||||||
|
// - https: Adds a new service resource and changes the service name in the
|
||||||
|
// stateful set.
|
||||||
|
//
|
||||||
|
// Top overlays:
|
||||||
|
// - dev: Combines the storage and config intermediate overlays.
|
||||||
|
// - prod: Combines the config, tolerations and https intermediate overlays.
|
||||||
|
|
||||||
|
func TestComplexComposition_Dev_Failure(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/dev")
|
||||||
|
writeStatefulSetBase(th)
|
||||||
|
writePatchingOverlays(th)
|
||||||
|
th.WriteK("/app/dev", `
|
||||||
|
resources:
|
||||||
|
- ../storage
|
||||||
|
- ../config
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected resource accumulation error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(), "already registered id: apps_v1_StatefulSet|~X|my-sts") {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const devDesiredResult = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: my-app
|
||||||
|
serviceName: my-svc
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: my-app
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: my-config
|
||||||
|
image: my-image
|
||||||
|
name: app
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- spec:
|
||||||
|
storageClassName: my-sc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
MY_ENV: foo
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: my-config
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestComplexComposition_Dev_SuccessWithRawTransformers(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarnessNoLoadRestrictor(t, "/app/dev")
|
||||||
|
writeStatefulSetBase(th)
|
||||||
|
writePatchingTransformersRaw(th)
|
||||||
|
th.WriteK("/app/dev", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
generators:
|
||||||
|
- ../config/map/generator.yaml
|
||||||
|
transformers:
|
||||||
|
- ../config/transformer/transformer.yaml
|
||||||
|
- ../storage/transformer.yaml
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, devDesiredResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComplexComposition_Dev_SuccessWithBaseTransformers(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/dev")
|
||||||
|
writeStatefulSetBase(th)
|
||||||
|
writePatchingTransformerBases(th)
|
||||||
|
th.WriteK("/app/dev", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
generators:
|
||||||
|
- ../config/map
|
||||||
|
transformers:
|
||||||
|
- ../config/transformer
|
||||||
|
- ../storage
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, devDesiredResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComplexComposition_Prod_Failure(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/prod")
|
||||||
|
writeStatefulSetBase(th)
|
||||||
|
writePatchingOverlays(th)
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
resources:
|
||||||
|
- ../config
|
||||||
|
- ../tolerations
|
||||||
|
- ../https
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected resource accumulation error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(), "already registered id: apps_v1_StatefulSet|~X|my-sts") {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const prodDesiredResult = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: my-sts
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: my-app
|
||||||
|
serviceName: my-https-svc
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: my-app
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: my-config
|
||||||
|
image: my-image
|
||||||
|
name: app
|
||||||
|
tolerations:
|
||||||
|
- effect: NoExecute
|
||||||
|
key: node.kubernetes.io/not-ready
|
||||||
|
tolerationSeconds: 30
|
||||||
|
volumeClaimTemplates:
|
||||||
|
- spec:
|
||||||
|
storageClassName: default
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: my-https-svc
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: https
|
||||||
|
port: 443
|
||||||
|
protocol: TCP
|
||||||
|
selector:
|
||||||
|
app: my-app
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
MY_ENV: foo
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: my-config
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestComplexComposition_Prod_SuccessWithRawTransformers(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarnessNoLoadRestrictor(t, "/app/prod")
|
||||||
|
writeStatefulSetBase(th)
|
||||||
|
writePatchingTransformersRaw(th)
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
- ../https/service/https-svc.yaml
|
||||||
|
generators:
|
||||||
|
- ../config/map/generator.yaml
|
||||||
|
transformers:
|
||||||
|
- ../config/transformer/transformer.yaml
|
||||||
|
- ../https/transformer/transformer.yaml
|
||||||
|
- ../tolerations/transformer.yaml
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, prodDesiredResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComplexComposition_Prod_SuccessWithBaseTransformers(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/prod")
|
||||||
|
writeStatefulSetBase(th)
|
||||||
|
writePatchingTransformerBases(th)
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
- ../https/service
|
||||||
|
generators:
|
||||||
|
- ../config/map
|
||||||
|
transformers:
|
||||||
|
- ../config/transformer
|
||||||
|
- ../https/transformer
|
||||||
|
- ../tolerations
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, prodDesiredResult)
|
||||||
|
}
|
||||||
313
api/internal/target/configmaps_test.go
Normal file
313
api/internal/target/configmaps_test.go
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
/*
|
||||||
|
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 target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate a Secret and a ConfigMap from the same data
|
||||||
|
// to compare the result.
|
||||||
|
func TestGeneratorBasics(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
namePrefix: blah-
|
||||||
|
configMapGenerator:
|
||||||
|
- name: bob
|
||||||
|
literals:
|
||||||
|
- fruit=apple
|
||||||
|
- vegetable=broccoli
|
||||||
|
envs:
|
||||||
|
- foo.env
|
||||||
|
files:
|
||||||
|
- passphrase=phrase.dat
|
||||||
|
- forces.txt
|
||||||
|
- name: json
|
||||||
|
literals:
|
||||||
|
- 'v2=[{"path": "var/druid/segment-cache"}]'
|
||||||
|
- >-
|
||||||
|
druid_segmentCache_locations=[{"path":
|
||||||
|
"var/druid/segment-cache",
|
||||||
|
"maxSize": 32000000000,
|
||||||
|
"freeSpacePercent": 1.0}]
|
||||||
|
secretGenerator:
|
||||||
|
- name: bob
|
||||||
|
literals:
|
||||||
|
- fruit=apple
|
||||||
|
- vegetable=broccoli
|
||||||
|
envs:
|
||||||
|
- foo.env
|
||||||
|
files:
|
||||||
|
- passphrase=phrase.dat
|
||||||
|
- forces.txt
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/foo.env", `
|
||||||
|
MOUNTAIN=everest
|
||||||
|
OCEAN=pacific
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/phrase.dat", `
|
||||||
|
Life is short.
|
||||||
|
But the years are long.
|
||||||
|
Not while the evil days come not.
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/forces.txt", `
|
||||||
|
gravitational
|
||||||
|
electromagnetic
|
||||||
|
strong nuclear
|
||||||
|
weak nuclear
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
MOUNTAIN: everest
|
||||||
|
OCEAN: pacific
|
||||||
|
forces.txt: |2
|
||||||
|
|
||||||
|
gravitational
|
||||||
|
electromagnetic
|
||||||
|
strong nuclear
|
||||||
|
weak nuclear
|
||||||
|
fruit: apple
|
||||||
|
passphrase: |2
|
||||||
|
|
||||||
|
Life is short.
|
||||||
|
But the years are long.
|
||||||
|
Not while the evil days come not.
|
||||||
|
vegetable: broccoli
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blah-bob-k772g5db55
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
druid_segmentCache_locations: '[{"path": "var/druid/segment-cache", "maxSize":
|
||||||
|
32000000000, "freeSpacePercent": 1.0}]'
|
||||||
|
v2: '[{"path": "var/druid/segment-cache"}]'
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blah-json-9gtcc2fgb4
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
MOUNTAIN: ZXZlcmVzdA==
|
||||||
|
OCEAN: cGFjaWZpYw==
|
||||||
|
forces.txt: CmdyYXZpdGF0aW9uYWwKZWxlY3Ryb21hZ25ldGljCnN0cm9uZyBudWNsZWFyCndlYWsgbnVjbGVhcgo=
|
||||||
|
fruit: YXBwbGU=
|
||||||
|
passphrase: CkxpZmUgaXMgc2hvcnQuCkJ1dCB0aGUgeWVhcnMgYXJlIGxvbmcuCk5vdCB3aGlsZSB0aGUgZXZpbCBkYXlzIGNvbWUgbm90Lgo=
|
||||||
|
vegetable: YnJvY2NvbGk=
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: blah-bob-gmc2824f4b
|
||||||
|
type: Opaque
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: These should be errors instead.
|
||||||
|
func TestGeneratorRepeatsInKustomization(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
namePrefix: blah-
|
||||||
|
configMapGenerator:
|
||||||
|
- name: bob
|
||||||
|
behavior: create
|
||||||
|
literals:
|
||||||
|
- bean=pinto
|
||||||
|
- star=wolf-rayet
|
||||||
|
literals:
|
||||||
|
- fruit=apple
|
||||||
|
- vegetable=broccoli
|
||||||
|
files:
|
||||||
|
- forces.txt
|
||||||
|
files:
|
||||||
|
- nobles=nobility.txt
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/forces.txt", `
|
||||||
|
gravitational
|
||||||
|
electromagnetic
|
||||||
|
strong nuclear
|
||||||
|
weak nuclear
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/nobility.txt", `
|
||||||
|
helium
|
||||||
|
neon
|
||||||
|
argon
|
||||||
|
krypton
|
||||||
|
xenon
|
||||||
|
radon
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
fruit: apple
|
||||||
|
nobles: |2
|
||||||
|
|
||||||
|
helium
|
||||||
|
neon
|
||||||
|
argon
|
||||||
|
krypton
|
||||||
|
xenon
|
||||||
|
radon
|
||||||
|
vegetable: broccoli
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blah-bob-gfkcbk5ckf
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeneratorOverlays(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
|
||||||
|
th.WriteK("/app/base1", `
|
||||||
|
namePrefix: p1-
|
||||||
|
configMapGenerator:
|
||||||
|
- name: com1
|
||||||
|
behavior: create
|
||||||
|
literals:
|
||||||
|
- from=base
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/base2", `
|
||||||
|
namePrefix: p2-
|
||||||
|
configMapGenerator:
|
||||||
|
- name: com2
|
||||||
|
behavior: create
|
||||||
|
literals:
|
||||||
|
- from=base
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlay/o1", `
|
||||||
|
resources:
|
||||||
|
- ../../base1
|
||||||
|
configMapGenerator:
|
||||||
|
- name: com1
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- from=overlay
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlay/o2", `
|
||||||
|
resources:
|
||||||
|
- ../../base2
|
||||||
|
configMapGenerator:
|
||||||
|
- name: com2
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- from=overlay
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
resources:
|
||||||
|
- o1
|
||||||
|
- o2
|
||||||
|
configMapGenerator:
|
||||||
|
- name: com1
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- foo=bar
|
||||||
|
- baz=qux
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
baz: qux
|
||||||
|
foo: bar
|
||||||
|
from: overlay
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations: {}
|
||||||
|
labels: {}
|
||||||
|
name: p1-com1-dhbbm922gd
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
from: overlay
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations: {}
|
||||||
|
labels: {}
|
||||||
|
name: p2-com2-c4b8md75k9
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigMapGeneratorMergeNamePrefix(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
configMapGenerator:
|
||||||
|
- name: cm
|
||||||
|
behavior: create
|
||||||
|
literals:
|
||||||
|
- foo=bar
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/o1", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
namePrefix: o1-
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/o2", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
nameSuffix: -o2
|
||||||
|
`)
|
||||||
|
th.WriteK("/app", `
|
||||||
|
resources:
|
||||||
|
- o1
|
||||||
|
- o2
|
||||||
|
configMapGenerator:
|
||||||
|
- name: o1-cm
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- big=bang
|
||||||
|
- name: cm-o2
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- big=crunch
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
big: bang
|
||||||
|
foo: bar
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations: {}
|
||||||
|
labels: {}
|
||||||
|
name: o1-cm-28g596k77k
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
big: crunch
|
||||||
|
foo: bar
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations: {}
|
||||||
|
labels: {}
|
||||||
|
name: cm-o2-gfcc59fg5m
|
||||||
|
`)
|
||||||
|
}
|
||||||
384
api/internal/target/crd_test.go
Normal file
384
api/internal/target/crd_test.go
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
/*
|
||||||
|
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 target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func writeBaseWithCrd(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
crds:
|
||||||
|
- mycrd.json
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- secret.yaml
|
||||||
|
- mykind.yaml
|
||||||
|
- bee.yaml
|
||||||
|
|
||||||
|
namePrefix: x-
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/bee.yaml", `
|
||||||
|
apiVersion: v1beta1
|
||||||
|
kind: Bee
|
||||||
|
metadata:
|
||||||
|
name: bee
|
||||||
|
spec:
|
||||||
|
action: fly
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/mykind.yaml", `
|
||||||
|
apiVersion: jingfang.example.com/v1beta1
|
||||||
|
kind: MyKind
|
||||||
|
metadata:
|
||||||
|
name: mykind
|
||||||
|
spec:
|
||||||
|
secretRef:
|
||||||
|
name: crdsecret
|
||||||
|
beeRef:
|
||||||
|
name: bee
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/secret.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: crdsecret
|
||||||
|
data:
|
||||||
|
PATH: yellowBrickRoad
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/mycrd.json", `
|
||||||
|
{
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.Bee": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "Bee",
|
||||||
|
"properties": {
|
||||||
|
"apiVersion": {
|
||||||
|
"description": "APIVersion defines the versioned schema of this representation of an object.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"description": "Kind is a string value representing the REST resource this object represents.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$ref": "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": [
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec",
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus",
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeList": {
|
||||||
|
"Schema": {
|
||||||
|
"required": [
|
||||||
|
"items"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"apiVersion": {
|
||||||
|
"description": "APIVersion defines the versioned schema of this representation of an object.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.Bee"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"description": "Kind is a string value representing the REST resource this object represents.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$ref": "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ListMeta"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": [
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.Bee",
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ListMeta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference": {
|
||||||
|
"Schema": {
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": []
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeSpec": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "BeeSpec defines the desired state of Bee"
|
||||||
|
},
|
||||||
|
"Dependencies": []
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeStatus": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "BeeStatus defines the observed state of Bee"
|
||||||
|
},
|
||||||
|
"Dependencies": []
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKind": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "MyKind",
|
||||||
|
"properties": {
|
||||||
|
"apiVersion": {
|
||||||
|
"description": "APIVersion defines the versioned schema of this representation of an object.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"description": "Kind is a string value representing the REST resource this object represents.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$ref": "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": [
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec",
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus",
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ObjectMeta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindList": {
|
||||||
|
"Schema": {
|
||||||
|
"required": [
|
||||||
|
"items"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"apiVersion": {
|
||||||
|
"description": "APIVersion defines the versioned schema of this representation of an object.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.MyKind"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"kind": {
|
||||||
|
"description": "Kind is a string value representing the REST resource this object represents.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"$ref": "sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ListMeta"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": [
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKind",
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/apimachinery/pkg/apis/meta/v1.ListMeta"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindSpec": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "MyKindSpec defines the desired state of MyKind",
|
||||||
|
"properties": {
|
||||||
|
"beeRef": {
|
||||||
|
"x-kubernetes-object-ref-api-version": "v1beta1",
|
||||||
|
"x-kubernetes-object-ref-kind": "Bee",
|
||||||
|
"$ref": "github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference"
|
||||||
|
},
|
||||||
|
"secretRef": {
|
||||||
|
"description": "If defined, use this secret for configuring the MYSQL_ROOT_PASSWORD",
|
||||||
|
"x-kubernetes-object-ref-api-version": "v1",
|
||||||
|
"x-kubernetes-object-ref-kind": "Secret",
|
||||||
|
"$ref": "sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1.LocalObjectReference"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Dependencies": [
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.BeeObjectReference",
|
||||||
|
"sigs.k8s.io/kustomize/pseudo/k8s/api/core/v1.LocalObjectReference"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"github.com/example/pkg/apis/jingfang/v1beta1.MyKindStatus": {
|
||||||
|
"Schema": {
|
||||||
|
"description": "MyKindStatus defines the observed state of MyKind"
|
||||||
|
},
|
||||||
|
"Dependencies": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCrdBase(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
writeBaseWithCrd(th)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
PATH: yellowBrickRoad
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: x-crdsecret
|
||||||
|
---
|
||||||
|
apiVersion: jingfang.example.com/v1beta1
|
||||||
|
kind: MyKind
|
||||||
|
metadata:
|
||||||
|
name: x-mykind
|
||||||
|
spec:
|
||||||
|
beeRef:
|
||||||
|
name: x-bee
|
||||||
|
secretRef:
|
||||||
|
name: x-crdsecret
|
||||||
|
---
|
||||||
|
apiVersion: v1beta1
|
||||||
|
kind: Bee
|
||||||
|
metadata:
|
||||||
|
name: x-bee
|
||||||
|
spec:
|
||||||
|
action: fly
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCrdWithOverlay(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
|
||||||
|
writeBaseWithCrd(th)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namePrefix: prod-
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- bee.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/bee.yaml", `
|
||||||
|
apiVersion: v1beta1
|
||||||
|
kind: Bee
|
||||||
|
metadata:
|
||||||
|
name: bee
|
||||||
|
spec:
|
||||||
|
action: makehoney
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
PATH: yellowBrickRoad
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: prod-x-crdsecret
|
||||||
|
---
|
||||||
|
apiVersion: jingfang.example.com/v1beta1
|
||||||
|
kind: MyKind
|
||||||
|
metadata:
|
||||||
|
name: prod-x-mykind
|
||||||
|
spec:
|
||||||
|
beeRef:
|
||||||
|
name: prod-x-bee
|
||||||
|
secretRef:
|
||||||
|
name: prod-x-crdsecret
|
||||||
|
---
|
||||||
|
apiVersion: v1beta1
|
||||||
|
kind: Bee
|
||||||
|
metadata:
|
||||||
|
name: prod-x-bee
|
||||||
|
spec:
|
||||||
|
action: makehoney
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCrdWithContainers(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/crd/containers")
|
||||||
|
th.WriteK("/app/crd/containers", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- crd.yaml
|
||||||
|
images:
|
||||||
|
- name: test/test
|
||||||
|
newName: registry.gitlab.com/test
|
||||||
|
newTag: latest
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/crd/containers/crd.yaml", `
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: crontabs.stable.example.com
|
||||||
|
spec:
|
||||||
|
group: stable.example.com
|
||||||
|
scope: Namespaced
|
||||||
|
names:
|
||||||
|
plural: crontabs
|
||||||
|
singular: crontab
|
||||||
|
kind: CronTab
|
||||||
|
shortNames:
|
||||||
|
- ct
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
description: Containers allows injecting additional containers
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: crontabs.stable.example.com
|
||||||
|
spec:
|
||||||
|
group: stable.example.com
|
||||||
|
names:
|
||||||
|
kind: CronTab
|
||||||
|
plural: crontabs
|
||||||
|
shortNames:
|
||||||
|
- ct
|
||||||
|
singular: crontab
|
||||||
|
scope: Namespaced
|
||||||
|
validation:
|
||||||
|
openAPIV3Schema:
|
||||||
|
properties:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
description: Containers allows injecting additional containers
|
||||||
|
`)
|
||||||
|
}
|
||||||
332
api/internal/target/customconfig_test.go
Normal file
332
api/internal/target/customconfig_test.go
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
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 target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeBaseReferencingCustomConfig(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
namePrefix: x-
|
||||||
|
commonLabels:
|
||||||
|
app: myApp
|
||||||
|
vars:
|
||||||
|
- name: APRIL_DIET
|
||||||
|
objref:
|
||||||
|
kind: Giraffe
|
||||||
|
name: april
|
||||||
|
fieldref:
|
||||||
|
fieldpath: spec.diet
|
||||||
|
- name: KOKO_DIET
|
||||||
|
objref:
|
||||||
|
kind: Gorilla
|
||||||
|
name: koko
|
||||||
|
fieldref:
|
||||||
|
fieldpath: spec.diet
|
||||||
|
resources:
|
||||||
|
- animalPark.yaml
|
||||||
|
- giraffes.yaml
|
||||||
|
- gorilla.yaml
|
||||||
|
configurations:
|
||||||
|
- config/defaults.yaml
|
||||||
|
- config/custom.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/giraffes.yaml", `
|
||||||
|
kind: Giraffe
|
||||||
|
metadata:
|
||||||
|
name: april
|
||||||
|
spec:
|
||||||
|
diet: mimosa
|
||||||
|
location: NE
|
||||||
|
---
|
||||||
|
kind: Giraffe
|
||||||
|
metadata:
|
||||||
|
name: may
|
||||||
|
spec:
|
||||||
|
diet: acacia
|
||||||
|
location: SE
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/gorilla.yaml", `
|
||||||
|
kind: Gorilla
|
||||||
|
metadata:
|
||||||
|
name: koko
|
||||||
|
spec:
|
||||||
|
diet: bambooshoots
|
||||||
|
location: SW
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/animalPark.yaml", `
|
||||||
|
kind: AnimalPark
|
||||||
|
metadata:
|
||||||
|
name: sandiego
|
||||||
|
spec:
|
||||||
|
gorillaRef:
|
||||||
|
name: koko
|
||||||
|
giraffeRef:
|
||||||
|
name: april
|
||||||
|
food:
|
||||||
|
- "$(APRIL_DIET)"
|
||||||
|
- "$(KOKO_DIET)"
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomConfig(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
makeBaseReferencingCustomConfig(th)
|
||||||
|
th.WriteDefaultConfigs("/app/base/config/defaults.yaml")
|
||||||
|
th.WriteF("/app/base/config/custom.yaml", `
|
||||||
|
nameReference:
|
||||||
|
- kind: Gorilla
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: AnimalPark
|
||||||
|
path: spec/gorillaRef/name
|
||||||
|
- kind: Giraffe
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: AnimalPark
|
||||||
|
path: spec/giraffeRef/name
|
||||||
|
varReference:
|
||||||
|
- path: spec/food
|
||||||
|
kind: AnimalPark
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
kind: AnimalPark
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: x-sandiego
|
||||||
|
spec:
|
||||||
|
food:
|
||||||
|
- mimosa
|
||||||
|
- bambooshoots
|
||||||
|
giraffeRef:
|
||||||
|
name: x-april
|
||||||
|
gorillaRef:
|
||||||
|
name: x-koko
|
||||||
|
---
|
||||||
|
kind: Giraffe
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: x-april
|
||||||
|
spec:
|
||||||
|
diet: mimosa
|
||||||
|
location: NE
|
||||||
|
---
|
||||||
|
kind: Giraffe
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: x-may
|
||||||
|
spec:
|
||||||
|
diet: acacia
|
||||||
|
location: SE
|
||||||
|
---
|
||||||
|
kind: Gorilla
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: x-koko
|
||||||
|
spec:
|
||||||
|
diet: bambooshoots
|
||||||
|
location: SW
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomConfigWithDefaultOverspecification(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
makeBaseReferencingCustomConfig(th)
|
||||||
|
th.WriteDefaultConfigs("/app/base/config/defaults.yaml")
|
||||||
|
// Specifying namePrefix here conflicts with (is the same as)
|
||||||
|
// the defaults written above. This is intentional in the
|
||||||
|
// test to assure duplicate config doesn't cause problems.
|
||||||
|
th.WriteF("/app/base/config/custom.yaml", `
|
||||||
|
namePrefix:
|
||||||
|
- path: metadata/name
|
||||||
|
nameReference:
|
||||||
|
- kind: Gorilla
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: AnimalPark
|
||||||
|
path: spec/gorillaRef/name
|
||||||
|
- kind: Giraffe
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: AnimalPark
|
||||||
|
path: spec/giraffeRef/name
|
||||||
|
varReference:
|
||||||
|
- path: spec/food
|
||||||
|
kind: AnimalPark
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
kind: AnimalPark
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: x-sandiego
|
||||||
|
spec:
|
||||||
|
food:
|
||||||
|
- mimosa
|
||||||
|
- bambooshoots
|
||||||
|
giraffeRef:
|
||||||
|
name: x-april
|
||||||
|
gorillaRef:
|
||||||
|
name: x-koko
|
||||||
|
---
|
||||||
|
kind: Giraffe
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: x-april
|
||||||
|
spec:
|
||||||
|
diet: mimosa
|
||||||
|
location: NE
|
||||||
|
---
|
||||||
|
kind: Giraffe
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: x-may
|
||||||
|
spec:
|
||||||
|
diet: acacia
|
||||||
|
location: SE
|
||||||
|
---
|
||||||
|
kind: Gorilla
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
name: x-koko
|
||||||
|
spec:
|
||||||
|
diet: bambooshoots
|
||||||
|
location: SW
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFixedBug605_BaseCustomizationAvailableInOverlay(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
|
||||||
|
makeBaseReferencingCustomConfig(th)
|
||||||
|
th.WriteDefaultConfigs("/app/base/config/defaults.yaml")
|
||||||
|
th.WriteF("/app/base/config/custom.yaml", `
|
||||||
|
nameReference:
|
||||||
|
- kind: Gorilla
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: AnimalPark
|
||||||
|
path: spec/gorillaRef/name
|
||||||
|
- kind: Giraffe
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: AnimalPark
|
||||||
|
path: spec/giraffeRef/name
|
||||||
|
varReference:
|
||||||
|
- path: spec/food
|
||||||
|
kind: AnimalPark
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
namePrefix: o-
|
||||||
|
commonLabels:
|
||||||
|
movie: planetOfTheApes
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- animalPark.yaml
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
- ursus.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/ursus.yaml", `
|
||||||
|
kind: Gorilla
|
||||||
|
metadata:
|
||||||
|
name: ursus
|
||||||
|
spec:
|
||||||
|
diet: heston
|
||||||
|
location: Arizona
|
||||||
|
`)
|
||||||
|
// The following replaces the gorillaRef in the AnimalPark.
|
||||||
|
th.WriteF("/app/overlay/animalPark.yaml", `
|
||||||
|
kind: AnimalPark
|
||||||
|
metadata:
|
||||||
|
name: sandiego
|
||||||
|
spec:
|
||||||
|
gorillaRef:
|
||||||
|
name: ursus
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
kind: AnimalPark
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
movie: planetOfTheApes
|
||||||
|
name: o-x-sandiego
|
||||||
|
spec:
|
||||||
|
food:
|
||||||
|
- mimosa
|
||||||
|
- bambooshoots
|
||||||
|
giraffeRef:
|
||||||
|
name: o-x-april
|
||||||
|
gorillaRef:
|
||||||
|
name: o-ursus
|
||||||
|
---
|
||||||
|
kind: Giraffe
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
movie: planetOfTheApes
|
||||||
|
name: o-x-april
|
||||||
|
spec:
|
||||||
|
diet: mimosa
|
||||||
|
location: NE
|
||||||
|
---
|
||||||
|
kind: Giraffe
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
movie: planetOfTheApes
|
||||||
|
name: o-x-may
|
||||||
|
spec:
|
||||||
|
diet: acacia
|
||||||
|
location: SE
|
||||||
|
---
|
||||||
|
kind: Gorilla
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: myApp
|
||||||
|
movie: planetOfTheApes
|
||||||
|
name: o-x-koko
|
||||||
|
spec:
|
||||||
|
diet: bambooshoots
|
||||||
|
location: SW
|
||||||
|
---
|
||||||
|
kind: Gorilla
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
movie: planetOfTheApes
|
||||||
|
name: o-ursus
|
||||||
|
spec:
|
||||||
|
diet: heston
|
||||||
|
location: Arizona
|
||||||
|
`)
|
||||||
|
}
|
||||||
216
api/internal/target/customconfigofbuiltinplugin_test.go
Normal file
216
api/internal/target/customconfigofbuiltinplugin_test.go
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Demo custom configuration of a builtin transformation.
|
||||||
|
// This is a NamePrefixer that only touches Deployments
|
||||||
|
// and Services.
|
||||||
|
func TestCustomNamePrefixer(t *testing.T) {
|
||||||
|
tc := kusttest_test.NewPluginTestEnv(t).Set()
|
||||||
|
defer tc.Reset()
|
||||||
|
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"builtin", "", "PrefixSuffixTransformer")
|
||||||
|
|
||||||
|
th := kusttest_test.NewKustTestHarnessAllowPlugins(t, "/app")
|
||||||
|
|
||||||
|
th.WriteK("/app", `
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- role.yaml
|
||||||
|
- service.yaml
|
||||||
|
transformers:
|
||||||
|
- prefixer.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/prefixer.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: PrefixSuffixTransformer
|
||||||
|
metadata:
|
||||||
|
name: customPrefixer
|
||||||
|
prefix: zzz-
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: Deployment
|
||||||
|
path: metadata/name
|
||||||
|
- kind: Service
|
||||||
|
path: metadata/name
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: myDeployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
backend: awesome
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: whatever
|
||||||
|
image: whatever
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/role.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: myRole
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: zzz-myDeployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
backend: awesome
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: whatever
|
||||||
|
name: whatever
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: myRole
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: zzz-myService
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Demo custom configuration as a base.
|
||||||
|
func TestReusableCustomNamePrefixer(t *testing.T) {
|
||||||
|
tc := kusttest_test.NewPluginTestEnv(t).Set()
|
||||||
|
defer tc.Reset()
|
||||||
|
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"builtin", "", "PrefixSuffixTransformer")
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"builtin", "", "LabelTransformer")
|
||||||
|
|
||||||
|
th := kusttest_test.NewKustTestHarnessAllowPlugins(t, "/app/foo")
|
||||||
|
|
||||||
|
// This kustomization file contains resources that
|
||||||
|
// all happen to be plugin configurations. This makes
|
||||||
|
// these plugins all available as part of a base,
|
||||||
|
// re-usable in any number of other kustomizations.
|
||||||
|
// Just specify the path (or URL) to this base in the
|
||||||
|
// "transformers:" field (not the "resources" field).
|
||||||
|
th.WriteK("/app/mytransformers", `
|
||||||
|
resources:
|
||||||
|
- prefixer.yaml
|
||||||
|
- labeller.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/mytransformers/prefixer.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: PrefixSuffixTransformer
|
||||||
|
metadata:
|
||||||
|
name: myPrefixer
|
||||||
|
prefix: zzz-
|
||||||
|
fieldSpecs:
|
||||||
|
- kind: Deployment
|
||||||
|
path: metadata/name
|
||||||
|
- kind: Service
|
||||||
|
path: metadata/name
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/mytransformers/labeller.yaml", `
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: LabelTransformer
|
||||||
|
metadata:
|
||||||
|
name: myLabeller
|
||||||
|
labels:
|
||||||
|
company: acmeCorp
|
||||||
|
fieldSpecs:
|
||||||
|
- path: spec/template/metadata/labels
|
||||||
|
kind: Deployment
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteK("/app/foo", `
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- role.yaml
|
||||||
|
- service.yaml
|
||||||
|
transformers:
|
||||||
|
- ../mytransformers
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/foo/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: myDeployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
backend: awesome
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: whatever
|
||||||
|
image: whatever
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/foo/role.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: myRole
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/foo/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: myService
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: zzz-myDeployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
backend: awesome
|
||||||
|
company: acmeCorp
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: whatever
|
||||||
|
name: whatever
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
name: myRole
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: zzz-myService
|
||||||
|
`)
|
||||||
|
}
|
||||||
490
api/internal/target/diamondcomposition_test.go
Normal file
490
api/internal/target/diamondcomposition_test.go
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
const patchAddProbe = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: my-deployment
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
`
|
||||||
|
|
||||||
|
const container = `{ "image": "my-image", "livenessProbe": { "httpGet" : {"path": "/healthz", "port": 8080 } }, "name": "my-deployment"}`
|
||||||
|
|
||||||
|
const patchJsonAddProbe = `[{"op": "replace", "path": "/spec/template/spec/containers/0", "value": ` +
|
||||||
|
container + `}]`
|
||||||
|
|
||||||
|
const patchDnsPolicy = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
`
|
||||||
|
const patchJsonDnsPolicy = `[{"op": "add", "path": "/spec/template/spec/dnsPolicy", "value": "ClusterFirst"}]`
|
||||||
|
|
||||||
|
const patchRestartPolicy = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
restartPolicy: Always
|
||||||
|
`
|
||||||
|
const patchJsonRestartPolicy = `[{"op": "add", "path": "/spec/template/spec/restartPolicy", "value": "Always"}]`
|
||||||
|
|
||||||
|
func writeDeploymentBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteF("/app/base/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
dnsPolicy: "None"
|
||||||
|
containers:
|
||||||
|
- name: my-deployment
|
||||||
|
image: my-image
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeProbeOverlay(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/probe", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- dep-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/probe/dep-patch.yaml", patchAddProbe)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeDNSOverlay(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/dns", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- dep-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/dns/dep-patch.yaml", patchDnsPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeRestartOverlay(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/restart", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- dep-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/restart/dep-patch.yaml", patchRestartPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here's a composite kustomization, that combines multiple overlays
|
||||||
|
// (replicas, dns and metadata) which patch the same base resource.
|
||||||
|
//
|
||||||
|
// The base resource is a deployment and the overlays patch aspects
|
||||||
|
// of it, without using any of the `namePrefix`, `nameSuffix` or `namespace`
|
||||||
|
// kustomization keywords.
|
||||||
|
//
|
||||||
|
// composite
|
||||||
|
// / | \
|
||||||
|
// probe dns restart
|
||||||
|
// \ | /
|
||||||
|
// base
|
||||||
|
//
|
||||||
|
func TestIssue1251_CompositeDiamond_Failure(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/composite")
|
||||||
|
writeDeploymentBase(th)
|
||||||
|
writeProbeOverlay(th)
|
||||||
|
writeDNSOverlay(th)
|
||||||
|
writeRestartOverlay(th)
|
||||||
|
|
||||||
|
th.WriteK("/app/composite", `
|
||||||
|
resources:
|
||||||
|
- ../probe
|
||||||
|
- ../dns
|
||||||
|
- ../restart
|
||||||
|
`)
|
||||||
|
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected resource accumulation error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(), "already registered id: apps_v1_Deployment|~X|my-deployment") {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedPatchedDeployment = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: my-image
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
name: my-deployment
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
restartPolicy: Always
|
||||||
|
`
|
||||||
|
|
||||||
|
// This test reuses some methods from TestIssue1251_CompositeDiamond,
|
||||||
|
// but overwrites the kustomization files in the overlays.
|
||||||
|
func TestIssue1251_Patches_Overlayed(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/restart")
|
||||||
|
writeDeploymentBase(th)
|
||||||
|
|
||||||
|
// probe overlays base.
|
||||||
|
writeProbeOverlay(th)
|
||||||
|
|
||||||
|
// dns overlays probe.
|
||||||
|
writeDNSOverlay(th)
|
||||||
|
th.WriteK("/app/dns", `
|
||||||
|
resources:
|
||||||
|
- ../probe
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- dep-patch.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
// restart overlays dns.
|
||||||
|
writeRestartOverlay(th)
|
||||||
|
th.WriteK("/app/restart", `
|
||||||
|
resources:
|
||||||
|
- ../dns
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- dep-patch.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, expectedPatchedDeployment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue1251_Patches_Local(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/composite")
|
||||||
|
writeDeploymentBase(th)
|
||||||
|
|
||||||
|
th.WriteK("/app/composite", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- patchAddProbe.yaml
|
||||||
|
- patchDnsPolicy.yaml
|
||||||
|
- patchRestartPolicy.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/composite/patchRestartPolicy.yaml", patchRestartPolicy)
|
||||||
|
th.WriteF("/app/composite/patchDnsPolicy.yaml", patchDnsPolicy)
|
||||||
|
th.WriteF("/app/composite/patchAddProbe.yaml", patchAddProbe)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, expectedPatchedDeployment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func definePatchDirStructure(th *kusttest_test.KustTestHarness) {
|
||||||
|
writeDeploymentBase(th)
|
||||||
|
|
||||||
|
th.WriteF("/app/patches/patchRestartPolicy.yaml", patchRestartPolicy)
|
||||||
|
th.WriteF("/app/patches/patchDnsPolicy.yaml", patchDnsPolicy)
|
||||||
|
th.WriteF("/app/patches/patchAddProbe.yaml", patchAddProbe)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fails due to file load restrictor.
|
||||||
|
func TestIssue1251_Patches_ProdVsDev_Failure(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/prod")
|
||||||
|
definePatchDirStructure(th)
|
||||||
|
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- ../patches/patchAddProbe.yaml
|
||||||
|
- ../patches/patchDnsPolicy.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(),
|
||||||
|
"security; file '/app/patches/patchAddProbe.yaml' is not in or below '/app/prod'") {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const prodDevMergeResult1 = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: my-image
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
name: my-deployment
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
`
|
||||||
|
|
||||||
|
const prodDevMergeResult2 = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: my-deployment
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: my-image
|
||||||
|
name: my-deployment
|
||||||
|
dnsPolicy: ClusterFirst
|
||||||
|
restartPolicy: Always
|
||||||
|
`
|
||||||
|
|
||||||
|
// This test does what
|
||||||
|
// TestIssue1251_Patches_ProdVsDev_Failure
|
||||||
|
// failed to do, because this test does the equivalent
|
||||||
|
// os specifying `--load_restrictor none` on the build.
|
||||||
|
//
|
||||||
|
// This allows the use patch files located outside the
|
||||||
|
// kustomization root, and not in a kustomization
|
||||||
|
// themselves.
|
||||||
|
//
|
||||||
|
// Doing so means the kustomization using them is no
|
||||||
|
// longer relocatable, not addressible via a git URL,
|
||||||
|
// and not git clonable. It's no longer self-contained.
|
||||||
|
//
|
||||||
|
// Likewise suppressing load restrictions happens for
|
||||||
|
// the entire build (i.e. everything can reach outside
|
||||||
|
// the kustomization root), opening the user to whatever
|
||||||
|
// threat the load restrictor was meant to address.
|
||||||
|
func TestIssue1251_Patches_ProdVsDev(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarnessNoLoadRestrictor(t, "/app/prod")
|
||||||
|
definePatchDirStructure(th)
|
||||||
|
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- ../patches/patchAddProbe.yaml
|
||||||
|
- ../patches/patchDnsPolicy.yaml
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, prodDevMergeResult1)
|
||||||
|
|
||||||
|
th = kusttest_test.NewKustTestHarnessNoLoadRestrictor(t, "/app/dev")
|
||||||
|
definePatchDirStructure(th)
|
||||||
|
|
||||||
|
th.WriteK("/app/dev", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- ../patches/patchDnsPolicy.yaml
|
||||||
|
- ../patches/patchRestartPolicy.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err = th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, prodDevMergeResult2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue1251_Plugins_ProdVsDev(t *testing.T) {
|
||||||
|
tc := kusttest_test.NewPluginTestEnv(t).Set()
|
||||||
|
defer tc.Reset()
|
||||||
|
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"builtin", "", "PatchJson6902Transformer")
|
||||||
|
|
||||||
|
th := kusttest_test.NewKustTestHarnessAllowPlugins(t, "/app/prod")
|
||||||
|
defineTransformerDirStructure(th)
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
transformers:
|
||||||
|
- ../patches/addProbe
|
||||||
|
- ../patches/addDnsPolicy
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, prodDevMergeResult1)
|
||||||
|
|
||||||
|
th = kusttest_test.NewKustTestHarnessAllowPlugins(t, "/app/dev")
|
||||||
|
defineTransformerDirStructure(th)
|
||||||
|
th.WriteK("/app/dev", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
transformers:
|
||||||
|
- ../patches/addRestartPolicy
|
||||||
|
- ../patches/addDnsPolicy
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err = th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, prodDevMergeResult2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue1251_Plugins_Local(t *testing.T) {
|
||||||
|
tc := kusttest_test.NewPluginTestEnv(t).Set()
|
||||||
|
defer tc.Reset()
|
||||||
|
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"builtin", "", "PatchJson6902Transformer")
|
||||||
|
|
||||||
|
th := kusttest_test.NewKustTestHarnessAllowPlugins(t, "/app/composite")
|
||||||
|
writeDeploymentBase(th)
|
||||||
|
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/composite", "addDnsPolicy", patchJsonDnsPolicy)
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/composite", "addRestartPolicy", patchJsonRestartPolicy)
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/composite", "addProbe", patchJsonAddProbe)
|
||||||
|
|
||||||
|
th.WriteK("/app/composite", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
transformers:
|
||||||
|
- addDnsPolicyConfig.yaml
|
||||||
|
- addRestartPolicyConfig.yaml
|
||||||
|
- addProbeConfig.yaml
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, expectedPatchedDeployment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeJsonTransformerPluginConfig(
|
||||||
|
th *kusttest_test.KustTestHarness, path, name, patch string) {
|
||||||
|
th.WriteF(filepath.Join(path, name+"Config.yaml"),
|
||||||
|
fmt.Sprintf(`
|
||||||
|
apiVersion: builtin
|
||||||
|
kind: PatchJson6902Transformer
|
||||||
|
metadata:
|
||||||
|
name: %s
|
||||||
|
target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: Deployment
|
||||||
|
name: my-deployment
|
||||||
|
jsonOp: '%s'
|
||||||
|
`, name, patch))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remote in the sense that they are bundled in a different kustomization.
|
||||||
|
func TestIssue1251_Plugins_Bundled(t *testing.T) {
|
||||||
|
tc := kusttest_test.NewPluginTestEnv(t).Set()
|
||||||
|
defer tc.Reset()
|
||||||
|
|
||||||
|
tc.BuildGoPlugin(
|
||||||
|
"builtin", "", "PatchJson6902Transformer")
|
||||||
|
|
||||||
|
th := kusttest_test.NewKustTestHarnessAllowPlugins(t, "/app/composite")
|
||||||
|
writeDeploymentBase(th)
|
||||||
|
|
||||||
|
th.WriteK("/app/patches", `
|
||||||
|
resources:
|
||||||
|
- addDnsPolicyConfig.yaml
|
||||||
|
- addRestartPolicyConfig.yaml
|
||||||
|
- addProbeConfig.yaml
|
||||||
|
`)
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/patches", "addDnsPolicy", patchJsonDnsPolicy)
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/patches", "addRestartPolicy", patchJsonRestartPolicy)
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/patches", "addProbe", patchJsonAddProbe)
|
||||||
|
|
||||||
|
th.WriteK("/app/composite", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
transformers:
|
||||||
|
- ../patches
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, expectedPatchedDeployment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func defineTransformerDirStructure(th *kusttest_test.KustTestHarness) {
|
||||||
|
writeDeploymentBase(th)
|
||||||
|
|
||||||
|
th.WriteK("/app/patches/addDnsPolicy", `
|
||||||
|
resources:
|
||||||
|
- addDnsPolicyConfig.yaml
|
||||||
|
`)
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/patches/addDnsPolicy", "addDnsPolicy", patchJsonDnsPolicy)
|
||||||
|
|
||||||
|
th.WriteK("/app/patches/addRestartPolicy", `
|
||||||
|
resources:
|
||||||
|
- addRestartPolicyConfig.yaml
|
||||||
|
`)
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/patches/addRestartPolicy", "addRestartPolicy", patchJsonRestartPolicy)
|
||||||
|
|
||||||
|
th.WriteK("/app/patches/addProbe", `
|
||||||
|
resources:
|
||||||
|
- addProbeConfig.yaml
|
||||||
|
`)
|
||||||
|
writeJsonTransformerPluginConfig(
|
||||||
|
th, "/app/patches/addProbe", "addProbe", patchJsonAddProbe)
|
||||||
|
}
|
||||||
225
api/internal/target/diamonds_test.go
Normal file
225
api/internal/target/diamonds_test.go
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Here's a structure of two kustomizations,
|
||||||
|
// `dev` and `prod`, individually deployable,
|
||||||
|
// that depend on a diamond that combines
|
||||||
|
// multiple tenants (kirk, spock and bones),
|
||||||
|
// each sharing a common base.
|
||||||
|
//
|
||||||
|
// The objects used are contrived to avoid
|
||||||
|
// clouding the example with authentic
|
||||||
|
// but verbose Deployment boilerplate.
|
||||||
|
//
|
||||||
|
// Patches are applied at various levels,
|
||||||
|
// requiring more specificity as needed.
|
||||||
|
//
|
||||||
|
// dev prod
|
||||||
|
// \ /
|
||||||
|
// tenants
|
||||||
|
// / | \
|
||||||
|
// kirk spock bones
|
||||||
|
// \ | /
|
||||||
|
// base
|
||||||
|
//
|
||||||
|
func writeDiamondBase(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
resources:
|
||||||
|
- deploy.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/deploy.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: storefront
|
||||||
|
spec:
|
||||||
|
numReplicas: 1
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeKirk(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/kirk", `
|
||||||
|
namePrefix: kirk-
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
- configmap.yaml
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- dep-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/kirk/dep-patch.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: storefront
|
||||||
|
spec:
|
||||||
|
type: Confident
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/kirk/configmap.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: settings
|
||||||
|
data:
|
||||||
|
phaser: caress
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSpock(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/spock", `
|
||||||
|
namePrefix: spock-
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- dep-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/spock/dep-patch.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: storefront
|
||||||
|
spec:
|
||||||
|
type: Logical
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeBones(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/bones", `
|
||||||
|
namePrefix: bones-
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- dep-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/bones/dep-patch.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: storefront
|
||||||
|
spec:
|
||||||
|
type: Concerned
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeTenants(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/tenants", `
|
||||||
|
namePrefix: t-
|
||||||
|
resources:
|
||||||
|
- ../kirk
|
||||||
|
- ../spock
|
||||||
|
- ../bones
|
||||||
|
- configMap.yaml
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- bones-patch.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/tenants/bones-patch.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: bones-storefront
|
||||||
|
spec:
|
||||||
|
mood: Cantankerous
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/tenants/configMap.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: federation
|
||||||
|
data:
|
||||||
|
zone: neutral
|
||||||
|
guardian: forever
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasicDiamond(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/prod")
|
||||||
|
writeDiamondBase(th)
|
||||||
|
writeKirk(th)
|
||||||
|
writeSpock(th)
|
||||||
|
writeBones(th)
|
||||||
|
writeTenants(th)
|
||||||
|
th.WriteK("/app/prod", `
|
||||||
|
namePrefix: prod-
|
||||||
|
resources:
|
||||||
|
- ../tenants
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- patches.yaml
|
||||||
|
`)
|
||||||
|
// The patch only has to be specific enough
|
||||||
|
// to match the item.
|
||||||
|
th.WriteF("/app/prod/patches.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: t-kirk-storefront
|
||||||
|
spec:
|
||||||
|
numReplicas: 10000
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: federation
|
||||||
|
data:
|
||||||
|
guardian: ofTheGalaxy
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: t-federation
|
||||||
|
data:
|
||||||
|
zone: twilight
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: prod-t-kirk-storefront
|
||||||
|
spec:
|
||||||
|
numReplicas: 10000
|
||||||
|
type: Confident
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
phaser: caress
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: prod-t-kirk-settings
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: prod-t-spock-storefront
|
||||||
|
spec:
|
||||||
|
numReplicas: 1
|
||||||
|
type: Logical
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: prod-t-bones-storefront
|
||||||
|
spec:
|
||||||
|
mood: Cantankerous
|
||||||
|
numReplicas: 1
|
||||||
|
type: Concerned
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
guardian: ofTheGalaxy
|
||||||
|
zone: twilight
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: prod-t-federation
|
||||||
|
`)
|
||||||
|
}
|
||||||
1248
api/internal/target/extendedpatch_test.go
Normal file
1248
api/internal/target/extendedpatch_test.go
Normal file
File diff suppressed because it is too large
Load Diff
597
api/internal/target/generatormergeandreplace_test.go
Normal file
597
api/internal/target/generatormergeandreplace_test.go
Normal file
@@ -0,0 +1,597 @@
|
|||||||
|
/*
|
||||||
|
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 target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSimpleBase(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namePrefix: team-foo-
|
||||||
|
commonLabels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
commonAnnotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
resources:
|
||||||
|
- service.yaml
|
||||||
|
- deployment.yaml
|
||||||
|
- networkpolicy.yaml
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/networkpolicy.yaml", `
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- {key: app, operator: In, values: [test]}
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: nginx
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: team-foo-nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: team-foo-nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: team-foo-nginx
|
||||||
|
spec:
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
podSelector:
|
||||||
|
matchExpressions:
|
||||||
|
- key: app
|
||||||
|
operator: In
|
||||||
|
values:
|
||||||
|
- test
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeBaseWithGenerators(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namePrefix: team-foo-
|
||||||
|
commonLabels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
commonAnnotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- service.yaml
|
||||||
|
configMapGenerator:
|
||||||
|
- name: configmap-in-base
|
||||||
|
literals:
|
||||||
|
- foo=bar
|
||||||
|
secretGenerator:
|
||||||
|
- name: secret-in-base
|
||||||
|
literals:
|
||||||
|
- username=admin
|
||||||
|
- password=somepw
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
mountPath: /tmp/ps
|
||||||
|
volumes:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
emptyDir: {}
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-base
|
||||||
|
name: configmap-in-base
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBaseWithGeneratorsAlone(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
makeBaseWithGenerators(th)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: team-foo-nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/ps
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: team-foo-configmap-in-base-bbdmdh7m8t
|
||||||
|
name: configmap-in-base
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: team-foo-nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
foo: bar
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: team-foo-configmap-in-base-bbdmdh7m8t
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
password: c29tZXB3
|
||||||
|
username: YWRtaW4=
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: team-foo-secret-in-base-tkm7hhtf8d
|
||||||
|
type: Opaque
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeAndReplaceGenerators(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/overlay")
|
||||||
|
makeBaseWithGenerators(th)
|
||||||
|
th.WriteF("/overlay/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
volumes:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
emptyDir: null
|
||||||
|
gcePersistentDisk:
|
||||||
|
pdName: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-overlay
|
||||||
|
name: configmap-in-overlay
|
||||||
|
`)
|
||||||
|
th.WriteK("/overlay", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namePrefix: staging-
|
||||||
|
commonLabels:
|
||||||
|
env: staging
|
||||||
|
team: override-foo
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- deployment.yaml
|
||||||
|
resources:
|
||||||
|
- ../app
|
||||||
|
configMapGenerator:
|
||||||
|
- name: configmap-in-overlay
|
||||||
|
literals:
|
||||||
|
- hello=world
|
||||||
|
- name: configmap-in-base
|
||||||
|
behavior: replace
|
||||||
|
literals:
|
||||||
|
- foo=override-bar
|
||||||
|
secretGenerator:
|
||||||
|
- name: secret-in-base
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- proxy=haproxy
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: override-foo
|
||||||
|
name: staging-team-foo-nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: override-foo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: override-foo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: nginx
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/ps
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
volumes:
|
||||||
|
- gcePersistentDisk:
|
||||||
|
pdName: nginx-persistent-storage
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||||
|
name: configmap-in-overlay
|
||||||
|
- configMap:
|
||||||
|
name: staging-team-foo-configmap-in-base-gh9d7t85gb
|
||||||
|
name: configmap-in-base
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: override-foo
|
||||||
|
name: staging-team-foo-nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: override-foo
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
foo: override-bar
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: override-foo
|
||||||
|
name: staging-team-foo-configmap-in-base-gh9d7t85gb
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
password: c29tZXB3
|
||||||
|
proxy: aGFwcm94eQ==
|
||||||
|
username: YWRtaW4=
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: override-foo
|
||||||
|
name: staging-team-foo-secret-in-base-c8db7gk2m2
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
hello: world
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
env: staging
|
||||||
|
team: override-foo
|
||||||
|
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeneratingIntoNamespaces(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
configMapGenerator:
|
||||||
|
- name: test
|
||||||
|
namespace: default
|
||||||
|
literals:
|
||||||
|
- key=value
|
||||||
|
- name: test
|
||||||
|
namespace: kube-system
|
||||||
|
literals:
|
||||||
|
- key=value
|
||||||
|
secretGenerator:
|
||||||
|
- name: test
|
||||||
|
namespace: default
|
||||||
|
literals:
|
||||||
|
- username=admin
|
||||||
|
- password=somepw
|
||||||
|
- name: test
|
||||||
|
namespace: kube-system
|
||||||
|
literals:
|
||||||
|
- username=admin
|
||||||
|
- password=somepw
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
key: value
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: test-t5t4md8fdm
|
||||||
|
namespace: default
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
key: value
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: test-t5t4md8fdm
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
password: c29tZXB3
|
||||||
|
username: YWRtaW4=
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: test-h65t9hg6kc
|
||||||
|
namespace: default
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
password: c29tZXB3
|
||||||
|
username: YWRtaW4=
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: test-h65t9hg6kc
|
||||||
|
namespace: kube-system
|
||||||
|
type: Opaque
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid that conflict is detected is the name are identical
|
||||||
|
// and namespace left to default
|
||||||
|
func TestConfigMapGeneratingIntoSameNamespace(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
configMapGenerator:
|
||||||
|
- name: test
|
||||||
|
namespace: default
|
||||||
|
literals:
|
||||||
|
- key=value
|
||||||
|
- name: test
|
||||||
|
literals:
|
||||||
|
- key=value
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "must merge or replace") {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid that conflict is detected is the name are identical
|
||||||
|
// and namespace left to default
|
||||||
|
func TestSecretGeneratingIntoSameNamespace(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
secretGenerator:
|
||||||
|
- name: test
|
||||||
|
namespace: default
|
||||||
|
literals:
|
||||||
|
- username=admin
|
||||||
|
- password=somepw
|
||||||
|
- name: test
|
||||||
|
literals:
|
||||||
|
- username=admin
|
||||||
|
- password=somepw
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "must merge or replace") {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
115
api/internal/target/generatoroptions_test.go
Normal file
115
api/internal/target/generatoroptions_test.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 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 target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSecretGenerator(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
secretGenerator:
|
||||||
|
- name: bob
|
||||||
|
literals:
|
||||||
|
- FRUIT=apple
|
||||||
|
- VEGETABLE=carrot
|
||||||
|
files:
|
||||||
|
- foo.env
|
||||||
|
- passphrase=phrase.dat
|
||||||
|
envs:
|
||||||
|
- foo.env
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/foo.env", `
|
||||||
|
MOUNTAIN=everest
|
||||||
|
OCEAN=pacific
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/phrase.dat", "dat phrase")
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
FRUIT: YXBwbGU=
|
||||||
|
MOUNTAIN: ZXZlcmVzdA==
|
||||||
|
OCEAN: cGFjaWZpYw==
|
||||||
|
VEGETABLE: Y2Fycm90
|
||||||
|
foo.env: Ck1PVU5UQUlOPWV2ZXJlc3QKT0NFQU49cGFjaWZpYwo=
|
||||||
|
passphrase: ZGF0IHBocmFzZQ==
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: bob-kf5c9fccbt
|
||||||
|
type: Opaque
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeneratorOptionsWithBases(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
labels:
|
||||||
|
foo: bar
|
||||||
|
configMapGenerator:
|
||||||
|
- name: shouldNotHaveHash
|
||||||
|
literals:
|
||||||
|
- foo=bar
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: false
|
||||||
|
labels:
|
||||||
|
fruit: apple
|
||||||
|
configMapGenerator:
|
||||||
|
- name: shouldHaveHash
|
||||||
|
literals:
|
||||||
|
- fruit=apple
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
foo: bar
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
foo: bar
|
||||||
|
name: shouldNotHaveHash
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
fruit: apple
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
fruit: apple
|
||||||
|
name: shouldHaveHash-2k9hc848ff
|
||||||
|
`)
|
||||||
|
}
|
||||||
244
api/internal/target/inlinepatch_test.go
Normal file
244
api/internal/target/inlinepatch_test.go
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeResourcesForPatchTest(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteF("/app/base/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
mountPath: /tmp/ps
|
||||||
|
volumes:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
emptyDir: {}
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-base
|
||||||
|
name: configmap-in-base
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrategicMergePatchInline(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
makeResourcesForPatchTest(th)
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- |-
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: image1
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: image1
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/ps
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-base
|
||||||
|
name: configmap-in-base
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONPatchInline(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
makeResourcesForPatchTest(th)
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
|
||||||
|
patchesJson6902:
|
||||||
|
- target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: Deployment
|
||||||
|
name: nginx
|
||||||
|
patch: |-
|
||||||
|
- op: replace
|
||||||
|
path: /spec/template/spec/containers/0/image
|
||||||
|
value: image1
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: image1
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/ps
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-base
|
||||||
|
name: configmap-in-base
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtendedPatchInlineJSON(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
makeResourcesForPatchTest(th)
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
|
||||||
|
patches:
|
||||||
|
- target:
|
||||||
|
kind: Deployment
|
||||||
|
name: nginx
|
||||||
|
patch: |-
|
||||||
|
- op: replace
|
||||||
|
path: /spec/template/spec/containers/0/image
|
||||||
|
value: image1
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: image1
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/ps
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-base
|
||||||
|
name: configmap-in-base
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtendedPatchInlineYAML(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/base")
|
||||||
|
makeResourcesForPatchTest(th)
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
|
||||||
|
patches:
|
||||||
|
- target:
|
||||||
|
kind: Deployment
|
||||||
|
name: nginx
|
||||||
|
patch: |-
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: image1
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: image1
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/ps
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-base
|
||||||
|
name: configmap-in-base
|
||||||
|
`)
|
||||||
|
}
|
||||||
411
api/internal/target/kusttarget.go
Normal file
411
api/internal/target/kusttarget.go
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package target implements state for the set of all
|
||||||
|
// resources to customize.
|
||||||
|
package target
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"sigs.k8s.io/kustomize/api/builtins"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/loader"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/transform"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KustTarget encapsulates the entirety of a kustomization build.
|
||||||
|
type KustTarget struct {
|
||||||
|
kustomization *types.Kustomization
|
||||||
|
ldr ifc.Loader
|
||||||
|
validator ifc.Validator
|
||||||
|
rFactory *resmap.Factory
|
||||||
|
tFactory resmap.PatchFactory
|
||||||
|
pLdr *loader.Loader
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKustTarget returns a new instance of KustTarget primed with a Loader.
|
||||||
|
func NewKustTarget(
|
||||||
|
ldr ifc.Loader,
|
||||||
|
validator ifc.Validator,
|
||||||
|
rFactory *resmap.Factory,
|
||||||
|
tFactory resmap.PatchFactory,
|
||||||
|
pLdr *loader.Loader) (*KustTarget, error) {
|
||||||
|
content, err := loadKustFile(ldr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
content = types.FixKustomizationPreUnmarshalling(content)
|
||||||
|
var k types.Kustomization
|
||||||
|
err = unmarshal(content, &k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
k.FixKustomizationPostUnmarshalling()
|
||||||
|
errs := k.EnforceFields()
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Failed to read kustomization file under %s:\n"+
|
||||||
|
strings.Join(errs, "\n"), ldr.Root())
|
||||||
|
}
|
||||||
|
return &KustTarget{
|
||||||
|
kustomization: &k,
|
||||||
|
ldr: ldr,
|
||||||
|
validator: validator,
|
||||||
|
rFactory: rFactory,
|
||||||
|
tFactory: tFactory,
|
||||||
|
pLdr: pLdr,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func quoted(l []string) []string {
|
||||||
|
r := make([]string, len(l))
|
||||||
|
for i, v := range l {
|
||||||
|
r[i] = "'" + v + "'"
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func commaOr(q []string) string {
|
||||||
|
return strings.Join(q[:len(q)-1], ", ") + " or " + q[len(q)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadKustFile(ldr ifc.Loader) ([]byte, error) {
|
||||||
|
var content []byte
|
||||||
|
match := 0
|
||||||
|
for _, kf := range konfig.RecognizedKustomizationFileNames() {
|
||||||
|
c, err := ldr.Load(kf)
|
||||||
|
if err == nil {
|
||||||
|
match += 1
|
||||||
|
content = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch match {
|
||||||
|
case 0:
|
||||||
|
return nil, NewErrMissingKustomization(ldr.Root())
|
||||||
|
case 1:
|
||||||
|
return content, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"Found multiple kustomization files under: %s\n", ldr.Root())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type errMissingKustomization struct {
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errMissingKustomization) Error() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"unable to find one of %v in directory '%s'",
|
||||||
|
commaOr(quoted(konfig.RecognizedKustomizationFileNames())),
|
||||||
|
e.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewErrMissingKustomization(p string) *errMissingKustomization {
|
||||||
|
return &errMissingKustomization{path: p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsMissingKustomizationFileError(err error) bool {
|
||||||
|
_, ok := err.(*errMissingKustomization)
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_, ok = errors.Cause(err).(*errMissingKustomization)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshal(y []byte, o interface{}) error {
|
||||||
|
j, err := yaml.YAMLToJSON(y)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dec := json.NewDecoder(bytes.NewReader(j))
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
return dec.Decode(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeCustomizedResMap creates a ResMap per kustomization instructions.
|
||||||
|
// The Resources in the returned ResMap are fully customized.
|
||||||
|
func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) {
|
||||||
|
return kt.makeCustomizedResMap(types.GarbageIgnore)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) MakePruneConfigMap() (resmap.ResMap, error) {
|
||||||
|
return kt.makeCustomizedResMap(types.GarbageCollect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) makeCustomizedResMap(
|
||||||
|
garbagePolicy types.GarbagePolicy) (resmap.ResMap, error) {
|
||||||
|
ra, err := kt.AccumulateTarget()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following steps must be done last, not as part of
|
||||||
|
// the recursion implicit in AccumulateTarget.
|
||||||
|
|
||||||
|
err = kt.addHashesToNames(ra)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given that names have changed (prefixs/suffixes added),
|
||||||
|
// fix all the back references to those names.
|
||||||
|
err = ra.FixBackReferences()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// With all the back references fixed, it's OK to resolve Vars.
|
||||||
|
err = ra.ResolveVars()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kt.computeInventory(ra, garbagePolicy)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ra.ResMap(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) addHashesToNames(
|
||||||
|
ra *accumulator.ResAccumulator) error {
|
||||||
|
p := builtins.NewHashTransformerPlugin()
|
||||||
|
err := kt.configureBuiltinPlugin(p, nil, builtinhelpers.HashTransformer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ra.Transform(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) computeInventory(
|
||||||
|
ra *accumulator.ResAccumulator, garbagePolicy types.GarbagePolicy) error {
|
||||||
|
inv := kt.kustomization.Inventory
|
||||||
|
if inv == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if inv.Type != "ConfigMap" {
|
||||||
|
return fmt.Errorf("don't know how to do that")
|
||||||
|
}
|
||||||
|
|
||||||
|
if inv.ConfigMap.Namespace != kt.kustomization.Namespace {
|
||||||
|
return fmt.Errorf("namespace mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
var c struct {
|
||||||
|
Policy string
|
||||||
|
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||||
|
}
|
||||||
|
c.Name = inv.ConfigMap.Name
|
||||||
|
c.Namespace = inv.ConfigMap.Namespace
|
||||||
|
c.Policy = garbagePolicy.String()
|
||||||
|
p := builtins.NewInventoryTransformerPlugin()
|
||||||
|
err := kt.configureBuiltinPlugin(p, c, builtinhelpers.InventoryTransformer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ra.Transform(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) shouldAddHashSuffixesToGeneratedResources() bool {
|
||||||
|
return kt.kustomization.GeneratorOptions == nil ||
|
||||||
|
!kt.kustomization.GeneratorOptions.DisableNameSuffixHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccumulateTarget returns a new ResAccumulator,
|
||||||
|
// holding customized resources and the data/rules used
|
||||||
|
// to do so. The name back references and vars are
|
||||||
|
// not yet fixed.
|
||||||
|
func (kt *KustTarget) AccumulateTarget() (
|
||||||
|
ra *accumulator.ResAccumulator, err error) {
|
||||||
|
ra = accumulator.MakeEmptyAccumulator()
|
||||||
|
err = kt.accumulateResources(ra, kt.kustomization.Resources)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "accumulating resources")
|
||||||
|
}
|
||||||
|
tConfig, err := builtinconfig.MakeTransformerConfig(
|
||||||
|
kt.ldr, kt.kustomization.Configurations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ra.MergeConfig(tConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
err, "merging config %v", tConfig)
|
||||||
|
}
|
||||||
|
crdTc, err := accumulator.LoadConfigFromCRDs(kt.ldr, kt.kustomization.Crds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
err, "loading CRDs %v", kt.kustomization.Crds)
|
||||||
|
}
|
||||||
|
err = ra.MergeConfig(crdTc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
err, "merging CRDs %v", crdTc)
|
||||||
|
}
|
||||||
|
err = kt.runGenerators(ra)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = kt.runTransformers(ra)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = ra.MergeVars(kt.kustomization.Vars)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
err, "merging vars %v", kt.kustomization.Vars)
|
||||||
|
}
|
||||||
|
return ra, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) runGenerators(
|
||||||
|
ra *accumulator.ResAccumulator) error {
|
||||||
|
var generators []resmap.Generator
|
||||||
|
gs, err := kt.configureBuiltinGenerators()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
generators = append(generators, gs...)
|
||||||
|
gs, err = kt.configureExternalGenerators()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "loading generator plugins")
|
||||||
|
}
|
||||||
|
generators = append(generators, gs...)
|
||||||
|
for _, g := range generators {
|
||||||
|
resMap, err := g.Generate()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ra.AbsorbAll(resMap)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "merging from generator %v", g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureExternalGenerators() ([]resmap.Generator, error) {
|
||||||
|
ra := accumulator.MakeEmptyAccumulator()
|
||||||
|
err := kt.accumulateResources(ra, kt.kustomization.Generators)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kt.pLdr.LoadGenerators(kt.ldr, kt.validator, ra.ResMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) runTransformers(ra *accumulator.ResAccumulator) error {
|
||||||
|
var r []resmap.Transformer
|
||||||
|
tConfig := ra.GetTransformerConfig()
|
||||||
|
lts, err := kt.configureBuiltinTransformers(tConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r = append(r, lts...)
|
||||||
|
lts, err = kt.configureExternalTransformers()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r = append(r, lts...)
|
||||||
|
t := transform.NewMultiTransformer(r)
|
||||||
|
return ra.Transform(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureExternalTransformers() ([]resmap.Transformer, error) {
|
||||||
|
ra := accumulator.MakeEmptyAccumulator()
|
||||||
|
err := kt.accumulateResources(ra, kt.kustomization.Transformers)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kt.pLdr.LoadTransformers(kt.ldr, kt.validator, ra.ResMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
// accumulateResources fills the given resourceAccumulator
|
||||||
|
// with resources read from the given list of paths.
|
||||||
|
func (kt *KustTarget) accumulateResources(
|
||||||
|
ra *accumulator.ResAccumulator, paths []string) error {
|
||||||
|
for _, path := range paths {
|
||||||
|
ldr, err := kt.ldr.New(path)
|
||||||
|
if err == nil {
|
||||||
|
err = kt.accumulateDirectory(ra, ldr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err2 := kt.accumulateFile(ra, path)
|
||||||
|
if err2 != nil {
|
||||||
|
// Log ldr.New() error to highlight git failures.
|
||||||
|
log.Print(err.Error())
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) accumulateDirectory(
|
||||||
|
ra *accumulator.ResAccumulator, ldr ifc.Loader) error {
|
||||||
|
defer ldr.Cleanup()
|
||||||
|
subKt, err := NewKustTarget(
|
||||||
|
ldr, kt.validator, kt.rFactory, kt.tFactory, kt.pLdr)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(
|
||||||
|
err, "couldn't make target for path '%s'", ldr.Root())
|
||||||
|
}
|
||||||
|
subRa, err := subKt.AccumulateTarget()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(
|
||||||
|
err, "recursed accumulation of path '%s'", ldr.Root())
|
||||||
|
}
|
||||||
|
err = ra.MergeAccumulator(subRa)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(
|
||||||
|
err, "recursed merging from path '%s'", ldr.Root())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) accumulateFile(
|
||||||
|
ra *accumulator.ResAccumulator, path string) error {
|
||||||
|
resources, err := kt.rFactory.FromFile(kt.ldr, path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "accumulating resources from '%s'", path)
|
||||||
|
}
|
||||||
|
err = ra.AppendAll(resources)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "merging resources from '%s'", path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinPlugin(
|
||||||
|
p resmap.Configurable, c interface{}, bpt builtinhelpers.BuiltinPluginType) (err error) {
|
||||||
|
var y []byte
|
||||||
|
if c != nil {
|
||||||
|
y, err = yaml.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(
|
||||||
|
err, "builtin %s marshal", bpt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = p.Config(resmap.NewPluginHelpers(kt.ldr, kt.validator, kt.rFactory), y)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "builtin %s config: %v", bpt, y)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
297
api/internal/target/kusttarget_configplugin.go
Normal file
297
api/internal/target/kusttarget_configplugin.go
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinhelpers"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Functions dedicated to configuring the builtin
|
||||||
|
// transformer and generator plugins using config data
|
||||||
|
// read from a kustomization file and from the
|
||||||
|
// config.TransformerConfig, whose data may be a
|
||||||
|
// mix of hardcoded values and data read from file.
|
||||||
|
//
|
||||||
|
// Non-builtin plugins will get their configuration
|
||||||
|
// from their own dedicated structs and YAML files.
|
||||||
|
//
|
||||||
|
// There are some loops in the functions below because
|
||||||
|
// the kustomization file would, say, allow someone to
|
||||||
|
// request multiple secrets be made, or run multiple
|
||||||
|
// image tag transforms. In these cases, we'll need
|
||||||
|
// N plugin instances with differing configurations.
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinGenerators() (
|
||||||
|
result []resmap.Generator, err error) {
|
||||||
|
for _, bpt := range []builtinhelpers.BuiltinPluginType{
|
||||||
|
builtinhelpers.ConfigMapGenerator,
|
||||||
|
builtinhelpers.SecretGenerator,
|
||||||
|
} {
|
||||||
|
r, err := generatorConfigurators[bpt](
|
||||||
|
kt, bpt, builtinhelpers.GeneratorFactories[bpt])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, r...)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kt *KustTarget) configureBuiltinTransformers(
|
||||||
|
tc *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
for _, bpt := range []builtinhelpers.BuiltinPluginType{
|
||||||
|
builtinhelpers.PatchStrategicMergeTransformer,
|
||||||
|
builtinhelpers.PatchTransformer,
|
||||||
|
builtinhelpers.NamespaceTransformer,
|
||||||
|
builtinhelpers.PrefixSuffixTransformer,
|
||||||
|
builtinhelpers.LabelTransformer,
|
||||||
|
builtinhelpers.AnnotationsTransformer,
|
||||||
|
builtinhelpers.PatchJson6902Transformer,
|
||||||
|
builtinhelpers.ReplicaCountTransformer,
|
||||||
|
builtinhelpers.ImageTagTransformer,
|
||||||
|
} {
|
||||||
|
r, err := transformerConfigurators[bpt](
|
||||||
|
kt, bpt, builtinhelpers.TransformerFactories[bpt], tc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, r...)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type gFactory func() resmap.GeneratorPlugin
|
||||||
|
|
||||||
|
var generatorConfigurators = map[builtinhelpers.BuiltinPluginType]func(
|
||||||
|
kt *KustTarget,
|
||||||
|
bpt builtinhelpers.BuiltinPluginType,
|
||||||
|
factory gFactory) (result []resmap.Generator, err error){
|
||||||
|
builtinhelpers.SecretGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
|
||||||
|
result []resmap.Generator, err error) {
|
||||||
|
var c struct {
|
||||||
|
types.GeneratorOptions
|
||||||
|
types.SecretArgs
|
||||||
|
}
|
||||||
|
if kt.kustomization.GeneratorOptions != nil {
|
||||||
|
c.GeneratorOptions = *kt.kustomization.GeneratorOptions
|
||||||
|
}
|
||||||
|
for _, args := range kt.kustomization.SecretGenerator {
|
||||||
|
c.SecretArgs = args
|
||||||
|
p := f()
|
||||||
|
err := kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
|
||||||
|
builtinhelpers.ConfigMapGenerator: func(kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f gFactory) (
|
||||||
|
result []resmap.Generator, err error) {
|
||||||
|
var c struct {
|
||||||
|
types.GeneratorOptions
|
||||||
|
types.ConfigMapArgs
|
||||||
|
}
|
||||||
|
if kt.kustomization.GeneratorOptions != nil {
|
||||||
|
c.GeneratorOptions = *kt.kustomization.GeneratorOptions
|
||||||
|
}
|
||||||
|
for _, args := range kt.kustomization.ConfigMapGenerator {
|
||||||
|
c.ConfigMapArgs = args
|
||||||
|
p := f()
|
||||||
|
err := kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type tFactory func() resmap.TransformerPlugin
|
||||||
|
|
||||||
|
var transformerConfigurators = map[builtinhelpers.BuiltinPluginType]func(
|
||||||
|
kt *KustTarget,
|
||||||
|
bpt builtinhelpers.BuiltinPluginType,
|
||||||
|
f tFactory,
|
||||||
|
tc *builtinconfig.TransformerConfig) (result []resmap.Transformer, err error){
|
||||||
|
builtinhelpers.NamespaceTransformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
var c struct {
|
||||||
|
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||||
|
FieldSpecs []types.FieldSpec
|
||||||
|
}
|
||||||
|
c.Namespace = kt.kustomization.Namespace
|
||||||
|
c.FieldSpecs = tc.NameSpace
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
return
|
||||||
|
},
|
||||||
|
|
||||||
|
builtinhelpers.PatchJson6902Transformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
var c struct {
|
||||||
|
Target types.PatchTarget `json:"target,omitempty" yaml:"target,omitempty"`
|
||||||
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
JsonOp string `json:"jsonOp,omitempty" yaml:"jsonOp,omitempty"`
|
||||||
|
}
|
||||||
|
for _, args := range kt.kustomization.PatchesJson6902 {
|
||||||
|
c.Target = *args.Target
|
||||||
|
c.Path = args.Path
|
||||||
|
c.JsonOp = args.Patch
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
builtinhelpers.PatchStrategicMergeTransformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
if len(kt.kustomization.PatchesStrategicMerge) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var c struct {
|
||||||
|
Paths []types.PatchStrategicMerge `json:"paths,omitempty" yaml:"paths,omitempty"`
|
||||||
|
Patches string `json:"patches,omitempty" yaml:"patches,omitempty"`
|
||||||
|
}
|
||||||
|
c.Paths = kt.kustomization.PatchesStrategicMerge
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
return
|
||||||
|
},
|
||||||
|
builtinhelpers.PatchTransformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, _ *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
if len(kt.kustomization.Patches) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var c struct {
|
||||||
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||||
|
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
|
||||||
|
}
|
||||||
|
for _, pc := range kt.kustomization.Patches {
|
||||||
|
c.Target = pc.Target
|
||||||
|
c.Patch = pc.Patch
|
||||||
|
c.Path = pc.Path
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
builtinhelpers.LabelTransformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
var c struct {
|
||||||
|
Labels map[string]string
|
||||||
|
FieldSpecs []types.FieldSpec
|
||||||
|
}
|
||||||
|
c.Labels = kt.kustomization.CommonLabels
|
||||||
|
c.FieldSpecs = tc.CommonLabels
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
return
|
||||||
|
},
|
||||||
|
builtinhelpers.AnnotationsTransformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
var c struct {
|
||||||
|
Annotations map[string]string
|
||||||
|
FieldSpecs []types.FieldSpec
|
||||||
|
}
|
||||||
|
c.Annotations = kt.kustomization.CommonAnnotations
|
||||||
|
c.FieldSpecs = tc.CommonAnnotations
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
return
|
||||||
|
},
|
||||||
|
builtinhelpers.PrefixSuffixTransformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
var c struct {
|
||||||
|
Prefix string
|
||||||
|
Suffix string
|
||||||
|
FieldSpecs []types.FieldSpec
|
||||||
|
}
|
||||||
|
c.Prefix = kt.kustomization.NamePrefix
|
||||||
|
c.Suffix = kt.kustomization.NameSuffix
|
||||||
|
c.FieldSpecs = tc.NamePrefix
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
return
|
||||||
|
},
|
||||||
|
builtinhelpers.ImageTagTransformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
var c struct {
|
||||||
|
ImageTag types.Image
|
||||||
|
FieldSpecs []types.FieldSpec
|
||||||
|
}
|
||||||
|
for _, args := range kt.kustomization.Images {
|
||||||
|
c.ImageTag = args
|
||||||
|
c.FieldSpecs = tc.Images
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
builtinhelpers.ReplicaCountTransformer: func(
|
||||||
|
kt *KustTarget, bpt builtinhelpers.BuiltinPluginType, f tFactory, tc *builtinconfig.TransformerConfig) (
|
||||||
|
result []resmap.Transformer, err error) {
|
||||||
|
var c struct {
|
||||||
|
Replica types.Replica
|
||||||
|
FieldSpecs []types.FieldSpec
|
||||||
|
}
|
||||||
|
for _, args := range kt.kustomization.Replicas {
|
||||||
|
c.Replica = args
|
||||||
|
c.FieldSpecs = tc.Replicas
|
||||||
|
p := f()
|
||||||
|
err = kt.configureBuiltinPlugin(p, c, bpt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
},
|
||||||
|
}
|
||||||
446
api/internal/target/kusttarget_test.go
Normal file
446
api/internal/target/kusttarget_test.go
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/loadertest"
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/target"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
kustomizationContent = `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namePrefix: foo-
|
||||||
|
nameSuffix: -bar
|
||||||
|
namespace: ns1
|
||||||
|
commonLabels:
|
||||||
|
app: nginx
|
||||||
|
commonAnnotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- namespace.yaml
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: false
|
||||||
|
configMapGenerator:
|
||||||
|
- name: literalConfigMap
|
||||||
|
literals:
|
||||||
|
- DB_USERNAME=admin
|
||||||
|
- DB_PASSWORD=somepw
|
||||||
|
secretGenerator:
|
||||||
|
- name: secret
|
||||||
|
literals:
|
||||||
|
- DB_USERNAME=admin
|
||||||
|
- DB_PASSWORD=somepw
|
||||||
|
type: Opaque
|
||||||
|
patchesJson6902:
|
||||||
|
- target:
|
||||||
|
group: apps
|
||||||
|
version: v1
|
||||||
|
kind: Deployment
|
||||||
|
name: dply1
|
||||||
|
path: jsonpatch.json
|
||||||
|
`
|
||||||
|
deploymentContent = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
metadata:
|
||||||
|
name: dply1
|
||||||
|
kind: Deployment
|
||||||
|
`
|
||||||
|
namespaceContent = `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: ns1
|
||||||
|
`
|
||||||
|
jsonpatchContent = `[
|
||||||
|
{"op": "add", "path": "/spec/replica", "value": "3"}
|
||||||
|
]`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResources(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/whatever")
|
||||||
|
th.WriteK("/whatever/", kustomizationContent)
|
||||||
|
th.WriteF("/whatever/deployment.yaml", deploymentContent)
|
||||||
|
th.WriteF("/whatever/namespace.yaml", namespaceContent)
|
||||||
|
th.WriteF("/whatever/jsonpatch.json", jsonpatchContent)
|
||||||
|
|
||||||
|
resources := []*resource.Resource{
|
||||||
|
th.RF().FromMapWithName("dply1", map[string]interface{}{
|
||||||
|
"apiVersion": "apps/v1",
|
||||||
|
"kind": "Deployment",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-dply1-bar",
|
||||||
|
"namespace": "ns1",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"spec": map[string]interface{}{
|
||||||
|
"replica": "3",
|
||||||
|
"selector": map[string]interface{}{
|
||||||
|
"matchLabels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"template": map[string]interface{}{
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
th.RF().FromMapWithName("ns1", map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Namespace",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-ns1-bar",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
th.RF().FromMapWithName("literalConfigMap",
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-literalConfigMap-bar-8d2dkb8k24",
|
||||||
|
"namespace": "ns1",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"DB_USERNAME": "admin",
|
||||||
|
"DB_PASSWORD": "somepw",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
th.RF().FromMapWithName("secret",
|
||||||
|
map[string]interface{}{
|
||||||
|
"apiVersion": "v1",
|
||||||
|
"kind": "Secret",
|
||||||
|
"metadata": map[string]interface{}{
|
||||||
|
"name": "foo-secret-bar-9btc7bt4kb",
|
||||||
|
"namespace": "ns1",
|
||||||
|
"labels": map[string]interface{}{
|
||||||
|
"app": "nginx",
|
||||||
|
},
|
||||||
|
"annotations": map[string]interface{}{
|
||||||
|
"note": "This is a test annotation",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"type": ifc.SecretTypeOpaque,
|
||||||
|
"data": map[string]interface{}{
|
||||||
|
"DB_USERNAME": base64.StdEncoding.EncodeToString([]byte("admin")),
|
||||||
|
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := resmap.New()
|
||||||
|
for _, r := range resources {
|
||||||
|
if err := expected.Append(r); err != nil {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected Resources error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = expected.ErrorIfNotEqualLists(actual); err != nil {
|
||||||
|
t.Fatalf("unexpected inequality: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKustomizationNotFound(t *testing.T) {
|
||||||
|
_, err := NewKustTarget(
|
||||||
|
loadertest.NewFakeLoader("/foo"),
|
||||||
|
valtest_test.MakeFakeValidator(), nil, nil, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected an error")
|
||||||
|
}
|
||||||
|
if err.Error() !=
|
||||||
|
`unable to find one of 'kustomization.yaml', 'kustomization.yml' or 'Kustomization' in directory '/foo'` {
|
||||||
|
t.Fatalf("unexpected error: %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResourceNotFound(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/whatever")
|
||||||
|
th.WriteK("/whatever", kustomizationContent)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Didn't get the expected error for an unknown resource")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), `cannot read file`) {
|
||||||
|
t.Fatalf("unexpected error: %q", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findSecret(m resmap.ResMap) *resource.Resource {
|
||||||
|
for _, r := range m.Resources() {
|
||||||
|
if r.OrgId().Kind == "Secret" {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDisableNameSuffixHash(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/whatever")
|
||||||
|
th.WriteK("/whatever/", kustomizationContent)
|
||||||
|
th.WriteF("/whatever/deployment.yaml", deploymentContent)
|
||||||
|
th.WriteF("/whatever/namespace.yaml", namespaceContent)
|
||||||
|
th.WriteF("/whatever/jsonpatch.json", jsonpatchContent)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected Resources error %v", err)
|
||||||
|
}
|
||||||
|
secret := findSecret(m)
|
||||||
|
if secret == nil {
|
||||||
|
t.Errorf("Expected to find a Secret")
|
||||||
|
}
|
||||||
|
if secret.GetName() != "foo-secret-bar-9btc7bt4kb" {
|
||||||
|
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
th.WriteK("/whatever/",
|
||||||
|
strings.Replace(kustomizationContent,
|
||||||
|
"disableNameSuffixHash: false",
|
||||||
|
"disableNameSuffixHash: true", -1))
|
||||||
|
m, err = th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected Resources error %v", err)
|
||||||
|
}
|
||||||
|
secret = findSecret(m)
|
||||||
|
if secret == nil {
|
||||||
|
t.Errorf("Expected to find a Secret")
|
||||||
|
}
|
||||||
|
if secret.GetName() != "foo-secret-bar" { // No hash at end.
|
||||||
|
t.Errorf("unexpected secret resource name: %s", secret.GetName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIssue596AllowDirectoriesThatAreSubstringsOfEachOther(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlays/aws-sandbox2.us-east-1")
|
||||||
|
th.WriteK("/app/base", "")
|
||||||
|
th.WriteK("/app/overlays/aws", `
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlays/aws-nonprod", `
|
||||||
|
resources:
|
||||||
|
- ../aws
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlays/aws-sandbox2.us-east-1", `
|
||||||
|
resources:
|
||||||
|
- ../aws-nonprod
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// To simplify tests, these vars specified in alphabetical order.
|
||||||
|
var someVars = []types.Var{
|
||||||
|
{
|
||||||
|
Name: "AWARD",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
APIVersion: "v7",
|
||||||
|
Gvk: resid.Gvk{Kind: "Service"},
|
||||||
|
Name: "nobelPrize"},
|
||||||
|
FieldRef: types.FieldSelector{FieldPath: "some.arbitrary.path"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "BIRD",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
APIVersion: "v300",
|
||||||
|
Gvk: resid.Gvk{Kind: "Service"},
|
||||||
|
Name: "heron"},
|
||||||
|
FieldRef: types.FieldSelector{FieldPath: "metadata.name"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "FRUIT",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Kind: "Service"},
|
||||||
|
Name: "apple"},
|
||||||
|
FieldRef: types.FieldSelector{FieldPath: "metadata.name"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "VEGETABLE",
|
||||||
|
ObjRef: types.Target{
|
||||||
|
Gvk: resid.Gvk{Kind: "Leafy"},
|
||||||
|
Name: "kale"},
|
||||||
|
FieldRef: types.FieldSelector{FieldPath: "metadata.name"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAllVarsSimple(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
vars:
|
||||||
|
- name: AWARD
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: nobelPrize
|
||||||
|
apiVersion: v7
|
||||||
|
fieldref:
|
||||||
|
fieldpath: some.arbitrary.path
|
||||||
|
- name: BIRD
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: heron
|
||||||
|
apiVersion: v300
|
||||||
|
`)
|
||||||
|
ra, err := th.MakeKustTarget().AccumulateTarget()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
vars := ra.Vars()
|
||||||
|
if len(vars) != 2 {
|
||||||
|
t.Fatalf("unexpected size %d", len(vars))
|
||||||
|
}
|
||||||
|
for i := range vars[:2] {
|
||||||
|
// By using Var.DeepEqual, we are protecting the code
|
||||||
|
// from a potential invocation of vars[i].ObjRef.GVK()
|
||||||
|
// during AccumulateTarget
|
||||||
|
if !vars[i].DeepEqual(someVars[i]) {
|
||||||
|
t.Fatalf("unexpected var[%d]:\n %v\n %v", i, vars[i], someVars[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAllVarsNested(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlays/o2")
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
vars:
|
||||||
|
- name: AWARD
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: nobelPrize
|
||||||
|
apiVersion: v7
|
||||||
|
fieldref:
|
||||||
|
fieldpath: some.arbitrary.path
|
||||||
|
- name: BIRD
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: heron
|
||||||
|
apiVersion: v300
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlays/o1", `
|
||||||
|
vars:
|
||||||
|
- name: FRUIT
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: apple
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlays/o2", `
|
||||||
|
vars:
|
||||||
|
- name: VEGETABLE
|
||||||
|
objref:
|
||||||
|
kind: Leafy
|
||||||
|
name: kale
|
||||||
|
resources:
|
||||||
|
- ../o1
|
||||||
|
`)
|
||||||
|
ra, err := th.MakeKustTarget().AccumulateTarget()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
vars := ra.Vars()
|
||||||
|
if len(vars) != 4 {
|
||||||
|
for i, v := range vars {
|
||||||
|
fmt.Printf("%v: %v\n", i, v)
|
||||||
|
}
|
||||||
|
t.Fatalf("expected 4 vars, got %d", len(vars))
|
||||||
|
}
|
||||||
|
for i := range vars {
|
||||||
|
// By using Var.DeepEqual, we are protecting the code
|
||||||
|
// from a potential invocation of vars[i].ObjRef.GVK()
|
||||||
|
// during AccumulateTarget
|
||||||
|
if !vars[i].DeepEqual(someVars[i]) {
|
||||||
|
t.Fatalf("unexpected var[%d]:\n %v\n %v", i, vars[i], someVars[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVarCollisionsForbidden(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlays/o2")
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
vars:
|
||||||
|
- name: AWARD
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: nobelPrize
|
||||||
|
apiVersion: v7
|
||||||
|
fieldref:
|
||||||
|
fieldpath: some.arbitrary.path
|
||||||
|
- name: BIRD
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: heron
|
||||||
|
apiVersion: v300
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlays/o1", `
|
||||||
|
vars:
|
||||||
|
- name: AWARD
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: academy
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlays/o2", `
|
||||||
|
vars:
|
||||||
|
- name: VEGETABLE
|
||||||
|
objref:
|
||||||
|
kind: Leafy
|
||||||
|
name: kale
|
||||||
|
resources:
|
||||||
|
- ../o1
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().AccumulateTarget()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected var collision")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(),
|
||||||
|
"var 'AWARD' already encountered") {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
477
api/internal/target/multiplepatch_test.go
Normal file
477
api/internal/target/multiplepatch_test.go
Normal file
@@ -0,0 +1,477 @@
|
|||||||
|
/*
|
||||||
|
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 target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeCommonFileForMultiplePatchTest(th *kusttest_test.KustTestHarness) {
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namePrefix: team-foo-
|
||||||
|
commonLabels:
|
||||||
|
app: mynginx
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
commonAnnotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
resources:
|
||||||
|
- deployment.yaml
|
||||||
|
- service.yaml
|
||||||
|
configMapGenerator:
|
||||||
|
- name: configmap-in-base
|
||||||
|
literals:
|
||||||
|
- foo=bar
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/deployment.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
mountPath: /tmp/ps
|
||||||
|
- name: sidecar
|
||||||
|
image: sidecar:latest
|
||||||
|
volumes:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
emptyDir: {}
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-base
|
||||||
|
name: configmap-in-base
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/base/service.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
labels:
|
||||||
|
app: nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: nginx
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlay/staging", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
namePrefix: staging-
|
||||||
|
commonLabels:
|
||||||
|
env: staging
|
||||||
|
patchesStrategicMerge:
|
||||||
|
- deployment-patch1.yaml
|
||||||
|
- deployment-patch2.yaml
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
configMapGenerator:
|
||||||
|
- name: configmap-in-overlay
|
||||||
|
literals:
|
||||||
|
- hello=world
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiplePatchesNoConflict(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay/staging")
|
||||||
|
makeCommonFileForMultiplePatchTest(th)
|
||||||
|
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
image: nginx:latest
|
||||||
|
env:
|
||||||
|
- name: ENVKEY
|
||||||
|
value: ENVVALUE
|
||||||
|
volumes:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
emptyDir: null
|
||||||
|
gcePersistentDisk:
|
||||||
|
pdName: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-overlay
|
||||||
|
name: configmap-in-overlay
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
env:
|
||||||
|
- name: ANOTHERENV
|
||||||
|
value: FOO
|
||||||
|
volumes:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: staging-team-foo-nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- name: ANOTHERENV
|
||||||
|
value: FOO
|
||||||
|
- name: ENVKEY
|
||||||
|
value: ENVVALUE
|
||||||
|
image: nginx:latest
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/ps
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- image: sidecar:latest
|
||||||
|
name: sidecar
|
||||||
|
volumes:
|
||||||
|
- gcePersistentDisk:
|
||||||
|
pdName: nginx-persistent-storage
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||||
|
name: configmap-in-overlay
|
||||||
|
- configMap:
|
||||||
|
name: staging-team-foo-configmap-in-base-g7k6gt2889
|
||||||
|
name: configmap-in-base
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: staging-team-foo-nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
foo: bar
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: staging-team-foo-configmap-in-base-g7k6gt2889
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
hello: world
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
env: staging
|
||||||
|
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiplePatchesWithConflict(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay/staging")
|
||||||
|
makeCommonFileForMultiplePatchTest(th)
|
||||||
|
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
env:
|
||||||
|
- name: ENABLE_FEATURE_FOO
|
||||||
|
value: TRUE
|
||||||
|
volumes:
|
||||||
|
- name: nginx-persistent-storage
|
||||||
|
emptyDir: null
|
||||||
|
gcePersistentDisk:
|
||||||
|
pdName: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: configmap-in-overlay
|
||||||
|
name: configmap-in-overlay
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
env:
|
||||||
|
- name: ENABLE_FEATURE_FOO
|
||||||
|
value: FALSE
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected conflict")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(), "conflict between ") {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiplePatchesWithOnePatchDeleteDirective(t *testing.T) {
|
||||||
|
additivePatch := `apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: nginx
|
||||||
|
env:
|
||||||
|
- name: SOME_NAME
|
||||||
|
value: somevalue
|
||||||
|
`
|
||||||
|
deletePatch := `apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- $patch: delete
|
||||||
|
name: sidecar
|
||||||
|
`
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
patch1 string
|
||||||
|
patch2 string
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Patch with delete directive first",
|
||||||
|
patch1: deletePatch,
|
||||||
|
patch2: additivePatch,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Patch with delete directive second",
|
||||||
|
patch1: additivePatch,
|
||||||
|
patch2: deletePatch,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay/staging")
|
||||||
|
|
||||||
|
makeCommonFileForMultiplePatchTest(th)
|
||||||
|
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", c.patch1)
|
||||||
|
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", c.patch2)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: staging-team-foo-nginx
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- name: SOME_NAME
|
||||||
|
value: somevalue
|
||||||
|
image: nginx
|
||||||
|
name: nginx
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /tmp/ps
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
volumes:
|
||||||
|
- emptyDir: {}
|
||||||
|
name: nginx-persistent-storage
|
||||||
|
- configMap:
|
||||||
|
name: staging-team-foo-configmap-in-base-g7k6gt2889
|
||||||
|
name: configmap-in-base
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: staging-team-foo-nginx
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
selector:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
foo: bar
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
note: This is a test annotation
|
||||||
|
labels:
|
||||||
|
app: mynginx
|
||||||
|
env: staging
|
||||||
|
org: example.com
|
||||||
|
team: foo
|
||||||
|
name: staging-team-foo-configmap-in-base-g7k6gt2889
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
hello: world
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
env: staging
|
||||||
|
name: staging-configmap-in-overlay-k7cbc75tg8
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiplePatchesBothWithPatchDeleteDirective(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay/staging")
|
||||||
|
makeCommonFileForMultiplePatchTest(th)
|
||||||
|
th.WriteF("/app/overlay/staging/deployment-patch1.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- $patch: delete
|
||||||
|
name: sidecar
|
||||||
|
`)
|
||||||
|
th.WriteF("/app/overlay/staging/deployment-patch2.yaml", `
|
||||||
|
apiVersion: apps/v1beta2
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- $patch: delete
|
||||||
|
name: nginx
|
||||||
|
`)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(
|
||||||
|
err.Error(), "both containing ") {
|
||||||
|
t.Fatalf("Unexpected err: %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
129
api/internal/target/namespacedgenerators_test.go
Normal file
129
api/internal/target/namespacedgenerators_test.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
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 target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNamespacedGenerator(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
th.WriteK("/app", `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
configMapGenerator:
|
||||||
|
- name: the-non-default-namespace-map
|
||||||
|
namespace: non-default
|
||||||
|
literals:
|
||||||
|
- altGreeting=Good Morning from non-default namespace!
|
||||||
|
- enableRisky="false"
|
||||||
|
- name: the-map
|
||||||
|
literals:
|
||||||
|
- altGreeting=Good Morning from default namespace!
|
||||||
|
- enableRisky="false"
|
||||||
|
|
||||||
|
secretGenerator:
|
||||||
|
- name: the-non-default-namespace-secret
|
||||||
|
namespace: non-default
|
||||||
|
literals:
|
||||||
|
- password.txt=verySecret
|
||||||
|
- name: the-secret
|
||||||
|
literals:
|
||||||
|
- password.txt=anotherSecret
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
altGreeting: Good Morning from non-default namespace!
|
||||||
|
enableRisky: "false"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: the-non-default-namespace-map-b6h49k7mt8
|
||||||
|
namespace: non-default
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
altGreeting: Good Morning from default namespace!
|
||||||
|
enableRisky: "false"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: the-map-4959m5tm6c
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
password.txt: dmVyeVNlY3JldA==
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: the-non-default-namespace-secret-h8d9hkgtb9
|
||||||
|
namespace: non-default
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
password.txt: YW5vdGhlclNlY3JldA==
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: the-secret-fgb45h45bh
|
||||||
|
type: Opaque
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamespacedGeneratorWithOverlays(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app/overlay")
|
||||||
|
th.WriteK("/app/base", `
|
||||||
|
namespace: base
|
||||||
|
|
||||||
|
configMapGenerator:
|
||||||
|
- name: testCase
|
||||||
|
literals:
|
||||||
|
- base=true
|
||||||
|
`)
|
||||||
|
th.WriteK("/app/overlay", `
|
||||||
|
resources:
|
||||||
|
- ../base
|
||||||
|
|
||||||
|
namespace: overlay
|
||||||
|
|
||||||
|
configMapGenerator:
|
||||||
|
- name: testCase
|
||||||
|
behavior: merge
|
||||||
|
literals:
|
||||||
|
- overlay=true
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
base: "true"
|
||||||
|
overlay: "true"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
annotations: {}
|
||||||
|
labels: {}
|
||||||
|
name: testCase-4g75kbk6gm
|
||||||
|
namespace: overlay
|
||||||
|
`)
|
||||||
|
}
|
||||||
602
api/internal/target/namespaces_test.go
Normal file
602
api/internal/target/namespaces_test.go
Normal file
@@ -0,0 +1,602 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package target_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/testutils/kusttest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNamespacedSecrets(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/app")
|
||||||
|
|
||||||
|
th.WriteF("/app/secrets.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: dummy
|
||||||
|
namespace: default
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
dummy: ""
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: dummy
|
||||||
|
namespace: kube-system
|
||||||
|
type: Opaque
|
||||||
|
data:
|
||||||
|
dummy: ""
|
||||||
|
`)
|
||||||
|
|
||||||
|
// This should find the proper secret.
|
||||||
|
th.WriteF("/app/role.yaml", `
|
||||||
|
kind: ClusterRole
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: dummy
|
||||||
|
rules:
|
||||||
|
- apiGroups: [""]
|
||||||
|
resources: ["secrets"]
|
||||||
|
resourceNames: ["dummy"]
|
||||||
|
verbs: ["get"]
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteK("/app", `
|
||||||
|
resources:
|
||||||
|
- secrets.yaml
|
||||||
|
- role.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
// This validates Fix #1444. This should not be an error anymore -
|
||||||
|
// the secrets have the same name but are in different namespaces.
|
||||||
|
// The ClusterRole (by def) is not in a namespace,
|
||||||
|
// an in this case applies to *any* Secret resource
|
||||||
|
// named "dummy"
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
dummy: ""
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: dummy
|
||||||
|
namespace: default
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
dummy: ""
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: dummy
|
||||||
|
namespace: kube-system
|
||||||
|
type: Opaque
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: dummy
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resourceNames:
|
||||||
|
- dummy
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestNameAndNsTransformation validates that NamespaceTransformer,
|
||||||
|
// PrefixSuffixTransformer and namereference transformers are
|
||||||
|
// able to deal with simultaneous change of namespace and name.
|
||||||
|
func TestNameAndNsTransformation(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/nameandns")
|
||||||
|
|
||||||
|
th.WriteK("/nameandns", `
|
||||||
|
namePrefix: p1-
|
||||||
|
nameSuffix: -s1
|
||||||
|
namespace: newnamespace
|
||||||
|
resources:
|
||||||
|
- resources.yaml
|
||||||
|
`)
|
||||||
|
|
||||||
|
th.WriteF("/nameandns/resources.yaml", `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cm1
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: cm2
|
||||||
|
namespace: ns1
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: svc1
|
||||||
|
namespace: ns1
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: svc2
|
||||||
|
namespace: ns1
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: sa1
|
||||||
|
namespace: ns1
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: sa2
|
||||||
|
namespace: ns1
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: manager-rolebinding
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: sa1
|
||||||
|
namespace: ns1
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: sa2
|
||||||
|
namespace: ns1
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: sa3
|
||||||
|
namespace: random
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: default
|
||||||
|
namespace: irrelevant
|
||||||
|
---
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||||
|
kind: ValidatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
name: example
|
||||||
|
webhooks:
|
||||||
|
- name: example1
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: svc1
|
||||||
|
namespace: ns1
|
||||||
|
- name: example2
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: svc2
|
||||||
|
namespace: ns1
|
||||||
|
- name: example3
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: svc3
|
||||||
|
namespace: random
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: crds.my.org
|
||||||
|
---
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: cr1
|
||||||
|
---
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: crb1
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: default
|
||||||
|
namespace: irrelevant
|
||||||
|
---
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: pv1
|
||||||
|
`)
|
||||||
|
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: p1-cm1-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: p1-cm2-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: p1-svc1-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: p1-svc2-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: p1-sa1-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: p1-sa2-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: p1-manager-rolebinding-s1
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: p1-sa1-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: p1-sa2-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: sa3
|
||||||
|
namespace: random
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: default
|
||||||
|
namespace: newnamespace
|
||||||
|
---
|
||||||
|
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||||
|
kind: ValidatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
name: p1-example-s1
|
||||||
|
webhooks:
|
||||||
|
- clientConfig:
|
||||||
|
service:
|
||||||
|
name: p1-svc1-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
name: example1
|
||||||
|
- clientConfig:
|
||||||
|
service:
|
||||||
|
name: p1-svc2-s1
|
||||||
|
namespace: newnamespace
|
||||||
|
name: example2
|
||||||
|
- clientConfig:
|
||||||
|
service:
|
||||||
|
name: svc3
|
||||||
|
namespace: random
|
||||||
|
name: example3
|
||||||
|
---
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: crds.my.org
|
||||||
|
---
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: p1-cr1-s1
|
||||||
|
---
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: p1-crb1-s1
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: default
|
||||||
|
namespace: newnamespace
|
||||||
|
---
|
||||||
|
kind: PersistentVolume
|
||||||
|
metadata:
|
||||||
|
name: p1-pv1-s1
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This serie of constants is used to prove the need of
|
||||||
|
// the namespace field in the objref field of the var declaration.
|
||||||
|
// The following tests demonstrate that it creates umbiguous variable
|
||||||
|
// declaration if two entities of the kind with the same name
|
||||||
|
// but in different namespaces are declared.
|
||||||
|
// This is tracking the following issue:
|
||||||
|
// https://github.com/kubernetes-sigs/kustomize/issues/1298
|
||||||
|
const namespaceNeedInVarMyApp string = `
|
||||||
|
resources:
|
||||||
|
- elasticsearch-dev-service.yaml
|
||||||
|
- elasticsearch-test-service.yaml
|
||||||
|
vars:
|
||||||
|
- name: elasticsearch-test-service-name
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: metadata.name
|
||||||
|
- name: elasticsearch-test-protocol
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: spec.ports[0].protocol
|
||||||
|
- name: elasticsearch-dev-service-name
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: metadata.name
|
||||||
|
- name: elasticsearch-dev-protocol
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: spec.ports[0].protocol
|
||||||
|
`
|
||||||
|
|
||||||
|
const namespaceNeedInVarDevResources string = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: dev
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: elasticsearch
|
||||||
|
env:
|
||||||
|
- name: DISCOVERY_SERVICE
|
||||||
|
value: "$(elasticsearch-dev-service-name).monitoring.svc.cluster.local"
|
||||||
|
- name: DISCOVERY_PROTOCOL
|
||||||
|
value: "$(elasticsearch-dev-protocol)"
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: dev
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: transport
|
||||||
|
port: 9300
|
||||||
|
protocol: TCP
|
||||||
|
clusterIP: None
|
||||||
|
`
|
||||||
|
|
||||||
|
const namespaceNeedInVarTestResources string = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: test
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: elasticsearch
|
||||||
|
env:
|
||||||
|
- name: DISCOVERY_SERVICE
|
||||||
|
value: "$(elasticsearch-test-service-name).monitoring.svc.cluster.local"
|
||||||
|
- name: DISCOVERY_PROTOCOL
|
||||||
|
value: "$(elasticsearch-test-protocol)"
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: test
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: transport
|
||||||
|
port: 9300
|
||||||
|
protocol: UDP
|
||||||
|
clusterIP: None
|
||||||
|
`
|
||||||
|
|
||||||
|
const namespaceNeedInVarExpectedOutput string = `
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: dev
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- name: DISCOVERY_SERVICE
|
||||||
|
value: elasticsearch.monitoring.svc.cluster.local
|
||||||
|
- name: DISCOVERY_PROTOCOL
|
||||||
|
value: TCP
|
||||||
|
name: elasticsearch
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: dev
|
||||||
|
spec:
|
||||||
|
clusterIP: None
|
||||||
|
ports:
|
||||||
|
- name: transport
|
||||||
|
port: 9300
|
||||||
|
protocol: TCP
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: StatefulSet
|
||||||
|
metadata:
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: test
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- env:
|
||||||
|
- name: DISCOVERY_SERVICE
|
||||||
|
value: elasticsearch.monitoring.svc.cluster.local
|
||||||
|
- name: DISCOVERY_PROTOCOL
|
||||||
|
value: UDP
|
||||||
|
name: elasticsearch
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: test
|
||||||
|
spec:
|
||||||
|
clusterIP: None
|
||||||
|
ports:
|
||||||
|
- name: transport
|
||||||
|
port: 9300
|
||||||
|
protocol: UDP
|
||||||
|
`
|
||||||
|
|
||||||
|
// TestVariablesAmbiguous demonstrates how two variables pointing at two different resources
|
||||||
|
// using the same name in different namespaces are treated as ambiguous if the namespace is
|
||||||
|
// not specified
|
||||||
|
func TestVariablesAmbiguous(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/namespaceNeedInVar/myapp")
|
||||||
|
th.WriteK("/namespaceNeedInVar/myapp", namespaceNeedInVarMyApp)
|
||||||
|
th.WriteF("/namespaceNeedInVar/myapp/elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources)
|
||||||
|
th.WriteF("/namespaceNeedInVar/myapp/elasticsearch-test-service.yaml", namespaceNeedInVarTestResources)
|
||||||
|
_, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "unable to disambiguate") {
|
||||||
|
t.Fatalf("unexpected error %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const namespaceNeedInVarDevFolder string = `
|
||||||
|
resources:
|
||||||
|
- elasticsearch-dev-service.yaml
|
||||||
|
vars:
|
||||||
|
- name: elasticsearch-dev-service-name
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: metadata.name
|
||||||
|
- name: elasticsearch-dev-protocol
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: spec.ports[0].protocol
|
||||||
|
`
|
||||||
|
|
||||||
|
const namespaceNeedInVarTestFolder string = `
|
||||||
|
resources:
|
||||||
|
- elasticsearch-test-service.yaml
|
||||||
|
vars:
|
||||||
|
- name: elasticsearch-test-service-name
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: metadata.name
|
||||||
|
- name: elasticsearch-test-protocol
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: spec.ports[0].protocol
|
||||||
|
`
|
||||||
|
|
||||||
|
// TestVariablesAmbiguousWorkaround demonstrates a possible workaround
|
||||||
|
// to TestVariablesAmbiguous problem. It requires to separate the variables
|
||||||
|
// and resources into multiple kustomization context/folders instead of one.
|
||||||
|
func TestVariablesAmbiguousWorkaround(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/namespaceNeedInVar/workaround")
|
||||||
|
th.WriteK("/namespaceNeedInVar/dev", namespaceNeedInVarDevFolder)
|
||||||
|
th.WriteF("/namespaceNeedInVar/dev/elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources)
|
||||||
|
th.WriteK("/namespaceNeedInVar/test", namespaceNeedInVarTestFolder)
|
||||||
|
th.WriteF("/namespaceNeedInVar/test/elasticsearch-test-service.yaml", namespaceNeedInVarTestResources)
|
||||||
|
th.WriteK("/namespaceNeedInVar/workaround", `
|
||||||
|
resources:
|
||||||
|
- ../dev
|
||||||
|
- ../test
|
||||||
|
`)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, namespaceNeedInVarExpectedOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
const namespaceNeedInVarMyAppWithNamespace string = `
|
||||||
|
resources:
|
||||||
|
- elasticsearch-dev-service.yaml
|
||||||
|
- elasticsearch-test-service.yaml
|
||||||
|
vars:
|
||||||
|
- name: elasticsearch-test-service-name
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: test
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: metadata.name
|
||||||
|
- name: elasticsearch-test-protocol
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: test
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: spec.ports[0].protocol
|
||||||
|
- name: elasticsearch-dev-service-name
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: dev
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: metadata.name
|
||||||
|
- name: elasticsearch-dev-protocol
|
||||||
|
objref:
|
||||||
|
kind: Service
|
||||||
|
name: elasticsearch
|
||||||
|
namespace: dev
|
||||||
|
apiVersion: v1
|
||||||
|
fieldref:
|
||||||
|
fieldpath: spec.ports[0].protocol
|
||||||
|
`
|
||||||
|
|
||||||
|
// TestVariablesDisambiguatedWithNamespace demonstrates that adding the namespace
|
||||||
|
// to the variable declarations allows to disambiguate the variables.
|
||||||
|
func TestVariablesDisambiguatedWithNamespace(t *testing.T) {
|
||||||
|
th := kusttest_test.NewKustTestHarness(t, "/namespaceNeedInVar/myapp")
|
||||||
|
th.WriteK("/namespaceNeedInVar/myapp", namespaceNeedInVarMyAppWithNamespace)
|
||||||
|
th.WriteF("/namespaceNeedInVar/myapp/elasticsearch-dev-service.yaml", namespaceNeedInVarDevResources)
|
||||||
|
th.WriteF("/namespaceNeedInVar/myapp/elasticsearch-test-service.yaml", namespaceNeedInVarTestResources)
|
||||||
|
m, err := th.MakeKustTarget().MakeCustomizedResMap()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Err: %v", err)
|
||||||
|
}
|
||||||
|
th.AssertActualEqualsExpected(m, namespaceNeedInVarExpectedOutput)
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user