mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-14 10:30:59 +00:00
Compare commits
1066 Commits
kyaml/v0.1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
313aacedd3 | ||
|
|
9a61304919 | ||
|
|
c43ab3cee1 | ||
|
|
7be21de377 | ||
|
|
d6bd9d9b08 | ||
|
|
0bb03d98ba | ||
|
|
5e7858b8ea | ||
|
|
1c87a5063d | ||
|
|
2f9dbe434d | ||
|
|
71a99512bd | ||
|
|
72b0fd14d1 | ||
|
|
1af74f2d9e | ||
|
|
80f63ae0c3 | ||
|
|
b317895057 | ||
|
|
66af726162 | ||
|
|
bcb4803f09 | ||
|
|
4ef1cf8234 | ||
|
|
14ed8d90e0 | ||
|
|
29bf16c673 | ||
|
|
fb91123a1c | ||
|
|
4d331ef81a | ||
|
|
194aac685f | ||
|
|
825d1cfb8a | ||
|
|
811e616841 | ||
|
|
c8dfcc123f | ||
|
|
0fb3416c16 | ||
|
|
1ad9f9d96c | ||
|
|
5d2d80d41b | ||
|
|
0343400a36 | ||
|
|
c07b901856 | ||
|
|
68e507eb6f | ||
|
|
09b04840a1 | ||
|
|
0e14dbd8cd | ||
|
|
c52ba638ea | ||
|
|
78ce39820d | ||
|
|
b7ee1340a9 | ||
|
|
793f58251c | ||
|
|
5231e099e5 | ||
|
|
e78e03405b | ||
|
|
8eb136f67e | ||
|
|
dc6b2b978c | ||
|
|
be8d7eb980 | ||
|
|
9790a1c3ef | ||
|
|
4190d3dedc | ||
|
|
401be204e1 | ||
|
|
8073ce87f0 | ||
|
|
be024c92ba | ||
|
|
bb26a70573 | ||
|
|
798d339d10 | ||
|
|
6c8c9cc0e8 | ||
|
|
02d23d21f0 | ||
|
|
c6ccb4f208 | ||
|
|
54848c1049 | ||
|
|
297f2b59bb | ||
|
|
7e45799272 | ||
|
|
4e75138d24 | ||
|
|
a397f5b491 | ||
|
|
008b7a0c47 | ||
|
|
6d7267f345 | ||
|
|
17a06a72be | ||
|
|
e6621df4d5 | ||
|
|
77cf6d6b88 | ||
|
|
95a6f1fec3 | ||
|
|
c79a356bb2 | ||
|
|
311dbbf975 | ||
|
|
77b3446b36 | ||
|
|
764ad39d1d | ||
|
|
0054b5e296 | ||
|
|
16391f3dbe | ||
|
|
6661fefabc | ||
|
|
3c59244887 | ||
|
|
ade7bd609f | ||
|
|
0fc75546f1 | ||
|
|
87617912bf | ||
|
|
153a37286d | ||
|
|
de011378a4 | ||
|
|
4d37afef6f | ||
|
|
f71e4d7309 | ||
|
|
d427f61f84 | ||
|
|
9df8bbdc44 | ||
|
|
24ea1b951a | ||
|
|
68fa5177e2 | ||
|
|
2a1862d8b7 | ||
|
|
278dd6e55d | ||
|
|
b62d746b80 | ||
|
|
ab48be3747 | ||
|
|
e5bb51847d | ||
|
|
1a988bbed5 | ||
|
|
4bdc3f3f7e | ||
|
|
66c1478c1b | ||
|
|
bbe53c2c45 | ||
|
|
cd30471046 | ||
|
|
2dc0d0da8b | ||
|
|
5fd7b14fc8 | ||
|
|
b472396d10 | ||
|
|
39086340ad | ||
|
|
4468c8c9c7 | ||
|
|
05fa95ea95 | ||
|
|
1c0f1bf5ac | ||
|
|
2a79ea148d | ||
|
|
11f9435b50 | ||
|
|
b97b705232 | ||
|
|
efbd2a6ef1 | ||
|
|
e68c754cce | ||
|
|
c8d3d5709e | ||
|
|
ffcda0158e | ||
|
|
785fce97df | ||
|
|
caa42b7125 | ||
|
|
c9a0d10d5a | ||
|
|
87d0629bd1 | ||
|
|
b581903858 | ||
|
|
d54e9b2b40 | ||
|
|
75839a81b3 | ||
|
|
7c04cbb237 | ||
|
|
f74736130c | ||
|
|
9043c223d4 | ||
|
|
2859474e3c | ||
|
|
d0d64c5bc4 | ||
|
|
168971a501 | ||
|
|
0c3ce2c8fb | ||
|
|
8b42cd9918 | ||
|
|
5b313f57c4 | ||
|
|
792b241a4a | ||
|
|
8192ab34ee | ||
|
|
87f462af25 | ||
|
|
3866a30826 | ||
|
|
5cb1b4e3f9 | ||
|
|
b6128950c9 | ||
|
|
042a2cf177 | ||
|
|
e115ba6240 | ||
|
|
9376a5c4b7 | ||
|
|
f9ab532a4a | ||
|
|
76eb28bad9 | ||
|
|
7f30f0e23a | ||
|
|
35c9d0fece | ||
|
|
b1bfac465f | ||
|
|
40d1f35940 | ||
|
|
1a515925ab | ||
|
|
f54b2b4c59 | ||
|
|
03ae5c93ca | ||
|
|
0fe722e99a | ||
|
|
a7703f685c | ||
|
|
ba617e5126 | ||
|
|
755880462f | ||
|
|
832f873855 | ||
|
|
731ffd3d53 | ||
|
|
32e82514a8 | ||
|
|
13ed4a59f5 | ||
|
|
b7340bd369 | ||
|
|
367ebb6990 | ||
|
|
616c084805 | ||
|
|
dba6dee214 | ||
|
|
009369bfc9 | ||
|
|
65567a3733 | ||
|
|
d53c17b874 | ||
|
|
aa2c9ca440 | ||
|
|
b201d97541 | ||
|
|
fd97ddaca1 | ||
|
|
7372f97c19 | ||
|
|
e3f0349319 | ||
|
|
dd08aec23e | ||
|
|
2e80cebf21 | ||
|
|
e683487ea8 | ||
|
|
53fa7285e9 | ||
|
|
61b8724e87 | ||
|
|
31b37540e3 | ||
|
|
2643c51364 | ||
|
|
6b830658d1 | ||
|
|
5036c077cc | ||
|
|
418f233314 | ||
|
|
c5f69b002f | ||
|
|
447a60903c | ||
|
|
06535d623a | ||
|
|
cc7a71c288 | ||
|
|
3be1af6798 | ||
|
|
33e9361a61 | ||
|
|
160de8ce76 | ||
|
|
d339dca90c | ||
|
|
e4fff94c68 | ||
|
|
9c68bd2ee2 | ||
|
|
38eb24492a | ||
|
|
7664c4ba38 | ||
|
|
48686ac4a3 | ||
|
|
f03e31389d | ||
|
|
ba56dd28ca | ||
|
|
821aebc3b3 | ||
|
|
95db4aa0ed | ||
|
|
07f62bd986 | ||
|
|
b69e76509f | ||
|
|
8a51255ea1 | ||
|
|
ce667b70a0 | ||
|
|
1b33db58f2 | ||
|
|
880a7a0187 | ||
|
|
2867f353c2 | ||
|
|
214aa2aae2 | ||
|
|
6f62ee7198 | ||
|
|
fcc95370ff | ||
|
|
84e6594e9b | ||
|
|
86af799ce0 | ||
|
|
b79c5f652d | ||
|
|
70e4d2e24c | ||
|
|
a8c5b10d39 | ||
|
|
ae7ff36f1b | ||
|
|
bb7a280709 | ||
|
|
d135197eba | ||
|
|
4c5c585592 | ||
|
|
4f6e3cbe3f | ||
|
|
afe7793676 | ||
|
|
ae98655f0f | ||
|
|
faccc12624 | ||
|
|
fdf9f59cb5 | ||
|
|
e2967cf3ce | ||
|
|
d22fa37ae0 | ||
|
|
01cce4f6cc | ||
|
|
54cbcdc698 | ||
|
|
b36b222b26 | ||
|
|
2cd9a2e73a | ||
|
|
d32eacf034 | ||
|
|
88f19bffa9 | ||
|
|
a3c0b4add7 | ||
|
|
b67ce5bb73 | ||
|
|
5ba8523df7 | ||
|
|
4034e36ee1 | ||
|
|
c3872ce3d9 | ||
|
|
d35d21c2d0 | ||
|
|
3872752338 | ||
|
|
a5f43ec75a | ||
|
|
e6266d4559 | ||
|
|
99efd6995a | ||
|
|
5ef10d35ee | ||
|
|
b7cdd9168a | ||
|
|
27d508fa03 | ||
|
|
05339dafe5 | ||
|
|
bbb0b08576 | ||
|
|
77daec89b8 | ||
|
|
a1fd6efe5d | ||
|
|
6c27970019 | ||
|
|
cc9dd34216 | ||
|
|
2aaa42f950 | ||
|
|
f660160a0f | ||
|
|
d4248b1213 | ||
|
|
bd8b8a49c9 | ||
|
|
ddeb572a7f | ||
|
|
36d78f67fd | ||
|
|
4e52632bd3 | ||
|
|
8eacab0fc6 | ||
|
|
1a41303fbb | ||
|
|
7cbaf78b1a | ||
|
|
735ad0beef | ||
|
|
c1de0301f5 | ||
|
|
5cfd3ab3e7 | ||
|
|
dfb30644f4 | ||
|
|
e3a7615ccb | ||
|
|
b02d02a6cd | ||
|
|
804f69bacf | ||
|
|
9ab7762a40 | ||
|
|
44a99b7284 | ||
|
|
692b40e515 | ||
|
|
bef46a1a04 | ||
|
|
48d79c745a | ||
|
|
856662835f | ||
|
|
cba3688960 | ||
|
|
16a7ce2b8b | ||
|
|
92e862c233 | ||
|
|
8db4c4b062 | ||
|
|
cb432b0350 | ||
|
|
88c89f422a | ||
|
|
ce80dc9e2b | ||
|
|
226d56b5cf | ||
|
|
f309dfc54a | ||
|
|
a34ac31a80 | ||
|
|
9cc25a511c | ||
|
|
f6ad718ee6 | ||
|
|
da14e76359 | ||
|
|
7424956ccf | ||
|
|
77354d73b9 | ||
|
|
3065eb36dd | ||
|
|
b67959894e | ||
|
|
6a829feef8 | ||
|
|
e244b83844 | ||
|
|
f9838461af | ||
|
|
49a645f05d | ||
|
|
e7a15496dd | ||
|
|
0d7d830236 | ||
|
|
e676d056b2 | ||
|
|
b1a9bffd8b | ||
|
|
a83f102cc9 | ||
|
|
0e649599d0 | ||
|
|
a68f40738a | ||
|
|
72d95b5f41 | ||
|
|
671de1662d | ||
|
|
25c7e17fb8 | ||
|
|
2e6171a9ea | ||
|
|
7f99cebdc6 | ||
|
|
5d127e4138 | ||
|
|
bcb1a367aa | ||
|
|
ed09399cd1 | ||
|
|
82ee768212 | ||
|
|
116b307b88 | ||
|
|
fb9f45ebe0 | ||
|
|
536c1c0a8b | ||
|
|
e20e438d05 | ||
|
|
42873c8d2a | ||
|
|
277da9ed21 | ||
|
|
2b00d887fd | ||
|
|
08d0593c3e | ||
|
|
31706fd7fd | ||
|
|
e862612703 | ||
|
|
1b449768b5 | ||
|
|
a9f7a0427a | ||
|
|
cd954ce6fe | ||
|
|
10c292f501 | ||
|
|
a9bfabc771 | ||
|
|
398aa3666f | ||
|
|
8f2d2436ec | ||
|
|
fd06780f3e | ||
|
|
562cbc132d | ||
|
|
d5f3c4fad9 | ||
|
|
f1599f6498 | ||
|
|
c0b3801c7a | ||
|
|
f0681429ea | ||
|
|
a6bbbe843c | ||
|
|
2c68a4d2d4 | ||
|
|
b3d1df2644 | ||
|
|
8d21d43cf7 | ||
|
|
eabf2d41d3 | ||
|
|
ed2ca23400 | ||
|
|
63329d175a | ||
|
|
6145a4be44 | ||
|
|
8fef99fa35 | ||
|
|
f63e919e3e | ||
|
|
3d840a6584 | ||
|
|
a6149b1c88 | ||
|
|
91b92b52c1 | ||
|
|
d7e60b8451 | ||
|
|
846d3c09eb | ||
|
|
fbc102dbd3 | ||
|
|
a7de0cc8cd | ||
|
|
931f924189 | ||
|
|
50dc813731 | ||
|
|
9db92fd28d | ||
|
|
d514df3db0 | ||
|
|
22ce9c02bf | ||
|
|
43868688d5 | ||
|
|
434a55a244 | ||
|
|
a6ea3e2bb6 | ||
|
|
72f0a3cfb1 | ||
|
|
1eccd8f4b7 | ||
|
|
4dbc0d22e1 | ||
|
|
e9fc57abd6 | ||
|
|
d35edbf80d | ||
|
|
da3985c284 | ||
|
|
14a9a9849f | ||
|
|
8aafbacd17 | ||
|
|
4da880d6cb | ||
|
|
62eca858f3 | ||
|
|
11704312be | ||
|
|
74ba2fb141 | ||
|
|
cc410bc23a | ||
|
|
2252fd951a | ||
|
|
33caee50cb | ||
|
|
28ef9da0d9 | ||
|
|
db2240c9c1 | ||
|
|
d3329453a2 | ||
|
|
6ffcc08591 | ||
|
|
facabded61 | ||
|
|
9d66eb16c8 | ||
|
|
6088692165 | ||
|
|
9e68399e04 | ||
|
|
e25f99ee02 | ||
|
|
37715863f0 | ||
|
|
3e69c2e36a | ||
|
|
17eab513e9 | ||
|
|
800e12b5ae | ||
|
|
96c6bbad2c | ||
|
|
1fa02e729d | ||
|
|
f93b0ead3c | ||
|
|
f80650e8ce | ||
|
|
bcf100f592 | ||
|
|
9546529f1d | ||
|
|
91ccf00ac8 | ||
|
|
f8f4203fb7 | ||
|
|
537c4fa5c2 | ||
|
|
dc7ebef925 | ||
|
|
b154361c00 | ||
|
|
3bb9a6d414 | ||
|
|
14c091aec7 | ||
|
|
6c1fea79ed | ||
|
|
ca8d629230 | ||
|
|
fd09a6ed50 | ||
|
|
23fbdd2ab5 | ||
|
|
27a8ff9d23 | ||
|
|
cf01ceb2f6 | ||
|
|
3bd9ea8ee7 | ||
|
|
d223b9d55e | ||
|
|
31b852c7bc | ||
|
|
add367bf2e | ||
|
|
b1b61ad4cf | ||
|
|
d73f0fd097 | ||
|
|
bf286dce76 | ||
|
|
d768fc371c | ||
|
|
7e392f9117 | ||
|
|
4675bec08a | ||
|
|
26165a86b7 | ||
|
|
69826668a7 | ||
|
|
a85dfd4141 | ||
|
|
abdcae870c | ||
|
|
dd49bd4c6d | ||
|
|
4adb7f9604 | ||
|
|
40ce15cca3 | ||
|
|
4e93959754 | ||
|
|
ab519fdc13 | ||
|
|
f3fedac429 | ||
|
|
3f921e159b | ||
|
|
d56e1d0f46 | ||
|
|
872968c420 | ||
|
|
f72db33d5e | ||
|
|
cfa2c41b44 | ||
|
|
0c461d61df | ||
|
|
27ae0693b4 | ||
|
|
42d5870546 | ||
|
|
f814039f99 | ||
|
|
b28e0445a2 | ||
|
|
7db7de65c1 | ||
|
|
a34dd1fc3f | ||
|
|
cd886102a9 | ||
|
|
2831689a1b | ||
|
|
069c5fd5d7 | ||
|
|
504e805da0 | ||
|
|
3cf12635d4 | ||
|
|
9adb7535fa | ||
|
|
c87c7a139c | ||
|
|
50583c4b0e | ||
|
|
9a7014cc14 | ||
|
|
32a78f3915 | ||
|
|
f866701088 | ||
|
|
49e911fcef | ||
|
|
665bfbc32d | ||
|
|
a0a9bdfe05 | ||
|
|
16f7e42392 | ||
|
|
bf485f66d3 | ||
|
|
557d6cba2d | ||
|
|
8eee90d2c6 | ||
|
|
9da0cf8b4c | ||
|
|
c259c478e5 | ||
|
|
c2ec4bb482 | ||
|
|
29d0214cbd | ||
|
|
faf93bb71d | ||
|
|
71f6f4c7d7 | ||
|
|
b22dbc7db9 | ||
|
|
bfe18b8198 | ||
|
|
82bd395289 | ||
|
|
e3031f3a7b | ||
|
|
6f47203e3f | ||
|
|
afc2357d5f | ||
|
|
c1ae80d25d | ||
|
|
f23d45fcb4 | ||
|
|
0b4974eb1c | ||
|
|
228d22cff0 | ||
|
|
7b1eaf1e4f | ||
|
|
bfb00ecb27 | ||
|
|
fefa21fa01 | ||
|
|
bb7003e557 | ||
|
|
0122aa82ef | ||
|
|
7db6c203d5 | ||
|
|
42394090df | ||
|
|
447d433457 | ||
|
|
9b54286933 | ||
|
|
d2549312d6 | ||
|
|
fdf8f44c90 | ||
|
|
30893b0184 | ||
|
|
eb7f91ffcd | ||
|
|
413e01e52c | ||
|
|
b57b3abc49 | ||
|
|
4ae9f53593 | ||
|
|
265195ca80 | ||
|
|
8b52e04c3a | ||
|
|
572a096e60 | ||
|
|
4d7b8efc3e | ||
|
|
fa32631993 | ||
|
|
ec3be81217 | ||
|
|
53c86258a6 | ||
|
|
7226ba5086 | ||
|
|
e219b8864e | ||
|
|
863ca93f0b | ||
|
|
68e0997dc9 | ||
|
|
dc29923a08 | ||
|
|
f7bc0aced8 | ||
|
|
e7c8ed1899 | ||
|
|
b8443683f9 | ||
|
|
2fda12d220 | ||
|
|
790ca0e7b6 | ||
|
|
d5dd5f4567 | ||
|
|
5b51722720 | ||
|
|
6640f8799e | ||
|
|
790dbf0fdf | ||
|
|
b7b85b10fc | ||
|
|
5505af439a | ||
|
|
cc35d3c4e5 | ||
|
|
a0f131cf86 | ||
|
|
f87942ec76 | ||
|
|
af7c088053 | ||
|
|
7af4e772bd | ||
|
|
67a0b43c81 | ||
|
|
2bff0d6882 | ||
|
|
2ab117166f | ||
|
|
8effd35d3f | ||
|
|
5c7f8b8d73 | ||
|
|
b1f100e3da | ||
|
|
e002b49244 | ||
|
|
bd435d4154 | ||
|
|
a869386996 | ||
|
|
a40379782e | ||
|
|
761893d1fc | ||
|
|
9f4f9b323c | ||
|
|
48fd585620 | ||
|
|
96ad106ee1 | ||
|
|
e2b0297d18 | ||
|
|
d6fa69d26e | ||
|
|
71546359b8 | ||
|
|
234ab80086 | ||
|
|
cba3f1115f | ||
|
|
5568521cd1 | ||
|
|
a5a2db577f | ||
|
|
b6387b9eaf | ||
|
|
0f2618b21d | ||
|
|
0465637335 | ||
|
|
e71072b90b | ||
|
|
289e78c136 | ||
|
|
001ab61b37 | ||
|
|
16395012d1 | ||
|
|
5497967665 | ||
|
|
0115ae6da4 | ||
|
|
bb5022441a | ||
|
|
8668b74622 | ||
|
|
752bb2ef41 | ||
|
|
bde28b1320 | ||
|
|
689dcefa3a | ||
|
|
f3e263a051 | ||
|
|
fa48ab16bf | ||
|
|
0eca5c4b06 | ||
|
|
6b76c807f5 | ||
|
|
76695414c7 | ||
|
|
d64ac13447 | ||
|
|
50159d47c2 | ||
|
|
f80d9c41e5 | ||
|
|
2f99707792 | ||
|
|
7a08dde7ac | ||
|
|
81297df4ff | ||
|
|
c4137c0c2a | ||
|
|
944c72a275 | ||
|
|
46e2d72213 | ||
|
|
6cb85e8544 | ||
|
|
6ef9773c02 | ||
|
|
11aa57e67f | ||
|
|
779f153071 | ||
|
|
60d7ee6710 | ||
|
|
852739c477 | ||
|
|
bee2e5f50c | ||
|
|
ace2d53b18 | ||
|
|
f910219c7a | ||
|
|
aeb7cb2a40 | ||
|
|
a7e065ddb4 | ||
|
|
713842330e | ||
|
|
5936a892a7 | ||
|
|
c3a4f3eab6 | ||
|
|
fb7ee2f487 | ||
|
|
45e57f0228 | ||
|
|
d20a7c4996 | ||
|
|
9920586b22 | ||
|
|
61dad14345 | ||
|
|
f65506d285 | ||
|
|
cbbd1599f2 | ||
|
|
4b34ff3075 | ||
|
|
654d7953d9 | ||
|
|
d91ea65f5a | ||
|
|
7911b2c001 | ||
|
|
0d854a5144 | ||
|
|
cb5b241715 | ||
|
|
039b05fb16 | ||
|
|
9c1d412dac | ||
|
|
a318d4db26 | ||
|
|
0cfafddacc | ||
|
|
f5ca753377 | ||
|
|
0d4849ff98 | ||
|
|
e3b9afcfaa | ||
|
|
af781bfa5e | ||
|
|
7dd9637b1a | ||
|
|
ddce57b585 | ||
|
|
11fc419cee | ||
|
|
a8dec66a84 | ||
|
|
7e541ca380 | ||
|
|
1bd58bdc15 | ||
|
|
9107fa3c81 | ||
|
|
df0cd3c4a3 | ||
|
|
f3b34c44b5 | ||
|
|
9e101ee97f | ||
|
|
433be59ea7 | ||
|
|
2c444d6bf4 | ||
|
|
a90c19d577 | ||
|
|
8f283499e9 | ||
|
|
d78fe665a7 | ||
|
|
532abf77e7 | ||
|
|
59696d1ace | ||
|
|
d30e457031 | ||
|
|
ae041b5c54 | ||
|
|
a2b60e4bcb | ||
|
|
9d126f6dd8 | ||
|
|
bd8045b648 | ||
|
|
5c1a022a3c | ||
|
|
e19ca5405a | ||
|
|
56d37acc7d | ||
|
|
0571a2f15d | ||
|
|
0cdfa5b3dc | ||
|
|
7b9880aaab | ||
|
|
7c36ed21b3 | ||
|
|
985835f96f | ||
|
|
f81765b96e | ||
|
|
a2ceaff053 | ||
|
|
94181b1be7 | ||
|
|
9966387002 | ||
|
|
169fdd7330 | ||
|
|
78b8139d46 | ||
|
|
76f8d2828b | ||
|
|
b692e49b1e | ||
|
|
bd7f001c26 | ||
|
|
d6ff768298 | ||
|
|
4947a905fa | ||
|
|
cd9a16cfab | ||
|
|
02a53f193d | ||
|
|
b1717c8a97 | ||
|
|
911ddcda40 | ||
|
|
daf06622a2 | ||
|
|
9f1d5acdc8 | ||
|
|
9756d92a91 | ||
|
|
d32d1937e6 | ||
|
|
ddcbae54ab | ||
|
|
ca748faa3f | ||
|
|
5e6cbac589 | ||
|
|
ff75dd6cd5 | ||
|
|
f8391994b4 | ||
|
|
5ce14e5024 | ||
|
|
ee22c9cab7 | ||
|
|
4a893ce8c6 | ||
|
|
9ce923ebeb | ||
|
|
494a807f28 | ||
|
|
820f17c73b | ||
|
|
cd7ba1744e | ||
|
|
da4e881007 | ||
|
|
878cda7c55 | ||
|
|
47327616df | ||
|
|
eeff67d88d | ||
|
|
6adf4f294a | ||
|
|
5e7cc3437d | ||
|
|
ded76df3af | ||
|
|
d7362ed22d | ||
|
|
4711bfe40c | ||
|
|
8c0e0b3b47 | ||
|
|
096b2c4435 | ||
|
|
129d0f90af | ||
|
|
c76fd5eb85 | ||
|
|
9e42f8d57e | ||
|
|
168e31bfb6 | ||
|
|
d37c0eb477 | ||
|
|
691b7d1df3 | ||
|
|
336bc14e1a | ||
|
|
b2c39b61bb | ||
|
|
da31b966fb | ||
|
|
dce94267fb | ||
|
|
d1647a51af | ||
|
|
5b76aa9d2f | ||
|
|
0add0f95e2 | ||
|
|
6ce0bf390c | ||
|
|
7c98e531f2 | ||
|
|
e1ab8c79c1 | ||
|
|
cf3e81b590 | ||
|
|
565cff2d07 | ||
|
|
8383b28322 | ||
|
|
e1c530e420 | ||
|
|
035924bc1e | ||
|
|
fd508f0e3a | ||
|
|
9407e26433 | ||
|
|
3cac8448d3 | ||
|
|
84642b1fed | ||
|
|
fa574866b4 | ||
|
|
4b807107a6 | ||
|
|
6bb62dd6ef | ||
|
|
3d6f40bd5e | ||
|
|
c2bd42e774 | ||
|
|
529a25d30b | ||
|
|
ccdc148472 | ||
|
|
7dcb2a50ff | ||
|
|
1e3215226e | ||
|
|
459d0198d9 | ||
|
|
3abf91644e | ||
|
|
1f98338481 | ||
|
|
91078545e6 | ||
|
|
467633c2de | ||
|
|
e44df7b386 | ||
|
|
bbff6768da | ||
|
|
0020a496f7 | ||
|
|
5a3e920902 | ||
|
|
96f893f350 | ||
|
|
97c8cd7d1a | ||
|
|
ee3f506d10 | ||
|
|
0f244a4a07 | ||
|
|
c79916b1ba | ||
|
|
2ce1c7cce3 | ||
|
|
75fa235498 | ||
|
|
7413c6a5fb | ||
|
|
315ed56450 | ||
|
|
9eddc3c5a7 | ||
|
|
38d5bf8e09 | ||
|
|
85d623bc86 | ||
|
|
928b823d8f | ||
|
|
2c2b2ab825 | ||
|
|
d3184da4c6 | ||
|
|
eadb469712 | ||
|
|
2649d39fd3 | ||
|
|
faab836ec9 | ||
|
|
c9f500cc0b | ||
|
|
42bf3c0e2b | ||
|
|
3b395a9da2 | ||
|
|
633da991d2 | ||
|
|
e07b8a5d15 | ||
|
|
71a7a7df13 | ||
|
|
436a047617 | ||
|
|
4d70a36c25 | ||
|
|
304d0e951f | ||
|
|
2fdb35614d | ||
|
|
5dff9df1f7 | ||
|
|
84682a1159 | ||
|
|
c151147258 | ||
|
|
317fcadccb | ||
|
|
a2e9682002 | ||
|
|
5dcf0ae683 | ||
|
|
ce3e394a41 | ||
|
|
0c92647760 | ||
|
|
39527da73c | ||
|
|
fa90046136 | ||
|
|
efe1374940 | ||
|
|
b751ffe5a0 | ||
|
|
28a2a01c29 | ||
|
|
0c650423ed | ||
|
|
4ffc861854 | ||
|
|
8338873e56 | ||
|
|
dd520f8889 | ||
|
|
4842d8be60 | ||
|
|
bf6e6ad33b | ||
|
|
e1094da3cf | ||
|
|
7d150ce973 | ||
|
|
39264a7057 | ||
|
|
736e166168 | ||
|
|
e8b521e3ab | ||
|
|
694b3c9318 | ||
|
|
a4954d386a | ||
|
|
976193ce70 | ||
|
|
ee0b26601b | ||
|
|
78e310231e | ||
|
|
ffa554b371 | ||
|
|
68a0fc95da | ||
|
|
9b43e76947 | ||
|
|
adf760e246 | ||
|
|
7a89df8350 | ||
|
|
93d7511b38 | ||
|
|
7c33fe30b7 | ||
|
|
cd49194383 | ||
|
|
22dbd3eb17 | ||
|
|
53a4134379 | ||
|
|
51e2714408 | ||
|
|
d825beff27 | ||
|
|
aa4eb59bb0 | ||
|
|
00d450cce7 | ||
|
|
a090ceac6c | ||
|
|
63c37b1780 | ||
|
|
dfd2d7600d | ||
|
|
0fd385d719 | ||
|
|
57d8583887 | ||
|
|
9c116e9031 | ||
|
|
e6c16a6ddc | ||
|
|
03669a1804 | ||
|
|
3c44db8746 | ||
|
|
9a1a203b52 | ||
|
|
8b51c295a1 | ||
|
|
738ca56ccd | ||
|
|
f901b4a5fd | ||
|
|
4fff8399ea | ||
|
|
e532d6f1a1 | ||
|
|
db2a3800ed | ||
|
|
183928e6ba | ||
|
|
c0e2030905 | ||
|
|
87c428e7cd | ||
|
|
f5ad795995 | ||
|
|
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 | ||
|
|
ffd45f6893 | ||
|
|
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 | ||
|
|
d96e47cc38 | ||
|
|
afc1815f3f | ||
|
|
7aa33ff471 |
@@ -1,7 +1,5 @@
|
|||||||
.github
|
.github
|
||||||
docs
|
docs
|
||||||
examples
|
examples
|
||||||
hack
|
|
||||||
site
|
|
||||||
travis
|
travis
|
||||||
*.md
|
*.md
|
||||||
|
|||||||
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://kubectl.docs.kubernetes.io/contributing/kustomize/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 -->
|
|
||||||
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@@ -4,3 +4,11 @@ updates:
|
|||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
|
||||||
|
- package-ecosystem: gomod
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
vulnerability-alerts:
|
||||||
|
enabled: true
|
||||||
36
.github/workflows/apidiff.yml
vendored
Normal file
36
.github/workflows/apidiff.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: APIDiff
|
||||||
|
|
||||||
|
# Trigger the workflow on pull requests and direct pushes to any branch
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
go-apidiff:
|
||||||
|
name: Verify API differences
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Pull requests from different repository only trigger this checks
|
||||||
|
if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository)
|
||||||
|
steps:
|
||||||
|
- name: Clone the code
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
|
||||||
|
with:
|
||||||
|
go-version-file: go.work
|
||||||
|
- name: Execute go-apidiff
|
||||||
|
uses: joelanford/go-apidiff@60c4206be8f84348ebda2a3e0c3ac9cb54b8f685
|
||||||
|
with:
|
||||||
|
compare-imports: true
|
||||||
|
print-compatible: true
|
||||||
|
- name: Report failure
|
||||||
|
uses: nashmaniac/create-issue-action@6814b79f58a9e25070c226b0c847e67b0c06efdd
|
||||||
|
# Only report failures of pushes (PRs have are visible through the Checks section) to the default branch
|
||||||
|
if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
|
with:
|
||||||
|
title: 🐛 go-apidiff failed for ${{ github.sha }}
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
labels: kind/bug
|
||||||
|
body: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
202
.github/workflows/go.yml
vendored
202
.github/workflows/go.yml
vendored
@@ -2,101 +2,157 @@ name: Go
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [master]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [master]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
## TODO: conditional-changes checker is not working
|
||||||
|
conditional-changes:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: read
|
||||||
|
outputs:
|
||||||
|
doc: ${{ steps.filter.outputs.doc }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||||
|
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d
|
||||||
|
id: filter
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
doc:
|
||||||
|
- 'site/**'
|
||||||
|
|
||||||
|
check-modules:
|
||||||
|
name: check-synced-go-modules
|
||||||
|
# needs: conditional-changes
|
||||||
|
# if: needs.conditional-changes.outputs.doc == 'false'
|
||||||
|
runs-on: [ubuntu-latest]
|
||||||
|
steps:
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
|
||||||
|
with:
|
||||||
|
go-version-file: go.work
|
||||||
|
cache: true
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
id: go
|
||||||
|
- name: sync go modules
|
||||||
|
run: make workspace-sync
|
||||||
|
- name: check for changes with 'make workspace-sync'
|
||||||
|
run: git diff --exit-code
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
|
# needs: conditional-changes
|
||||||
|
# if: needs.conditional-changes.outputs.doc == 'false'
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Set up Go 1.x
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ^1.18
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
|
||||||
|
with:
|
||||||
|
go-version-file: go.work
|
||||||
|
cache: true
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
id: go
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: make lint
|
run: make lint
|
||||||
|
|
||||||
- name: Verify boilerplate
|
- name: Verify boilerplate
|
||||||
run: make check-license
|
run: make check-license
|
||||||
|
|
||||||
test-linux:
|
## Test all modules without plugins and released modules
|
||||||
|
test-non-released-modules:
|
||||||
name: Test Linux
|
name: Test Linux
|
||||||
|
# needs: conditional-changes
|
||||||
|
# if: needs.conditional-changes.outputs.doc == 'false'
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
steps:
|
steps:
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
|
||||||
|
with:
|
||||||
|
go-version-file: go.work
|
||||||
|
cache: true
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
id: go
|
||||||
|
- name: Test all modules without plugins and released modules
|
||||||
|
run: make test-unit-non-plugin-and-non-released
|
||||||
|
env:
|
||||||
|
KUSTOMIZE_DOCKER_E2E: true
|
||||||
|
|
||||||
- name: Set up Go 1.x
|
test-modules:
|
||||||
uses: actions/setup-go@v3
|
name: Test ${{ matrix.os }} - ${{ matrix.module }}
|
||||||
with:
|
# needs: conditional-changes
|
||||||
go-version: ^1.18
|
# if: needs.conditional-changes.outputs.doc == 'false'
|
||||||
id: go
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
- name: Check out code into the Go module directory
|
fail-fast: false
|
||||||
uses: actions/checkout@v2
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
- name: Test all modules
|
module:
|
||||||
run: make test-unit-non-plugin
|
- kyaml
|
||||||
env:
|
- cmd/config
|
||||||
KUSTOMIZE_DOCKER_E2E: true
|
- api
|
||||||
|
- kustomize
|
||||||
test-macos:
|
include:
|
||||||
name: Test MacOS
|
- module: kyaml
|
||||||
runs-on: [macos-latest]
|
test-cmd: go test -race -v -cover ./...
|
||||||
|
- module: cmd/config
|
||||||
|
test-cmd: go test -v -cover ./...
|
||||||
|
- module: api
|
||||||
|
test-cmd: go test -v -cover ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.buildDate=2023-01-31T23:38:41Z -X sigs.k8s.io/kustomize/api/provenance.version=(test)"
|
||||||
|
- module: kustomize
|
||||||
|
test-cmd: go test -v -cover ./...
|
||||||
|
- os: ubuntu-latest
|
||||||
|
docker-e2e: true
|
||||||
|
- os: macos-latest
|
||||||
|
docker-e2e: false
|
||||||
|
- os: windows-latest
|
||||||
|
docker-e2e: false
|
||||||
|
env:
|
||||||
|
KUSTOMIZE_DOCKER_E2E: ${{ matrix.docker-e2e }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
- name: Set up Go 1.x
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||||
uses: actions/setup-go@v3
|
- name: Set up Go 1.x
|
||||||
with:
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
|
||||||
go-version: ^1.18
|
with:
|
||||||
id: go
|
go-version-file: go.work
|
||||||
|
cache: true
|
||||||
- name: Check out code into the Go module directory
|
cache-dependency-path: |
|
||||||
uses: actions/checkout@v2
|
**/go.sum
|
||||||
|
id: go
|
||||||
- name: Test all modules
|
- name: Test ${{ matrix.module }}
|
||||||
run: make test-unit-non-plugin
|
run: ${{ matrix.test-cmd }}
|
||||||
env:
|
|
||||||
KUSTOMIZE_DOCKER_E2E: false # docker not installed on mac
|
|
||||||
|
|
||||||
test-windows:
|
|
||||||
name: Test Windows
|
|
||||||
runs-on: [windows-latest]
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: Set up Go 1.x
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: ^1.18
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Test kyaml
|
|
||||||
run: go test -cover ./...
|
|
||||||
working-directory: ./kyaml
|
|
||||||
|
|
||||||
- name: Test cmd/config
|
|
||||||
run: go test -cover ./...
|
|
||||||
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.
|
# TODO (#4001): replace specific modules above with this once Windows tests are passing.
|
||||||
#- name: Test all modules
|
if: ${{ !(matrix.os == 'windows-latest' && (matrix.module == 'api' || matrix.module == 'kustomize')) }}
|
||||||
# run: make test-unit-non-plugin
|
working-directory: ./${{ matrix.module }}
|
||||||
# env:
|
|
||||||
# KUSTOMIZE_DOCKER_E2E: false # docker on windows not working well yet
|
# Aggregation matrix tests from test-modules for branch protection rules
|
||||||
|
test-modules-summary:
|
||||||
|
name: Test Summary
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test-modules
|
||||||
|
if: always()
|
||||||
|
steps:
|
||||||
|
- name: Check test results
|
||||||
|
run: |
|
||||||
|
if [[ "${{ needs.test-modules.result }}" != "success" ]]; then
|
||||||
|
echo "Some tests failed or were cancelled"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "All tests passed successfully"
|
||||||
|
|||||||
30
.github/workflows/release.yaml
vendored
Normal file
30
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: release
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write # Allow actions to update dependabot PRs
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- kyaml/v*
|
||||||
|
- cmd/config/v*
|
||||||
|
- api/v*
|
||||||
|
- kustomize/v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out code into the Go module directory
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Set up Go 1.x
|
||||||
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c
|
||||||
|
with:
|
||||||
|
go-version-file: go.work
|
||||||
|
id: go
|
||||||
|
- run: ./releasing/create-release.sh "${tag}"
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -31,3 +31,4 @@ site/.hugo_build.lock
|
|||||||
|
|
||||||
# goreleaser artifacts
|
# goreleaser artifacts
|
||||||
**/dist/
|
**/dist/
|
||||||
|
/output/
|
||||||
|
|||||||
138
.golangci.yml
138
.golangci.yml
@@ -3,91 +3,50 @@
|
|||||||
|
|
||||||
run:
|
run:
|
||||||
deadline: 5m
|
deadline: 5m
|
||||||
|
go: "1.25"
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
# please, do not use `enable-all`: it's deprecated and will be removed soon.
|
enable-all: true
|
||||||
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
|
disable:
|
||||||
disable-all: true
|
- cyclop
|
||||||
enable:
|
|
||||||
- asciicheck
|
|
||||||
- bidichk
|
|
||||||
- bodyclose
|
|
||||||
- contextcheck
|
|
||||||
# - cyclop
|
|
||||||
- deadcode
|
|
||||||
- depguard
|
- depguard
|
||||||
- dogsled
|
- exhaustivestruct
|
||||||
- dupl
|
- forbidigo
|
||||||
- durationcheck
|
- funlen
|
||||||
- errcheck
|
- gci
|
||||||
- errname
|
- gocognit
|
||||||
- errorlint
|
- godot
|
||||||
- exhaustive
|
- godox
|
||||||
# - exhaustivestruct
|
- goerr113
|
||||||
- exportloopref
|
- gofumpt
|
||||||
# - forbidigo
|
- ifshort # too many false positives
|
||||||
- forcetypeassert
|
- ireturn
|
||||||
# - funlen
|
- nilnil
|
||||||
# - gci
|
- nlreturn
|
||||||
- gochecknoglobals
|
- noctx
|
||||||
- gochecknoinits
|
- paralleltest
|
||||||
# - gocognit
|
- perfsprint
|
||||||
- goconst
|
- stylecheck
|
||||||
- gocritic
|
- testifylint
|
||||||
- gocyclo
|
- varnamelen
|
||||||
# - godot
|
- wsl
|
||||||
# - godox
|
- exhaustruct
|
||||||
# - goerr113
|
- deadcode
|
||||||
- gofmt
|
- scopelint
|
||||||
# - gofumpt
|
- nonamedreturns
|
||||||
- goheader
|
- golint
|
||||||
- goimports
|
- maintidx
|
||||||
- gomnd
|
- nosnakecase
|
||||||
- gomoddirectives
|
- testpackage # it's better to keep tests in the same package for now because kustomize does open box testing
|
||||||
- gomodguard
|
- structcheck # abandoned by author
|
||||||
- goprintffuncname
|
- varcheck # abandoned by author
|
||||||
- gosec
|
- maligned # abandoned by author
|
||||||
- gosimple
|
- interfacer # archived by author
|
||||||
- govet
|
# TODO(koba1t): temporarily disabled, will be addressed after upgrading to Go 1.24
|
||||||
# - ifshort # too many false positives
|
- usetesting
|
||||||
- importas
|
- mnd
|
||||||
- ineffassign
|
- copyloopvar
|
||||||
# - ireturn
|
- intrange
|
||||||
- lll
|
|
||||||
- makezero
|
|
||||||
- misspell
|
|
||||||
- nakedret
|
|
||||||
- nestif
|
|
||||||
- nilerr
|
|
||||||
# - nilnil
|
|
||||||
# - nlreturn
|
|
||||||
# - noctx
|
|
||||||
- nolintlint
|
|
||||||
# - paralleltest
|
|
||||||
- prealloc
|
|
||||||
- predeclared
|
|
||||||
- promlinter
|
|
||||||
- revive
|
|
||||||
- rowserrcheck
|
|
||||||
- sqlclosecheck
|
|
||||||
- staticcheck
|
|
||||||
- structcheck
|
|
||||||
# - stylecheck
|
|
||||||
- tagliatelle
|
|
||||||
- tenv
|
|
||||||
- testpackage
|
|
||||||
- thelper
|
|
||||||
- tparallel
|
|
||||||
- typecheck
|
|
||||||
- unconvert
|
|
||||||
- unparam
|
|
||||||
- unused
|
|
||||||
- varcheck
|
|
||||||
# - varnamelen
|
|
||||||
- wastedassign
|
|
||||||
- whitespace
|
|
||||||
- wrapcheck
|
|
||||||
# - wsl
|
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
dupl:
|
dupl:
|
||||||
@@ -98,18 +57,19 @@ linters-settings:
|
|||||||
min-complexity: 30
|
min-complexity: 30
|
||||||
revive:
|
revive:
|
||||||
rules:
|
rules:
|
||||||
- name: var-naming
|
- name: var-naming
|
||||||
arguments:
|
arguments:
|
||||||
- [ "ID", "API", "JSON" ] # AllowList
|
- ["ID", "API", "JSON"] # AllowList
|
||||||
- [ ] # DenyList
|
- [] # DenyList
|
||||||
|
gomnd:
|
||||||
|
ignored-functions:
|
||||||
|
- os.WriteFile
|
||||||
|
- make
|
||||||
gomoddirectives:
|
gomoddirectives:
|
||||||
replace-local: true
|
replace-local: true
|
||||||
gosec:
|
gosec:
|
||||||
config:
|
config:
|
||||||
G306: "0644"
|
G306: "0644"
|
||||||
gomnd:
|
|
||||||
ignored-functions:
|
|
||||||
- ioutil.WriteFile
|
|
||||||
wrapcheck:
|
wrapcheck:
|
||||||
ignoreSigs:
|
ignoreSigs:
|
||||||
# defaults
|
# defaults
|
||||||
|
|||||||
247
CONTRIBUTING.md
247
CONTRIBUTING.md
@@ -13,6 +13,7 @@
|
|||||||
[CNCF Code of Conduct]: https://github.com/cncf/foundation/blob/master/code-of-conduct.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
|
[Kubernetes Community Membership]: https://github.com/kubernetes/community/blob/master/community-membership.md
|
||||||
|
|
||||||
|
[Kustomize Architecture]: ARCHITECTURE.md
|
||||||
[Contribution Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/
|
[Contribution Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/
|
||||||
[MacOS Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/mac/
|
[MacOS Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/mac/
|
||||||
[Windows Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/windows/
|
[Windows Dev Guide]: https://kubectl.docs.kubernetes.io/contributing/kustomize/windows/
|
||||||
@@ -25,13 +26,113 @@ _As contributors and maintainers of this project, and in the interest of fosteri
|
|||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
Dev guides:
|
### Forking Kustomize and Working Locally
|
||||||
|
The Kustomize project uses a "Fork and Pull" workflow that is standard to GitHub. In git terms, your personal fork is referred to as the "origin" and the actual project's git repository is called "upstream". To keep your personal branch (origin) up to date with the project (upstream), it must be configured within your local working copy.
|
||||||
|
|
||||||
- [Contribution Guide]
|
### Create a fork in GitHub
|
||||||
- [MacOS Dev Guide]
|
1. Visit https://github.com/kubernetes-sigs/kustomize
|
||||||
- [Windows Dev Guide]
|
2. Click the `Fork` button on the top right
|
||||||
|
|
||||||
General resources for contributors:
|
### Clone the repository
|
||||||
|
```bash
|
||||||
|
# Clone your repository fork from the previous step
|
||||||
|
git clone --recurse-submodules git@github.com:<your github username>/kustomize.git
|
||||||
|
cd kustomize
|
||||||
|
|
||||||
|
# Configure upstream
|
||||||
|
git remote add upstream https://github.com/kubernetes-sigs/kustomize
|
||||||
|
git remote set-url --push upstream no_push
|
||||||
|
|
||||||
|
# Review git configuration
|
||||||
|
git remote -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a working branch
|
||||||
|
```bash
|
||||||
|
# Fetch changes from upstream master
|
||||||
|
cd kustomize
|
||||||
|
git fetch upstream
|
||||||
|
git checkout master
|
||||||
|
git rebase upstream/master
|
||||||
|
|
||||||
|
# Create your working branch
|
||||||
|
git checkout -b myfeature
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sync your working branch
|
||||||
|
You will need to periodically fetch changes from the `upstream` repository to keep your working branch in sync.
|
||||||
|
```bash
|
||||||
|
cd kustomize
|
||||||
|
git fetch upstream
|
||||||
|
git checkout myfeature
|
||||||
|
git rebase upstream/master
|
||||||
|
```
|
||||||
|
|
||||||
|
### Push to GitHub
|
||||||
|
When your changes are ready for review, push your working branch to your fork on GitHub.
|
||||||
|
```bash
|
||||||
|
cd kustomize
|
||||||
|
git push origin myfeature
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pull Request Rules
|
||||||
|
|
||||||
|
We are using [Conventional Commits v1.0.0](https://www.conventionalcommits.org/en/v1.0.0/) as the main guideline of making PR. This guideline serves to help contributor and maintainer to classify their changes, thus providing better insight on type of release will be covered on each Kustomize release cycle.
|
||||||
|
|
||||||
|
1. Please add these keywords on your PR titles accordingly
|
||||||
|
|
||||||
|
| Keyword | Description | Example |
|
||||||
|
| ------------- | ------------- | ------------- |
|
||||||
|
| fix | Patching or fixing bugs or improvements introduction from previous release. This type of change will mark a `PATCH` release. | fix: fix null value when generating yaml |
|
||||||
|
| feat | New features. This change will mark a `MINOR` release. | feat: new transformer and generator for ACME API CRD. |
|
||||||
|
| chore | Minor improvement outside main code base | chore: add exclusion for transformer test. |
|
||||||
|
| ci | CI/CD related changes (e.g. github workflow, scripts, CI steps). | ci: remove blocking tests |
|
||||||
|
| docs | Changes related to documentation. | docs: add rules documentation for PR. |
|
||||||
|
|
||||||
|
|
||||||
|
2. Add `BREAKING CHANGE:` on your commit message as footer to signify breaking changes. This will help maintainers identify `MAJOR` releases.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: change YAML parser from `yaml/v1` to `yaml/v2`
|
||||||
|
|
||||||
|
BREAKING CHANGE: parse() function now works with 2 arguments.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a Pull Request
|
||||||
|
|
||||||
|
1. Visit your fork at `https://github.com/<user>/kustomize`
|
||||||
|
2. Click the **Compare & Pull Request** button next to your `myfeature` branch.
|
||||||
|
3. Check out the pull request [process](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md) for more details and advice.
|
||||||
|
|
||||||
|
If you ran `git push` in the previous step, GitHub will return a useful link to create a Pull Request.
|
||||||
|
|
||||||
|
### Build Kustomize
|
||||||
|
The [Kustomize Architecture] document describes the respository organization and the kustomize build process.
|
||||||
|
```bash
|
||||||
|
# For go version >= 1.13
|
||||||
|
unset GOPATH
|
||||||
|
unset GO111MODULES
|
||||||
|
|
||||||
|
# Build kustomize binary and install in go bin path
|
||||||
|
cd kustomize
|
||||||
|
make kustomize
|
||||||
|
|
||||||
|
# Run unit tests
|
||||||
|
make test-unit-all
|
||||||
|
|
||||||
|
# Run linter
|
||||||
|
make lint
|
||||||
|
|
||||||
|
# Test examples against HEAD
|
||||||
|
make test-examples-kustomize-against-HEAD
|
||||||
|
|
||||||
|
# Run your development version
|
||||||
|
~/go/bin/kustomize version
|
||||||
|
```
|
||||||
|
|
||||||
|
### General resources for contributors
|
||||||
|
|
||||||
- [Contributor License Agreement] - Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests.
|
- [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.
|
- [Kubernetes Contributor Guide] - Main contributor documentation.
|
||||||
@@ -59,12 +160,144 @@ Kustomize follows the [Kubernetes Community Membership] contributor ladder. Role
|
|||||||
|
|
||||||
The kyaml module within the Kustomize repo has additional owners following the same ladder.
|
The kyaml module within the Kustomize repo has additional owners following the same ladder.
|
||||||
|
|
||||||
Administrative notes:
|
For the kustomize project, we have defined some specific guidelines on each step of the ladder:
|
||||||
|
|
||||||
|
To reach reviewer status, you must:
|
||||||
|
- Have been actively involved in kustomize for 3+ months
|
||||||
|
- Review at least 8 PRs that have been driven through to completion (see the reviewer guide below)
|
||||||
|
- Author at least 5 PRs that have been approved and merged
|
||||||
|
- Be a member of the kubernetes-sigs org. This should not be a blocker though, as once you meet the requirements for reviewer here,
|
||||||
|
the existing kustomize maintainers will be happy to sponsor your request to join the kubernetes-sigs org.
|
||||||
|
- Once you have met the above requirements, you may submit a PR adding yourself to the kustomize reviewers list, with links to your
|
||||||
|
contributions in the description.
|
||||||
|
|
||||||
|
To reach approver status, you must:
|
||||||
|
- Meet all the requirements of a reviewer
|
||||||
|
- Have been actively involved in kustomize for 6+ months
|
||||||
|
- Review at least 15 PRs that have been driven through to completion (see the reviewer guide below)
|
||||||
|
- Authored PRs meeting *either* of the following requirements:
|
||||||
|
- 15 PRs that have been approved and merged
|
||||||
|
- *OR* 10 PRs that have been approved and merged where some were more difficult, required greater thought/design,
|
||||||
|
or built up to larger features/long-term goals.
|
||||||
|
- File 3 issues. This can be any number of things, including but not limited to:
|
||||||
|
- Bugs with kustomize usage that you've found
|
||||||
|
- CI or release improvements
|
||||||
|
- Creating subtasks of a larger feature or project that you are in charge of.
|
||||||
|
- Long term improvements for the health of the project
|
||||||
|
- Triage at least 10 untriaged issues, including at least 1 feature request. The kustomize bug scrub is a great place to get practice with doing this, but you can
|
||||||
|
also follow the triage guide below to get started on your own.
|
||||||
|
- Demonstrate deeper understanding of kustomize goals. This can take many forms and is a bit subjective, but here are a few examples:
|
||||||
|
- saying no to an eschewed feature, instead recommending an alternative solution that is more aligned with the declarative configuration model
|
||||||
|
- active participation in discussion on a feature request issue
|
||||||
|
- filing an issue describing a long term problem and solution aligned with kustomize goals, for example: https://github.com/kubernetes-sigs/kustomize/issues/5140
|
||||||
|
- writing up KEPs for features that will improve the kustomize workflow while being aligned with kustomize goals, for example: https://github.com/kubernetes-sigs/kustomize/pull/4558
|
||||||
|
- Regularly interact with the existing kustomize maintainers, with clear communication about what you are working on or planning to work on. The kustomize
|
||||||
|
maintainers should know who you are and be familiar with your contributions.
|
||||||
|
- If you meet *most* of the above requirements while going above and beyond in a few areas, we will still consider your request to become an approver even
|
||||||
|
if you are missing one or two of the requirements. Please contact the maintainers directly to ask about getting approver status if you fall into this category.
|
||||||
|
- Otherwise, once you meet all the above requirements, you may:
|
||||||
|
- request to be added to the kustomize maintainer meeting that occurs each week with the kustomize PMs.
|
||||||
|
- submit a PR adding yourself to the kustomize approvers list, with links to your contributions in the description.
|
||||||
|
|
||||||
|
To reach owner status, you must:
|
||||||
|
- Meet all the requirements of an approver
|
||||||
|
- Have been actively involved with kustomize for 1+ year
|
||||||
|
- Assisted the current owner in driving the roadmap. This can be explicit or implicit help, such as:
|
||||||
|
- Editing the roadmap directly
|
||||||
|
- Reviewing the roadmap
|
||||||
|
- Providing suggestions for issues or prioritization in meetings that indirectly influence the roadmap
|
||||||
|
- Regularly triage issues and attend the kustomize bug scrub
|
||||||
|
- Regularly review PRs (1-2 a week)
|
||||||
|
- Periodically lead the kustomize bug scrub
|
||||||
|
- Periodically release kustomize (ensuring that there are no release blockers and that release notes are clean)
|
||||||
|
- Be the primary owner or point of contact for a particular project or area of code
|
||||||
|
- Ideally, there should be 2-3 owners at a time. Reach out to the current owners if you are interested in ownership. These
|
||||||
|
requirements are not strict and evaluation is somewhat subjective.
|
||||||
|
|
||||||
|
## Reviewer guide
|
||||||
|
Please watch this talk on how to review code from Tim Hockin: https://www.youtube.com/watch?v=OZVv7-o8i40
|
||||||
|
|
||||||
|
For reviewing PRs in kustomize, we have some specific guidelines:
|
||||||
|
- If the PR is introducing a new feature:
|
||||||
|
- *It must be implementing an issue that has already been triage/accepted or
|
||||||
|
a KEP that has been approved.* If it is not, then request the PR author to first file an issue.
|
||||||
|
- The PR must include thorough tests for the new feature, including unit and integration tests
|
||||||
|
- The code must be clean and readable, with thought given to how we will maintain the code in the future
|
||||||
|
- If the feature requires being broken up into multiple PRs to ease review, the feature should not be exposed to users
|
||||||
|
until the feature is completed in the last PR. For example, while we were building `kustomize localize`, we
|
||||||
|
built the feature almost entirely under the `api` module as a library with all the needed tests. There was no way
|
||||||
|
for users to invoke the localize code until the last PR that actually exposed the `kustomize localize` command in the
|
||||||
|
kustomize binary. This allowed us to continue development of `kustomize localize` without blocking kustomize releases.
|
||||||
|
If this type of development is not possible, then new features requiring multiple PRs should be
|
||||||
|
developed in their own feature branch.
|
||||||
|
- If the PR is introducing a bug fix:
|
||||||
|
- If the PR is not fixing an issue that has already been triage/accepted, follow the triage guide below on bug
|
||||||
|
fixes to decide if this is a PR we want to accept.
|
||||||
|
- The PR should have two distinct commits:
|
||||||
|
- The first commit should add a test demonstrating incorrect behavior
|
||||||
|
- The second commit should include the bug fix
|
||||||
|
- Some sample PRs:
|
||||||
|
- https://github.com/kubernetes-sigs/kustomize/pull/5263/commits
|
||||||
|
- https://github.com/kubernetes-sigs/kustomize/pull/3931/commits
|
||||||
|
- The regression test is absolutely required, and we cannot accept bug fixes without tests.
|
||||||
|
- If the PR is introducing a performance improvement:
|
||||||
|
- The PR description should give an indication of how much the performance is being improved and how we
|
||||||
|
can measure it - benchmark tests are fantastic.
|
||||||
|
- Other PRs (documentation, CI improvements, etc.) should be reviewed based on your best judgment.
|
||||||
|
|
||||||
|
## Triage guide
|
||||||
|
The possible triage labels are listed here: https://github.com/kubernetes-sigs/kustomize/labels?q=triage.
|
||||||
|
|
||||||
|
Triaging a feature request means:
|
||||||
|
- Understand what the user is asking for, and their use case.
|
||||||
|
- Verify that it is not an [eschewed feature](https://kubectl.docs.kubernetes.io/faq/kustomize/eschewedfeatures/#build-time-side-effects-from-cli-args-or-env-variables)
|
||||||
|
- Verify that it is not a duplicate issue.
|
||||||
|
- Look into workarounds. Is there another way that the user can achieve their use case with existing features?
|
||||||
|
- If you are new to this role, prior to leaving a comment on the issue, please bring it to weekly standup
|
||||||
|
for group discussion to make sure that we are all on the same page.
|
||||||
|
- Once you feel ready, you can label it with a triage label. Here's an [example](https://github.com/kubernetes-sigs/kustomize/issues/5049). You can also
|
||||||
|
look at other feature request issues to see how they were triaged and resolved. There are a few different triage labels that you can use, you can see the
|
||||||
|
full list [here](https://github.com/kubernetes-sigs/kustomize/labels?q=triage).
|
||||||
|
|
||||||
|
Triaging a bug means:
|
||||||
|
- First, verify that you can reproduce the issue. If you cannot reproduce the issue or need more information to give
|
||||||
|
it a go, triage it accordingly.
|
||||||
|
- Try to understand if this is really a bug or if this is intended behavior from kustomize. If it seems like intended
|
||||||
|
behavior, do your best to explain to the user why this is the case.
|
||||||
|
- If it seems to be a genuine bug, you can /triage accept the issue. In addition, investigate if there are workarounds or
|
||||||
|
alternative solutions for the user that they can try until the issue gets resolved.
|
||||||
|
|
||||||
|
The triage party for kustomize is here https://cli.triage.k8s.io/s/kustomize and can be a easy way to
|
||||||
|
find issues that have not been triaged yet.
|
||||||
|
|
||||||
|
## Project/Product Managers
|
||||||
|
|
||||||
|
Kustomize will have opportunities to join in a project/product manager role. You can reach out to
|
||||||
|
the existing kustomize maintainers if you are interested in this type of role. Project management work
|
||||||
|
can greatly help supplement your contributions as you climb from reviewer to approver
|
||||||
|
to owner.
|
||||||
|
|
||||||
|
Expectations for this role are:
|
||||||
|
|
||||||
|
- Triage 1 feature request each week, and bring it to weekly stand-up for discussion. Feature
|
||||||
|
requests are issues labeled kind/feature, and you can find them [here](https://github.com/kubernetes-sigs/kustomize/issues?q=is%3Aissue+is%3Aopen+kind+feature+label%3Akind%2Ffeature).
|
||||||
|
Please view the above triage guide for details on how to approach feature request triage.
|
||||||
|
- Monitor the kustomize Slack channel and try to help users if you can. It is a pretty
|
||||||
|
active channel, so responding to 4-5 users per week is sufficient even if some
|
||||||
|
questions go unanswered. If there is an interesting topic or a recurring problem that many
|
||||||
|
users are having, please bring it up in weekly stand-up.
|
||||||
|
- Keeping track of a queue of backlog issues or PRs that are not being actively looked at in any existing project board.
|
||||||
|
- Organizing or reorganizing project tracking boards when it makes sense.
|
||||||
|
|
||||||
|
You will also be asked to help with roadmap planning, deprecation communication, prioritization,
|
||||||
|
and doing research on kustomize usage when appropriate, though these responsibilities will occur less
|
||||||
|
frequently.
|
||||||
|
|
||||||
|
## Administrative notes:
|
||||||
|
|
||||||
- The [OWNERS file spec] is a useful resources in making changes.
|
- 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).
|
- 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
|
## Contact Information
|
||||||
|
|
||||||
- [Slack channel]
|
- [Slack channel]
|
||||||
|
|||||||
55
Makefile
55
Makefile
@@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
# Makefile for kustomize CLI and API.
|
# Makefile for kustomize CLI and API.
|
||||||
|
|
||||||
LATEST_V4_RELEASE=v4.5.6
|
LATEST_RELEASE=v5.8.1
|
||||||
|
|
||||||
SHELL := /usr/bin/env bash
|
SHELL := /usr/bin/env bash
|
||||||
GOOS = $(shell go env GOOS)
|
GOOS = $(shell go env GOOS)
|
||||||
@@ -56,26 +56,25 @@ uninstall-local-tools:
|
|||||||
|
|
||||||
# Build from local source.
|
# Build from local source.
|
||||||
$(MYGOBIN)/gorepomod:
|
$(MYGOBIN)/gorepomod:
|
||||||
cd cmd/gorepomod; \
|
cd cmd/gorepomod && go install .
|
||||||
go install .
|
|
||||||
|
|
||||||
# Build from local source.
|
# Build from local source.
|
||||||
$(MYGOBIN)/k8scopy:
|
$(MYGOBIN)/k8scopy:
|
||||||
cd cmd/k8scopy; \
|
cd cmd/k8scopy && go install .
|
||||||
go install .
|
|
||||||
|
|
||||||
# Build from local source.
|
# Build from local source.
|
||||||
$(MYGOBIN)/pluginator:
|
$(MYGOBIN)/pluginator:
|
||||||
cd cmd/pluginator; \
|
cd cmd/pluginator && go install .
|
||||||
go install .
|
|
||||||
|
|
||||||
|
|
||||||
# --- Build targets ---
|
# --- Build targets ---
|
||||||
|
|
||||||
# Build from local source.
|
# Build from local source.
|
||||||
$(MYGOBIN)/kustomize: build-kustomize-api
|
$(MYGOBIN)/kustomize: build-kustomize-api
|
||||||
cd kustomize; \
|
cd kustomize && go install -ldflags \
|
||||||
go install .
|
"-X sigs.k8s.io/kustomize/api/provenance.buildDate=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') \
|
||||||
|
-X sigs.k8s.io/kustomize/api/provenance.version=$(shell git describe --tags --always --dirty)" \
|
||||||
|
.
|
||||||
|
|
||||||
kustomize: $(MYGOBIN)/kustomize
|
kustomize: $(MYGOBIN)/kustomize
|
||||||
|
|
||||||
@@ -83,11 +82,11 @@ kustomize: $(MYGOBIN)/kustomize
|
|||||||
# plugin-to-api compatibility checks.
|
# plugin-to-api compatibility checks.
|
||||||
.PHONY: build-kustomize-api
|
.PHONY: build-kustomize-api
|
||||||
build-kustomize-api: $(MYGOBIN)/goimports $(builtinplugins)
|
build-kustomize-api: $(MYGOBIN)/goimports $(builtinplugins)
|
||||||
cd api; $(MAKE) build
|
cd api && $(MAKE) build
|
||||||
|
|
||||||
.PHONY: generate-kustomize-api
|
.PHONY: generate-kustomize-api
|
||||||
generate-kustomize-api:
|
generate-kustomize-api:
|
||||||
cd api; $(MAKE) generate
|
cd api && $(MAKE) generate
|
||||||
|
|
||||||
|
|
||||||
# --- Verification targets ---
|
# --- Verification targets ---
|
||||||
@@ -100,18 +99,21 @@ verify-kustomize-repo: \
|
|||||||
build-non-plugin-all \
|
build-non-plugin-all \
|
||||||
test-go-mod \
|
test-go-mod \
|
||||||
test-examples-kustomize-against-HEAD \
|
test-examples-kustomize-against-HEAD \
|
||||||
test-examples-kustomize-against-v4-release
|
test-examples-kustomize-against-latest-release
|
||||||
|
|
||||||
# The following target referenced by a file in
|
# The following target referenced by a file in
|
||||||
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
|
# https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-sigs/kustomize
|
||||||
.PHONY: prow-presubmit-check
|
.PHONY: prow-presubmit-check
|
||||||
prow-presubmit-check: \
|
prow-presubmit-check: \
|
||||||
install-tools \
|
install-tools \
|
||||||
|
workspace-sync \
|
||||||
|
generate-kustomize-builtin-plugins \
|
||||||
|
builtin-plugins-diff \
|
||||||
test-unit-kustomize-plugins \
|
test-unit-kustomize-plugins \
|
||||||
test-go-mod \
|
test-go-mod \
|
||||||
build-non-plugin-all \
|
build-non-plugin-all \
|
||||||
test-examples-kustomize-against-HEAD \
|
test-examples-kustomize-against-HEAD \
|
||||||
test-examples-kustomize-against-v4-release
|
test-examples-kustomize-against-latest-release
|
||||||
|
|
||||||
.PHONY: license
|
.PHONY: license
|
||||||
license: $(MYGOBIN)/addlicense
|
license: $(MYGOBIN)/addlicense
|
||||||
@@ -125,22 +127,30 @@ check-license: $(MYGOBIN)/addlicense
|
|||||||
lint: $(MYGOBIN)/golangci-lint $(MYGOBIN)/goimports $(builtinplugins)
|
lint: $(MYGOBIN)/golangci-lint $(MYGOBIN)/goimports $(builtinplugins)
|
||||||
./hack/for-each-module.sh "make lint"
|
./hack/for-each-module.sh "make lint"
|
||||||
|
|
||||||
|
.PHONY: apidiff
|
||||||
|
apidiff: $(MYGOBIN)/go-apidiff ## Run the go-apidiff to verify any API differences compared with origin/master
|
||||||
|
go-apidiff master --compare-imports --print-compatible --repo-path=.
|
||||||
|
|
||||||
.PHONY: test-unit-all
|
.PHONY: test-unit-all
|
||||||
test-unit-all: \
|
test-unit-all: \
|
||||||
test-unit-non-plugin \
|
test-unit-non-plugin \
|
||||||
test-unit-kustomize-plugins
|
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
|
.PHONY: test-unit-non-plugin
|
||||||
test-unit-non-plugin:
|
test-unit-non-plugin:
|
||||||
./hack/for-each-module.sh "make test" "./plugin/*" 15
|
./hack/for-each-module.sh "make test" "./plugin/*" 20
|
||||||
|
|
||||||
|
# This target is used by our Github Actions CI to run unit tests for all non-plugin and non-released modules in multiple GOOS environments.
|
||||||
|
.PHONY: test-unit-non-plugin-and-non-released
|
||||||
|
test-unit-non-plugin-and-non-released:
|
||||||
|
./hack/for-each-module.sh "make test" "./plugin/*|./kyaml/go.mod|./cmd/config/go.mod|./api/go.mod|./kustomize/go.mod" 16
|
||||||
|
|
||||||
.PHONY: build-non-plugin-all
|
.PHONY: build-non-plugin-all
|
||||||
build-non-plugin-all:
|
build-non-plugin-all:
|
||||||
./hack/for-each-module.sh "make build" "./plugin/*" 15
|
./hack/for-each-module.sh "make build" "./plugin/*" 20
|
||||||
|
|
||||||
.PHONY: test-unit-kustomize-plugins
|
.PHONY: test-unit-kustomize-plugins
|
||||||
test-unit-kustomize-plugins:
|
test-unit-kustomize-plugins: build-kustomize-external-go-plugin
|
||||||
./hack/testUnitKustomizePlugins.sh
|
./hack/testUnitKustomizePlugins.sh
|
||||||
|
|
||||||
.PHONY: functions-examples-all
|
.PHONY: functions-examples-all
|
||||||
@@ -152,7 +162,7 @@ functions-examples-all:
|
|||||||
done
|
done
|
||||||
|
|
||||||
test-go-mod:
|
test-go-mod:
|
||||||
./hack/for-each-module.sh "go list -m -json all > /dev/null && go mod tidy -v"
|
./hack/for-each-module.sh "go mod tidy -v"
|
||||||
|
|
||||||
.PHONY:
|
.PHONY:
|
||||||
verify-kustomize-e2e: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
|
verify-kustomize-e2e: $(MYGOBIN)/mdrip $(MYGOBIN)/kind
|
||||||
@@ -169,9 +179,14 @@ test-examples-kustomize-against-HEAD: $(MYGOBIN)/kustomize $(MYGOBIN)/mdrip
|
|||||||
./hack/testExamplesAgainstKustomize.sh HEAD
|
./hack/testExamplesAgainstKustomize.sh HEAD
|
||||||
|
|
||||||
.PHONY:
|
.PHONY:
|
||||||
test-examples-kustomize-against-v4-release: $(MYGOBIN)/mdrip
|
test-examples-kustomize-against-latest-release: $(MYGOBIN)/mdrip
|
||||||
./hack/testExamplesAgainstKustomize.sh v4@$(LATEST_V4_RELEASE)
|
./hack/testExamplesAgainstKustomize.sh v5@$(LATEST_RELEASE)
|
||||||
|
|
||||||
|
# Pushes dependencies in the go.work file back to go.mod files of each workspace module.
|
||||||
|
.PHONY: workspace-sync
|
||||||
|
workspace-sync:
|
||||||
|
go work sync
|
||||||
|
./hack/doGoMod.sh tidy
|
||||||
|
|
||||||
# --- Cleanup targets ---
|
# --- Cleanup targets ---
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ include $(KUSTOMIZE_ROOT)/Makefile-tools.mk
|
|||||||
.PHONY: lint test fix fmt tidy vet build
|
.PHONY: lint test fix fmt tidy vet build
|
||||||
|
|
||||||
lint: $(MYGOBIN)/golangci-lint
|
lint: $(MYGOBIN)/golangci-lint
|
||||||
|
$(MYGOBIN)/golangci-lint cache clean # Workaround for https://github.com/golangci/golangci-lint/issues/3228
|
||||||
$(MYGOBIN)/golangci-lint \
|
$(MYGOBIN)/golangci-lint \
|
||||||
-c $$KUSTOMIZE_ROOT/.golangci.yml \
|
-c $$KUSTOMIZE_ROOT/.golangci.yml \
|
||||||
--path-prefix $(shell pwd | sed -E 's|(.*\/kustomize)/(.*)|\2|') \
|
--path-prefix $(shell pwd | sed -E 's|(.*\/kustomize)/(.*)|\2|') \
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ _builtinplugins = \
|
|||||||
HashTransformer.go \
|
HashTransformer.go \
|
||||||
ImageTagTransformer.go \
|
ImageTagTransformer.go \
|
||||||
LabelTransformer.go \
|
LabelTransformer.go \
|
||||||
LegacyOrderTransformer.go \
|
SortOrderTransformer.go \
|
||||||
NamespaceTransformer.go \
|
NamespaceTransformer.go \
|
||||||
PatchJson6902Transformer.go \
|
PatchJson6902Transformer.go \
|
||||||
PatchStrategicMergeTransformer.go \
|
PatchStrategicMergeTransformer.go \
|
||||||
@@ -63,7 +63,7 @@ $(pGen)/GkeSaGenerator.go: $(pSrc)/gkesagenerator/GkeSaGenerator.go
|
|||||||
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
|
$(pGen)/HashTransformer.go: $(pSrc)/hashtransformer/HashTransformer.go
|
||||||
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
|
$(pGen)/ImageTagTransformer.go: $(pSrc)/imagetagtransformer/ImageTagTransformer.go
|
||||||
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
|
$(pGen)/LabelTransformer.go: $(pSrc)/labeltransformer/LabelTransformer.go
|
||||||
$(pGen)/LegacyOrderTransformer.go: $(pSrc)/legacyordertransformer/LegacyOrderTransformer.go
|
$(pGen)/SortOrderTransformer.go: $(pSrc)/sortordertransformer/SortOrderTransformer.go
|
||||||
$(pGen)/NamespaceTransformer.go: $(pSrc)/namespacetransformer/NamespaceTransformer.go
|
$(pGen)/NamespaceTransformer.go: $(pSrc)/namespacetransformer/NamespaceTransformer.go
|
||||||
$(pGen)/PatchJson6902Transformer.go: $(pSrc)/patchjson6902transformer/PatchJson6902Transformer.go
|
$(pGen)/PatchJson6902Transformer.go: $(pSrc)/patchjson6902transformer/PatchJson6902Transformer.go
|
||||||
$(pGen)/PatchStrategicMergeTransformer.go: $(pSrc)/patchstrategicmergetransformer/PatchStrategicMergeTransformer.go
|
$(pGen)/PatchStrategicMergeTransformer.go: $(pSrc)/patchstrategicmergetransformer/PatchStrategicMergeTransformer.go
|
||||||
@@ -79,7 +79,7 @@ $(pGen)/HelmChartInflationGenerator.go: $(pSrc)/helmchartinflationgenerator/Helm
|
|||||||
# The (verbose but portable) Makefile way to convert to lowercase.
|
# 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))))))))))))))))))))))))))
|
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
|
$(pGen)/%.go: $(MYGOBIN)/pluginator $(MYGOBIN)/goimports
|
||||||
@echo "generating $*"
|
@echo "generating $*"
|
||||||
( \
|
( \
|
||||||
set -e; \
|
set -e; \
|
||||||
@@ -89,9 +89,29 @@ $(pGen)/%.go: $(MYGOBIN)/pluginator
|
|||||||
$(MYGOBIN)/goimports -w $*.go \
|
$(MYGOBIN)/goimports -w $*.go \
|
||||||
)
|
)
|
||||||
|
|
||||||
# Target is for debugging.
|
# Generate builtin plugins
|
||||||
.PHONY: generate-kustomize-builtin-plugins
|
.PHONY: generate-kustomize-builtin-plugins
|
||||||
generate-kustomize-builtin-plugins: $(builtinplugins)
|
generate-kustomize-builtin-plugins: $(builtplugins)
|
||||||
|
for plugin in $(abspath $(wildcard $(pSrc)/*)); do \
|
||||||
|
echo "generating $${plugin} ..."; \
|
||||||
|
set -e; \
|
||||||
|
cd $${plugin}; \
|
||||||
|
go generate .; \
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check for diff by comparing current revision of generated plugins on HEAD and newly generated plugins on local branch,
|
||||||
|
# If diff is found, throw error code 1
|
||||||
|
.PHONY: builtin-plugins-diff
|
||||||
|
builtin-plugins-diff: $(builtplugins)
|
||||||
|
for file in $(abspath $(builtinplugins)); do \
|
||||||
|
echo "Checking for diff... $${file}" ; \
|
||||||
|
set -e ; \
|
||||||
|
if [ "`git diff $${file} | wc -c`" -gt 0 ]; then\
|
||||||
|
echo "Error(1): diff found on $${file}"; \
|
||||||
|
git diff $${file};\
|
||||||
|
exit 1;\
|
||||||
|
fi \
|
||||||
|
done
|
||||||
|
|
||||||
.PHONY: build-kustomize-external-go-plugin
|
.PHONY: build-kustomize-external-go-plugin
|
||||||
build-kustomize-external-go-plugin:
|
build-kustomize-external-go-plugin:
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
# Copyright 2022 The Kubernetes Authors.
|
# Copyright 2022 The Kubernetes Authors.
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
GOOS = $(shell go env GOOS)
|
||||||
|
GOARCH = $(shell go env GOARCH)
|
||||||
MYGOBIN = $(shell go env GOBIN)
|
MYGOBIN = $(shell go env GOBIN)
|
||||||
ifeq ($(MYGOBIN),)
|
ifeq ($(MYGOBIN),)
|
||||||
MYGOBIN = $(shell go env GOPATH)/bin
|
MYGOBIN = $(shell go env GOPATH)/bin
|
||||||
endif
|
endif
|
||||||
export PATH := $(MYGOBIN):$(PATH)
|
export PATH := $(MYGOBIN):$(PATH)
|
||||||
|
|
||||||
|
REPO_ROOT=$(shell git rev-parse --show-toplevel)
|
||||||
|
GOLANGCI_LINT_VERSION ?= $(shell cd $(REPO_ROOT)/hack && go list -m -f '{{.Version}}' github.com/golangci/golangci-lint)
|
||||||
|
|
||||||
# determines whether to run tests that only behave locally; can be overridden by override variable
|
# determines whether to run tests that only behave locally; can be overridden by override variable
|
||||||
export IS_LOCAL = false
|
export IS_LOCAL = false
|
||||||
|
|
||||||
@@ -16,8 +21,7 @@ install-out-of-tree-tools: \
|
|||||||
$(MYGOBIN)/golangci-lint \
|
$(MYGOBIN)/golangci-lint \
|
||||||
$(MYGOBIN)/helmV3 \
|
$(MYGOBIN)/helmV3 \
|
||||||
$(MYGOBIN)/mdrip \
|
$(MYGOBIN)/mdrip \
|
||||||
$(MYGOBIN)/stringer \
|
$(MYGOBIN)/stringer
|
||||||
$(MYGOBIN)/goimports
|
|
||||||
|
|
||||||
.PHONY: uninstall-out-of-tree-tools
|
.PHONY: uninstall-out-of-tree-tools
|
||||||
uninstall-out-of-tree-tools:
|
uninstall-out-of-tree-tools:
|
||||||
@@ -27,75 +31,68 @@ uninstall-out-of-tree-tools:
|
|||||||
rm -f $(MYGOBIN)/mdrip
|
rm -f $(MYGOBIN)/mdrip
|
||||||
rm -f $(MYGOBIN)/stringer
|
rm -f $(MYGOBIN)/stringer
|
||||||
|
|
||||||
|
# golangci-lint is not guaranteed to use from tool directive, so we install it directly.
|
||||||
|
# https://golangci-lint.run/docs/welcome/install/local/#install-from-sources
|
||||||
|
.PHONY: $(MYGOBIN)/golangci-lint
|
||||||
$(MYGOBIN)/golangci-lint:
|
$(MYGOBIN)/golangci-lint:
|
||||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2
|
go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/mdrip
|
||||||
$(MYGOBIN)/mdrip:
|
$(MYGOBIN)/mdrip:
|
||||||
go install github.com/monopole/mdrip@v1.0.2
|
cd $(REPO_ROOT)/hack && go install github.com/monopole/mdrip
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/stringer
|
||||||
$(MYGOBIN)/stringer:
|
$(MYGOBIN)/stringer:
|
||||||
go install golang.org/x/tools/cmd/stringer@latest
|
cd $(REPO_ROOT)/hack && go install golang.org/x/tools/cmd/stringer
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/goimports
|
||||||
$(MYGOBIN)/goimports:
|
$(MYGOBIN)/goimports:
|
||||||
go install golang.org/x/tools/cmd/goimports@latest
|
cd $(REPO_ROOT)/hack && go install golang.org/x/tools/cmd/goimports
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/mdtogo
|
||||||
$(MYGOBIN)/mdtogo:
|
$(MYGOBIN)/mdtogo:
|
||||||
go install sigs.k8s.io/kustomize/cmd/mdtogo@latest
|
cd $(REPO_ROOT)/hack && go install sigs.k8s.io/kustomize/cmd/mdtogo
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/addlicense
|
||||||
$(MYGOBIN)/addlicense:
|
$(MYGOBIN)/addlicense:
|
||||||
go install github.com/google/addlicense@latest
|
cd $(REPO_ROOT)/hack && go install github.com/google/addlicense
|
||||||
|
|
||||||
$(MYGOBIN)/statik:
|
|
||||||
go install github.com/rakyll/statik@latest
|
|
||||||
|
|
||||||
$(MYGOBIN)/goreleaser:
|
|
||||||
go install github.com/goreleaser/goreleaser@v0.179.0 # https://github.com/kubernetes-sigs/kustomize/issues/4542
|
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/kind
|
||||||
$(MYGOBIN)/kind:
|
$(MYGOBIN)/kind:
|
||||||
( \
|
cd $(REPO_ROOT)/hack && go install sigs.k8s.io/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.
|
.PHONY: $(MYGOBIN)/controller-gen
|
||||||
|
$(MYGOBIN)/controller-gen:
|
||||||
|
cd $(REPO_ROOT)/hack && go install sigs.k8s.io/controller-tools/cmd/controller-gen
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/embedmd
|
||||||
|
$(MYGOBIN)/embedmd:
|
||||||
|
cd $(REPO_ROOT)/hack && go install github.com/campoy/embedmd
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/go-bindata
|
||||||
|
$(MYGOBIN)/go-bindata:
|
||||||
|
cd $(REPO_ROOT)/hack && go install github.com/go-bindata/go-bindata/v3/go-bindata
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/go-apidiff
|
||||||
|
$(MYGOBIN)/go-apidiff:
|
||||||
|
cd $(REPO_ROOT)/hack && go install github.com/joelanford/go-apidiff
|
||||||
|
|
||||||
|
.PHONY: $(MYGOBIN)/gh
|
||||||
$(MYGOBIN)/gh:
|
$(MYGOBIN)/gh:
|
||||||
( \
|
cd $(REPO_ROOT)/hack && go install github.com/cli/cli/cmd/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.
|
.PHONY: $(MYGOBIN)/kubeval
|
||||||
# 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:
|
$(MYGOBIN)/kubeval:
|
||||||
( \
|
cd $(REPO_ROOT)/hack && go install github.com/instrumenta/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
|
# Helm V3 differs from helm V2; downloading it to provide coverage for the
|
||||||
# chart inflator plugin under helm v3.
|
# chart inflator plugin under helm v3.
|
||||||
|
.PHONY: $(MYGOBIN)/helmV3
|
||||||
$(MYGOBIN)/helmV3:
|
$(MYGOBIN)/helmV3:
|
||||||
( \
|
( \
|
||||||
set -e; \
|
set -e; \
|
||||||
d=$(shell mktemp -d); cd $$d; \
|
d=$(shell mktemp -d); cd $$d; \
|
||||||
tgzFile=helm-v3.6.3-$(GOOS)-$(GOARCH).tar.gz; \
|
tgzFile=helm-v3.10.2-$(GOOS)-$(GOARCH).tar.gz; \
|
||||||
wget https://get.helm.sh/$$tgzFile; \
|
wget https://get.helm.sh/$$tgzFile; \
|
||||||
tar -xvzf $$tgzFile; \
|
tar -xvzf $$tgzFile; \
|
||||||
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \
|
mv $(GOOS)-$(GOARCH)/helm $(MYGOBIN)/helmV3; \
|
||||||
|
|||||||
1
OWNERS
1
OWNERS
@@ -1,6 +1,5 @@
|
|||||||
# See https://github.com/kubernetes/community/blob/master/community-membership.md
|
# See https://github.com/kubernetes/community/blob/master/community-membership.md
|
||||||
approvers:
|
approvers:
|
||||||
- kustomize-approvers
|
- kustomize-approvers
|
||||||
|
|
||||||
reviewers:
|
reviewers:
|
||||||
- kustomize-reviewers
|
- kustomize-reviewers
|
||||||
|
|||||||
@@ -3,27 +3,35 @@
|
|||||||
aliases:
|
aliases:
|
||||||
kustomize-owners:
|
kustomize-owners:
|
||||||
- knverey
|
- knverey
|
||||||
- natasha41575
|
- koba1t
|
||||||
kustomize-approvers:
|
kustomize-approvers:
|
||||||
- knverey
|
- knverey
|
||||||
- natasha41575
|
- koba1t
|
||||||
|
- varshaprasad96
|
||||||
kustomize-reviewers:
|
kustomize-reviewers:
|
||||||
- knverey
|
- knverey
|
||||||
- natasha41575
|
- koba1t
|
||||||
- yuwenma
|
- varshaprasad96
|
||||||
|
- ncapps
|
||||||
|
- sarab97
|
||||||
|
|
||||||
kyaml-approvers:
|
docs-approvers:
|
||||||
- mengqiy
|
- ncapps
|
||||||
- mortent
|
docs-reviewers:
|
||||||
- phanimarupaka
|
- ncapps
|
||||||
kyaml-reviewers:
|
|
||||||
- mengqiy
|
|
||||||
- mortent
|
|
||||||
- phanimarupaka
|
|
||||||
|
|
||||||
emeritus-approvers:
|
commands-approvers: []
|
||||||
- liujingfang1
|
commands-reviewers: []
|
||||||
- Shell32-Natsu
|
|
||||||
- justinsb
|
# emeritus:
|
||||||
- monopole
|
# - liujingfang1
|
||||||
- pwittrock
|
# - Shell32-Natsu
|
||||||
|
# - justinsb
|
||||||
|
# - monopole
|
||||||
|
# - pwittrock
|
||||||
|
# - mengqiy
|
||||||
|
# - mortent
|
||||||
|
# - phanimarupaka
|
||||||
|
# - natasha41575
|
||||||
|
# - annasong20
|
||||||
|
# - yuwenma
|
||||||
|
|||||||
92
README.md
92
README.md
@@ -20,6 +20,14 @@ This tool is sponsored by [sig-cli] ([KEP]).
|
|||||||
|
|
||||||
## kubectl integration
|
## kubectl integration
|
||||||
|
|
||||||
|
To find the kustomize version embedded in recent versions of kubectl, run `kubectl version`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
> kubectl version --client
|
||||||
|
Client Version: v1.31.0
|
||||||
|
Kustomize Version: v5.4.2
|
||||||
|
```
|
||||||
|
|
||||||
The kustomize build flow at [v2.0.3] was added
|
The kustomize build flow at [v2.0.3] was added
|
||||||
to [kubectl v1.14][kubectl announcement]. The kustomize
|
to [kubectl v1.14][kubectl announcement]. The kustomize
|
||||||
flow in kubectl remained frozen at v2.0.3 until kubectl v1.21,
|
flow in kubectl remained frozen at v2.0.3 until kubectl v1.21,
|
||||||
@@ -28,11 +36,16 @@ be updated on a regular basis going forward, and such updates
|
|||||||
will be reflected in the Kubernetes release notes.
|
will be reflected in the Kubernetes release notes.
|
||||||
|
|
||||||
| Kubectl version | Kustomize version |
|
| Kubectl version | Kustomize version |
|
||||||
| --- | --- |
|
| --------------- | ----------------- |
|
||||||
| < v1.14 | n/a |
|
| < v1.14 | n/a |
|
||||||
| v1.14-v1.20 | v2.0.3 |
|
| v1.14-v1.20 | v2.0.3 |
|
||||||
| v1.21 | v4.0.5 |
|
| v1.21 | v4.0.5 |
|
||||||
| v1.22 | v4.2.0 |
|
| v1.22 | v4.2.0 |
|
||||||
|
| v1.23 | v4.4.1 |
|
||||||
|
| v1.24 | v4.5.4 |
|
||||||
|
| v1.25 | v4.5.7 |
|
||||||
|
| v1.26 | v4.5.7 |
|
||||||
|
| v1.27 | v5.0.1 |
|
||||||
|
|
||||||
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
|
[v2.0.3]: https://github.com/kubernetes-sigs/kustomize/releases/tag/v2.0.3
|
||||||
[#2506]: https://github.com/kubernetes-sigs/kustomize/issues/2506
|
[#2506]: https://github.com/kubernetes-sigs/kustomize/issues/2506
|
||||||
@@ -55,7 +68,37 @@ This file should declare those resources, and any
|
|||||||
customization to apply to them, e.g. _add a common
|
customization to apply to them, e.g. _add a common
|
||||||
label_.
|
label_.
|
||||||
|
|
||||||
![base image][imageBase]
|
```
|
||||||
|
|
||||||
|
base: kustomization + resources
|
||||||
|
|
||||||
|
kustomization.yaml deployment.yaml service.yaml
|
||||||
|
+---------------------------------------------+ +-------------------------------------------------------+ +-----------------------------------+
|
||||||
|
| apiVersion: kustomize.config.k8s.io/v1beta1 | | apiVersion: apps/v1 | | apiVersion: v1 |
|
||||||
|
| kind: Kustomization | | kind: Deployment | | kind: Service |
|
||||||
|
| labels: | | metadata: | | metadata: |
|
||||||
|
| - includeSelectors: true | | name: myapp | | name: myapp |
|
||||||
|
| pairs: | | spec: | | spec: |
|
||||||
|
| app: myapp | | selector: | | selector: |
|
||||||
|
| resources: | | matchLabels: | | app: myapp |
|
||||||
|
| - deployment.yaml | | app: myapp | | ports: |
|
||||||
|
| - service.yaml | | template: | | - port: 6060 |
|
||||||
|
| configMapGenerator: | | metadata: | | targetPort: 6060 |
|
||||||
|
| - name: myapp-map | | labels: | +-----------------------------------+
|
||||||
|
| literals: | | app: myapp |
|
||||||
|
| - KEY=value | | spec: |
|
||||||
|
+---------------------------------------------+ | containers: |
|
||||||
|
| - name: myapp |
|
||||||
|
| image: myapp |
|
||||||
|
| resources: |
|
||||||
|
| limits: |
|
||||||
|
| memory: "128Mi" |
|
||||||
|
| cpu: "500m" |
|
||||||
|
| ports: |
|
||||||
|
| - containerPort: 6060 |
|
||||||
|
+-------------------------------------------------------+
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
File structure:
|
File structure:
|
||||||
|
|
||||||
@@ -91,20 +134,41 @@ Manage traditional [variants] of a configuration - like
|
|||||||
_development_, _staging_ and _production_ - using
|
_development_, _staging_ and _production_ - using
|
||||||
[overlays] that modify a common [base].
|
[overlays] that modify a common [base].
|
||||||
|
|
||||||
![overlay image][imageOverlay]
|
```
|
||||||
|
|
||||||
|
overlay: kustomization + patches
|
||||||
|
|
||||||
|
kustomization.yaml replica_count.yaml cpu_count.yaml
|
||||||
|
+-----------------------------------------------+ +-------------------------------+ +------------------------------------------+
|
||||||
|
| apiVersion: kustomize.config.k8s.io/v1beta1 | | apiVersion: apps/v1 | | apiVersion: apps/v1 |
|
||||||
|
| kind: Kustomization | | kind: Deployment | | kind: Deployment |
|
||||||
|
| labels: | | metadata: | | metadata: |
|
||||||
|
| - includeSelectors: true | | name: myapp | | name: myapp |
|
||||||
|
| pairs: | | spec: | | spec: |
|
||||||
|
| variant: prod | | replicas: 80 | | template: |
|
||||||
|
| resources: | +-------------------------------+ | spec: |
|
||||||
|
| - ../../base | | containers: |
|
||||||
|
| patches: | | - name: myapp |
|
||||||
|
| - path: replica_count.yaml | | resources: |
|
||||||
|
| - path: cpu_count.yaml | | limits: |
|
||||||
|
+-----------------------------------------------+ | memory: "128Mi" |
|
||||||
|
| cpu: "7000m" |
|
||||||
|
+------------------------------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
File structure:
|
File structure:
|
||||||
> ```
|
> ```
|
||||||
> ~/someApp
|
> ~/someApp
|
||||||
> ├── base
|
> ├── base
|
||||||
> │ ├── deployment.yaml
|
> │ ├── deployment.yaml
|
||||||
> │ ├── kustomization.yaml
|
> │ ├── kustomization.yaml
|
||||||
> │ └── service.yaml
|
> │ └── service.yaml
|
||||||
> └── overlays
|
> └── overlays
|
||||||
> ├── development
|
> ├── development
|
||||||
> │ ├── cpu_count.yaml
|
> │ ├── cpu_count.yaml
|
||||||
> │ ├── kustomization.yaml
|
> │ ├── kustomization.yaml
|
||||||
> │ └── replica_count.yaml
|
> │ └── replica_count.yaml
|
||||||
> └── production
|
> └── production
|
||||||
> ├── cpu_count.yaml
|
> ├── cpu_count.yaml
|
||||||
> ├── kustomization.yaml
|
> ├── kustomization.yaml
|
||||||
@@ -158,8 +222,6 @@ is governed by the [Kubernetes Code of Conduct].
|
|||||||
[applied]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#apply
|
[applied]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#apply
|
||||||
[base]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#base
|
[base]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#base
|
||||||
[declarative configuration]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#declarative-application-management
|
[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 announcement]: https://kubernetes.io/blog/2019/03/25/kubernetes-1-14-release-announcement
|
||||||
[kubernetes documentation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
|
[kubernetes documentation]: https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/
|
||||||
[kubernetes style]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#kubernetes-style-object
|
[kubernetes style]: https://kubectl.docs.kubernetes.io/references/kustomize/glossary/#kubernetes-style-object
|
||||||
|
|||||||
241
ROADMAP.md
241
ROADMAP.md
@@ -1,112 +1,185 @@
|
|||||||
# Kustomize roadmap 2022
|
# Kustomize roadmap 2023-2024
|
||||||
|
|
||||||
Presented at the [January 26, 2022, SIG-CLI meeting](https://youtu.be/l2plzJ9MRlk?t=1321)
|
This document describes the items that we hope to make progress on over the next
|
||||||
|
1 year (H2 2023 and H1 2024). Take this roadmap as more of what we hope to achieve
|
||||||
|
rather than what we promise to achieve, as some items in this doc are highly dependent
|
||||||
|
on the success that we have on-ramping new contributors to the project, and other
|
||||||
|
items will depend on external contributions, which can vary.
|
||||||
|
|
||||||
kustomize maintainers: @knverey, @natasha41575
|
If you are interested in contributing to a particular area, you can look through
|
||||||
|
the project board for that area and assign yourself to one of the issues. It is
|
||||||
|
recommended to start with smaller issues to ramp up on the project before starting
|
||||||
|
to tackle larger issues.
|
||||||
|
|
||||||
[Objective: Improve contributor community](#objective-improve-contributor-community)
|
Project boards:
|
||||||
|
https://github.com/orgs/kubernetes-sigs/projects/50
|
||||||
|
https://github.com/orgs/kubernetes-sigs/projects/51
|
||||||
|
https://github.com/orgs/kubernetes-sigs/projects/52
|
||||||
|
https://github.com/orgs/kubernetes-sigs/projects/53
|
||||||
|
https://github.com/orgs/kubernetes-sigs/projects/54
|
||||||
|
|
||||||
[Objective: Improve end-user experience](#objective-improve-end-user-experience)
|
## Kustomize contributors (at time of writing):
|
||||||
|
|
||||||
[Objective: Improve extension experience](#objective-improve-extension-experience)
|
kustomize owner: @natasha41575
|
||||||
|
|
||||||
## Objective: Improve contributor community
|
kustomize maintainers: @annasong20, @koba1t
|
||||||
|
|
||||||
**_WHO: End user who also contributes source code._**
|
kustomize contributors: @varshaprasad96
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
|
# H2 2023
|
||||||
|
|
||||||
## Objective: Improve end-user experience
|
## Goal: Create kustomize leadership and contributor playbooks
|
||||||
|
|
||||||
**_WHO: End user that wants kustomize build artifacts (binaries, containers)._**
|
Contributors: natasha41575, annasong20
|
||||||
|
|
||||||
Top priorities:
|
Priority: High
|
||||||
|
|
||||||
- Bug fixes:
|
Effort: Medium
|
||||||
- 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:
|
In the past, when the leads stopped contributing (for various reasons, not covered here)
|
||||||
|
in various kubernetes projects, it left a wide hole that few could easily fill,
|
||||||
|
leaving the remaining leads in a bad position and the project understaffed. We should assume
|
||||||
|
that we will need to onboard new maintainers in the future, and should have playbooks
|
||||||
|
for doing so. As we grow the contributor base in kustomize, we will build these playbooks for
|
||||||
|
those who are contributing and those who are looking to grow into kustomize leaders.
|
||||||
|
To ensure the long term health and stability of the project, we should have:
|
||||||
|
|
||||||
- kustomize cli v5 ([PROJECT](https://github.com/kubernetes-sigs/kustomize/projects/14))
|
- On-boarding guides for new contributors
|
||||||
- [Drop the --reorder flag](https://github.com/kubernetes-sigs/kustomize/issues/3947)
|
- Clear guidelines for how to climb the kustomize ladder from contributor to approver to owner
|
||||||
- [Graduate cfg read-only commands out of alpha](https://github.com/kubernetes-sigs/kustomize/issues/4090).
|
- A plan (maybe a schedule) for future kustomize cohorts
|
||||||
- [Drop the –enable-managedby-label](https://github.com/kubernetes-sigs/kustomize/issues/4047)
|
- A succession plan, in case the current kustomize leads ever decide to step down
|
||||||
- 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:
|
## Goal: Onboard 2-5 new contributors to kustomize
|
||||||
|
|
||||||
- [Overinclusion of root directory error in error messages](https://github.com/kubernetes-sigs/kustomize/issues/4348)
|
Contributors: natasha41575, annasong20, koba1t
|
||||||
- [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)
|
|
||||||
|
|
||||||
|
Priority: High
|
||||||
|
|
||||||
## Objective: Improve extension experience
|
Effort: High
|
||||||
|
|
||||||
**_WHO: Plugin developers: end users who extend kustomize, but don’t think about internals._**
|
In order to make progress on kustomize goals in the future, we need to increase the
|
||||||
|
level of staffing on kustomize. We should leverage community contributions to keep kustomize
|
||||||
|
healthy and making progress.
|
||||||
|
|
||||||
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) .
|
The primary means in which we will try to find new kustomize contributors is through the new kustomize
|
||||||
|
maintainer training cohort. We will lead a group of ~20 kubernetes community members through a 3-6 month
|
||||||
|
training program, involving talk sessions, bug scrubs, issue triage, PR reviews, and coding projects for
|
||||||
|
each member.
|
||||||
|
|
||||||
Top priorities:
|
See [our call for help](https://groups.google.com/a/kubernetes.io/g/dev/c/M5OphEVsv5o/m/zc6G4H15AAAJ) for more
|
||||||
|
specific details about the program.
|
||||||
|
|
||||||
- Fix core usability issues with KRM Function extensions:
|
The effort from existing kustomize maintainers here will be to:
|
||||||
- [Better errors for function config failures](https://github.com/kubernetes-sigs/kustomize/issues/4398)
|
- Organize the cohort, so that each cohort member feels productive and understands what they should work on
|
||||||
- [Container KRM Mounts are not mounting via function parameters](https://github.com/kubernetes-sigs/kustomize/issues/4290)
|
- Align motivation of the cohort members with the work that we assign to them.
|
||||||
- [Resolution of local file references in extensions transformer configuration](https://github.com/kubernetes-sigs/kustomize/issues/4154)
|
- Review PRs from cohort members in a timely manner.
|
||||||
- [Do not silently ignore plugins when config has typo](https://github.com/kubernetes-sigs/kustomize/issues/4399)
|
- Be the point(s) of contact for questions/escalations
|
||||||
- [KRM Exec Function can't locate executable when referencing a base](https://github.com/kubernetes-sigs/kustomize/issues/4347)
|
- Lead weekly stand-ups and monthly bug scrubs
|
||||||
- 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:
|
At the end of all this, if we have a small team of contributors to kustomize, who understand its founding
|
||||||
|
philosophy and intentions, we should be able to keep the project up to date.
|
||||||
|
|
||||||
- [Remove Starlark support](https://github.com/kubernetes-sigs/kustomize/issues/4349)
|
## Goal: Improve kustomize extensibility through KRM functions and CRD support
|
||||||
- [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)
|
|
||||||
|
|
||||||
|
Contributors: koba1t, varshaprasad96, external contributors
|
||||||
|
|
||||||
Also very valuable to the project:
|
Priority: High
|
||||||
|
|
||||||
- [Improve docs for kyaml libraries](https://github.com/kubernetes-sigs/kustomize/issues/3950), especially by adding examples.
|
Effort: High
|
||||||
- [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)
|
Project board: https://github.com/orgs/kubernetes-sigs/projects/53/views/1
|
||||||
|
|
||||||
|
For a long time, we have supported KRM functions as the proper way to implement custom generators and transformers.
|
||||||
|
However, due to limited staffing, we have been unable to drive this feature out of alpha in kustomize. The two
|
||||||
|
main features which we hope to make progress on are Composition and Catalog, two long-standing proposals for which
|
||||||
|
numerous users have been waiting for a long time. There are several open issues
|
||||||
|
regarding KRM functions where our long-term answer has been these two features, but users have been hearing about them
|
||||||
|
for over a year without seeing any progress. If we can implement them, they will vastly improve usability and security
|
||||||
|
of KRM functions.
|
||||||
|
|
||||||
|
One item that falls under this category that does not currently have a contributor is improving CRD support.
|
||||||
|
Currently, it is difficult to use CRDs properly, as there are three different fields (configurations, openapi, and crds)
|
||||||
|
where users have to input their CRD configuration. We need to consolidate these fields into one easy to use feature to
|
||||||
|
support CRDs. If you are interested in putting together a design proposal for how to tackle this task, please reach
|
||||||
|
out to the kustomize maintainers.
|
||||||
|
|
||||||
|
# H1 2024
|
||||||
|
|
||||||
|
## Goal: Improve the kustomize documentation
|
||||||
|
|
||||||
|
Contributors: annasong20, external contributions
|
||||||
|
|
||||||
|
Priority: High
|
||||||
|
|
||||||
|
Effort: High
|
||||||
|
|
||||||
|
Project board: https://github.com/orgs/kubernetes-sigs/projects/50
|
||||||
|
|
||||||
|
The kustomize documentation is currently fragmented, out of date, and lacks examples to fully understand its value.
|
||||||
|
We have had a "docs project" for a long time; we need to prioritize implementing it so that the documentation is in
|
||||||
|
one place, easy to find, and helps new users get started more easily. Some outcomes from this project should be:
|
||||||
|
|
||||||
|
- A single, unified website hosted on kustomize.io
|
||||||
|
- Updated information architecture, and a plan to keep it up to date
|
||||||
|
- End to end examples of using kustomize, including complex use cases
|
||||||
|
|
||||||
|
## Goal: Fix core usability bugs in kustomize
|
||||||
|
|
||||||
|
Contributors: external contributions
|
||||||
|
|
||||||
|
Priority: High
|
||||||
|
|
||||||
|
Effort: High
|
||||||
|
|
||||||
|
Project board: https://github.com/orgs/kubernetes-sigs/projects/51
|
||||||
|
|
||||||
|
There are several core usability issues that block some users from adopting kustomize features or in
|
||||||
|
some base block users from using kustomize entirely. These issues range from small bugs with workable but
|
||||||
|
inconvenient workarounds, to enormous feature gaps.
|
||||||
|
|
||||||
|
As part of this goal, we should work toward reducing the number of such issues that we have, making
|
||||||
|
kustomize work more smoothly and predictably, and be usable for a larger range of users.
|
||||||
|
|
||||||
|
There are a lot of important issues in this project, but the biggest and highest priority one is that
|
||||||
|
kustomize doesn't currently support metadata.GenerateName. Unfortunately, we don't currently have anyone
|
||||||
|
actively working on this issue, we would need an external contributor to reach out to the kustomize
|
||||||
|
maintainers to pick it up.
|
||||||
|
|
||||||
|
## Goal: Improve kustomize CI, release, & security patch processes
|
||||||
|
|
||||||
|
Contributors: external contributions
|
||||||
|
|
||||||
|
Priority: Medium
|
||||||
|
|
||||||
|
Effort: High
|
||||||
|
|
||||||
|
Project board: https://github.com/orgs/kubernetes-sigs/projects/54
|
||||||
|
|
||||||
|
The kustomize release process is currently done on-demand and is strictly linear. This means that if we find a CVE,
|
||||||
|
we are forced to release the next version of kustomize ASAP, and we are required to release every PR that has merged
|
||||||
|
since the last release. This can put us in a sticky situation if we have a breaking change that we are
|
||||||
|
not ready to release yet, but we need a patch quickly.
|
||||||
|
|
||||||
|
We should try to improve the kustomize release process so that we can release frequently, reliably, and with some
|
||||||
|
flexibility. The outcome of this effort should be:
|
||||||
|
|
||||||
|
- kustomize is released on a regular cadence (biweekly or monthly)
|
||||||
|
- kustomize is able to separate patch and feature releases, so that we can fix CVEs without needing to release
|
||||||
|
everything that we have in flight
|
||||||
|
- We can detect and fix CVEs early
|
||||||
|
|
||||||
|
## Goal: Take long-standing alpha commands out of alpha
|
||||||
|
|
||||||
|
Contributors: external contributions
|
||||||
|
|
||||||
|
Priority: Medium
|
||||||
|
|
||||||
|
Effort: Medium
|
||||||
|
|
||||||
|
Project board: https://github.com/orgs/kubernetes-sigs/projects/52
|
||||||
|
|
||||||
|
There are several commands in kustomize that have been alpha for a long time, including the cfg command group and
|
||||||
|
localize. Moving them forward can indicate good health of a project and these commands are useful to many users.
|
||||||
|
Some of these projects can be good starter issues for new contributors to have an easier onramp while others will
|
||||||
|
require more effort and thought.
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
#
|
#
|
||||||
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||||
# INSTRUCTIONS AT https://kubernetes.io/security/
|
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||||
|
eddiezane
|
||||||
monopole
|
KnVerey
|
||||||
Liujingfang1
|
natasha41575
|
||||||
pwittrock
|
soltysh
|
||||||
|
|||||||
20
api/Makefile
20
api/Makefile
@@ -4,11 +4,25 @@
|
|||||||
include ../Makefile-modules.mk
|
include ../Makefile-modules.mk
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test -v -timeout 45m -cover ./... -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222"
|
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)"
|
||||||
cd krusty/openapitests; OPENAPI_TEST=true go test -v -timeout 45m -p 1 -cover ./...
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build -ldflags "-X sigs.k8s.io/kustomize/api/provenance.version=v444.333.222" ./...
|
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
|
generate: $(MYGOBIN)/k8scopy $(MYGOBIN)/stringer
|
||||||
go generate ./...
|
go generate ./...
|
||||||
|
|
||||||
|
lint: lint-api-static
|
||||||
|
|
||||||
|
## lint-api-static runs the linter on the API module
|
||||||
|
## with build-tag kustomize_disable_go_plugin_support
|
||||||
|
## this aims to catch any issues with the API module
|
||||||
|
## that would prevent the API module from being used
|
||||||
|
## when the go plugin support is disabled.
|
||||||
|
lint-api-static:
|
||||||
|
$(MYGOBIN)/golangci-lint cache clean # Workaround for https://github.com/golangci/golangci-lint/issues/3228
|
||||||
|
$(MYGOBIN)/golangci-lint \
|
||||||
|
-c $$KUSTOMIZE_ROOT/.golangci.yml \
|
||||||
|
--build-tags kustomize_disable_go_plugin_support \
|
||||||
|
--path-prefix api \
|
||||||
|
run ./...
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ type (
|
|||||||
IAMPolicyGeneratorPlugin = internal.IAMPolicyGeneratorPlugin
|
IAMPolicyGeneratorPlugin = internal.IAMPolicyGeneratorPlugin
|
||||||
ImageTagTransformerPlugin = internal.ImageTagTransformerPlugin
|
ImageTagTransformerPlugin = internal.ImageTagTransformerPlugin
|
||||||
LabelTransformerPlugin = internal.LabelTransformerPlugin
|
LabelTransformerPlugin = internal.LabelTransformerPlugin
|
||||||
LegacyOrderTransformerPlugin = internal.LegacyOrderTransformerPlugin
|
|
||||||
NamespaceTransformerPlugin = internal.NamespaceTransformerPlugin
|
NamespaceTransformerPlugin = internal.NamespaceTransformerPlugin
|
||||||
PatchJson6902TransformerPlugin = internal.PatchJson6902TransformerPlugin
|
PatchJson6902TransformerPlugin = internal.PatchJson6902TransformerPlugin
|
||||||
PatchStrategicMergeTransformerPlugin = internal.PatchStrategicMergeTransformerPlugin
|
PatchStrategicMergeTransformerPlugin = internal.PatchStrategicMergeTransformerPlugin
|
||||||
@@ -37,7 +36,6 @@ var (
|
|||||||
NewIAMPolicyGeneratorPlugin = internal.NewIAMPolicyGeneratorPlugin
|
NewIAMPolicyGeneratorPlugin = internal.NewIAMPolicyGeneratorPlugin
|
||||||
NewImageTagTransformerPlugin = internal.NewImageTagTransformerPlugin
|
NewImageTagTransformerPlugin = internal.NewImageTagTransformerPlugin
|
||||||
NewLabelTransformerPlugin = internal.NewLabelTransformerPlugin
|
NewLabelTransformerPlugin = internal.NewLabelTransformerPlugin
|
||||||
NewLegacyOrderTransformerPlugin = internal.NewLegacyOrderTransformerPlugin
|
|
||||||
NewNamespaceTransformerPlugin = internal.NewNamespaceTransformerPlugin
|
NewNamespaceTransformerPlugin = internal.NewNamespaceTransformerPlugin
|
||||||
NewPatchJson6902TransformerPlugin = internal.NewPatchJson6902TransformerPlugin
|
NewPatchJson6902TransformerPlugin = internal.NewPatchJson6902TransformerPlugin
|
||||||
NewPatchStrategicMergeTransformerPlugin = internal.NewPatchStrategicMergeTransformerPlugin
|
NewPatchStrategicMergeTransformerPlugin = internal.NewPatchStrategicMergeTransformerPlugin
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
@@ -621,7 +622,7 @@ kind: Pod
|
|||||||
|
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
err := yaml.Unmarshal([]byte(tc.fieldSpec), &filter.FieldSpec)
|
err := yaml.Unmarshal([]byte(tc.fieldSpec), &filter.FieldSpec)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rw := &kio.ByteReadWriter{
|
rw := &kio.ByteReadWriter{
|
||||||
Reader: bytes.NewBufferString(tc.input),
|
Reader: bytes.NewBufferString(tc.input),
|
||||||
Writer: &bytes.Buffer{},
|
Writer: &bytes.Buffer{},
|
||||||
@@ -635,7 +636,7 @@ kind: Pod
|
|||||||
Outputs: []kio.Writer{rw},
|
Outputs: []kio.Writer{rw},
|
||||||
}.Execute()
|
}.Execute()
|
||||||
|
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, tc.expected, fieldPaths)
|
assert.Equal(t, tc.expected, fieldPaths)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,5 +102,7 @@ func TestTrackableSetter_SetEntryIfEmpty_BadInputNodeKind(t *testing.T) {
|
|||||||
fn := filtersutil.TrackableSetter{}.SetEntryIfEmpty("foo", "false", yaml.NodeTagBool)
|
fn := filtersutil.TrackableSetter{}.SetEntryIfEmpty("foo", "false", yaml.NodeTagBool)
|
||||||
rn := yaml.NewListRNode("nope")
|
rn := yaml.NewListRNode("nope")
|
||||||
rn.AppendToFieldPath("dummy", "path")
|
rn.AppendToFieldPath("dummy", "path")
|
||||||
assert.EqualError(t, fn(rn), "wrong Node Kind for dummy.path expected: MappingNode was SequenceNode: value: {- nope}")
|
assert.EqualError(t, fn(rn), `wrong node kind: expected MappingNode but got SequenceNode: node contents:
|
||||||
|
- nope
|
||||||
|
`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -879,6 +879,84 @@ spec:
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"update image volume in pod template": {
|
||||||
|
input: `
|
||||||
|
group: apps
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: imagevolume
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
volumes:
|
||||||
|
- name: volume
|
||||||
|
image:
|
||||||
|
reference: nginx
|
||||||
|
`,
|
||||||
|
expectedOutput: `
|
||||||
|
group: apps
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: imagevolume
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
volumes:
|
||||||
|
- name: volume
|
||||||
|
image:
|
||||||
|
reference: apache@12345
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
ImageTag: types.Image{
|
||||||
|
Name: "nginx",
|
||||||
|
NewName: "apache",
|
||||||
|
Digest: "12345",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fsSlice: []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Path: "spec/template/spec/volumes[]/image/reference",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"update image volume in pod spec": {
|
||||||
|
input: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: imagevolume
|
||||||
|
spec:
|
||||||
|
volumes:
|
||||||
|
- name: volume
|
||||||
|
image:
|
||||||
|
reference: nginx
|
||||||
|
`,
|
||||||
|
expectedOutput: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
name: imagevolume
|
||||||
|
spec:
|
||||||
|
volumes:
|
||||||
|
- name: volume
|
||||||
|
image:
|
||||||
|
reference: apache@12345
|
||||||
|
`,
|
||||||
|
filter: Filter{
|
||||||
|
ImageTag: types.Image{
|
||||||
|
Name: "nginx",
|
||||||
|
NewName: "apache",
|
||||||
|
Digest: "12345",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fsSlice: []types.FieldSpec{
|
||||||
|
{
|
||||||
|
Path: "spec/volumes[]/image/reference",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for tn, tc := range testCases {
|
for tn, tc := range testCases {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package imagetag
|
|||||||
import (
|
import (
|
||||||
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
"sigs.k8s.io/kustomize/api/filters/filtersutil"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/image"
|
"sigs.k8s.io/kustomize/api/internal/image"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
"sigs.k8s.io/kustomize/api/filters/fieldspec"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
@@ -64,7 +64,7 @@ func (f Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
|||||||
FieldSpec: f.NameFieldToUpdate,
|
FieldSpec: f.NameFieldToUpdate,
|
||||||
SetValue: f.set,
|
SetValue: f.set,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, errors.Wrapf(
|
return nil, errors.WrapPrefixf(
|
||||||
err, "updating name reference in '%s' field of '%s'",
|
err, "updating name reference in '%s' field of '%s'",
|
||||||
f.NameFieldToUpdate.Path, f.Referrer.CurId().String())
|
f.NameFieldToUpdate.Path, f.Referrer.CurId().String())
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ func (f Filter) setMapping(node *yaml.RNode) error {
|
|||||||
}
|
}
|
||||||
nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"})
|
nameNode, err := node.Pipe(yaml.FieldMatcher{Name: "name"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "trying to match 'name' field")
|
return errors.WrapPrefixf(err, "trying to match 'name' field")
|
||||||
}
|
}
|
||||||
if nameNode == nil {
|
if nameNode == nil {
|
||||||
// This is a _configuration_ error; the field path
|
// This is a _configuration_ error; the field path
|
||||||
@@ -153,7 +153,7 @@ func (f Filter) filterMapCandidatesByNamespace(
|
|||||||
node *yaml.RNode) ([]*resource.Resource, error) {
|
node *yaml.RNode) ([]*resource.Resource, error) {
|
||||||
namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"})
|
namespaceNode, err := node.Pipe(yaml.FieldMatcher{Name: "namespace"})
|
||||||
if err != nil {
|
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 {
|
if namespaceNode == nil {
|
||||||
return f.ReferralCandidates.Resources(), nil
|
return f.ReferralCandidates.Resources(), nil
|
||||||
@@ -205,16 +205,14 @@ func getRoleRefGvk(n *resource.Resource) (*resid.Gvk, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if apiGroup.IsNil() {
|
if apiGroup.IsNil() {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf("apiGroup cannot be found in roleRef %s", roleRef.MustString())
|
||||||
"apiGroup cannot be found in roleRef %s", roleRef.MustString())
|
|
||||||
}
|
}
|
||||||
kind, err := roleRef.Pipe(yaml.Lookup("kind"))
|
kind, err := roleRef.Pipe(yaml.Lookup("kind"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if kind.IsNil() {
|
if kind.IsNil() {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf("kind cannot be found in roleRef %s", roleRef.MustString())
|
||||||
"kind cannot be found in roleRef %s", roleRef.MustString())
|
|
||||||
}
|
}
|
||||||
return &resid.Gvk{
|
return &resid.Gvk{
|
||||||
Group: apiGroup.YNode().Value,
|
Group: apiGroup.YNode().Value,
|
||||||
@@ -284,9 +282,9 @@ func (f Filter) roleRefFilter() sieveFunc {
|
|||||||
return previousIdSelectedByGvk(roleRefGvk)
|
return previousIdSelectedByGvk(roleRefGvk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prefixSuffixEquals(other resource.ResCtx) sieveFunc {
|
func prefixSuffixEquals(other resource.ResCtx, allowEmpty bool) sieveFunc {
|
||||||
return func(r *resource.Resource) bool {
|
return func(r *resource.Resource) bool {
|
||||||
return r.PrefixesSuffixesEquals(other)
|
return r.PrefixesSuffixesEquals(other, allowEmpty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,7 +323,10 @@ func (f Filter) selectReferral(
|
|||||||
if len(candidates) == 1 {
|
if len(candidates) == 1 {
|
||||||
return candidates[0], nil
|
return candidates[0], nil
|
||||||
}
|
}
|
||||||
candidates = doSieve(candidates, prefixSuffixEquals(f.Referrer))
|
candidates = doSieve(candidates, prefixSuffixEquals(f.Referrer, true))
|
||||||
|
if len(candidates) > 1 {
|
||||||
|
candidates = doSieve(candidates, prefixSuffixEquals(f.Referrer, false))
|
||||||
|
}
|
||||||
if len(candidates) == 1 {
|
if len(candidates) == 1 {
|
||||||
return candidates[0], nil
|
return candidates[0], nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,9 +56,11 @@ func (ns Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|||||||
|
|
||||||
// Run runs the filter on a single node rather than a slice
|
// Run runs the filter on a single node rather than a slice
|
||||||
func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
||||||
// Special handling for metadata.namespace -- :(
|
// Special handling for metadata.namespace and metadata.name -- :(
|
||||||
// never let SetEntry handle metadata.namespace--it will incorrectly include cluster-scoped resources
|
// never let SetEntry handle metadata.namespace--it will incorrectly include cluster-scoped resources
|
||||||
ns.FsSlice = ns.removeMetaNamespaceFieldSpecs(ns.FsSlice)
|
// only update metadata.name if api version is expected one--so-as it leaves other resources of kind namespace alone
|
||||||
|
apiVersion := node.GetApiVersion()
|
||||||
|
ns.FsSlice = ns.removeUnneededMetaFieldSpecs(apiVersion, ns.FsSlice)
|
||||||
gvk := resid.GvkFromNode(node)
|
gvk := resid.GvkFromNode(node)
|
||||||
if err := ns.metaNamespaceHack(node, gvk); err != nil {
|
if err := ns.metaNamespaceHack(node, gvk); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -79,7 +81,11 @@ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) {
|
|||||||
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode
|
||||||
CreateTag: yaml.NodeTagString,
|
CreateTag: yaml.NodeTagString,
|
||||||
})
|
})
|
||||||
return node, 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")
|
||||||
|
}
|
||||||
|
return node, errors.WrapPrefixf(err, "namespace transformation failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// metaNamespaceHack is a hack for implementing the namespace transform
|
// metaNamespaceHack is a hack for implementing the namespace transform
|
||||||
@@ -174,8 +180,7 @@ func setNamespaceField(node *yaml.RNode, setter filtersutil.SetFn) error {
|
|||||||
func (ns Filter) removeRoleBindingSubjectFieldSpecs(fs types.FsSlice) types.FsSlice {
|
func (ns Filter) removeRoleBindingSubjectFieldSpecs(fs types.FsSlice) types.FsSlice {
|
||||||
var val types.FsSlice
|
var val types.FsSlice
|
||||||
for i := range fs {
|
for i := range fs {
|
||||||
if isRoleBinding(fs[i].Kind) &&
|
if isRoleBinding(fs[i].Kind) && fs[i].Path == subjectsNamespacePath {
|
||||||
(fs[i].Path == subjectsNamespacePath || fs[i].Path == subjectsField) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val = append(val, fs[i])
|
val = append(val, fs[i])
|
||||||
@@ -183,12 +188,15 @@ func (ns Filter) removeRoleBindingSubjectFieldSpecs(fs types.FsSlice) types.FsSl
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns Filter) removeMetaNamespaceFieldSpecs(fs types.FsSlice) types.FsSlice {
|
func (ns Filter) removeUnneededMetaFieldSpecs(apiVersion string, fs types.FsSlice) types.FsSlice {
|
||||||
var val types.FsSlice
|
var val types.FsSlice
|
||||||
for i := range fs {
|
for i := range fs {
|
||||||
if fs[i].Path == types.MetadataNamespacePath {
|
if fs[i].Path == types.MetadataNamespacePath {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if apiVersion != types.MetadataNamespaceApiVersion && fs[i].Path == types.MetadataNamePath {
|
||||||
|
continue
|
||||||
|
}
|
||||||
val = append(val, fs[i])
|
val = append(val, fs[i])
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package patchjson6902
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
jsonpatch "gopkg.in/evanphx/json-patch.v4"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio"
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
k8syaml "sigs.k8s.io/yaml"
|
k8syaml "sigs.k8s.io/yaml"
|
||||||
|
|||||||
@@ -732,6 +732,177 @@ spec:
|
|||||||
protocol: "TCP"
|
protocol: "TCP"
|
||||||
- containerPort: 8301
|
- containerPort: 8301
|
||||||
protocol: "UDP"
|
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
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Issue #4928
|
||||||
|
"support numeric keys": {
|
||||||
|
input: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "foobar"
|
||||||
|
`,
|
||||||
|
patch: yaml.MustParse(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "barfoo"
|
||||||
|
"9110": "foo-foo"
|
||||||
|
`),
|
||||||
|
expected: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "barfoo"
|
||||||
|
"9110": "foo-foo"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"honor different key style one": {
|
||||||
|
input: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
'6443': "foobar"
|
||||||
|
`,
|
||||||
|
patch: yaml.MustParse(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "barfoo"
|
||||||
|
9110: "foo-foo"
|
||||||
|
`),
|
||||||
|
expected: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
'6443': "barfoo"
|
||||||
|
9110: "foo-foo"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"honor different key style two": {
|
||||||
|
input: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "foobar"
|
||||||
|
`,
|
||||||
|
patch: yaml.MustParse(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "barfoo"
|
||||||
|
'9110': "foo-foo"
|
||||||
|
`),
|
||||||
|
expected: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "barfoo"
|
||||||
|
'9110': "foo-foo"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
|
||||||
|
"different key types": {
|
||||||
|
input: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "key-string-double-quoted"
|
||||||
|
`,
|
||||||
|
patch: yaml.MustParse(`
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
6443: "key-int"
|
||||||
|
`),
|
||||||
|
expected: `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: blabla
|
||||||
|
namespace: blabla-ns
|
||||||
|
data:
|
||||||
|
"6443": "key-int"
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
package replacement
|
package replacement
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/internal/utils"
|
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
kyaml_utils "sigs.k8s.io/kustomize/kyaml/utils"
|
kyaml_utils "sigs.k8s.io/kustomize/kyaml/utils"
|
||||||
"sigs.k8s.io/kustomize/kyaml/yaml"
|
"sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
)
|
)
|
||||||
@@ -23,7 +23,7 @@ type Filter struct {
|
|||||||
// Filter replaces values of targets with values from sources
|
// Filter replaces values of targets with values from sources
|
||||||
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
for i, r := range f.Replacements {
|
for i, r := range f.Replacements {
|
||||||
if r.Source == nil || r.Targets == nil {
|
if (r.SourceValue == nil && r.Source == nil) || r.Targets == nil {
|
||||||
return nil, fmt.Errorf("replacements must specify a source and at least one target")
|
return nil, fmt.Errorf("replacements must specify a source and at least one target")
|
||||||
}
|
}
|
||||||
value, err := getReplacement(nodes, &f.Replacements[i])
|
value, err := getReplacement(nodes, &f.Replacements[i])
|
||||||
@@ -39,6 +39,13 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, error) {
|
func getReplacement(nodes []*yaml.RNode, r *types.Replacement) (*yaml.RNode, error) {
|
||||||
|
if r.SourceValue != nil && r.Source != nil {
|
||||||
|
return nil, fmt.Errorf("value and resource selectors are mutually exclusive")
|
||||||
|
}
|
||||||
|
if r.SourceValue != nil {
|
||||||
|
return yaml.NewScalarRNode(*r.SourceValue), nil
|
||||||
|
}
|
||||||
|
|
||||||
source, err := selectSourceNode(nodes, r.Source)
|
source, err := selectSourceNode(nodes, r.Source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -105,11 +112,15 @@ func getRefinedValue(options *types.FieldOptions, rn *yaml.RNode) (*yaml.RNode,
|
|||||||
func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targetSelectors []*types.TargetSelector) ([]*yaml.RNode, error) {
|
func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targetSelectors []*types.TargetSelector) ([]*yaml.RNode, error) {
|
||||||
for _, selector := range targetSelectors {
|
for _, selector := range targetSelectors {
|
||||||
if selector.Select == nil {
|
if selector.Select == nil {
|
||||||
return nil, errors.New("target must specify resources to select")
|
return nil, errors.Errorf("target must specify resources to select")
|
||||||
}
|
}
|
||||||
if len(selector.FieldPaths) == 0 {
|
if len(selector.FieldPaths) == 0 {
|
||||||
selector.FieldPaths = []string{types.DefaultReplacementFieldPath}
|
selector.FieldPaths = []string{types.DefaultReplacementFieldPath}
|
||||||
}
|
}
|
||||||
|
tsr, err := types.NewTargetSelectorRegex(selector)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating target selector: %w", err)
|
||||||
|
}
|
||||||
for _, possibleTarget := range nodes {
|
for _, possibleTarget := range nodes {
|
||||||
ids, err := utils.MakeResIds(possibleTarget)
|
ids, err := utils.MakeResIds(possibleTarget)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -125,9 +136,13 @@ func applyReplacement(nodes []*yaml.RNode, value *yaml.RNode, targetSelectors []
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tsr.RejectsAny(ids) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// filter targets by matching resource IDs
|
// filter targets by matching resource IDs
|
||||||
for i, id := range ids {
|
for _, id := range ids {
|
||||||
if id.IsSelectedBy(selector.Select.ResId) && !rejectId(selector.Reject, &ids[i]) {
|
if tsr.Selects(id) {
|
||||||
err := copyValueToTarget(possibleTarget, value, selector)
|
err := copyValueToTarget(possibleTarget, value, selector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -168,52 +183,50 @@ func matchesAnnoAndLabelSelector(n *yaml.RNode, selector *types.Selector) (bool,
|
|||||||
return annoMatch && labelMatch, nil
|
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 {
|
func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.TargetSelector) error {
|
||||||
for _, fp := range selector.FieldPaths {
|
for _, fp := range selector.FieldPaths {
|
||||||
fieldPath := kyaml_utils.SmarterPathSplitter(fp, ".")
|
createKind := yaml.Kind(0) // do not create
|
||||||
create, err := shouldCreateField(selector.Options, fieldPath)
|
if selector.Options != nil && selector.Options.Create {
|
||||||
if err != nil {
|
createKind = value.YNode().Kind
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetFields []*yaml.RNode
|
// Check if this fieldPath contains structured data access
|
||||||
if create {
|
if err := setValueInStructuredData(target, value, fp, selector.Options); err == nil {
|
||||||
createdField, createErr := target.Pipe(yaml.LookupCreate(value.YNode().Kind, fieldPath...))
|
// Successfully handled as structured data
|
||||||
if createErr != nil {
|
continue
|
||||||
return fmt.Errorf("error creating replacement node: %w", createErr)
|
}
|
||||||
}
|
|
||||||
targetFields = append(targetFields, createdField)
|
// Fall back to normal path handling
|
||||||
} else {
|
targetFieldList, err := target.Pipe(&yaml.PathMatcher{
|
||||||
// may return multiple fields, always wrapped in a sequence node
|
Path: kyaml_utils.SmarterPathSplitter(fp, "."),
|
||||||
foundFieldSequence, lookupErr := target.Pipe(&yaml.PathMatcher{Path: fieldPath})
|
Create: createKind})
|
||||||
if lookupErr != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error finding field in replacement target: %w", lookupErr)
|
return errors.WrapPrefixf(err, "%s", fieldRetrievalError(fp, createKind != 0))
|
||||||
}
|
}
|
||||||
targetFields, err = foundFieldSequence.Elements()
|
targetFields, err := targetFieldList.Elements()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error fetching elements in replacement target: %w", err)
|
return errors.WrapPrefixf(err, "%s", fieldRetrievalError(fp, createKind != 0))
|
||||||
}
|
}
|
||||||
|
if len(targetFields) == 0 {
|
||||||
|
return errors.Errorf("%s", fieldRetrievalError(fp, createKind != 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range targetFields {
|
for _, t := range targetFields {
|
||||||
if err := setFieldValue(selector.Options, t, value); err != nil {
|
if err := setFieldValue(selector.Options, t, value); err != nil {
|
||||||
return err
|
return fmt.Errorf("%w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
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 {
|
func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value *yaml.RNode) error {
|
||||||
value = value.Copy()
|
value = value.Copy()
|
||||||
if options != nil && options.Delimiter != "" {
|
if options != nil && options.Delimiter != "" {
|
||||||
@@ -244,15 +257,149 @@ func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value *
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldCreateField(options *types.FieldOptions, fieldPath []string) (bool, error) {
|
// setValueInStructuredData handles setting values within structured data (JSON/YAML) in scalar fields
|
||||||
if options == nil || !options.Create {
|
func setValueInStructuredData(target *yaml.RNode, value *yaml.RNode, fieldPath string, options *types.FieldOptions) error {
|
||||||
return false, nil
|
pathParts := kyaml_utils.SmarterPathSplitter(fieldPath, ".")
|
||||||
|
if len(pathParts) < 2 {
|
||||||
|
return fmt.Errorf("not a structured data path")
|
||||||
}
|
}
|
||||||
// create option is not supported in a wildcard matching
|
|
||||||
for _, f := range fieldPath {
|
// Find the potential scalar field that might contain structured data
|
||||||
if f == "*" {
|
var scalarFieldPath []string
|
||||||
return false, fmt.Errorf("cannot support create option in a multi-value target")
|
var structuredDataPath []string
|
||||||
|
var foundScalar = false
|
||||||
|
|
||||||
|
// Try to find where the scalar field ends and structured data begins
|
||||||
|
for i := 1; i <= len(pathParts); i++ {
|
||||||
|
potentialScalarPath := pathParts[:i]
|
||||||
|
scalarField, err := target.Pipe(yaml.Lookup(potentialScalarPath...))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if scalarField != nil && scalarField.YNode().Kind == yaml.ScalarNode && i < len(pathParts) {
|
||||||
|
// Try to parse the scalar value as structured data
|
||||||
|
scalarValue := scalarField.YNode().Value
|
||||||
|
var parsedNode yaml.Node
|
||||||
|
if err := yaml.Unmarshal([]byte(scalarValue), &parsedNode); err == nil {
|
||||||
|
// Successfully parsed - this is structured data
|
||||||
|
scalarFieldPath = potentialScalarPath
|
||||||
|
structuredDataPath = pathParts[i:]
|
||||||
|
foundScalar = true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true, nil
|
|
||||||
|
if !foundScalar {
|
||||||
|
return fmt.Errorf("no structured data found in path")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the scalar field containing structured data
|
||||||
|
scalarField, err := target.Pipe(yaml.Lookup(scalarFieldPath...))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the structured data
|
||||||
|
scalarValue := scalarField.YNode().Value
|
||||||
|
var parsedNode yaml.Node
|
||||||
|
if err := yaml.Unmarshal([]byte(scalarValue), &parsedNode); err != nil {
|
||||||
|
return fmt.Errorf("%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
structuredData := yaml.NewRNode(&parsedNode)
|
||||||
|
|
||||||
|
createKind := yaml.Kind(0) // do not create
|
||||||
|
if options != nil && options.Create {
|
||||||
|
createKind = value.YNode().Kind
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate to the target location within the structured data
|
||||||
|
targetInStructured, err := structuredData.Pipe(&yaml.PathMatcher{
|
||||||
|
Path: structuredDataPath,
|
||||||
|
Create: createKind,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFields, err := targetInStructured.Elements()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(targetFields) == 0 {
|
||||||
|
return fmt.Errorf("unable to find field in structured data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the value in the structured data
|
||||||
|
for _, t := range targetFields {
|
||||||
|
err = setFieldValue(options, t, value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize the modified structured data back to the scalar field
|
||||||
|
// Try to detect if original was JSON or YAML and preserve formatting
|
||||||
|
serializedData, err := serializeStructuredData(structuredData, scalarValue)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the original scalar field
|
||||||
|
scalarField.YNode().Value = serializedData
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializeStructuredData handles the serialization of structured data back to string format
|
||||||
|
// preserving the original format (JSON vs YAML) and style (pretty vs compact)
|
||||||
|
func serializeStructuredData(structuredData *yaml.RNode, originalValue string) (string, error) {
|
||||||
|
firstChar := rune(strings.TrimSpace(originalValue)[0])
|
||||||
|
if firstChar == '{' || firstChar == '[' {
|
||||||
|
return serializeAsJSON(structuredData, originalValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to YAML format
|
||||||
|
return serializeAsYAML(structuredData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializeAsJSON converts structured data back to JSON format
|
||||||
|
func serializeAsJSON(structuredData *yaml.RNode, originalValue string) (string, error) {
|
||||||
|
modifiedData, err := structuredData.String()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to serialize structured data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the YAML output as JSON
|
||||||
|
var jsonData interface{}
|
||||||
|
if err := yaml.Unmarshal([]byte(modifiedData), &jsonData); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to unmarshal YAML data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if original was pretty-printed by looking for newlines and indentation
|
||||||
|
if strings.Contains(originalValue, "\n") && strings.Contains(originalValue, " ") {
|
||||||
|
// Pretty-print the JSON to match original formatting
|
||||||
|
if prettyJSON, err := json.MarshalIndent(jsonData, "", " "); err == nil {
|
||||||
|
return string(prettyJSON), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compact JSON
|
||||||
|
if compactJSON, err := json.Marshal(jsonData); err == nil {
|
||||||
|
return string(compactJSON), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("failed to marshal JSON data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializeAsYAML converts structured data back to YAML format
|
||||||
|
func serializeAsYAML(structuredData *yaml.RNode) (string, error) {
|
||||||
|
modifiedData, err := structuredData.String()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to serialize YAML data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(modifiedData), nil
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
64
api/go.mod
64
api/go.mod
@@ -1,38 +1,44 @@
|
|||||||
module sigs.k8s.io/kustomize/api
|
module sigs.k8s.io/kustomize/api
|
||||||
|
|
||||||
go 1.18
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/evanphx/json-patch v4.11.0+incompatible
|
github.com/blang/semver/v4 v4.0.0
|
||||||
github.com/go-errors/errors v1.0.1
|
github.com/go-errors/errors v1.4.2
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/imdario/mergo v0.3.6
|
go.uber.org/goleak v1.3.0
|
||||||
github.com/pkg/errors v0.9.1
|
go.yaml.in/yaml/v2 v2.4.2
|
||||||
github.com/stretchr/testify v1.7.0
|
gopkg.in/evanphx/json-patch.v4 v4.13.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
k8s.io/kube-openapi v0.0.0-20260502001324-b7f5293f4787
|
||||||
k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661
|
sigs.k8s.io/kustomize/kyaml v0.21.1
|
||||||
sigs.k8s.io/kustomize/kyaml v0.13.8
|
sigs.k8s.io/yaml v1.5.0
|
||||||
sigs.k8s.io/yaml v1.2.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
github.com/go-openapi/swag v0.25.4 // indirect
|
||||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
github.com/go-openapi/swag/cmdutils v0.25.4 // indirect
|
||||||
github.com/go-openapi/swag v0.19.14 // indirect
|
github.com/go-openapi/swag/conv v0.25.4 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/go-openapi/swag/fileutils v0.25.4 // indirect
|
||||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
github.com/go-openapi/swag/jsonname v0.25.4 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/go-openapi/swag/jsonutils v0.25.4 // indirect
|
||||||
github.com/mailru/easyjson v0.7.6 // indirect
|
github.com/go-openapi/swag/loading v0.25.4 // indirect
|
||||||
|
github.com/go-openapi/swag/mangling v0.25.4 // indirect
|
||||||
|
github.com/go-openapi/swag/netutils v0.25.4 // indirect
|
||||||
|
github.com/go-openapi/swag/stringutils v0.25.4 // indirect
|
||||||
|
github.com/go-openapi/swag/typeutils v0.25.4 // indirect
|
||||||
|
github.com/go-openapi/swag/yamlutils v0.25.4 // indirect
|
||||||
|
github.com/google/gnostic-models v0.7.0 // indirect
|
||||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/xlab/treeprint v1.1.0 // indirect
|
github.com/sergi/go-diff v1.4.0 // indirect
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
github.com/xlab/treeprint v1.2.0 // indirect
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
golang.org/x/sys v0.42.0 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
google.golang.org/protobuf v1.28.0 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace sigs.k8s.io/kustomize/kyaml => ../kyaml
|
||||||
|
|||||||
289
api/go.sum
289
api/go.sum
@@ -1,216 +1,111 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
|
||||||
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/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
|
||||||
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
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.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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||||
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
|
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||||
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
|
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU=
|
||||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ=
|
||||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4=
|
||||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
|
||||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||||
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
|
github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y=
|
||||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk=
|
||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||||
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
|
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||||
github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54=
|
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||||
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
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.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
|
||||||
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/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
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/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
|
||||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
|
||||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
|
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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
|
||||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
|
||||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
|
||||||
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||||
|
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o=
|
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
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-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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/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.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.27.1/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 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
|
||||||
|
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.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.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
k8s.io/kube-openapi v0.0.0-20260502001324-b7f5293f4787 h1:kHv8PETbPIVHfqKBYwTNNSjqChf/7xn3JOS3re+NWs8=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
k8s.io/kube-openapi v0.0.0-20260502001324-b7f5293f4787/go.mod h1:Cyq7UE0QtGe+Zo+/6XFrxiS4Mq0tLyQEONkFzSkfp9o=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||||
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||||
k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661 h1:nqYOUleKLC/0P1zbU29F5q6aoezM6MOAVz+iyfQbZ5M=
|
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
|
||||||
k8s.io/kube-openapi v0.0.0-20220401212409-b28bf2818661/go.mod h1:daOouuuwd9JXpv1L7Y34iV3yf6nxzipkKMWWlqlvK9M=
|
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
|
||||||
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.13.8 h1:L4dSaDb6dL5mzv0UWSrUw8bskcEW+EnNtIObT5BoRsU=
|
|
||||||
sigs.k8s.io/kustomize/kyaml v0.13.8/go.mod h1:QsRbD0/KcU+wdk0/L0fIp2KLnohkVzs6fQ85/nOXac4=
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
|
||||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
|
||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
|
||||||
|
|||||||
@@ -28,12 +28,20 @@ type KvLoader interface {
|
|||||||
|
|
||||||
// Loader interface exposes methods to read bytes.
|
// Loader interface exposes methods to read bytes.
|
||||||
type Loader interface {
|
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 returns the root location for this Loader.
|
||||||
Root() string
|
Root() string
|
||||||
|
|
||||||
// New returns Loader located at newRoot.
|
// New returns Loader located at newRoot.
|
||||||
New(newRoot string) (Loader, error)
|
New(newRoot string) (Loader, error)
|
||||||
|
|
||||||
// Load returns the bytes read from the location or an error.
|
// Load returns the bytes read from the location or an error.
|
||||||
Load(location string) ([]byte, error)
|
Load(location string) ([]byte, error)
|
||||||
|
|
||||||
// Cleanup cleans the loader
|
// Cleanup cleans the loader
|
||||||
Cleanup() error
|
Cleanup() error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||||
"sigs.k8s.io/kustomize/api/ifc"
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
@@ -39,7 +39,7 @@ func LoadConfigFromCRDs(
|
|||||||
}
|
}
|
||||||
m, err := makeNameToApiMap(content)
|
m, err := makeNameToApiMap(content)
|
||||||
if err != nil {
|
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)
|
otherTc, err := makeConfigFromApiMap(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -144,7 +144,7 @@ func loadCrdIntoConfig(
|
|||||||
}
|
}
|
||||||
_, label := property.Extensions.GetString(xLabelSelector)
|
_, label := property.Extensions.GetString(xLabelSelector)
|
||||||
if label {
|
if label {
|
||||||
err = theConfig.AddLabelFieldSpec(
|
err = theConfig.AddCommonLabelsFieldSpec(
|
||||||
makeFs(theGvk, append(path, propName)))
|
makeFs(theGvk, append(path, propName)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
. "sigs.k8s.io/kustomize/api/internal/accumulator"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||||
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
"sigs.k8s.io/kustomize/api/internal/plugins/builtinconfig"
|
||||||
"sigs.k8s.io/kustomize/api/loader"
|
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
"sigs.k8s.io/kustomize/kyaml/resid"
|
"sigs.k8s.io/kustomize/kyaml/resid"
|
||||||
|
|||||||
@@ -590,7 +590,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
|
|||||||
func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
|
func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
|
||||||
rf := provider.NewDefaultDepProvider().GetResourceFactory()
|
rf := provider.NewDefaultDepProvider().GetResourceFactory()
|
||||||
|
|
||||||
v1 := rf.FromMapWithName(
|
v1, err := rf.FromMapWithName(
|
||||||
"volume1",
|
"volume1",
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
@@ -599,7 +599,10 @@ func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
|
|||||||
"name": "someprefix-volume1",
|
"name": "someprefix-volume1",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
c1 := rf.FromMapWithName(
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get new instance with given name: %v", err)
|
||||||
|
}
|
||||||
|
c1, err := rf.FromMapWithName(
|
||||||
"claim1",
|
"claim1",
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
@@ -612,9 +615,11 @@ func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
|
|||||||
"volumeName": "volume1",
|
"volumeName": "volume1",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get new instance with given name: %v", err)
|
||||||
|
}
|
||||||
v2 := v1.DeepCopy()
|
v2 := v1.DeepCopy()
|
||||||
c2 := rf.FromMapWithName(
|
c2, err := rf.FromMapWithName(
|
||||||
"claim1",
|
"claim1",
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
@@ -627,6 +632,9 @@ func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
|
|||||||
"volumeName": "someprefix-volume1",
|
"volumeName": "someprefix-volume1",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get new instance with given name: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(v1).AddR(c1).ResMap()
|
m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(v1).AddR(c1).ResMap()
|
||||||
|
|
||||||
|
|||||||
@@ -170,9 +170,10 @@ func (ra *ResAccumulator) FixBackReferences() (err error) {
|
|||||||
|
|
||||||
// Intersection drops the resources which "other" does not have.
|
// Intersection drops the resources which "other" does not have.
|
||||||
func (ra *ResAccumulator) Intersection(other resmap.ResMap) error {
|
func (ra *ResAccumulator) Intersection(other resmap.ResMap) error {
|
||||||
|
otherIds := other.AllIds() //nolint:revive
|
||||||
for _, curId := range ra.resMap.AllIds() {
|
for _, curId := range ra.resMap.AllIds() {
|
||||||
toDelete := true
|
toDelete := true
|
||||||
for _, otherId := range other.AllIds() {
|
for _, otherId := range otherIds {
|
||||||
if otherId == curId {
|
if otherId == curId {
|
||||||
toDelete = false
|
toDelete = false
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func makeResAccumulator(t *testing.T) *ResAccumulator {
|
|||||||
"name": "backendTwo",
|
"name": "backendTwo",
|
||||||
}}).ResMap())
|
}}).ResMap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("failed to append resources: %v", err)
|
||||||
}
|
}
|
||||||
return ra
|
return ra
|
||||||
}
|
}
|
||||||
@@ -143,22 +143,26 @@ func expectLog(t *testing.T, log bytes.Buffer, expect string) {
|
|||||||
func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
|
func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
|
||||||
ra := makeResAccumulator(t)
|
ra := makeResAccumulator(t)
|
||||||
rm0 := resmap.New()
|
rm0 := resmap.New()
|
||||||
err := rm0.Append(
|
|
||||||
provider.NewDefaultDepProvider().GetResourceFactory().FromMap(
|
r, err := provider.NewDefaultDepProvider().GetResourceFactory().FromMap(
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
"apiVersion": "v1",
|
"apiVersion": "v1",
|
||||||
"kind": "Service",
|
"kind": "Service",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]interface{}{
|
||||||
"name": "backendOne",
|
"name": "backendOne",
|
||||||
"namespace": "fooNamespace",
|
"namespace": "fooNamespace",
|
||||||
},
|
},
|
||||||
}))
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("failed to get instance of resources: %v", err)
|
||||||
|
}
|
||||||
|
err = rm0.Append(r)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to append a resource to ResMap: %v", err)
|
||||||
}
|
}
|
||||||
err = ra.AppendAll(rm0)
|
err = ra.AppendAll(rm0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected err: %v", err)
|
t.Fatalf("failed to append a resource to ResAccumulator: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ra.MergeVars([]types.Var{
|
err = ra.MergeVars([]types.Var{
|
||||||
@@ -227,7 +231,11 @@ func TestResolveVarConflicts(t *testing.T) {
|
|||||||
// create accumulators holding apparently conflicting vars that are not
|
// create accumulators holding apparently conflicting vars that are not
|
||||||
// actually in conflict because they point to the same concrete value.
|
// actually in conflict because they point to the same concrete value.
|
||||||
rm0 := resmap.New()
|
rm0 := resmap.New()
|
||||||
err := rm0.Append(rf.FromMap(fooAws))
|
r0, err0 := rf.FromMap(fooAws)
|
||||||
|
if err0 != nil {
|
||||||
|
t.Fatalf("failed to get instance of resources: %v", err0)
|
||||||
|
}
|
||||||
|
err := rm0.Append(r0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ac0 := MakeEmptyAccumulator()
|
ac0 := MakeEmptyAccumulator()
|
||||||
err = ac0.AppendAll(rm0)
|
err = ac0.AppendAll(rm0)
|
||||||
@@ -236,7 +244,11 @@ func TestResolveVarConflicts(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rm1 := resmap.New()
|
rm1 := resmap.New()
|
||||||
err = rm1.Append(rf.FromMap(barAws))
|
r1, err1 := rf.FromMap(barAws)
|
||||||
|
if err1 != nil {
|
||||||
|
t.Fatalf("failed to get instance of resources: %v", err1)
|
||||||
|
}
|
||||||
|
err = rm1.Append(r1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ac1 := MakeEmptyAccumulator()
|
ac1 := MakeEmptyAccumulator()
|
||||||
err = ac1.AppendAll(rm1)
|
err = ac1.AppendAll(rm1)
|
||||||
@@ -255,7 +267,11 @@ func TestResolveVarConflicts(t *testing.T) {
|
|||||||
// two above (because it contains a variable whose name is used in the other
|
// two above (because it contains a variable whose name is used in the other
|
||||||
// accumulators AND whose concrete values are different).
|
// accumulators AND whose concrete values are different).
|
||||||
rm2 := resmap.New()
|
rm2 := resmap.New()
|
||||||
err = rm2.Append(rf.FromMap(barGcp))
|
r2, err2 := rf.FromMap(barGcp)
|
||||||
|
if err2 != nil {
|
||||||
|
t.Fatalf("failed to get instance of resources: %v", err2)
|
||||||
|
}
|
||||||
|
err = rm2.Append(r2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ac2 := MakeEmptyAccumulator()
|
ac2 := MakeEmptyAccumulator()
|
||||||
err = ac2.AppendAll(rm2)
|
err = ac2.AppendAll(rm2)
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on AnnotationsTransformer; DO NOT EDIT.
|
// Code generated by pluginator on AnnotationsTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
|
// Code generated by pluginator on ConfigMapGenerator; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on HashTransformer; DO NOT EDIT.
|
// Code generated by pluginator on HashTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
// Code generated by pluginator on HelmChartInflationGenerator; DO NOT EDIT.
|
// Code generated by pluginator on HelmChartInflationGenerator; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/kio"
|
||||||
|
kyaml "sigs.k8s.io/kustomize/kyaml/yaml"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/yaml/merge2"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HelmChartInflationGeneratorPlugin is a plugin to generate resources
|
// Generate resources from a remote or local helm chart.
|
||||||
// from a remote or local helm chart.
|
|
||||||
type HelmChartInflationGeneratorPlugin struct {
|
type HelmChartInflationGeneratorPlugin struct {
|
||||||
h *resmap.PluginHelpers
|
h *resmap.PluginHelpers
|
||||||
types.HelmGlobals
|
types.HelmGlobals
|
||||||
@@ -29,8 +29,6 @@ type HelmChartInflationGeneratorPlugin struct {
|
|||||||
tmpDir string
|
tmpDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
var KustomizePlugin HelmChartInflationGeneratorPlugin
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
valuesMergeOptionMerge = "merge"
|
valuesMergeOptionMerge = "merge"
|
||||||
valuesMergeOptionOverride = "override"
|
valuesMergeOptionOverride = "override"
|
||||||
@@ -56,6 +54,18 @@ func (p *HelmChartInflationGeneratorPlugin) Config(
|
|||||||
if h.GeneralConfig().HelmConfig.Command == "" {
|
if h.GeneralConfig().HelmConfig.Command == "" {
|
||||||
return fmt.Errorf("must specify --helm-command")
|
return fmt.Errorf("must specify --helm-command")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CLI args takes precedence
|
||||||
|
if h.GeneralConfig().HelmConfig.KubeVersion != "" {
|
||||||
|
p.HelmChart.KubeVersion = h.GeneralConfig().HelmConfig.KubeVersion
|
||||||
|
}
|
||||||
|
if len(h.GeneralConfig().HelmConfig.ApiVersions) != 0 {
|
||||||
|
p.HelmChart.ApiVersions = h.GeneralConfig().HelmConfig.ApiVersions
|
||||||
|
}
|
||||||
|
if h.GeneralConfig().HelmConfig.Debug {
|
||||||
|
p.HelmChart.Debug = h.GeneralConfig().HelmConfig.Debug
|
||||||
|
}
|
||||||
|
|
||||||
p.h = h
|
p.h = h
|
||||||
if err = yaml.Unmarshal(config, p); err != nil {
|
if err = yaml.Unmarshal(config, p); err != nil {
|
||||||
return
|
return
|
||||||
@@ -73,7 +83,7 @@ func (p *HelmChartInflationGeneratorPlugin) establishTmpDir() (err error) {
|
|||||||
// already done.
|
// already done.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
p.tmpDir, err = ioutil.TempDir("", "kustomize-helm-")
|
p.tmpDir, err = os.MkdirTemp("", "kustomize-helm-")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,14 +97,22 @@ func (p *HelmChartInflationGeneratorPlugin) validateArgs() (err error) {
|
|||||||
// the loader root (unless root restrictions are
|
// the loader root (unless root restrictions are
|
||||||
// disabled, in which case this can be an absolute path).
|
// disabled, in which case this can be an absolute path).
|
||||||
if p.ChartHome == "" {
|
if p.ChartHome == "" {
|
||||||
p.ChartHome = "charts"
|
p.ChartHome = types.HelmDefaultHome
|
||||||
}
|
}
|
||||||
|
|
||||||
// The ValuesFile may be consulted by the plugin, so it must
|
// The ValuesFile(s) may be consulted by the plugin, so it must
|
||||||
// be under the loader root (unless root restrictions are
|
// be under the loader root (unless root restrictions are
|
||||||
// disabled).
|
// disabled).
|
||||||
if p.ValuesFile == "" {
|
if p.ValuesFile == "" {
|
||||||
p.ValuesFile = filepath.Join(p.ChartHome, p.Name, "values.yaml")
|
p.ValuesFile = filepath.Join(p.absChartHome(), p.Name, "values.yaml")
|
||||||
|
}
|
||||||
|
for i, file := range p.AdditionalValuesFiles {
|
||||||
|
// use Load() to enforce root restrictions
|
||||||
|
if _, err := p.h.Loader().Load(file); err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "could not load additionalValuesFile")
|
||||||
|
}
|
||||||
|
// the additional values filepaths must be relative to the kust root
|
||||||
|
p.AdditionalValuesFiles[i] = filepath.Join(p.h.Loader().Root(), file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = p.errIfIllegalValuesMerge(); err != nil {
|
if err = p.errIfIllegalValuesMerge(); err != nil {
|
||||||
@@ -104,7 +122,7 @@ func (p *HelmChartInflationGeneratorPlugin) validateArgs() (err error) {
|
|||||||
// ConfigHome is not loaded by the plugin, and can be located anywhere.
|
// ConfigHome is not loaded by the plugin, and can be located anywhere.
|
||||||
if p.ConfigHome == "" {
|
if p.ConfigHome == "" {
|
||||||
if err = p.establishTmpDir(); err != nil {
|
if err = p.establishTmpDir(); err != nil {
|
||||||
return errors.Wrap(
|
return errors.WrapPrefixf(
|
||||||
err, "unable to create tmp dir for HELM_CONFIG_HOME")
|
err, "unable to create tmp dir for HELM_CONFIG_HOME")
|
||||||
}
|
}
|
||||||
p.ConfigHome = filepath.Join(p.tmpDir, "helm")
|
p.ConfigHome = filepath.Join(p.tmpDir, "helm")
|
||||||
@@ -127,10 +145,17 @@ func (p *HelmChartInflationGeneratorPlugin) errIfIllegalValuesMerge() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) absChartHome() string {
|
func (p *HelmChartInflationGeneratorPlugin) absChartHome() string {
|
||||||
|
var chartHome string
|
||||||
if filepath.IsAbs(p.ChartHome) {
|
if filepath.IsAbs(p.ChartHome) {
|
||||||
return p.ChartHome
|
chartHome = p.ChartHome
|
||||||
|
} else {
|
||||||
|
chartHome = filepath.Join(p.h.Loader().Root(), p.ChartHome)
|
||||||
}
|
}
|
||||||
return filepath.Join(p.h.Loader().Root(), p.ChartHome)
|
|
||||||
|
if p.Version != "" && p.Repo != "" {
|
||||||
|
return filepath.Join(chartHome, fmt.Sprintf("%s-%s", p.Name, p.Version))
|
||||||
|
}
|
||||||
|
return chartHome
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) runHelmCommand(
|
func (p *HelmChartInflationGeneratorPlugin) runHelmCommand(
|
||||||
@@ -146,13 +171,17 @@ func (p *HelmChartInflationGeneratorPlugin) runHelmCommand(
|
|||||||
fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.ConfigHome)}
|
fmt.Sprintf("HELM_DATA_HOME=%s/.data", p.ConfigHome)}
|
||||||
cmd.Env = append(os.Environ(), env...)
|
cmd.Env = append(os.Environ(), env...)
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
|
errorOutput := stderr.String()
|
||||||
|
if slices.Contains(args, "--debug") {
|
||||||
|
errorOutput = " Helm stack trace:\n" + errorOutput + "\nHelm template:\n" + stdout.String() + "\n"
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helm := p.h.GeneralConfig().HelmConfig.Command
|
helm := p.h.GeneralConfig().HelmConfig.Command
|
||||||
err = errors.Wrap(
|
err = errors.WrapPrefixf(
|
||||||
fmt.Errorf(
|
fmt.Errorf(
|
||||||
"unable to run: '%s %s' with env=%s (is '%s' installed?)",
|
"unable to run: '%s %s' with env=%s (is '%s' installed?): %w",
|
||||||
helm, strings.Join(args, " "), env, helm),
|
helm, strings.Join(args, " "), env, helm, err),
|
||||||
stderr.String(),
|
"%s", errorOutput,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return stdout.Bytes(), err
|
return stdout.Bytes(), err
|
||||||
@@ -180,18 +209,33 @@ func (p *HelmChartInflationGeneratorPlugin) replaceValuesInline() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
chValues := make(map[string]interface{})
|
chValues, err := kyaml.Parse(string(pValues))
|
||||||
if err = yaml.Unmarshal(pValues, &chValues); err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.WrapPrefixf(err, "could not parse values file into rnode")
|
||||||
}
|
}
|
||||||
|
inlineValues, err := kyaml.FromMap(p.ValuesInline)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "could not parse values inline into rnode")
|
||||||
|
}
|
||||||
|
var outValues *kyaml.RNode
|
||||||
switch p.ValuesMerge {
|
switch p.ValuesMerge {
|
||||||
|
// Function `merge2.Merge` overrides values in dest with values from src.
|
||||||
|
// To achieve override or merge behavior, we pass parameters in different order.
|
||||||
|
// Object passed as dest will be modified, so we copy it just in case someone
|
||||||
|
// decides to use it after this is called.
|
||||||
case valuesMergeOptionOverride:
|
case valuesMergeOptionOverride:
|
||||||
err = mergo.Merge(
|
outValues, err = merge2.Merge(inlineValues, chValues.Copy(), kyaml.MergeOptions{})
|
||||||
&chValues, p.ValuesInline, mergo.WithOverride)
|
|
||||||
case valuesMergeOptionMerge:
|
case valuesMergeOptionMerge:
|
||||||
err = mergo.Merge(&chValues, p.ValuesInline)
|
outValues, err = merge2.Merge(chValues, inlineValues.Copy(), kyaml.MergeOptions{})
|
||||||
}
|
}
|
||||||
p.ValuesInline = chValues
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "could not merge values")
|
||||||
|
}
|
||||||
|
mapValues, err := outValues.Map()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "could not parse merged values into map")
|
||||||
|
}
|
||||||
|
p.ValuesInline = mapValues
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +255,7 @@ func (p *HelmChartInflationGeneratorPlugin) writeValuesBytes(
|
|||||||
return "", fmt.Errorf("cannot create tmp dir to write helm values")
|
return "", fmt.Errorf("cannot create tmp dir to write helm values")
|
||||||
}
|
}
|
||||||
path := filepath.Join(p.tmpDir, p.Name+"-kustomize-values.yaml")
|
path := filepath.Join(p.tmpDir, p.Name+"-kustomize-values.yaml")
|
||||||
return path, ioutil.WriteFile(path, b, 0644)
|
return path, errors.WrapPrefixf(os.WriteFile(path, b, 0644), "failed to write values file")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) cleanup() {
|
func (p *HelmChartInflationGeneratorPlugin) cleanup() {
|
||||||
@@ -244,46 +288,37 @@ func (p *HelmChartInflationGeneratorPlugin) Generate() (rm resmap.ResMap, err er
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var stdout []byte
|
var stdout []byte
|
||||||
stdout, err = p.runHelmCommand(p.templateCommand())
|
stdout, err = p.runHelmCommand(p.AsHelmArgs(p.absChartHome()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rm, err = p.h.ResmapFactory().NewResMapFromBytes(stdout)
|
rm, resMapErr := p.h.ResmapFactory().NewResMapFromBytes(stdout)
|
||||||
if err == nil {
|
if resMapErr == nil {
|
||||||
|
if err := p.markHelmGeneratedResources(rm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return rm, nil
|
return rm, nil
|
||||||
}
|
}
|
||||||
// try to remove the contents before first "---" because
|
// try to remove the contents before first "---" because
|
||||||
// helm may produce messages to stdout before it
|
// helm may produce messages to stdout before it
|
||||||
stdoutStr := string(stdout)
|
r := &kio.ByteReader{Reader: bytes.NewBuffer(stdout), OmitReaderAnnotations: true}
|
||||||
if idx := strings.Index(stdoutStr, "---"); idx != -1 {
|
nodes, err := r.Read()
|
||||||
return p.h.ResmapFactory().NewResMapFromBytes([]byte(stdoutStr[idx:]))
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading helm output: %w", err)
|
||||||
}
|
}
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) templateCommand() []string {
|
if len(nodes) != 0 {
|
||||||
args := []string{"template"}
|
rm, err = p.h.ResmapFactory().NewResMapFromRNodeSlice(nodes)
|
||||||
if p.ReleaseName != "" {
|
if err != nil {
|
||||||
args = append(args, p.ReleaseName)
|
return nil, fmt.Errorf("could not parse rnode slice into resource map: %w", err)
|
||||||
|
}
|
||||||
|
if err := p.markHelmGeneratedResources(rm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return rm, nil
|
||||||
}
|
}
|
||||||
if p.Namespace != "" {
|
return nil, fmt.Errorf("could not parse bytes into resource map: %w", resMapErr)
|
||||||
args = append(args, "--namespace", p.Namespace)
|
|
||||||
}
|
|
||||||
args = append(args, filepath.Join(p.absChartHome(), p.Name))
|
|
||||||
if p.ValuesFile != "" {
|
|
||||||
args = append(args, "--values", p.ValuesFile)
|
|
||||||
}
|
|
||||||
if p.ReleaseName == "" {
|
|
||||||
// AFAICT, this doesn't work as intended due to a bug in helm.
|
|
||||||
// See https://github.com/helm/helm/issues/6019
|
|
||||||
// I've tried placing the flag before and after the name argument.
|
|
||||||
args = append(args, "--generate-name")
|
|
||||||
}
|
|
||||||
if p.IncludeCRDs {
|
|
||||||
args = append(args, "--include-crds")
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string {
|
func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string {
|
||||||
@@ -291,11 +326,24 @@ func (p *HelmChartInflationGeneratorPlugin) pullCommand() []string {
|
|||||||
"pull",
|
"pull",
|
||||||
"--untar",
|
"--untar",
|
||||||
"--untardir", p.absChartHome(),
|
"--untardir", p.absChartHome(),
|
||||||
"--repo", p.Repo,
|
}
|
||||||
p.Name}
|
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(p.Repo, "oci://"):
|
||||||
|
args = append(args, strings.TrimSuffix(p.Repo, "/")+"/"+p.Name)
|
||||||
|
case p.Repo != "":
|
||||||
|
args = append(args, "--repo", p.Repo)
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
args = append(args, p.Name)
|
||||||
|
}
|
||||||
|
|
||||||
if p.Version != "" {
|
if p.Version != "" {
|
||||||
args = append(args, "--version", p.Version)
|
args = append(args, "--version", p.Version)
|
||||||
}
|
}
|
||||||
|
if p.Devel {
|
||||||
|
args = append(args, "--devel")
|
||||||
|
}
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,9 +358,18 @@ func (p *HelmChartInflationGeneratorPlugin) chartExistsLocally() (string, bool)
|
|||||||
return path, s.IsDir()
|
return path, s.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkHelmVersion will return an error if the helm version is not V3
|
func (p *HelmChartInflationGeneratorPlugin) markHelmGeneratedResources(rm resmap.ResMap) error {
|
||||||
|
for _, r := range rm.Resources() {
|
||||||
|
if err := r.RNode.PipeE(kyaml.SetAnnotation(konfig.HelmGeneratedAnnotation, "true")); err != nil {
|
||||||
|
return fmt.Errorf("failed to set helm annotation: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkHelmVersion will return an error if the helm version is not V3 or V4
|
||||||
func (p *HelmChartInflationGeneratorPlugin) checkHelmVersion() error {
|
func (p *HelmChartInflationGeneratorPlugin) checkHelmVersion() error {
|
||||||
stdout, err := p.runHelmCommand([]string{"version", "-c", "--short"})
|
stdout, err := p.runHelmCommand([]string{"version", "--short"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -328,8 +385,8 @@ func (p *HelmChartInflationGeneratorPlugin) checkHelmVersion() error {
|
|||||||
v = v[1:]
|
v = v[1:]
|
||||||
}
|
}
|
||||||
majorVersion := strings.Split(v, ".")[0]
|
majorVersion := strings.Split(v, ".")[0]
|
||||||
if majorVersion != "3" {
|
if majorVersion != "3" && majorVersion != "4" {
|
||||||
return fmt.Errorf("this plugin requires helm V3 but got v%s", v)
|
return fmt.Errorf("this plugin requires helm V3 or V4 but got v%s", v)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on IAMPolicyGenerator; DO NOT EDIT.
|
// Code generated by pluginator on IAMPolicyGenerator; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
|
// Code generated by pluginator on ImageTagTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on LabelTransformer; DO NOT EDIT.
|
// Code generated by pluginator on LabelTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -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,12 +1,11 @@
|
|||||||
// Code generated by pluginator on NamespaceTransformer; DO NOT EDIT.
|
// Code generated by pluginator on NamespaceTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"sigs.k8s.io/kustomize/api/filters/namespace"
|
"sigs.k8s.io/kustomize/api/filters/namespace"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/errors"
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
@@ -14,6 +13,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Change or set the namespace of non-cluster level resources.
|
// Change or set the namespace of non-cluster level resources.
|
||||||
|
//
|
||||||
|
//nolint:tagalign
|
||||||
type NamespaceTransformerPlugin struct {
|
type NamespaceTransformerPlugin struct {
|
||||||
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
types.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||||
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
FieldSpecs []types.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"`
|
||||||
@@ -51,6 +52,10 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
|
|||||||
// Don't mutate empty objects?
|
// Don't mutate empty objects?
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if annotations := r.GetAnnotations(konfig.HelmGeneratedAnnotation); annotations[konfig.HelmGeneratedAnnotation] == "true" {
|
||||||
|
// Don't apply namespace on Helm generated manifest. Helm should take care of it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
r.StorePreviousId()
|
r.StorePreviousId()
|
||||||
if err := r.ApplyFilter(namespace.Filter{
|
if err := r.ApplyFilter(namespace.Filter{
|
||||||
Namespace: p.Namespace,
|
Namespace: p.Namespace,
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
// Code generated by pluginator on PatchJson6902Transformer; DO NOT EDIT.
|
// Code generated by pluginator on PatchJson6902Transformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
jsonpatch "gopkg.in/evanphx/json-patch.v4"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
||||||
"sigs.k8s.io/kustomize/api/ifc"
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
@@ -61,7 +59,7 @@ func (p *PatchJson6902TransformerPlugin) Config(
|
|||||||
}
|
}
|
||||||
p.decodedPatch, err = jsonpatch.DecodePatch([]byte(p.JsonOp))
|
p.decodedPatch, err = jsonpatch.DecodePatch([]byte(p.JsonOp))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "decoding %s", p.JsonOp)
|
return errors.WrapPrefixf(err, "decoding %s", p.JsonOp)
|
||||||
}
|
}
|
||||||
if len(p.decodedPatch) == 0 {
|
if len(p.decodedPatch) == 0 {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on PatchStrategicMergeTransformer; DO NOT EDIT.
|
// Code generated by pluginator on PatchStrategicMergeTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,110 +1,135 @@
|
|||||||
// Code generated by pluginator on PatchTransformer; DO NOT EDIT.
|
// Code generated by pluginator on PatchTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
jsonpatch "github.com/evanphx/json-patch"
|
jsonpatch "gopkg.in/evanphx/json-patch.v4"
|
||||||
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
"sigs.k8s.io/kustomize/api/filters/patchjson6902"
|
||||||
"sigs.k8s.io/kustomize/api/resmap"
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
"sigs.k8s.io/kustomize/api/resource"
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
"sigs.k8s.io/kustomize/kyaml/kio/kioutil"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PatchTransformerPlugin struct {
|
type PatchTransformerPlugin struct {
|
||||||
loadedPatch *resource.Resource
|
smPatches []*resource.Resource // strategic-merge patches
|
||||||
decodedPatch jsonpatch.Patch
|
jsonPatches jsonpatch.Patch // json6902 patch
|
||||||
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
// patchText is pure patch text created by Path or Patch
|
||||||
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
patchText string
|
||||||
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
|
// patchSource is patch source message
|
||||||
Options map[string]bool `json:"options,omitempty" yaml:"options,omitempty"`
|
patchSource string
|
||||||
|
Path string `json:"path,omitempty" yaml:"path,omitempty"`
|
||||||
|
Patch string `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||||
|
Target *types.Selector `json:"target,omitempty" yaml:"target,omitempty"`
|
||||||
|
Options *types.PatchArgs `json:"options,omitempty" yaml:"options,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PatchTransformerPlugin) Config(
|
func (p *PatchTransformerPlugin) Config(h *resmap.PluginHelpers, c []byte) error {
|
||||||
h *resmap.PluginHelpers, c []byte) error {
|
if err := yaml.Unmarshal(c, p); err != nil {
|
||||||
err := yaml.Unmarshal(c, p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Patch = strings.TrimSpace(p.Patch)
|
p.Patch = strings.TrimSpace(p.Patch)
|
||||||
if p.Patch == "" && p.Path == "" {
|
switch {
|
||||||
return fmt.Errorf(
|
case p.Patch == "" && p.Path == "":
|
||||||
"must specify one of patch and path in\n%s", string(c))
|
return fmt.Errorf("must specify one of patch and path in\n%s", string(c))
|
||||||
}
|
case p.Patch != "" && p.Path != "":
|
||||||
if p.Patch != "" && p.Path != "" {
|
return fmt.Errorf("patch and path can't be set at the same time\n%s", string(c))
|
||||||
return fmt.Errorf(
|
case p.Patch != "":
|
||||||
"patch and path can't be set at the same time\n%s", string(c))
|
p.patchText = p.Patch
|
||||||
}
|
p.patchSource = fmt.Sprintf("[patch: %q]", p.patchText)
|
||||||
if p.Path != "" {
|
case p.Path != "":
|
||||||
loaded, loadErr := h.Loader().Load(p.Path)
|
loaded, err := h.Loader().Load(p.Path)
|
||||||
if loadErr != nil {
|
if err != nil {
|
||||||
return loadErr
|
return fmt.Errorf("failed to get the patch file from path(%s): %w", p.Path, err)
|
||||||
}
|
}
|
||||||
p.Patch = string(loaded)
|
p.patchText = string(loaded)
|
||||||
|
p.patchSource = fmt.Sprintf("[path: %q]", p.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
patchSM, errSM := h.ResmapFactory().RF().FromBytes([]byte(p.Patch))
|
patchesSM, errSM := h.ResmapFactory().RF().SliceFromBytes([]byte(p.patchText))
|
||||||
patchJson, errJson := jsonPatchFromBytes([]byte(p.Patch))
|
patchesJson, errJson := jsonPatchFromBytes([]byte(p.patchText))
|
||||||
if (errSM == nil && errJson == nil) ||
|
|
||||||
(patchSM != nil && patchJson != nil) {
|
if ((errSM == nil && errJson == nil) ||
|
||||||
|
(patchesSM != nil && patchesJson != nil)) &&
|
||||||
|
(len(patchesSM) > 0 && len(patchesJson) > 0) {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"illegally qualifies as both an SM and JSON patch: [%v]",
|
"illegally qualifies as both an SM and JSON patch: %s",
|
||||||
p.Patch)
|
p.patchSource)
|
||||||
}
|
}
|
||||||
if errSM != nil && errJson != nil {
|
if errSM != nil && errJson != nil {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"unable to parse SM or JSON patch from [%v]", p.Patch)
|
"unable to parse SM or JSON patch from %s", p.patchSource)
|
||||||
}
|
}
|
||||||
if errSM == nil {
|
if errSM == nil {
|
||||||
p.loadedPatch = patchSM
|
p.smPatches = patchesSM
|
||||||
if p.Options["allowNameChange"] {
|
for _, loadedPatch := range p.smPatches {
|
||||||
p.loadedPatch.AllowNameChange()
|
if p.Options == nil {
|
||||||
}
|
continue
|
||||||
if p.Options["allowKindChange"] {
|
}
|
||||||
p.loadedPatch.AllowKindChange()
|
|
||||||
|
if p.Options.AllowNameChange {
|
||||||
|
loadedPatch.AllowNameChange()
|
||||||
|
}
|
||||||
|
if p.Options.AllowKindChange {
|
||||||
|
loadedPatch.AllowKindChange()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p.decodedPatch = patchJson
|
p.jsonPatches = patchesJson
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
|
func (p *PatchTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||||
if p.loadedPatch == nil {
|
if p.smPatches != nil {
|
||||||
return p.transformJson6902(m, p.decodedPatch)
|
return p.transformStrategicMerge(m)
|
||||||
}
|
}
|
||||||
// The patch was a strategic merge patch
|
if p.jsonPatches != nil {
|
||||||
return p.transformStrategicMerge(m, p.loadedPatch)
|
return p.transformJson6902(m)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// transformStrategicMerge applies the provided strategic merge patch
|
// transformStrategicMerge applies each loaded strategic merge patch
|
||||||
// to all the resources in the ResMap that match either the Target or
|
// to the resource in the ResMap that matches the identifier of the patch.
|
||||||
// the identifier of the patch.
|
// If only one patch is specified, the Target can be used instead.
|
||||||
func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap, patch *resource.Resource) error {
|
func (p *PatchTransformerPlugin) transformStrategicMerge(m resmap.ResMap) error {
|
||||||
if p.Target == nil {
|
if p.Target != nil {
|
||||||
|
if len(p.smPatches) > 1 {
|
||||||
|
// detail: https://github.com/kubernetes-sigs/kustomize/issues/5049#issuecomment-1440604403
|
||||||
|
return fmt.Errorf("Multiple Strategic-Merge Patches in one `patches` entry is not allowed to set `patches.target` field: %s", p.patchSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
// single patch
|
||||||
|
patch := p.smPatches[0]
|
||||||
|
selected, err := m.Select(*p.Target)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to find patch target %q in `resources`: %w", p.Target, err)
|
||||||
|
}
|
||||||
|
return errors.Wrap(m.ApplySmPatch(resource.MakeIdSet(selected), patch))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, patch := range p.smPatches {
|
||||||
target, err := m.GetById(patch.OrgId())
|
target, err := m.GetById(patch.OrgId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("no resource matches strategic merge patch %q: %w", patch.OrgId(), err)
|
||||||
|
}
|
||||||
|
if err := target.ApplySmPatch(patch); err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
}
|
}
|
||||||
return target.ApplySmPatch(patch)
|
|
||||||
}
|
}
|
||||||
selected, err := m.Select(*p.Target)
|
return nil
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return m.ApplySmPatch(resource.MakeIdSet(selected), patch)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// transformJson6902 applies the provided json6902 patch
|
// transformJson6902 applies json6902 Patch to all the resources in the ResMap that match Target.
|
||||||
// to all the resources in the ResMap that match the Target.
|
func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap) error {
|
||||||
func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpatch.Patch) error {
|
|
||||||
if p.Target == nil {
|
if p.Target == nil {
|
||||||
return fmt.Errorf("must specify a target for patch %s", p.Patch)
|
return fmt.Errorf("must specify a target for JSON patch %s", p.patchSource)
|
||||||
}
|
}
|
||||||
resources, err := m.Select(*p.Target)
|
resources, err := m.Select(*p.Target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -114,7 +139,7 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
|
|||||||
res.StorePreviousId()
|
res.StorePreviousId()
|
||||||
internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode)
|
internalAnnotations := kioutil.GetInternalAnnotations(&res.RNode)
|
||||||
err = res.ApplyFilter(patchjson6902.Filter{
|
err = res.ApplyFilter(patchjson6902.Filter{
|
||||||
Patch: p.Patch,
|
Patch: p.patchText,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -129,16 +154,17 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// jsonPatchFromBytes loads a Json 6902 patch from
|
// jsonPatchFromBytes loads a Json 6902 patch from a bytes input
|
||||||
// a bytes input
|
func jsonPatchFromBytes(in []byte) (jsonpatch.Patch, error) {
|
||||||
func jsonPatchFromBytes(
|
|
||||||
in []byte) (jsonpatch.Patch, error) {
|
|
||||||
ops := string(in)
|
ops := string(in)
|
||||||
if ops == "" {
|
if ops == "" {
|
||||||
return nil, fmt.Errorf("empty json patch operations")
|
return nil, fmt.Errorf("empty json patch operations")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ops[0] != '[' {
|
if ops[0] != '[' {
|
||||||
|
// TODO(5049):
|
||||||
|
// In the case of multiple yaml documents, return error instead of ignoring all but first.
|
||||||
|
// Details: https://github.com/kubernetes-sigs/kustomize/pull/5194#discussion_r1256686728
|
||||||
jsonOps, err := yaml.YAMLToJSON(in)
|
jsonOps, err := yaml.YAMLToJSON(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on PrefixTransformer; DO NOT EDIT.
|
// Code generated by pluginator on PrefixTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on ReplacementTransformer; DO NOT EDIT.
|
// Code generated by pluginator on ReplacementTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -16,7 +14,7 @@ import (
|
|||||||
// Replace values in targets with values from a source
|
// Replace values in targets with values from a source
|
||||||
type ReplacementTransformerPlugin struct {
|
type ReplacementTransformerPlugin struct {
|
||||||
ReplacementList []types.ReplacementField `json:"replacements,omitempty" yaml:"replacements,omitempty"`
|
ReplacementList []types.ReplacementField `json:"replacements,omitempty" yaml:"replacements,omitempty"`
|
||||||
Replacements []types.Replacement `json:"omitempty" yaml:"omitempty"`
|
replacements []types.Replacement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ReplacementTransformerPlugin) Config(
|
func (p *ReplacementTransformerPlugin) Config(
|
||||||
@@ -49,19 +47,19 @@ func (p *ReplacementTransformerPlugin) Config(
|
|||||||
if err := yaml.Unmarshal(content, &repl); err != nil {
|
if err := yaml.Unmarshal(content, &repl); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Replacements = append(p.Replacements, repl...)
|
p.replacements = append(p.replacements, repl...)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
repl := types.Replacement{}
|
repl := types.Replacement{}
|
||||||
if err := yaml.Unmarshal(content, &repl); err != nil {
|
if err := yaml.Unmarshal(content, &repl); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
p.Replacements = append(p.Replacements, repl)
|
p.replacements = append(p.replacements, repl)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported replacement type encountered within replacement path: %v", items.Kind())
|
return fmt.Errorf("unsupported replacement type encountered within replacement path: %v", items.Kind())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// replacement information is already loaded
|
// replacement information is already loaded
|
||||||
p.Replacements = append(p.Replacements, r.Replacement)
|
p.replacements = append(p.replacements, r.Replacement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -69,7 +67,7 @@ func (p *ReplacementTransformerPlugin) Config(
|
|||||||
|
|
||||||
func (p *ReplacementTransformerPlugin) Transform(m resmap.ResMap) (err error) {
|
func (p *ReplacementTransformerPlugin) Transform(m resmap.ResMap) (err error) {
|
||||||
return m.ApplyFilter(replacement.Filter{
|
return m.ApplyFilter(replacement.Filter{
|
||||||
Replacements: p.Replacements,
|
Replacements: p.replacements,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on ReplicaCountTransformer; DO NOT EDIT.
|
// Code generated by pluginator on ReplicaCountTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
|
// Code generated by pluginator on SecretGenerator; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
236
api/internal/builtins/SortOrderTransformer.go
Normal file
236
api/internal/builtins/SortOrderTransformer.go
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
// Code generated by pluginator on SortOrderTransformer; DO NOT EDIT.
|
||||||
|
package builtins
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"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/resid"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sort the resources using a customizable ordering based of Kind.
|
||||||
|
// Defaults to the ordering of the GVK struct, which 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 SortOrderTransformerPlugin struct {
|
||||||
|
SortOptions *types.SortOptions `json:"sortOptions,omitempty" yaml:"sortOptions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SortOrderTransformerPlugin) Config(
|
||||||
|
_ *resmap.PluginHelpers, c []byte) error {
|
||||||
|
return errors.WrapPrefixf(yaml.Unmarshal(c, p), "Failed to unmarshal SortOrderTransformer config")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SortOrderTransformerPlugin) applyDefaults() {
|
||||||
|
// Default to FIFO sort, aka no-op.
|
||||||
|
if p.SortOptions == nil {
|
||||||
|
p.SortOptions = &types.SortOptions{
|
||||||
|
Order: types.FIFOSortOrder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If legacy sort is selected and no options are given, default to
|
||||||
|
// hardcoded order.
|
||||||
|
if p.SortOptions.Order == types.LegacySortOrder && p.SortOptions.LegacySortOptions == nil {
|
||||||
|
p.SortOptions.LegacySortOptions = &types.LegacySortOptions{
|
||||||
|
OrderFirst: defaultOrderFirst,
|
||||||
|
OrderLast: defaultOrderLast,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SortOrderTransformerPlugin) validate() error {
|
||||||
|
// Check valid values for SortOrder
|
||||||
|
if p.SortOptions.Order != types.FIFOSortOrder && p.SortOptions.Order != types.LegacySortOrder {
|
||||||
|
return errors.Errorf("the field 'sortOptions.order' must be one of [%s, %s]",
|
||||||
|
types.FIFOSortOrder, types.LegacySortOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that the only options set are the ones corresponding to the
|
||||||
|
// selected sort order.
|
||||||
|
if p.SortOptions.Order == types.FIFOSortOrder &&
|
||||||
|
p.SortOptions.LegacySortOptions != nil {
|
||||||
|
return errors.Errorf("the field 'sortOptions.legacySortOptions' is"+
|
||||||
|
" set but the selected sort order is '%v', not 'legacy'",
|
||||||
|
p.SortOptions.Order)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SortOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) {
|
||||||
|
p.applyDefaults()
|
||||||
|
err = p.validate()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort
|
||||||
|
if p.SortOptions.Order == types.LegacySortOrder {
|
||||||
|
s := newLegacyIDSorter(m.Resources(), p.SortOptions.LegacySortOptions)
|
||||||
|
sort.Sort(s)
|
||||||
|
|
||||||
|
// Clear the map and re-add the resources in the sorted order.
|
||||||
|
m.Clear()
|
||||||
|
for _, r := range s.resources {
|
||||||
|
err := m.Append(r)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "SortOrderTransformer: Failed to append to resources")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code for legacy sorting.
|
||||||
|
// Legacy sorting is a "fixed" order sorting maintained for backwards
|
||||||
|
// compatibility.
|
||||||
|
|
||||||
|
// legacyIDSorter sorts resources based on two priority lists:
|
||||||
|
// - orderFirst: Resources that should be placed in the start, in the given order.
|
||||||
|
// - orderLast: Resources that should be placed in the end, in the given order.
|
||||||
|
type legacyIDSorter struct {
|
||||||
|
// resids only stores the metadata of the object. This is an optimization as
|
||||||
|
// it's expensive to compute these again and again during ordering.
|
||||||
|
resids []resid.ResId
|
||||||
|
// Initially, we sorted the metadata (ResId) of each object and then called GetByCurrentId on each to construct the final list.
|
||||||
|
// The problem is that GetByCurrentId is inefficient and does a linear scan in a list every time we do that.
|
||||||
|
// So instead, we sort resources alongside the ResIds.
|
||||||
|
resources []*resource.Resource
|
||||||
|
|
||||||
|
typeOrders map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLegacyIDSorter(
|
||||||
|
resources []*resource.Resource,
|
||||||
|
options *types.LegacySortOptions) *legacyIDSorter {
|
||||||
|
// Precalculate a resource ranking based on the priority lists.
|
||||||
|
var typeOrders = func() map[string]int {
|
||||||
|
m := map[string]int{}
|
||||||
|
for i, n := range options.OrderFirst {
|
||||||
|
m[n] = -len(options.OrderFirst) + i
|
||||||
|
}
|
||||||
|
for i, n := range options.OrderLast {
|
||||||
|
m[n] = 1 + i
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}()
|
||||||
|
|
||||||
|
ret := &legacyIDSorter{typeOrders: typeOrders}
|
||||||
|
for _, res := range resources {
|
||||||
|
ret.resids = append(ret.resids, res.CurId())
|
||||||
|
ret.resources = append(ret.resources, res)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sort.Interface = legacyIDSorter{}
|
||||||
|
|
||||||
|
func (a legacyIDSorter) Len() int { return len(a.resids) }
|
||||||
|
func (a legacyIDSorter) Swap(i, j int) {
|
||||||
|
a.resids[i], a.resids[j] = a.resids[j], a.resids[i]
|
||||||
|
a.resources[i], a.resources[j] = a.resources[j], a.resources[i]
|
||||||
|
}
|
||||||
|
func (a legacyIDSorter) Less(i, j int) bool {
|
||||||
|
if !a.resids[i].Gvk.Equals(a.resids[j].Gvk) {
|
||||||
|
return gvkLessThan(a.resids[i].Gvk, a.resids[j].Gvk, a.typeOrders)
|
||||||
|
}
|
||||||
|
return legacyResIDSortString(a.resids[i]) < legacyResIDSortString(a.resids[j])
|
||||||
|
}
|
||||||
|
|
||||||
|
func gvkLessThan(gvk1, gvk2 resid.Gvk, typeOrders map[string]int) bool {
|
||||||
|
index1 := typeOrders[gvk1.Kind]
|
||||||
|
index2 := typeOrders[gvk2.Kind]
|
||||||
|
if index1 != index2 {
|
||||||
|
return index1 < index2
|
||||||
|
}
|
||||||
|
if (gvk1.Kind == types.NamespaceKind && gvk2.Kind == types.NamespaceKind) && (gvk1.Group == "" || gvk2.Group == "") {
|
||||||
|
return legacyGVKSortString(gvk1) > legacyGVKSortString(gvk2)
|
||||||
|
}
|
||||||
|
return legacyGVKSortString(gvk1) < legacyGVKSortString(gvk2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// legacyGVKSortString returns a string representation of given GVK used for
|
||||||
|
// stable sorting.
|
||||||
|
func legacyGVKSortString(x resid.Gvk) string {
|
||||||
|
legacyNoGroup := "~G"
|
||||||
|
legacyNoVersion := "~V"
|
||||||
|
legacyNoKind := "~K"
|
||||||
|
legacyFieldSeparator := "_"
|
||||||
|
|
||||||
|
g := x.Group
|
||||||
|
if g == "" {
|
||||||
|
g = legacyNoGroup
|
||||||
|
}
|
||||||
|
v := x.Version
|
||||||
|
if v == "" {
|
||||||
|
v = legacyNoVersion
|
||||||
|
}
|
||||||
|
k := x.Kind
|
||||||
|
if k == "" {
|
||||||
|
k = legacyNoKind
|
||||||
|
}
|
||||||
|
return strings.Join([]string{g, v, k}, legacyFieldSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// legacyResIDSortString returns a string representation of given ResID used for
|
||||||
|
// stable sorting.
|
||||||
|
func legacyResIDSortString(id resid.ResId) string {
|
||||||
|
legacyNoNamespace := "~X"
|
||||||
|
legacyNoName := "~N"
|
||||||
|
legacySeparator := "|"
|
||||||
|
|
||||||
|
ns := id.Namespace
|
||||||
|
if ns == "" {
|
||||||
|
ns = legacyNoNamespace
|
||||||
|
}
|
||||||
|
nm := id.Name
|
||||||
|
if nm == "" {
|
||||||
|
nm = legacyNoName
|
||||||
|
}
|
||||||
|
return strings.Join(
|
||||||
|
[]string{id.Gvk.String(), ns, nm}, legacySeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO NOT CHANGE!
|
||||||
|
// Final legacy ordering provided as a default by kustomize.
|
||||||
|
// Originally an attempt to apply resources in the correct order, an effort
|
||||||
|
// which later proved impossible as not all types are known beforehand.
|
||||||
|
// See: https://github.com/kubernetes-sigs/kustomize/issues/3913
|
||||||
|
var defaultOrderFirst = []string{ //nolint:gochecknoglobals
|
||||||
|
"Namespace",
|
||||||
|
"ResourceQuota",
|
||||||
|
"StorageClass",
|
||||||
|
"CustomResourceDefinition",
|
||||||
|
"ServiceAccount",
|
||||||
|
"PodSecurityPolicy",
|
||||||
|
"Role",
|
||||||
|
"ClusterRole",
|
||||||
|
"RoleBinding",
|
||||||
|
"ClusterRoleBinding",
|
||||||
|
"ConfigMap",
|
||||||
|
"Secret",
|
||||||
|
"Endpoints",
|
||||||
|
"Service",
|
||||||
|
"LimitRange",
|
||||||
|
"PriorityClass",
|
||||||
|
"PersistentVolume",
|
||||||
|
"PersistentVolumeClaim",
|
||||||
|
"Deployment",
|
||||||
|
"StatefulSet",
|
||||||
|
"CronJob",
|
||||||
|
"PodDisruptionBudget",
|
||||||
|
}
|
||||||
|
var defaultOrderLast = []string{ //nolint:gochecknoglobals
|
||||||
|
"MutatingWebhookConfiguration",
|
||||||
|
"ValidatingWebhookConfiguration",
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSortOrderTransformerPlugin() resmap.TransformerPlugin {
|
||||||
|
return &SortOrderTransformerPlugin{}
|
||||||
|
}
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on SuffixTransformer; DO NOT EDIT.
|
// Code generated by pluginator on SuffixTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
// Code generated by pluginator on ValueAddTransformer; DO NOT EDIT.
|
// Code generated by pluginator on ValueAddTransformer; DO NOT EDIT.
|
||||||
// pluginator {unknown 1970-01-01T00:00:00Z }
|
|
||||||
|
|
||||||
package builtins
|
package builtins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
. "sigs.k8s.io/kustomize/api/internal/generators"
|
. "sigs.k8s.io/kustomize/api/internal/generators"
|
||||||
"sigs.k8s.io/kustomize/api/kv"
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
"sigs.k8s.io/kustomize/api/loader"
|
"sigs.k8s.io/kustomize/api/pkg/loader"
|
||||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
. "sigs.k8s.io/kustomize/api/internal/generators"
|
. "sigs.k8s.io/kustomize/api/internal/generators"
|
||||||
"sigs.k8s.io/kustomize/api/kv"
|
"sigs.k8s.io/kustomize/api/kv"
|
||||||
"sigs.k8s.io/kustomize/api/loader"
|
"sigs.k8s.io/kustomize/api/pkg/loader"
|
||||||
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
valtest_test "sigs.k8s.io/kustomize/api/testutils/valtest"
|
||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ package generators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"sigs.k8s.io/kustomize/api/ifc"
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
@@ -95,3 +97,28 @@ func setImmutable(
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseFileSource parses the source given.
|
||||||
|
//
|
||||||
|
// Acceptable formats include:
|
||||||
|
// 1. source-path: the basename will become the key name
|
||||||
|
// 2. source-name=source-path: the source-name will become the key name and
|
||||||
|
// source-path is the path to the key file.
|
||||||
|
//
|
||||||
|
// Key names cannot include '='.
|
||||||
|
func ParseFileSource(source string) (keyName, filePath string, err error) {
|
||||||
|
numSeparators := strings.Count(source, "=")
|
||||||
|
switch {
|
||||||
|
case numSeparators == 0:
|
||||||
|
return path.Base(source), source, nil
|
||||||
|
case numSeparators == 1 && strings.HasPrefix(source, "="):
|
||||||
|
return "", "", errors.Errorf("missing key name for file path %q in source %q", strings.TrimPrefix(source, "="), source)
|
||||||
|
case numSeparators == 1 && strings.HasSuffix(source, "="):
|
||||||
|
return "", "", errors.Errorf("missing file path for key name %q in source %q", strings.TrimSuffix(source, "="), source)
|
||||||
|
case numSeparators > 1:
|
||||||
|
return "", "", errors.Errorf("source %q key name or file path contains '='", source)
|
||||||
|
default:
|
||||||
|
components := strings.Split(source, "=")
|
||||||
|
return components[0], components[1], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
51
api/internal/generators/utils_test.go
Normal file
51
api/internal/generators/utils_test.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package generators_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/generators"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseFileSource(t *testing.T) {
|
||||||
|
tests := map[string]*struct {
|
||||||
|
Input string
|
||||||
|
Error string
|
||||||
|
Key string
|
||||||
|
Filename string
|
||||||
|
}{
|
||||||
|
"filename only": {
|
||||||
|
Input: "./path/myfile",
|
||||||
|
Key: "myfile",
|
||||||
|
Filename: "./path/myfile",
|
||||||
|
},
|
||||||
|
"key and filename": {
|
||||||
|
Input: "newName.ini=oldName",
|
||||||
|
Key: "newName.ini",
|
||||||
|
Filename: "oldName",
|
||||||
|
},
|
||||||
|
"multiple =": {
|
||||||
|
Input: "newName.ini==oldName",
|
||||||
|
Error: `source "newName.ini==oldName" key name or file path contains '='`,
|
||||||
|
},
|
||||||
|
"missing key": {
|
||||||
|
Input: "=myfile",
|
||||||
|
Error: `missing key name for file path "myfile" in source "=myfile"`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
key, file, err := ParseFileSource(test.Input)
|
||||||
|
if test.Error != "" {
|
||||||
|
require.EqualError(t, err, test.Error)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, test.Key, key)
|
||||||
|
require.Equal(t, test.Filename, file)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,15 +22,17 @@ func ClonerUsingGitExec(repoSpec *RepoSpec) error {
|
|||||||
if err = r.run("init"); err != nil {
|
if err = r.run("init"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = r.run(
|
// git relative submodule need origin, see https://github.com/kubernetes-sigs/kustomize/issues/5131
|
||||||
"remote", "add", "origin", repoSpec.CloneSpec()); err != nil {
|
if err = r.run("remote", "add", "origin", repoSpec.CloneSpec()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ref := "HEAD"
|
ref := "HEAD"
|
||||||
if repoSpec.Ref != "" {
|
if repoSpec.Ref != "" {
|
||||||
ref = repoSpec.Ref
|
ref = repoSpec.Ref
|
||||||
}
|
}
|
||||||
if err = r.run("fetch", "--depth=1", "origin", ref); err != nil {
|
// we use repoSpec.CloneSpec() instead of origin because on error,
|
||||||
|
// the prior prints the actual repo url for the user.
|
||||||
|
if err = r.run("fetch", "--depth=1", repoSpec.CloneSpec(), ref); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = r.run("checkout", "FETCH_HEAD"); err != nil {
|
if err = r.run("checkout", "FETCH_HEAD"); err != nil {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"sigs.k8s.io/kustomize/api/internal/utils"
|
"sigs.k8s.io/kustomize/api/internal/utils"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ type gitRunner struct {
|
|||||||
func newCmdRunner(timeout time.Duration) (*gitRunner, error) {
|
func newCmdRunner(timeout time.Duration) (*gitRunner, error) {
|
||||||
gitProgram, err := exec.LookPath("git")
|
gitProgram, err := exec.LookPath("git")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "no 'git' program on path")
|
return nil, errors.WrapPrefixf(err, "no 'git' program on path")
|
||||||
}
|
}
|
||||||
dir, err := filesys.NewTmpConfirmedDir()
|
dir, err := filesys.NewTmpConfirmedDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -46,9 +46,9 @@ func (r gitRunner) run(args ...string) error {
|
|||||||
cmd.String(),
|
cmd.String(),
|
||||||
r.duration,
|
r.duration,
|
||||||
func() error {
|
func() error {
|
||||||
_, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "git cmd = '%s'", cmd.String())
|
return errors.WrapPrefixf(err, "failed to run '%s': %s", cmd.String(), string(out))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ package git
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,26 +30,23 @@ type RepoSpec struct {
|
|||||||
// TODO(monopole): Drop raw, use processed fields instead.
|
// TODO(monopole): Drop raw, use processed fields instead.
|
||||||
raw string
|
raw string
|
||||||
|
|
||||||
// Host, e.g. github.com
|
// Host, e.g. https://github.com/
|
||||||
Host string
|
Host string
|
||||||
|
|
||||||
// orgRepo name (organization/repoName),
|
// RepoPath name (Path to repository),
|
||||||
// e.g. kubernetes-sigs/kustomize
|
// e.g. kubernetes-sigs/kustomize
|
||||||
OrgRepo string
|
RepoPath string
|
||||||
|
|
||||||
// Dir where the orgRepo is cloned to.
|
// Dir is where the repository is cloned to.
|
||||||
Dir filesys.ConfirmedDir
|
Dir filesys.ConfirmedDir
|
||||||
|
|
||||||
// Relative path in the repository, and in the cloneDir,
|
// Relative path in the repository, and in the cloneDir,
|
||||||
// to a Kustomization.
|
// to a Kustomization.
|
||||||
Path string
|
KustRootPath string
|
||||||
|
|
||||||
// Branch or tag reference.
|
// Branch or tag reference.
|
||||||
Ref string
|
Ref string
|
||||||
|
|
||||||
// e.g. .git or empty in case of _git is present
|
|
||||||
GitSuffix string
|
|
||||||
|
|
||||||
// Submodules indicates whether or not to clone git submodules.
|
// Submodules indicates whether or not to clone git submodules.
|
||||||
Submodules bool
|
Submodules bool
|
||||||
|
|
||||||
@@ -56,10 +56,7 @@ type RepoSpec struct {
|
|||||||
|
|
||||||
// CloneSpec returns a string suitable for "git clone {spec}".
|
// CloneSpec returns a string suitable for "git clone {spec}".
|
||||||
func (x *RepoSpec) CloneSpec() string {
|
func (x *RepoSpec) CloneSpec() string {
|
||||||
if isAzureHost(x.Host) || isAWSHost(x.Host) {
|
return x.Host + x.RepoPath
|
||||||
return x.Host + x.OrgRepo
|
|
||||||
}
|
|
||||||
return x.Host + x.OrgRepo + x.GitSuffix
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RepoSpec) CloneDir() filesys.ConfirmedDir {
|
func (x *RepoSpec) CloneDir() filesys.ConfirmedDir {
|
||||||
@@ -71,81 +68,140 @@ func (x *RepoSpec) Raw() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (x *RepoSpec) AbsPath() string {
|
func (x *RepoSpec) AbsPath() string {
|
||||||
return x.Dir.Join(x.Path)
|
return x.Dir.Join(x.KustRootPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *RepoSpec) Cleaner(fSys filesys.FileSystem) func() error {
|
func (x *RepoSpec) Cleaner(fSys filesys.FileSystem) func() error {
|
||||||
return func() error { return fSys.RemoveAll(x.Dir.String()) }
|
return func() error { return fSys.RemoveAll(x.Dir.String()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
refQuery = "?ref="
|
||||||
|
gitSuffix = ".git"
|
||||||
|
gitRootDelimiter = "_git/"
|
||||||
|
pathSeparator = "/" // do not use filepath.Separator, as this is a URL
|
||||||
|
)
|
||||||
|
|
||||||
// NewRepoSpecFromURL parses git-like urls.
|
// NewRepoSpecFromURL parses git-like urls.
|
||||||
// From strings like git@github.com:someOrg/someRepo.git or
|
// From strings like git@github.com:someOrg/someRepo.git or
|
||||||
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
||||||
// the parts.
|
// the different parts of URL, set into a RepoSpec object and return RepoSpec object.
|
||||||
|
// It MUST return an error if the input is not a git-like URL, as this is used by some code paths
|
||||||
|
// to distinguish between local and remote paths.
|
||||||
|
//
|
||||||
|
// In particular, NewRepoSpecFromURL separates the URL used to clone the repo from the
|
||||||
|
// elements Kustomize uses for other purposes (e.g. query params that turn into args, and
|
||||||
|
// the path to the kustomization root within the repo).
|
||||||
func NewRepoSpecFromURL(n string) (*RepoSpec, error) {
|
func NewRepoSpecFromURL(n string) (*RepoSpec, error) {
|
||||||
|
repoSpec := &RepoSpec{raw: n, Dir: notCloned, Timeout: defaultTimeout, Submodules: defaultSubmodules}
|
||||||
if filepath.IsAbs(n) {
|
if filepath.IsAbs(n) {
|
||||||
return nil, fmt.Errorf("uri looks like abs path: %s", n)
|
return nil, fmt.Errorf("uri looks like abs path: %s", n)
|
||||||
}
|
}
|
||||||
host, orgRepo, path, gitRef, gitSubmodules, suffix, gitTimeout := parseGitURL(n)
|
|
||||||
if orgRepo == "" {
|
// Parse the query first. This is safe because according to rfc3986 "?" is only allowed in the
|
||||||
return nil, fmt.Errorf("url lacks orgRepo: %s", n)
|
// query and is not recognized %-encoded.
|
||||||
|
// Note that parseQuery returns default values for empty parameters.
|
||||||
|
n, query, _ := strings.Cut(n, "?")
|
||||||
|
repoSpec.Ref, repoSpec.Timeout, repoSpec.Submodules = parseQuery(query)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parse the host (e.g. scheme, username, domain) segment.
|
||||||
|
repoSpec.Host, n, err = extractHost(n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if host == "" {
|
|
||||||
return nil, fmt.Errorf("url lacks host: %s", n)
|
// In some cases, we're given a path to a git repo + a path to the kustomization root within
|
||||||
|
// that repo. We need to split them so that we can ultimately give the repo only to the cloner.
|
||||||
|
repoSpec.RepoPath, repoSpec.KustRootPath, err = parsePathParts(n, defaultRepoPathLength(repoSpec.Host))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return &RepoSpec{
|
|
||||||
raw: n, Host: host, OrgRepo: orgRepo,
|
return repoSpec, nil
|
||||||
Dir: notCloned, Path: path, Ref: gitRef, GitSuffix: suffix,
|
|
||||||
Submodules: gitSubmodules, Timeout: gitTimeout}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const allSegments = -999999
|
||||||
refQuery = "?ref="
|
const orgRepoSegments = 2
|
||||||
gitSuffix = ".git"
|
|
||||||
gitDelimiter = "_git/"
|
|
||||||
)
|
|
||||||
|
|
||||||
// From strings like git@github.com:someOrg/someRepo.git or
|
func defaultRepoPathLength(host string) int {
|
||||||
// https://github.com/someOrg/someRepo?ref=someHash, extract
|
if strings.HasPrefix(host, fileScheme) {
|
||||||
// the parts.
|
return allSegments
|
||||||
func parseGitURL(n string) (
|
|
||||||
host string, orgRepo string, path string, gitRef string, gitSubmodules bool, gitSuff string, gitTimeout time.Duration) {
|
|
||||||
if strings.Contains(n, gitDelimiter) {
|
|
||||||
index := strings.Index(n, gitDelimiter)
|
|
||||||
// Adding _git/ to host
|
|
||||||
host = normalizeGitHostSpec(n[:index+len(gitDelimiter)])
|
|
||||||
orgRepo = strings.Split(strings.Split(n[index+len(gitDelimiter):], "/")[0], "?")[0]
|
|
||||||
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n[index+len(gitDelimiter)+len(orgRepo):])
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
host, n = parseHostSpec(n)
|
return orgRepoSegments
|
||||||
gitSuff = gitSuffix
|
}
|
||||||
if strings.Contains(n, gitSuffix) {
|
|
||||||
index := strings.Index(n, gitSuffix)
|
// parsePathParts splits the repo path that will ultimately be passed to git to clone the
|
||||||
orgRepo = n[0:index]
|
// repo from the kustomization root path, which Kustomize will execute the build in after the repo
|
||||||
n = n[index+len(gitSuffix):]
|
// is cloned.
|
||||||
if len(n) > 0 && n[0] == '/' {
|
//
|
||||||
n = n[1:]
|
// We first try to do this based on explicit markers in the URL (e.g. _git, .git or //).
|
||||||
}
|
// If none are present, we try to apply a historical default repo path length that is derived from
|
||||||
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
|
// Github URLs. If there aren't enough segments, we have historically considered the URL invalid.
|
||||||
return
|
func parsePathParts(n string, defaultSegmentLength int) (string, string, error) {
|
||||||
|
repoPath, kustRootPath, success := tryExplicitMarkerSplit(n)
|
||||||
|
if !success {
|
||||||
|
repoPath, kustRootPath, success = tryDefaultLengthSplit(n, defaultSegmentLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
i := strings.Index(n, "/")
|
// Validate the result
|
||||||
if i < 1 {
|
if !success || len(repoPath) == 0 {
|
||||||
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
|
return "", "", fmt.Errorf("failed to parse repo path segment")
|
||||||
return
|
|
||||||
}
|
}
|
||||||
j := strings.Index(n[i+1:], "/")
|
if kustRootPathExitsRepo(kustRootPath) {
|
||||||
if j >= 0 {
|
return "", "", fmt.Errorf("url path exits repo: %s", n)
|
||||||
j += i + 1
|
|
||||||
orgRepo = n[:j]
|
|
||||||
path, gitRef, gitTimeout, gitSubmodules = peelQuery(n[j+1:])
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
path = ""
|
|
||||||
orgRepo, gitRef, gitTimeout, gitSubmodules = peelQuery(n)
|
return repoPath, strings.TrimPrefix(kustRootPath, pathSeparator), nil
|
||||||
return host, orgRepo, path, gitRef, gitSubmodules, gitSuff, gitTimeout
|
}
|
||||||
|
|
||||||
|
func tryExplicitMarkerSplit(n string) (string, string, bool) {
|
||||||
|
// Look for the _git delimiter, which by convention is expected to be ONE directory above the repo root.
|
||||||
|
// If found, split on the NEXT path element, which is the repo root.
|
||||||
|
// Example: https://username@dev.azure.com/org/project/_git/repo/path/to/kustomization/root
|
||||||
|
if gitRootIdx := strings.Index(n, gitRootDelimiter); gitRootIdx >= 0 {
|
||||||
|
gitRootPath := n[:gitRootIdx+len(gitRootDelimiter)]
|
||||||
|
subpathSegments := strings.Split(n[gitRootIdx+len(gitRootDelimiter):], pathSeparator)
|
||||||
|
return gitRootPath + subpathSegments[0], strings.Join(subpathSegments[1:], pathSeparator), true
|
||||||
|
|
||||||
|
// Look for a double-slash in the path, which if present separates the repo root from the kust path.
|
||||||
|
// It is a convention, not a real path element, so do not preserve it in the returned value.
|
||||||
|
// Example: https://github.com/org/repo//path/to/kustomozation/root
|
||||||
|
} else if repoRootIdx := strings.Index(n, "//"); repoRootIdx >= 0 {
|
||||||
|
return n[:repoRootIdx], n[repoRootIdx+2:], true
|
||||||
|
|
||||||
|
// Look for .git in the path, which if present is part of the directory name of the git repo.
|
||||||
|
// This means we want to grab everything up to and including that suffix
|
||||||
|
// Example: https://github.com/org/repo.git/path/to/kustomozation/root
|
||||||
|
} else if gitSuffixIdx := strings.Index(n, gitSuffix); gitSuffixIdx >= 0 {
|
||||||
|
upToGitSuffix := n[:gitSuffixIdx+len(gitSuffix)]
|
||||||
|
afterGitSuffix := n[gitSuffixIdx+len(gitSuffix):]
|
||||||
|
return upToGitSuffix, afterGitSuffix, true
|
||||||
|
}
|
||||||
|
return "", "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func tryDefaultLengthSplit(n string, defaultSegmentLength int) (string, string, bool) {
|
||||||
|
// If the default is to take all segments, do so.
|
||||||
|
if defaultSegmentLength == allSegments {
|
||||||
|
return n, "", true
|
||||||
|
|
||||||
|
// If the default is N segments, make sure we have at least that many and take them if so.
|
||||||
|
// If we have less than N, we have historically considered the URL invalid.
|
||||||
|
} else if segments := strings.Split(n, pathSeparator); len(segments) >= defaultSegmentLength {
|
||||||
|
firstNSegments := strings.Join(segments[:defaultSegmentLength], pathSeparator)
|
||||||
|
rest := strings.Join(segments[defaultSegmentLength:], pathSeparator)
|
||||||
|
return firstNSegments, rest, true
|
||||||
|
}
|
||||||
|
return "", "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func kustRootPathExitsRepo(kustRootPath string) bool {
|
||||||
|
cleanedPath := filepath.Clean(strings.TrimPrefix(kustRootPath, string(filepath.Separator)))
|
||||||
|
pathElements := strings.Split(cleanedPath, string(filepath.Separator))
|
||||||
|
return len(pathElements) > 0 &&
|
||||||
|
pathElements[0] == filesys.ParentDir
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone git submodules by default.
|
// Clone git submodules by default.
|
||||||
@@ -154,14 +210,12 @@ const defaultSubmodules = true
|
|||||||
// Arbitrary, but non-infinite, timeout for running commands.
|
// Arbitrary, but non-infinite, timeout for running commands.
|
||||||
const defaultTimeout = 27 * time.Second
|
const defaultTimeout = 27 * time.Second
|
||||||
|
|
||||||
func peelQuery(arg string) (string, string, time.Duration, bool) {
|
func parseQuery(query string) (string, time.Duration, bool) {
|
||||||
// Parse the given arg into a URL. In the event of a parse failure, return
|
values, err := url.ParseQuery(query)
|
||||||
// our defaults.
|
// in event of parse failure, return defaults
|
||||||
parsed, err := url.Parse(arg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return arg, "", defaultTimeout, defaultSubmodules
|
return "", defaultTimeout, defaultSubmodules
|
||||||
}
|
}
|
||||||
values := parsed.Query()
|
|
||||||
|
|
||||||
// ref is the desired git ref to target. Can be specified by in a git URL
|
// ref is the desired git ref to target. Can be specified by in a git URL
|
||||||
// with ?ref=<string> or ?version=<string>, although ref takes precedence.
|
// with ?ref=<string> or ?version=<string>, although ref takes precedence.
|
||||||
@@ -192,76 +246,142 @@ func peelQuery(arg string) (string, string, time.Duration, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed.Path, ref, duration, submodules
|
return ref, duration, submodules
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseHostSpec(n string) (string, string) {
|
func extractHost(n string) (string, string, error) {
|
||||||
var host string
|
n = ignoreForcedGitProtocol(n)
|
||||||
// Start accumulating the host part.
|
scheme, n := extractScheme(n)
|
||||||
for _, p := range []string{
|
username, n := extractUsername(n)
|
||||||
// Order matters here.
|
stdGithub := isStandardGithubHost(n)
|
||||||
"git::", "gh:", "ssh://", "https://", "http://",
|
acceptSCP := acceptSCPStyle(scheme, username, stdGithub)
|
||||||
"git@", "github.com:", "github.com/"} {
|
|
||||||
if len(p) < len(n) && strings.ToLower(n[:len(p)]) == p {
|
// Validate the username and scheme before attempting host/path parsing, because if the parsing
|
||||||
n = n[len(p):]
|
// so far has not succeeded, we will not be able to extract the host and path correctly.
|
||||||
host += p
|
if err := validateScheme(scheme, acceptSCP); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have extracted a valid scheme+username, we can parse host itself.
|
||||||
|
|
||||||
|
// The file protocol specifies an absolute path to a local git repo.
|
||||||
|
// Everything after the scheme (including any 'username' we found) is actually part of that path.
|
||||||
|
if scheme == fileScheme {
|
||||||
|
return scheme, username + n, nil
|
||||||
|
}
|
||||||
|
var host, rest = n, ""
|
||||||
|
if sepIndex := findPathSeparator(n, acceptSCP); sepIndex >= 0 {
|
||||||
|
host, rest = n[:sepIndex+1], n[sepIndex+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Github URLs are strictly normalized in a way that may discard scheme and username components.
|
||||||
|
if stdGithub {
|
||||||
|
scheme, username, host = normalizeGithubHostParts(scheme, username)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host is required, so do not concat the scheme and username if we didn't find one.
|
||||||
|
if host == "" {
|
||||||
|
return "", "", errors.Errorf("failed to parse host segment")
|
||||||
|
}
|
||||||
|
return scheme + username + host, rest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignoreForcedGitProtocol strips the "git::" prefix from URLs.
|
||||||
|
// We used to use go-getter to handle our urls: https://github.com/hashicorp/go-getter.
|
||||||
|
// The git:: prefix signaled go-getter to use the git protocol to fetch the url's contents.
|
||||||
|
// We silently strip this prefix to allow these go-getter-style urls to continue to work,
|
||||||
|
// although the git protocol (which is insecure and unsupported on many platforms, including Github)
|
||||||
|
// will not actually be used as intended.
|
||||||
|
func ignoreForcedGitProtocol(n string) string {
|
||||||
|
n, found := trimPrefixIgnoreCase(n, "git::")
|
||||||
|
if found {
|
||||||
|
log.Println("Warning: Forcing the git protocol using the 'git::' URL prefix is not supported. " +
|
||||||
|
"Kustomize currently strips this invalid prefix, but will stop doing so in a future release. " +
|
||||||
|
"Please remove the 'git::' prefix from your configuration.")
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptSCPStyle returns true if the scheme and username indicate potential use of an SCP-style URL.
|
||||||
|
// With this style, the scheme is not explicit and the path is delimited by a colon.
|
||||||
|
// Strictly speaking the username is optional in SCP-like syntax, but Kustomize has always
|
||||||
|
// required it for non-Github URLs.
|
||||||
|
// Example: user@host.xz:path/to/repo.git/
|
||||||
|
func acceptSCPStyle(scheme, username string, isGithubURL bool) bool {
|
||||||
|
return scheme == "" && (username != "" || isGithubURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateScheme(scheme string, acceptSCPStyle bool) error {
|
||||||
|
// see https://git-scm.com/docs/git-fetch#_git_urls for info relevant to these validations
|
||||||
|
switch scheme {
|
||||||
|
case "":
|
||||||
|
// Empty scheme is only ok if it's a Github URL or if it looks like SCP-style syntax
|
||||||
|
if !acceptSCPStyle {
|
||||||
|
return fmt.Errorf("failed to parse scheme")
|
||||||
|
}
|
||||||
|
case sshScheme, fileScheme, httpsScheme, httpScheme:
|
||||||
|
// These are all supported schemes
|
||||||
|
default:
|
||||||
|
// At time of writing, we should never end up here because we do not parse out
|
||||||
|
// unsupported schemes to begin with.
|
||||||
|
return fmt.Errorf("unsupported scheme %q", scheme)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileScheme = "file://"
|
||||||
|
const httpScheme = "http://"
|
||||||
|
const httpsScheme = "https://"
|
||||||
|
const sshScheme = "ssh://"
|
||||||
|
|
||||||
|
func extractScheme(s string) (string, string) {
|
||||||
|
for _, prefix := range []string{sshScheme, httpsScheme, httpScheme, fileScheme} {
|
||||||
|
if rest, found := trimPrefixIgnoreCase(s, prefix); found {
|
||||||
|
return prefix, rest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if host == "git@" {
|
return "", s
|
||||||
i := strings.Index(n, "/")
|
}
|
||||||
if i > -1 {
|
|
||||||
host += n[:i+1]
|
|
||||||
n = n[i+1:]
|
|
||||||
} else {
|
|
||||||
i = strings.Index(n, ":")
|
|
||||||
if i > -1 {
|
|
||||||
host += n[:i+1]
|
|
||||||
n = n[i+1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return host, n
|
|
||||||
}
|
|
||||||
|
|
||||||
// If host is a http(s) or ssh URL, grab the domain part.
|
func extractUsername(s string) (string, string) {
|
||||||
for _, p := range []string{
|
var userRegexp = regexp.MustCompile(`^([a-zA-Z][a-zA-Z0-9-]*)@`)
|
||||||
"ssh://", "https://", "http://"} {
|
if m := userRegexp.FindStringSubmatch(s); m != nil {
|
||||||
if strings.HasSuffix(host, p) {
|
username := m[1] + "@"
|
||||||
i := strings.Index(n, "/")
|
return username, s[len(username):]
|
||||||
if i > -1 {
|
}
|
||||||
host += n[0 : i+1]
|
return "", s
|
||||||
n = n[i+1:]
|
}
|
||||||
}
|
|
||||||
break
|
func isStandardGithubHost(s string) bool {
|
||||||
|
lowerCased := strings.ToLower(s)
|
||||||
|
return strings.HasPrefix(lowerCased, "github.com/") ||
|
||||||
|
strings.HasPrefix(lowerCased, "github.com:")
|
||||||
|
}
|
||||||
|
|
||||||
|
// trimPrefixIgnoreCase returns the rest of s and true if prefix, ignoring case, prefixes s.
|
||||||
|
// Otherwise, trimPrefixIgnoreCase returns s and false.
|
||||||
|
func trimPrefixIgnoreCase(s, prefix string) (string, bool) {
|
||||||
|
if len(prefix) <= len(s) && strings.ToLower(s[:len(prefix)]) == prefix {
|
||||||
|
return s[len(prefix):], true
|
||||||
|
}
|
||||||
|
return s, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPathSeparator(hostPath string, acceptSCP bool) int {
|
||||||
|
sepIndex := strings.Index(hostPath, pathSeparator)
|
||||||
|
if acceptSCP {
|
||||||
|
colonIndex := strings.Index(hostPath, ":")
|
||||||
|
// The colon acts as a delimiter in scp-style ssh URLs only if not prefixed by '/'.
|
||||||
|
if sepIndex == -1 || (colonIndex > 0 && colonIndex < sepIndex) {
|
||||||
|
sepIndex = colonIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return sepIndex
|
||||||
return normalizeGitHostSpec(host), n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizeGitHostSpec(host string) string {
|
func normalizeGithubHostParts(scheme, username string) (string, string, string) {
|
||||||
s := strings.ToLower(host)
|
if strings.HasPrefix(scheme, sshScheme) || username != "" {
|
||||||
if strings.Contains(s, "github.com") {
|
return "", username, "github.com:"
|
||||||
if strings.Contains(s, "git@") || strings.Contains(s, "ssh:") {
|
|
||||||
host = "git@github.com:"
|
|
||||||
} else {
|
|
||||||
host = "https://github.com/"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(s, "git::") {
|
return httpsScheme, "", "github.com/"
|
||||||
host = strings.TrimPrefix(s, "git::")
|
|
||||||
}
|
|
||||||
return host
|
|
||||||
}
|
|
||||||
|
|
||||||
// The format of Azure repo URL is documented
|
|
||||||
// https://docs.microsoft.com/en-us/azure/devops/repos/git/clone?view=vsts&tabs=visual-studio#clone_url
|
|
||||||
func isAzureHost(host string) bool {
|
|
||||||
return strings.Contains(host, "dev.azure.com") ||
|
|
||||||
strings.Contains(host, "visualstudio.com")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The format of AWS repo URL is documented
|
|
||||||
// https://docs.aws.amazon.com/codecommit/latest/userguide/regions.html
|
|
||||||
func isAWSHost(host string) bool {
|
|
||||||
return strings.Contains(host, "amazonaws.com")
|
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,13 @@
|
|||||||
// Copyright 2020 The Kubernetes Authors.
|
// Copyright 2020 The Kubernetes Authors.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package image
|
package image_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/image"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsImageMatched(t *testing.T) {
|
func TestIsImageMatched(t *testing.T) {
|
||||||
@@ -50,7 +51,7 @@ func TestIsImageMatched(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.testName, func(t *testing.T) {
|
t.Run(tc.testName, func(t *testing.T) {
|
||||||
assert.Equal(t, tc.isMatched, IsImageMatched(tc.value, tc.name))
|
assert.Equal(t, tc.isMatched, image.IsImageMatched(tc.value, tc.name))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +117,7 @@ func TestSplit(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.testName, func(t *testing.T) {
|
t.Run(tc.testName, func(t *testing.T) {
|
||||||
name, tag, digest := Split(tc.value)
|
name, tag, digest := image.Split(tc.value)
|
||||||
assert.Equal(t, tc.name, name)
|
assert.Equal(t, tc.name, name)
|
||||||
assert.Equal(t, tc.tag, tag)
|
assert.Equal(t, tc.tag, tag)
|
||||||
assert.Equal(t, tc.digest, digest)
|
assert.Equal(t, tc.digest, digest)
|
||||||
@@ -5,9 +5,6 @@ package builtinpluginconsts
|
|||||||
|
|
||||||
const commonLabelFieldSpecs = `
|
const commonLabelFieldSpecs = `
|
||||||
commonLabels:
|
commonLabels:
|
||||||
- path: metadata/labels
|
|
||||||
create: true
|
|
||||||
|
|
||||||
- path: spec/selector
|
- path: spec/selector
|
||||||
create: true
|
create: true
|
||||||
version: v1
|
version: v1
|
||||||
@@ -17,20 +14,10 @@ commonLabels:
|
|||||||
create: true
|
create: true
|
||||||
version: v1
|
version: v1
|
||||||
kind: ReplicationController
|
kind: ReplicationController
|
||||||
|
|
||||||
- path: spec/template/metadata/labels
|
|
||||||
create: true
|
|
||||||
version: v1
|
|
||||||
kind: ReplicationController
|
|
||||||
|
|
||||||
- path: spec/selector/matchLabels
|
- path: spec/selector/matchLabels
|
||||||
create: true
|
create: true
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
|
|
||||||
- path: spec/template/metadata/labels
|
|
||||||
create: true
|
|
||||||
kind: Deployment
|
|
||||||
|
|
||||||
- path: spec/template/spec/affinity/podAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels
|
- path: spec/template/spec/affinity/podAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels
|
||||||
create: false
|
create: false
|
||||||
group: apps
|
group: apps
|
||||||
@@ -60,28 +47,15 @@ commonLabels:
|
|||||||
create: true
|
create: true
|
||||||
kind: ReplicaSet
|
kind: ReplicaSet
|
||||||
|
|
||||||
- path: spec/template/metadata/labels
|
|
||||||
create: true
|
|
||||||
kind: ReplicaSet
|
|
||||||
|
|
||||||
- path: spec/selector/matchLabels
|
- path: spec/selector/matchLabels
|
||||||
create: true
|
create: true
|
||||||
kind: DaemonSet
|
kind: DaemonSet
|
||||||
|
|
||||||
- path: spec/template/metadata/labels
|
|
||||||
create: true
|
|
||||||
kind: DaemonSet
|
|
||||||
|
|
||||||
- path: spec/selector/matchLabels
|
- path: spec/selector/matchLabels
|
||||||
create: true
|
create: true
|
||||||
group: apps
|
group: apps
|
||||||
kind: StatefulSet
|
kind: StatefulSet
|
||||||
|
|
||||||
- path: spec/template/metadata/labels
|
|
||||||
create: true
|
|
||||||
group: apps
|
|
||||||
kind: StatefulSet
|
|
||||||
|
|
||||||
- path: spec/template/spec/affinity/podAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels
|
- path: spec/template/spec/affinity/podAffinity/preferredDuringSchedulingIgnoredDuringExecution/podAffinityTerm/labelSelector/matchLabels
|
||||||
create: false
|
create: false
|
||||||
group: apps
|
group: apps
|
||||||
@@ -107,36 +81,16 @@ commonLabels:
|
|||||||
group: apps
|
group: apps
|
||||||
kind: StatefulSet
|
kind: StatefulSet
|
||||||
|
|
||||||
- path: spec/volumeClaimTemplates[]/metadata/labels
|
|
||||||
create: true
|
|
||||||
group: apps
|
|
||||||
kind: StatefulSet
|
|
||||||
|
|
||||||
- path: spec/selector/matchLabels
|
- path: spec/selector/matchLabels
|
||||||
create: false
|
create: false
|
||||||
group: batch
|
group: batch
|
||||||
kind: Job
|
kind: Job
|
||||||
|
|
||||||
- path: spec/template/metadata/labels
|
|
||||||
create: true
|
|
||||||
group: batch
|
|
||||||
kind: Job
|
|
||||||
|
|
||||||
- path: spec/jobTemplate/spec/selector/matchLabels
|
- path: spec/jobTemplate/spec/selector/matchLabels
|
||||||
create: false
|
create: false
|
||||||
group: batch
|
group: batch
|
||||||
kind: CronJob
|
kind: CronJob
|
||||||
|
|
||||||
- path: spec/jobTemplate/metadata/labels
|
|
||||||
create: true
|
|
||||||
group: batch
|
|
||||||
kind: CronJob
|
|
||||||
|
|
||||||
- path: spec/jobTemplate/spec/template/metadata/labels
|
|
||||||
create: true
|
|
||||||
group: batch
|
|
||||||
kind: CronJob
|
|
||||||
|
|
||||||
- path: spec/selector/matchLabels
|
- path: spec/selector/matchLabels
|
||||||
create: false
|
create: false
|
||||||
group: policy
|
group: policy
|
||||||
@@ -156,4 +110,4 @@ commonLabels:
|
|||||||
create: false
|
create: false
|
||||||
group: networking.k8s.io
|
group: networking.k8s.io
|
||||||
kind: NetworkPolicy
|
kind: NetworkPolicy
|
||||||
`
|
` + metadataLabelsFieldSpecs + volumeClaimTemplatesMetadataLabels
|
||||||
@@ -13,6 +13,8 @@ func GetDefaultFieldSpecs() []byte {
|
|||||||
[]byte(namePrefixFieldSpecs),
|
[]byte(namePrefixFieldSpecs),
|
||||||
[]byte(nameSuffixFieldSpecs),
|
[]byte(nameSuffixFieldSpecs),
|
||||||
[]byte(commonLabelFieldSpecs),
|
[]byte(commonLabelFieldSpecs),
|
||||||
|
[]byte(templateLabelFieldSpecs),
|
||||||
|
[]byte(volumeClaimTemplateLabelFieldSpecs),
|
||||||
[]byte(commonAnnotationFieldSpecs),
|
[]byte(commonAnnotationFieldSpecs),
|
||||||
[]byte(namespaceFieldSpecs),
|
[]byte(namespaceFieldSpecs),
|
||||||
[]byte(varReferenceFieldSpecs),
|
[]byte(varReferenceFieldSpecs),
|
||||||
@@ -30,6 +32,8 @@ func GetDefaultFieldSpecsAsMap() map[string]string {
|
|||||||
result["nameprefix"] = namePrefixFieldSpecs
|
result["nameprefix"] = namePrefixFieldSpecs
|
||||||
result["namesuffix"] = nameSuffixFieldSpecs
|
result["namesuffix"] = nameSuffixFieldSpecs
|
||||||
result["commonlabels"] = commonLabelFieldSpecs
|
result["commonlabels"] = commonLabelFieldSpecs
|
||||||
|
result["templatelabels"] = templateLabelFieldSpecs
|
||||||
|
result["volumeclaimtemplatelabels"] = volumeClaimTemplateLabelFieldSpecs
|
||||||
result["commonannotations"] = commonAnnotationFieldSpecs
|
result["commonannotations"] = commonAnnotationFieldSpecs
|
||||||
result["namespace"] = namespaceFieldSpecs
|
result["namespace"] = namespaceFieldSpecs
|
||||||
result["varreference"] = varReferenceFieldSpecs
|
result["varreference"] = varReferenceFieldSpecs
|
||||||
@@ -10,9 +10,13 @@ images:
|
|||||||
create: true
|
create: true
|
||||||
- path: spec/initContainers[]/image
|
- path: spec/initContainers[]/image
|
||||||
create: true
|
create: true
|
||||||
|
- path: spec/volumes[]/image/reference
|
||||||
|
create: true
|
||||||
- path: spec/template/spec/containers[]/image
|
- path: spec/template/spec/containers[]/image
|
||||||
create: true
|
create: true
|
||||||
- path: spec/template/spec/initContainers[]/image
|
- path: spec/template/spec/initContainers[]/image
|
||||||
create: true
|
create: true
|
||||||
|
- path: spec/template/spec/volumes[]/image/reference
|
||||||
|
create: true
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
46
api/internal/konfig/builtinpluginconsts/metadatalabels.go
Normal file
46
api/internal/konfig/builtinpluginconsts/metadatalabels.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinpluginconsts
|
||||||
|
|
||||||
|
const metadataLabelsFieldSpecs = `
|
||||||
|
- path: metadata/labels
|
||||||
|
create: true
|
||||||
|
|
||||||
|
- path: spec/template/metadata/labels
|
||||||
|
create: true
|
||||||
|
version: v1
|
||||||
|
kind: ReplicationController
|
||||||
|
|
||||||
|
- path: spec/template/metadata/labels
|
||||||
|
create: true
|
||||||
|
kind: Deployment
|
||||||
|
|
||||||
|
- path: spec/template/metadata/labels
|
||||||
|
create: true
|
||||||
|
kind: ReplicaSet
|
||||||
|
|
||||||
|
- path: spec/template/metadata/labels
|
||||||
|
create: true
|
||||||
|
kind: DaemonSet
|
||||||
|
|
||||||
|
- path: spec/template/metadata/labels
|
||||||
|
create: true
|
||||||
|
group: apps
|
||||||
|
kind: StatefulSet
|
||||||
|
|
||||||
|
- path: spec/template/metadata/labels
|
||||||
|
create: true
|
||||||
|
group: batch
|
||||||
|
kind: Job
|
||||||
|
|
||||||
|
- path: spec/jobTemplate/metadata/labels
|
||||||
|
create: true
|
||||||
|
group: batch
|
||||||
|
kind: CronJob
|
||||||
|
|
||||||
|
- path: spec/jobTemplate/spec/template/metadata/labels
|
||||||
|
create: true
|
||||||
|
group: batch
|
||||||
|
kind: CronJob
|
||||||
|
`
|
||||||
@@ -421,6 +421,13 @@ nameReference:
|
|||||||
fieldSpecs:
|
fieldSpecs:
|
||||||
- path: spec/ingressClassName
|
- path: spec/ingressClassName
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
|
|
||||||
|
- kind: ValidatingAdmissionPolicy
|
||||||
|
group: admissionregistration.k8s.io
|
||||||
|
fieldSpecs:
|
||||||
|
- path: spec/policyName
|
||||||
|
kind: ValidatingAdmissionPolicyBinding
|
||||||
|
group: admissionregistration.k8s.io
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinpluginconsts
|
||||||
|
|
||||||
|
const templateLabelFieldSpecs = `
|
||||||
|
templateLabels:
|
||||||
|
` + metadataLabelsFieldSpecs
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2019 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package builtinpluginconsts
|
||||||
|
|
||||||
|
const volumeClaimTemplateLabelFieldSpecs = `
|
||||||
|
volumeClaimTemplateLabels:
|
||||||
|
` + volumeClaimTemplatesMetadataLabels
|
||||||
|
|
||||||
|
const volumeClaimTemplatesMetadataLabels = `
|
||||||
|
- path: spec/volumeClaimTemplates[]/metadata/labels
|
||||||
|
create: true
|
||||||
|
group: apps
|
||||||
|
kind: StatefulSet
|
||||||
|
`
|
||||||
@@ -5,7 +5,7 @@ package loader
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -18,7 +18,14 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fileLoader is a kustomization's interface to files.
|
// IsRemoteFile returns whether path has a url scheme that kustomize allows for
|
||||||
|
// remote files. See https://github.com/kubernetes-sigs/kustomize/blob/master/examples/remoteBuild.md
|
||||||
|
func IsRemoteFile(path string) bool {
|
||||||
|
u, err := url.Parse(path)
|
||||||
|
return err == nil && (u.Scheme == "http" || u.Scheme == "https")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileLoader is a kustomization's interface to files.
|
||||||
//
|
//
|
||||||
// The directory in which a kustomization file sits
|
// The directory in which a kustomization file sits
|
||||||
// is referred to below as the kustomization's _root_.
|
// is referred to below as the kustomization's _root_.
|
||||||
@@ -31,49 +38,48 @@ import (
|
|||||||
//
|
//
|
||||||
// * supplemental data paths
|
// * supplemental data paths
|
||||||
//
|
//
|
||||||
// `Load` is used to visit these paths.
|
// `Load` is used to visit these paths.
|
||||||
//
|
//
|
||||||
// These paths refer to resources, patches,
|
// These paths refer to resources, patches,
|
||||||
// data for ConfigMaps and Secrets, etc.
|
// data for ConfigMaps and Secrets, etc.
|
||||||
//
|
//
|
||||||
// The loadRestrictor may disallow certain paths
|
// The loadRestrictor may disallow certain paths
|
||||||
// or classes of paths.
|
// or classes of paths.
|
||||||
//
|
//
|
||||||
// * bases (other kustomizations)
|
// * bases (other kustomizations)
|
||||||
//
|
//
|
||||||
// `New` is used to load bases.
|
// `New` is used to load bases.
|
||||||
//
|
//
|
||||||
// A base can be either a remote git repo URL, or
|
// A base can be either a remote git repo URL, or
|
||||||
// a directory specified relative to the current
|
// a directory specified relative to the current
|
||||||
// root. In the former case, the repo is locally
|
// root. In the former case, the repo is locally
|
||||||
// cloned, and the new loader is rooted on a path
|
// cloned, and the new loader is rooted on a path
|
||||||
// in that clone.
|
// in that clone.
|
||||||
//
|
//
|
||||||
// As loaders create new loaders, a root history
|
// As loaders create new loaders, a root history
|
||||||
// is established, and used to disallow:
|
// is established, and used to disallow:
|
||||||
//
|
//
|
||||||
// - A base that is a repository that, in turn,
|
// - A base that is a repository that, in turn,
|
||||||
// specifies a base repository seen previously
|
// specifies a base repository seen previously
|
||||||
// in the loading stack (a cycle).
|
// in the loading stack (a cycle).
|
||||||
//
|
//
|
||||||
// - An overlay depending on a base positioned at
|
// - An overlay depending on a base positioned at
|
||||||
// or above it. I.e. '../foo' is OK, but '.',
|
// or above it. I.e. '../foo' is OK, but '.',
|
||||||
// '..', '../..', etc. are disallowed. Allowing
|
// '..', '../..', etc. are disallowed. Allowing
|
||||||
// such a base has no advantages and encourages
|
// such a base has no advantages and encourages
|
||||||
// cycles, particularly if some future change
|
// cycles, particularly if some future change
|
||||||
// were to introduce globbing to file
|
// were to introduce globbing to file
|
||||||
// specifications in the kustomization file.
|
// specifications in the kustomization file.
|
||||||
//
|
//
|
||||||
// These restrictions assure that kustomizations
|
// These restrictions assure that kustomizations
|
||||||
// are self-contained and relocatable, and impose
|
// are self-contained and relocatable, and impose
|
||||||
// some safety when relying on remote kustomizations,
|
// some safety when relying on remote kustomizations,
|
||||||
// e.g. a remotely loaded ConfigMap generator specified
|
// e.g. a remotely loaded ConfigMap generator specified
|
||||||
// to read from /etc/passwd will fail.
|
// to read from /etc/passwd will fail.
|
||||||
//
|
type FileLoader struct {
|
||||||
type fileLoader struct {
|
|
||||||
// Loader that spawned this loader.
|
// Loader that spawned this loader.
|
||||||
// Used to avoid cycles.
|
// Used to avoid cycles.
|
||||||
referrer *fileLoader
|
referrer *FileLoader
|
||||||
|
|
||||||
// An absolute, cleaned path to a directory.
|
// An absolute, cleaned path to a directory.
|
||||||
// The Load function will read non-absolute
|
// The Load function will read non-absolute
|
||||||
@@ -100,29 +106,24 @@ type fileLoader struct {
|
|||||||
cleaner func() error
|
cleaner func() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileLoaderAtCwd returns a loader that loads from PWD.
|
// Repo returns the absolute path to the repo that contains Root if this fileLoader was created from a url
|
||||||
// A convenience for kustomize edit commands.
|
// or the empty string otherwise.
|
||||||
func NewFileLoaderAtCwd(fSys filesys.FileSystem) *fileLoader {
|
func (fl *FileLoader) Repo() string {
|
||||||
return newLoaderOrDie(
|
if fl.repoSpec != nil {
|
||||||
RestrictionRootOnly, fSys, filesys.SelfDir)
|
return fl.repoSpec.Dir.String()
|
||||||
}
|
}
|
||||||
|
return ""
|
||||||
// NewFileLoaderAtRoot returns a loader that loads from "/".
|
|
||||||
// A convenience for tests.
|
|
||||||
func NewFileLoaderAtRoot(fSys filesys.FileSystem) *fileLoader {
|
|
||||||
return newLoaderOrDie(
|
|
||||||
RestrictionRootOnly, fSys, filesys.Separator)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root returns the absolute path that is prepended to any
|
// Root returns the absolute path that is prepended to any
|
||||||
// relative paths used in Load.
|
// relative paths used in Load.
|
||||||
func (fl *fileLoader) Root() string {
|
func (fl *FileLoader) Root() string {
|
||||||
return fl.root.String()
|
return fl.root.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLoaderOrDie(
|
func NewLoaderOrDie(
|
||||||
lr LoadRestrictorFunc,
|
lr LoadRestrictorFunc,
|
||||||
fSys filesys.FileSystem, path string) *fileLoader {
|
fSys filesys.FileSystem, path string) *FileLoader {
|
||||||
root, err := filesys.ConfirmDir(fSys, path)
|
root, err := filesys.ConfirmDir(fSys, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to make loader at '%s'; %v", path, err)
|
log.Fatalf("unable to make loader at '%s'; %v", path, err)
|
||||||
@@ -131,12 +132,12 @@ func newLoaderOrDie(
|
|||||||
lr, root, fSys, nil, git.ClonerUsingGitExec)
|
lr, root, fSys, nil, git.ClonerUsingGitExec)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newLoaderAtConfirmedDir returns a new fileLoader with given root.
|
// newLoaderAtConfirmedDir returns a new FileLoader with given root.
|
||||||
func newLoaderAtConfirmedDir(
|
func newLoaderAtConfirmedDir(
|
||||||
lr LoadRestrictorFunc,
|
lr LoadRestrictorFunc,
|
||||||
root filesys.ConfirmedDir, fSys filesys.FileSystem,
|
root filesys.ConfirmedDir, fSys filesys.FileSystem,
|
||||||
referrer *fileLoader, cloner git.Cloner) *fileLoader {
|
referrer *FileLoader, cloner git.Cloner) *FileLoader {
|
||||||
return &fileLoader{
|
return &FileLoader{
|
||||||
loadRestrictor: lr,
|
loadRestrictor: lr,
|
||||||
root: root,
|
root: root,
|
||||||
referrer: referrer,
|
referrer: referrer,
|
||||||
@@ -146,9 +147,29 @@ func newLoaderAtConfirmedDir(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasAmbiguousPathSegment reports whether path contains a segment with ".."
|
||||||
|
// embedded in it (not ".." itself). This pattern results from YAML indentation
|
||||||
|
// errors where two list entries merge into one scalar, e.g.:
|
||||||
|
//
|
||||||
|
// resources:
|
||||||
|
// - ../../base
|
||||||
|
// - ../../shared/prod
|
||||||
|
//
|
||||||
|
// YAML parses this as "../../base - ../../shared/prod", producing segment
|
||||||
|
// "base - .." which filepath.Clean absorbs, silently resolving to an
|
||||||
|
// unintended directory.
|
||||||
|
func hasAmbiguousPathSegment(path string) bool {
|
||||||
|
for _, part := range strings.Split(filepath.ToSlash(path), "/") {
|
||||||
|
if part != "" && part != "." && part != ".." && strings.Contains(part, "..") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a new Loader, rooted relative to current loader,
|
// New returns a new Loader, rooted relative to current loader,
|
||||||
// or rooted in a temp directory holding a git repo clone.
|
// or rooted in a temp directory holding a git repo clone.
|
||||||
func (fl *fileLoader) New(path string) (ifc.Loader, error) {
|
func (fl *FileLoader) New(path string) (ifc.Loader, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
return nil, errors.Errorf("new root cannot be empty")
|
return nil, errors.Errorf("new root cannot be empty")
|
||||||
}
|
}
|
||||||
@@ -166,9 +187,16 @@ func (fl *fileLoader) New(path string) (ifc.Loader, error) {
|
|||||||
if filepath.IsAbs(path) {
|
if filepath.IsAbs(path) {
|
||||||
return nil, fmt.Errorf("new root '%s' cannot be absolute", path)
|
return nil, fmt.Errorf("new root '%s' cannot be absolute", path)
|
||||||
}
|
}
|
||||||
|
if hasAmbiguousPathSegment(path) {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"path %q is ambiguous: resolves to %q after normalization, "+
|
||||||
|
"which is likely not the intended target; check for YAML "+
|
||||||
|
"indentation errors in your kustomization file",
|
||||||
|
path, filepath.Clean(path))
|
||||||
|
}
|
||||||
root, err := filesys.ConfirmDir(fl.fSys, fl.root.Join(path))
|
root, err := filesys.ConfirmDir(fl.fSys, fl.root.Join(path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WrapPrefixf(err, ErrRtNotDir.Error())
|
return nil, errors.WrapPrefixf(err, "%s", ErrRtNotDir.Error())
|
||||||
}
|
}
|
||||||
if err = fl.errIfGitContainmentViolation(root); err != nil {
|
if err = fl.errIfGitContainmentViolation(root); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -184,7 +212,7 @@ func (fl *fileLoader) New(path string) (ifc.Loader, error) {
|
|||||||
// directory holding a cloned git repo.
|
// directory holding a cloned git repo.
|
||||||
func newLoaderAtGitClone(
|
func newLoaderAtGitClone(
|
||||||
repoSpec *git.RepoSpec, fSys filesys.FileSystem,
|
repoSpec *git.RepoSpec, fSys filesys.FileSystem,
|
||||||
referrer *fileLoader, cloner git.Cloner) (ifc.Loader, error) {
|
referrer *FileLoader, cloner git.Cloner) (ifc.Loader, error) {
|
||||||
cleaner := repoSpec.Cleaner(fSys)
|
cleaner := repoSpec.Cleaner(fSys)
|
||||||
err := cloner(repoSpec)
|
err := cloner(repoSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -206,7 +234,14 @@ func newLoaderAtGitClone(
|
|||||||
"'%s' refers to file '%s'; expecting directory",
|
"'%s' refers to file '%s'; expecting directory",
|
||||||
repoSpec.AbsPath(), f)
|
repoSpec.AbsPath(), f)
|
||||||
}
|
}
|
||||||
return &fileLoader{
|
// Path in repo can contain symlinks that exit repo. We can only
|
||||||
|
// check for this after cloning repo.
|
||||||
|
if !root.HasPrefix(repoSpec.CloneDir()) {
|
||||||
|
_ = cleaner()
|
||||||
|
return nil, fmt.Errorf("%q refers to directory outside of repo %q", repoSpec.AbsPath(),
|
||||||
|
repoSpec.CloneDir())
|
||||||
|
}
|
||||||
|
return &FileLoader{
|
||||||
// Clones never allowed to escape root.
|
// Clones never allowed to escape root.
|
||||||
loadRestrictor: RestrictionRootOnly,
|
loadRestrictor: RestrictionRootOnly,
|
||||||
root: root,
|
root: root,
|
||||||
@@ -218,7 +253,7 @@ func newLoaderAtGitClone(
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fl *fileLoader) errIfGitContainmentViolation(
|
func (fl *FileLoader) errIfGitContainmentViolation(
|
||||||
base filesys.ConfirmedDir) error {
|
base filesys.ConfirmedDir) error {
|
||||||
containingRepo := fl.containingRepo()
|
containingRepo := fl.containingRepo()
|
||||||
if containingRepo == nil {
|
if containingRepo == nil {
|
||||||
@@ -236,7 +271,7 @@ func (fl *fileLoader) errIfGitContainmentViolation(
|
|||||||
|
|
||||||
// Looks back through referrers for a git repo, returning nil
|
// Looks back through referrers for a git repo, returning nil
|
||||||
// if none found.
|
// if none found.
|
||||||
func (fl *fileLoader) containingRepo() *git.RepoSpec {
|
func (fl *FileLoader) containingRepo() *git.RepoSpec {
|
||||||
if fl.repoSpec != nil {
|
if fl.repoSpec != nil {
|
||||||
return fl.repoSpec
|
return fl.repoSpec
|
||||||
}
|
}
|
||||||
@@ -248,7 +283,7 @@ func (fl *fileLoader) containingRepo() *git.RepoSpec {
|
|||||||
|
|
||||||
// errIfArgEqualOrHigher tests whether the argument,
|
// errIfArgEqualOrHigher tests whether the argument,
|
||||||
// is equal to or above the root of any ancestor.
|
// is equal to or above the root of any ancestor.
|
||||||
func (fl *fileLoader) errIfArgEqualOrHigher(
|
func (fl *FileLoader) errIfArgEqualOrHigher(
|
||||||
candidateRoot filesys.ConfirmedDir) error {
|
candidateRoot filesys.ConfirmedDir) error {
|
||||||
if fl.root.HasPrefix(candidateRoot) {
|
if fl.root.HasPrefix(candidateRoot) {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
@@ -265,7 +300,7 @@ func (fl *fileLoader) errIfArgEqualOrHigher(
|
|||||||
// I.e. Allow a distinction between git URI with
|
// I.e. Allow a distinction between git URI with
|
||||||
// path foo and tag bar and a git URI with the same
|
// path foo and tag bar and a git URI with the same
|
||||||
// path but a different tag?
|
// path but a different tag?
|
||||||
func (fl *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
|
func (fl *FileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
|
||||||
// TODO(monopole): Use parsed data instead of Raw().
|
// TODO(monopole): Use parsed data instead of Raw().
|
||||||
if fl.repoSpec != nil &&
|
if fl.repoSpec != nil &&
|
||||||
strings.HasPrefix(fl.repoSpec.Raw(), newRepoSpec.Raw()) {
|
strings.HasPrefix(fl.repoSpec.Raw(), newRepoSpec.Raw()) {
|
||||||
@@ -282,31 +317,9 @@ func (fl *fileLoader) errIfRepoCycle(newRepoSpec *git.RepoSpec) error {
|
|||||||
// Load returns the content of file at the given path,
|
// Load returns the content of file at the given path,
|
||||||
// else an error. Relative paths are taken relative
|
// else an error. Relative paths are taken relative
|
||||||
// to the root.
|
// to the root.
|
||||||
func (fl *fileLoader) Load(path string) ([]byte, error) {
|
func (fl *FileLoader) Load(path string) ([]byte, error) {
|
||||||
if u, err := url.Parse(path); err == nil && (u.Scheme == "http" || u.Scheme == "https") {
|
if IsRemoteFile(path) {
|
||||||
var hc *http.Client
|
return fl.httpClientGetContent(path)
|
||||||
if fl.http != nil {
|
|
||||||
hc = fl.http
|
|
||||||
} else {
|
|
||||||
hc = &http.Client{}
|
|
||||||
}
|
|
||||||
resp, err := hc.Get(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
|
||||||
_, err := git.NewRepoSpecFromURL(path)
|
|
||||||
if err == nil {
|
|
||||||
return nil, errors.Errorf("URL is a git repository")
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("%w: status code %d (%s)", ErrHTTP, resp.StatusCode, http.StatusText(resp.StatusCode))
|
|
||||||
}
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return body, nil
|
|
||||||
}
|
}
|
||||||
if !filepath.IsAbs(path) {
|
if !filepath.IsAbs(path) {
|
||||||
path = fl.root.Join(path)
|
path = fl.root.Join(path)
|
||||||
@@ -318,7 +331,35 @@ func (fl *fileLoader) Load(path string) ([]byte, error) {
|
|||||||
return fl.fSys.ReadFile(path)
|
return fl.fSys.ReadFile(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fl *FileLoader) httpClientGetContent(path string) ([]byte, error) {
|
||||||
|
var hc *http.Client
|
||||||
|
if fl.http != nil {
|
||||||
|
hc = fl.http
|
||||||
|
} else {
|
||||||
|
hc = &http.Client{}
|
||||||
|
}
|
||||||
|
parsedURL, err := url.ParseRequestURI(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
resp, err := hc.Get(parsedURL.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
// response unsuccessful
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode > 299 {
|
||||||
|
_, err = git.NewRepoSpecFromURL(path)
|
||||||
|
if err == nil {
|
||||||
|
return nil, errors.Errorf("URL is a git repository")
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%w: status code %d (%s)", ErrHTTP, resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||||
|
}
|
||||||
|
content, err := io.ReadAll(resp.Body)
|
||||||
|
return content, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
// Cleanup runs the cleaner.
|
// Cleanup runs the cleaner.
|
||||||
func (fl *fileLoader) Cleanup() error {
|
func (fl *FileLoader) Cleanup() error {
|
||||||
return fl.cleaner()
|
return fl.cleaner()
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,8 @@ package loader
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@@ -21,12 +22,51 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/kyaml/filesys"
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestIsRemoteFile(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
url string
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
"https file": {
|
||||||
|
"https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/helloWorld/configMap.yaml",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"malformed https": {
|
||||||
|
// TODO(annasong): Maybe we want to fix this. Needs more research.
|
||||||
|
"https:/raw.githubusercontent.com/kubernetes-sigs/kustomize/master/examples/helloWorld/configMap.yaml",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"https dir": {
|
||||||
|
"https://github.com/kubernetes-sigs/kustomize//examples/helloWorld/",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
"no scheme": {
|
||||||
|
"github.com/kubernetes-sigs/kustomize//examples/helloWorld/",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
"ssh": {
|
||||||
|
"ssh://git@github.com/kubernetes-sigs/kustomize.git",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
"local": {
|
||||||
|
"pod.yaml",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, test := range cases {
|
||||||
|
test := test
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
require.Equal(t, test.valid, IsRemoteFile(test.url))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type testData struct {
|
type testData struct {
|
||||||
path string
|
path string
|
||||||
expectedContent string
|
expectedContent string
|
||||||
}
|
}
|
||||||
|
|
||||||
var testCases = []testData{
|
var testCases = []testData{ //nolint:gochecknoglobals
|
||||||
{
|
{
|
||||||
path: "foo/project/fileA.yaml",
|
path: "foo/project/fileA.yaml",
|
||||||
expectedContent: "fileA content",
|
expectedContent: "fileA content",
|
||||||
@@ -48,19 +88,22 @@ var testCases = []testData{
|
|||||||
func MakeFakeFs(td []testData) filesys.FileSystem {
|
func MakeFakeFs(td []testData) filesys.FileSystem {
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
for _, x := range td {
|
for _, x := range td {
|
||||||
fSys.WriteFile(x.path, []byte(x.expectedContent))
|
_ = fSys.WriteFile(x.path, []byte(x.expectedContent))
|
||||||
}
|
}
|
||||||
return fSys
|
return fSys
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeLoader() *fileLoader {
|
func makeLoader() *FileLoader {
|
||||||
return NewFileLoaderAtRoot(MakeFakeFs(testCases))
|
return NewLoaderOrDie(
|
||||||
|
RestrictionRootOnly, MakeFakeFs(testCases), filesys.Separator)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoaderLoad(t *testing.T) {
|
func TestLoaderLoad(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
l1 := makeLoader()
|
l1 := makeLoader()
|
||||||
|
repo := l1.Repo()
|
||||||
|
require.Empty(repo)
|
||||||
require.Equal("/", l1.Root())
|
require.Equal("/", l1.Root())
|
||||||
|
|
||||||
for _, x := range testCases {
|
for _, x := range testCases {
|
||||||
@@ -73,6 +116,9 @@ func TestLoaderLoad(t *testing.T) {
|
|||||||
}
|
}
|
||||||
l2, err := l1.New("foo/project")
|
l2, err := l1.New("foo/project")
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
|
repo = l2.Repo()
|
||||||
|
require.Empty(repo)
|
||||||
require.Equal("/foo/project", l2.Root())
|
require.Equal("/foo/project", l2.Root())
|
||||||
|
|
||||||
for _, x := range testCases {
|
for _, x := range testCases {
|
||||||
@@ -115,31 +161,31 @@ func TestLoaderBadRelative(t *testing.T) {
|
|||||||
require.Equal("/foo/project/subdir1", l1.Root())
|
require.Equal("/foo/project/subdir1", l1.Root())
|
||||||
|
|
||||||
// Cannot cd into a file.
|
// Cannot cd into a file.
|
||||||
l2, err := l1.New("fileB.yaml")
|
_, err = l1.New("fileB.yaml")
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
// It's not okay to stay at the same place.
|
// It's not okay to stay at the same place.
|
||||||
l2, err = l1.New(filesys.SelfDir)
|
_, err = l1.New(filesys.SelfDir)
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
// It's not okay to go up and back down into same place.
|
// It's not okay to go up and back down into same place.
|
||||||
l2, err = l1.New("../subdir1")
|
_, err = l1.New("../subdir1")
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
// It's not okay to go up via a relative path.
|
// It's not okay to go up via a relative path.
|
||||||
l2, err = l1.New("..")
|
_, err = l1.New("..")
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
// It's not okay to go up via an absolute path.
|
// It's not okay to go up via an absolute path.
|
||||||
l2, err = l1.New("/foo/project")
|
_, err = l1.New("/foo/project")
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
// It's not okay to go to the root.
|
// It's not okay to go to the root.
|
||||||
l2, err = l1.New("/")
|
_, err = l1.New("/")
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
// It's okay to go up and down to a sibling.
|
// It's okay to go up and down to a sibling.
|
||||||
l2, err = l1.New("../subdir2")
|
l2, err := l1.New("../subdir2")
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal("/foo/project/subdir2", l2.Root())
|
require.Equal("/foo/project/subdir2", l2.Root())
|
||||||
|
|
||||||
@@ -152,17 +198,110 @@ func TestLoaderBadRelative(t *testing.T) {
|
|||||||
|
|
||||||
// It's not OK to go over to a previously visited directory.
|
// It's not OK to go over to a previously visited directory.
|
||||||
// Must disallow going back and forth in a cycle.
|
// Must disallow going back and forth in a cycle.
|
||||||
l1, err = l2.New("../subdir1")
|
_, err = l2.New("../subdir1")
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoaderMisc(t *testing.T) {
|
func TestNewEmptyLoader(t *testing.T) {
|
||||||
l := makeLoader()
|
_, err := makeLoader().New("")
|
||||||
_, err := l.New("")
|
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = l.New("https://google.com/project")
|
func TestLoaderRejectsMalformedPath(t *testing.T) {
|
||||||
|
// A YAML indentation error can collapse two resource entries into one:
|
||||||
|
// resources:
|
||||||
|
// - ../../base
|
||||||
|
// - ../../shared/prod
|
||||||
|
// becomes the single string: "../../base - ../../shared/prod"
|
||||||
|
//
|
||||||
|
// filepath.Clean normalizes this to "../../shared/prod", silently
|
||||||
|
// dropping the "../../base" reference. The loader must reject paths
|
||||||
|
// with inner ".." components that cause this silent absorption.
|
||||||
|
// See https://github.com/kubernetes-sigs/kustomize/issues/5979
|
||||||
|
fSys := filesys.MakeFsInMemory()
|
||||||
|
require.NoError(t, fSys.MkdirAll("/base"))
|
||||||
|
require.NoError(t, fSys.MkdirAll("/shared/prod"))
|
||||||
|
require.NoError(t, fSys.MkdirAll("/overlays/prod1"))
|
||||||
|
|
||||||
|
l1 := NewLoaderOrDie(RestrictionNone, fSys, "/overlays/prod1")
|
||||||
|
|
||||||
|
// The exact bug from issue #5979.
|
||||||
|
_, err := l1.New("../../base - ../../shared/prod")
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "ambiguous")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasAmbiguousPathSegment(t *testing.T) {
|
||||||
|
cases := map[string]bool{
|
||||||
|
// Safe: ".." only as standalone segments
|
||||||
|
"../base": false,
|
||||||
|
"../../shared/prod": false,
|
||||||
|
"..": false,
|
||||||
|
"foo/bar": false,
|
||||||
|
"foo/bar/": false,
|
||||||
|
"foo//bar": false,
|
||||||
|
"./foo/bar": false,
|
||||||
|
"https://root": false,
|
||||||
|
"foo/../bar": false,
|
||||||
|
"a/b/../../c": false,
|
||||||
|
"a/..": false,
|
||||||
|
// Winding paths are legitimate (used by localizer)
|
||||||
|
"delta/../../../../a/b/../../alpha/beta/sibling": false,
|
||||||
|
// Dangerous: segment contains embedded ".." from YAML merge errors
|
||||||
|
"../../base - ../../shared/prod": true,
|
||||||
|
"../../base ../../shared/prod": true,
|
||||||
|
"foo/bar..baz/qux": true,
|
||||||
|
"a/b/c..d/e": true,
|
||||||
|
"../../base\t- ../../shared/prod": true,
|
||||||
|
}
|
||||||
|
for path, want := range cases {
|
||||||
|
t.Run(path, func(t *testing.T) {
|
||||||
|
require.Equal(t, want, hasAmbiguousPathSegment(path), "hasAmbiguousPathSegment(%q)", path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRemoteLoaderDoesNotExist(t *testing.T) {
|
||||||
|
_, err := makeLoader().New("https://example.com/org/repo")
|
||||||
|
require.ErrorContains(t, err, "fetch")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoaderLocalScheme(t *testing.T) {
|
||||||
|
// It is unlikely but possible for a reference with a url scheme to
|
||||||
|
// actually refer to a local file or directory.
|
||||||
|
t.Run("file", func(t *testing.T) {
|
||||||
|
fSys, dir := setupOnDisk(t)
|
||||||
|
parts := []string{
|
||||||
|
"ssh:",
|
||||||
|
"resource.yaml",
|
||||||
|
}
|
||||||
|
require.NoError(t, fSys.Mkdir(dir.Join(parts[0])))
|
||||||
|
const content = "resource config"
|
||||||
|
require.NoError(t, fSys.WriteFile(
|
||||||
|
dir.Join(filepath.Join(parts...)),
|
||||||
|
[]byte(content),
|
||||||
|
))
|
||||||
|
actualContent, err := NewLoaderOrDie(RestrictionRootOnly,
|
||||||
|
fSys,
|
||||||
|
dir.String(),
|
||||||
|
).Load(strings.Join(parts, "//"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, content, string(actualContent))
|
||||||
|
})
|
||||||
|
t.Run("directory", func(t *testing.T) {
|
||||||
|
fSys, dir := setupOnDisk(t)
|
||||||
|
parts := []string{
|
||||||
|
"https:",
|
||||||
|
"root",
|
||||||
|
}
|
||||||
|
require.NoError(t, fSys.MkdirAll(dir.Join(filepath.Join(parts...))))
|
||||||
|
ldr, err := NewLoaderOrDie(RestrictionRootOnly,
|
||||||
|
fSys,
|
||||||
|
dir.String(),
|
||||||
|
).New(strings.Join(parts, "//"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Empty(t, ldr.Repo())
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -172,31 +311,32 @@ const (
|
|||||||
|
|
||||||
// Create a structure like this
|
// Create a structure like this
|
||||||
//
|
//
|
||||||
// /tmp/kustomize-test-random
|
// /tmp/kustomize-test-random
|
||||||
// ├── base
|
// ├── base
|
||||||
// │ ├── okayData
|
// │ ├── okayData
|
||||||
// │ ├── symLinkToOkayData -> okayData
|
// │ ├── symLinkToOkayData -> okayData
|
||||||
// │ └── symLinkToExteriorData -> ../exteriorData
|
// │ └── symLinkToExteriorData -> ../exteriorData
|
||||||
// └── exteriorData
|
// └── exteriorData
|
||||||
//
|
|
||||||
func commonSetupForLoaderRestrictionTest(t *testing.T) (string, filesys.FileSystem) {
|
func commonSetupForLoaderRestrictionTest(t *testing.T) (string, filesys.FileSystem) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
dir := t.TempDir()
|
require := require.New(t)
|
||||||
fSys := filesys.MakeFsOnDisk()
|
fSys, tmpDir := setupOnDisk(t)
|
||||||
fSys.Mkdir(filepath.Join(dir, "base"))
|
dir := tmpDir.String()
|
||||||
|
|
||||||
fSys.WriteFile(
|
require.NoError(fSys.Mkdir(filepath.Join(dir, "base")))
|
||||||
filepath.Join(dir, "base", "okayData"), []byte(contentOk))
|
|
||||||
|
|
||||||
fSys.WriteFile(
|
require.NoError(fSys.WriteFile(
|
||||||
filepath.Join(dir, "exteriorData"), []byte(contentExteriorData))
|
filepath.Join(dir, "base", "okayData"), []byte(contentOk)))
|
||||||
|
|
||||||
os.Symlink(
|
require.NoError(fSys.WriteFile(
|
||||||
|
filepath.Join(dir, "exteriorData"), []byte(contentExteriorData)))
|
||||||
|
|
||||||
|
require.NoError(os.Symlink(
|
||||||
filepath.Join(dir, "base", "okayData"),
|
filepath.Join(dir, "base", "okayData"),
|
||||||
filepath.Join(dir, "base", "symLinkToOkayData"))
|
filepath.Join(dir, "base", "symLinkToOkayData")))
|
||||||
os.Symlink(
|
require.NoError(os.Symlink(
|
||||||
filepath.Join(dir, "exteriorData"),
|
filepath.Join(dir, "exteriorData"),
|
||||||
filepath.Join(dir, "base", "symLinkToExteriorData"))
|
filepath.Join(dir, "base", "symLinkToExteriorData")))
|
||||||
return dir, fSys
|
return dir, fSys
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +378,7 @@ func TestRestrictionRootOnlyInRealLoader(t *testing.T) {
|
|||||||
|
|
||||||
var l ifc.Loader
|
var l ifc.Loader
|
||||||
|
|
||||||
l = newLoaderOrDie(RestrictionRootOnly, fSys, dir)
|
l = NewLoaderOrDie(RestrictionRootOnly, fSys, dir)
|
||||||
|
|
||||||
l = doSanityChecksAndDropIntoBase(t, l)
|
l = doSanityChecksAndDropIntoBase(t, l)
|
||||||
|
|
||||||
@@ -259,7 +399,7 @@ func TestRestrictionNoneInRealLoader(t *testing.T) {
|
|||||||
|
|
||||||
var l ifc.Loader
|
var l ifc.Loader
|
||||||
|
|
||||||
l = newLoaderOrDie(RestrictionNone, fSys, dir)
|
l = NewLoaderOrDie(RestrictionNone, fSys, dir)
|
||||||
|
|
||||||
l = doSanityChecksAndDropIntoBase(t, l)
|
l = doSanityChecksAndDropIntoBase(t, l)
|
||||||
|
|
||||||
@@ -306,14 +446,14 @@ func TestNewLoaderAtGitClone(t *testing.T) {
|
|||||||
url := rootURL + "/" + pathInRepo
|
url := rootURL + "/" + pathInRepo
|
||||||
coRoot := "/tmp"
|
coRoot := "/tmp"
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.MkdirAll(coRoot)
|
require.NoError(fSys.MkdirAll(coRoot))
|
||||||
fSys.MkdirAll(coRoot + "/" + pathInRepo)
|
require.NoError(fSys.MkdirAll(coRoot + "/" + pathInRepo))
|
||||||
fSys.WriteFile(
|
require.NoError(fSys.WriteFile(
|
||||||
coRoot+"/"+pathInRepo+"/"+
|
coRoot+"/"+pathInRepo+"/"+
|
||||||
konfig.DefaultKustomizationFileName(),
|
konfig.DefaultKustomizationFileName(),
|
||||||
[]byte(`
|
[]byte(`
|
||||||
whatever
|
whatever
|
||||||
`))
|
`)))
|
||||||
|
|
||||||
repoSpec, err := git.NewRepoSpecFromURL(url)
|
repoSpec, err := git.NewRepoSpecFromURL(url)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
@@ -322,6 +462,8 @@ whatever
|
|||||||
repoSpec, fSys, nil,
|
repoSpec, fSys, nil,
|
||||||
git.DoNothingCloner(filesys.ConfirmedDir(coRoot)))
|
git.DoNothingCloner(filesys.ConfirmedDir(coRoot)))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
repo := l.Repo()
|
||||||
|
require.Equal(coRoot, repo)
|
||||||
require.Equal(coRoot+"/"+pathInRepo, l.Root())
|
require.Equal(coRoot+"/"+pathInRepo, l.Root())
|
||||||
|
|
||||||
_, err = l.New(url)
|
_, err = l.New(url)
|
||||||
@@ -331,10 +473,13 @@ whatever
|
|||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
pathInRepo = "foo/overlay"
|
pathInRepo = "foo/overlay"
|
||||||
fSys.MkdirAll(coRoot + "/" + pathInRepo)
|
require.NoError(fSys.MkdirAll(coRoot + "/" + pathInRepo))
|
||||||
url = rootURL + "/" + pathInRepo
|
url = rootURL + "/" + pathInRepo
|
||||||
l2, err := l.New(url)
|
l2, err := l.New(url)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
|
repo = l2.Repo()
|
||||||
|
require.Equal(coRoot, repo)
|
||||||
require.Equal(coRoot+"/"+pathInRepo, l2.Root())
|
require.Equal(coRoot+"/"+pathInRepo, l2.Root())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,20 +490,20 @@ func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) {
|
|||||||
topDir := "/whatever"
|
topDir := "/whatever"
|
||||||
cloneRoot := topDir + "/someClone"
|
cloneRoot := topDir + "/someClone"
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.MkdirAll(topDir + "/highBase")
|
require.NoError(fSys.MkdirAll(topDir + "/highBase"))
|
||||||
fSys.MkdirAll(cloneRoot + "/foo/base")
|
require.NoError(fSys.MkdirAll(cloneRoot + "/foo/base"))
|
||||||
fSys.MkdirAll(cloneRoot + "/foo/overlay")
|
require.NoError(fSys.MkdirAll(cloneRoot + "/foo/overlay"))
|
||||||
|
|
||||||
var l1 ifc.Loader
|
var l1 ifc.Loader
|
||||||
|
|
||||||
// Establish that a local overlay can navigate
|
// Establish that a local overlay can navigate
|
||||||
// to the local bases.
|
// to the local bases.
|
||||||
l1 = newLoaderOrDie(
|
l1 = NewLoaderOrDie(
|
||||||
RestrictionRootOnly, fSys, cloneRoot+"/foo/overlay")
|
RestrictionRootOnly, fSys, cloneRoot+"/foo/overlay")
|
||||||
require.Equal(cloneRoot+"/foo/overlay", l1.Root())
|
require.Equal(cloneRoot+"/foo/overlay", l1.Root())
|
||||||
|
|
||||||
l2, err := l1.New("../base")
|
l2, err := l1.New("../base")
|
||||||
require.NoError(nil)
|
require.NoError(err)
|
||||||
require.Equal(cloneRoot+"/foo/base", l2.Root())
|
require.Equal(cloneRoot+"/foo/base", l2.Root())
|
||||||
|
|
||||||
l3, err := l2.New("../../../highBase")
|
l3, err := l2.New("../../../highBase")
|
||||||
@@ -389,6 +534,8 @@ func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) {
|
|||||||
// This is okay.
|
// This is okay.
|
||||||
l2, err = l1.New("../base")
|
l2, err = l1.New("../base")
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
repo := l2.Repo()
|
||||||
|
require.Empty(repo)
|
||||||
require.Equal(cloneRoot+"/foo/base", l2.Root())
|
require.Equal(cloneRoot+"/foo/base", l2.Root())
|
||||||
|
|
||||||
// This is not okay.
|
// This is not okay.
|
||||||
@@ -398,14 +545,31 @@ func TestLoaderDisallowsLocalBaseFromRemoteOverlay(t *testing.T) {
|
|||||||
"base '/whatever/highBase' is outside '/whatever/someClone'")
|
"base '/whatever/highBase' is outside '/whatever/someClone'")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoaderDisallowsRemoteBaseExitRepo(t *testing.T) {
|
||||||
|
fSys, dir := setupOnDisk(t)
|
||||||
|
|
||||||
|
repo := dir.Join("repo")
|
||||||
|
require.NoError(t, fSys.Mkdir(repo))
|
||||||
|
|
||||||
|
base := filepath.Join(repo, "base")
|
||||||
|
require.NoError(t, os.Symlink(dir.String(), base))
|
||||||
|
|
||||||
|
repoSpec, err := git.NewRepoSpecFromURL("https://github.com/org/repo/base")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = newLoaderAtGitClone(repoSpec, fSys, nil, git.DoNothingCloner(filesys.ConfirmedDir(repo)))
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), fmt.Sprintf("%q refers to directory outside of repo %q", base, repo))
|
||||||
|
}
|
||||||
|
|
||||||
func TestLocalLoaderReferencingGitBase(t *testing.T) {
|
func TestLocalLoaderReferencingGitBase(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
topDir := "/whatever"
|
topDir := "/whatever"
|
||||||
cloneRoot := topDir + "/someClone"
|
cloneRoot := topDir + "/someClone"
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.MkdirAll(topDir)
|
require.NoError(fSys.MkdirAll(topDir))
|
||||||
fSys.MkdirAll(cloneRoot + "/foo/base")
|
require.NoError(fSys.MkdirAll(cloneRoot + "/foo/base"))
|
||||||
|
|
||||||
l1 := newLoaderAtConfirmedDir(
|
l1 := newLoaderAtConfirmedDir(
|
||||||
RestrictionRootOnly, filesys.ConfirmedDir(topDir), fSys, nil,
|
RestrictionRootOnly, filesys.ConfirmedDir(topDir), fSys, nil,
|
||||||
@@ -414,6 +578,8 @@ func TestLocalLoaderReferencingGitBase(t *testing.T) {
|
|||||||
|
|
||||||
l2, err := l1.New("github.com/someOrg/someRepo/foo/base")
|
l2, err := l1.New("github.com/someOrg/someRepo/foo/base")
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
repo := l2.Repo()
|
||||||
|
require.Equal(cloneRoot, repo)
|
||||||
require.Equal(cloneRoot+"/foo/base", l2.Root())
|
require.Equal(cloneRoot+"/foo/base", l2.Root())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,8 +589,8 @@ func TestRepoDirectCycleDetection(t *testing.T) {
|
|||||||
topDir := "/cycles"
|
topDir := "/cycles"
|
||||||
cloneRoot := topDir + "/someClone"
|
cloneRoot := topDir + "/someClone"
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.MkdirAll(topDir)
|
require.NoError(fSys.MkdirAll(topDir))
|
||||||
fSys.MkdirAll(cloneRoot)
|
require.NoError(fSys.MkdirAll(cloneRoot))
|
||||||
|
|
||||||
l1 := newLoaderAtConfirmedDir(
|
l1 := newLoaderAtConfirmedDir(
|
||||||
RestrictionRootOnly, filesys.ConfirmedDir(topDir), fSys, nil,
|
RestrictionRootOnly, filesys.ConfirmedDir(topDir), fSys, nil,
|
||||||
@@ -445,8 +611,8 @@ func TestRepoIndirectCycleDetection(t *testing.T) {
|
|||||||
topDir := "/cycles"
|
topDir := "/cycles"
|
||||||
cloneRoot := topDir + "/someClone"
|
cloneRoot := topDir + "/someClone"
|
||||||
fSys := filesys.MakeFsInMemory()
|
fSys := filesys.MakeFsInMemory()
|
||||||
fSys.MkdirAll(topDir)
|
require.NoError(fSys.MkdirAll(topDir))
|
||||||
fSys.MkdirAll(cloneRoot)
|
require.NoError(fSys.MkdirAll(cloneRoot))
|
||||||
|
|
||||||
l0 := newLoaderAtConfirmedDir(
|
l0 := newLoaderAtConfirmedDir(
|
||||||
RestrictionRootOnly, filesys.ConfirmedDir(topDir), fSys, nil,
|
RestrictionRootOnly, filesys.ConfirmedDir(topDir), fSys, nil,
|
||||||
@@ -490,7 +656,8 @@ func TestLoaderHTTP(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
l1 := NewFileLoaderAtRoot(MakeFakeFs(testCasesFile))
|
l1 := NewLoaderOrDie(
|
||||||
|
RestrictionRootOnly, MakeFakeFs(testCasesFile), filesys.Separator)
|
||||||
require.Equal("/", l1.Root())
|
require.Equal("/", l1.Root())
|
||||||
|
|
||||||
for _, x := range testCasesFile {
|
for _, x := range testCasesFile {
|
||||||
@@ -517,8 +684,8 @@ func TestLoaderHTTP(t *testing.T) {
|
|||||||
u := req.URL.String()
|
u := req.URL.String()
|
||||||
require.Equal(x.path, u)
|
require.Equal(x.path, u)
|
||||||
return &http.Response{
|
return &http.Response{
|
||||||
StatusCode: 200,
|
StatusCode: http.StatusOK,
|
||||||
Body: ioutil.NopCloser(bytes.NewBufferString(x.expectedContent)),
|
Body: io.NopCloser(bytes.NewBufferString(x.expectedContent)),
|
||||||
Header: make(http.Header),
|
Header: make(http.Header),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -548,3 +715,31 @@ func TestLoaderHTTP(t *testing.T) {
|
|||||||
require.Error(err)
|
require.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setupOnDisk sets up a file system on disk and directory that is cleaned after
|
||||||
|
// test completion.
|
||||||
|
// TODO(annasong): Move all loader tests that require real file system into
|
||||||
|
// api/krusty.
|
||||||
|
func setupOnDisk(t *testing.T) (filesys.FileSystem, filesys.ConfirmedDir) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
fSys := filesys.MakeFsOnDisk()
|
||||||
|
dir, err := filesys.NewTmpConfirmedDir()
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = fSys.RemoveAll(dir.String())
|
||||||
|
})
|
||||||
|
return fSys, dir
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLoaderHTTPMalformedURL tests that malformed URLs are properly handled
|
||||||
|
// to prevent infinite loops in http.Client.Get
|
||||||
|
func TestLoaderHTTPMalformedURL(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
malformedURL := "https://example.com/example?ref=main - ../../example/example.yaml"
|
||||||
|
l1 := NewLoaderOrDie(
|
||||||
|
RestrictionRootOnly, MakeFakeFs([]testData{}), filesys.Separator)
|
||||||
|
_, err := l1.Load(malformedURL)
|
||||||
|
require.Error(err)
|
||||||
|
require.Equal("HTTP Error: status code 400 (Bad Request)", err.Error())
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ func NewLoader(
|
|||||||
}
|
}
|
||||||
root, err := filesys.ConfirmDir(fSys, target)
|
root, err := filesys.ConfirmDir(fSys, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.WrapPrefixf(err, ErrRtNotDir.Error())
|
return nil, errors.WrapPrefixf(err, "%s", ErrRtNotDir.Error())
|
||||||
}
|
}
|
||||||
return newLoaderAtConfirmedDir(
|
return newLoaderAtConfirmedDir(
|
||||||
lr, root, fSys, nil, git.ClonerUsingGitExec), nil
|
lr, root, fSys, nil, git.ClonerUsingGitExec), nil
|
||||||
152
api/internal/localizer/builtinplugins.go
Normal file
152
api/internal/localizer/builtinplugins.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
// Copyright 2022 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package localizer
|
||||||
|
|
||||||
|
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/internal/plugins/builtinhelpers"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// localizeBuiltinPlugins localizes built-in plugins with file paths.
|
||||||
|
// Note that this excludes helm, which needs a repo.
|
||||||
|
type localizeBuiltinPlugins struct {
|
||||||
|
lc *localizer
|
||||||
|
|
||||||
|
// locPathFn is used by localizeNode to set the localized path on the plugin.
|
||||||
|
locPathFn func(string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ kio.Filter = &localizeBuiltinPlugins{}
|
||||||
|
|
||||||
|
// Filter localizes the built-in plugins with file paths.
|
||||||
|
func (lbp *localizeBuiltinPlugins) Filter(plugins []*yaml.RNode) ([]*yaml.RNode, error) {
|
||||||
|
for _, singlePlugin := range plugins {
|
||||||
|
err := singlePlugin.PipeE(fsslice.Filter{
|
||||||
|
FsSlice: types.FsSlice{
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.ConfigMapGenerator.String()},
|
||||||
|
Path: "env",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.ConfigMapGenerator.String()},
|
||||||
|
Path: "envs",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.SecretGenerator.String()},
|
||||||
|
Path: "env",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.SecretGenerator.String()},
|
||||||
|
Path: "envs",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.HelmChartInflationGenerator.String()},
|
||||||
|
Path: "valuesFile",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.HelmChartInflationGenerator.String()},
|
||||||
|
Path: "additionalValuesFiles",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.PatchTransformer.String()},
|
||||||
|
Path: "path",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.PatchJson6902Transformer.String()},
|
||||||
|
Path: "path",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.ReplacementTransformer.String()},
|
||||||
|
Path: "replacements/path",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SetValue: func(node *yaml.RNode) error {
|
||||||
|
lbp.locPathFn = lbp.lc.localizeFile
|
||||||
|
return lbp.localizeAll(node)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fsslice.Filter{
|
||||||
|
FsSlice: types.FsSlice{
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.ConfigMapGenerator.String()},
|
||||||
|
Path: "files",
|
||||||
|
},
|
||||||
|
types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.SecretGenerator.String()},
|
||||||
|
Path: "files",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SetValue: func(node *yaml.RNode) error {
|
||||||
|
lbp.locPathFn = lbp.lc.localizeFileSource
|
||||||
|
return lbp.localizeAll(node)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
yaml.FilterFunc(func(node *yaml.RNode) (*yaml.RNode, error) {
|
||||||
|
isHelm := node.GetApiVersion() == konfig.BuiltinPluginApiVersion &&
|
||||||
|
node.GetKind() == builtinhelpers.HelmChartInflationGenerator.String()
|
||||||
|
if !isHelm {
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
home, err := node.Pipe(yaml.Lookup("chartHome"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
if home == nil {
|
||||||
|
_, err = lbp.lc.copyChartHomeEntry("")
|
||||||
|
} else {
|
||||||
|
lbp.locPathFn = lbp.lc.copyChartHomeEntry
|
||||||
|
err = lbp.localizeScalar(home)
|
||||||
|
}
|
||||||
|
return node, errors.WrapPrefixf(err, "plugin %s", resid.FromRNode(node))
|
||||||
|
}),
|
||||||
|
fieldspec.Filter{
|
||||||
|
FieldSpec: types.FieldSpec{
|
||||||
|
Gvk: resid.Gvk{Version: konfig.BuiltinPluginApiVersion, Kind: builtinhelpers.PatchStrategicMergeTransformer.String()},
|
||||||
|
Path: "paths",
|
||||||
|
},
|
||||||
|
SetValue: func(node *yaml.RNode) error {
|
||||||
|
lbp.locPathFn = lbp.lc.localizeK8sResource
|
||||||
|
return lbp.localizeAll(node)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return plugins, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeAll sets each entry in node to its value localized by locPathFn.
|
||||||
|
// Node is a sequence or scalar value.
|
||||||
|
func (lbp *localizeBuiltinPlugins) localizeAll(node *yaml.RNode) error {
|
||||||
|
// We rely on the build command to throw errors for nodes in
|
||||||
|
// built-in plugins that are sequences when expected to be scalar,
|
||||||
|
// and vice versa.
|
||||||
|
//nolint: exhaustive
|
||||||
|
switch node.YNode().Kind {
|
||||||
|
case yaml.SequenceNode:
|
||||||
|
return errors.Wrap(node.VisitElements(lbp.localizeScalar))
|
||||||
|
case yaml.ScalarNode:
|
||||||
|
return lbp.localizeScalar(node)
|
||||||
|
default:
|
||||||
|
return errors.Errorf("expected sequence or scalar node")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeScalar sets the scalar node to its value localized by locPathFn.
|
||||||
|
func (lbp *localizeBuiltinPlugins) localizeScalar(node *yaml.RNode) error {
|
||||||
|
localizedPath, err := lbp.locPathFn(node.YNode().Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return filtersutil.SetScalar(localizedPath)(node)
|
||||||
|
}
|
||||||
7
api/internal/localizer/doc.go
Normal file
7
api/internal/localizer/doc.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright 2022 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package localizer contains utilities for the command kustomize localize, which is
|
||||||
|
// documented under proposals/localize-command or at
|
||||||
|
// https://github.com/kubernetes-sigs/kustomize/blob/master/proposals/22-04-localize-command.md
|
||||||
|
package localizer
|
||||||
27
api/internal/localizer/errors.go
Normal file
27
api/internal/localizer/errors.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2022 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package localizer
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type ResourceLoadError struct {
|
||||||
|
InlineError error
|
||||||
|
FileError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rle ResourceLoadError) Error() string {
|
||||||
|
return fmt.Sprintf(`when parsing as inline received error: %s
|
||||||
|
when parsing as filepath received error: %s`, rle.InlineError, rle.FileError)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PathLocalizeError struct {
|
||||||
|
Path string
|
||||||
|
FileError error
|
||||||
|
RootError error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ple PathLocalizeError) Error() string {
|
||||||
|
return fmt.Sprintf(`could not localize path %q as file: %s; could not localize path %q as directory: %s`,
|
||||||
|
ple.Path, ple.FileError, ple.Path, ple.RootError)
|
||||||
|
}
|
||||||
613
api/internal/localizer/localizer.go
Normal file
613
api/internal/localizer/localizer.go
Normal file
@@ -0,0 +1,613 @@
|
|||||||
|
// Copyright 2022 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package localizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/generators"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/target"
|
||||||
|
"sigs.k8s.io/kustomize/api/provider"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// localizer encapsulates all state needed to localize the root at ldr.
|
||||||
|
type localizer struct {
|
||||||
|
fSys filesys.FileSystem
|
||||||
|
|
||||||
|
// underlying type is Loader
|
||||||
|
ldr ifc.Loader
|
||||||
|
|
||||||
|
// root is at ldr.Root()
|
||||||
|
root filesys.ConfirmedDir
|
||||||
|
|
||||||
|
rFactory *resmap.Factory
|
||||||
|
|
||||||
|
// destination directory in newDir that mirrors root
|
||||||
|
dst string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run attempts to localize the kustomization root at target with the given localize arguments
|
||||||
|
// and returns the path to the created newDir.
|
||||||
|
func Run(target, scope, newDir string, fSys filesys.FileSystem) (string, error) {
|
||||||
|
ldr, args, err := NewLoader(target, scope, newDir, fSys)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err)
|
||||||
|
}
|
||||||
|
defer func() { _ = ldr.Cleanup() }()
|
||||||
|
|
||||||
|
toDst, err := filepath.Rel(args.Scope.String(), args.Target.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("cannot find path from %q to child directory %q: %s", args.Scope, args.Target, err)
|
||||||
|
}
|
||||||
|
dst := args.NewDir.Join(toDst)
|
||||||
|
if err = fSys.MkdirAll(dst); err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "unable to create directory in localize destination")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = (&localizer{
|
||||||
|
fSys: fSys,
|
||||||
|
ldr: ldr,
|
||||||
|
root: args.Target,
|
||||||
|
rFactory: resmap.NewFactory(provider.NewDepProvider().GetResourceFactory()),
|
||||||
|
dst: dst,
|
||||||
|
}).localize()
|
||||||
|
if err != nil {
|
||||||
|
errCleanup := fSys.RemoveAll(args.NewDir.String())
|
||||||
|
if errCleanup != nil {
|
||||||
|
log.Printf("unable to clean localize destination: %s", errCleanup)
|
||||||
|
}
|
||||||
|
return "", errors.WrapPrefixf(err, "unable to localize target %q", target)
|
||||||
|
}
|
||||||
|
return args.NewDir.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localize localizes the root that lc is at
|
||||||
|
func (lc *localizer) localize() error {
|
||||||
|
kustomization, kustFileName, err := lc.load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = lc.localizeNativeFields(kustomization)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = lc.localizeBuiltinPlugins(kustomization)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := yaml.Marshal(kustomization)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to serialize localized kustomization file")
|
||||||
|
}
|
||||||
|
if err = lc.fSys.WriteFile(filepath.Join(lc.dst, kustFileName), content); err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to write localized kustomization file")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// load returns the kustomization at lc.root and the file name under which it was found
|
||||||
|
func (lc *localizer) load() (*types.Kustomization, string, error) {
|
||||||
|
content, kustFileName, err := target.LoadKustFile(lc.ldr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var kust types.Kustomization
|
||||||
|
err = (&kust).Unmarshal(content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", errors.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Localize intentionally does not replace legacy fields to return a localized kustomization
|
||||||
|
// with as much resemblance to the original as possible.
|
||||||
|
// Localize also intentionally does not enforce fields, as localize does not wish to unnecessarily
|
||||||
|
// repeat the responsibilities of kustomize build.
|
||||||
|
|
||||||
|
return &kust, kustFileName, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeNativeFields localizes paths on kustomize-native fields, like configMapGenerator, that kustomize has a
|
||||||
|
// built-in understanding of. This excludes helm-related fields, such as `helmGlobals` and `helmCharts`.
|
||||||
|
func (lc *localizer) localizeNativeFields(kust *types.Kustomization) error {
|
||||||
|
if path, exists := kust.OpenAPI["path"]; exists {
|
||||||
|
locPath, err := lc.localizeFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize openapi path")
|
||||||
|
}
|
||||||
|
kust.OpenAPI["path"] = locPath
|
||||||
|
}
|
||||||
|
|
||||||
|
for fieldName, field := range map[string]struct {
|
||||||
|
paths []string
|
||||||
|
locFn func(string) (string, error)
|
||||||
|
}{
|
||||||
|
"bases": {
|
||||||
|
// Allow use of deprecated field
|
||||||
|
//nolint:staticcheck
|
||||||
|
kust.Bases,
|
||||||
|
lc.localizeRoot,
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
kust.Components,
|
||||||
|
lc.localizeRoot,
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
kust.Configurations,
|
||||||
|
lc.localizeFile,
|
||||||
|
},
|
||||||
|
"crds": {
|
||||||
|
kust.Crds,
|
||||||
|
lc.localizeFile,
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
kust.Resources,
|
||||||
|
lc.localizeResource,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
for i, path := range field.paths {
|
||||||
|
locPath, err := field.locFn(path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize %s entry", fieldName)
|
||||||
|
}
|
||||||
|
field.paths[i] = locPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range kust.ConfigMapGenerator {
|
||||||
|
if err := lc.localizeGenerator(&kust.ConfigMapGenerator[i].GeneratorArgs); err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize configMapGenerator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range kust.SecretGenerator {
|
||||||
|
if err := lc.localizeGenerator(&kust.SecretGenerator[i].GeneratorArgs); err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize secretGenerator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := lc.localizeHelmInflationGenerator(kust); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := lc.localizeHelmCharts(kust); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := lc.localizePatches(kust.Patches); err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize patches")
|
||||||
|
}
|
||||||
|
//nolint:staticcheck
|
||||||
|
if err := lc.localizePatches(kust.PatchesJson6902); err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize patchesJson6902")
|
||||||
|
}
|
||||||
|
//nolint:staticcheck
|
||||||
|
for i, patch := range kust.PatchesStrategicMerge {
|
||||||
|
locPath, err := lc.localizeK8sResource(string(patch))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize patchesStrategicMerge entry")
|
||||||
|
}
|
||||||
|
kust.PatchesStrategicMerge[i] = types.PatchStrategicMerge(locPath)
|
||||||
|
}
|
||||||
|
for i, replacement := range kust.Replacements {
|
||||||
|
locPath, err := lc.localizeFile(replacement.Path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize replacements entry")
|
||||||
|
}
|
||||||
|
kust.Replacements[i].Path = locPath
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeGenerator localizes the file paths on generator.
|
||||||
|
func (lc *localizer) localizeGenerator(generator *types.GeneratorArgs) error {
|
||||||
|
locEnvSrc, err := lc.localizeFile(generator.EnvSource)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize generator env file")
|
||||||
|
}
|
||||||
|
locEnvs := make([]string, len(generator.EnvSources))
|
||||||
|
for i, env := range generator.EnvSources {
|
||||||
|
locEnvs[i], err = lc.localizeFile(env)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize generator envs file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
locFiles := make([]string, len(generator.FileSources))
|
||||||
|
for i, file := range generator.FileSources {
|
||||||
|
locFiles[i], err = lc.localizeFileSource(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generator.EnvSource = locEnvSrc
|
||||||
|
generator.EnvSources = locEnvs
|
||||||
|
generator.FileSources = locFiles
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeFileSource returns the localized file source found in configMap and
|
||||||
|
// secretGenerators.
|
||||||
|
func (lc *localizer) localizeFileSource(source string) (string, error) {
|
||||||
|
key, file, err := generators.ParseFileSource(source)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err)
|
||||||
|
}
|
||||||
|
locFile, err := lc.localizeFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "invalid file source %q", source)
|
||||||
|
}
|
||||||
|
var locSource string
|
||||||
|
if source == file {
|
||||||
|
locSource = locFile
|
||||||
|
} else {
|
||||||
|
locSource = key + "=" + locFile
|
||||||
|
}
|
||||||
|
return locSource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeHelmInflationGenerator localizes helmChartInflationGenerator on kust.
|
||||||
|
// localizeHelmInflationGenerator localizes values files and copies local chart homes.
|
||||||
|
func (lc *localizer) localizeHelmInflationGenerator(kust *types.Kustomization) error {
|
||||||
|
for i, chart := range kust.HelmChartInflationGenerator {
|
||||||
|
locFile, err := lc.localizeFile(chart.Values)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize helmChartInflationGenerator entry %d values", i)
|
||||||
|
}
|
||||||
|
kust.HelmChartInflationGenerator[i].Values = locFile
|
||||||
|
|
||||||
|
locDir, err := lc.copyChartHomeEntry(chart.ChartHome)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to copy helmChartInflationGenerator entry %d", i)
|
||||||
|
}
|
||||||
|
kust.HelmChartInflationGenerator[i].ChartHome = locDir
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeHelmCharts localizes helmCharts and helmGlobals on kust.
|
||||||
|
// localizeHelmCharts localizes values files and copies a local chart home.
|
||||||
|
func (lc *localizer) localizeHelmCharts(kust *types.Kustomization) error {
|
||||||
|
for i, chart := range kust.HelmCharts {
|
||||||
|
locFile, err := lc.localizeFile(chart.ValuesFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize helmCharts entry %d valuesFile", i)
|
||||||
|
}
|
||||||
|
kust.HelmCharts[i].ValuesFile = locFile
|
||||||
|
|
||||||
|
for j, valuesFile := range chart.AdditionalValuesFiles {
|
||||||
|
locFile, err = lc.localizeFile(valuesFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize helmCharts entry %d additionalValuesFiles", i)
|
||||||
|
}
|
||||||
|
kust.HelmCharts[i].AdditionalValuesFiles[j] = locFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if kust.HelmGlobals != nil {
|
||||||
|
locDir, err := lc.copyChartHomeEntry(kust.HelmGlobals.ChartHome)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to copy helmGlobals")
|
||||||
|
}
|
||||||
|
kust.HelmGlobals.ChartHome = locDir
|
||||||
|
} else if len(kust.HelmCharts) > 0 {
|
||||||
|
_, err := lc.copyChartHomeEntry("")
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to copy default chart home")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizePatches localizes the file paths on patches if they are non-empty
|
||||||
|
func (lc *localizer) localizePatches(patches []types.Patch) error {
|
||||||
|
for i := range patches {
|
||||||
|
locPath, err := lc.localizeFile(patches[i].Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
patches[i].Path = locPath
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeResource localizes resource path, a file or root, and returns the
|
||||||
|
// localized path
|
||||||
|
func (lc *localizer) localizeResource(path string) (string, error) {
|
||||||
|
var locPath string
|
||||||
|
|
||||||
|
content, fileErr := lc.ldr.Load(path)
|
||||||
|
// The following check catches the case where path is a repo root.
|
||||||
|
// Load on a repo will successfully return its README in HTML.
|
||||||
|
// Because HTML does not follow resource formatting, we then correctly try
|
||||||
|
// to localize path as a root.
|
||||||
|
if fileErr == nil {
|
||||||
|
_, resErr := lc.rFactory.NewResMapFromBytes(content)
|
||||||
|
if resErr != nil {
|
||||||
|
fileErr = errors.WrapPrefixf(resErr, "invalid resource at file %q", path)
|
||||||
|
} else {
|
||||||
|
locPath, fileErr = lc.localizeFileWithContent(path, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if fileErr != nil {
|
||||||
|
var rootErr error
|
||||||
|
locPath, rootErr = lc.localizeRoot(path)
|
||||||
|
if rootErr != nil {
|
||||||
|
err := PathLocalizeError{
|
||||||
|
Path: path,
|
||||||
|
FileError: fileErr,
|
||||||
|
RootError: rootErr,
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeFile localizes file path if set and returns the localized path
|
||||||
|
func (lc *localizer) localizeFile(path string) (string, error) {
|
||||||
|
// Some localizable fields can be empty, for example, replacements.path.
|
||||||
|
// We rely on the build command to throw errors for the ones that cannot.
|
||||||
|
if path == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
content, err := lc.ldr.Load(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err)
|
||||||
|
}
|
||||||
|
return lc.localizeFileWithContent(path, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeFileWithContent writes content to the localized file path and returns the localized path.
|
||||||
|
func (lc *localizer) localizeFileWithContent(path string, content []byte) (string, error) {
|
||||||
|
var locPath string
|
||||||
|
if loader.IsRemoteFile(path) {
|
||||||
|
if lc.fSys.Exists(lc.root.Join(LocalizeDir)) {
|
||||||
|
return "", errors.Errorf("%s already contains %s needed to store file %q", lc.root, LocalizeDir, path)
|
||||||
|
}
|
||||||
|
locPath = locFilePath(path)
|
||||||
|
} else {
|
||||||
|
// ldr has checked that path must be relative; this is subject to change in beta.
|
||||||
|
|
||||||
|
// We must clean path to:
|
||||||
|
// 1. avoid symlinks. A `kustomize build` run will fail if we write files to
|
||||||
|
// symlink paths outside the current root, given that we don't want to recreate
|
||||||
|
// the symlinks. Even worse, we could be writing files outside the localize destination.
|
||||||
|
// 2. avoid paths that temporarily traverse outside the current root,
|
||||||
|
// i.e. ../../../scope/target/current-root. The localized file will be surrounded by
|
||||||
|
// different directories than its source, and so an uncleaned path may no longer be valid.
|
||||||
|
locPath = cleanedRelativePath(lc.fSys, lc.root, path)
|
||||||
|
}
|
||||||
|
absPath := filepath.Join(lc.dst, locPath)
|
||||||
|
if err := lc.fSys.MkdirAll(filepath.Dir(absPath)); err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "unable to create directories to localize file %q", path)
|
||||||
|
}
|
||||||
|
if err := lc.fSys.WriteFile(absPath, content); err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "unable to localize file %q", path)
|
||||||
|
}
|
||||||
|
return locPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeRoot localizes root path if set and returns the localized path
|
||||||
|
func (lc *localizer) localizeRoot(path string) (string, error) {
|
||||||
|
if path == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
ldr, err := lc.ldr.New(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err)
|
||||||
|
}
|
||||||
|
defer func() { _ = ldr.Cleanup() }()
|
||||||
|
|
||||||
|
root, err := filesys.ConfirmDir(lc.fSys, ldr.Root())
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("unable to establish validated root reference %q: %s", path, err)
|
||||||
|
}
|
||||||
|
var locPath string
|
||||||
|
if repo := ldr.Repo(); repo != "" {
|
||||||
|
if lc.fSys.Exists(lc.root.Join(LocalizeDir)) {
|
||||||
|
return "", errors.Errorf("%s already contains %s needed to store root %q", lc.root, LocalizeDir, path)
|
||||||
|
}
|
||||||
|
locPath, err = locRootPath(path, repo, root, lc.fSys)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
locPath, err = filepath.Rel(lc.root.String(), root.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("cannot find relative path between scoped localize roots %q and %q: %s", lc.root, root, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newDst := filepath.Join(lc.dst, locPath)
|
||||||
|
if err = lc.fSys.MkdirAll(newDst); err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "unable to create root %q in localize destination", path)
|
||||||
|
}
|
||||||
|
err = (&localizer{
|
||||||
|
fSys: lc.fSys,
|
||||||
|
ldr: ldr,
|
||||||
|
root: root,
|
||||||
|
rFactory: lc.rFactory,
|
||||||
|
dst: newDst,
|
||||||
|
}).localize()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "unable to localize root %q", path)
|
||||||
|
}
|
||||||
|
return locPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyChartHomeEntry copies the helm chart home entry to lc dst
|
||||||
|
// at the same location relative to the root and returns said relative path.
|
||||||
|
// If entry is empty, copyChartHomeEntry returns the empty string.
|
||||||
|
// If entry does not exist, copyChartHome returns entry.
|
||||||
|
//
|
||||||
|
// copyChartHomeEntry copies the default home to the same location at dst,
|
||||||
|
// without following symlinks. An empty entry also indicates the default home.
|
||||||
|
func (lc *localizer) copyChartHomeEntry(entry string) (string, error) {
|
||||||
|
path := entry
|
||||||
|
if entry == "" {
|
||||||
|
path = types.HelmDefaultHome
|
||||||
|
}
|
||||||
|
if filepath.IsAbs(path) {
|
||||||
|
return "", errors.Errorf("absolute path %q not handled in alpha", path)
|
||||||
|
}
|
||||||
|
isDefault := lc.root.Join(path) == lc.root.Join(types.HelmDefaultHome)
|
||||||
|
locPath, err := lc.copyChartHome(path, !isDefault)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "unable to copy home %q", entry)
|
||||||
|
}
|
||||||
|
if entry == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return locPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyChartHome copies path relative to lc root to dst and returns the
|
||||||
|
// copied location relative to dst. If clean is true, copyChartHome uses path's
|
||||||
|
// delinked location as the copy destination.
|
||||||
|
//
|
||||||
|
// If path does not exist, copyChartHome returns path.
|
||||||
|
func (lc *localizer) copyChartHome(path string, clean bool) (string, error) {
|
||||||
|
path, err := filepath.Rel(lc.root.String(), lc.root.Join(path))
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "no path to chart home %q", path)
|
||||||
|
}
|
||||||
|
// Chart home may serve as untar destination.
|
||||||
|
// Note that we don't check if path is in scope.
|
||||||
|
if !lc.fSys.Exists(lc.root.Join(path)) {
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
// Perform localize directory checks.
|
||||||
|
ldr, err := lc.ldr.New(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "invalid chart home")
|
||||||
|
}
|
||||||
|
cleaned, err := filesys.ConfirmDir(lc.fSys, ldr.Root())
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("unable to confirm validated directory %q: %s", ldr.Root(), err)
|
||||||
|
}
|
||||||
|
toDst := path
|
||||||
|
if clean {
|
||||||
|
toDst, err = filepath.Rel(lc.root.String(), cleaned.String())
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("no path between scoped directories %q and %q: %s", lc.root, cleaned, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Note this check does not guarantee that we copied the entire directory.
|
||||||
|
if dst := filepath.Join(lc.dst, toDst); !lc.fSys.Exists(dst) {
|
||||||
|
err = lc.copyDir(cleaned, filepath.Join(lc.dst, toDst))
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.WrapPrefixf(err, "unable to copy chart home %q", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toDst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyDir copies src to dst. copyDir does not follow symlinks.
|
||||||
|
func (lc *localizer) copyDir(src filesys.ConfirmedDir, dst string) error {
|
||||||
|
err := lc.fSys.Walk(src.String(),
|
||||||
|
func(path string, info fs.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pathToCreate, err := filepath.Rel(src.String(), path)
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("no path from %q to child file %q: %s", src, path, err)
|
||||||
|
}
|
||||||
|
pathInDst := filepath.Join(dst, pathToCreate)
|
||||||
|
if info.Mode()&os.ModeSymlink == os.ModeSymlink {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
err = lc.fSys.MkdirAll(pathInDst)
|
||||||
|
} else {
|
||||||
|
var content []byte
|
||||||
|
content, err = lc.fSys.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
err = lc.fSys.WriteFile(pathInDst, content)
|
||||||
|
}
|
||||||
|
return errors.Wrap(err)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to copy directory %q", src)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeBuiltinPlugins localizes built-in plugins on kust that can contain file paths. The built-in plugins
|
||||||
|
// can be inline or in a file. This excludes the HelmChartInflationGenerator.
|
||||||
|
//
|
||||||
|
// Note that the localization in this function has not been implemented yet.
|
||||||
|
func (lc *localizer) localizeBuiltinPlugins(kust *types.Kustomization) error {
|
||||||
|
for fieldName, entries := range map[string][]string{
|
||||||
|
"generators": kust.Generators,
|
||||||
|
"transformers": kust.Transformers,
|
||||||
|
"validators": kust.Validators,
|
||||||
|
} {
|
||||||
|
for i, entry := range entries {
|
||||||
|
rm, isPath, err := lc.loadK8sResource(entry)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to load %s entry", fieldName)
|
||||||
|
}
|
||||||
|
err = rm.ApplyFilter(&localizeBuiltinPlugins{lc: lc})
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err)
|
||||||
|
}
|
||||||
|
localizedPlugin, err := rm.AsYaml()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to serialize localized %s entry %q", fieldName, entry)
|
||||||
|
}
|
||||||
|
var localizedEntry string
|
||||||
|
if isPath {
|
||||||
|
localizedEntry, err = lc.localizeFileWithContent(entry, localizedPlugin)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WrapPrefixf(err, "unable to localize %s entry", fieldName)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
localizedEntry = string(localizedPlugin)
|
||||||
|
}
|
||||||
|
entries[i] = localizedEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// localizeK8sResource returns the localized resourceEntry if it is a file
|
||||||
|
// containing a kubernetes resource.
|
||||||
|
// localizeK8sResource returns resourceEntry if it is an inline resource.
|
||||||
|
func (lc *localizer) localizeK8sResource(resourceEntry string) (string, error) {
|
||||||
|
_, isFile, err := lc.loadK8sResource(resourceEntry)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if isFile {
|
||||||
|
return lc.localizeFile(resourceEntry)
|
||||||
|
}
|
||||||
|
return resourceEntry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadK8sResource tries to load resourceEntry as a file path or inline
|
||||||
|
// kubernetes resource.
|
||||||
|
// On success, loadK8sResource returns the loaded resource map and whether
|
||||||
|
// resourceEntry is a file path.
|
||||||
|
func (lc *localizer) loadK8sResource(resourceEntry string) (resmap.ResMap, bool, error) {
|
||||||
|
rm, inlineErr := lc.rFactory.NewResMapFromBytes([]byte(resourceEntry))
|
||||||
|
if inlineErr != nil {
|
||||||
|
var fileErr error
|
||||||
|
rm, fileErr = lc.rFactory.FromFile(lc.ldr, resourceEntry)
|
||||||
|
if fileErr != nil {
|
||||||
|
err := ResourceLoadError{
|
||||||
|
InlineError: inlineErr,
|
||||||
|
FileError: fileErr,
|
||||||
|
}
|
||||||
|
return nil, false, errors.WrapPrefixf(err, "unable to load resource entry %q", resourceEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rm, inlineErr != nil, nil
|
||||||
|
}
|
||||||
1629
api/internal/localizer/localizer_test.go
Normal file
1629
api/internal/localizer/localizer_test.go
Normal file
File diff suppressed because it is too large
Load Diff
132
api/internal/localizer/locloader.go
Normal file
132
api/internal/localizer/locloader.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2022 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package localizer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/git"
|
||||||
|
"sigs.k8s.io/kustomize/api/internal/loader"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/errors"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Args holds localize arguments
|
||||||
|
type Args struct {
|
||||||
|
// target; local copy if remote
|
||||||
|
Target filesys.ConfirmedDir
|
||||||
|
|
||||||
|
// directory that bounds target's local references
|
||||||
|
// repo directory of local copy if target is remote
|
||||||
|
Scope filesys.ConfirmedDir
|
||||||
|
|
||||||
|
// localize destination
|
||||||
|
NewDir filesys.ConfirmedDir
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loader is an ifc.Loader that enforces additional constraints specific to kustomize localize.
|
||||||
|
type Loader struct {
|
||||||
|
fSys filesys.FileSystem
|
||||||
|
|
||||||
|
args *Args
|
||||||
|
|
||||||
|
// loader at Loader's current directory
|
||||||
|
ifc.Loader
|
||||||
|
|
||||||
|
// whether Loader and all its ancestors are the result of local references
|
||||||
|
local bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ifc.Loader = &Loader{}
|
||||||
|
|
||||||
|
// NewLoader is the factory method for Loader, under localize constraints, at rawTarget. For invalid localize arguments,
|
||||||
|
// NewLoader returns an error.
|
||||||
|
func NewLoader(rawTarget string, rawScope string, rawNewDir string, fSys filesys.FileSystem) (*Loader, Args, error) {
|
||||||
|
// check earlier to avoid cleanup
|
||||||
|
repoSpec, err := git.NewRepoSpecFromURL(rawTarget)
|
||||||
|
if err == nil && repoSpec.Ref == "" {
|
||||||
|
return nil, Args{}, errors.Errorf("localize remote root %q missing ref query string parameter", rawTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
// for security, should enforce load restrictions
|
||||||
|
ldr, err := loader.NewLoader(loader.RestrictionRootOnly, rawTarget, fSys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, Args{}, errors.WrapPrefixf(err, "unable to establish localize target %q", rawTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
scope, err := establishScope(rawScope, rawTarget, ldr, fSys)
|
||||||
|
if err != nil {
|
||||||
|
_ = ldr.Cleanup()
|
||||||
|
return nil, Args{}, errors.WrapPrefixf(err, "invalid localize scope %q", rawScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
newDir, err := createNewDir(rawNewDir, ldr, repoSpec, fSys)
|
||||||
|
if err != nil {
|
||||||
|
_ = ldr.Cleanup()
|
||||||
|
return nil, Args{}, errors.WrapPrefixf(err, "invalid localize destination %q", rawNewDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := Args{
|
||||||
|
Target: filesys.ConfirmedDir(ldr.Root()),
|
||||||
|
Scope: scope,
|
||||||
|
NewDir: newDir,
|
||||||
|
}
|
||||||
|
return &Loader{
|
||||||
|
fSys: fSys,
|
||||||
|
args: &args,
|
||||||
|
Loader: ldr,
|
||||||
|
local: scope != "",
|
||||||
|
}, args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load returns the contents of path if path is a valid localize file.
|
||||||
|
// Otherwise, Load returns an error.
|
||||||
|
func (ll *Loader) Load(path string) ([]byte, error) {
|
||||||
|
// checks in root, and thus in scope
|
||||||
|
content, err := ll.Loader.Load(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WrapPrefixf(err, "invalid file reference")
|
||||||
|
}
|
||||||
|
if !loader.IsRemoteFile(path) && ll.local {
|
||||||
|
cleanPath := cleanedRelativePath(ll.fSys, filesys.ConfirmedDir(ll.Root()), path)
|
||||||
|
cleanAbs := filepath.Join(ll.Root(), cleanPath)
|
||||||
|
dir := filesys.ConfirmedDir(filepath.Dir(cleanAbs))
|
||||||
|
// target cannot reference newDir, as this load would've failed prior to localize;
|
||||||
|
// not a problem if remote because then reference could only be in newDir if repo copy,
|
||||||
|
// which will be cleaned, is inside newDir
|
||||||
|
if dir.HasPrefix(ll.args.NewDir) {
|
||||||
|
return nil, errors.Errorf("file %q at %q enters localize destination %q", path, cleanAbs, ll.args.NewDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a Loader at path if path is a valid localize root.
|
||||||
|
// Otherwise, New returns an error.
|
||||||
|
func (ll *Loader) New(path string) (ifc.Loader, error) {
|
||||||
|
ldr, err := ll.Loader.New(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WrapPrefixf(err, "invalid root reference")
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo := ldr.Repo(); repo == "" {
|
||||||
|
if ll.local && !filesys.ConfirmedDir(ldr.Root()).HasPrefix(ll.args.Scope) {
|
||||||
|
return nil, errors.Errorf("root %q outside localize scope %q", ldr.Root(), ll.args.Scope)
|
||||||
|
}
|
||||||
|
if ll.local && filesys.ConfirmedDir(ldr.Root()).HasPrefix(ll.args.NewDir) {
|
||||||
|
return nil, errors.Errorf(
|
||||||
|
"root %q references into localize destination %q", ldr.Root(), ll.args.NewDir)
|
||||||
|
}
|
||||||
|
} else if !hasRef(path) {
|
||||||
|
return nil, errors.Errorf("localize remote root %q missing ref query string parameter", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Loader{
|
||||||
|
fSys: ll.fSys,
|
||||||
|
args: ll.args,
|
||||||
|
Loader: ldr,
|
||||||
|
local: ll.local && ldr.Repo() == "",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
298
api/internal/localizer/locloader_test.go
Normal file
298
api/internal/localizer/locloader_test.go
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
// Copyright 2022 The Kubernetes Authors.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package localizer_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"sigs.k8s.io/kustomize/api/ifc"
|
||||||
|
. "sigs.k8s.io/kustomize/api/internal/localizer"
|
||||||
|
"sigs.k8s.io/kustomize/kyaml/filesys"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkNewLoader(req *require.Assertions, ldr *Loader, args *Args, target string, scope string, newDir string, fSys filesys.FileSystem) {
|
||||||
|
checkLoader(req, ldr, target)
|
||||||
|
checkArgs(req, args, target, scope, newDir, fSys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLoader(req *require.Assertions, ldr ifc.Loader, root string) {
|
||||||
|
req.Equal(root, ldr.Root())
|
||||||
|
req.Empty(ldr.Repo())
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkArgs(req *require.Assertions, args *Args, target string, scope string, newDir string, fSys filesys.FileSystem) {
|
||||||
|
req.Equal(target, args.Target.String())
|
||||||
|
req.Equal(scope, args.Scope.String())
|
||||||
|
req.Equal(newDir, args.NewDir.String())
|
||||||
|
req.True(fSys.Exists(newDir))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLocalLoadNewAndCleanup(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
fSys := makeMemoryFs(t)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer func() {
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
}()
|
||||||
|
// typical setup
|
||||||
|
ldr, args, err := NewLoader("a", "/", "/newDir", fSys)
|
||||||
|
req.NoError(err)
|
||||||
|
checkNewLoader(req, ldr, &args, "/a", "/", "/newDir", fSys)
|
||||||
|
|
||||||
|
fSysCopy := makeMemoryFs(t)
|
||||||
|
req.NoError(fSysCopy.Mkdir("/newDir"))
|
||||||
|
req.Equal(fSysCopy, fSys)
|
||||||
|
|
||||||
|
// easy load directly in root
|
||||||
|
content, err := ldr.Load("pod.yaml")
|
||||||
|
req.NoError(err)
|
||||||
|
req.Equal([]byte(podConfiguration), content)
|
||||||
|
|
||||||
|
// typical sibling root reference
|
||||||
|
sibLdr, err := ldr.New("../alpha")
|
||||||
|
req.NoError(err)
|
||||||
|
checkLoader(req, sibLdr, "/alpha")
|
||||||
|
|
||||||
|
// only need to test once, since don't need to call Cleanup() on local target
|
||||||
|
req.NoError(sibLdr.Cleanup())
|
||||||
|
req.NoError(ldr.Cleanup())
|
||||||
|
|
||||||
|
// file system and buffer checks are also one-time
|
||||||
|
req.Equal(fSysCopy, fSys)
|
||||||
|
req.Empty(buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewLocLoaderDefaultForRootTarget(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
target string
|
||||||
|
scope string
|
||||||
|
}{
|
||||||
|
"explicit": {
|
||||||
|
"/",
|
||||||
|
".",
|
||||||
|
},
|
||||||
|
"implicit": {
|
||||||
|
".",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, params := range cases {
|
||||||
|
params := params
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
fSys := makeMemoryFs(t)
|
||||||
|
|
||||||
|
ldr, args, err := NewLoader(params.target, params.scope, "", fSys)
|
||||||
|
req.NoError(err)
|
||||||
|
checkNewLoader(req, ldr, &args, "/", "/", "/"+DstPrefix, fSys)
|
||||||
|
|
||||||
|
// file in root, but nested
|
||||||
|
content, err := ldr.Load("a/pod.yaml")
|
||||||
|
req.NoError(err)
|
||||||
|
req.Equal([]byte(podConfiguration), content)
|
||||||
|
|
||||||
|
childLdr, err := ldr.New("a")
|
||||||
|
req.NoError(err)
|
||||||
|
checkLoader(req, childLdr, "/a")
|
||||||
|
|
||||||
|
// messy, uncleaned path
|
||||||
|
content, err = childLdr.Load("./../a/pod.yaml")
|
||||||
|
req.NoError(err)
|
||||||
|
req.Equal([]byte(podConfiguration), content)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewMultiple(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
fSys := makeMemoryFs(t)
|
||||||
|
|
||||||
|
// default destination for non-file system root target
|
||||||
|
// destination outside of scope
|
||||||
|
ldr, args, err := NewLoader("/alpha/beta", "/alpha", "", fSys)
|
||||||
|
req.NoError(err)
|
||||||
|
checkNewLoader(req, ldr, &args, "/alpha/beta", "/alpha", "/"+DstPrefix+"-beta", fSys)
|
||||||
|
|
||||||
|
// nested child root that isn't cleaned
|
||||||
|
descLdr, err := ldr.New("../beta/gamma/delta")
|
||||||
|
req.NoError(err)
|
||||||
|
checkLoader(req, descLdr, "/alpha/beta/gamma/delta")
|
||||||
|
|
||||||
|
// upwards traversal
|
||||||
|
higherLdr, err := descLdr.New("../../say")
|
||||||
|
req.NoError(err)
|
||||||
|
checkLoader(req, higherLdr, "/alpha/beta/say")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeWdFs(t *testing.T) map[string]filesys.FileSystem {
|
||||||
|
t.Helper()
|
||||||
|
req := require.New(t)
|
||||||
|
|
||||||
|
root := filesys.MakeEmptyDirInMemory()
|
||||||
|
req.NoError(root.MkdirAll("a/b/c/d/e"))
|
||||||
|
|
||||||
|
outer, err := root.Find("a")
|
||||||
|
req.NoError(err)
|
||||||
|
middle, err := root.Find("a/b/c")
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
return map[string]filesys.FileSystem{
|
||||||
|
"a": outer,
|
||||||
|
"a/b/c": middle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewLocLoaderCwdNotRoot(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
wd string
|
||||||
|
target string
|
||||||
|
scope string
|
||||||
|
newDir string
|
||||||
|
}{
|
||||||
|
// target not immediate child of scope
|
||||||
|
"outer dir": {
|
||||||
|
"a",
|
||||||
|
"b/c/d/e",
|
||||||
|
"b/c",
|
||||||
|
"b/newDir",
|
||||||
|
},
|
||||||
|
"scope": {
|
||||||
|
"a/b/c",
|
||||||
|
"d/e",
|
||||||
|
".",
|
||||||
|
"d/e/newDir",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, test := range cases {
|
||||||
|
test := test
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
fSys := makeWdFs(t)[test.wd]
|
||||||
|
|
||||||
|
ldr, args, err := NewLoader(test.target, test.scope, test.newDir, fSys)
|
||||||
|
req.NoError(err)
|
||||||
|
checkLoader(req, ldr, "a/b/c/d/e")
|
||||||
|
|
||||||
|
req.Equal("a/b/c/d/e", args.Target.String())
|
||||||
|
req.Equal("a/b/c", args.Scope.String())
|
||||||
|
req.Equal(test.wd+"/"+test.newDir, args.NewDir.String())
|
||||||
|
// memory file system can only find paths rooted at current node
|
||||||
|
req.True(fSys.Exists(test.newDir))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewLocLoaderFails(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
target string
|
||||||
|
scope string
|
||||||
|
dest string
|
||||||
|
}{
|
||||||
|
"non-existent target": {
|
||||||
|
"/b",
|
||||||
|
"/",
|
||||||
|
"/newDir",
|
||||||
|
},
|
||||||
|
"file target": {
|
||||||
|
"/a/pod.yaml",
|
||||||
|
"/",
|
||||||
|
"/newDir",
|
||||||
|
},
|
||||||
|
"inner scope": {
|
||||||
|
"/alpha",
|
||||||
|
"/alpha/beta",
|
||||||
|
"/newDir",
|
||||||
|
},
|
||||||
|
"side scope": {
|
||||||
|
"/alpha",
|
||||||
|
"/a",
|
||||||
|
"/newDir",
|
||||||
|
},
|
||||||
|
"existing dst": {
|
||||||
|
"/alpha",
|
||||||
|
"/",
|
||||||
|
"/a",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for name, params := range cases {
|
||||||
|
params := params
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
log.SetOutput(&buf)
|
||||||
|
defer func() {
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, _, err := NewLoader(params.target, params.scope, params.dest, makeMemoryFs(t))
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Empty(t, buf.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewFails(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
fSys := makeMemoryFs(t)
|
||||||
|
|
||||||
|
ldr, args, err := NewLoader("/alpha/beta/gamma", "alpha", "alpha/beta/gamma/newDir", fSys)
|
||||||
|
req.NoError(err)
|
||||||
|
checkNewLoader(req, ldr, &args, "/alpha/beta/gamma", "/alpha", "/alpha/beta/gamma/newDir", fSys)
|
||||||
|
|
||||||
|
cases := map[string]string{
|
||||||
|
"outside scope": "../../../a",
|
||||||
|
"at dst": "newDir",
|
||||||
|
"ancestor": "../../beta",
|
||||||
|
"non-existent root": "delt",
|
||||||
|
"file": "delta/deployment.yaml",
|
||||||
|
}
|
||||||
|
for name, root := range cases {
|
||||||
|
root := root
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
fSys := makeMemoryFs(t)
|
||||||
|
|
||||||
|
ldr, _, err := NewLoader("/alpha/beta/gamma", "alpha", "alpha/beta/gamma/newDir", fSys)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = ldr.New(root)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFails(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
fSys := makeMemoryFs(t)
|
||||||
|
|
||||||
|
ldr, args, err := NewLoader("./a/../a", "/a/../a", "/a/newDir", fSys)
|
||||||
|
req.NoError(err)
|
||||||
|
checkNewLoader(req, ldr, &args, "/a", "/a", "/a/newDir", fSys)
|
||||||
|
|
||||||
|
cases := map[string]string{
|
||||||
|
"directory": "b",
|
||||||
|
"non-existent file": "kubectl.yaml",
|
||||||
|
"file outside root": "../alpha/beta/gamma/delta/deployment.yaml",
|
||||||
|
"inside dst": "newDir/pod.yaml",
|
||||||
|
"winding inside dst": "/a/test/../newDir/pod.yaml",
|
||||||
|
}
|
||||||
|
for name, file := range cases {
|
||||||
|
file := file
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
fSys := makeMemoryFs(t)
|
||||||
|
|
||||||
|
ldr, _, err := NewLoader("./a/../a", "/a/../a", "/a/newDir", fSys)
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
_, err = ldr.Load(file)
|
||||||
|
req.Error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user