mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 10:30:59 +00:00
Compare commits
1164 Commits
api/v0.8.6
...
api/v0.13.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f851cc7181 | ||
|
|
7e3ad53890 | ||
|
|
5cdc080406 | ||
|
|
10fb04cdf2 | ||
|
|
ea8fc77f2f | ||
|
|
e9507c940d | ||
|
|
bea105d793 | ||
|
|
e4d7eaa479 | ||
|
|
85949329b8 | ||
|
|
0c34cf4192 | ||
|
|
95edcc0681 | ||
|
|
9d8ed39d3d | ||
|
|
1957d5c746 | ||
|
|
236166097e | ||
|
|
3370177b9d | ||
|
|
c8b112c79f | ||
|
|
d91e31cf18 | ||
|
|
00b0bd8473 | ||
|
|
e2aff13587 | ||
|
|
361154dabc | ||
|
|
7db330d2cc | ||
|
|
e9f47abcc1 | ||
|
|
58defe31e7 | ||
|
|
ee4fb17170 | ||
|
|
142360b0ed | ||
|
|
f7c3fce6a5 | ||
|
|
af3e8ee353 | ||
|
|
90281a62e0 | ||
|
|
fb294921f5 | ||
|
|
af9a13111b | ||
|
|
279ad3e815 | ||
|
|
96351a9278 | ||
|
|
8b21844b10 | ||
|
|
d315600e5e | ||
|
|
8f75682b9c | ||
|
|
659c0ee156 | ||
|
|
298b87a223 | ||
|
|
a69092c4b7 | ||
|
|
0a1aa7c2cb | ||
|
|
89d3ca1757 | ||
|
|
d858ed82fa | ||
|
|
f47f897440 | ||
|
|
ace8132edc | ||
|
|
2405da65d2 | ||
|
|
7e000aa820 | ||
|
|
dbc11a43a8 | ||
|
|
def01f0af8 | ||
|
|
ec4d1e802b | ||
|
|
7899b80a00 | ||
|
|
00208394d6 | ||
|
|
db7b057956 | ||
|
|
aaf9d39975 | ||
|
|
8ab4b8d167 | ||
|
|
2b7eb6d92a | ||
|
|
2b38c12c83 | ||
|
|
18a60ef036 | ||
|
|
d70315fed6 | ||
|
|
dc34520ac1 | ||
|
|
da5d572df2 | ||
|
|
3134e9b0c2 | ||
|
|
c2885642d6 | ||
|
|
a885ee12c6 | ||
|
|
1a201ab913 | ||
|
|
240282fc6f | ||
|
|
445622147f | ||
|
|
ddf14ea688 | ||
|
|
64d2366e87 | ||
|
|
06999462e7 | ||
|
|
2591303430 | ||
|
|
150134758f | ||
|
|
43d96ba91b | ||
|
|
4821259f01 | ||
|
|
7e6cc7aafb | ||
|
|
dc05fa153f | ||
|
|
fbb94584dc | ||
|
|
772fafa892 | ||
|
|
32f31c34ba | ||
|
|
9bc75c16d9 | ||
|
|
5be179305a | ||
|
|
cb6553430d | ||
|
|
c6ca3ff799 | ||
|
|
6489e35b7c | ||
|
|
1308d905fa | ||
|
|
6c79645fb0 | ||
|
|
b7746d09e4 | ||
|
|
06da3b96a2 | ||
|
|
6949c83840 | ||
|
|
368697f8ef | ||
|
|
3022dd2931 | ||
|
|
aec35009ed | ||
|
|
de6162625f | ||
|
|
a0e94c1642 | ||
|
|
20b0d3c7ce | ||
|
|
194a017c81 | ||
|
|
a1bfab382a | ||
|
|
ef60d5f9bb | ||
|
|
5fed0f76c8 | ||
|
|
e3981daf0e | ||
|
|
faddb49c89 | ||
|
|
4cccb838ce | ||
|
|
292d18983d | ||
|
|
2d854c46dd | ||
|
|
55edae1648 | ||
|
|
d5e1940fc2 | ||
|
|
6cf7014b2c | ||
|
|
8a6602db94 | ||
|
|
24bf1d07e7 | ||
|
|
487703ee8f | ||
|
|
2db573b6a0 | ||
|
|
8a869f6045 | ||
|
|
c16957fc8f | ||
|
|
03c1534c38 | ||
|
|
903fbb6ed2 | ||
|
|
2ed910abb0 | ||
|
|
570865baae | ||
|
|
71eb865cea | ||
|
|
a502717460 | ||
|
|
1e2e7bbc0b | ||
|
|
e34d5b5608 | ||
|
|
0f4f978956 | ||
|
|
663b777910 | ||
|
|
3c8db55f53 | ||
|
|
b8611cf0a9 | ||
|
|
588da7e392 | ||
|
|
baf777c56e | ||
|
|
a612cd2b7e | ||
|
|
37ad9b1d4e | ||
|
|
fb0f567b63 | ||
|
|
f53d018302 | ||
|
|
d9d9785791 | ||
|
|
76fab6e7ba | ||
|
|
832b552076 | ||
|
|
0eff094faf | ||
|
|
1b7db20504 | ||
|
|
adce67301b | ||
|
|
a6eb8e8ce8 | ||
|
|
7f9f6140ff | ||
|
|
07063a2652 | ||
|
|
e5ab220821 | ||
|
|
0c4f863bc5 | ||
|
|
78978723db | ||
|
|
8ea5eb22f1 | ||
|
|
91a2c2b1a4 | ||
|
|
62f0dab2ac | ||
|
|
d642b959cf | ||
|
|
d49f6e17ad | ||
|
|
0de4e0ba11 | ||
|
|
54569e4591 | ||
|
|
b2dc88447e | ||
|
|
314b8fbefa | ||
|
|
c0840b6804 | ||
|
|
1eccbc4e30 | ||
|
|
f79e16b352 | ||
|
|
b20e611413 | ||
|
|
e638e408b0 | ||
|
|
473c0a5f19 | ||
|
|
84bd402cc0 | ||
|
|
e724e25fec | ||
|
|
50e731a1d9 | ||
|
|
beb2825f82 | ||
|
|
6d9b54004e | ||
|
|
1f04fd2f23 | ||
|
|
6ffd22a1f1 | ||
|
|
3e6ede9645 | ||
|
|
14f3cea24f | ||
|
|
f0f414d53a | ||
|
|
f9d553689e | ||
|
|
69435e059a | ||
|
|
e16b0f0dfc | ||
|
|
997e6fcc63 | ||
|
|
daf81df437 | ||
|
|
3e447da6ef | ||
|
|
ea21b37d67 | ||
|
|
d29febecb7 | ||
|
|
1b1e6ccab0 | ||
|
|
7ee6dd551d | ||
|
|
ccb68aa881 | ||
|
|
1252e45576 | ||
|
|
19d163ca0f | ||
|
|
bf7a0f9004 | ||
|
|
3b1a711cee | ||
|
|
cd35e95560 | ||
|
|
0a16b6cf40 | ||
|
|
7b84613ad1 | ||
|
|
d14dfb604c | ||
|
|
acba8fff62 | ||
|
|
38da7ca15c | ||
|
|
9ef7ba9c95 | ||
|
|
4e7f4bce7b | ||
|
|
f8f444d92a | ||
|
|
c2312c4018 | ||
|
|
0fbaa2d0e8 | ||
|
|
ae20497fd3 | ||
|
|
506dc4f9fe | ||
|
|
482e8930fc | ||
|
|
d37fabf876 | ||
|
|
eb1529b516 | ||
|
|
c750c0089d | ||
|
|
2b9ef61f88 | ||
|
|
344b257c1f | ||
|
|
e2196d9bd1 | ||
|
|
65aeefd281 | ||
|
|
6ce230f2b2 | ||
|
|
401cf9579c | ||
|
|
4dcc040ec1 | ||
|
|
662ccf1915 | ||
|
|
e62480d11c | ||
|
|
88cf251ef7 | ||
|
|
d6e40a3f6c | ||
|
|
eb51117adf | ||
|
|
0d68e0c7be | ||
|
|
7c2e8845ad | ||
|
|
ce18530656 | ||
|
|
ca71736140 | ||
|
|
e7d5f665b8 | ||
|
|
6c587c9542 | ||
|
|
56a47bd901 | ||
|
|
a8c0be49ae | ||
|
|
e49bf52928 | ||
|
|
a54226e0a9 | ||
|
|
f086269d6e | ||
|
|
e2e9181bed | ||
|
|
21ee7f7125 | ||
|
|
cbb61fc668 | ||
|
|
23de1499c2 | ||
|
|
cb2636335f | ||
|
|
c266537b27 | ||
|
|
f80cf9f433 | ||
|
|
032bf3338e | ||
|
|
b4d25b1b26 | ||
|
|
d917876949 | ||
|
|
14b7282ed8 | ||
|
|
8868d91670 | ||
|
|
b6fae2a959 | ||
|
|
a8388f473b | ||
|
|
08918ea352 | ||
|
|
5dcbd35e40 | ||
|
|
2e2674fa24 | ||
|
|
170eca7c52 | ||
|
|
bf17fe1d8f | ||
|
|
a8010ac469 | ||
|
|
f66936f6bf | ||
|
|
eb54c97947 | ||
|
|
b84885d5c4 | ||
|
|
298d977ee7 | ||
|
|
d5a2009d3f | ||
|
|
d0ae8fba13 | ||
|
|
f8d1e778c7 | ||
|
|
1948af8190 | ||
|
|
79a9154cf8 | ||
|
|
26fcafdb57 | ||
|
|
2ec4b971e9 | ||
|
|
1d44793d79 | ||
|
|
55a37de686 | ||
|
|
f6b72077c8 | ||
|
|
416eed97c4 | ||
|
|
5762794793 | ||
|
|
91e002a560 | ||
|
|
15545cc228 | ||
|
|
3f0c21304c | ||
|
|
bb7ebe029c | ||
|
|
64f42ea45f | ||
|
|
1b0fe2a078 | ||
|
|
56d82a8378 | ||
|
|
39dbdddb86 | ||
|
|
27a1de1a19 | ||
|
|
7229f5fbed | ||
|
|
ba9d5ee16f | ||
|
|
001b0c5fe5 | ||
|
|
bbeff6ddd6 | ||
|
|
e57b5d283f | ||
|
|
dbd719bd3f | ||
|
|
29ca6935bd | ||
|
|
f5c70b3929 | ||
|
|
b816903c93 | ||
|
|
d32fe662af | ||
|
|
4b5fbf2b80 | ||
|
|
283f8ae0a5 | ||
|
|
f6a1e4af3b | ||
|
|
e7fe132df8 | ||
|
|
631f623d9c | ||
|
|
19c467a924 | ||
|
|
afa2b52807 | ||
|
|
30843e2dac | ||
|
|
2a3b645758 | ||
|
|
b89cb92c2d | ||
|
|
9a6aad3ae4 | ||
|
|
c4b4a41913 | ||
|
|
0c37ee89af | ||
|
|
6917214251 | ||
|
|
a3a3140e4c | ||
|
|
d244c8a8bb | ||
|
|
d96e47cc38 | ||
|
|
2f2ba40876 | ||
|
|
ab09d27ec7 | ||
|
|
5e95c6ab06 | ||
|
|
0c6e827ab8 | ||
|
|
17cbd96667 | ||
|
|
0fce7d53a9 | ||
|
|
68780b4c0c | ||
|
|
04e1663b70 | ||
|
|
ba77fd5b7f | ||
|
|
77814ac12b | ||
|
|
e1b6aa393a | ||
|
|
3229c810cc | ||
|
|
344d0eec83 | ||
|
|
387c95be1f | ||
|
|
c02d0e502a | ||
|
|
5806c6f4f7 | ||
|
|
396b693e1c | ||
|
|
f95ea9e6a0 | ||
|
|
8c4d35470a | ||
|
|
6fb96b7d53 | ||
|
|
ca01b49614 | ||
|
|
4653f5d9d3 | ||
|
|
bd67fc2781 | ||
|
|
17af5d518f | ||
|
|
670cef4fd1 | ||
|
|
5c9adf248c | ||
|
|
fd847108cd | ||
|
|
d2ceab5f30 | ||
|
|
86a84ca486 | ||
|
|
876ec8fa6b | ||
|
|
2145d1984f | ||
|
|
92288a8a02 | ||
|
|
a6449aa03f | ||
|
|
9c827f5362 | ||
|
|
e201dbb31a | ||
|
|
bc981fcbc3 | ||
|
|
8a729459aa | ||
|
|
d1102fb726 | ||
|
|
edb6294741 | ||
|
|
175c99f9f6 | ||
|
|
20a93fdd12 | ||
|
|
0421c9fb50 | ||
|
|
8c8a2125b7 | ||
|
|
f39f914a98 | ||
|
|
c583f4164c | ||
|
|
6a7cecddd7 | ||
|
|
d8efc15169 | ||
|
|
86d48b2a95 | ||
|
|
7e0fd02783 | ||
|
|
1c53932166 | ||
|
|
721a905e13 | ||
|
|
3fdf88d10e | ||
|
|
4b19643b36 | ||
|
|
b0b6c962dc | ||
|
|
78191b45fb | ||
|
|
37a403fc65 | ||
|
|
fd7e170b69 | ||
|
|
afc1815f3f | ||
|
|
daa3e5e2c2 | ||
|
|
12adc1ff95 | ||
|
|
41283c4727 | ||
|
|
30740f87f1 | ||
|
|
6c8db65a90 | ||
|
|
35b5b7554f | ||
|
|
6395344bcb | ||
|
|
4a3bb926c5 | ||
|
|
ba5335cf48 | ||
|
|
03ac2e1ada | ||
|
|
2e036c24c2 | ||
|
|
1cf5b00af8 | ||
|
|
9097f7b5a6 | ||
|
|
0c8174544f | ||
|
|
a37572d193 | ||
|
|
ba7315ca76 | ||
|
|
ddf768195c | ||
|
|
b11cc3ae67 | ||
|
|
22668eae16 | ||
|
|
02eb788b78 | ||
|
|
41fb6915b3 | ||
|
|
596519d3f2 | ||
|
|
d3d92157fa | ||
|
|
c83ebd9530 | ||
|
|
c3b5d8fa19 | ||
|
|
7a773a3a48 | ||
|
|
46d4934b68 | ||
|
|
d0ef2f70a1 | ||
|
|
b8d2ff2afa | ||
|
|
7e0158e1e9 | ||
|
|
5948f6aa63 | ||
|
|
496a962a53 | ||
|
|
17b42a99f5 | ||
|
|
271f393218 | ||
|
|
c0dc68d6e7 | ||
|
|
188e35fbfd | ||
|
|
233b6adf7e | ||
|
|
41296b9814 | ||
|
|
66e4e1582b | ||
|
|
2e230b4d4b | ||
|
|
bcae65770a | ||
|
|
d49e5aa5aa | ||
|
|
94af647556 | ||
|
|
9d5491c2e2 | ||
|
|
cf89eae804 | ||
|
|
9452a031ba | ||
|
|
a464ed0c59 | ||
|
|
ec212711d4 | ||
|
|
2f2e14e953 | ||
|
|
ed72bb02d4 | ||
|
|
02e0b38bb0 | ||
|
|
01ab069bd2 | ||
|
|
cdc4a5083b | ||
|
|
5e84de2a89 | ||
|
|
e5041bae6f | ||
|
|
0d600af35a | ||
|
|
0989b26098 | ||
|
|
7888aef305 | ||
|
|
fe604fd3d1 | ||
|
|
4fc02497ae | ||
|
|
e96c38e3ab | ||
|
|
29104d6fa9 | ||
|
|
df57e196d8 | ||
|
|
f3c825f550 | ||
|
|
03c94eabb7 | ||
|
|
c071cdaedd | ||
|
|
f68a0c50d9 | ||
|
|
db34e923c5 | ||
|
|
df80b29694 | ||
|
|
ea193328e3 | ||
|
|
2d4bce5112 | ||
|
|
af06ae6b69 | ||
|
|
46875b6ac4 | ||
|
|
0fa010c7e7 | ||
|
|
3e652d6fea | ||
|
|
f54014ce3b | ||
|
|
08924dc2f5 | ||
|
|
49c0ed1326 | ||
|
|
7440f974b8 | ||
|
|
ab9b89213b | ||
|
|
9fc012cc4e | ||
|
|
877d72b10b | ||
|
|
c743f13d0d | ||
|
|
0d32543ebd | ||
|
|
28ee975948 | ||
|
|
2a9adbeb1e | ||
|
|
672c751715 | ||
|
|
d3a7b9008b | ||
|
|
b0d2e4bdcd | ||
|
|
71bf0d5d14 | ||
|
|
0a9c5cb0cf | ||
|
|
ff4136b1a2 | ||
|
|
1567b96ed3 | ||
|
|
14947e449b | ||
|
|
b368b347d1 | ||
|
|
5c359bda28 | ||
|
|
c94b5d8f25 | ||
|
|
b699204a9e | ||
|
|
cf3a452ddd | ||
|
|
faad014f96 | ||
|
|
8e46ef57be | ||
|
|
458d48430a | ||
|
|
3cecdc6214 | ||
|
|
7cac778866 | ||
|
|
0aa379c228 | ||
|
|
cd0f9b0c5b | ||
|
|
651f0c1097 | ||
|
|
6348ea9515 | ||
|
|
4b64f1e0e8 | ||
|
|
7306402cca | ||
|
|
485f7d44d9 | ||
|
|
9e57ab72fc | ||
|
|
d17ef91d94 | ||
|
|
e97cecf9dc | ||
|
|
5833f4ca80 | ||
|
|
e86c479690 | ||
|
|
d082c75b65 | ||
|
|
d2e59002ae | ||
|
|
c857ff8371 | ||
|
|
866e84059f | ||
|
|
497d2ee031 | ||
|
|
c4febc59d5 | ||
|
|
19955e73ff | ||
|
|
271bf31ce9 | ||
|
|
809182c6b6 | ||
|
|
0dd3d84104 | ||
|
|
de6b978491 | ||
|
|
8fa8e14eeb | ||
|
|
26b5e628cf | ||
|
|
80853c61b8 | ||
|
|
c5ff592810 | ||
|
|
b9df5686f4 | ||
|
|
7ff87ef5b9 | ||
|
|
c4d38108cf | ||
|
|
784ae5efa3 | ||
|
|
e8640724a9 | ||
|
|
7b0ec99d90 | ||
|
|
fbfcb0479a | ||
|
|
3421fcf81e | ||
|
|
97de780feb | ||
|
|
3490fb8716 | ||
|
|
2fe04496c2 | ||
|
|
b0d7721049 | ||
|
|
18f22f10a3 | ||
|
|
3ae5aa9e13 | ||
|
|
48f21e920a | ||
|
|
dcdefca70e | ||
|
|
c64351aa01 | ||
|
|
c60979ee3e | ||
|
|
cb80659c22 | ||
|
|
b1086ac49b | ||
|
|
1fcd66258f | ||
|
|
d1d578c392 | ||
|
|
f67dd5bbbd | ||
|
|
ac5c51ba2c | ||
|
|
53cc76fe43 | ||
|
|
07eb595eb2 | ||
|
|
8cb7acfdcb | ||
|
|
975482390f | ||
|
|
701695c343 | ||
|
|
8db1267983 | ||
|
|
da7ec577b2 | ||
|
|
d17b171207 | ||
|
|
70ce89d993 | ||
|
|
e25db3df2e | ||
|
|
3eae520532 | ||
|
|
3ec7b10bc0 | ||
|
|
6dabba1d23 | ||
|
|
bd05631887 | ||
|
|
6950a0d246 | ||
|
|
c90504a19d | ||
|
|
8dab94964f | ||
|
|
ff40460d3b | ||
|
|
a7f4db7fb4 | ||
|
|
5d0762411c | ||
|
|
b4e116346e | ||
|
|
9091919699 | ||
|
|
702a56e2f1 | ||
|
|
00a9c59dd7 | ||
|
|
4dfc2a9507 | ||
|
|
2f17803c0a | ||
|
|
5ed96a34d7 | ||
|
|
14cb815b5d | ||
|
|
a5df6f7fd9 | ||
|
|
4f5dfb5d42 | ||
|
|
6374d3d593 | ||
|
|
22f9daa3ab | ||
|
|
c3c7013f09 | ||
|
|
3277ff9dbf | ||
|
|
746bd18a8c | ||
|
|
d7763045ea | ||
|
|
30612cfd17 | ||
|
|
3006846d67 | ||
|
|
2f9617ff56 | ||
|
|
28b3e9fb9c | ||
|
|
6a7caf4192 | ||
|
|
32e13bdf66 | ||
|
|
fcfd30ca18 | ||
|
|
a86723c3a3 | ||
|
|
01d7fae382 | ||
|
|
39349c4fbc | ||
|
|
836a721a47 | ||
|
|
78e8d4318a | ||
|
|
67591762a6 | ||
|
|
f38648df34 | ||
|
|
d46bb0a453 | ||
|
|
939545a007 | ||
|
|
fb768687ea | ||
|
|
7bc51153d0 | ||
|
|
2b3d9dd67b | ||
|
|
98fce99f96 | ||
|
|
3c1e695f3f | ||
|
|
91ff4eefb2 | ||
|
|
f0c0d931fe | ||
|
|
f5b2b751eb | ||
|
|
cfd9278fd8 | ||
|
|
b79d77a8a7 | ||
|
|
91f65b3441 | ||
|
|
ba55d95542 | ||
|
|
851b3fc28c | ||
|
|
90493ec374 | ||
|
|
51b767b06e | ||
|
|
e3160373f0 | ||
|
|
a5b61016bb | ||
|
|
d62cc6d6a2 | ||
|
|
bcebad1664 | ||
|
|
9abf5fca3c | ||
|
|
26b9af0379 | ||
|
|
c65ef489ca | ||
|
|
c754eadabc | ||
|
|
816e2365bf | ||
|
|
302cc866ad | ||
|
|
bf97d23a00 | ||
|
|
3687250ca2 | ||
|
|
69e5228264 | ||
|
|
7af5ce56bd | ||
|
|
d711b275f0 | ||
|
|
9577d61167 | ||
|
|
2c23b960ff | ||
|
|
6e5d2674ce | ||
|
|
2554d690c8 | ||
|
|
cec9298b2d | ||
|
|
54ae9ba9fc | ||
|
|
b73dfe7f35 | ||
|
|
6e82b210a9 | ||
|
|
b0636459dd | ||
|
|
13e26004fd | ||
|
|
a1a0a49822 | ||
|
|
ff2a3d368b | ||
|
|
1c5ce6975f | ||
|
|
fe99674fcd | ||
|
|
7674c220b1 | ||
|
|
469ae33b50 | ||
|
|
fbd949a95d | ||
|
|
c996d1fcab | ||
|
|
41917ca588 | ||
|
|
37668d87c4 | ||
|
|
92197fda9e | ||
|
|
ff7b2f20d5 | ||
|
|
59c8268245 | ||
|
|
4f21d60045 | ||
|
|
77b1af2d91 | ||
|
|
bb0a520f40 | ||
|
|
987437857f | ||
|
|
1843b6f25f | ||
|
|
31b7cf9e0a | ||
|
|
964bb38ba2 | ||
|
|
c659306ee2 | ||
|
|
233f1a3c2a | ||
|
|
75de98e2ef | ||
|
|
3dbc88bf94 | ||
|
|
d701792aa1 | ||
|
|
4079056501 | ||
|
|
6dfc238aa2 | ||
|
|
3608f335fd | ||
|
|
56efec5abc | ||
|
|
2a608bd71c | ||
|
|
ee4b7847f0 | ||
|
|
ec38bbeb99 | ||
|
|
26999664e6 | ||
|
|
c0b61b9442 | ||
|
|
274a76fe84 | ||
|
|
a8f58b4080 | ||
|
|
542b7c7c4c | ||
|
|
16495c6ed7 | ||
|
|
738573b079 | ||
|
|
e65e571ed1 | ||
|
|
49b464fd4d | ||
|
|
9a875add84 | ||
|
|
a3d547ccd3 | ||
|
|
c4a8a99834 | ||
|
|
bc3b249489 | ||
|
|
cd2c6a1ad1 | ||
|
|
8c6af9440c | ||
|
|
6850408f6c | ||
|
|
ec445049be | ||
|
|
646915cb86 | ||
|
|
fe551be87b | ||
|
|
30280f81af | ||
|
|
8cb60f0c5d | ||
|
|
9d29f57789 | ||
|
|
a3e1c99915 | ||
|
|
0fe1236e20 | ||
|
|
b28f1e55b7 | ||
|
|
25ee506af4 | ||
|
|
a1c5d79d94 | ||
|
|
de5210b43a | ||
|
|
d9c4c749e2 | ||
|
|
01420768c8 | ||
|
|
d11342489a | ||
|
|
bd7bad19a1 | ||
|
|
dfc627068b | ||
|
|
166c2e766b | ||
|
|
2ee2d3e389 | ||
|
|
81edfb7ee8 | ||
|
|
4e7aebc20c | ||
|
|
5caed5b90a | ||
|
|
f4d8ccda10 | ||
|
|
4cde50ab14 | ||
|
|
2f115223cc | ||
|
|
92c505a211 | ||
|
|
e9ea7657ee | ||
|
|
4bcc57de74 | ||
|
|
b2d65ddc98 | ||
|
|
894ffec36a | ||
|
|
3db4a94281 | ||
|
|
326a57a9cc | ||
|
|
9dfdebc6c7 | ||
|
|
b896e04c20 | ||
|
|
6ecae1ad50 | ||
|
|
9abb72e4d6 | ||
|
|
6365b3d0cf | ||
|
|
33c2ea01c4 | ||
|
|
863ff0ef1b | ||
|
|
a5117083ec | ||
|
|
a143688a1d | ||
|
|
0676d0bd11 | ||
|
|
b6cb6c8ae9 | ||
|
|
b16e4ec566 | ||
|
|
cb1cbbe044 | ||
|
|
86fb408b2c | ||
|
|
ca5d691199 | ||
|
|
394567079d | ||
|
|
e0c8ebc41f | ||
|
|
8668691ade | ||
|
|
374d790a21 | ||
|
|
d8f406d06f | ||
|
|
46b3cd2109 | ||
|
|
20c608989a | ||
|
|
ba4d83f75f | ||
|
|
067559127d | ||
|
|
37ab5579f0 | ||
|
|
ef5f1d347d | ||
|
|
2c4b195516 | ||
|
|
04396ab4e6 | ||
|
|
4fd77b3a6e | ||
|
|
3ea8b79925 | ||
|
|
71b978da1a | ||
|
|
7110298c52 | ||
|
|
b4a69f08c0 | ||
|
|
572d5841c6 | ||
|
|
984a2dab3d | ||
|
|
c3c02887ec | ||
|
|
ba051c863b | ||
|
|
4d59146e48 | ||
|
|
5765ab4dbc | ||
|
|
4769751943 | ||
|
|
e506ce021e | ||
|
|
ed763991de | ||
|
|
55ac9ca88d | ||
|
|
548f5ffca9 | ||
|
|
dd3377b1a0 | ||
|
|
605239a1e5 | ||
|
|
67c58ad4f4 | ||
|
|
11e19a3d0f | ||
|
|
6fffcb9203 | ||
|
|
3479b6691e | ||
|
|
68790e00a9 | ||
|
|
6cf06fac12 | ||
|
|
0d8c107362 | ||
|
|
f30e45c549 | ||
|
|
250ea13767 | ||
|
|
608128738d | ||
|
|
7153f33466 | ||
|
|
274b12318d | ||
|
|
94c82f61a3 | ||
|
|
d260f50573 | ||
|
|
40c014a991 | ||
|
|
75c8aec29d | ||
|
|
fcb9c0065e | ||
|
|
63ec6bdb3d | ||
|
|
25bfe6f306 | ||
|
|
635c4fd43b | ||
|
|
c455215f55 | ||
|
|
8f56f51307 | ||
|
|
70a8ed6ed3 | ||
|
|
febfaf16dc | ||
|
|
8268b17700 | ||
|
|
0889995a61 | ||
|
|
4e476ae574 | ||
|
|
7efd7d23fe | ||
|
|
6fb944815b | ||
|
|
5e3432fbbe | ||
|
|
f0c6bd7773 | ||
|
|
dd579c905d | ||
|
|
22b735885a | ||
|
|
b7c5058e37 | ||
|
|
baff5f4359 | ||
|
|
dce4ea5846 | ||
|
|
c47fc48607 | ||
|
|
1d9b6cbe57 | ||
|
|
1cb93123fc | ||
|
|
c6cb42ec27 | ||
|
|
67a5f6d68f | ||
|
|
e997cc5486 | ||
|
|
53577a5190 | ||
|
|
c1ae234a64 | ||
|
|
02cb395ec2 | ||
|
|
65e7529ca0 | ||
|
|
f70743b267 | ||
|
|
f4382738ab | ||
|
|
a100dca303 | ||
|
|
50414208d1 | ||
|
|
e17a007719 | ||
|
|
dd3c5f5c0a | ||
|
|
fb3f560e0c | ||
|
|
12c177a365 | ||
|
|
402f6ca72b | ||
|
|
2b8a39373e | ||
|
|
17f18604e4 | ||
|
|
99e404cb61 | ||
|
|
d4e3b4f832 | ||
|
|
6552b90657 | ||
|
|
bf57d698b1 | ||
|
|
4d002af735 | ||
|
|
2bfc7cc1b0 | ||
|
|
0244f0919e | ||
|
|
f122fb12f3 | ||
|
|
f7cd553044 | ||
|
|
7d0b7e2113 | ||
|
|
c3a67cfdca | ||
|
|
4315e982be | ||
|
|
634464353f | ||
|
|
9c36004493 | ||
|
|
1b1034442c | ||
|
|
a89863c84c | ||
|
|
f7340e0615 | ||
|
|
bf6b207cc9 | ||
|
|
f93b4877f7 | ||
|
|
cd17338759 | ||
|
|
c46867c3a7 | ||
|
|
3c321ef79c | ||
|
|
7938fdb596 | ||
|
|
a2111869e6 | ||
|
|
f8288e2f02 | ||
|
|
f2f90d1185 | ||
|
|
459e800ecf | ||
|
|
8f00d3fd53 | ||
|
|
cd94cb13c6 | ||
|
|
e100be620e | ||
|
|
1e1b9b484a | ||
|
|
3e7246690f | ||
|
|
360585dfaf | ||
|
|
f604619dd5 | ||
|
|
0fa056327a | ||
|
|
6db2bf69f3 | ||
|
|
20fb9578c0 | ||
|
|
4eb8232495 | ||
|
|
6c4e8019f8 | ||
|
|
28707bf5df | ||
|
|
023a580f00 | ||
|
|
a2eaae5555 | ||
|
|
75df1a5422 | ||
|
|
f0b4cc4581 | ||
|
|
7a41e479c9 | ||
|
|
3350c7213c | ||
|
|
7b5e43d343 | ||
|
|
06661ea310 | ||
|
|
38b2b33503 | ||
|
|
f735d6fb3a | ||
|
|
5cb5e07ac0 | ||
|
|
54778504ed | ||
|
|
1bfe0d08dc | ||
|
|
56da9a58fc | ||
|
|
54383bca25 | ||
|
|
88461b4fed | ||
|
|
aabbea3e78 | ||
|
|
adedca09f2 | ||
|
|
9a27a9f19f | ||
|
|
3ebdb3fcef | ||
|
|
97e7cb1512 | ||
|
|
262a2d9288 | ||
|
|
91b862b556 | ||
|
|
b8ffc725c7 | ||
|
|
76f1411922 | ||
|
|
d1003d6f8f | ||
|
|
91f74e8d16 | ||
|
|
94c5096a95 | ||
|
|
f35aeb6a8e | ||
|
|
d6ce846047 | ||
|
|
ec069e4f19 | ||
|
|
c5adafd9ce | ||
|
|
16dcc98cff | ||
|
|
59c410a70a | ||
|
|
9b586162d0 | ||
|
|
803885049b | ||
|
|
be4fe7540e | ||
|
|
927568eea2 | ||
|
|
436d5e717c | ||
|
|
d37fa66ebc | ||
|
|
e8a4bf6edc | ||
|
|
35d1c3f9b4 | ||
|
|
e17785af21 | ||
|
|
0537b59f27 | ||
|
|
339e33d2f3 | ||
|
|
f082ac02cf | ||
|
|
9538ae1258 | ||
|
|
34981b664f | ||
|
|
477d8930e0 | ||
|
|
b5091a566a | ||
|
|
9981c45554 | ||
|
|
0f736ec7fd | ||
|
|
7826ad1e06 | ||
|
|
f4e6816338 | ||
|
|
4a13725678 | ||
|
|
ab9b010856 | ||
|
|
29be7fabe4 | ||
|
|
74e867833a | ||
|
|
91dc6d2a0f | ||
|
|
3c1fd0e9cf | ||
|
|
4deeb7d59b | ||
|
|
89b12cfc62 | ||
|
|
c07ffa5c1e | ||
|
|
259fcfcef8 | ||
|
|
f81201b74d | ||
|
|
6dbc74b32e | ||
|
|
ed38b5fe2b | ||
|
|
a84badb834 | ||
|
|
e1804cbc76 | ||
|
|
d13eef7951 | ||
|
|
0b4c6baf44 | ||
|
|
b3af54340c | ||
|
|
8c14b9d1af | ||
|
|
d818ccae92 | ||
|
|
4cea8b9785 | ||
|
|
84a36801e0 | ||
|
|
6eb7b3508d | ||
|
|
2a5f4ac7d7 | ||
|
|
518a16d3ac | ||
|
|
d53a2ad45d | ||
|
|
bb02a7645b | ||
|
|
5a9d90c872 | ||
|
|
4fd7269643 | ||
|
|
1eb3c1a075 | ||
|
|
a1746f2f8c | ||
|
|
b727febd08 | ||
|
|
c819d69ae4 | ||
|
|
bb6f83fb96 | ||
|
|
02d14d724a | ||
|
|
aa92d83d8c | ||
|
|
5427ab7cc3 | ||
|
|
e583f199b8 | ||
|
|
b465c20f65 | ||
|
|
5c2c617ff0 | ||
|
|
3ab0665c19 | ||
|
|
4b66043735 | ||
|
|
979f03e76c | ||
|
|
c8b049f57f | ||
|
|
f3d8883046 | ||
|
|
e308f321d3 | ||
|
|
beea785ead | ||
|
|
95c5b686be | ||
|
|
0ddf68cc8a | ||
|
|
4cadad5cfe | ||
|
|
9e4a6397d6 | ||
|
|
2e8a3b7c45 | ||
|
|
276d0430bf | ||
|
|
1aa7a1e709 | ||
|
|
78737f5a38 | ||
|
|
dac84d867e | ||
|
|
217e5c7268 | ||
|
|
936ac37a2e | ||
|
|
cb4f5c3983 | ||
|
|
1801d33287 | ||
|
|
b01da61d83 | ||
|
|
23e28bb18a | ||
|
|
a1f1c2d32f | ||
|
|
10331d9560 | ||
|
|
60038d44f9 | ||
|
|
24d06f83ca | ||
|
|
e76638f98d | ||
|
|
ef1b9d4854 | ||
|
|
3c0f805674 | ||
|
|
324581594c | ||
|
|
7fae7d1bd6 | ||
|
|
0af3a75708 | ||
|
|
d39d7db9ed | ||
|
|
a04a6de0ef | ||
|
|
69a6708f9b | ||
|
|
c19a972739 | ||
|
|
2e674337b3 | ||
|
|
727e24f365 | ||
|
|
7e8ba62e9f | ||
|
|
fe60d0c403 | ||
|
|
2e0556b544 | ||
|
|
479acac581 | ||
|
|
3b37fed24b | ||
|
|
8fdb3f1703 | ||
|
|
95e242353b | ||
|
|
199802a176 | ||
|
|
065432e074 | ||
|
|
62fd36facb | ||
|
|
f121e74744 | ||
|
|
5aa2f534be | ||
|
|
86dd74fd62 | ||
|
|
218da9858f | ||
|
|
cebda58437 | ||
|
|
6a82437bc9 | ||
|
|
6b9e8eb891 | ||
|
|
615984bf2d | ||
|
|
bc6ac8a68a | ||
|
|
39f24ef8d2 | ||
|
|
24294d3bd0 | ||
|
|
234fcbfc02 | ||
|
|
b54093ebca | ||
|
|
db307a7084 | ||
|
|
a0c7997b66 | ||
|
|
7458a53a73 | ||
|
|
cf6e6ca4db | ||
|
|
e847ec7474 | ||
|
|
440026b9b3 | ||
|
|
64331ad845 | ||
|
|
294070b3ab | ||
|
|
cabbea0d97 | ||
|
|
732a8522df | ||
|
|
8f82c4c748 | ||
|
|
d0bc25f339 | ||
|
|
ed3200e4f5 | ||
|
|
a3ed120efb | ||
|
|
f1b191c02f | ||
|
|
1493b24b46 | ||
|
|
5993eae1aa | ||
|
|
3e506eae02 | ||
|
|
0305860078 | ||
|
|
0205090e0d | ||
|
|
6adefe4562 | ||
|
|
da1bd901b4 | ||
|
|
636b9c7aeb | ||
|
|
942f112ef5 | ||
|
|
03bbb076bf | ||
|
|
e468d6b4d2 | ||
|
|
57206a628d | ||
|
|
f061bb887b | ||
|
|
75fd9a43a3 | ||
|
|
58165dfc89 | ||
|
|
0e8257c387 | ||
|
|
62e78f8349 | ||
|
|
84724a3ebf | ||
|
|
23544e0431 | ||
|
|
b1fda3d62e | ||
|
|
b8ae69b748 | ||
|
|
4014440d06 | ||
|
|
74b0b3adc6 | ||
|
|
382f09a126 | ||
|
|
f9afdc5c95 | ||
|
|
5e4fb4796e | ||
|
|
76f8988865 | ||
|
|
fa3e829eb6 | ||
|
|
d9435bd1b1 | ||
|
|
af96bb4bda | ||
|
|
8607e0adec | ||
|
|
5a2a7709a4 | ||
|
|
437e8f90f6 | ||
|
|
06ac670951 | ||
|
|
3ee1579688 | ||
|
|
5954314b98 | ||
|
|
c0324456a7 | ||
|
|
172adc404f | ||
|
|
501748192b | ||
|
|
f6e6ac0320 | ||
|
|
a10ce1d787 | ||
|
|
839cc2467c | ||
|
|
dbc11ed29f | ||
|
|
0f614e92f7 | ||
|
|
afaf7c62bc | ||
|
|
78d22069d7 | ||
|
|
22720a8b7a | ||
|
|
38c66d213a | ||
|
|
871de80544 | ||
|
|
c24daec480 | ||
|
|
0849d12572 | ||
|
|
23bd8ff749 | ||
|
|
b5759305af | ||
|
|
da518668b5 | ||
|
|
51605beb3b | ||
|
|
2646861a4c | ||
|
|
f8c910bd3b | ||
|
|
cb82dced8a | ||
|
|
f618f9ce96 | ||
|
|
020dc9c216 | ||
|
|
a6b9445702 | ||
|
|
36408a120c | ||
|
|
c1bca9cd62 | ||
|
|
701973b73e | ||
|
|
24a64bdee3 | ||
|
|
3f3d3b17a4 | ||
|
|
53c87a32e9 | ||
|
|
0fdf0f825f | ||
|
|
73da51d0ac | ||
|
|
df10d5a17d | ||
|
|
9557888b32 | ||
|
|
3e14a31312 | ||
|
|
dca13a4770 | ||
|
|
017a094438 | ||
|
|
b8e7cf04b6 | ||
|
|
a8dacdaffc | ||
|
|
5c4e363f11 | ||
|
|
1e3ce57077 | ||
|
|
01ddeb476d | ||
|
|
714af0cd66 | ||
|
|
82abd7e9ea | ||
|
|
823ff2d048 | ||
|
|
fcfdf6be51 | ||
|
|
627118c438 | ||
|
|
5bb7364967 | ||
|
|
a46926c1eb | ||
|
|
4f760a0850 | ||
|
|
e905411207 | ||
|
|
1eb77a6cab | ||
|
|
8b1704bcf3 | ||
|
|
a0edb2d966 | ||
|
|
02dff45d7d | ||
|
|
3cf18adae9 | ||
|
|
2bec25b46e | ||
|
|
8ee308d5d6 | ||
|
|
3c3c97f9b5 | ||
|
|
c3e8f6008e | ||
|
|
660847225d | ||
|
|
48d16f877b | ||
|
|
a4f4945455 | ||
|
|
bd7229ea17 | ||
|
|
72441ce3ef | ||
|
|
fe5b7a3b41 | ||
|
|
a4db686b6c | ||
|
|
10026758d3 | ||
|
|
c8dddac5b9 | ||
|
|
5a8a4d47a5 | ||
|
|
6c35c06f3e | ||
|
|
1235047742 | ||
|
|
6c041c3a79 | ||
|
|
1e7260b69a | ||
|
|
1de5fe608f | ||
|
|
6c9bf58e7f | ||
|
|
accd71a105 | ||
|
|
e3fd691459 | ||
|
|
3a508da641 | ||
|
|
a64a1022e6 | ||
|
|
b6fba0ad5c | ||
|
|
45fc67062e | ||
|
|
32cef014bb | ||
|
|
677ec868e0 | ||
|
|
7716b1bd3d | ||
|
|
8a529ca399 | ||
|
|
914a82bfa4 | ||
|
|
197c95f6ef | ||
|
|
323672bc38 | ||
|
|
66ce6f8801 | ||
|
|
6bc9d7358c | ||
|
|
bcbfa069ae | ||
|
|
7e622e1382 | ||
|
|
a2871181fe | ||
|
|
86c3863bc9 | ||
|
|
14d2f4bb15 | ||
|
|
c6f575ce37 | ||
|
|
e86fd7f009 | ||
|
|
d03a5ab95f | ||
|
|
8ef9f75de7 | ||
|
|
cf3a125940 | ||
|
|
8049f7b1af | ||
|
|
985fe4821d | ||
|
|
c828b1e49a | ||
|
|
d5d44ce3fe | ||
|
|
5d3dac04fa | ||
|
|
d2f5fe13aa | ||
|
|
705c6ad5ce | ||
|
|
94f8d4ec63 | ||
|
|
225bae8491 | ||
|
|
53f78260a9 | ||
|
|
06add3ab35 | ||
|
|
a59ec8fe23 | ||
|
|
f93cee9440 | ||
|
|
21e65990c1 | ||
|
|
38e9c34f08 | ||
|
|
038bc7713b | ||
|
|
a5914abad8 | ||
|
|
3f2b98ff01 | ||
|
|
aa1dd9ddc2 | ||
|
|
5ba45f1ef8 | ||
|
|
f3752dc75c | ||
|
|
d5f4da1261 | ||
|
|
7680392d96 | ||
|
|
d8015d3c93 | ||
|
|
7487e2f9cb | ||
|
|
4b8bc7d6ba | ||
|
|
59af49522e | ||
|
|
72d3eb15e0 | ||
|
|
14e31de6b1 | ||
|
|
162b8f3d37 | ||
|
|
003cf61a48 | ||
|
|
74f0df8b9d | ||
|
|
0df531e7c6 | ||
|
|
e3ce61647f | ||
|
|
82ff64c374 |
@@ -1,7 +1,6 @@
|
||||
.github
|
||||
docs
|
||||
examples
|
||||
functions
|
||||
hack
|
||||
site
|
||||
travis
|
||||
|
||||
68
.github/ISSUE_TEMPLATE/bug_report.md
vendored
68
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,68 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ""
|
||||
labels:
|
||||
- kind/bug
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
<!--
|
||||
Please read this page: https://kubernetes-sigs.github.io/kustomize/contributing/bugs/ before
|
||||
filing a bug
|
||||
-->
|
||||
|
||||
<!-- Feel free to skip the sections if they are not applicable. -->
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**Files that can reproduce the issue**
|
||||
|
||||
<!--
|
||||
We cannot figure out or fix the issue if we don't know how to reproduce. Please
|
||||
provide a minimum set of files that can reproduce the issue. You can paste the
|
||||
file contents here or provide a link to a tarball or git repo.
|
||||
|
||||
Example:
|
||||
|
||||
kustomization.yaml
|
||||
|
||||
```
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
...
|
||||
```
|
||||
|
||||
resources.yaml
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
...
|
||||
```
|
||||
|
||||
...
|
||||
-->
|
||||
|
||||
**Expected output**
|
||||
|
||||
<!-- What's the expected output? -->
|
||||
|
||||
**Actual output**
|
||||
|
||||
<!-- What's the actual output? -->
|
||||
|
||||
**Kustomize version**
|
||||
|
||||
<!-- Please use the latest version when it's possible. -->
|
||||
|
||||
**Platform**
|
||||
|
||||
<!-- Linux/macOS/Windows? -->
|
||||
|
||||
**Additional context**
|
||||
|
||||
<!-- Add any other context about the problem here. -->
|
||||
87
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
87
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
name: Bug report
|
||||
description: File a bug report
|
||||
labels: ["kind/bug"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: |
|
||||
Please provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner.
|
||||
If this matter is security related, please disclose it privately via https://kubernetes.io/security
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: What did you expect to happen?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: repro-files
|
||||
attributes:
|
||||
label: How can we reproduce it (as minimally and precisely as possible)?
|
||||
description: Please provide a minimum set of files that can reproduce the issue. You can paste the file contents here or provide a link to a tarball or git repo. Even better, submit a tests case in the api/krusty package! For more information on how to do that, see https://kubectl.docs.kubernetes.io/contributing/kustomize/bugs/.
|
||||
value: |
|
||||
```yaml
|
||||
# kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- resources.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# resources.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-object
|
||||
data:
|
||||
placeholder: data
|
||||
```
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected-output
|
||||
attributes:
|
||||
label: Expected output
|
||||
description: If you are able to provide reproduction files, please include the output you expect here.
|
||||
value: |
|
||||
```yaml
|
||||
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: actual-output
|
||||
attributes:
|
||||
label: Actual output
|
||||
description: If you are able to provide reproduction files, please include the output they currently produce here.
|
||||
value: |
|
||||
```yaml
|
||||
|
||||
```
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: kustomize-version
|
||||
attributes:
|
||||
label: Kustomize version
|
||||
description: Please use the latest version whenever possible.
|
||||
placeholder: What version of Kustomize are you using?
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating system
|
||||
options:
|
||||
- Linux
|
||||
- MacOS
|
||||
- Windows
|
||||
- Other
|
||||
validations:
|
||||
required: false
|
||||
1
.github/ISSUE_TEMPLATE/config.yaml
vendored
1
.github/ISSUE_TEMPLATE/config.yaml
vendored
@@ -1 +0,0 @@
|
||||
blank_issues_enabled: true
|
||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
6
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
contact_links:
|
||||
- name: Support request
|
||||
url: https://discuss.kubernetes.io
|
||||
about: |
|
||||
Please do not submit support requests or questions as issues.
|
||||
Ask your question in the Kubernetes Community Forums, or in the #kustomize channel on Kubernetes Slack (https://slack.k8s.io).
|
||||
26
.github/ISSUE_TEMPLATE/feature_request.md
vendored
26
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,26 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ""
|
||||
labels:
|
||||
- kind/feature
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
<!-- Feel free to skip the sections if they are not applicable. -->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
**Describe the solution you'd like**
|
||||
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Additional context**
|
||||
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
64
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
64
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
name: Feature request
|
||||
description: Propose an enhancement to Kustomize
|
||||
labels: kind/feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Small, straightforward enhancements can be proposed in regular GitHub issues using the template below. As a rule of thumb, the enhancement should be resolvable in a single PR that is at most size L. Anything more involved requires a mini (in-repo) enhancement proposal, and features with implications for kubectl require a full [KEP](https://github.com/kubernetes/enhancements).
|
||||
|
||||
For more information on the Kustomize enhancement process, see: https://github.com/kubernetes-sigs/kustomize/tree/master/proposals.
|
||||
|
||||
When in doubt, go ahead and fill out the template below; the maintainers will let you know if a KEP is required.
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Eschewed features
|
||||
description: Some features are out of scope for Kustomize because they are incompatible with its foundational design principles. Please review the [Eschewed Features](https://kubectl.docs.kubernetes.io/faq/kustomize/eschewedfeatures/) documentation before submitting your feature request.
|
||||
options:
|
||||
- label: This issue is not requesting templating, unstuctured edits, build-time side-effects from args or env vars, or any other eschewed feature.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: feature-description
|
||||
attributes:
|
||||
label: What would you like to have added?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: rationale
|
||||
attributes:
|
||||
label: Why is this needed?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: current-alternatives
|
||||
attributes:
|
||||
label: Can you accomplish the motivating task without this feature, and if so, how?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
- type: textarea
|
||||
id: design-alternatives
|
||||
attributes:
|
||||
label: What other solutions have you considered?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional-info
|
||||
attributes:
|
||||
label: Anything else we should know?
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Feature ownership
|
||||
description: The Kustomize project, like many areas of Kubernetes, currently lacks enough contributors to adequately respond to all proposals that have merit. Offering to build and support the feature yourself can help get traction for your request.
|
||||
options:
|
||||
- label: I am interested in contributing this feature myself! 🎉
|
||||
required: false
|
||||
9
.github/ISSUE_TEMPLATE/question.md
vendored
9
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,9 +0,0 @@
|
||||
---
|
||||
name: Question
|
||||
about: Ask a question about the kustomize
|
||||
title: "[Question]"
|
||||
labels: ""
|
||||
assignees: ""
|
||||
---
|
||||
|
||||
<!-- Please describe your question here -->
|
||||
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
60
.github/workflows/go.yml
vendored
60
.github/workflows/go.yml
vendored
@@ -6,6 +6,9 @@ on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
|
||||
lint:
|
||||
@@ -14,18 +17,21 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.13
|
||||
go-version: '^1.19.0'
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Lint
|
||||
run: ./scripts/kyaml-pre-commit.sh
|
||||
env:
|
||||
KUSTOMIZE_DOCKER_E2E: false # don't need to do e2e tests for linting
|
||||
run: make lint
|
||||
|
||||
- name: Verify boilerplate
|
||||
run: make check-license
|
||||
|
||||
test-linux:
|
||||
name: Test Linux
|
||||
@@ -33,21 +39,16 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.13
|
||||
go-version: '^1.19.0'
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Test kyaml
|
||||
run: go test -cover ./...
|
||||
working-directory: ./kyaml
|
||||
|
||||
- name: Test cmd/config
|
||||
run: go test -cover ./...
|
||||
working-directory: ./cmd/config
|
||||
- name: Test all modules
|
||||
run: make test-unit-non-plugin
|
||||
env:
|
||||
KUSTOMIZE_DOCKER_E2E: true
|
||||
|
||||
@@ -57,21 +58,16 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.13
|
||||
go-version: '^1.19.0'
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Test kyaml
|
||||
run: go test -cover ./...
|
||||
working-directory: ./kyaml
|
||||
|
||||
- name: Test cmd/config
|
||||
run: go test -cover ./...
|
||||
working-directory: ./cmd/config
|
||||
- name: Test all modules
|
||||
run: make test-unit-non-plugin
|
||||
env:
|
||||
KUSTOMIZE_DOCKER_E2E: false # docker not installed on mac
|
||||
|
||||
@@ -81,13 +77,13 @@ jobs:
|
||||
steps:
|
||||
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.13
|
||||
go-version: '^1.19.0'
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Test kyaml
|
||||
run: go test -cover ./...
|
||||
@@ -98,3 +94,9 @@ jobs:
|
||||
working-directory: ./cmd/config
|
||||
env:
|
||||
KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet
|
||||
|
||||
# TODO (#4001): replace specific modules above with this once Windows tests are passing.
|
||||
#- name: Test all modules
|
||||
# run: make test-unit-non-plugin
|
||||
# env:
|
||||
# KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -21,3 +21,13 @@
|
||||
*.DS_store
|
||||
|
||||
.bin
|
||||
|
||||
# Hugo site
|
||||
publishedSite/
|
||||
site/public/
|
||||
site/resources/
|
||||
site/.hugo_build.lock
|
||||
**/node_modules/
|
||||
|
||||
# goreleaser artifacts
|
||||
**/dist/
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "site/themes/docsy"]
|
||||
path = site/themes/docsy
|
||||
url = https://github.com/google/docsy.git
|
||||
@@ -1,50 +0,0 @@
|
||||
run:
|
||||
deadline: 5m
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- bodyclose
|
||||
- deadcode
|
||||
- depguard
|
||||
# - dogsled
|
||||
- dupl
|
||||
# - errcheck
|
||||
# - funlen
|
||||
# - gochecknoinits
|
||||
- goconst
|
||||
# - gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- goimports
|
||||
- golint
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- interfacer
|
||||
- lll
|
||||
- misspell
|
||||
- nakedret
|
||||
# - scopelint
|
||||
- staticcheck
|
||||
- structcheck
|
||||
# stylecheck demands that acronyms not be treated as words
|
||||
# in camelCase, so JsonOp become JSONOp, etc. Yuck.
|
||||
# - stylecheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- varcheck
|
||||
# - whitespace
|
||||
|
||||
linters-settings:
|
||||
dupl:
|
||||
threshold: 400
|
||||
lll:
|
||||
line-length: 170
|
||||
gocyclo:
|
||||
min-complexity: 15
|
||||
golint:
|
||||
min-confidence: 0.85
|
||||
142
.golangci.yml
Normal file
142
.golangci.yml
Normal file
@@ -0,0 +1,142 @@
|
||||
# Copyright 2019 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
run:
|
||||
deadline: 5m
|
||||
go: '1.19'
|
||||
|
||||
linters:
|
||||
# please, do not use `enable-all`: it's deprecated and will be removed soon.
|
||||
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
|
||||
disable-all: true
|
||||
enable:
|
||||
- asciicheck
|
||||
- bidichk
|
||||
- bodyclose
|
||||
- contextcheck
|
||||
# - cyclop
|
||||
- depguard
|
||||
- dogsled
|
||||
- dupl
|
||||
- dupword
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errname
|
||||
- errorlint
|
||||
- exhaustive
|
||||
# - exhaustivestruct
|
||||
- exportloopref
|
||||
# - forbidigo
|
||||
- forcetypeassert
|
||||
# - funlen
|
||||
# - gci
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
# - gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
# - godot
|
||||
# - godox
|
||||
# - goerr113
|
||||
- gofmt
|
||||
# - gofumpt
|
||||
- goheader
|
||||
- goimports
|
||||
- gomnd
|
||||
- gomoddirectives
|
||||
- gomodguard
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
# - ifshort # too many false positives
|
||||
- importas
|
||||
- ineffassign
|
||||
# - ireturn
|
||||
- lll
|
||||
- makezero
|
||||
- misspell
|
||||
- nakedret
|
||||
- nestif
|
||||
- nilerr
|
||||
# - nilnil
|
||||
# - nlreturn
|
||||
# - noctx
|
||||
- nolintlint
|
||||
# - paralleltest
|
||||
- prealloc
|
||||
- predeclared
|
||||
- promlinter
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
# - stylecheck
|
||||
- tagliatelle
|
||||
- tenv
|
||||
- testpackage
|
||||
- testableexamples
|
||||
- thelper
|
||||
- tparallel
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
# - varnamelen
|
||||
- wastedassign
|
||||
- whitespace
|
||||
- wrapcheck
|
||||
# - wsl
|
||||
- asasalint
|
||||
- usestdlibvars
|
||||
- interfacebloat
|
||||
- loggercheck
|
||||
- reassign
|
||||
|
||||
linters-settings:
|
||||
dupl:
|
||||
threshold: 400
|
||||
lll:
|
||||
line-length: 170
|
||||
gocyclo:
|
||||
min-complexity: 30
|
||||
revive:
|
||||
rules:
|
||||
- name: var-naming
|
||||
arguments:
|
||||
- [ "ID", "API", "JSON" ] # AllowList
|
||||
- [ ] # DenyList
|
||||
gomnd:
|
||||
ignored-functions:
|
||||
- os.WriteFile
|
||||
- make
|
||||
gomoddirectives:
|
||||
replace-local: true
|
||||
gosec:
|
||||
config:
|
||||
G306: "0644"
|
||||
wrapcheck:
|
||||
ignoreSigs:
|
||||
# defaults
|
||||
- .Errorf(
|
||||
- errors.New(
|
||||
- errors.Unwrap(
|
||||
- .Wrap(
|
||||
- .Wrapf(
|
||||
- .WithMessage(
|
||||
- .WithMessagef(
|
||||
- .WithStack(
|
||||
# from kyaml's errors package
|
||||
- .WrapPrefixf(
|
||||
|
||||
issues:
|
||||
new-from-rev: c94b5d8f2 # enables us to enforce a larger set of linters for new code than pass on existing code
|
||||
max-same-issues: 0
|
||||
exclude-rules:
|
||||
- linters:
|
||||
- revive
|
||||
text: "don't use leading"
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "SA1019: kioutil.Legacy"
|
||||
299
ARCHITECTURE.md
Normal file
299
ARCHITECTURE.md
Normal file
@@ -0,0 +1,299 @@
|
||||
# Architecture
|
||||
|
||||
* _Updated: December 2021_
|
||||
|
||||
This document describes the repository organization and the kustomize
|
||||
build process. It's meant to lower the barrier to learning and
|
||||
contributing to the code base.
|
||||
|
||||
If not kept up to date, it will just be a historical snapshot.
|
||||
|
||||
|
||||
## Repository layout
|
||||
|
||||
[human-edited docs]: https://github.com/kubernetes-sigs/cli-experimental/tree/master/site
|
||||
[generated docs]: https://github.com/kubernetes-sigs/cli-experimental/tree/master/docs
|
||||
[rendered docs]: https://kubectl.docs.kubernetes.io
|
||||
[openapi]: https://kubernetes.io/blog/2016/12/kubernetes-supports-openapi
|
||||
|
||||
[`api` module]: https://github.com/kubernetes-sigs/kustomize/blob/master/api/go.mod
|
||||
[`api`]: #the-api-module
|
||||
[`cmd/config` module]: https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/go.mod
|
||||
[`cmd/config`]: #the-cmdconfig-module
|
||||
[`kustomize` module]: https://github.com/kubernetes-sigs/kustomize/blob/master/kustomize/go.mod
|
||||
[`kustomize`]: #the-kustomize-module
|
||||
[`kyaml` module]: https://github.com/kubernetes-sigs/kustomize/blob/master/kyaml/go.mod
|
||||
[`kyaml`]: #the-kyaml-module
|
||||
[`kyaml/kio.Filter`]: https://github.com/Kubernetes-sigs/kustomize/blob/master/kyaml/kio/kio.go
|
||||
[`go-yaml`]: https://github.com/go-yaml/yaml/tree/v3
|
||||
|
||||
|
||||
[3922]: https://github.com/kubernetes-sigs/kustomize/issues/3922
|
||||
|
||||
|
||||
|
||||
| directory | purpose |
|
||||
| ---------: | :---------- |
|
||||
| `api` | The [`api`] module, holding high level kustomize code, suitable for import by other programs. |
|
||||
| `cmd` | Various Go programs aiding repo management. See also `hack`. As an outlier, includes the special [`cmd/config`] module. |
|
||||
| `docs` | Old home of documentation; contains pointers to new homes: [human-edited docs], [generated docs] and [rendered docs]. |
|
||||
| `examples` | Full kustomization examples that run as pre-merge tests. |
|
||||
| `functions` | Examples of plugins in KRM function form. TODO([3922]): Move under `plugin`. |
|
||||
| `hack` | Various shell scripts to help with code management. |
|
||||
| `kustomize` | The [`kustomize`] module holds the `main.go` for kustomize. |
|
||||
| `kyaml` | The [`kyaml`] module, holding Kubernetes-specific YAML editing packages used by the [`api`] module. Wraps [`go-yaml`] v3.|
|
||||
| `plugin` | Examples of Kustomize plugins. |
|
||||
| `releasing` | Instructions for releasing the various modules. |
|
||||
| `site` | Old generated documentation, kept to provide redirection links to the new docs. |
|
||||
|
||||
|
||||
## Modules
|
||||
|
||||
[semantically versioned]: https://semver.org
|
||||
[Go modules]: https://github.com/golang/go/wiki/Modules
|
||||
|
||||
The [Go modules] in the kustomize repository are [semantically versioned].
|
||||
|
||||
|
||||
### `kustomize`
|
||||
|
||||
> _Depends on [`api`], [`cmd/config`], [`kyaml`]_
|
||||
|
||||
The [`kustomize` module] contains the `main.go` for `kustomize`, buildable with
|
||||
|
||||
```
|
||||
(cd kustomize; go install .)
|
||||
```
|
||||
|
||||
[appears in kubectl]: https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/kubectl/pkg/cmd/kustomize/kustomize.go
|
||||
|
||||
Below this are packages containing
|
||||
[cobra](http://github.com/spf13/cobra) commands implementing `build`,
|
||||
`edit`, `fix`, etc., packages linked together by `main.go`.
|
||||
|
||||
These command packages are intentionally public, semantically
|
||||
versioned, and can be used in other programs. Specifically, the
|
||||
`kustomize build` command [appears in kubectl] as `kubectl kustomize`.
|
||||
|
||||
The code in the `build` package is dominated by flag validation,
|
||||
with minimal business logic. The critical lines are something
|
||||
like
|
||||
|
||||
```
|
||||
# Make a kustomizer.
|
||||
k := krusty.MakeKustomizer(
|
||||
HonorKustomizeFlags(krusty.MakeDefaultOptions()),
|
||||
)
|
||||
|
||||
# Run the kustomizer, sending location of kustomization.yaml
|
||||
m := k.Run(fSys, "/path/to/dir")
|
||||
|
||||
# Write the result as YAML.
|
||||
writer.Write(m.AsYaml())
|
||||
```
|
||||
|
||||
The `krusty` package is in the [`api`] module.
|
||||
|
||||
### `api`
|
||||
|
||||
> _Depends on [`kyaml`] and code generated from builtin plugin modules_
|
||||
|
||||
The [`api` module] is used by CLI programs like `kustomize` and `kubectl`
|
||||
to read and honor `kustomization.yaml` files and all that implies.
|
||||
|
||||
The main public packages in the [`api` module] are
|
||||
|
||||
| package | |
|
||||
| --------: | :---------- |
|
||||
| `filters` | Implementations of [`kyaml/kio.Filter`] used by kustomize to transform Kubernetes objects. |
|
||||
| `konfig` | Configuration methods and constants in the kustomize API. |
|
||||
| `krusty` | Primary API entry point. Holds the kustomizer and hundreds of tests for it. |
|
||||
| `loader` | Loads kustomization files and the files they refer to, enforcing security rules. |
|
||||
| `resmap` | The primary internal data structure over which the kustomizer and filters work. |
|
||||
| `types` | The `Kustomization` object and ancillary structs. |
|
||||
|
||||
### `cmd/config`
|
||||
|
||||
> _Depends on [`kyaml`]_
|
||||
|
||||
This module contains cobra commands and kyaml-based functionality to
|
||||
provide unix-like file manipulation commands to kustomize like `grep`
|
||||
and `tree`. These commands may be included in any program that
|
||||
manipulates k8s YAML (e.g. kustomize).
|
||||
|
||||
### `kyaml`
|
||||
|
||||
> _Has no in-repo dependence_
|
||||
|
||||
The [`kyaml` module] is a kubernetes-focussed enhancement of [go-yaml].
|
||||
|
||||
The YAML manipulation performed by a kustomize is based on these libraries.
|
||||
|
||||
These libraries evolve independently of kustomize, and other programs depend on them.
|
||||
|
||||
The key public packages in the [`kyaml` module] include
|
||||
|
||||
| package | |
|
||||
| --------: | :---------- |
|
||||
| `errors` | Wrapper for the go-errors/errors lib |
|
||||
| `filesys` | A kustomize-specific file system abstraction, to ease writing tests |
|
||||
| `fn/framework` | An SDK for writing KRM Functions in Go |
|
||||
| `fn/runtime` | Implements the runtime for KRM Function extensions |
|
||||
| `kio` | Libraries for reading and writing collections of Kubernetes resources as RNodes |
|
||||
| `openapi` | Loads and accesses openapi schemas for schema-aware resource manipultaion |
|
||||
| `resid` | Representations to aid in unique identification of Kubernetes resources |
|
||||
| `yaml` | A Kubernetes-focused wrapper of [go-yaml], notably including the RNode object |
|
||||
|
||||
|
||||
-------
|
||||
|
||||
## How _kustomize build_ works
|
||||
|
||||
The command `kustomize build` accepts a single string argument,
|
||||
which must resolve to a directory, possibly in a git repository,
|
||||
called the _kustomization root_.
|
||||
|
||||
This directory must contain a file called `kustomization.yaml`, with
|
||||
YAML that marshals into a single instance of a `Kustomization` object.
|
||||
|
||||
For the remainder of this document, the word _kustomization_ refers to
|
||||
either of these things.
|
||||
|
||||
This kustomization is the access point to a directed, acyclic graph of
|
||||
Kubernetes objects, including other kustomizations, to include in a
|
||||
build.
|
||||
|
||||
Execution of `build` starts and ends in the [`api`] module,
|
||||
frequently dipping into the [`kyaml`] module for lower level
|
||||
YAML manipulation.
|
||||
|
||||
### The `build` flow
|
||||
|
||||
- Validate command lines arguments and flags.
|
||||
|
||||
- Make a `Kustomizer` as a function of those arguments.
|
||||
|
||||
- Call `Run` on the kustomizer, passing it the path to the
|
||||
kustomization.
|
||||
|
||||
`Run` returns an instance of `ResMap`, the `api` package's
|
||||
representation of a set of kubernetes `Resource` objects.
|
||||
|
||||
This structure offers resource lookup methods (map behavior),
|
||||
but also retains the resources in the order they were
|
||||
specified in kustomization files (list behavior).
|
||||
|
||||
Post-run, the objects are fully hydrated, per the
|
||||
instructions in the kustomization.
|
||||
|
||||
- Marshal the objects as YAML to a file or `stdout`.
|
||||
|
||||
|
||||
### The `Run` function
|
||||
|
||||
- Create various objects
|
||||
|
||||
- A `ResMap` factory.
|
||||
|
||||
Makes `ResMaps` from byte streams, other `ResMaps`, etc.
|
||||
|
||||
- A file `loader.Loader`.
|
||||
|
||||
It's fed an appropriate set of restrictions, and the path to the kustomization.
|
||||
|
||||
- A plugin loader.
|
||||
|
||||
It finds plugins (transformers, generators or validators)
|
||||
and prepares them for running.
|
||||
|
||||
- A `KustTarget` encapsulating all of the above.
|
||||
|
||||
A KustTarget contains one `Kustomization` and represents
|
||||
everything that kustomization can reach. This will include
|
||||
other `KustTarget` instances, each having a smaller purview than
|
||||
the one referencing it.
|
||||
|
||||
- Call `KustTarget.Load` to load its kustomization.
|
||||
|
||||
This step deals with deprecations and field changes.
|
||||
|
||||
- Load [openapi] data specified by the kustomization.
|
||||
|
||||
This is needed to recognize k8s kinds and their special
|
||||
properties, e.g. which kinds are cluster-scoped, which kinds
|
||||
refer to others, etc.
|
||||
|
||||
- Call `KustTarget.makeCustomizedResmap` to create the `ResMap` result.
|
||||
|
||||
This visits everything referenced by the kustomization,
|
||||
performing all generation, transformation and validation.
|
||||
|
||||
- Finish the `Run` with
|
||||
|
||||
- Optional reordering of objects in `ResMap`, overriding the
|
||||
FIFO rule.
|
||||
|
||||
- Optional addition of _kustomize build annotations_ to the
|
||||
resources. E.g. from which repo and file the resource was
|
||||
read, the fact that kustomize touched the resource, etc.
|
||||
These kustomize-specific annotations are intended for
|
||||
server-side data analytics, file structure traceability and
|
||||
reconstruction, etc.
|
||||
|
||||
### The `makeCustomizedResmap` function
|
||||
|
||||
This function starts the process of object transformation,
|
||||
as well as accumulation of recursively referenced data.
|
||||
|
||||
- Call `ra := KustTarget.AccumulateTarget`.
|
||||
|
||||
The result, `ra`, is a resource accumulator that contains
|
||||
everything referred to by the current kustomization, now fully
|
||||
hydrated.
|
||||
|
||||
- Uniquify names of generated objects by appending content hashes.
|
||||
|
||||
This cannot be done until the objects are complete.
|
||||
|
||||
- Fix all name references (given that names may have changed).
|
||||
|
||||
E.g. if a ConfigMaps was given a generated name, all objects that
|
||||
refer to that ConfigMap must be given its name.
|
||||
|
||||
- Resolve vars, replacing them with whatever they refer to (a legacy feature).
|
||||
|
||||
### The `AccumulateTarget` function
|
||||
|
||||
- Call `AccumulateResources` over the `resources` field (this can recurse).
|
||||
- Call `AccumulateComponents` over the `components` field (this can recurse),
|
||||
- Load legacy (pre-plugin) global kustomize configuration,
|
||||
- Load legacy (pre-openapi) _Custom Resource Definition_ data.
|
||||
- In the context of the data loaded above, run the kustomization's
|
||||
- generators,
|
||||
- transformers,
|
||||
- and validators.
|
||||
- Accumulate `vars` (make note of them for later replacement).
|
||||
|
||||
### `AccumulateResources` and component accumulation
|
||||
|
||||
- If the path is a file:
|
||||
- Accumulate the objects in the file (treating them
|
||||
as opaque kubernetes objects).
|
||||
|
||||
- If the path is a directory:
|
||||
- Create a new `KustTarget` referring to that directory's kustomization.
|
||||
- Call `subRa := KustTarget.AccumulateTarget`.
|
||||
- Call `ra.MergeAccumulator(subRa)`
|
||||
This completes a recursion.
|
||||
|
||||
- If the path is a git URL:
|
||||
- Clone the repository to a temporary directory.
|
||||
- Process the path optionally specified in the URL
|
||||
as a path in the clone.
|
||||
- If no path specified, work from the repository root.
|
||||
|
||||
|
||||
That's as deep as this discussion will go.
|
||||
|
||||
The deeper this document goes into the details, the faster
|
||||
it will get out of date.
|
||||
@@ -1,6 +1,25 @@
|
||||
[SIG-CLI]: https://github.com/kubernetes/community/tree/master/sig-cli
|
||||
[Slack channel]: https://kubernetes.slack.com/messages/kustomize
|
||||
[Mailing list]: https://groups.google.com/forum/#!forum/kubernetes-sig-cli
|
||||
|
||||
[OWNERS file spec]: https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md
|
||||
[Kustomize OWNERS_ALIASES]: https://github.com/kubernetes-sigs/kustomize/blob/8049f7b1af52e8a7ec26faf6cf714f560d0043c5/OWNERS_ALIASES
|
||||
[SIG-CLI Teams]: https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml
|
||||
[Github permissions]: https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization#repository-access-for-each-permission-level
|
||||
|
||||
[Contributor License Agreement]: https://git.k8s.io/community/CLA.md
|
||||
[Kubernetes Contributor Guide]: http://git.k8s.io/community/contributors/guide
|
||||
[Contributor Cheat Sheet]: https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md
|
||||
[CNCF Code of Conduct]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md
|
||||
[Kubernetes Community Membership]: https://github.com/kubernetes/community/blob/master/community-membership.md
|
||||
|
||||
[Contribution Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/
|
||||
[MacOS Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/mac/
|
||||
[Windows Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/windows/
|
||||
|
||||
# Contributing Guidelines
|
||||
|
||||
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt:
|
||||
Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the [CNCF Code of Conduct]. Here is an excerpt:
|
||||
|
||||
_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._
|
||||
|
||||
@@ -8,19 +27,45 @@ _As contributors and maintainers of this project, and in the interest of fosteri
|
||||
|
||||
Dev guides:
|
||||
|
||||
- [Mac](docs/macDevGuide.md)
|
||||
- [Contribution Guide]
|
||||
- [MacOS Dev Guide]
|
||||
- [Windows Dev Guide]
|
||||
|
||||
We have full documentation on how to get started contributing here:
|
||||
General resources for contributors:
|
||||
|
||||
- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests
|
||||
- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing)
|
||||
- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md) - Common resources for existing developers
|
||||
- [Contributor License Agreement] - Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests.
|
||||
- [Kubernetes Contributor Guide] - Main contributor documentation.
|
||||
- [Contributor Cheat Sheet] - Common resources for existing developers.
|
||||
|
||||
Here are some additional ideas to help you get started with Kustomize:
|
||||
- Attend a Kustomize Bug Scrub. Check the [SIG-CLI] meetings list to find the next one.
|
||||
- Help triage issues by confirming validity and applying the appropriate `kind` label (e.g. comment `/kind bug`).
|
||||
- Pick up an issue to fix. Issues with the `help-wanted` label are a good place to start, but you can also look for any issue with the `triage/accepted` label and no assignee. Remember to `/assign` yourself to let others know you're working on it.
|
||||
- Help confirm new issues labelled `kind/bug` by reproducing them with the latest release.
|
||||
- Support Kustomize users by responding to questions on issues labelled `kind/support` or in the [Slack channel].
|
||||
|
||||
## Mentorship
|
||||
|
||||
- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers!
|
||||
|
||||
## Contributor Ladder
|
||||
|
||||
Kustomize follows the [Kubernetes Community Membership] contributor ladder. Roles are as follows:
|
||||
|
||||
1. Contributor: Anyone who actively contributes code, issues or reviews to the project. All contributors must sign the [Contributor License Agreement].
|
||||
1. Reviewer: Contributors with a history of review and authorship on Kustomize. Has LGTM rights on the Kustomize repo (as do all kubernetes-sigs org members). Active contributors are encouraged to join the reviewers list to be automatically pinged on PRs.
|
||||
1. Approver: Highly experienced active reviewer and contributor to Kustomize. Has both LTGM and approval rights on the Kustomize repo, as well as "maintain" [Github permissions].
|
||||
1. Owner: Approver who sets technical direction and makes or approves design decisions for the project. Has LGTM and approval rights on the Kustomize repo as well as "admin" [Github permissions].
|
||||
|
||||
The kyaml module within the Kustomize repo has additional owners following the same ladder.
|
||||
|
||||
Administrative notes:
|
||||
|
||||
- The [OWNERS file spec] is a useful resources in making changes.
|
||||
- Maintainers and admins must be added to the appropriate lists in both [Kustomize OWNERS_ALIASES] and [SIG-CLI Teams]. If this isn't done, the individual in question will lack either PR approval rights (Kustomize list) or the appropriate Github repository permissions (community list).
|
||||
|
||||
|
||||
## Contact Information
|
||||
|
||||
- [Slack channel](https://kubernetes.slack.com/messages/sig-cli)
|
||||
- [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-cli)
|
||||
- [Slack channel]
|
||||
- [Mailing list]
|
||||
|
||||
375
Makefile
375
Makefile
@@ -3,13 +3,16 @@
|
||||
#
|
||||
# Makefile for kustomize CLI and API.
|
||||
|
||||
LATEST_V4_RELEASE=v4.5.6
|
||||
|
||||
SHELL := /usr/bin/env bash
|
||||
GOOS = $(shell go env GOOS)
|
||||
GOARCH = $(shell go env GOARCH)
|
||||
MYGOBIN = $(shell go env GOBIN)
|
||||
ifeq ($(MYGOBIN),)
|
||||
MYGOBIN = $(shell go env GOPATH)/bin
|
||||
endif
|
||||
export PATH := $(MYGOBIN):$(PATH)
|
||||
MODULES := '"cmd/config" "api/" "kustomize/" "kyaml/"'
|
||||
|
||||
# Provide defaults for REPO_OWNER and REPO_NAME if not present.
|
||||
# Typically these values would be provided by Prow.
|
||||
@@ -21,59 +24,35 @@ ifndef REPO_NAME
|
||||
REPO_NAME := "kustomize"
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: verify-kustomize
|
||||
|
||||
.PHONY: verify-kustomize
|
||||
verify-kustomize: \
|
||||
lint-kustomize \
|
||||
test-unit-kustomize-all \
|
||||
test-examples-kustomize-against-HEAD \
|
||||
test-examples-kustomize-against-4.0
|
||||
# --- Plugins ---
|
||||
include Makefile-plugins.mk
|
||||
|
||||
# The following target referenced by a file in
|
||||
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
|
||||
.PHONY: prow-presubmit-check
|
||||
prow-presubmit-check: \
|
||||
lint-kustomize \
|
||||
test-multi-module \
|
||||
test-unit-kustomize-all \
|
||||
test-unit-cmd-all \
|
||||
test-go-mod \
|
||||
test-examples-kustomize-against-HEAD \
|
||||
test-examples-kustomize-against-4.0
|
||||
|
||||
.PHONY: verify-kustomize-e2e
|
||||
verify-kustomize-e2e: test-examples-e2e-kustomize
|
||||
# --- Tool management ---
|
||||
include Makefile-tools.mk
|
||||
|
||||
# Other builds in this repo might want a different linter version.
|
||||
# Without one Makefile to rule them all, the different makes
|
||||
# cannot assume that golanci-lint is at the version they want
|
||||
# since everything uses the same implicit GOPATH.
|
||||
# This installs in a temp dir to avoid overwriting someone else's
|
||||
# linter, then installs in MYGOBIN with a new name.
|
||||
# Version pinned by hack/go.mod
|
||||
$(MYGOBIN)/golangci-lint-kustomize:
|
||||
( \
|
||||
set -e; \
|
||||
cd hack; \
|
||||
GO111MODULE=on go build -tags=tools -o $(MYGOBIN)/golangci-lint-kustomize github.com/golangci/golangci-lint/cmd/golangci-lint; \
|
||||
)
|
||||
.PHONY: install-tools
|
||||
install-tools: \
|
||||
install-local-tools \
|
||||
install-out-of-tree-tools
|
||||
|
||||
# Install from version specified in api/go.mod.
|
||||
$(MYGOBIN)/mdrip:
|
||||
cd api; \
|
||||
go install github.com/monopole/mdrip
|
||||
.PHONY: uninstall-tools
|
||||
uninstall-tools: \
|
||||
uninstall-local-tools \
|
||||
uninstall-out-of-tree-tools
|
||||
|
||||
# Install from version specified in api/go.mod.
|
||||
$(MYGOBIN)/stringer:
|
||||
cd api; \
|
||||
go install golang.org/x/tools/cmd/stringer
|
||||
.PHONY: install-local-tools
|
||||
install-local-tools: \
|
||||
$(MYGOBIN)/gorepomod \
|
||||
$(MYGOBIN)/k8scopy \
|
||||
$(MYGOBIN)/pluginator
|
||||
|
||||
# Install from version specified in api/go.mod.
|
||||
$(MYGOBIN)/goimports:
|
||||
cd api; \
|
||||
go install golang.org/x/tools/cmd/goimports
|
||||
.PHONY: uninstall-local-tools
|
||||
uninstall-local-tools:
|
||||
rm -f $(MYGOBIN)/gorepomod
|
||||
rm -f $(MYGOBIN)/k8scopy
|
||||
rm -f $(MYGOBIN)/pluginator
|
||||
|
||||
# Build from local source.
|
||||
$(MYGOBIN)/gorepomod:
|
||||
@@ -90,185 +69,94 @@ $(MYGOBIN)/pluginator:
|
||||
cd cmd/pluginator; \
|
||||
go install .
|
||||
|
||||
# Build from local source.
|
||||
$(MYGOBIN)/prchecker:
|
||||
cd cmd/prchecker; \
|
||||
go install .
|
||||
|
||||
# --- Build targets ---
|
||||
|
||||
# Build from local source.
|
||||
$(MYGOBIN)/kustomize: build-kustomize-api
|
||||
cd kustomize; \
|
||||
go install .
|
||||
go install -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')" \
|
||||
.
|
||||
|
||||
.PHONY: install-tools
|
||||
install-tools: \
|
||||
$(MYGOBIN)/goimports \
|
||||
$(MYGOBIN)/golangci-lint-kustomize \
|
||||
$(MYGOBIN)/gorepomod \
|
||||
$(MYGOBIN)/helmV3 \
|
||||
$(MYGOBIN)/k8scopy \
|
||||
$(MYGOBIN)/mdrip \
|
||||
$(MYGOBIN)/pluginator \
|
||||
$(MYGOBIN)/prchecker \
|
||||
$(MYGOBIN)/stringer
|
||||
|
||||
### Begin kustomize plugin rules.
|
||||
#
|
||||
# The rules to deal with builtin plugins are a bit
|
||||
# complicated because
|
||||
#
|
||||
# - Every builtin plugin is a Go plugin -
|
||||
# meaning it gets its own module directory
|
||||
# (outside of the api module) with Go
|
||||
# code in a 'main' package per Go plugin rules.
|
||||
# - kustomize locates plugins using the
|
||||
# 'apiVersion' and 'kind' fields from the
|
||||
# plugin config file.
|
||||
# - k8s wants CamelCase in 'kind' fields.
|
||||
# - The module name (the last name in the path)
|
||||
# must be the lowercased 'kind' of the
|
||||
# plugin because Go and related tools
|
||||
# demand lowercase in import paths, but
|
||||
# allow CamelCase in file names.
|
||||
# - the generated code must live in the api
|
||||
# module (it's linked into the api).
|
||||
|
||||
# Where all generated builtin plugin code should go.
|
||||
pGen=api/builtins
|
||||
# Where the builtin Go plugin modules live.
|
||||
pSrc=plugin/builtin
|
||||
|
||||
_builtinplugins = \
|
||||
AnnotationsTransformer.go \
|
||||
ConfigMapGenerator.go \
|
||||
HashTransformer.go \
|
||||
ImageTagTransformer.go \
|
||||
LabelTransformer.go \
|
||||
LegacyOrderTransformer.go \
|
||||
NamespaceTransformer.go \
|
||||
PatchJson6902Transformer.go \
|
||||
PatchStrategicMergeTransformer.go \
|
||||
PatchTransformer.go \
|
||||
PrefixSuffixTransformer.go \
|
||||
ReplicaCountTransformer.go \
|
||||
SecretGenerator.go \
|
||||
ValueAddTransformer.go \
|
||||
HelmChartInflationGenerator.go
|
||||
|
||||
# Maintaining this explicit list of generated files, and
|
||||
# adding it as a dependency to a few targets, to assure
|
||||
# they get recreated if deleted. The rules below on how
|
||||
# to make them don't, by themselves, assure they will be
|
||||
# recreated if deleted.
|
||||
builtinplugins = $(patsubst %,$(pGen)/%,$(_builtinplugins))
|
||||
|
||||
# These rules are verbose, but assure that if a source file
|
||||
# is modified, the corresponding generated file, and only
|
||||
# that file, will be recreated.
|
||||
$(pGen)/AnnotationsTransformer.go: $(pSrc)/annotationstransformer/AnnotationsTransformer.go
|
||||
$(pGen)/ConfigMapGenerator.go: $(pSrc)/configmapgenerator/ConfigMapGenerator.go
|
||||
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
|
||||
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
|
||||
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
|
||||
$(pGen)/LegacyOrderTransformer.go: $(pSrc)/legacyordertransformer/LegacyOrderTransformer.go
|
||||
$(pGen)/NamespaceTransformer.go: $(pSrc)/namespacetransformer/NamespaceTransformer.go
|
||||
$(pGen)/PatchJson6902Transformer.go: $(pSrc)/patchjson6902transformer/PatchJson6902Transformer.go
|
||||
$(pGen)/PatchStrategicMergeTransformer.go: $(pSrc)/patchstrategicmergetransformer/PatchStrategicMergeTransformer.go
|
||||
$(pGen)/PatchTransformer.go: $(pSrc)/patchtransformer/PatchTransformer.go
|
||||
$(pGen)/PrefixSuffixTransformer.go: $(pSrc)/prefixsuffixtransformer/PrefixSuffixTransformer.go
|
||||
$(pGen)/ReplicaCountTransformer.go: $(pSrc)/replicacounttransformer/ReplicaCountTransformer.go
|
||||
$(pGen)/SecretGenerator.go: $(pSrc)/secretgenerator/SecretGenerator.go
|
||||
$(pGen)/ValueAddTransformer.go: $(pSrc)/valueaddtransformer/ValueAddTransformer.go
|
||||
$(pGen)/HelmChartInflationGenerator.go: $(pSrc)/helmchartinflationgenerator/HelmChartInflationGenerator.go
|
||||
|
||||
# The (verbose but portable) Makefile way to convert to lowercase.
|
||||
toLowerCase = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
|
||||
|
||||
$(pGen)/%.go: $(MYGOBIN)/pluginator
|
||||
@echo "generating $*"
|
||||
( \
|
||||
set -e; \
|
||||
cd $(pSrc)/$(call toLowerCase,$*); \
|
||||
go generate .; \
|
||||
cd ../../../$(pGen); \
|
||||
$(MYGOBIN)/goimports -w $*.go \
|
||||
)
|
||||
|
||||
# Target is for debugging.
|
||||
.PHONY: generate-kustomize-builtin-plugins
|
||||
generate-kustomize-builtin-plugins: $(builtinplugins)
|
||||
|
||||
.PHONY: build-kustomize-external-go-plugin
|
||||
build-kustomize-external-go-plugin:
|
||||
./hack/buildExternalGoPlugins.sh ./plugin
|
||||
|
||||
.PHONY: clean-kustomize-external-go-plugin
|
||||
clean-kustomize-external-go-plugin:
|
||||
./hack/buildExternalGoPlugins.sh ./plugin clean
|
||||
|
||||
### End kustomize plugin rules.
|
||||
|
||||
.PHONY: lint-kustomize
|
||||
lint-kustomize: install-tools $(builtinplugins)
|
||||
cd api; $(MYGOBIN)/golangci-lint-kustomize \
|
||||
-c ../.golangci-kustomize.yml \
|
||||
run ./...
|
||||
cd kustomize; $(MYGOBIN)/golangci-lint-kustomize \
|
||||
-c ../.golangci-kustomize.yml \
|
||||
run ./...
|
||||
cd cmd/pluginator; $(MYGOBIN)/golangci-lint-kustomize \
|
||||
-c ../../.golangci-kustomize.yml \
|
||||
run ./...
|
||||
kustomize: $(MYGOBIN)/kustomize
|
||||
|
||||
# Used to add non-default compilation flags when experimenting with
|
||||
# plugin-to-api compatibility checks.
|
||||
.PHONY: build-kustomize-api
|
||||
build-kustomize-api: $(builtinplugins)
|
||||
cd api; go build ./...
|
||||
build-kustomize-api: $(MYGOBIN)/goimports $(builtinplugins)
|
||||
cd api; $(MAKE) build
|
||||
|
||||
.PHONY: generate-kustomize-api
|
||||
generate-kustomize-api: $(MYGOBIN)/k8scopy
|
||||
cd api; go generate ./...
|
||||
generate-kustomize-api:
|
||||
cd api; $(MAKE) generate
|
||||
|
||||
.PHONY: test-unit-kustomize-api
|
||||
test-unit-kustomize-api: build-kustomize-api
|
||||
cd api; go test ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222"
|
||||
|
||||
# --- Verification targets ---
|
||||
.PHONY: verify-kustomize-repo
|
||||
verify-kustomize-repo: \
|
||||
install-tools \
|
||||
lint \
|
||||
check-license \
|
||||
test-unit-all \
|
||||
build-non-plugin-all \
|
||||
test-go-mod \
|
||||
test-examples-kustomize-against-HEAD \
|
||||
test-examples-kustomize-against-v4-release
|
||||
|
||||
# The following target referenced by a file in
|
||||
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
|
||||
.PHONY: prow-presubmit-check
|
||||
prow-presubmit-check: \
|
||||
install-tools \
|
||||
test-unit-kustomize-plugins \
|
||||
test-go-mod \
|
||||
build-non-plugin-all \
|
||||
test-examples-kustomize-against-HEAD \
|
||||
test-examples-kustomize-against-v4-release
|
||||
|
||||
.PHONY: license
|
||||
license: $(MYGOBIN)/addlicense
|
||||
./hack/add-license.sh run
|
||||
|
||||
.PHONY: check-license
|
||||
check-license: $(MYGOBIN)/addlicense
|
||||
./hack/add-license.sh check
|
||||
|
||||
.PHONY: lint
|
||||
lint: $(MYGOBIN)/golangci-lint $(MYGOBIN)/goimports $(builtinplugins)
|
||||
./hack/for-each-module.sh "make lint"
|
||||
|
||||
.PHONY: test-unit-all
|
||||
test-unit-all: \
|
||||
test-unit-non-plugin \
|
||||
test-unit-kustomize-plugins
|
||||
|
||||
# This target is used by our Github Actions CI to run unit tests for all non-plugin modules in multiple GOOS environments.
|
||||
.PHONY: test-unit-non-plugin
|
||||
test-unit-non-plugin:
|
||||
./hack/for-each-module.sh "make test" "./plugin/*" 15
|
||||
|
||||
.PHONY: build-non-plugin-all
|
||||
build-non-plugin-all:
|
||||
./hack/for-each-module.sh "make build" "./plugin/*" 15
|
||||
|
||||
.PHONY: test-unit-kustomize-plugins
|
||||
test-unit-kustomize-plugins:
|
||||
./hack/testUnitKustomizePlugins.sh
|
||||
|
||||
.PHONY: test-unit-kustomize-cli
|
||||
test-unit-kustomize-cli:
|
||||
cd kustomize; go test ./...
|
||||
|
||||
.PHONY: test-unit-kustomize-all
|
||||
test-unit-kustomize-all: \
|
||||
test-unit-kustomize-api \
|
||||
test-unit-kustomize-cli \
|
||||
test-unit-kustomize-plugins
|
||||
|
||||
test-unit-cmd-all:
|
||||
./scripts/kyaml-pre-commit.sh
|
||||
.PHONY: functions-examples-all
|
||||
functions-examples-all:
|
||||
for dir in $(abspath $(wildcard functions/examples/*/.)); do \
|
||||
echo -e "\n---Running make tasks for function $$dir---"; \
|
||||
set -e; \
|
||||
cd $$dir; $(MAKE) all; \
|
||||
done
|
||||
|
||||
test-go-mod:
|
||||
./scripts/check-go-mod.sh
|
||||
|
||||
# Environment variables are defined at
|
||||
# https://github.com/kubernetes/test-infra/blob/master/prow/jobs.md#job-environment-variables
|
||||
.PHONY: test-multi-module
|
||||
test-multi-module: $(MYGOBIN)/prchecker
|
||||
( \
|
||||
export MYGOBIN=$(MYGOBIN); \
|
||||
export REPO_OWNER=$(REPO_OWNER); \
|
||||
export REPO_NAME=$(REPO_NAME); \
|
||||
export PULL_NUMBER=$(PULL_NUMBER); \
|
||||
export MODULES=$(MODULES); \
|
||||
./scripts/check-multi-module.sh; \
|
||||
)
|
||||
./hack/for-each-module.sh "\$$KUSTOMIZE_ROOT/hack/with-unpinned-kust-dev.sh 'go mod tidy -v'"
|
||||
|
||||
.PHONY:
|
||||
test-examples-e2e-kustomize: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
|
||||
verify-kustomize-e2e: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
|
||||
( \
|
||||
set -e; \
|
||||
/bin/rm -f $(MYGOBIN)/kustomize; \
|
||||
@@ -282,85 +170,16 @@ test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
|
||||
./hack/testExamplesAgainstKustomize.sh HEAD
|
||||
|
||||
.PHONY:
|
||||
test-examples-kustomize-against-4.0: $(MYGOBIN)/mdrip
|
||||
./hack/testExamplesAgainstKustomize.sh v4@v4.0.5
|
||||
test-examples-kustomize-against-v4-release: $(MYGOBIN)/mdrip
|
||||
./hack/testExamplesAgainstKustomize.sh v4@$(LATEST_V4_RELEASE)
|
||||
|
||||
# linux only.
|
||||
# This is for testing an example plugin that
|
||||
# uses kubeval for validation.
|
||||
# Don't want to add a hard dependence in go.mod file
|
||||
# to github.com/instrumenta/kubeval.
|
||||
# Instead, download the binary.
|
||||
$(MYGOBIN)/kubeval:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz; \
|
||||
tar xf kubeval-linux-amd64.tar.gz; \
|
||||
mv kubeval $(MYGOBIN); \
|
||||
rm -rf $$d; \
|
||||
)
|
||||
|
||||
# linux only.
|
||||
# This is for testing an example plugin that uses helm to inflate a chart
|
||||
# for subsequent kustomization.
|
||||
# Don't want to add a hard dependence in go.mod file to helm.
|
||||
# Instead, download the binaries.
|
||||
$(MYGOBIN)/helmV2:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
tgzFile=helm-v2.13.1-linux-amd64.tar.gz; \
|
||||
wget https://storage.googleapis.com/kubernetes-helm/$$tgzFile; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv linux-amd64/helm $(MYGOBIN)/helmV2; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
|
||||
# Helm V3 differs from helm V2; downloading it to provide coverage for the
|
||||
# chart inflator plugin under helm v3.
|
||||
$(MYGOBIN)/helmV3:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
tgzFile=helm-v3.5.3-linux-amd64.tar.gz; \
|
||||
wget https://get.helm.sh/$$tgzFile; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv linux-amd64/helm $(MYGOBIN)/helmV3; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
|
||||
$(MYGOBIN)/kind:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
wget -O ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(shell uname)-amd64; \
|
||||
chmod +x ./kind; \
|
||||
mv ./kind $(MYGOBIN); \
|
||||
rm -rf $$d; \
|
||||
)
|
||||
|
||||
# linux only.
|
||||
$(MYGOBIN)/gh:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
tgzFile=gh_1.0.0_linux_amd64.tar.gz; \
|
||||
wget https://github.com/cli/cli/releases/download/v1.0.0/$$tgzFile; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv gh_1.0.0_linux_amd64/bin/gh $(MYGOBIN)/gh; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
|
||||
# --- Cleanup targets ---
|
||||
.PHONY: clean
|
||||
clean: clean-kustomize-external-go-plugin
|
||||
clean: clean-kustomize-external-go-plugin uninstall-tools
|
||||
go clean --cache
|
||||
rm -f $(builtinplugins)
|
||||
rm -f $(MYGOBIN)/kustomize
|
||||
rm -f $(MYGOBIN)/golangci-lint-kustomize
|
||||
|
||||
# Handle pluginator manually.
|
||||
# rm -f $(MYGOBIN)/pluginator
|
||||
|
||||
# Nuke the site from orbit. It's the only way to be sure.
|
||||
.PHONY: nuke
|
||||
|
||||
38
Makefile-modules.mk
Normal file
38
Makefile-modules.mk
Normal file
@@ -0,0 +1,38 @@
|
||||
# Copyright 2022 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
MYGOBIN = $(shell go env GOBIN)
|
||||
ifeq ($(MYGOBIN),)
|
||||
MYGOBIN = $(shell go env GOPATH)/bin
|
||||
endif
|
||||
export PATH := $(MYGOBIN):$(PATH)
|
||||
|
||||
# only set this if not already set, so importing makefiles can override it
|
||||
export KUSTOMIZE_ROOT ?= $(shell pwd | sed -E 's|(.*\/kustomize)/(.*)|\1|')
|
||||
include $(KUSTOMIZE_ROOT)/Makefile-tools.mk
|
||||
|
||||
.PHONY: lint test fix fmt tidy vet build
|
||||
|
||||
lint: $(MYGOBIN)/golangci-lint
|
||||
$(MYGOBIN)/golangci-lint \
|
||||
-c $$KUSTOMIZE_ROOT/.golangci.yml \
|
||||
--path-prefix $(shell pwd | sed -E 's|(.*\/kustomize)/(.*)|\2|') \
|
||||
run ./...
|
||||
|
||||
test:
|
||||
go test -v -timeout 45m -cover ./...
|
||||
|
||||
fix:
|
||||
go fix ./...
|
||||
|
||||
fmt:
|
||||
go fmt ./...
|
||||
|
||||
tidy:
|
||||
go mod tidy
|
||||
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
build:
|
||||
go build -v -o $(MYGOBIN) ./...
|
||||
102
Makefile-plugins.mk
Normal file
102
Makefile-plugins.mk
Normal file
@@ -0,0 +1,102 @@
|
||||
# Copyright 2022 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
### Kustomize plugin rules.
|
||||
#
|
||||
# The rules to deal with builtin plugins are a bit
|
||||
# complicated because
|
||||
#
|
||||
# - Every builtin plugin is a Go plugin -
|
||||
# meaning it gets its own module directory
|
||||
# (outside of the api module) with Go
|
||||
# code in a 'main' package per Go plugin rules.
|
||||
# - kustomize locates plugins using the
|
||||
# 'apiVersion' and 'kind' fields from the
|
||||
# plugin config file.
|
||||
# - k8s wants CamelCase in 'kind' fields.
|
||||
# - The module name (the last name in the path)
|
||||
# must be the lowercased 'kind' of the
|
||||
# plugin because Go and related tools
|
||||
# demand lowercase in import paths, but
|
||||
# allow CamelCase in file names.
|
||||
# - the generated code must live in the api
|
||||
# module (it's linked into the api).
|
||||
|
||||
# Where all generated builtin plugin code should go.
|
||||
pGen=api/internal/builtins
|
||||
# Where the builtin Go plugin modules live.
|
||||
pSrc=plugin/builtin
|
||||
|
||||
_builtinplugins = \
|
||||
AnnotationsTransformer.go \
|
||||
ConfigMapGenerator.go \
|
||||
IAMPolicyGenerator.go \
|
||||
HashTransformer.go \
|
||||
ImageTagTransformer.go \
|
||||
LabelTransformer.go \
|
||||
SortOrderTransformer.go \
|
||||
NamespaceTransformer.go \
|
||||
PatchJson6902Transformer.go \
|
||||
PatchStrategicMergeTransformer.go \
|
||||
PatchTransformer.go \
|
||||
PrefixTransformer.go \
|
||||
SuffixTransformer.go \
|
||||
ReplacementTransformer.go \
|
||||
ReplicaCountTransformer.go \
|
||||
SecretGenerator.go \
|
||||
ValueAddTransformer.go \
|
||||
HelmChartInflationGenerator.go
|
||||
|
||||
# Maintaining this explicit list of generated files, and
|
||||
# adding it as a dependency to a few targets, to assure
|
||||
# they get recreated if deleted. The rules below on how
|
||||
# to make them don't, by themselves, assure they will be
|
||||
# recreated if deleted.
|
||||
builtinplugins = $(patsubst %,$(pGen)/%,$(_builtinplugins))
|
||||
|
||||
# These rules are verbose, but assure that if a source file
|
||||
# is modified, the corresponding generated file, and only
|
||||
# that file, will be recreated.
|
||||
$(pGen)/AnnotationsTransformer.go: $(pSrc)/annotationstransformer/AnnotationsTransformer.go
|
||||
$(pGen)/ConfigMapGenerator.go: $(pSrc)/configmapgenerator/ConfigMapGenerator.go
|
||||
$(pGen)/GkeSaGenerator.go: $(pSrc)/gkesagenerator/GkeSaGenerator.go
|
||||
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
|
||||
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
|
||||
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
|
||||
$(pGen)/SortOrderTransformer.go: $(pSrc)/sortordertransformer/SortOrderTransformer.go
|
||||
$(pGen)/NamespaceTransformer.go: $(pSrc)/namespacetransformer/NamespaceTransformer.go
|
||||
$(pGen)/PatchJson6902Transformer.go: $(pSrc)/patchjson6902transformer/PatchJson6902Transformer.go
|
||||
$(pGen)/PatchStrategicMergeTransformer.go: $(pSrc)/patchstrategicmergetransformer/PatchStrategicMergeTransformer.go
|
||||
$(pGen)/PatchTransformer.go: $(pSrc)/patchtransformer/PatchTransformer.go
|
||||
$(pGen)/PrefixTransformer.go: $(pSrc)/prefixtransformer/PrefixTransformer.go
|
||||
$(pGen)/SuffixTransformer.go: $(pSrc)/suffixtransformer/SuffixTransformer.go
|
||||
$(pGen)/ReplacementTransformer.go: $(pSrc)/replacementtransformer/ReplacementTransformer.go
|
||||
$(pGen)/ReplicaCountTransformer.go: $(pSrc)/replicacounttransformer/ReplicaCountTransformer.go
|
||||
$(pGen)/SecretGenerator.go: $(pSrc)/secretgenerator/SecretGenerator.go
|
||||
$(pGen)/ValueAddTransformer.go: $(pSrc)/valueaddtransformer/ValueAddTransformer.go
|
||||
$(pGen)/HelmChartInflationGenerator.go: $(pSrc)/helmchartinflationgenerator/HelmChartInflationGenerator.go
|
||||
|
||||
# The (verbose but portable) Makefile way to convert to lowercase.
|
||||
toLowerCase = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
|
||||
|
||||
$(pGen)/%.go: $(MYGOBIN)/pluginator $(MYGOBIN)/goimports
|
||||
@echo "generating $*"
|
||||
( \
|
||||
set -e; \
|
||||
cd $(pSrc)/$(call toLowerCase,$*); \
|
||||
go generate .; \
|
||||
cd ../../../$(pGen); \
|
||||
$(MYGOBIN)/goimports -w $*.go \
|
||||
)
|
||||
|
||||
# Target is for debugging.
|
||||
.PHONY: generate-kustomize-builtin-plugins
|
||||
generate-kustomize-builtin-plugins: $(builtinplugins)
|
||||
|
||||
.PHONY: build-kustomize-external-go-plugin
|
||||
build-kustomize-external-go-plugin:
|
||||
./hack/buildExternalGoPlugins.sh ./plugin
|
||||
|
||||
.PHONY: clean-kustomize-external-go-plugin
|
||||
clean-kustomize-external-go-plugin:
|
||||
./hack/buildExternalGoPlugins.sh ./plugin clean
|
||||
102
Makefile-tools.mk
Normal file
102
Makefile-tools.mk
Normal file
@@ -0,0 +1,102 @@
|
||||
# Copyright 2022 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
GOLANGCI_LINT_VERSION=v1.50.1
|
||||
|
||||
MYGOBIN = $(shell go env GOBIN)
|
||||
ifeq ($(MYGOBIN),)
|
||||
MYGOBIN = $(shell go env GOPATH)/bin
|
||||
endif
|
||||
export PATH := $(MYGOBIN):$(PATH)
|
||||
|
||||
# determines whether to run tests that only behave locally; can be overridden by override variable
|
||||
export IS_LOCAL = false
|
||||
|
||||
.PHONY: install-out-of-tree-tools
|
||||
install-out-of-tree-tools: \
|
||||
$(MYGOBIN)/goimports \
|
||||
$(MYGOBIN)/golangci-lint \
|
||||
$(MYGOBIN)/helmV3 \
|
||||
$(MYGOBIN)/mdrip \
|
||||
$(MYGOBIN)/stringer \
|
||||
$(MYGOBIN)/goimports
|
||||
|
||||
.PHONY: uninstall-out-of-tree-tools
|
||||
uninstall-out-of-tree-tools:
|
||||
rm -f $(MYGOBIN)/goimports
|
||||
rm -f $(MYGOBIN)/golangci-lint
|
||||
rm -f $(MYGOBIN)/helmV3
|
||||
rm -f $(MYGOBIN)/mdrip
|
||||
rm -f $(MYGOBIN)/stringer
|
||||
|
||||
$(MYGOBIN)/golangci-lint:
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
|
||||
|
||||
$(MYGOBIN)/mdrip:
|
||||
go install github.com/monopole/mdrip@v1.0.2
|
||||
|
||||
$(MYGOBIN)/stringer:
|
||||
go install golang.org/x/tools/cmd/stringer@latest
|
||||
|
||||
$(MYGOBIN)/goimports:
|
||||
go install golang.org/x/tools/cmd/goimports@latest
|
||||
|
||||
$(MYGOBIN)/mdtogo:
|
||||
go install sigs.k8s.io/kustomize/cmd/mdtogo@latest
|
||||
|
||||
$(MYGOBIN)/addlicense:
|
||||
go install github.com/google/addlicense@latest
|
||||
|
||||
$(MYGOBIN)/goreleaser:
|
||||
go install github.com/goreleaser/goreleaser@v0.179.0 # https://github.com/kubernetes-sigs/kustomize/issues/4542
|
||||
|
||||
$(MYGOBIN)/kind:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
wget -O ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(GOOS)-$(GOARCH); \
|
||||
chmod +x ./kind; \
|
||||
mv ./kind $(MYGOBIN); \
|
||||
rm -rf $$d; \
|
||||
)
|
||||
|
||||
# linux only.
|
||||
$(MYGOBIN)/gh:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
tgzFile=gh_1.0.0_$(GOOS)_$(GOARCH).tar.gz; \
|
||||
wget https://github.com/cli/cli/releases/download/v1.0.0/$$tgzFile; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv gh_1.0.0_$(GOOS)_$(GOARCH)/bin/gh $(MYGOBIN)/gh; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
|
||||
# linux only.
|
||||
# This is for testing an example plugin that
|
||||
# uses kubeval for validation.
|
||||
# Don't want to add a hard dependence in go.mod file
|
||||
# to github.com/instrumenta/kubeval.
|
||||
# Instead, download the binary.
|
||||
$(MYGOBIN)/kubeval:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-$(GOOS)-$(GOARCH).tar.gz; \
|
||||
tar xf kubeval-$(GOOS)-$(GOARCH).tar.gz; \
|
||||
mv kubeval $(MYGOBIN); \
|
||||
rm -rf $$d; \
|
||||
)
|
||||
|
||||
# Helm V3 differs from helm V2; downloading it to provide coverage for the
|
||||
# chart inflator plugin under helm v3.
|
||||
$(MYGOBIN)/helmV3:
|
||||
( \
|
||||
set -e; \
|
||||
d=$(shell mktemp -d); cd $$d; \
|
||||
tgzFile=helm-v3.10.2-$(GOOS)-$(GOARCH).tar.gz; \
|
||||
wget https://get.helm.sh/$$tgzFile; \
|
||||
tar -xvzf $$tgzFile; \
|
||||
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \
|
||||
rm -rf $$d \
|
||||
)
|
||||
6
OWNERS
6
OWNERS
@@ -1,4 +1,6 @@
|
||||
# See https://github.com/kubernetes/community/blob/master/community-membership.md
|
||||
approvers:
|
||||
- kustomize-admins
|
||||
- kustomize-maintainers
|
||||
- kustomize-approvers
|
||||
|
||||
reviewers:
|
||||
- kustomize-reviewers
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
# Keep *-owners and *-approvers lists in sync with *-admins and *-maintainers in
|
||||
# https://github.com/kubernetes/org/blob/main/config/kubernetes-sigs/sig-cli/teams.yaml
|
||||
aliases:
|
||||
kustomize-admins:
|
||||
- monopole
|
||||
- pwittrock
|
||||
kustomize-maintainers:
|
||||
- droot
|
||||
- justinsb
|
||||
- liujingfang1
|
||||
kustomize-owners:
|
||||
- knverey
|
||||
- natasha41575
|
||||
kustomize-approvers:
|
||||
- knverey
|
||||
- natasha41575
|
||||
kustomize-reviewers:
|
||||
- knverey
|
||||
- natasha41575
|
||||
- yuwenma
|
||||
- annasong20
|
||||
- koba1t
|
||||
|
||||
kyaml-approvers:
|
||||
- mengqiy
|
||||
- monopole
|
||||
- pwittrock
|
||||
- mortent
|
||||
- phanimarupaka
|
||||
kyaml-reviewers:
|
||||
- mengqiy
|
||||
- mortent
|
||||
- phanimarupaka
|
||||
|
||||
emeritus-approvers:
|
||||
- liujingfang1
|
||||
- Shell32-Natsu
|
||||
- natasha41575
|
||||
- justinsb
|
||||
- monopole
|
||||
- pwittrock
|
||||
|
||||
63
README.md
63
README.md
@@ -11,8 +11,8 @@ and it's like [`sed`], in that it emits edited text.
|
||||
|
||||
This tool is sponsored by [sig-cli] ([KEP]).
|
||||
|
||||
- [Installation instructions](https://kubernetes-sigs.github.io/kustomize/installation)
|
||||
- [General documentation](https://kubernetes-sigs.github.io/kustomize)
|
||||
- [Installation instructions](https://kubectl.docs.kubernetes.io/installation/kustomize/)
|
||||
- [General documentation](https://kubectl.docs.kubernetes.io/references/kustomize/)
|
||||
- [Examples](examples)
|
||||
|
||||
[](https://prow.k8s.io/job-history/kubernetes-jenkins/pr-logs/directory/kustomize-presubmit-master)
|
||||
@@ -22,18 +22,25 @@ This tool is sponsored by [sig-cli] ([KEP]).
|
||||
|
||||
The kustomize build flow at [v2.0.3] was added
|
||||
to [kubectl v1.14][kubectl announcement]. The kustomize
|
||||
flow in kubectl has remained frozen at v2.0.3 while work
|
||||
to extract kubectl from the k/k repo, and work to remove
|
||||
kustomize's dependence on core k/k code ([#2506]) has proceeded.
|
||||
The reintegration effort is tracked in [#1500] (and its blocking
|
||||
issues).
|
||||
flow in kubectl remained frozen at v2.0.3 until kubectl v1.21,
|
||||
which [updated it to v4.0.5][kust-in-kubectl update]. It will
|
||||
be updated on a regular basis going forward, and such updates
|
||||
will be reflected in the Kubernetes release notes.
|
||||
|
||||
[v2.0.3]: /../../tree/v2.0.3
|
||||
| Kubectl version | Kustomize version |
|
||||
| --- | --- |
|
||||
| < v1.14 | n/a |
|
||||
| v1.14-v1.20 | v2.0.3 |
|
||||
| v1.21 | v4.0.5 |
|
||||
| v1.22 | v4.2.0 |
|
||||
|
||||
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
|
||||
[#2506]: https://github.com/kubernetes-sigs/kustomize/issues/2506
|
||||
[#1500]: https://github.com/kubernetes-sigs/kustomize/issues/1500
|
||||
[kust-in-kubectl update]: https://github.com/kubernetes/kubernetes/blob/4d75a6238a6e330337526e0513e67d02b1940b63/CHANGELOG/CHANGELOG-1.21.md#kustomize-updates-in-kubectl
|
||||
|
||||
For examples and guides for using the kubectl integration please
|
||||
see the [kubectl book] or the [kubernetes documentation].
|
||||
see the [kubernetes documentation].
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -134,8 +141,9 @@ The YAML can be directly [applied] to a cluster:
|
||||
|
||||
## Community
|
||||
|
||||
- [file a bug](https://kubernetes-sigs.github.io/kustomize/contributing/bugs/) instructions
|
||||
- [contribute a feature](https://kubernetes-sigs.github.io/kustomize/contributing/features/) instructions
|
||||
- [file a bug](https://kubectl.docs.kubernetes.io/contributing/kustomize/bugs/)
|
||||
- [contribute a feature](https://kubectl.docs.kubernetes.io/contributing/kustomize/features/)
|
||||
- [propose a larger enhancement](https://github.com/kubernetes-sigs/kustomize/tree/master/proposals)
|
||||
|
||||
### Code of conduct
|
||||
|
||||
@@ -144,27 +152,22 @@ is governed by the [Kubernetes Code of Conduct].
|
||||
|
||||
[`make`]: https://www.gnu.org/software/make
|
||||
[`sed`]: https://www.gnu.org/software/sed
|
||||
[DAM]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#declarative-application-management
|
||||
[KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/0008-kustomize.md
|
||||
[DAM]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#declarative-application-management
|
||||
[KEP]: https://github.com/kubernetes/enhancements/blob/master/keps/sig-cli/2377-Kustomize/README.md
|
||||
[Kubernetes Code of Conduct]: code-of-conduct.md
|
||||
[applied]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#apply
|
||||
[base]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#base
|
||||
[declarative configuration]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#declarative-application-management
|
||||
[imageBase]: docs/images/base.jpg
|
||||
[imageOverlay]: docs/images/overlay.jpg
|
||||
[applied]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#apply
|
||||
[base]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#base
|
||||
[declarative configuration]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#declarative-application-management
|
||||
[imageBase]: images/base.jpg
|
||||
[imageOverlay]: images/overlay.jpg
|
||||
[kubectl announcement]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
|
||||
[kubectl book]: https://kubectl.docs.kubernetes.io/guides/introduction/kustomize/
|
||||
[kubernetes documentation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
|
||||
[kubernetes style]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#kubernetes-style-object
|
||||
[kustomization]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#kustomization
|
||||
[overlay]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#overlay
|
||||
[overlays]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#overlay
|
||||
[kubernetes style]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#kubernetes-style-object
|
||||
[kustomization]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#kustomization
|
||||
[overlay]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#overlay
|
||||
[overlays]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#overlay
|
||||
[release page]: https://github.com/kubernetes-sigs/kustomize/releases
|
||||
[resource]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#resource
|
||||
[resources]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#resource
|
||||
[resource]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#resource
|
||||
[resources]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#resource
|
||||
[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md
|
||||
[variant]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#variant
|
||||
[variants]: https://kubernetes-sigs.github.io/kustomize/api-reference/glossary#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]: https://kubernetes-sigs.github.io/kustomize/guides
|
||||
[variants]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#variant
|
||||
|
||||
112
ROADMAP.md
Normal file
112
ROADMAP.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Kustomize roadmap 2022
|
||||
|
||||
Presented at the [January 26, 2022, SIG-CLI meeting](https://youtu.be/l2plzJ9MRlk?t=1321)
|
||||
|
||||
kustomize maintainers: @knverey, @natasha41575
|
||||
|
||||
[Objective: Improve contributor community](#objective-improve-contributor-community)
|
||||
|
||||
[Objective: Improve end-user experience](#objective-improve-end-user-experience)
|
||||
|
||||
[Objective: Improve extension experience](#objective-improve-extension-experience)
|
||||
|
||||
## Objective: Improve contributor community
|
||||
|
||||
**_WHO: End user who also contributes source code._**
|
||||
|
||||
Top priority:
|
||||
|
||||
- Kustomization v1 (also end-user impact) ([PROJECT](https://github.com/kubernetes-sigs/kustomize/projects/12))
|
||||
- Remove the following fields:
|
||||
- [vars](https://github.com/kubernetes-sigs/kustomize/issues/2052)
|
||||
- [patchesJson6902, patchesStrategicMerge (consolidate on \`patches)](https://github.com/kubernetes-sigs/kustomize/issues/4376)
|
||||
- [helmChartInflationGenerator, helmCharts, helmGlobals](https://github.com/kubernetes-sigs/kustomize/issues/4401)
|
||||
- all long-deprecated fields in Kustomization v1 such as \`bases\` and those being accommodate by kustomize edit \[[see code snippet](https://github.com/kubernetes-sigs/kustomize/blob/ee4b7847f0beb6c0d2070673b10f23f7b3e92e82/api/types/fix.go#L15)\]
|
||||
- Ensure that \`kustomize edit fix\` handles migrations for all those, and that anything it changes is not still present in v1.
|
||||
- [Add reorder field](https://github.com/kubernetes-sigs/kustomize/issues/3913). Default should be FIFO and legacy should also be supported (could add alphabetic and custom sort support eventually). Replaces -reorder flag.
|
||||
- [Reconcile openapi and crds field](https://github.com/kubernetes-sigs/kustomize/issues/3944)
|
||||
- [Consider deprecating configurations field](https://github.com/kubernetes-sigs/kustomize/issues/3945) (old, pre-plugin, pre-openapi global configuration)
|
||||
- [Add a field to enable the managedby label](https://github.com/kubernetes-sigs/kustomize/issues/4047)
|
||||
|
||||
Second priority:
|
||||
|
||||
- Improve contributor documentation
|
||||
- [Instructions to upgrade kustomize-in-kubectl](https://github.com/kubernetes-sigs/kustomize/issues/3951)
|
||||
|
||||
Also very valuable to the project:
|
||||
|
||||
- [Improve the release process](https://github.com/kubernetes-sigs/kustomize/issues/3952) to support regular biweekly releases [PROJECT](https://github.com/kubernetes-sigs/kustomize/projects/7)
|
||||
- Release sigs.k8s.io/kustomize/api v1.0.0 [PROJECT](https://github.com/kubernetes-sigs/kustomize/projects/5)
|
||||
- [Reduce the public surface of the API module](https://github.com/kubernetes-sigs/kustomize/issues/3942)
|
||||
- [Vendor all transitive deps](https://github.com/kubernetes-sigs/kustomize/issues/3706). Since kustomize is in kubectl, we must do as kubectl does to manage deps, exposing new transitive deps in code review.
|
||||
- Project administration
|
||||
- [Rename master branch to main](https://github.com/kubernetes-sigs/kustomize/issues/3954)
|
||||
|
||||
|
||||
|
||||
## Objective: Improve end-user experience
|
||||
|
||||
**_WHO: End user that wants kustomize build artifacts (binaries, containers)._**
|
||||
|
||||
Top priorities:
|
||||
|
||||
- Bug fixes:
|
||||
- Fix bugs in basic anchor support: [issue query](https://github.com/kubernetes-sigs/kustomize/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Fanchors)
|
||||
- integer keys support: [#3446](https://github.com/kubernetes-sigs/kustomize/issues/3446)
|
||||
- kyaml not respecting \`$patch replace|retainKeys\`: [#2037](https://github.com/kubernetes-sigs/kustomize/issues/2037)
|
||||
- kustomize removing quotes from namespace field values: [#4146](https://github.com/kubernetes-sigs/kustomize/issues/4146)
|
||||
- Kustomize doesn’t support metadata.generateName: [#641](https://github.com/kubernetes-sigs/kustomize/issues/641)
|
||||
- Send kustomize CLI version number into kubectl ([kubectl issue](https://github.com/kubernetes/kubectl/issues/797) / [kustomize issue](https://github.com/kubernetes-sigs/kustomize/issues/1424))
|
||||
- Kustomize performance investigations/improvements [PROJECT](https://github.com/kubernetes-sigs/kustomize/projects/13)
|
||||
- [Support generic resource references in name reference tracking](https://github.com/kubernetes-sigs/kustomize/issues/3418)
|
||||
- [KEP 4267: retain the resource origin and transformer data in annotations](https://github.com/kubernetes-sigs/kustomize/pull/4267)
|
||||
|
||||
Secondary priorities:
|
||||
|
||||
- kustomize cli v5 ([PROJECT](https://github.com/kubernetes-sigs/kustomize/projects/14))
|
||||
- [Drop the --reorder flag](https://github.com/kubernetes-sigs/kustomize/issues/3947)
|
||||
- [Graduate cfg read-only commands out of alpha](https://github.com/kubernetes-sigs/kustomize/issues/4090).
|
||||
- [Drop the –enable-managedby-label](https://github.com/kubernetes-sigs/kustomize/issues/4047)
|
||||
- Drop old plugin-related fields in favor of [the Catalog-style fields](https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/2906-kustomize-function-catalog).
|
||||
- [Drop the helm flags](https://github.com/kubernetes-sigs/kustomize/issues/4401)
|
||||
- [Confusion around namespace replacement](https://github.com/kubernetes-sigs/kustomize/issues/880).
|
||||
|
||||
Also very valuable to the project:
|
||||
|
||||
- [Overinclusion of root directory error in error messages](https://github.com/kubernetes-sigs/kustomize/issues/4348)
|
||||
- [Add kustomize localize command](https://github.com/kubernetes-sigs/kustomize/issues/3980)
|
||||
- [Fix Windows support in test suite](https://github.com/kubernetes-sigs/kustomize/issues/4001)
|
||||
- Improve end-user documentation [PROJECT](https://github.com/kubernetes-sigs/kustomize/projects/9)
|
||||
|
||||
|
||||
## Objective: Improve extension experience
|
||||
|
||||
**_WHO: Plugin developers: end users who extend kustomize, but don’t think about internals._**
|
||||
|
||||
This objective is described in detail in the [Kustomize Plugin Graduation KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/2953-kustomize-plugin-graduation) / [PROJECT](https://github.com/kubernetes-sigs/kustomize/projects/15) .
|
||||
|
||||
Top priorities:
|
||||
|
||||
- Fix core usability issues with KRM Function extensions:
|
||||
- [Better errors for function config failures](https://github.com/kubernetes-sigs/kustomize/issues/4398)
|
||||
- [Container KRM Mounts are not mounting via function parameters](https://github.com/kubernetes-sigs/kustomize/issues/4290)
|
||||
- [Resolution of local file references in extensions transformer configuration](https://github.com/kubernetes-sigs/kustomize/issues/4154)
|
||||
- [Do not silently ignore plugins when config has typo](https://github.com/kubernetes-sigs/kustomize/issues/4399)
|
||||
- [KRM Exec Function can't locate executable when referencing a base](https://github.com/kubernetes-sigs/kustomize/issues/4347)
|
||||
- Once core usability issues are fixed, [deprecate legacy exec and Go plugin support](https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/2953-kustomize-plugin-graduation)
|
||||
- [Catalog KEP](https://github.com/kubernetes/enhancements/tree/master/keps/sig-cli/2906-kustomize-function-catalog)
|
||||
|
||||
Secondary priorities:
|
||||
|
||||
- [Remove Starlark support](https://github.com/kubernetes-sigs/kustomize/issues/4349)
|
||||
- [Composition KEP](https://github.com/kubernetes/enhancements/pull/2300). The implementation is complete in [#4223](https://github.com/kubernetes-sigs/kustomize/pull/4323), but depends on:
|
||||
- [Convert resources and components to be backed by a reusable generator](https://github.com/kubernetes-sigs/kustomize/issues/4402)
|
||||
- [Enable explicitly invoked transformers to use default fieldSpecs](https://github.com/kubernetes-sigs/kustomize/issues/4404)
|
||||
- [Enable built-in generators to be used in the transformers field ](https://github.com/kubernetes-sigs/kustomize/issues/4403)
|
||||
|
||||
|
||||
Also very valuable to the project:
|
||||
|
||||
- [Improve docs for kyaml libraries](https://github.com/kubernetes-sigs/kustomize/issues/3950), especially by adding examples.
|
||||
- [Create a reserved field for plugin runtime information](https://github.com/kubernetes-sigs/kustomize/issues/4405)
|
||||
- [Develop new standard process for implementing builtin transformers](https://github.com/kubernetes-sigs/kustomize/issues/4400)
|
||||
13
api/Makefile
Normal file
13
api/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
# Copyright 2022 The Kubernetes Authors.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
include ../Makefile-modules.mk
|
||||
|
||||
test:
|
||||
go test -v -timeout 45m -cover ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=2023-01-31T23:38:41Z -X sigs.k8s.io/kustomize/api/provenance.version=(test)"
|
||||
|
||||
build:
|
||||
go build -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")" ./...
|
||||
|
||||
generate: $(MYGOBIN)/k8scopy $(MYGOBIN)/stringer
|
||||
go generate ./...
|
||||
@@ -1,50 +0,0 @@
|
||||
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
|
||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/filters/imagetag"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"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(
|
||||
_ *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() {
|
||||
// traverse all fields at first
|
||||
err := r.ApplyFilter(imagetag.LegacyFilter{
|
||||
ImageTag: p.ImageTag,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// then use user specified field specs
|
||||
err = r.ApplyFilter(imagetag.Filter{
|
||||
ImageTag: p.ImageTag,
|
||||
FsSlice: p.FieldSpecs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewImageTagTransformerPlugin() resmap.TransformerPlugin {
|
||||
return &ImageTagTransformerPlugin{}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
// 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(
|
||||
_ *resmap.PluginHelpers, _ []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{}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// 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/filters/namespace"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// Change or set the namespace of non-cluster level resources.
|
||||
type NamespaceTransformerPlugin struct {
|
||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
}
|
||||
|
||||
func (p *NamespaceTransformerPlugin) Config(
|
||||
_ *resmap.PluginHelpers, c []byte) (err error) {
|
||||
p.Namespace = ""
|
||||
p.FieldSpecs = nil
|
||||
return yaml.Unmarshal(c, p)
|
||||
}
|
||||
|
||||
func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
if len(p.Namespace) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, r := range m.Resources() {
|
||||
if r.IsEmpty() {
|
||||
// Don't mutate empty objects?
|
||||
continue
|
||||
}
|
||||
r.StorePreviousId()
|
||||
if err := r.ApplyFilter(namespace.Filter{
|
||||
Namespace: p.Namespace,
|
||||
FsSlice: p.FieldSpecs,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
matches := m.GetMatchingResourcesByCurrentId(r.CurId().Equals)
|
||||
if len(matches) != 1 {
|
||||
return fmt.Errorf(
|
||||
"namespace transformation produces ID conflict: %+v", matches)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewNamespaceTransformerPlugin() resmap.TransformerPlugin {
|
||||
return &NamespaceTransformerPlugin{}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// Code generated by pluginator on PrefixSuffixTransformer; DO NOT EDIT.
|
||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
||||
|
||||
package builtins
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"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.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
}
|
||||
|
||||
// A Gvk skip list for prefix/suffix modification.
|
||||
// hard coded for now - eventually should be part of config.
|
||||
var prefixSuffixFieldSpecsToSkip = types.FsSlice{
|
||||
{Gvk: resid.Gvk{Kind: "CustomResourceDefinition"}},
|
||||
{Gvk: resid.Gvk{Group: "apiregistration.k8s.io", Kind: "APIService"}},
|
||||
{Gvk: resid.Gvk{Kind: "Namespace"}},
|
||||
}
|
||||
|
||||
func (p *PrefixSuffixTransformerPlugin) Config(
|
||||
_ *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() {
|
||||
// TODO: move this test into the filter (i.e. make a better filter)
|
||||
if p.shouldSkip(r.OrgId()) {
|
||||
continue
|
||||
}
|
||||
id := r.OrgId()
|
||||
// current default configuration contains
|
||||
// only one entry: "metadata/name" with no GVK
|
||||
for _, fs := range p.FieldSpecs {
|
||||
// TODO: this is redundant to filter (but needed for now)
|
||||
if !id.IsSelected(&fs.Gvk) {
|
||||
continue
|
||||
}
|
||||
// TODO: move this test into the filter.
|
||||
if smellsLikeANameChange(&fs) {
|
||||
// "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)
|
||||
if p.Prefix != "" || p.Suffix != "" {
|
||||
r.StorePreviousId()
|
||||
}
|
||||
}
|
||||
err := r.ApplyFilter(prefixsuffix.Filter{
|
||||
Prefix: p.Prefix,
|
||||
Suffix: p.Suffix,
|
||||
FieldSpec: fs,
|
||||
})
|
||||
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 NewPrefixSuffixTransformerPlugin() resmap.TransformerPlugin {
|
||||
return &PrefixSuffixTransformerPlugin{}
|
||||
}
|
||||
49
api/builtins/builtins.go
Normal file
49
api/builtins/builtins.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Deprecated: Package api/builtins will not be available in API v1.
|
||||
package builtins
|
||||
|
||||
import (
|
||||
internal "sigs.k8s.io/kustomize/api/internal/builtins"
|
||||
)
|
||||
|
||||
type (
|
||||
AnnotationsTransformerPlugin = internal.AnnotationsTransformerPlugin
|
||||
ConfigMapGeneratorPlugin = internal.ConfigMapGeneratorPlugin
|
||||
HashTransformerPlugin = internal.HashTransformerPlugin
|
||||
HelmChartInflationGeneratorPlugin = internal.HelmChartInflationGeneratorPlugin
|
||||
IAMPolicyGeneratorPlugin = internal.IAMPolicyGeneratorPlugin
|
||||
ImageTagTransformerPlugin = internal.ImageTagTransformerPlugin
|
||||
LabelTransformerPlugin = internal.LabelTransformerPlugin
|
||||
NamespaceTransformerPlugin = internal.NamespaceTransformerPlugin
|
||||
PatchJson6902TransformerPlugin = internal.PatchJson6902TransformerPlugin
|
||||
PatchStrategicMergeTransformerPlugin = internal.PatchStrategicMergeTransformerPlugin
|
||||
PatchTransformerPlugin = internal.PatchTransformerPlugin
|
||||
PrefixTransformerPlugin = internal.PrefixTransformerPlugin
|
||||
SuffixTransformerPlugin = internal.SuffixTransformerPlugin
|
||||
ReplacementTransformerPlugin = internal.ReplacementTransformerPlugin
|
||||
ReplicaCountTransformerPlugin = internal.ReplicaCountTransformerPlugin
|
||||
SecretGeneratorPlugin = internal.SecretGeneratorPlugin
|
||||
ValueAddTransformerPlugin = internal.ValueAddTransformerPlugin
|
||||
)
|
||||
|
||||
var (
|
||||
NewAnnotationsTransformerPlugin = internal.NewAnnotationsTransformerPlugin
|
||||
NewConfigMapGeneratorPlugin = internal.NewConfigMapGeneratorPlugin
|
||||
NewHashTransformerPlugin = internal.NewHashTransformerPlugin
|
||||
NewHelmChartInflationGeneratorPlugin = internal.NewHelmChartInflationGeneratorPlugin
|
||||
NewIAMPolicyGeneratorPlugin = internal.NewIAMPolicyGeneratorPlugin
|
||||
NewImageTagTransformerPlugin = internal.NewImageTagTransformerPlugin
|
||||
NewLabelTransformerPlugin = internal.NewLabelTransformerPlugin
|
||||
NewNamespaceTransformerPlugin = internal.NewNamespaceTransformerPlugin
|
||||
NewPatchJson6902TransformerPlugin = internal.NewPatchJson6902TransformerPlugin
|
||||
NewPatchStrategicMergeTransformerPlugin = internal.NewPatchStrategicMergeTransformerPlugin
|
||||
NewPatchTransformerPlugin = internal.NewPatchTransformerPlugin
|
||||
NewPrefixTransformerPlugin = internal.NewPrefixTransformerPlugin
|
||||
NewSuffixTransformerPlugin = internal.NewSuffixTransformerPlugin
|
||||
NewReplacementTransformerPlugin = internal.NewReplacementTransformerPlugin
|
||||
NewReplicaCountTransformerPlugin = internal.NewReplicaCountTransformerPlugin
|
||||
NewSecretGeneratorPlugin = internal.NewSecretGeneratorPlugin
|
||||
NewValueAddTransformerPlugin = internal.NewValueAddTransformerPlugin
|
||||
)
|
||||
61
api/filesys/filesys.go
Normal file
61
api/filesys/filesys.go
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package filesys provides a file system abstraction,
|
||||
// a subset of that provided by golang.org/pkg/os,
|
||||
// with an on-disk and in-memory representation.
|
||||
//
|
||||
// Deprecated: use sigs.k8s.io/kustomize/kyaml/filesys instead.
|
||||
package filesys
|
||||
|
||||
import "sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
|
||||
const (
|
||||
// Separator is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.Separator.
|
||||
Separator = filesys.Separator
|
||||
// SelfDir is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.SelfDir.
|
||||
SelfDir = filesys.SelfDir
|
||||
// ParentDir is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.ParentDir.
|
||||
ParentDir = filesys.ParentDir
|
||||
)
|
||||
|
||||
type (
|
||||
// FileSystem is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.FileSystem.
|
||||
FileSystem = filesys.FileSystem
|
||||
// FileSystemOrOnDisk is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.FileSystemOrOnDisk.
|
||||
FileSystemOrOnDisk = filesys.FileSystemOrOnDisk
|
||||
// ConfirmedDir is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.ConfirmedDir.
|
||||
ConfirmedDir = filesys.ConfirmedDir
|
||||
)
|
||||
|
||||
// MakeEmptyDirInMemory is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.MakeEmptyDirInMemory.
|
||||
func MakeEmptyDirInMemory() FileSystem { return filesys.MakeEmptyDirInMemory() }
|
||||
|
||||
// MakeFsInMemory is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.MakeFsInMemory.
|
||||
func MakeFsInMemory() FileSystem { return filesys.MakeFsInMemory() }
|
||||
|
||||
// MakeFsOnDisk is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.MakeFsOnDisk.
|
||||
func MakeFsOnDisk() FileSystem { return filesys.MakeFsOnDisk() }
|
||||
|
||||
// NewTmpConfirmedDir is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.NewTmpConfirmedDir.
|
||||
func NewTmpConfirmedDir() (filesys.ConfirmedDir, error) { return filesys.NewTmpConfirmedDir() }
|
||||
|
||||
// RootedPath is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.RootedPath.
|
||||
func RootedPath(elem ...string) string { return filesys.RootedPath(elem...) }
|
||||
|
||||
// StripTrailingSeps is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.StripTrailingSeps.
|
||||
func StripTrailingSeps(s string) string { return filesys.StripTrailingSeps(s) }
|
||||
|
||||
// StripLeadingSeps is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.StripLeadingSeps.
|
||||
func StripLeadingSeps(s string) string { return filesys.StripLeadingSeps(s) }
|
||||
|
||||
// PathSplit is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.PathSplit.
|
||||
func PathSplit(incoming string) []string { return filesys.PathSplit(incoming) }
|
||||
|
||||
// PathJoin is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.PathJoin.
|
||||
func PathJoin(incoming []string) string { return filesys.PathJoin(incoming) }
|
||||
|
||||
// InsertPathPart is deprecated, use sigs.k8s.io/kustomize/kyaml/filesys.InsertPathPart.
|
||||
func InsertPathPart(path string, pos int, part string) string {
|
||||
return filesys.InsertPathPart(path, pos, part)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package filesys provides a file system abstraction layer.
|
||||
package filesys
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
Separator = string(filepath.Separator)
|
||||
SelfDir = "."
|
||||
ParentDir = ".."
|
||||
)
|
||||
|
||||
// FileSystem groups basic os filesystem methods.
|
||||
// It's supposed be functional subset of https://golang.org/pkg/os
|
||||
type FileSystem interface {
|
||||
// Create a file.
|
||||
Create(path string) (File, error)
|
||||
// MkDir makes a directory.
|
||||
Mkdir(path string) error
|
||||
// MkDirAll makes a directory path, creating intervening directories.
|
||||
MkdirAll(path string) error
|
||||
// RemoveAll removes path and any children it contains.
|
||||
RemoveAll(path string) error
|
||||
// Open opens the named file for reading.
|
||||
Open(path string) (File, error)
|
||||
// IsDir returns true if the path is a directory.
|
||||
IsDir(path string) bool
|
||||
// CleanedAbs converts the given path into a
|
||||
// directory and a file name, where the directory
|
||||
// is represented as a ConfirmedDir and all that implies.
|
||||
// If the entire path is a directory, the file component
|
||||
// is an empty string.
|
||||
CleanedAbs(path string) (ConfirmedDir, string, error)
|
||||
// Exists is true if the path exists in the file system.
|
||||
Exists(path string) bool
|
||||
// Glob returns the list of matching files,
|
||||
// emulating https://golang.org/pkg/path/filepath/#Glob
|
||||
Glob(pattern string) ([]string, error)
|
||||
// ReadFile returns the contents of the file at the given path.
|
||||
ReadFile(path string) ([]byte, error)
|
||||
// WriteFile writes the data to a file at the given path,
|
||||
// overwriting anything that's already there.
|
||||
WriteFile(path string, data []byte) error
|
||||
// Walk walks the file system with the given WalkFunc.
|
||||
Walk(path string, walkFn filepath.WalkFunc) error
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
// 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)
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filesys
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RootedPath returns a rooted path, e.g. "/foo/bar" as
|
||||
// opposed to "foo/bar".
|
||||
func RootedPath(elem ...string) string {
|
||||
return Separator + filepath.Join(elem...)
|
||||
}
|
||||
|
||||
// StripTrailingSeps trims trailing filepath separators from input.
|
||||
func StripTrailingSeps(s string) string {
|
||||
k := len(s)
|
||||
for k > 0 && s[k-1] == filepath.Separator {
|
||||
k--
|
||||
}
|
||||
return s[:k]
|
||||
}
|
||||
|
||||
// StripLeadingSeps trims leading filepath separators from input.
|
||||
func StripLeadingSeps(s string) string {
|
||||
k := 0
|
||||
for k < len(s) && s[k] == filepath.Separator {
|
||||
k++
|
||||
}
|
||||
return s[k:]
|
||||
}
|
||||
|
||||
// PathSplit converts a file path to a slice of string.
|
||||
// If the path is absolute (if the path has a leading slash),
|
||||
// then the first entry in the result is an empty string.
|
||||
// Desired: path == PathJoin(PathSplit(path))
|
||||
func PathSplit(incoming string) []string {
|
||||
if incoming == "" {
|
||||
return []string{}
|
||||
}
|
||||
dir, path := filepath.Split(incoming)
|
||||
if dir == string(os.PathSeparator) {
|
||||
if path == "" {
|
||||
return []string{""}
|
||||
}
|
||||
return []string{"", path}
|
||||
}
|
||||
dir = strings.TrimSuffix(dir, string(os.PathSeparator))
|
||||
if dir == "" {
|
||||
return []string{path}
|
||||
}
|
||||
return append(PathSplit(dir), path)
|
||||
}
|
||||
|
||||
// PathJoin converts a slice of string to a file path.
|
||||
// If the first entry is an empty string, then the returned
|
||||
// path is absolute (it has a leading slash).
|
||||
// Desired: path == PathJoin(PathSplit(path))
|
||||
func PathJoin(incoming []string) string {
|
||||
if len(incoming) == 0 {
|
||||
return ""
|
||||
}
|
||||
if incoming[0] == "" {
|
||||
return string(os.PathSeparator) + filepath.Join(incoming[1:]...)
|
||||
}
|
||||
return filepath.Join(incoming...)
|
||||
}
|
||||
|
||||
// InsertPathPart inserts 'part' at position 'pos' in the given filepath.
|
||||
// The first position is 0.
|
||||
//
|
||||
// E.g. if part == 'PEACH'
|
||||
//
|
||||
// OLD : NEW : POS
|
||||
// --------------------------------------------------------
|
||||
// {empty} : PEACH : irrelevant
|
||||
// / : /PEACH : irrelevant
|
||||
// pie : PEACH/pie : 0 (or negative)
|
||||
// /pie : /PEACH/pie : 0 (or negative)
|
||||
// raw : raw/PEACH : 1 (or larger)
|
||||
// /raw : /raw/PEACH : 1 (or larger)
|
||||
// a/nice/warm/pie : a/nice/warm/PEACH/pie : 3
|
||||
// /a/nice/warm/pie : /a/nice/warm/PEACH/pie : 3
|
||||
//
|
||||
// * An empty part results in no change.
|
||||
//
|
||||
// * Absolute paths get their leading '/' stripped, treated like
|
||||
// relative paths, and the leading '/' is re-added on output.
|
||||
// The meaning of pos is intentionally the same in either absolute or
|
||||
// relative paths; if it weren't, this function could convert absolute
|
||||
// paths to relative paths, which is not desirable.
|
||||
//
|
||||
// * For robustness (liberal input, conservative output) Pos values that
|
||||
// that are too small (large) to index the split filepath result in a
|
||||
// prefix (postfix) rather than an error. Use extreme position values
|
||||
// to assure a prefix or postfix (e.g. 0 will always prefix, and
|
||||
// 9999 will presumably always postfix).
|
||||
func InsertPathPart(path string, pos int, part string) string {
|
||||
if part == "" {
|
||||
return path
|
||||
}
|
||||
parts := PathSplit(path)
|
||||
if pos < 0 {
|
||||
pos = 0
|
||||
} else if pos > len(parts) {
|
||||
pos = len(parts)
|
||||
}
|
||||
if len(parts) > 0 && parts[0] == "" && pos < len(parts) {
|
||||
// An empty string at 0 indicates an absolute path, and means
|
||||
// we must increment pos. This change means that a position
|
||||
// specification has the same meaning in relative and absolute paths.
|
||||
// E.g. in either the path 'a/b/c' or the path '/a/b/c',
|
||||
// 'a' is at 0, 'b' is at 1 and 'c' is at 2, and inserting at
|
||||
// zero means a new first field _without_ changing an absolute
|
||||
// path to a relative path.
|
||||
pos++
|
||||
}
|
||||
result := make([]string, len(parts)+1)
|
||||
copy(result, parts[0:pos])
|
||||
result[pos] = part
|
||||
return PathJoin(append(result, parts[pos:]...))
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
package filesys_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "sigs.k8s.io/kustomize/api/filesys"
|
||||
)
|
||||
|
||||
// Confirm behavior of filepath.Match
|
||||
func TestFilePathMatch(t *testing.T) {
|
||||
cases := []struct {
|
||||
pattern string
|
||||
path string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
pattern: "*e*",
|
||||
path: "hey",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
pattern: "*e*",
|
||||
path: "hay",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
pattern: "*e*",
|
||||
path: filepath.Join("h", "e", "y"),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
pattern: "*/e/*",
|
||||
path: filepath.Join("h", "e", "y"),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
pattern: "h/e/*",
|
||||
path: filepath.Join("h", "e", "y"),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
pattern: "*/e/y",
|
||||
path: filepath.Join("h", "e", "y"),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
pattern: "*/*/*",
|
||||
path: filepath.Join("h", "e", "y"),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
pattern: "*/*/*",
|
||||
path: filepath.Join("h", "e", "y", "there"),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
pattern: "*/*/*/t*e",
|
||||
path: filepath.Join("h", "e", "y", "there"),
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, item := range cases {
|
||||
match, err := filepath.Match(item.pattern, item.path)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if match != item.expected {
|
||||
t.Fatalf("'%s' '%s' %v\n", item.pattern, item.path, match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm behavior of filepath.Split
|
||||
func TestFilePathSplit(t *testing.T) {
|
||||
cases := []struct {
|
||||
full string
|
||||
dir string
|
||||
file string
|
||||
}{
|
||||
{
|
||||
full: "",
|
||||
dir: "",
|
||||
file: "",
|
||||
},
|
||||
{
|
||||
full: SelfDir,
|
||||
dir: "",
|
||||
file: SelfDir,
|
||||
},
|
||||
{
|
||||
full: "rabbit.jpg",
|
||||
dir: "",
|
||||
file: "rabbit.jpg",
|
||||
},
|
||||
{
|
||||
full: "/",
|
||||
dir: "/",
|
||||
file: "",
|
||||
},
|
||||
{
|
||||
full: "/beans",
|
||||
dir: "/",
|
||||
file: "beans",
|
||||
},
|
||||
{
|
||||
full: "/home/foo/bar",
|
||||
dir: "/home/foo/",
|
||||
file: "bar",
|
||||
},
|
||||
{
|
||||
full: "/usr/local/",
|
||||
dir: "/usr/local/",
|
||||
file: "",
|
||||
},
|
||||
{
|
||||
full: "/usr//local//go",
|
||||
dir: "/usr//local//",
|
||||
file: "go",
|
||||
},
|
||||
}
|
||||
for _, p := range cases {
|
||||
dir, file := filepath.Split(p.full)
|
||||
if dir != p.dir || file != p.file {
|
||||
t.Fatalf(
|
||||
"in '%s',\ngot dir='%s' (expected '%s'),\n got file='%s' (expected %s).",
|
||||
p.full, dir, p.dir, file, p.file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathSplitAndJoin(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
original string
|
||||
expected []string
|
||||
}{
|
||||
"Empty": {
|
||||
original: "",
|
||||
expected: []string{},
|
||||
},
|
||||
"One": {
|
||||
original: "hello",
|
||||
expected: []string{"hello"},
|
||||
},
|
||||
"Two": {
|
||||
original: "hello/there",
|
||||
expected: []string{"hello", "there"},
|
||||
},
|
||||
"Three": {
|
||||
original: "hello/my/friend",
|
||||
expected: []string{"hello", "my", "friend"},
|
||||
},
|
||||
}
|
||||
for n, c := range cases {
|
||||
f := func(t *testing.T, original string, expected []string) {
|
||||
actual := PathSplit(original)
|
||||
if len(actual) != len(expected) {
|
||||
t.Fatalf(
|
||||
"expected len %d, got len %d",
|
||||
len(expected), len(actual))
|
||||
}
|
||||
for i := range expected {
|
||||
if expected[i] != actual[i] {
|
||||
t.Fatalf(
|
||||
"at i=%d, expected '%s', got '%s'",
|
||||
i, expected[i], actual[i])
|
||||
}
|
||||
}
|
||||
joined := PathJoin(actual)
|
||||
if joined != original {
|
||||
t.Fatalf(
|
||||
"when rejoining, expected '%s', got '%s'",
|
||||
original, joined)
|
||||
}
|
||||
}
|
||||
t.Run("relative"+n, func(t *testing.T) {
|
||||
f(t, c.original, c.expected)
|
||||
})
|
||||
t.Run("absolute"+n, func(t *testing.T) {
|
||||
f(t,
|
||||
string(os.PathSeparator)+c.original,
|
||||
append([]string{""}, c.expected...))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsertPathPart(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
original string
|
||||
pos int
|
||||
part string
|
||||
expected string
|
||||
}{
|
||||
"rootOne": {
|
||||
original: "/",
|
||||
pos: 0,
|
||||
part: "___",
|
||||
expected: "/___",
|
||||
},
|
||||
"rootTwo": {
|
||||
original: "/",
|
||||
pos: 444,
|
||||
part: "___",
|
||||
expected: "/___",
|
||||
},
|
||||
"rootedFirst": {
|
||||
original: "/apple",
|
||||
pos: 0,
|
||||
part: "___",
|
||||
expected: "/___/apple",
|
||||
},
|
||||
"rootedSecond": {
|
||||
original: "/apple",
|
||||
pos: 444,
|
||||
part: "___",
|
||||
expected: "/apple/___",
|
||||
},
|
||||
"rootedThird": {
|
||||
original: "/apple/banana",
|
||||
pos: 444,
|
||||
part: "___",
|
||||
expected: "/apple/banana/___",
|
||||
},
|
||||
"emptyLow": {
|
||||
original: "",
|
||||
pos: -3,
|
||||
part: "___",
|
||||
expected: "___",
|
||||
},
|
||||
"emptyHigh": {
|
||||
original: "",
|
||||
pos: 444,
|
||||
part: "___",
|
||||
expected: "___",
|
||||
},
|
||||
"peachPie": {
|
||||
original: "a/nice/warm/pie",
|
||||
pos: 3,
|
||||
part: "PEACH",
|
||||
expected: "a/nice/warm/PEACH/pie",
|
||||
},
|
||||
"rootedPeachPie": {
|
||||
original: "/a/nice/warm/pie",
|
||||
pos: 3,
|
||||
part: "PEACH",
|
||||
expected: "/a/nice/warm/PEACH/pie",
|
||||
},
|
||||
"longStart": {
|
||||
original: "a/b/c/d/e/f",
|
||||
pos: 0,
|
||||
part: "___",
|
||||
expected: "___/a/b/c/d/e/f",
|
||||
},
|
||||
"rootedLongStart": {
|
||||
original: "/a/b/c/d/e/f",
|
||||
pos: 0,
|
||||
part: "___",
|
||||
expected: "/___/a/b/c/d/e/f",
|
||||
},
|
||||
"longMiddle": {
|
||||
original: "a/b/c/d/e/f",
|
||||
pos: 3,
|
||||
part: "___",
|
||||
expected: "a/b/c/___/d/e/f",
|
||||
},
|
||||
"rootedLongMiddle": {
|
||||
original: "/a/b/c/d/e/f",
|
||||
pos: 3,
|
||||
part: "___",
|
||||
expected: "/a/b/c/___/d/e/f",
|
||||
},
|
||||
"longEnd": {
|
||||
original: "a/b/c/d/e/f",
|
||||
pos: 444,
|
||||
part: "___",
|
||||
expected: "a/b/c/d/e/f/___",
|
||||
},
|
||||
"rootedLongEnd": {
|
||||
original: "/a/b/c/d/e/f",
|
||||
pos: 444,
|
||||
part: "___",
|
||||
expected: "/a/b/c/d/e/f/___",
|
||||
},
|
||||
}
|
||||
for n, c := range cases {
|
||||
t.Run(n, func(t *testing.T) {
|
||||
actual := InsertPathPart(c.original, c.pos, c.part)
|
||||
if actual != c.expected {
|
||||
t.Fatalf("expected '%s', got '%s'", c.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripTrailingSeps(t *testing.T) {
|
||||
cases := []struct {
|
||||
full string
|
||||
rem string
|
||||
}{
|
||||
{
|
||||
full: "foo",
|
||||
rem: "foo",
|
||||
},
|
||||
{
|
||||
full: "",
|
||||
rem: "",
|
||||
},
|
||||
{
|
||||
full: "foo/",
|
||||
rem: "foo",
|
||||
},
|
||||
{
|
||||
full: "foo///bar///",
|
||||
rem: "foo///bar",
|
||||
},
|
||||
{
|
||||
full: "/////",
|
||||
rem: "",
|
||||
},
|
||||
{
|
||||
full: "/",
|
||||
rem: "",
|
||||
},
|
||||
}
|
||||
for _, p := range cases {
|
||||
dir := StripTrailingSeps(p.full)
|
||||
if dir != p.rem {
|
||||
t.Fatalf(
|
||||
"in '%s', got dir='%s' (expected '%s')",
|
||||
p.full, dir, p.rem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripLeadingSeps(t *testing.T) {
|
||||
cases := []struct {
|
||||
full string
|
||||
rem string
|
||||
}{
|
||||
{
|
||||
full: "foo",
|
||||
rem: "foo",
|
||||
},
|
||||
{
|
||||
full: "",
|
||||
rem: "",
|
||||
},
|
||||
{
|
||||
full: "/foo",
|
||||
rem: "foo",
|
||||
},
|
||||
{
|
||||
full: "///foo///bar///",
|
||||
rem: "foo///bar///",
|
||||
},
|
||||
{
|
||||
full: "/////",
|
||||
rem: "",
|
||||
},
|
||||
{
|
||||
full: "/",
|
||||
rem: "",
|
||||
},
|
||||
}
|
||||
for _, p := range cases {
|
||||
dir := StripLeadingSeps(p.full)
|
||||
if dir != p.rem {
|
||||
t.Fatalf(
|
||||
"in '%s', got dir='%s' (expected '%s')",
|
||||
p.full, dir, p.rem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,17 @@ type Filter struct {
|
||||
|
||||
// FsSlice contains the FieldSpecs to locate the namespace field
|
||||
FsSlice types.FsSlice
|
||||
|
||||
trackableSetter filtersutil.TrackableSetter
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
var _ kio.TrackableFilter = &Filter{}
|
||||
|
||||
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||
func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||
f.trackableSetter.WithMutationTracker(callback)
|
||||
}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
keys := yaml.SortedMapKeys(f.Annotations)
|
||||
@@ -30,7 +38,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for _, k := range keys {
|
||||
if err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: filtersutil.SetEntry(
|
||||
SetValue: f.trackableSetter.SetEntry(
|
||||
k, f.Annotations[k], yaml.NodeTagString),
|
||||
CreateKind: yaml.MappingNode, // Annotations are MappingNodes.
|
||||
CreateTag: yaml.NodeTagMap,
|
||||
|
||||
@@ -11,16 +11,20 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var annosFs = builtinconfig.MakeDefaultConfig().CommonAnnotations
|
||||
|
||||
func TestAnnotations_Filter(t *testing.T) {
|
||||
mutationTrackStub := filtertest_test.MutationTrackerStub{}
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
filter Filter
|
||||
fsslice types.FsSlice
|
||||
input string
|
||||
expectedOutput string
|
||||
filter Filter
|
||||
fsslice types.FsSlice
|
||||
setEntryCallback func(key, value, tag string, node *yaml.RNode)
|
||||
expectedSetEntryArgs []filtertest_test.SetValueArg
|
||||
}{
|
||||
"add": {
|
||||
input: `
|
||||
@@ -210,17 +214,86 @@ metadata:
|
||||
"b": "b1",
|
||||
}},
|
||||
},
|
||||
|
||||
// test usage of SetEntryCallback
|
||||
"set_entry_callback": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
annotations:
|
||||
a: a1
|
||||
b: b1
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
a: a1
|
||||
b: b1
|
||||
`,
|
||||
filter: Filter{
|
||||
Annotations: annoMap{
|
||||
"a": "a1",
|
||||
"b": "b1",
|
||||
},
|
||||
},
|
||||
setEntryCallback: mutationTrackStub.MutationTracker,
|
||||
fsslice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/template/metadata/annotations",
|
||||
CreateIfNotPresent: true,
|
||||
},
|
||||
},
|
||||
expectedSetEntryArgs: []filtertest_test.SetValueArg{
|
||||
{
|
||||
Key: "a",
|
||||
Value: "a1",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"metadata", "annotations"},
|
||||
},
|
||||
{
|
||||
Key: "a",
|
||||
Value: "a1",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"spec", "template", "metadata", "annotations"},
|
||||
},
|
||||
{
|
||||
Key: "b",
|
||||
Value: "b1",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"metadata", "annotations"},
|
||||
},
|
||||
{
|
||||
Key: "b",
|
||||
Value: "b1",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"spec", "template", "metadata", "annotations"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
mutationTrackStub.Reset()
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
filter := tc.filter
|
||||
filter.FsSlice = append(annosFs, tc.fsslice...)
|
||||
filter.WithMutationTracker(tc.setEntryCallback)
|
||||
filter.FsSlice = append(annosFs, tc.fsslice...) //nolint:gocritic
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, tc.expectedSetEntryArgs, mutationTrackStub.SetValueArgs()) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
8
api/filters/doc.go
Normal file
8
api/filters/doc.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filters
|
||||
|
||||
// Package filters collects various implementations
|
||||
// sigs.k8s.io/kustomize/kyaml/kio.Filter used by kustomize
|
||||
// transformers to modify kubernetes objects.
|
||||
@@ -8,9 +8,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
"sigs.k8s.io/kustomize/kyaml/utils"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -45,15 +46,13 @@ type Filter struct {
|
||||
|
||||
func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) {
|
||||
// check if the FieldSpec applies to the object
|
||||
if match, err := isMatchGVK(fltr.FieldSpec, obj); !match || err != nil {
|
||||
return obj, errors.Wrap(err)
|
||||
if match := isMatchGVK(fltr.FieldSpec, obj); !match {
|
||||
return obj, nil
|
||||
}
|
||||
fltr.path = utils.PathSplitter(fltr.FieldSpec.Path)
|
||||
err := fltr.filter(obj)
|
||||
if err != nil {
|
||||
s, _ := obj.String()
|
||||
fltr.path = utils.PathSplitter(fltr.FieldSpec.Path, "/")
|
||||
if err := fltr.filter(obj); err != nil {
|
||||
return nil, errors.WrapPrefixf(err,
|
||||
"considering field '%s' of object\n%v", fltr.FieldSpec.Path, s)
|
||||
"considering field '%s' of object %s", fltr.FieldSpec.Path, resid.FromRNode(obj))
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
@@ -138,6 +137,8 @@ func (fltr Filter) handleMap(obj *yaml.RNode) error {
|
||||
// seq calls filter on all sequence elements
|
||||
func (fltr Filter) handleSequence(obj *yaml.RNode) error {
|
||||
if err := obj.VisitElements(func(node *yaml.RNode) error {
|
||||
// set an accurate FieldPath for nested elements
|
||||
node.AppendToFieldPath(obj.FieldPath()...)
|
||||
// recurse on each element -- re-allocating a Filter is
|
||||
// not strictly required, but is more consistent with field
|
||||
// and less likely to have side effects
|
||||
@@ -158,28 +159,24 @@ func isSequenceField(name string) (string, bool) {
|
||||
}
|
||||
|
||||
// isMatchGVK returns true if the fs.GVK matches the obj GVK.
|
||||
func isMatchGVK(fs types.FieldSpec, obj *yaml.RNode) (bool, error) {
|
||||
meta, err := obj.GetMeta()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if fs.Kind != "" && fs.Kind != meta.Kind {
|
||||
func isMatchGVK(fs types.FieldSpec, obj *yaml.RNode) bool {
|
||||
if kind := obj.GetKind(); fs.Kind != "" && fs.Kind != kind {
|
||||
// kind doesn't match
|
||||
return false, err
|
||||
return false
|
||||
}
|
||||
|
||||
// parse the group and version from the apiVersion field
|
||||
group, version := parseGV(meta.APIVersion)
|
||||
group, version := resid.ParseGroupVersion(obj.GetApiVersion())
|
||||
|
||||
if fs.Group != "" && fs.Group != group {
|
||||
// group doesn't match
|
||||
return false, nil
|
||||
return false
|
||||
}
|
||||
|
||||
if fs.Version != "" && fs.Version != version {
|
||||
// version doesn't match
|
||||
return false, nil
|
||||
return false
|
||||
}
|
||||
|
||||
return true, nil
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -46,10 +46,11 @@ xxx:
|
||||
"empty path": {
|
||||
fieldSpec: `
|
||||
group: foo
|
||||
version: v1
|
||||
kind: Bar
|
||||
`,
|
||||
input: `
|
||||
apiVersion: foo
|
||||
apiVersion: foo/v1
|
||||
kind: Bar
|
||||
xxx:
|
||||
`,
|
||||
@@ -58,11 +59,7 @@ apiVersion: foo
|
||||
kind: Bar
|
||||
xxx:
|
||||
`,
|
||||
error: `considering field '' of object
|
||||
apiVersion: foo
|
||||
kind: Bar
|
||||
xxx:
|
||||
: cannot set or create an empty field name`,
|
||||
error: `considering field '' of object Bar.v1.foo/[noName].[noNs]: cannot set or create an empty field name`,
|
||||
filter: fieldspec.Filter{
|
||||
SetValue: filtersutil.SetScalar("e"),
|
||||
},
|
||||
@@ -195,11 +192,14 @@ kind: Bar
|
||||
input: `
|
||||
a:
|
||||
b: c
|
||||
`,
|
||||
expected: `
|
||||
a:
|
||||
b: c
|
||||
`,
|
||||
filter: fieldspec.Filter{
|
||||
SetValue: filtersutil.SetScalar("e"),
|
||||
},
|
||||
error: "missing Resource metadata",
|
||||
},
|
||||
|
||||
"miss-match-type": {
|
||||
@@ -212,11 +212,7 @@ kind: Bar
|
||||
a:
|
||||
b: a
|
||||
`,
|
||||
error: `considering field 'a/b/c' of object
|
||||
kind: Bar
|
||||
a:
|
||||
b: a
|
||||
: expected sequence or mapping node`,
|
||||
error: `considering field 'a/b/c' of object Bar.[noVer].[noGrp]/[noName].[noNs]: expected sequence or mapping node`,
|
||||
filter: fieldspec.Filter{
|
||||
SetValue: filtersutil.SetScalar("e"),
|
||||
},
|
||||
@@ -562,3 +558,85 @@ a:
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilter_FieldPaths(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
fieldSpec string
|
||||
expected []string
|
||||
}{
|
||||
"fieldpath containing SequenceNode": {
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: app
|
||||
spec:
|
||||
containers:
|
||||
- name: store
|
||||
image: redis:6.2.6
|
||||
- name: server
|
||||
image: nginx:latest
|
||||
`,
|
||||
fieldSpec: `
|
||||
path: spec/containers[]/image
|
||||
kind: Pod
|
||||
`,
|
||||
expected: []string{
|
||||
"spec.containers.image",
|
||||
"spec.containers.image",
|
||||
},
|
||||
},
|
||||
"fieldpath with MappingNode": {
|
||||
input: `
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: app
|
||||
spec:
|
||||
containers:
|
||||
- name: store
|
||||
image: redis:6.2.6
|
||||
- name: server
|
||||
image: nginx:latest
|
||||
`,
|
||||
fieldSpec: `
|
||||
path: metadata/name
|
||||
kind: Pod
|
||||
`,
|
||||
expected: []string{
|
||||
"metadata.name",
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
var fieldPaths []string
|
||||
trackableSetter := filtersutil.TrackableSetter{}
|
||||
trackableSetter.WithMutationTracker(func(key, value, tag string, node *yaml.RNode) {
|
||||
fieldPaths = append(fieldPaths, strings.Join(node.FieldPath(), "."))
|
||||
})
|
||||
filter := fieldspec.Filter{
|
||||
SetValue: trackableSetter.SetScalar("foo"),
|
||||
}
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
err := yaml.Unmarshal([]byte(tc.fieldSpec), &filter.FieldSpec)
|
||||
assert.NoError(t, err)
|
||||
rw := &kio.ByteReadWriter{
|
||||
Reader: bytes.NewBufferString(tc.input),
|
||||
Writer: &bytes.Buffer{},
|
||||
OmitReaderAnnotations: true,
|
||||
}
|
||||
|
||||
// run the filter
|
||||
err = kio.Pipeline{
|
||||
Inputs: []kio.Reader{rw},
|
||||
Filters: []kio.Filter{kio.FilterAll(filter)},
|
||||
Outputs: []kio.Writer{rw},
|
||||
}.Execute()
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expected, fieldPaths)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package fieldspec
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Return true for 'v' followed by a 1 or 2, and don't look at rest.
|
||||
// I.e. 'v1', 'v1beta1', 'v2', would return true.
|
||||
func looksLikeACoreApiVersion(s string) bool {
|
||||
if len(s) < 2 {
|
||||
return false
|
||||
}
|
||||
if s[0:1] != "v" {
|
||||
return false
|
||||
}
|
||||
return s[1:2] == "1" || s[1:2] == "2"
|
||||
}
|
||||
|
||||
// parseGV parses apiVersion field into group and version.
|
||||
func parseGV(apiVersion string) (group, version string) {
|
||||
// parse the group and version from the apiVersion field
|
||||
parts := strings.SplitN(apiVersion, "/", 2)
|
||||
group = parts[0]
|
||||
if len(parts) > 1 {
|
||||
version = parts[1]
|
||||
}
|
||||
// Special case the original "apiVersion" of what
|
||||
// we now call the "core" (empty) group.
|
||||
if version == "" && looksLikeACoreApiVersion(group) {
|
||||
version = group
|
||||
group = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetGVK parses the metadata into a GVK
|
||||
func GetGVK(meta yaml.ResourceMeta) resid.Gvk {
|
||||
group, version := parseGV(meta.APIVersion)
|
||||
return resid.Gvk{
|
||||
Group: group,
|
||||
Version: version,
|
||||
Kind: meta.Kind,
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package fieldspec
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestParseGV(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedGroup string
|
||||
expectedVersion string
|
||||
}{
|
||||
"empty": {
|
||||
input: "",
|
||||
expectedGroup: "",
|
||||
expectedVersion: "",
|
||||
},
|
||||
"certSigning": {
|
||||
input: "certificates.k8s.io/v1beta1",
|
||||
expectedGroup: "certificates.k8s.io",
|
||||
expectedVersion: "v1beta1",
|
||||
},
|
||||
"extensions": {
|
||||
input: "extensions/v1beta1",
|
||||
expectedGroup: "extensions",
|
||||
expectedVersion: "v1beta1",
|
||||
},
|
||||
"normal": {
|
||||
input: "apps/v1",
|
||||
expectedGroup: "apps",
|
||||
expectedVersion: "v1",
|
||||
},
|
||||
"justApps": {
|
||||
input: "apps",
|
||||
expectedGroup: "apps",
|
||||
expectedVersion: "",
|
||||
},
|
||||
"coreV1": {
|
||||
input: "v1",
|
||||
expectedGroup: "",
|
||||
expectedVersion: "v1",
|
||||
},
|
||||
"coreV2": {
|
||||
input: "v2",
|
||||
expectedGroup: "",
|
||||
expectedVersion: "v2",
|
||||
},
|
||||
"coreV2Beta1": {
|
||||
input: "v2beta1",
|
||||
expectedGroup: "",
|
||||
expectedVersion: "v2beta1",
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
group, version := parseGV(tc.input)
|
||||
if !assert.Equal(t, tc.expectedGroup, group) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, tc.expectedVersion, version) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGVK(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expected resid.Gvk
|
||||
parseError string
|
||||
metaError string
|
||||
}{
|
||||
"empty": {
|
||||
input: `
|
||||
`,
|
||||
parseError: "EOF",
|
||||
},
|
||||
"junk": {
|
||||
input: `
|
||||
congress: effective
|
||||
`,
|
||||
metaError: "missing Resource metadata",
|
||||
},
|
||||
"normal": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
`,
|
||||
expected: resid.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"},
|
||||
},
|
||||
"apiVersionOnlyWithSlash": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
`,
|
||||
expected: resid.Gvk{Group: "apps", Version: "v1", Kind: ""},
|
||||
},
|
||||
"apiVersionOnlyNoSlash1": {
|
||||
input: `
|
||||
apiVersion: apps
|
||||
`,
|
||||
expected: resid.Gvk{Group: "apps", Version: "", Kind: ""},
|
||||
},
|
||||
"apiVersionOnlyNoSlash2": {
|
||||
input: `
|
||||
apiVersion: v1
|
||||
`,
|
||||
expected: resid.Gvk{Group: "", Version: "v1", Kind: ""},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
obj, err := yaml.Parse(tc.input)
|
||||
if len(tc.parseError) != 0 {
|
||||
if err == nil {
|
||||
t.Error("expected parse error")
|
||||
return
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.parseError) {
|
||||
t.Errorf("expected parse err '%s', got '%v'", tc.parseError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
meta, err := obj.GetMeta()
|
||||
if len(tc.metaError) != 0 {
|
||||
if err == nil {
|
||||
t.Error("expected meta error")
|
||||
return
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.metaError) {
|
||||
t.Errorf("expected meta err '%s', got '%v'", tc.metaError, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
gvk := GetGVK(meta)
|
||||
if !assert.Equal(t, tc.expected, gvk) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filtersutil
|
||||
|
||||
import (
|
||||
@@ -9,25 +12,94 @@ type SetFn func(*yaml.RNode) error
|
||||
|
||||
// SetScalar returns a SetFn to set a scalar value
|
||||
func SetScalar(value string) SetFn {
|
||||
return func(node *yaml.RNode) error {
|
||||
return node.PipeE(yaml.FieldSetter{StringValue: value})
|
||||
}
|
||||
return SetEntry("", value, yaml.NodeTagEmpty)
|
||||
}
|
||||
|
||||
// SetEntry returns a SetFn to set an entry in a map
|
||||
func SetEntry(key, value, tag string) SetFn {
|
||||
// SetEntry returns a SetFn to set a field or a map entry to a value.
|
||||
// It can be used with an empty name to set both a value and a tag on a scalar node.
|
||||
// When setting only a value on a scalar node, use SetScalar instead.
|
||||
func SetEntry(name, value, tag string) SetFn {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Value: value,
|
||||
Tag: tag,
|
||||
}
|
||||
if tag == yaml.NodeTagString && yaml.IsYaml1_1NonString(n) {
|
||||
n.Style = yaml.DoubleQuotedStyle
|
||||
}
|
||||
return func(node *yaml.RNode) error {
|
||||
return node.PipeE(yaml.FieldSetter{
|
||||
Name: key,
|
||||
Name: name,
|
||||
Value: yaml.NewRNode(n),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type TrackableSetter struct {
|
||||
// SetValueCallback will be invoked each time a field is set
|
||||
setValueCallback func(name, value, tag string, node *yaml.RNode)
|
||||
}
|
||||
|
||||
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||
func (s *TrackableSetter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) *TrackableSetter {
|
||||
s.setValueCallback = callback
|
||||
return s
|
||||
}
|
||||
|
||||
// SetScalar returns a SetFn to set a scalar value.
|
||||
// if a mutation tracker has been registered, the tracker will be invoked each
|
||||
// time a scalar is set
|
||||
func (s TrackableSetter) SetScalar(value string) SetFn {
|
||||
return s.SetEntry("", value, yaml.NodeTagEmpty)
|
||||
}
|
||||
|
||||
// SetScalarIfEmpty returns a SetFn to set a scalar value only if it isn't already set.
|
||||
// If a mutation tracker has been registered, the tracker will be invoked each
|
||||
// time a scalar is actually set.
|
||||
func (s TrackableSetter) SetScalarIfEmpty(value string) SetFn {
|
||||
return s.SetEntryIfEmpty("", value, yaml.NodeTagEmpty)
|
||||
}
|
||||
|
||||
// SetEntry returns a SetFn to set a field or a map entry to a value.
|
||||
// It can be used with an empty name to set both a value and a tag on a scalar node.
|
||||
// When setting only a value on a scalar node, use SetScalar instead.
|
||||
// If a mutation tracker has been registered, the tracker will be invoked each
|
||||
// time an entry is set.
|
||||
func (s TrackableSetter) SetEntry(name, value, tag string) SetFn {
|
||||
origSetEntry := SetEntry(name, value, tag)
|
||||
return func(node *yaml.RNode) error {
|
||||
if s.setValueCallback != nil {
|
||||
s.setValueCallback(name, value, tag, node)
|
||||
}
|
||||
return origSetEntry(node)
|
||||
}
|
||||
}
|
||||
|
||||
// SetEntryIfEmpty returns a SetFn to set a field or a map entry to a value only if it isn't already set.
|
||||
// It can be used with an empty name to set both a value and a tag on a scalar node.
|
||||
// When setting only a value on a scalar node, use SetScalar instead.
|
||||
// If a mutation tracker has been registered, the tracker will be invoked each
|
||||
// time an entry is actually set.
|
||||
func (s TrackableSetter) SetEntryIfEmpty(key, value, tag string) SetFn {
|
||||
origSetEntry := SetEntry(key, value, tag)
|
||||
return func(node *yaml.RNode) error {
|
||||
if hasExistingValue(node, key) {
|
||||
return nil
|
||||
}
|
||||
if s.setValueCallback != nil {
|
||||
s.setValueCallback(key, value, tag, node)
|
||||
}
|
||||
return origSetEntry(node)
|
||||
}
|
||||
}
|
||||
|
||||
func hasExistingValue(node *yaml.RNode, key string) bool {
|
||||
if node.IsNilOrEmpty() {
|
||||
return false
|
||||
}
|
||||
if err := yaml.ErrorIfInvalid(node, yaml.ScalarNode); err == nil {
|
||||
return yaml.GetValue(node) != ""
|
||||
}
|
||||
entry := node.Field(key)
|
||||
if entry.IsNilOrEmpty() {
|
||||
return false
|
||||
}
|
||||
return yaml.GetValue(entry.Value) != ""
|
||||
}
|
||||
|
||||
108
api/filters/filtersutil/setters_test.go
Normal file
108
api/filters/filtersutil/setters_test.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package filtersutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestTrackableSetter_SetScalarIfEmpty(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input *yaml.RNode
|
||||
value string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "sets null values",
|
||||
input: yaml.MakeNullNode(),
|
||||
value: "foo",
|
||||
want: "foo",
|
||||
},
|
||||
{
|
||||
name: "sets empty values",
|
||||
input: yaml.NewScalarRNode(""),
|
||||
value: "foo",
|
||||
want: "foo",
|
||||
},
|
||||
{
|
||||
name: "does not overwrite values",
|
||||
input: yaml.NewStringRNode("a"),
|
||||
value: "foo",
|
||||
want: "a",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
wasSet := false
|
||||
s := (&filtersutil.TrackableSetter{}).WithMutationTracker(func(_, _, _ string, _ *yaml.RNode) {
|
||||
wasSet = true
|
||||
})
|
||||
wantSet := tt.value == tt.want
|
||||
fn := s.SetScalarIfEmpty(tt.value)
|
||||
require.NoError(t, fn(tt.input))
|
||||
assert.Equal(t, tt.want, yaml.GetValue(tt.input))
|
||||
assert.Equal(t, wantSet, wasSet, "tracker invoked even though value was not changed")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackableSetter_SetEntryIfEmpty(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input *yaml.RNode
|
||||
key string
|
||||
value string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "sets empty values",
|
||||
input: yaml.NewMapRNode(&map[string]string{"setMe": ""}),
|
||||
key: "setMe",
|
||||
value: "foo",
|
||||
want: "foo",
|
||||
},
|
||||
{
|
||||
name: "sets missing keys",
|
||||
input: yaml.NewMapRNode(&map[string]string{}),
|
||||
key: "setMe",
|
||||
value: "foo",
|
||||
want: "foo",
|
||||
},
|
||||
{
|
||||
name: "does not overwrite values",
|
||||
input: yaml.NewMapRNode(&map[string]string{"existing": "original"}),
|
||||
key: "existing",
|
||||
value: "foo",
|
||||
want: "original",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
wasSet := false
|
||||
s := (&filtersutil.TrackableSetter{}).WithMutationTracker(func(_, _, _ string, _ *yaml.RNode) {
|
||||
wasSet = true
|
||||
})
|
||||
wantSet := tt.value == tt.want
|
||||
fn := s.SetEntryIfEmpty(tt.key, tt.value, "")
|
||||
require.NoError(t, fn(tt.input))
|
||||
assert.Equal(t, tt.want, yaml.GetValue(tt.input.Field(tt.key).Value))
|
||||
assert.Equal(t, wantSet, wasSet, "tracker invoked even though value was not changed")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrackableSetter_SetEntryIfEmpty_BadInputNodeKind(t *testing.T) {
|
||||
fn := filtersutil.TrackableSetter{}.SetEntryIfEmpty("foo", "false", yaml.NodeTagBool)
|
||||
rn := yaml.NewListRNode("nope")
|
||||
rn.AppendToFieldPath("dummy", "path")
|
||||
assert.EqualError(t, fn(rn), `wrong node kind: expected MappingNode but got SequenceNode: node contents:
|
||||
- nope
|
||||
`)
|
||||
}
|
||||
6
api/filters/iampolicygenerator/doc.go
Normal file
6
api/filters/iampolicygenerator/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package gkesagenerator contains a kio.Filter that that generates a
|
||||
// iampolicy-related resources for a given cloud provider
|
||||
package iampolicygenerator
|
||||
46
api/filters/iampolicygenerator/example_test.go
Normal file
46
api/filters/iampolicygenerator/example_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package iampolicygenerator
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
f := Filter{}
|
||||
var err = yaml.Unmarshal([]byte(`
|
||||
cloud: gke
|
||||
kubernetesService:
|
||||
namespace: k8s-namespace
|
||||
name: k8s-sa-name
|
||||
serviceAccount:
|
||||
name: gsa-name
|
||||
projectId: project-id
|
||||
`), &f)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = kio.Pipeline{
|
||||
Inputs: []kio.Reader{},
|
||||
Filters: []kio.Filter{f},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: v1
|
||||
// kind: ServiceAccount
|
||||
// metadata:
|
||||
// annotations:
|
||||
// iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
|
||||
// name: k8s-sa-name
|
||||
// namespace: k8s-namespace
|
||||
}
|
||||
55
api/filters/iampolicygenerator/iampolicygenerator.go
Normal file
55
api/filters/iampolicygenerator/iampolicygenerator.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package iampolicygenerator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
IAMPolicyGenerator types.IAMPolicyGeneratorArgs `json:",inline,omitempty" yaml:",inline,omitempty"`
|
||||
}
|
||||
|
||||
// Filter adds a GKE service account object to nodes
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
switch f.IAMPolicyGenerator.Cloud {
|
||||
case types.GKE:
|
||||
IAMPolicyResources, err := f.generateGkeIAMPolicyResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nodes = append(nodes, IAMPolicyResources...)
|
||||
default:
|
||||
return nil, fmt.Errorf("cloud provider %s not supported yet", f.IAMPolicyGenerator.Cloud)
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func (f Filter) generateGkeIAMPolicyResources() ([]*yaml.RNode, error) {
|
||||
var result []*yaml.RNode
|
||||
input := fmt.Sprintf(`
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
annotations:
|
||||
iam.gke.io/gcp-service-account: %s@%s.iam.gserviceaccount.com
|
||||
name: %s
|
||||
`, f.IAMPolicyGenerator.ServiceAccount.Name,
|
||||
f.IAMPolicyGenerator.ProjectId,
|
||||
f.IAMPolicyGenerator.KubernetesService.Name)
|
||||
|
||||
if f.IAMPolicyGenerator.Namespace != "" {
|
||||
input += fmt.Sprintf("\n namespace: %s", f.IAMPolicyGenerator.Namespace)
|
||||
}
|
||||
|
||||
sa, err := yaml.Parse(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(result, sa), nil
|
||||
}
|
||||
75
api/filters/iampolicygenerator/iampolicygenerator_test.go
Normal file
75
api/filters/iampolicygenerator/iampolicygenerator_test.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package iampolicygenerator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
args types.IAMPolicyGeneratorArgs
|
||||
expected string
|
||||
}{
|
||||
"with namespace": {
|
||||
args: types.IAMPolicyGeneratorArgs{
|
||||
Cloud: types.GKE,
|
||||
KubernetesService: types.KubernetesService{
|
||||
Namespace: "k8s-namespace",
|
||||
Name: "k8s-sa-name",
|
||||
},
|
||||
ServiceAccount: types.ServiceAccount{
|
||||
Name: "gsa-name",
|
||||
ProjectId: "project-id",
|
||||
},
|
||||
},
|
||||
expected: `
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
annotations:
|
||||
iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
|
||||
name: k8s-sa-name
|
||||
namespace: k8s-namespace
|
||||
`,
|
||||
},
|
||||
"without namespace": {
|
||||
args: types.IAMPolicyGeneratorArgs{
|
||||
Cloud: types.GKE,
|
||||
KubernetesService: types.KubernetesService{
|
||||
Name: "k8s-sa-name",
|
||||
},
|
||||
ServiceAccount: types.ServiceAccount{
|
||||
Name: "gsa-name",
|
||||
ProjectId: "project-id",
|
||||
},
|
||||
},
|
||||
expected: `
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
annotations:
|
||||
iam.gke.io/gcp-service-account: gsa-name@project-id.iam.gserviceaccount.com
|
||||
name: k8s-sa-name
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
f := Filter{
|
||||
IAMPolicyGenerator: tc.args,
|
||||
}
|
||||
actual := filtertest.RunFilter(t, "", f)
|
||||
if !assert.Equal(t, strings.TrimSpace(tc.expected), strings.TrimSpace(actual)) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,17 @@ type Filter struct {
|
||||
// FsSlice contains the FieldSpecs to locate an image field,
|
||||
// e.g. Path: "spec/myContainers[]/image"
|
||||
FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
|
||||
trackableSetter filtersutil.TrackableSetter
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
var _ kio.TrackableFilter = &Filter{}
|
||||
|
||||
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||
func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||
f.trackableSetter.WithMutationTracker(callback)
|
||||
}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
_, err := kio.FilterAll(yaml.FilterFunc(f.filter)).Filter(nodes)
|
||||
@@ -40,8 +48,11 @@ func (f Filter) filter(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
return node, nil
|
||||
}
|
||||
if err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: updateImageTagFn(f.ImageTag),
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: imageTagUpdater{
|
||||
ImageTag: f.ImageTag,
|
||||
trackableSetter: f.trackableSetter,
|
||||
}.SetImageValue,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -59,11 +70,3 @@ func (f Filter) isOnDenyList(node *yaml.RNode) bool {
|
||||
// https://github.com/kubernetes-sigs/kustomize/issues/890
|
||||
return meta.Kind == `CustomResourceDefinition`
|
||||
}
|
||||
|
||||
func updateImageTagFn(imageTag types.Image) filtersutil.SetFn {
|
||||
return func(node *yaml.RNode) error {
|
||||
return node.PipeE(imageTagUpdater{
|
||||
ImageTag: imageTag,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,18 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestImageTagUpdater_Filter(t *testing.T) {
|
||||
mutationTrackerStub := filtertest.MutationTrackerStub{}
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
filter Filter
|
||||
fsSlice types.FsSlice
|
||||
input string
|
||||
expectedOutput string
|
||||
filter Filter
|
||||
fsSlice types.FsSlice
|
||||
setValueCallback func(key, value, tag string, node *yaml.RNode)
|
||||
expectedSetValueArgs []filtertest.SetValueArg
|
||||
}{
|
||||
"ignore CustomResourceDefinition": {
|
||||
input: `
|
||||
@@ -658,17 +662,237 @@ spec:
|
||||
},
|
||||
},
|
||||
},
|
||||
"mutation tracker": {
|
||||
input: `
|
||||
group: apps
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.7.9
|
||||
name: nginx-tagged
|
||||
- image: nginx:latest
|
||||
name: nginx-latest
|
||||
- image: foobar:1
|
||||
name: replaced-with-digest
|
||||
- image: postgres:1.8.0
|
||||
name: postgresdb
|
||||
initContainers:
|
||||
- image: nginx
|
||||
name: nginx-notag
|
||||
- image: nginx@sha256:111111111111111111
|
||||
name: nginx-sha256
|
||||
- image: alpine:1.8.0
|
||||
name: init-alpine
|
||||
`,
|
||||
expectedOutput: `
|
||||
group: apps
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: busybox:v3
|
||||
name: nginx-tagged
|
||||
- image: busybox:v3
|
||||
name: nginx-latest
|
||||
- image: foobar:1
|
||||
name: replaced-with-digest
|
||||
- image: postgres:1.8.0
|
||||
name: postgresdb
|
||||
initContainers:
|
||||
- image: busybox:v3
|
||||
name: nginx-notag
|
||||
- image: busybox:v3
|
||||
name: nginx-sha256
|
||||
- image: alpine:1.8.0
|
||||
name: init-alpine
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "busybox",
|
||||
NewTag: "v3",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/template/spec/containers[]/image",
|
||||
},
|
||||
{
|
||||
Path: "spec/template/spec/initContainers[]/image",
|
||||
},
|
||||
},
|
||||
setValueCallback: mutationTrackerStub.MutationTracker,
|
||||
expectedSetValueArgs: []filtertest.SetValueArg{
|
||||
{
|
||||
Value: "busybox:v3",
|
||||
NodePath: []string{"spec", "template", "spec", "containers", "image"},
|
||||
},
|
||||
{
|
||||
Value: "busybox:v3",
|
||||
NodePath: []string{"spec", "template", "spec", "containers", "image"},
|
||||
},
|
||||
{
|
||||
Value: "busybox:v3",
|
||||
NodePath: []string{"spec", "template", "spec", "initContainers", "image"},
|
||||
},
|
||||
{
|
||||
Value: "busybox:v3",
|
||||
NodePath: []string{"spec", "template", "spec", "initContainers", "image"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"image with tag and digest new name": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: nginx:1.2.1@sha256:46d5b90a7f4e9996351ad893a26bcbd27216676ad4d5316088ce351fb2c2c3dd
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: apache:1.2.1@sha256:46d5b90a7f4e9996351ad893a26bcbd27216676ad4d5316088ce351fb2c2c3dd
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/image",
|
||||
},
|
||||
},
|
||||
},
|
||||
"image with tag and digest new name new tag": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: nginx:1.2.1@sha256:46d5b90a7f4e9996351ad893a26bcbd27216676ad4d5316088ce351fb2c2c3dd
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: apache:1.3.0
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
NewTag: "1.3.0",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/image",
|
||||
},
|
||||
},
|
||||
},
|
||||
"image with tag and digest new name new tag and digest": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: nginx:1.2.1@sha256:46d5b90a7f4e9996351ad893a26bcbd27216676ad4d5316088ce351fb2c2c3dd
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
spec:
|
||||
image: apache:1.3.0@sha256:xyz
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "nginx",
|
||||
NewName: "apache",
|
||||
NewTag: "1.3.0",
|
||||
Digest: "sha256:xyz",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/image",
|
||||
},
|
||||
},
|
||||
},
|
||||
"updateimagesuffix": {
|
||||
input: `
|
||||
group: apps
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploysuffix
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: redis:6.2.6
|
||||
name: redis
|
||||
`,
|
||||
expectedOutput: `
|
||||
group: apps
|
||||
apiVersion: v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploysuffix
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: redis:6.2.6-alpine
|
||||
name: redis
|
||||
`,
|
||||
filter: Filter{
|
||||
ImageTag: types.Image{
|
||||
Name: "redis",
|
||||
TagSuffix: "-alpine",
|
||||
},
|
||||
},
|
||||
fsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "spec/template/spec/containers[]/image",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
mutationTrackerStub.Reset()
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
filter := tc.filter
|
||||
filter.WithMutationTracker(tc.setValueCallback)
|
||||
filter.FsSlice = tc.fsSlice
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(filtertest.RunFilter(t, tc.input, filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, tc.expectedSetValueArgs, mutationTrackerStub.SetValueArgs())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
@@ -74,28 +76,17 @@ func (f findFieldsFilter) walk(node *yaml.RNode) error {
|
||||
return err
|
||||
}
|
||||
key := n.Key.YNode().Value
|
||||
if contains(f.fields, key) {
|
||||
if utils.StringSliceContains(f.fields, key) {
|
||||
return f.fieldCallback(n.Value)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
case yaml.SequenceNode:
|
||||
return node.VisitElements(func(n *yaml.RNode) error {
|
||||
return f.walk(n)
|
||||
})
|
||||
return errors.Wrap(node.VisitElements(f.walk))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func contains(slice []string, str string) bool {
|
||||
for _, s := range slice {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkImageTagsFn(imageTag types.Image) fieldCallback {
|
||||
return func(node *yaml.RNode) error {
|
||||
if node.YNode().Kind != yaml.SequenceNode {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
package imagetag
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/image"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
@@ -13,31 +15,57 @@ import (
|
||||
// that will update the value of the yaml node based on the provided
|
||||
// ImageTag if the current value matches the format of an image reference.
|
||||
type imageTagUpdater struct {
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
ImageTag types.Image `yaml:"imageTag,omitempty"`
|
||||
Kind string `yaml:"kind,omitempty"`
|
||||
ImageTag types.Image `yaml:"imageTag,omitempty"`
|
||||
trackableSetter filtersutil.TrackableSetter
|
||||
}
|
||||
|
||||
func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) {
|
||||
func (u imageTagUpdater) SetImageValue(rn *yaml.RNode) error {
|
||||
if err := yaml.ErrorIfInvalid(rn, yaml.ScalarNode); err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
value := rn.YNode().Value
|
||||
|
||||
if !image.IsImageMatched(value, u.ImageTag.Name) {
|
||||
return rn, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
name, tag := image.Split(value)
|
||||
name, tag, digest := image.Split(value)
|
||||
if u.ImageTag.NewName != "" {
|
||||
name = u.ImageTag.NewName
|
||||
}
|
||||
if u.ImageTag.NewTag != "" {
|
||||
tag = ":" + u.ImageTag.NewTag
|
||||
}
|
||||
if u.ImageTag.Digest != "" {
|
||||
tag = "@" + u.ImageTag.Digest
|
||||
|
||||
// overriding tag or digest will replace both original tag and digest values
|
||||
switch {
|
||||
case u.ImageTag.NewTag != "" && u.ImageTag.Digest != "":
|
||||
tag = u.ImageTag.NewTag
|
||||
digest = u.ImageTag.Digest
|
||||
case u.ImageTag.NewTag != "":
|
||||
tag = u.ImageTag.NewTag
|
||||
digest = ""
|
||||
case u.ImageTag.Digest != "":
|
||||
tag = ""
|
||||
digest = u.ImageTag.Digest
|
||||
case u.ImageTag.TagSuffix != "":
|
||||
tag += u.ImageTag.TagSuffix
|
||||
digest = ""
|
||||
}
|
||||
|
||||
return rn.Pipe(yaml.FieldSetter{StringValue: name + tag})
|
||||
// build final image name
|
||||
if tag != "" {
|
||||
name += ":" + tag
|
||||
}
|
||||
if digest != "" {
|
||||
name += "@" + digest
|
||||
}
|
||||
|
||||
return u.trackableSetter.SetScalar(name)(rn)
|
||||
}
|
||||
|
||||
func (u imageTagUpdater) Filter(rn *yaml.RNode) (*yaml.RNode, error) {
|
||||
if err := u.SetImageValue(rn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rn, nil
|
||||
}
|
||||
|
||||
@@ -20,9 +20,17 @@ type Filter struct {
|
||||
|
||||
// FsSlice identifies the label fields.
|
||||
FsSlice types.FsSlice
|
||||
|
||||
trackableSetter filtersutil.TrackableSetter
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
var _ kio.TrackableFilter = &Filter{}
|
||||
|
||||
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||
func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||
f.trackableSetter.WithMutationTracker(callback)
|
||||
}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
keys := yaml.SortedMapKeys(f.Labels)
|
||||
@@ -31,7 +39,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for _, k := range keys {
|
||||
if err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: f.FsSlice,
|
||||
SetValue: filtersutil.SetEntry(
|
||||
SetValue: f.trackableSetter.SetEntry(
|
||||
k, f.Labels[k], yaml.NodeTagString),
|
||||
CreateKind: yaml.MappingNode, // Labels are MappingNodes.
|
||||
CreateTag: yaml.NodeTagMap,
|
||||
|
||||
@@ -8,16 +8,20 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestLabels_Filter(t *testing.T) {
|
||||
mutationTrackerStub := filtertest_test.MutationTrackerStub{}
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
filter Filter
|
||||
input string
|
||||
expectedOutput string
|
||||
filter Filter
|
||||
setEntryCallback func(key, value, tag string, node *yaml.RNode)
|
||||
expectedSetEntryArgs []filtertest_test.SetValueArg
|
||||
}{
|
||||
"add": {
|
||||
input: `
|
||||
@@ -399,15 +403,74 @@ metadata:
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// test usage of SetEntryCallback
|
||||
"set_entry_callback": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
labels:
|
||||
witcher: geralt
|
||||
`,
|
||||
expectedOutput: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
labels:
|
||||
witcher: geralt
|
||||
mage: yennefer
|
||||
a:
|
||||
b:
|
||||
mage: yennefer
|
||||
`,
|
||||
filter: Filter{
|
||||
Labels: labelMap{
|
||||
"mage": "yennefer",
|
||||
},
|
||||
FsSlice: []types.FieldSpec{
|
||||
{
|
||||
Path: "metadata/labels",
|
||||
CreateIfNotPresent: true,
|
||||
},
|
||||
{
|
||||
Path: "a/b",
|
||||
CreateIfNotPresent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
setEntryCallback: mutationTrackerStub.MutationTracker,
|
||||
expectedSetEntryArgs: []filtertest_test.SetValueArg{
|
||||
{
|
||||
Key: "mage",
|
||||
Value: "yennefer",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"metadata", "labels"},
|
||||
},
|
||||
{
|
||||
Key: "mage",
|
||||
Value: "yennefer",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"a", "b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
mutationTrackerStub.Reset()
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
tc.filter.WithMutationTracker(tc.setEntryCallback)
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expectedOutput),
|
||||
strings.TrimSpace(filtertest_test.RunFilter(t, tc.input, tc.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
if !assert.Equal(t, tc.expectedSetEntryArgs, mutationTrackerStub.SetValueArgs()) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package nameref contains a kio.Filter implementation of the kustomize
|
||||
// name reference transformer.
|
||||
package nameref
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nameref
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||
"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/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -56,11 +59,12 @@ func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
// sanity check.
|
||||
return nil, err
|
||||
}
|
||||
f.NameFieldToUpdate.Gvk = f.Referrer.GetGvk()
|
||||
if err := node.PipeE(fieldspec.Filter{
|
||||
FieldSpec: f.NameFieldToUpdate,
|
||||
SetValue: f.set,
|
||||
}); err != nil {
|
||||
return nil, errors.Wrapf(
|
||||
return nil, errors.WrapPrefixf(
|
||||
err, "updating name reference in '%s' field of '%s'",
|
||||
f.NameFieldToUpdate.Path, f.Referrer.CurId().String())
|
||||
}
|
||||
@@ -100,7 +104,7 @@ func (f Filter) setMapping(node *yaml.RNode) error {
|
||||
}
|
||||
nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "trying to match 'name' field")
|
||||
return errors.WrapPrefixf(err, "trying to match 'name' field")
|
||||
}
|
||||
if nameNode == nil {
|
||||
// This is a _configuration_ error; the field path
|
||||
@@ -114,7 +118,9 @@ func (f Filter) setMapping(node *yaml.RNode) error {
|
||||
return err
|
||||
}
|
||||
oldName := nameNode.YNode().Value
|
||||
referral, err := f.selectReferral(oldName, candidates)
|
||||
// use allNamesAndNamespacesAreTheSame to compare referral candidates for functional identity,
|
||||
// because we source both name and namespace values from the referral in this case.
|
||||
referral, err := f.selectReferral(oldName, candidates, allNamesAndNamespacesAreTheSame)
|
||||
if err != nil || referral == nil {
|
||||
// Nil referral means nothing to do.
|
||||
return err
|
||||
@@ -147,7 +153,7 @@ func (f Filter) filterMapCandidatesByNamespace(
|
||||
node *yaml.RNode) ([]*resource.Resource, error) {
|
||||
namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "trying to match 'namespace' field")
|
||||
return nil, errors.WrapPrefixf(err, "trying to match 'namespace' field")
|
||||
}
|
||||
if namespaceNode == nil {
|
||||
return f.ReferralCandidates.Resources(), nil
|
||||
@@ -163,8 +169,10 @@ func (f Filter) filterMapCandidatesByNamespace(
|
||||
}
|
||||
|
||||
func (f Filter) setScalar(node *yaml.RNode) error {
|
||||
// use allNamesAreTheSame to compare referral candidates for functional identity,
|
||||
// because we only source the name from the referral in this case.
|
||||
referral, err := f.selectReferral(
|
||||
node.YNode().Value, f.ReferralCandidates.Resources())
|
||||
node.YNode().Value, f.ReferralCandidates.Resources(), allNamesAreTheSame)
|
||||
if err != nil || referral == nil {
|
||||
// Nil referral means nothing to do.
|
||||
return err
|
||||
@@ -184,7 +192,7 @@ func (f Filter) recordTheReferral(referral *resource.Resource) {
|
||||
|
||||
// getRoleRefGvk returns a Gvk in the roleRef field. Return error
|
||||
// if the roleRef, roleRef/apiGroup or roleRef/kind is missing.
|
||||
func getRoleRefGvk(n *yaml.RNode) (*resid.Gvk, error) {
|
||||
func getRoleRefGvk(n *resource.Resource) (*resid.Gvk, error) {
|
||||
roleRef, err := n.Pipe(yaml.Lookup("roleRef"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -257,8 +265,7 @@ func previousIdSelectedByGvk(gvk *resid.Gvk) sieveFunc {
|
||||
|
||||
// If the we are updating a 'roleRef/name' field, the 'apiGroup' and 'kind'
|
||||
// fields in the same 'roleRef' map must be considered.
|
||||
// If either object is cluster-scoped (!IsNamespaceableKind), there
|
||||
// can be a referral.
|
||||
// If either object is cluster-scoped, there can be a referral.
|
||||
// E.g. a RoleBinding (which exists in a namespace) can refer
|
||||
// to a ClusterRole (cluster-scoped) object.
|
||||
// https://kubernetes.io/docs/reference/access-authn-authz/rbac/#role-and-clusterrole
|
||||
@@ -270,7 +277,7 @@ func (f Filter) roleRefFilter() sieveFunc {
|
||||
if !strings.HasSuffix(f.NameFieldToUpdate.Path, "roleRef/name") {
|
||||
return acceptAll
|
||||
}
|
||||
roleRefGvk, err := getRoleRefGvk(f.Referrer.AsRNode())
|
||||
roleRefGvk, err := getRoleRefGvk(f.Referrer)
|
||||
if err != nil {
|
||||
return acceptAll
|
||||
}
|
||||
@@ -285,12 +292,12 @@ func prefixSuffixEquals(other resource.ResCtx) sieveFunc {
|
||||
|
||||
func (f Filter) sameCurrentNamespaceAsReferrer() sieveFunc {
|
||||
referrerCurId := f.Referrer.CurId()
|
||||
if !referrerCurId.IsNamespaceableKind() {
|
||||
if referrerCurId.IsClusterScoped() {
|
||||
// If the referrer is cluster-scoped, let anything through.
|
||||
return acceptAll
|
||||
}
|
||||
return func(r *resource.Resource) bool {
|
||||
if !r.CurId().IsNamespaceableKind() {
|
||||
if r.CurId().IsClusterScoped() {
|
||||
// Allow cluster-scoped through.
|
||||
return true
|
||||
}
|
||||
@@ -308,7 +315,9 @@ func (f Filter) sameCurrentNamespaceAsReferrer() sieveFunc {
|
||||
func (f Filter) selectReferral(
|
||||
// The name referral that may need to be updated.
|
||||
oldName string,
|
||||
candidates []*resource.Resource) (*resource.Resource, error) {
|
||||
candidates []*resource.Resource,
|
||||
// function that returns whether two referrals are identical for the purposes of the transformation
|
||||
candidatesIdentical func(resources []*resource.Resource) bool) (*resource.Resource, error) {
|
||||
candidates = doSieve(candidates, previousNameMatches(oldName))
|
||||
candidates = doSieve(candidates, previousIdSelectedByGvk(&f.ReferralTarget))
|
||||
candidates = doSieve(candidates, f.roleRefFilter())
|
||||
@@ -323,24 +332,21 @@ func (f Filter) selectReferral(
|
||||
if len(candidates) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if allNamesAreTheSame(candidates) {
|
||||
if candidatesIdentical(candidates) {
|
||||
// Just take the first one.
|
||||
return candidates[0], nil
|
||||
}
|
||||
ids := getIds(candidates)
|
||||
f.failureDetails(candidates)
|
||||
return nil, fmt.Errorf(" found multiple possible referrals: %s", ids)
|
||||
return nil, fmt.Errorf("found multiple possible referrals: %s\n%s", ids, f.failureDetails(candidates))
|
||||
}
|
||||
|
||||
func (f Filter) failureDetails(resources []*resource.Resource) {
|
||||
fmt.Printf(
|
||||
"\n**** Too many possible referral targets to referrer:\n%s\n",
|
||||
f.Referrer.MustYaml())
|
||||
func (f Filter) failureDetails(resources []*resource.Resource) string {
|
||||
msg := strings.Builder{}
|
||||
msg.WriteString(fmt.Sprintf("\n**** Too many possible referral targets to referrer:\n%s\n", f.Referrer.MustYaml()))
|
||||
for i, r := range resources {
|
||||
fmt.Printf(
|
||||
"--- possible referral %d:\n%s", i, r.MustYaml())
|
||||
fmt.Println("------")
|
||||
msg.WriteString(fmt.Sprintf("--- possible referral %d:\n%s\n", i, r.MustYaml()))
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
||||
func allNamesAreTheSame(resources []*resource.Resource) bool {
|
||||
@@ -353,6 +359,17 @@ func allNamesAreTheSame(resources []*resource.Resource) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func allNamesAndNamespacesAreTheSame(resources []*resource.Resource) bool {
|
||||
name := resources[0].GetName()
|
||||
namespace := resources[0].GetNamespace()
|
||||
for i := 1; i < len(resources); i++ {
|
||||
if name != resources[i].GetName() || namespace != resources[i].GetNamespace() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func getIds(rs []*resource.Resource) string {
|
||||
var result []string
|
||||
for _, r := range rs {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nameref
|
||||
|
||||
import (
|
||||
@@ -6,10 +9,10 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
)
|
||||
|
||||
func TestNamerefFilter(t *testing.T) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nameref
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package nameref
|
||||
|
||||
import (
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
package namespace
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/api/filters/fsslice"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
@@ -18,9 +19,36 @@ type Filter struct {
|
||||
|
||||
// FsSlice contains the FieldSpecs to locate the namespace field
|
||||
FsSlice types.FsSlice `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||
|
||||
// UnsetOnly means only blank namespace fields will be set
|
||||
UnsetOnly bool `json:"unsetOnly" yaml:"unsetOnly"`
|
||||
|
||||
// SetRoleBindingSubjects determines which subject fields in RoleBinding and ClusterRoleBinding
|
||||
// objects will have their namespace fields set. Overrides field specs provided for these types, if any.
|
||||
// - defaultOnly (default): namespace will be set only on subjects named "default".
|
||||
// - allServiceAccounts: namespace will be set on all subjects with "kind: ServiceAccount"
|
||||
// - none: all subjects will be skipped.
|
||||
SetRoleBindingSubjects RoleBindingSubjectMode `json:"setRoleBindingSubjects" yaml:"setRoleBindingSubjects"`
|
||||
|
||||
trackableSetter filtersutil.TrackableSetter
|
||||
}
|
||||
|
||||
type RoleBindingSubjectMode string
|
||||
|
||||
const (
|
||||
DefaultSubjectsOnly RoleBindingSubjectMode = "defaultOnly"
|
||||
SubjectModeUnspecified RoleBindingSubjectMode = ""
|
||||
AllServiceAccountSubjects RoleBindingSubjectMode = "allServiceAccounts"
|
||||
NoSubjects RoleBindingSubjectMode = "none"
|
||||
)
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
var _ kio.TrackableFilter = &Filter{}
|
||||
|
||||
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||
func (ns *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||
ns.trackableSetter.WithMutationTracker(callback)
|
||||
}
|
||||
|
||||
func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(ns.run)).Filter(nodes)
|
||||
@@ -28,74 +56,63 @@ func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
|
||||
// Run runs the filter on a single node rather than a slice
|
||||
func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
// hacks for hardcoded types -- :(
|
||||
if err := ns.hacks(node); err != nil {
|
||||
// Special handling for metadata.namespace -- :(
|
||||
// never let SetEntry handle metadata.namespace--it will incorrectly include cluster-scoped resources
|
||||
ns.FsSlice = ns.removeMetaNamespaceFieldSpecs(ns.FsSlice)
|
||||
gvk := resid.GvkFromNode(node)
|
||||
if err := ns.metaNamespaceHack(node, gvk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove the fieldspecs that are for hardcoded fields. The fieldspecs
|
||||
// exist for backwards compatibility with other implementations
|
||||
// of this transformation.
|
||||
// This implementation of the namespace transformation
|
||||
// Does not use the fieldspecs for implementing cases which
|
||||
// require hardcoded logic.
|
||||
ns.FsSlice = ns.removeFieldSpecsForHacks(ns.FsSlice)
|
||||
// Special handling for (cluster) role binding subjects -- :(
|
||||
if isRoleBinding(gvk.Kind) {
|
||||
ns.FsSlice = ns.removeRoleBindingSubjectFieldSpecs(ns.FsSlice)
|
||||
if err := ns.roleBindingHack(node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// transformations based on data -- :)
|
||||
err := node.PipeE(fsslice.Filter{
|
||||
FsSlice: ns.FsSlice,
|
||||
SetValue: filtersutil.SetScalar(ns.Namespace),
|
||||
SetValue: ns.fieldSetter(),
|
||||
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
||||
CreateTag: yaml.NodeTagString,
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
// hacks applies the namespace transforms that are hardcoded rather
|
||||
// than specified through FieldSpecs.
|
||||
func (ns Filter) hacks(obj *yaml.RNode) error {
|
||||
meta, err := obj.GetMeta()
|
||||
if err != nil {
|
||||
return err
|
||||
invalidKindErr := &yaml.InvalidNodeKindError{}
|
||||
if err != nil && errors.As(err, &invalidKindErr) && invalidKindErr.ActualNodeKind() != yaml.ScalarNode {
|
||||
return nil, errors.WrapPrefixf(err, "namespace field specs must target scalar nodes")
|
||||
}
|
||||
|
||||
if err := ns.metaNamespaceHack(obj, meta); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ns.roleBindingHack(obj, meta)
|
||||
return node, errors.WrapPrefixf(err, "namespace transformation failed")
|
||||
}
|
||||
|
||||
// metaNamespaceHack is a hack for implementing the namespace transform
|
||||
// for the metadata.namespace field on namespace scoped resources.
|
||||
// namespace scoped resources are determined by NOT being present
|
||||
// in a hard-coded list of cluster-scoped resource types (by apiVersion and kind).
|
||||
//
|
||||
// This hack should be updated to allow individual resources to specify
|
||||
// if they are cluster scoped through either an annotation on the resources,
|
||||
// or through inlined OpenAPI on the resource as a YAML comment.
|
||||
func (ns Filter) metaNamespaceHack(obj *yaml.RNode, meta yaml.ResourceMeta) error {
|
||||
gvk := fieldspec.GetGVK(meta)
|
||||
if !gvk.IsNamespaceableKind() {
|
||||
func (ns Filter) metaNamespaceHack(obj *yaml.RNode, gvk resid.Gvk) error {
|
||||
if gvk.IsClusterScoped() {
|
||||
return nil
|
||||
}
|
||||
f := fsslice.Filter{
|
||||
FsSlice: []types.FieldSpec{
|
||||
{Path: types.MetadataNamespacePath, CreateIfNotPresent: true},
|
||||
},
|
||||
SetValue: filtersutil.SetScalar(ns.Namespace),
|
||||
SetValue: ns.fieldSetter(),
|
||||
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
||||
}
|
||||
_, err := f.Filter(obj)
|
||||
return err
|
||||
}
|
||||
|
||||
// roleBindingHack is a hack for implementing the namespace transform
|
||||
// roleBindingHack is a hack for implementing the transformer's SetRoleBindingSubjects option
|
||||
// for RoleBinding and ClusterRoleBinding resource types.
|
||||
// RoleBinding and ClusterRoleBinding have namespace set on
|
||||
//
|
||||
// In NoSubjects mode, it does nothing.
|
||||
//
|
||||
// In AllServiceAccountSubjects mode, it sets the namespace on subjects with "kind: ServiceAccount".
|
||||
//
|
||||
// In DefaultSubjectsOnly mode (default mode), RoleBinding and ClusterRoleBinding have namespace set on
|
||||
// elements of the "subjects" field if and only if the subject elements
|
||||
// "name" is "default". Otherwise the namespace is not set.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// kind: RoleBinding
|
||||
@@ -104,56 +121,64 @@ func (ns Filter) metaNamespaceHack(obj *yaml.RNode, meta yaml.ResourceMeta) erro
|
||||
// ...
|
||||
// - name: "something-else" # this will not have the namespace set
|
||||
// ...
|
||||
func (ns Filter) roleBindingHack(obj *yaml.RNode, meta yaml.ResourceMeta) error {
|
||||
if meta.Kind != roleBindingKind && meta.Kind != clusterRoleBindingKind {
|
||||
func (ns Filter) roleBindingHack(obj *yaml.RNode) error {
|
||||
var visitor filtersutil.SetFn
|
||||
switch ns.SetRoleBindingSubjects {
|
||||
case NoSubjects:
|
||||
return nil
|
||||
case DefaultSubjectsOnly, SubjectModeUnspecified:
|
||||
visitor = ns.setSubjectsNamedDefault
|
||||
case AllServiceAccountSubjects:
|
||||
visitor = ns.setServiceAccountNamespaces
|
||||
default:
|
||||
return errors.Errorf("invalid value %q for setRoleBindingSubjects: "+
|
||||
"must be one of %q, %q or %q", ns.SetRoleBindingSubjects,
|
||||
DefaultSubjectsOnly, NoSubjects, AllServiceAccountSubjects)
|
||||
}
|
||||
|
||||
// Lookup the namespace field on all elements.
|
||||
// We should change the fieldspec so this isn't necessary.
|
||||
// Lookup the subjects field on all elements.
|
||||
obj, err := obj.Pipe(yaml.Lookup(subjectsField))
|
||||
if err != nil || yaml.IsMissingOrNull(obj) {
|
||||
return err
|
||||
}
|
||||
|
||||
// add the namespace to each "subject" with name: default
|
||||
err = obj.VisitElements(func(o *yaml.RNode) error {
|
||||
// The only case we need to force the namespace
|
||||
// if for the "service account". "default" is
|
||||
// kind of hardcoded here for right now.
|
||||
name, err := o.Pipe(
|
||||
yaml.Lookup("name"), yaml.Match("default"),
|
||||
)
|
||||
if err != nil || yaml.IsMissingOrNull(name) {
|
||||
return err
|
||||
}
|
||||
|
||||
// set the namespace for the default account
|
||||
v := yaml.NewScalarRNode(ns.Namespace)
|
||||
return o.PipeE(
|
||||
yaml.LookupCreate(yaml.ScalarNode, "namespace"),
|
||||
yaml.FieldSetter{Value: v},
|
||||
)
|
||||
})
|
||||
|
||||
return err
|
||||
// Use the appropriate visitor to set the namespace field on the correct subset of subjects
|
||||
return errors.WrapPrefixf(obj.VisitElements(visitor), "setting namespace on (cluster)role binding subjects")
|
||||
}
|
||||
|
||||
// removeFieldSpecsForHacks removes from the list fieldspecs that
|
||||
func isRoleBinding(kind string) bool {
|
||||
return kind == roleBindingKind || kind == clusterRoleBindingKind
|
||||
}
|
||||
|
||||
func (ns Filter) setServiceAccountNamespaces(o *yaml.RNode) error {
|
||||
name, err := o.Pipe(yaml.Lookup("kind"), yaml.Match("ServiceAccount"))
|
||||
if err != nil || yaml.IsMissingOrNull(name) {
|
||||
return errors.WrapPrefixf(err, "looking up kind on (cluster)role binding subject")
|
||||
}
|
||||
return setNamespaceField(o, ns.fieldSetter())
|
||||
}
|
||||
|
||||
func (ns Filter) setSubjectsNamedDefault(o *yaml.RNode) error {
|
||||
name, err := o.Pipe(yaml.Lookup("name"), yaml.Match("default"))
|
||||
if err != nil || yaml.IsMissingOrNull(name) {
|
||||
return errors.WrapPrefixf(err, "looking up name on (cluster)role binding subject")
|
||||
}
|
||||
return setNamespaceField(o, ns.fieldSetter())
|
||||
}
|
||||
|
||||
func setNamespaceField(node *yaml.RNode, setter filtersutil.SetFn) error {
|
||||
node, err := node.Pipe(yaml.LookupCreate(yaml.ScalarNode, "namespace"))
|
||||
if err != nil {
|
||||
return errors.WrapPrefixf(err, "setting namespace field on (cluster)role binding subject")
|
||||
}
|
||||
return setter(node)
|
||||
}
|
||||
|
||||
// removeRoleBindingSubjectFieldSpecs removes from the list fieldspecs that
|
||||
// have hardcoded implementations
|
||||
func (ns Filter) removeFieldSpecsForHacks(fs types.FsSlice) types.FsSlice {
|
||||
func (ns Filter) removeRoleBindingSubjectFieldSpecs(fs types.FsSlice) types.FsSlice {
|
||||
var val types.FsSlice
|
||||
for i := range fs {
|
||||
// implemented by metaNamespaceHack
|
||||
if fs[i].Path == types.MetadataNamespacePath {
|
||||
continue
|
||||
}
|
||||
// implemented by roleBindingHack
|
||||
if fs[i].Kind == roleBindingKind && fs[i].Path == subjectsField {
|
||||
continue
|
||||
}
|
||||
// implemented by roleBindingHack
|
||||
if fs[i].Kind == clusterRoleBindingKind && fs[i].Path == subjectsField {
|
||||
if isRoleBinding(fs[i].Kind) && fs[i].Path == subjectsNamespacePath {
|
||||
continue
|
||||
}
|
||||
val = append(val, fs[i])
|
||||
@@ -161,8 +186,27 @@ func (ns Filter) removeFieldSpecsForHacks(fs types.FsSlice) types.FsSlice {
|
||||
return val
|
||||
}
|
||||
|
||||
func (ns Filter) removeMetaNamespaceFieldSpecs(fs types.FsSlice) types.FsSlice {
|
||||
var val types.FsSlice
|
||||
for i := range fs {
|
||||
if fs[i].Path == types.MetadataNamespacePath {
|
||||
continue
|
||||
}
|
||||
val = append(val, fs[i])
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (ns *Filter) fieldSetter() filtersutil.SetFn {
|
||||
if ns.UnsetOnly {
|
||||
return ns.trackableSetter.SetEntryIfEmpty("", ns.Namespace, yaml.NodeTagString)
|
||||
}
|
||||
return ns.trackableSetter.SetEntry("", ns.Namespace, yaml.NodeTagString)
|
||||
}
|
||||
|
||||
const (
|
||||
subjectsField = "subjects"
|
||||
subjectsNamespacePath = "subjects/namespace"
|
||||
roleBindingKind = "RoleBinding"
|
||||
clusterRoleBindingKind = "ClusterRoleBinding"
|
||||
)
|
||||
|
||||
@@ -12,8 +12,11 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var mutationTrackerStub = filtertest_test.MutationTrackerStub{}
|
||||
|
||||
var tests = []TestCase{
|
||||
{
|
||||
name: "add",
|
||||
@@ -283,29 +286,134 @@ a:
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "mutation tracker",
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: RoleBinding
|
||||
subjects:
|
||||
- name: default
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
namespace: bar
|
||||
a:
|
||||
b:
|
||||
c: bar
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: RoleBinding
|
||||
subjects:
|
||||
- name: default
|
||||
namespace: bar
|
||||
metadata:
|
||||
namespace: bar
|
||||
a:
|
||||
b:
|
||||
c: bar
|
||||
`,
|
||||
filter: namespace.Filter{Namespace: "bar"},
|
||||
fsslice: []types.FieldSpec{
|
||||
{
|
||||
Path: "a/b/c",
|
||||
CreateIfNotPresent: true,
|
||||
},
|
||||
},
|
||||
mutationTracker: mutationTrackerStub.MutationTracker,
|
||||
expectedSetValueArgs: []filtertest_test.SetValueArg{
|
||||
{
|
||||
Value: "bar",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"metadata", "namespace"},
|
||||
},
|
||||
{
|
||||
Value: "bar",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"a", "b", "c"},
|
||||
},
|
||||
{
|
||||
Value: "bar",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"metadata", "namespace"},
|
||||
},
|
||||
{
|
||||
Value: "bar",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"namespace"},
|
||||
},
|
||||
{
|
||||
Value: "bar",
|
||||
Tag: "!!str",
|
||||
NodePath: []string{"a", "b", "c"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "numeric",
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
namespace: "01234"
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
namespace: "01234"
|
||||
`,
|
||||
filter: namespace.Filter{Namespace: "01234"},
|
||||
},
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
filter namespace.Filter
|
||||
fsslice types.FsSlice
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
filter namespace.Filter
|
||||
fsslice types.FsSlice
|
||||
mutationTracker func(key, value, tag string, node *yaml.RNode)
|
||||
expectedSetValueArgs []filtertest_test.SetValueArg
|
||||
}
|
||||
|
||||
var config = builtinconfig.MakeDefaultConfig()
|
||||
|
||||
func TestNamespace_Filter(t *testing.T) {
|
||||
for i := range tests {
|
||||
mutationTrackerStub.Reset()
|
||||
test := tests[i]
|
||||
test.filter.WithMutationTracker(test.mutationTracker)
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
test.filter.FsSlice = append(config.NameSpace, test.fsslice...)
|
||||
test.filter.FsSlice = append(config.NameSpace, test.fsslice...) //nolint:gocritic
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest_test.RunFilter(t, test.input, test.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package patchstrategicmerge
|
||||
|
||||
import (
|
||||
@@ -729,6 +732,50 @@ spec:
|
||||
protocol: "TCP"
|
||||
- containerPort: 8301
|
||||
protocol: "UDP"
|
||||
`,
|
||||
},
|
||||
|
||||
// Issue #4628
|
||||
"should retain existing null values in targets": {
|
||||
input: `
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: chart
|
||||
spec:
|
||||
releaseName: helm-chart
|
||||
timeout: 15m
|
||||
values:
|
||||
chart:
|
||||
replicaCount: null
|
||||
autoscaling: true
|
||||
`,
|
||||
patch: yaml.MustParse(`
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: chart
|
||||
spec:
|
||||
releaseName: helm-chart
|
||||
timeout: 15m
|
||||
values:
|
||||
deepgram-api:
|
||||
some: value
|
||||
`),
|
||||
expected: `
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: chart
|
||||
spec:
|
||||
releaseName: helm-chart
|
||||
timeout: 15m
|
||||
values:
|
||||
chart:
|
||||
replicaCount: null
|
||||
autoscaling: true
|
||||
deepgram-api:
|
||||
some: value
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
6
api/filters/prefix/doc.go
Normal file
6
api/filters/prefix/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package prefix contains a kio.Filter implementation of the kustomize
|
||||
// PrefixTransformer.
|
||||
package prefix
|
||||
47
api/filters/prefix/example_test.go
Normal file
47
api/filters/prefix/example_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefix_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/prefix"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`)}},
|
||||
Filters: []kio.Filter{prefix.Filter{
|
||||
Prefix: "baz-", FieldSpec: types.FieldSpec{Path: "metadata/name"}}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: baz-instance
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: baz-instance
|
||||
}
|
||||
50
api/filters/prefix/prefix.go
Normal file
50
api/filters/prefix/prefix.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filter applies resource name prefix's using the fieldSpecs
|
||||
type Filter struct {
|
||||
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||
|
||||
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
|
||||
|
||||
trackableSetter filtersutil.TrackableSetter
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
var _ kio.TrackableFilter = &Filter{}
|
||||
|
||||
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||
func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||
f.trackableSetter.WithMutationTracker(callback)
|
||||
}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
err := node.PipeE(fieldspec.Filter{
|
||||
FieldSpec: f.FieldSpec,
|
||||
SetValue: f.evaluateField,
|
||||
CreateKind: yaml.ScalarNode, // Name is a ScalarNode
|
||||
CreateTag: yaml.NodeTagString,
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
func (f Filter) evaluateField(node *yaml.RNode) error {
|
||||
return f.trackableSetter.SetScalar(fmt.Sprintf(
|
||||
"%s%s", f.Prefix, node.YNode().Value))(node)
|
||||
}
|
||||
154
api/filters/prefix/prefix_test.go
Normal file
154
api/filters/prefix/prefix_test.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefix_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/filters/prefix"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var mutationTrackerStub = filtertest_test.MutationTrackerStub{}
|
||||
|
||||
var tests = map[string]TestCase{
|
||||
"prefix": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo-instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: foo-instance
|
||||
`,
|
||||
filter: prefix.Filter{
|
||||
Prefix: "foo-",
|
||||
FieldSpec: types.FieldSpec{Path: "metadata/name"},
|
||||
},
|
||||
},
|
||||
|
||||
"data-fieldspecs": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: foo-d
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: foo-d
|
||||
`,
|
||||
filter: prefix.Filter{
|
||||
Prefix: "foo-",
|
||||
FieldSpec: types.FieldSpec{Path: "a/b/c"},
|
||||
},
|
||||
},
|
||||
|
||||
"mutation tracker": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo-instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: foo-instance
|
||||
`,
|
||||
filter: prefix.Filter{
|
||||
Prefix: "foo-",
|
||||
FieldSpec: types.FieldSpec{Path: "metadata/name"},
|
||||
},
|
||||
mutationTracker: mutationTrackerStub.MutationTracker,
|
||||
expectedSetValueArgs: []filtertest_test.SetValueArg{
|
||||
{
|
||||
Value: "foo-instance",
|
||||
NodePath: []string{"metadata", "name"},
|
||||
},
|
||||
{
|
||||
Value: "foo-instance",
|
||||
NodePath: []string{"metadata", "name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
input string
|
||||
expected string
|
||||
filter prefix.Filter
|
||||
mutationTracker func(key, value, tag string, node *yaml.RNode)
|
||||
expectedSetValueArgs []filtertest_test.SetValueArg
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
for name := range tests {
|
||||
mutationTrackerStub.Reset()
|
||||
test := tests[name]
|
||||
test.filter.WithMutationTracker(test.mutationTracker)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest_test.RunFilter(t, test.input, test.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package prefixsuffix contains a kio.Filter implementation of the kustomize
|
||||
// PrefixSuffixTransformer.
|
||||
package prefixsuffix
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2020 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefixsuffix_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`)}},
|
||||
Filters: []kio.Filter{prefixsuffix.Filter{
|
||||
Prefix: "baz-", FieldSpec: types.FieldSpec{Path: "metadata/name"}}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: baz-instance
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: baz-instance
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefixsuffix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filter applies resource name prefix's and suffix's using the fieldSpecs
|
||||
type Filter struct {
|
||||
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
||||
|
||||
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
err := node.PipeE(fieldspec.Filter{
|
||||
FieldSpec: f.FieldSpec,
|
||||
SetValue: f.evaluateField,
|
||||
CreateKind: yaml.ScalarNode, // Name is a ScalarNode
|
||||
CreateTag: yaml.NodeTagString,
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
func (f Filter) evaluateField(node *yaml.RNode) error {
|
||||
return filtersutil.SetScalar(fmt.Sprintf(
|
||||
"%s%s%s", f.Prefix, node.YNode().Value, f.Suffix))(node)
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package prefixsuffix_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/filters/prefixsuffix"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
)
|
||||
|
||||
var tests = map[string]TestCase{
|
||||
"prefix": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: foo-instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: foo-instance
|
||||
`,
|
||||
filter: prefixsuffix.Filter{Prefix: "foo-"},
|
||||
fs: types.FieldSpec{Path: "metadata/name"},
|
||||
},
|
||||
|
||||
"suffix": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance-foo
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance-foo
|
||||
`,
|
||||
filter: prefixsuffix.Filter{Suffix: "-foo"},
|
||||
fs: types.FieldSpec{Path: "metadata/name"},
|
||||
},
|
||||
|
||||
"prefix-suffix": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: bar-instance-foo
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: bar-instance-foo
|
||||
`,
|
||||
filter: prefixsuffix.Filter{Prefix: "bar-", Suffix: "-foo"},
|
||||
fs: types.FieldSpec{Path: "metadata/name"},
|
||||
},
|
||||
|
||||
"data-fieldspecs": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: foo-d
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: foo-d
|
||||
`,
|
||||
filter: prefixsuffix.Filter{Prefix: "foo-"},
|
||||
fs: types.FieldSpec{Path: "a/b/c"},
|
||||
},
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
input string
|
||||
expected string
|
||||
filter prefixsuffix.Filter
|
||||
fs types.FieldSpec
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
for name := range tests {
|
||||
test := tests[name]
|
||||
t.Run(name, func(t *testing.T) {
|
||||
test.filter.FieldSpec = test.fs
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest_test.RunFilter(t, test.input, test.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package refvar contains a kio.Filter implementation of the kustomize
|
||||
// refvar transformer (find and replace $(FOO) style variables in strings).
|
||||
package refvar
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package refvar
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package refvar_test
|
||||
|
||||
import (
|
||||
@@ -17,7 +20,6 @@ var makeMf = func(theMap map[string]interface{}) MappingFunc {
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expected string
|
||||
@@ -243,17 +245,7 @@ metadata:
|
||||
data:
|
||||
slice:
|
||||
- false`,
|
||||
expectedError: `considering field 'data/slice' of object
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
data:
|
||||
slice:
|
||||
- false
|
||||
: invalid value type expect a string`,
|
||||
expectedError: `considering field 'data/slice' of object Deployment.v1.apps/dep.[noNs]: invalid value type expect a string`,
|
||||
filter: Filter{
|
||||
MappingFunc: makeMf(map[string]interface{}{
|
||||
"VAR": int64(5),
|
||||
@@ -269,16 +261,7 @@ metadata:
|
||||
name: dep
|
||||
data:
|
||||
1: str`,
|
||||
expectedError: `considering field 'data' of object
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
annotations:
|
||||
config.kubernetes.io/index: '0'
|
||||
data:
|
||||
1: str
|
||||
: invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`,
|
||||
expectedError: `considering field 'data' of object Deployment.v1.apps/dep.[noNs]: invalid map key: value='1', tag='` + yaml.NodeTagInt + `'`,
|
||||
filter: Filter{
|
||||
MappingFunc: makeMf(map[string]interface{}{
|
||||
"VAR": int64(5),
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package replacement contains a kio.Filter implementation of the kustomize
|
||||
// replacement transformer (accepts sources and looks for targets to replace
|
||||
// their values with values from the sources).
|
||||
|
||||
@@ -7,22 +7,26 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
kyaml_utils "sigs.k8s.io/kustomize/kyaml/utils"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
Replacements []types.Replacement
|
||||
Replacements []types.Replacement `json:"replacements,omitempty" yaml:"replacements,omitempty"`
|
||||
}
|
||||
|
||||
// Filter replaces values of targets with values from sources
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
for _, r := range f.Replacements {
|
||||
for i, r := range f.Replacements {
|
||||
if r.Source == nil || r.Targets == nil {
|
||||
return nil, fmt.Errorf("replacements must specify a source and at least one target")
|
||||
}
|
||||
value, err := getReplacement(nodes, &r)
|
||||
value, err := getReplacement(nodes, &f.Replacements[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -34,82 +38,6 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targets []*types.TargetSelector) ([]*yaml.RNode, error) {
|
||||
for _, t := range targets {
|
||||
if t.Select == nil {
|
||||
return nil, fmt.Errorf("target must specify resources to select")
|
||||
}
|
||||
if len(t.FieldPaths) == 0 {
|
||||
t.FieldPaths = []string{types.DefaultReplacementFieldPath}
|
||||
}
|
||||
for _, n := range nodes {
|
||||
nodeId := getKrmId(n)
|
||||
if t.Select.KrmId.Match(nodeId) && !rejectId(t.Reject, nodeId) {
|
||||
err := applyToNode(n, value, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func rejectId(rejects []*types.Selector, nodeId *types.KrmId) bool {
|
||||
for _, r := range rejects {
|
||||
if r.KrmId.Match(nodeId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func applyToNode(node *yaml.RNode, value *yaml.RNode, target *types.TargetSelector) error {
|
||||
for _, fp := range target.FieldPaths {
|
||||
fieldPath := strings.Split(fp, ".")
|
||||
var t *yaml.RNode
|
||||
var err error
|
||||
if target.Options != nil && target.Options.Create {
|
||||
t, err = node.Pipe(yaml.LookupCreate(value.YNode().Kind, fieldPath...))
|
||||
} else {
|
||||
t, err = node.Pipe(yaml.Lookup(fieldPath...))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t != nil {
|
||||
if err = setTargetValue(target.Options, t, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setTargetValue(options *types.FieldOptions, t *yaml.RNode, value *yaml.RNode) error {
|
||||
if options != nil && options.Delimiter != "" {
|
||||
|
||||
if t.YNode().Kind != yaml.ScalarNode {
|
||||
return fmt.Errorf("delimiter option can only be used with scalar nodes")
|
||||
}
|
||||
|
||||
tv := strings.Split(t.YNode().Value, options.Delimiter)
|
||||
v := yaml.GetValue(value)
|
||||
// TODO: Add a way to remove an element
|
||||
switch {
|
||||
case options.Index < 0: // prefix
|
||||
tv = append([]string{v}, tv...)
|
||||
case options.Index >= len(tv): // suffix
|
||||
tv = append(tv, v)
|
||||
default: // replace an element
|
||||
tv[options.Index] = v
|
||||
}
|
||||
value.YNode().Value = strings.Join(tv, options.Delimiter)
|
||||
}
|
||||
t.SetYNode(value.YNode())
|
||||
return nil
|
||||
}
|
||||
|
||||
func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, error) {
|
||||
source, err := selectSourceNode(nodes, r.Source)
|
||||
if err != nil {
|
||||
@@ -119,16 +47,43 @@ func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, err
|
||||
if r.Source.FieldPath == "" {
|
||||
r.Source.FieldPath = types.DefaultReplacementFieldPath
|
||||
}
|
||||
fieldPath := strings.Split(r.Source.FieldPath, ".")
|
||||
fieldPath := kyaml_utils.SmarterPathSplitter(r.Source.FieldPath, ".")
|
||||
|
||||
rn, err := source.Pipe(yaml.Lookup(fieldPath...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("error looking up replacement source: %w", err)
|
||||
}
|
||||
if !rn.IsNilOrEmpty() {
|
||||
return getRefinedValue(r.Source.Options, rn)
|
||||
if rn.IsNilOrEmpty() {
|
||||
return nil, fmt.Errorf("fieldPath `%s` is missing for replacement source %s", r.Source.FieldPath, r.Source.ResId)
|
||||
}
|
||||
return rn, nil
|
||||
|
||||
return getRefinedValue(r.Source.Options, rn)
|
||||
}
|
||||
|
||||
// selectSourceNode finds the node that matches the selector, returning
|
||||
// an error if multiple or none are found
|
||||
func selectSourceNode(nodes []*yaml.RNode, selector *types.SourceSelector) (*yaml.RNode, error) {
|
||||
var matches []*yaml.RNode
|
||||
for _, n := range nodes {
|
||||
ids, err := utils.MakeResIds(n)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting node IDs: %w", err)
|
||||
}
|
||||
for _, id := range ids {
|
||||
if id.IsSelectedBy(selector.ResId) {
|
||||
if len(matches) > 0 {
|
||||
return nil, fmt.Errorf(
|
||||
"multiple matches for selector %s", selector)
|
||||
}
|
||||
matches = append(matches, n)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return nil, fmt.Errorf("nothing selected by %s", selector)
|
||||
}
|
||||
return matches[0], nil
|
||||
}
|
||||
|
||||
func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode, error) {
|
||||
@@ -147,39 +102,143 @@ func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode,
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// selectSourceNode finds the node that matches the selector, returning
|
||||
// an error if multiple or none are found
|
||||
func selectSourceNode(nodes []*yaml.RNode, selector *types.SourceSelector) (*yaml.RNode, error) {
|
||||
var matches []*yaml.RNode
|
||||
for _, n := range nodes {
|
||||
if selector.KrmId.Match(getKrmId(n)) {
|
||||
if len(matches) > 0 {
|
||||
return nil, fmt.Errorf("more than one match for source %v", selector)
|
||||
func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targetSelectors []*types.TargetSelector) ([]*yaml.RNode, error) {
|
||||
for _, selector := range targetSelectors {
|
||||
if selector.Select == nil {
|
||||
return nil, errors.Errorf("target must specify resources to select")
|
||||
}
|
||||
if len(selector.FieldPaths) == 0 {
|
||||
selector.FieldPaths = []string{types.DefaultReplacementFieldPath}
|
||||
}
|
||||
for _, possibleTarget := range nodes {
|
||||
ids, err := utils.MakeResIds(possibleTarget)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// filter targets by label and annotation selectors
|
||||
selectByAnnoAndLabel, err := selectByAnnoAndLabel(possibleTarget, selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !selectByAnnoAndLabel {
|
||||
continue
|
||||
}
|
||||
|
||||
// filter targets by matching resource IDs
|
||||
for i, id := range ids {
|
||||
if id.IsSelectedBy(selector.Select.ResId) && !rejectId(selector.Reject, &ids[i]) {
|
||||
err := copyValueToTarget(possibleTarget, value, selector)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
matches = append(matches, n)
|
||||
}
|
||||
}
|
||||
if len(matches) == 0 {
|
||||
return nil, fmt.Errorf("found no matches for source %v", selector)
|
||||
}
|
||||
return matches[0], nil
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func getKrmId(n *yaml.RNode) *types.KrmId {
|
||||
ns, err := n.GetNamespace()
|
||||
if err != nil {
|
||||
// Resource has no metadata (no apiVersion, kind, nor metadata field).
|
||||
// In this case, it cannot be selected.
|
||||
return &types.KrmId{}
|
||||
func selectByAnnoAndLabel(n *yaml.RNode, t *types.TargetSelector) (bool, error) {
|
||||
if matchesSelect, err := matchesAnnoAndLabelSelector(n, t.Select); !matchesSelect || err != nil {
|
||||
return false, err
|
||||
}
|
||||
apiVersion := n.Field(yaml.APIVersionField)
|
||||
var group, version string
|
||||
if apiVersion != nil {
|
||||
group, version = resid.ParseGroupVersion(yaml.GetValue(apiVersion.Value))
|
||||
}
|
||||
return &types.KrmId{
|
||||
Gvk: resid.Gvk{Group: group, Version: version, Kind: n.GetKind()},
|
||||
Name: n.GetName(),
|
||||
Namespace: ns,
|
||||
for _, reject := range t.Reject {
|
||||
if reject.AnnotationSelector == "" && reject.LabelSelector == "" {
|
||||
continue
|
||||
}
|
||||
if m, err := matchesAnnoAndLabelSelector(n, reject); m || err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func matchesAnnoAndLabelSelector(n *yaml.RNode, selector *types.Selector) (bool, error) {
|
||||
r := resource.Resource{RNode: *n}
|
||||
annoMatch, err := r.MatchesAnnotationSelector(selector.AnnotationSelector)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
labelMatch, err := r.MatchesLabelSelector(selector.LabelSelector)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return annoMatch && labelMatch, nil
|
||||
}
|
||||
|
||||
func rejectId(rejects []*types.Selector, id *resid.ResId) bool {
|
||||
for _, r := range rejects {
|
||||
if !r.ResId.IsEmpty() && id.IsSelectedBy(r.ResId) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.TargetSelector) error {
|
||||
for _, fp := range selector.FieldPaths {
|
||||
createKind := yaml.Kind(0) // do not create
|
||||
if selector.Options != nil && selector.Options.Create {
|
||||
createKind = value.YNode().Kind
|
||||
}
|
||||
targetFieldList, err := target.Pipe(&yaml.PathMatcher{
|
||||
Path: kyaml_utils.SmarterPathSplitter(fp, "."),
|
||||
Create: createKind})
|
||||
if err != nil {
|
||||
return errors.WrapPrefixf(err, fieldRetrievalError(fp, createKind != 0))
|
||||
}
|
||||
targetFields, err := targetFieldList.Elements()
|
||||
if err != nil {
|
||||
return errors.WrapPrefixf(err, fieldRetrievalError(fp, createKind != 0))
|
||||
}
|
||||
if len(targetFields) == 0 {
|
||||
return errors.Errorf(fieldRetrievalError(fp, createKind != 0))
|
||||
}
|
||||
|
||||
for _, t := range targetFields {
|
||||
if err := setFieldValue(selector.Options, t, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fieldRetrievalError(fieldPath string, isCreate bool) string {
|
||||
if isCreate {
|
||||
return fmt.Sprintf("unable to find or create field %q in replacement target", fieldPath)
|
||||
}
|
||||
return fmt.Sprintf("unable to find field %q in replacement target", fieldPath)
|
||||
}
|
||||
|
||||
func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value *yaml.RNode) error {
|
||||
value = value.Copy()
|
||||
if options != nil && options.Delimiter != "" {
|
||||
if targetField.YNode().Kind != yaml.ScalarNode {
|
||||
return fmt.Errorf("delimiter option can only be used with scalar nodes")
|
||||
}
|
||||
tv := strings.Split(targetField.YNode().Value, options.Delimiter)
|
||||
v := yaml.GetValue(value)
|
||||
// TODO: Add a way to remove an element
|
||||
switch {
|
||||
case options.Index < 0: // prefix
|
||||
tv = append([]string{v}, tv...)
|
||||
case options.Index >= len(tv): // suffix
|
||||
tv = append(tv, v)
|
||||
default: // replace an element
|
||||
tv[options.Index] = v
|
||||
}
|
||||
value.YNode().Value = strings.Join(tv, options.Delimiter)
|
||||
}
|
||||
|
||||
if targetField.YNode().Kind == yaml.ScalarNode {
|
||||
// For scalar, only copy the value (leave any type intact to auto-convert int->string or string->int)
|
||||
targetField.YNode().Value = value.YNode().Value
|
||||
} else {
|
||||
targetField.SetYNode(value.YNode())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package replicacount
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package replicacount
|
||||
|
||||
import (
|
||||
@@ -14,9 +17,17 @@ import (
|
||||
type Filter struct {
|
||||
Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"`
|
||||
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
|
||||
|
||||
trackableSetter filtersutil.TrackableSetter
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
var _ kio.TrackableFilter = &Filter{}
|
||||
|
||||
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||
func (rc *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||
rc.trackableSetter.WithMutationTracker(callback)
|
||||
}
|
||||
|
||||
func (rc Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(rc.run)).Filter(nodes)
|
||||
@@ -33,5 +44,5 @@ func (rc Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
}
|
||||
|
||||
func (rc Filter) set(node *yaml.RNode) error {
|
||||
return filtersutil.SetScalar(strconv.FormatInt(rc.Replica.Count, 10))(node)
|
||||
return rc.trackableSetter.SetEntry("", strconv.FormatInt(rc.Replica.Count, 10), yaml.NodeTagInt)(node)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2022 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package replicacount
|
||||
|
||||
import (
|
||||
@@ -7,14 +10,17 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
|
||||
mutationTrackerStub := filtertest_test.MutationTrackerStub{}
|
||||
testCases := map[string]struct {
|
||||
input string
|
||||
expected string
|
||||
filter Filter
|
||||
input string
|
||||
expected string
|
||||
filter Filter
|
||||
mutationTracker func(key, value, tag string, node *yaml.RNode)
|
||||
expectedSetValueArgs []filtertest_test.SetValueArg
|
||||
}{
|
||||
"update field": {
|
||||
input: `
|
||||
@@ -161,9 +167,44 @@ spec:
|
||||
FieldSpec: types.FieldSpec{Path: "spec/template/replicas"},
|
||||
},
|
||||
},
|
||||
"mutation tracker": {
|
||||
input: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 5
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dep
|
||||
spec:
|
||||
replicas: 42
|
||||
`,
|
||||
filter: Filter{
|
||||
Replica: types.Replica{
|
||||
Name: "dep",
|
||||
Count: 42,
|
||||
},
|
||||
FieldSpec: types.FieldSpec{Path: "spec/replicas"},
|
||||
},
|
||||
mutationTracker: mutationTrackerStub.MutationTracker,
|
||||
expectedSetValueArgs: []filtertest_test.SetValueArg{
|
||||
{
|
||||
Value: "42",
|
||||
Tag: "!!int",
|
||||
NodePath: []string{"spec", "replicas"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for tn, tc := range testCases {
|
||||
mutationTrackerStub.Reset()
|
||||
tc.filter.WithMutationTracker(tc.mutationTracker)
|
||||
t.Run(tn, func(t *testing.T) {
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(tc.expected),
|
||||
@@ -171,6 +212,7 @@ spec:
|
||||
filtertest_test.RunFilter(t, tc.input, tc.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, tc.expectedSetValueArgs, mutationTrackerStub.SetValueArgs())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
6
api/filters/suffix/doc.go
Normal file
6
api/filters/suffix/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package suffix contains a kio.Filter implementation of the kustomize
|
||||
// SuffixTransformer.
|
||||
package suffix
|
||||
47
api/filters/suffix/example_test.go
Normal file
47
api/filters/suffix/example_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package suffix_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/suffix"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
)
|
||||
|
||||
func ExampleFilter() {
|
||||
err := kio.Pipeline{
|
||||
Inputs: []kio.Reader{&kio.ByteReader{Reader: bytes.NewBufferString(`
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`)}},
|
||||
Filters: []kio.Filter{suffix.Filter{
|
||||
Suffix: "-baz", FieldSpec: types.FieldSpec{Path: "metadata/name"}}},
|
||||
Outputs: []kio.Writer{kio.ByteWriter{Writer: os.Stdout}},
|
||||
}.Execute()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Foo
|
||||
// metadata:
|
||||
// name: instance-baz
|
||||
// ---
|
||||
// apiVersion: example.com/v1
|
||||
// kind: Bar
|
||||
// metadata:
|
||||
// name: instance-baz
|
||||
}
|
||||
50
api/filters/suffix/suffix.go
Normal file
50
api/filters/suffix/suffix.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package suffix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
// Filter applies resource name suffix's using the fieldSpecs
|
||||
type Filter struct {
|
||||
Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"`
|
||||
|
||||
FieldSpec types.FieldSpec `json:"fieldSpec,omitempty" yaml:"fieldSpec,omitempty"`
|
||||
|
||||
trackableSetter filtersutil.TrackableSetter
|
||||
}
|
||||
|
||||
var _ kio.Filter = Filter{}
|
||||
var _ kio.TrackableFilter = &Filter{}
|
||||
|
||||
// WithMutationTracker registers a callback which will be invoked each time a field is mutated
|
||||
func (f *Filter) WithMutationTracker(callback func(key, value, tag string, node *yaml.RNode)) {
|
||||
f.trackableSetter.WithMutationTracker(callback)
|
||||
}
|
||||
|
||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||
return kio.FilterAll(yaml.FilterFunc(f.run)).Filter(nodes)
|
||||
}
|
||||
|
||||
func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||
err := node.PipeE(fieldspec.Filter{
|
||||
FieldSpec: f.FieldSpec,
|
||||
SetValue: f.evaluateField,
|
||||
CreateKind: yaml.ScalarNode, // Name is a ScalarNode
|
||||
CreateTag: yaml.NodeTagString,
|
||||
})
|
||||
return node, err
|
||||
}
|
||||
|
||||
func (f Filter) evaluateField(node *yaml.RNode) error {
|
||||
return f.trackableSetter.SetScalar(fmt.Sprintf(
|
||||
"%s%s", node.YNode().Value, f.Suffix))(node)
|
||||
}
|
||||
154
api/filters/suffix/suffix_test.go
Normal file
154
api/filters/suffix/suffix_test.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2021 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package suffix_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/kustomize/api/filters/suffix"
|
||||
filtertest_test "sigs.k8s.io/kustomize/api/testutils/filtertest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
var mutationTrackerStub = filtertest_test.MutationTrackerStub{}
|
||||
|
||||
var tests = map[string]TestCase{
|
||||
"suffix": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance-foo
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance-foo
|
||||
`,
|
||||
filter: suffix.Filter{
|
||||
Suffix: "-foo",
|
||||
FieldSpec: types.FieldSpec{Path: "metadata/name"},
|
||||
},
|
||||
},
|
||||
|
||||
"data-fieldspecs": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d-foo
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
a:
|
||||
b:
|
||||
c: d-foo
|
||||
`,
|
||||
filter: suffix.Filter{
|
||||
Suffix: "-foo",
|
||||
FieldSpec: types.FieldSpec{Path: "a/b/c"},
|
||||
},
|
||||
},
|
||||
|
||||
"mutation tracker": {
|
||||
input: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance
|
||||
`,
|
||||
expected: `
|
||||
apiVersion: example.com/v1
|
||||
kind: Foo
|
||||
metadata:
|
||||
name: instance-foo
|
||||
---
|
||||
apiVersion: example.com/v1
|
||||
kind: Bar
|
||||
metadata:
|
||||
name: instance-foo
|
||||
`,
|
||||
filter: suffix.Filter{
|
||||
Suffix: "-foo",
|
||||
FieldSpec: types.FieldSpec{Path: "metadata/name"},
|
||||
},
|
||||
mutationTracker: mutationTrackerStub.MutationTracker,
|
||||
expectedSetValueArgs: []filtertest_test.SetValueArg{
|
||||
{
|
||||
Value: "instance-foo",
|
||||
NodePath: []string{"metadata", "name"},
|
||||
},
|
||||
{
|
||||
Value: "instance-foo",
|
||||
NodePath: []string{"metadata", "name"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type TestCase struct {
|
||||
input string
|
||||
expected string
|
||||
filter suffix.Filter
|
||||
mutationTracker func(key, value, tag string, node *yaml.RNode)
|
||||
expectedSetValueArgs []filtertest_test.SetValueArg
|
||||
}
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
for name := range tests {
|
||||
mutationTrackerStub.Reset()
|
||||
test := tests[name]
|
||||
test.filter.WithMutationTracker(test.mutationTracker)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if !assert.Equal(t,
|
||||
strings.TrimSpace(test.expected),
|
||||
strings.TrimSpace(
|
||||
filtertest_test.RunFilter(t, test.input, test.filter))) {
|
||||
t.FailNow()
|
||||
}
|
||||
assert.Equal(t, test.expectedSetValueArgs, mutationTrackerStub.SetValueArgs())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ package valueadd
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||
)
|
||||
|
||||
39
api/go.mod
39
api/go.mod
@@ -1,16 +1,37 @@
|
||||
module sigs.k8s.io/kustomize/api
|
||||
|
||||
go 1.16
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/evanphx/json-patch v4.5.0+incompatible
|
||||
github.com/go-errors/errors v1.0.1
|
||||
github.com/go-openapi/spec v0.19.5
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible
|
||||
github.com/go-errors/errors v1.4.2
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/imdario/mergo v0.3.12
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/imdario/mergo v0.3.6
|
||||
github.com/stretchr/testify v1.8.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
sigs.k8s.io/kustomize/kyaml v0.10.17
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.0
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.1 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.1.0 // indirect
|
||||
github.com/xlab/treeprint v1.1.0 // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
308
api/go.sum
308
api/go.sum
@@ -1,271 +1,155 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.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-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/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
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/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
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/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
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.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
||||
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5 h1:Xm0Ao53uqnk9QE/LlYV5DEU09UAgpliA85QoT9LzqPw=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
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-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-openapi/validate v0.19.8/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM=
|
||||
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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8=
|
||||
github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
|
||||
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/markbates/pkger v0.17.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
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 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
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/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI=
|
||||
github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
|
||||
github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
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-20181005035420-146acd28ed58/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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/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-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
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/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
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=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
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/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.10.17 h1:4zrV0ym5AYa0e512q7K3Wp1u7mzoWW0xR3UHJcGWGIg=
|
||||
sigs.k8s.io/kustomize/kyaml v0.10.17/go.mod h1:mlQFagmkm1P+W4lZJbJ/yaxMd8PqMRSC4cPcfUVt5Hg=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596 h1:8cNCQs+WqqnSpZ7y0LMQPKD+RZUHU17VqLPMW3qxnxc=
|
||||
k8s.io/kube-openapi v0.0.0-20230109183929-3758b55a6596/go.mod h1:/BYxry62FuDzmI+i9B+X2pqfySRmSOW2ARmj5Zbqhj0=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.0 h1:uzH0MzMtYypHW09LbMDk8k/lT/LSsUuCoZIuEGhIBNE=
|
||||
sigs.k8s.io/kustomize/kyaml v0.14.0/go.mod h1:AN1/IpawKilWD7V+YvQwRGUvuUOOWpjsHu6uHwonSF4=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
@@ -341,6 +341,7 @@ data:
|
||||
// The return value indicates whether we should skip the rest of the test case
|
||||
// due to the error result.
|
||||
func SkipRest(t *testing.T, desc string, err error, contains string) bool {
|
||||
t.Helper()
|
||||
if err != nil {
|
||||
if len(contains) == 0 {
|
||||
t.Errorf("case %q, expect nil error but got %q", desc, err.Error())
|
||||
|
||||
@@ -28,12 +28,20 @@ type KvLoader interface {
|
||||
|
||||
// Loader interface exposes methods to read bytes.
|
||||
type Loader interface {
|
||||
|
||||
// Repo returns the repo location if this Loader was created from a url
|
||||
// or the empty string otherwise.
|
||||
Repo() string
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -14,37 +14,53 @@ func IsImageMatched(s, t string) bool {
|
||||
// Tag values are limited to [a-zA-Z0-9_.{}-].
|
||||
// Some tools like Bazel rules_k8s allow tag patterns with {} characters.
|
||||
// More info: https://github.com/bazelbuild/rules_k8s/pull/423
|
||||
pattern, _ := regexp.Compile("^" + t + "(@sha256)?(:[a-zA-Z0-9_.{}-]*)?$")
|
||||
pattern, _ := regexp.Compile("^" + t + "(:[a-zA-Z0-9_.{}-]*)?(@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) {
|
||||
// image reference pattern: [[host[:port]/]component/]component[:tag][@digest]
|
||||
func Split(imageName string) (name string, tag string, digest 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, ":")
|
||||
searchName := imageName
|
||||
slashIndex := strings.Index(imageName, "/")
|
||||
if slashIndex > 0 {
|
||||
searchName = imageName[slashIndex:]
|
||||
} 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, ""
|
||||
slashIndex = 0
|
||||
}
|
||||
|
||||
i := ic
|
||||
if ia > 0 {
|
||||
i = ia
|
||||
id := strings.Index(searchName, "@")
|
||||
ic := strings.Index(searchName, ":")
|
||||
|
||||
// no tag or digest
|
||||
if ic < 0 && id < 0 {
|
||||
return imageName, "", ""
|
||||
}
|
||||
|
||||
name = imageName[:i]
|
||||
tag = imageName[i:]
|
||||
return
|
||||
// digest only
|
||||
if id >= 0 && (id < ic || ic < 0) {
|
||||
id += slashIndex
|
||||
name = imageName[:id]
|
||||
digest = strings.TrimPrefix(imageName[id:], "@")
|
||||
return name, "", digest
|
||||
}
|
||||
|
||||
// tag and digest
|
||||
if id >= 0 && ic >= 0 {
|
||||
id += slashIndex
|
||||
ic += slashIndex
|
||||
name = imageName[:ic]
|
||||
tag = strings.TrimPrefix(imageName[ic:id], ":")
|
||||
digest = strings.TrimPrefix(imageName[id:], "@")
|
||||
return name, tag, digest
|
||||
}
|
||||
|
||||
// tag only
|
||||
ic += slashIndex
|
||||
name = imageName[:ic]
|
||||
tag = strings.TrimPrefix(imageName[ic:], ":")
|
||||
return name, tag, ""
|
||||
}
|
||||
|
||||
@@ -23,11 +23,23 @@ func TestIsImageMatched(t *testing.T) {
|
||||
isMatched: true,
|
||||
},
|
||||
{
|
||||
testName: "name is match",
|
||||
testName: "name is match with tag",
|
||||
value: "nginx:12345",
|
||||
name: "nginx",
|
||||
isMatched: true,
|
||||
},
|
||||
{
|
||||
testName: "name is match with digest",
|
||||
value: "nginx@sha256:xyz",
|
||||
name: "nginx",
|
||||
isMatched: true,
|
||||
},
|
||||
{
|
||||
testName: "name is match with tag and digest",
|
||||
value: "nginx:12345@sha256:xyz",
|
||||
name: "nginx",
|
||||
isMatched: true,
|
||||
},
|
||||
{
|
||||
testName: "name is not a match",
|
||||
value: "apache:12345",
|
||||
@@ -49,32 +61,65 @@ func TestSplit(t *testing.T) {
|
||||
value string
|
||||
name string
|
||||
tag string
|
||||
digest string
|
||||
}{
|
||||
{
|
||||
testName: "no tag",
|
||||
value: "nginx",
|
||||
name: "nginx",
|
||||
tag: "",
|
||||
digest: "",
|
||||
},
|
||||
{
|
||||
testName: "with tag",
|
||||
value: "nginx:1.2.3",
|
||||
name: "nginx",
|
||||
tag: ":1.2.3",
|
||||
tag: "1.2.3",
|
||||
digest: "",
|
||||
},
|
||||
{
|
||||
testName: "with digest",
|
||||
value: "nginx@12345",
|
||||
value: "nginx@sha256:12345",
|
||||
name: "nginx",
|
||||
tag: "@12345",
|
||||
tag: "",
|
||||
digest: "sha256:12345",
|
||||
},
|
||||
{
|
||||
testName: "with tag and digest",
|
||||
value: "nginx:1.2.3@sha256:12345",
|
||||
name: "nginx",
|
||||
tag: "1.2.3",
|
||||
digest: "sha256:12345",
|
||||
},
|
||||
{
|
||||
testName: "with domain",
|
||||
value: "docker.io/nginx:1.2.3",
|
||||
name: "docker.io/nginx",
|
||||
tag: "1.2.3",
|
||||
digest: "",
|
||||
},
|
||||
{
|
||||
testName: "with domain and port",
|
||||
value: "foo.com:443/nginx:1.2.3",
|
||||
name: "foo.com:443/nginx",
|
||||
tag: "1.2.3",
|
||||
digest: "",
|
||||
},
|
||||
{
|
||||
testName: "with domain, port, tag and digest",
|
||||
value: "foo.com:443/nginx:1.2.3@sha256:12345",
|
||||
name: "foo.com:443/nginx",
|
||||
tag: "1.2.3",
|
||||
digest: "sha256:12345",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.testName, func(t *testing.T) {
|
||||
name, tag := Split(tc.value)
|
||||
name, tag, digest := Split(tc.value)
|
||||
assert.Equal(t, tc.name, name)
|
||||
assert.Equal(t, tc.tag, tag)
|
||||
assert.Equal(t, tc.digest, digest)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
"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/kustomize/kyaml/errors"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
@@ -39,7 +39,7 @@ func LoadConfigFromCRDs(
|
||||
}
|
||||
m, err := makeNameToApiMap(content)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to parse open API definition from '%s'", path)
|
||||
return nil, errors.WrapPrefixf(err, "unable to parse open API definition from '%s'", path)
|
||||
}
|
||||
otherTc, err := makeConfigFromApiMap(m)
|
||||
if err != nil {
|
||||
@@ -178,9 +178,12 @@ func loadCrdIntoConfig(
|
||||
}
|
||||
}
|
||||
if property.Ref.GetURL() != nil {
|
||||
loadCrdIntoConfig(
|
||||
err = loadCrdIntoConfig(
|
||||
theConfig, theGvk, theMap,
|
||||
property.Ref.String(), append(path, propName))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -7,12 +7,13 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/filesys"
|
||||
"github.com/stretchr/testify/require"
|
||||
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/loader"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
)
|
||||
|
||||
// This defines two CRD's: Bee and MyKind.
|
||||
@@ -162,16 +163,13 @@ func TestLoadCRDs(t *testing.T) {
|
||||
}
|
||||
|
||||
fSys := filesys.MakeFsInMemory()
|
||||
fSys.WriteFile("/testpath/crd.json", []byte(crdContent))
|
||||
err := fSys.WriteFile("/testpath/crd.json", []byte(crdContent))
|
||||
require.NoError(t, err)
|
||||
ldr, err := loader.NewLoader(loader.RestrictionRootOnly, "/testpath", fSys)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error:%v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
actualTc, err := LoadConfigFromCRDs(ldr, []string{"crd.json"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error:%v", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
if !reflect.DeepEqual(actualTc, expectedTc) {
|
||||
t.Fatalf("expected\n %v\n but got\n %v\n", expectedTc, actualTc)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
)
|
||||
|
||||
type nameReferenceTransformer struct {
|
||||
@@ -51,7 +52,10 @@ func (t *nameReferenceTransformer) Transform(m resmap.ResMap) error {
|
||||
fMap := t.determineFilters(m.Resources())
|
||||
debug(fMap)
|
||||
for r, fList := range fMap {
|
||||
c := m.SubsetThatCouldBeReferencedByResource(r)
|
||||
c, err := m.SubsetThatCouldBeReferencedByResource(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, f := range fList {
|
||||
f.Referrer = r
|
||||
f.ReferralCandidates = c
|
||||
@@ -109,11 +113,17 @@ func debug(fMap filterMap) {
|
||||
// 'spec/scaleTargetRef/name' field. Return a filter that can do that.
|
||||
func (t *nameReferenceTransformer) determineFilters(
|
||||
resources []*resource.Resource) (fMap filterMap) {
|
||||
// We cache the resource OrgId values because they don't change and otherwise are very visible in a memory pprof
|
||||
resourceOrgIds := make([]resid.ResId, len(resources))
|
||||
for i, resource := range resources {
|
||||
resourceOrgIds[i] = resource.OrgId()
|
||||
}
|
||||
|
||||
fMap = make(filterMap)
|
||||
for _, backReference := range t.backRefs {
|
||||
for _, referrerSpec := range backReference.Referrers {
|
||||
for _, res := range resources {
|
||||
if res.OrgId().IsSelected(&referrerSpec.Gvk) {
|
||||
for i, res := range resources {
|
||||
if resourceOrgIds[i].IsSelected(&referrerSpec.Gvk) {
|
||||
// If this is true, the res might be a referrer, and if
|
||||
// so, the name reference it holds might need an update.
|
||||
if resHasField(res, referrerSpec.Path) {
|
||||
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
)
|
||||
|
||||
const notEqualErrFmt = "expected (self) doesn't match actual (other): %v"
|
||||
@@ -520,19 +521,56 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}).ResMap(),
|
||||
expectedErr: `updating name reference in 'rules/resourceNames' field of ` +
|
||||
`'rbac.authorization.k8s.io_v1_ClusterRole|~X|cr'` +
|
||||
`: considering field 'rules/resourceNames' of object
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: cr
|
||||
rules:
|
||||
- resourceNames:
|
||||
foo: bar
|
||||
resources:
|
||||
- secrets
|
||||
: visit traversal on path: [resourceNames]: path config error; no 'name' field in node`},
|
||||
expectedErr: `updating name reference in 'rules/resourceNames' field of 'ClusterRole.v1.rbac.authorization.k8s.io/cr.[noNs]': ` +
|
||||
`considering field 'rules/resourceNames' of object ClusterRole.v1.rbac.authorization.k8s.io/cr.[noNs]: visit traversal on ` +
|
||||
`path: [resourceNames]: path config error; no 'name' field in node`,
|
||||
},
|
||||
{
|
||||
// When targeting a map reference, we need to update both name and namespace, so multiple
|
||||
// possible referral targets with different namespaces should not be considered identical.
|
||||
// This test covers a bug where the difference in namespace was ignored and one candidate was chosen at random.
|
||||
resMap: resmaptest_test.NewRmBuilderDefault(t).AddWithNsAndName(
|
||||
"", orgname,
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ServiceAccount",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": orgname,
|
||||
"namespace": ns1,
|
||||
},
|
||||
},
|
||||
).AddWithNsAndName(
|
||||
"", orgname,
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ServiceAccount",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": orgname,
|
||||
"namespace": ns2,
|
||||
},
|
||||
},
|
||||
).Add(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||
"kind": "ClusterRoleBinding",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": orgname,
|
||||
},
|
||||
"roleRef": map[string]interface{}{
|
||||
"apiGroup": "rbac.authorization.k8s.io",
|
||||
"kind": "ClusterRole",
|
||||
"name": orgname,
|
||||
},
|
||||
"subjects": []interface{}{
|
||||
map[string]interface{}{
|
||||
"kind": "ServiceAccount",
|
||||
"name": orgname,
|
||||
},
|
||||
},
|
||||
},
|
||||
).ResMap(),
|
||||
expectedErr: "found multiple possible referrals: ServiceAccount.v1.[noGrp]/uniquename.ns1, ServiceAccount.v1.[noGrp]/uniquename.ns2",
|
||||
},
|
||||
}
|
||||
|
||||
nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
|
||||
@@ -885,9 +923,9 @@ func TestNameReferenceClusterWide(t *testing.T) {
|
||||
}).ResMap()
|
||||
|
||||
clusterRoleId := resid.NewResId(
|
||||
resid.Gvk{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole"}, modifiedname)
|
||||
resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRole"), modifiedname)
|
||||
clusterRoleBindingId := resid.NewResId(
|
||||
resid.Gvk{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}, modifiedname)
|
||||
resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"), modifiedname)
|
||||
clusterRole, _ := expected.GetByCurrentId(clusterRoleId)
|
||||
clusterRole.AppendRefBy(clusterRoleBindingId)
|
||||
|
||||
@@ -897,7 +935,9 @@ func TestNameReferenceClusterWide(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
expected.RemoveBuildAnnotations()
|
||||
m.RemoveBuildAnnotations()
|
||||
|
||||
if err = expected.ErrorIfNotEqualLists(m); err != nil {
|
||||
t.Fatalf(notEqualErrFmt, err)
|
||||
}
|
||||
@@ -1012,9 +1052,11 @@ func TestNameReferenceNamespaceTransformation(t *testing.T) {
|
||||
}).ResMap()
|
||||
|
||||
clusterRoleId := resid.NewResId(
|
||||
resid.Gvk{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRole"}, modifiedname)
|
||||
resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRole"),
|
||||
modifiedname)
|
||||
clusterRoleBindingId := resid.NewResId(
|
||||
resid.Gvk{Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}, modifiedname)
|
||||
resid.NewGvk("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"),
|
||||
modifiedname)
|
||||
clusterRole, _ := expected.GetByCurrentId(clusterRoleId)
|
||||
clusterRole.AppendRefBy(clusterRoleBindingId)
|
||||
|
||||
@@ -1066,3 +1108,76 @@ func TestNameReferenceCandidateSelection(t *testing.T) {
|
||||
t.Fatalf(notEqualErrFmt, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameReferenceCandidateDisambiguationByNamespace(t *testing.T) {
|
||||
// The ClusterRole refers to both configmaps, since it is not namespace-specific.
|
||||
// Since both names are updated consistently, the transformer should be able to
|
||||
// silently update the ClusterRole as well.
|
||||
// This test guards against a regression where allNamesAndNamespacesAreTheSame would be
|
||||
// used to detect referral candidate identity instead of allNamesAreTheSame.
|
||||
m := resmaptest_test.NewRmBuilderDefault(t).AddWithName(orgname,
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": suffixedname,
|
||||
"namespace": ns1,
|
||||
},
|
||||
},
|
||||
).AddWithName(orgname,
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": suffixedname,
|
||||
"namespace": ns2,
|
||||
},
|
||||
},
|
||||
).Add(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||
"kind": "ClusterRole",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": orgname,
|
||||
},
|
||||
"rules": []interface{}{
|
||||
map[string]interface{}{
|
||||
"resources": []interface{}{
|
||||
"configmaps",
|
||||
},
|
||||
"resourceNames": []interface{}{
|
||||
orgname,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
).ResMap()
|
||||
|
||||
expected := resmaptest_test.NewSeededRmBuilderDefault(t, m.ShallowCopy()).
|
||||
ReplaceResource(map[string]interface{}{
|
||||
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||
"kind": "ClusterRole",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": orgname,
|
||||
},
|
||||
"rules": []interface{}{
|
||||
map[string]interface{}{
|
||||
"resources": []interface{}{
|
||||
"configmaps",
|
||||
},
|
||||
"resourceNames": []interface{}{
|
||||
suffixedname,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
).ResMap()
|
||||
|
||||
nrt := newNameReferenceTransformer(builtinconfig.MakeDefaultConfig().NameReference)
|
||||
require.NoError(t, nrt.Transform(m))
|
||||
|
||||
m.RemoveBuildAnnotations()
|
||||
if err := expected.ErrorIfNotEqualLists(m); err != nil {
|
||||
t.Fatalf(notEqualErrFmt, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
)
|
||||
|
||||
func TestRefVarTransformer(t *testing.T) {
|
||||
@@ -120,15 +120,7 @@ func TestRefVarTransformer(t *testing.T) {
|
||||
"slice": []interface{}{5}, // noticeably *not* a []string
|
||||
}}).ResMap(),
|
||||
},
|
||||
errMessage: `considering field 'data/slice' of object
|
||||
apiVersion: v1
|
||||
data:
|
||||
slice:
|
||||
- 5
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm1
|
||||
: invalid value type expect a string`,
|
||||
errMessage: `considering field 'data/slice' of object ConfigMap.v1.[noGrp]/cm1.[noNs]: invalid value type expect a string`,
|
||||
},
|
||||
"var replacement in nil": {
|
||||
given: given{
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"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"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
)
|
||||
|
||||
// ResAccumulator accumulates resources and the rules
|
||||
@@ -72,7 +72,7 @@ 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() {
|
||||
if targetId.Namespace != "" || targetId.IsClusterScoped() {
|
||||
// Preserve backward compatibility. An empty namespace means
|
||||
// wildcard search on the namespace hence we still use GvknEquals
|
||||
idMatcher = targetId.Equals
|
||||
@@ -167,3 +167,23 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
|
||||
return ra.Transform(
|
||||
newNameReferenceTransformer(ra.tConfig.NameReference))
|
||||
}
|
||||
|
||||
// Intersection drops the resources which "other" does not have.
|
||||
func (ra *ResAccumulator) Intersection(other resmap.ResMap) error {
|
||||
for _, curId := range ra.resMap.AllIds() {
|
||||
toDelete := true
|
||||
for _, otherId := range other.AllIds() {
|
||||
if otherId == curId {
|
||||
toDelete = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if toDelete {
|
||||
err := ra.resMap.Remove(curId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -10,17 +10,19 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||
"sigs.k8s.io/kustomize/api/provider"
|
||||
"sigs.k8s.io/kustomize/api/resid"
|
||||
"sigs.k8s.io/kustomize/api/resmap"
|
||||
"sigs.k8s.io/kustomize/api/resource"
|
||||
resmaptest_test "sigs.k8s.io/kustomize/api/testutils/resmaptest"
|
||||
"sigs.k8s.io/kustomize/api/types"
|
||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||
)
|
||||
|
||||
func makeResAccumulator(t *testing.T) *ResAccumulator {
|
||||
t.Helper()
|
||||
ra := MakeEmptyAccumulator()
|
||||
err := ra.MergeConfig(builtinconfig.MakeDefaultConfig())
|
||||
if err != nil {
|
||||
@@ -132,6 +134,7 @@ func TestResolveVarsOneUnused(t *testing.T) {
|
||||
}
|
||||
|
||||
func expectLog(t *testing.T, log bytes.Buffer, expect string) {
|
||||
t.Helper()
|
||||
if !strings.Contains(log.String(), expect) {
|
||||
t.Fatalf("expected log containing '%s', got '%s'", expect, log.String())
|
||||
}
|
||||
@@ -224,20 +227,26 @@ func TestResolveVarConflicts(t *testing.T) {
|
||||
// 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))
|
||||
err := rm0.Append(rf.FromMap(fooAws))
|
||||
require.NoError(t, err)
|
||||
ac0 := MakeEmptyAccumulator()
|
||||
ac0.AppendAll(rm0)
|
||||
ac0.MergeVars([]types.Var{varFoo})
|
||||
err = ac0.AppendAll(rm0)
|
||||
require.NoError(t, err)
|
||||
err = ac0.MergeVars([]types.Var{varFoo})
|
||||
require.NoError(t, err)
|
||||
|
||||
rm1 := resmap.New()
|
||||
rm1.Append(rf.FromMap(barAws))
|
||||
err = rm1.Append(rf.FromMap(barAws))
|
||||
require.NoError(t, err)
|
||||
ac1 := MakeEmptyAccumulator()
|
||||
ac1.AppendAll(rm1)
|
||||
ac1.MergeVars([]types.Var{varBar})
|
||||
err = ac1.AppendAll(rm1)
|
||||
require.NoError(t, err)
|
||||
err = ac1.MergeVars([]types.Var{varBar})
|
||||
require.NoError(t, err)
|
||||
|
||||
// validate that two vars of the same name which reference the same concrete
|
||||
// value do not produce a conflict.
|
||||
err := ac0.MergeAccumulator(ac1)
|
||||
err = ac0.MergeAccumulator(ac1)
|
||||
if err == nil {
|
||||
t.Fatalf("see bug gh-1600")
|
||||
}
|
||||
@@ -246,10 +255,13 @@ func TestResolveVarConflicts(t *testing.T) {
|
||||
// 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))
|
||||
err = rm2.Append(rf.FromMap(barGcp))
|
||||
require.NoError(t, err)
|
||||
ac2 := MakeEmptyAccumulator()
|
||||
ac2.AppendAll(rm2)
|
||||
ac2.MergeVars([]types.Var{varBar})
|
||||
err = ac2.AppendAll(rm2)
|
||||
require.NoError(t, err)
|
||||
err = ac2.MergeVars([]types.Var{varBar})
|
||||
require.NoError(t, err)
|
||||
err = ac1.MergeAccumulator(ac2)
|
||||
if err == nil {
|
||||
t.Fatalf("dupe vars w/ different concrete values should conflict")
|
||||
@@ -352,10 +364,10 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "sub-backendOne",
|
||||
"annotations": map[string]interface{}{
|
||||
"config.kubernetes.io/previousKinds": "Service",
|
||||
"config.kubernetes.io/previousNames": "backendOne",
|
||||
"config.kubernetes.io/previousNamespaces": "default",
|
||||
"config.kubernetes.io/prefixes": "sub-",
|
||||
"internal.config.kubernetes.io/previousKinds": "Service",
|
||||
"internal.config.kubernetes.io/previousNames": "backendOne",
|
||||
"internal.config.kubernetes.io/previousNamespaces": "default",
|
||||
"internal.config.kubernetes.io/prefixes": "sub-",
|
||||
},
|
||||
}}).ResMap()
|
||||
|
||||
|
||||
@@ -27,16 +27,10 @@ func (p *AnnotationsTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
if len(p.Annotations) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, r := range m.Resources() {
|
||||
err := r.ApplyFilter(annotations.Filter{
|
||||
Annotations: p.Annotations,
|
||||
FsSlice: p.FieldSpecs,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return m.ApplyFilter(annotations.Filter{
|
||||
Annotations: p.Annotations,
|
||||
FsSlice: p.FieldSpecs,
|
||||
})
|
||||
}
|
||||
|
||||
func NewAnnotationsTransformerPlugin() resmap.TransformerPlugin {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user