mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-03-27 19:08:47 +01:00
Compare commits
1394 Commits
commit-ser
...
v3.2.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1515e91ce8 | ||
|
|
a35e85dcd6 | ||
|
|
5fca1ce7d8 | ||
|
|
65378e6d14 | ||
|
|
e7d33de05c | ||
|
|
48549a2035 | ||
|
|
10c3fd02f4 | ||
|
|
ca08f90e96 | ||
|
|
1f03b27fd5 | ||
|
|
9c128e2d4c | ||
|
|
75eddbd910 | ||
|
|
65b029342d | ||
|
|
2ff406ae33 | ||
|
|
76fc92f655 | ||
|
|
ad117b88a6 | ||
|
|
508da9c791 | ||
|
|
20866f4557 | ||
|
|
e3b108b738 | ||
|
|
c56f4400f2 | ||
|
|
e9d03a633e | ||
|
|
030b4f982b | ||
|
|
fafbd44489 | ||
|
|
d7d9674e33 | ||
|
|
e6f54030f0 | ||
|
|
b4146969ed | ||
|
|
51c6375130 | ||
|
|
b67eb40a45 | ||
|
|
8a0633b74a | ||
|
|
0d4f505954 | ||
|
|
2b6251dfed | ||
|
|
8f903c3a11 | ||
|
|
8d0dde1388 | ||
|
|
784f62ca6d | ||
|
|
33b5043405 | ||
|
|
88fe638aff | ||
|
|
a29703877e | ||
|
|
95e7cdb16f | ||
|
|
122f4db3db | ||
|
|
2d65b26420 | ||
|
|
0ace9bb9a3 | ||
|
|
6398ec3dcb | ||
|
|
732b16fb2a | ||
|
|
024c7e6020 | ||
|
|
26b7fb2c61 | ||
|
|
8c4ab63a9c | ||
|
|
29f869c82f | ||
|
|
c11e67d4bf | ||
|
|
a0a18438ab | ||
|
|
dabdf39772 | ||
|
|
cd8df1721c | ||
|
|
27c5065308 | ||
|
|
1545390cd8 | ||
|
|
7bd02d7f02 | ||
|
|
86c9994394 | ||
|
|
6dd5e7a6d2 | ||
|
|
66b2f302d9 | ||
|
|
a1df57df93 | ||
|
|
8884b27381 | ||
|
|
be8e79eb31 | ||
|
|
6aa9c20e47 | ||
|
|
1963030721 | ||
|
|
b227ef1559 | ||
|
|
d1251f407a | ||
|
|
3db95b1fbe | ||
|
|
7628473802 | ||
|
|
059e8d220e | ||
|
|
1ba3929520 | ||
|
|
a42ccaeeca | ||
|
|
d75bcfd7b2 | ||
|
|
35e3897f61 | ||
|
|
dc309cbe0d | ||
|
|
a1f42488d9 | ||
|
|
973eccee0a | ||
|
|
8f8a1ecacb | ||
|
|
46409ae734 | ||
|
|
5f5d46c78b | ||
|
|
722036d447 | ||
|
|
001bfda068 | ||
|
|
4821d71e3d | ||
|
|
ef8ac49807 | ||
|
|
feab307df3 | ||
|
|
087378c669 | ||
|
|
f3c8e1d5e3 | ||
|
|
28510cdda6 | ||
|
|
6a2df4380a | ||
|
|
cd87a13a0d | ||
|
|
1453367645 | ||
|
|
50531e6ab3 | ||
|
|
bf9f927d55 | ||
|
|
ee0de13be4 | ||
|
|
4ac3f920d5 | ||
|
|
06ef059f9f | ||
|
|
45c8fd9d2b | ||
|
|
2bba563a76 | ||
|
|
f13aa46e7f | ||
|
|
3af3a056a2 | ||
|
|
aed63c628d | ||
|
|
22d3ef0ef6 | ||
|
|
e8875bbe7b | ||
|
|
5c10b47d27 | ||
|
|
1680134dc2 | ||
|
|
a330ae4355 | ||
|
|
cd3dc7a1cf | ||
|
|
f4541a60c0 | ||
|
|
81da5ea740 | ||
|
|
c4d99bb224 | ||
|
|
21ec075fd9 | ||
|
|
b834987db9 | ||
|
|
139debe3bb | ||
|
|
f4c4c66f38 | ||
|
|
0793efb5e4 | ||
|
|
15a35daf16 | ||
|
|
4e5b201ba5 | ||
|
|
bb56b9ea67 | ||
|
|
b18ea682c4 | ||
|
|
7fafc99a7a | ||
|
|
ba38778d8c | ||
|
|
48933252b4 | ||
|
|
adf89ea322 | ||
|
|
e492587fb1 | ||
|
|
fed3c7eef7 | ||
|
|
922e459665 | ||
|
|
0fe2a2110c | ||
|
|
1e4cfcc4a0 | ||
|
|
8d018bbf2e | ||
|
|
41f664493e | ||
|
|
939d88c5c6 | ||
|
|
0174fccf28 | ||
|
|
6212ea2afb | ||
|
|
873c2fcfc7 | ||
|
|
2229f9d6fc | ||
|
|
5a8b427322 | ||
|
|
2e5601f932 | ||
|
|
7ae14c89d9 | ||
|
|
8b8d04ecfa | ||
|
|
c64183717b | ||
|
|
d54c8afc09 | ||
|
|
762114c6df | ||
|
|
564e507dd7 | ||
|
|
e4eb86d2db | ||
|
|
bed3d56d17 | ||
|
|
f401a0ee11 | ||
|
|
bc4775468a | ||
|
|
17e5c1f68f | ||
|
|
df324c07d8 | ||
|
|
6028dea3a5 | ||
|
|
b68601255c | ||
|
|
e24d8d4024 | ||
|
|
93148b52c4 | ||
|
|
12d3f5dba1 | ||
|
|
f5a562ac30 | ||
|
|
1268dd9bff | ||
|
|
8b2799c51c | ||
|
|
993344e232 | ||
|
|
670d383f69 | ||
|
|
7829e2c6c1 | ||
|
|
ed752cb540 | ||
|
|
12b1bf5f34 | ||
|
|
52683fdd3e | ||
|
|
3247474212 | ||
|
|
c69d30e52d | ||
|
|
2cfc70afa9 | ||
|
|
728674f922 | ||
|
|
cb2b7faa6d | ||
|
|
a608753071 | ||
|
|
cc39e63e24 | ||
|
|
db7acf8501 | ||
|
|
23f3472f25 | ||
|
|
b96401bb76 | ||
|
|
f953976d92 | ||
|
|
26b970b5bd | ||
|
|
f44de4b854 | ||
|
|
634ef6ff1c | ||
|
|
4a3884f516 | ||
|
|
fd4355baae | ||
|
|
d954789d47 | ||
|
|
66f7b4caa1 | ||
|
|
c447628913 | ||
|
|
9922336968 | ||
|
|
8fa3e47d17 | ||
|
|
ae16c00916 | ||
|
|
267eb2ff0f | ||
|
|
1cec174803 | ||
|
|
05385b3dd8 | ||
|
|
c07768cd64 | ||
|
|
b88527cb39 | ||
|
|
e8f86101f5 | ||
|
|
5e5c4b7d03 | ||
|
|
c7588ffb44 | ||
|
|
6f6c39d8f4 | ||
|
|
4c9291152b | ||
|
|
f2233ccd67 | ||
|
|
871b0b434c | ||
|
|
35331553bf | ||
|
|
3e70033247 | ||
|
|
22c652cf97 | ||
|
|
2ffaf43c1d | ||
|
|
4445dbafb2 | ||
|
|
04cf408264 | ||
|
|
5b8e4b57ac | ||
|
|
88a32d6aab | ||
|
|
51fa4e8a54 | ||
|
|
56320a7b08 | ||
|
|
87faf58733 | ||
|
|
1db5f2e618 | ||
|
|
d269e6f936 | ||
|
|
e6a7c1d4e2 | ||
|
|
91b8bba570 | ||
|
|
29805b0e8f | ||
|
|
69f24f007b | ||
|
|
7168674403 | ||
|
|
24d4cb57c5 | ||
|
|
d135f73160 | ||
|
|
37b0f0f767 | ||
|
|
8c3b78ef88 | ||
|
|
68e5a4a12c | ||
|
|
d154627681 | ||
|
|
e85e353b81 | ||
|
|
c39fde74f0 | ||
|
|
08cd547750 | ||
|
|
4362e8ccb7 | ||
|
|
a06dfeb832 | ||
|
|
b20fd4342f | ||
|
|
1c5d7f1f65 | ||
|
|
49f3c05d7d | ||
|
|
9bc35de19d | ||
|
|
a4919edffb | ||
|
|
9e804f99f0 | ||
|
|
ef8d03cea5 | ||
|
|
6265da106e | ||
|
|
b1b157068e | ||
|
|
7129a2c147 | ||
|
|
6c38186f7f | ||
|
|
d3bdc9d5f3 | ||
|
|
728262ac55 | ||
|
|
928aee5dff | ||
|
|
5ce60ca6e3 | ||
|
|
88fccc91c6 | ||
|
|
7a2dc7e80f | ||
|
|
8657798324 | ||
|
|
98f2760d50 | ||
|
|
7ed0f2300e | ||
|
|
5d5d17ae35 | ||
|
|
bfe8b30d9a | ||
|
|
65a082b12c | ||
|
|
14a22ad926 | ||
|
|
70c8f4612f | ||
|
|
b1a9fab70c | ||
|
|
1e5761c1d0 | ||
|
|
8c8902b93f | ||
|
|
13c47ee244 | ||
|
|
82391027d9 | ||
|
|
0c82f4079b | ||
|
|
97af89a3b3 | ||
|
|
d737f8fe43 | ||
|
|
57cccb65c2 | ||
|
|
9bca4859e0 | ||
|
|
940a489cfa | ||
|
|
7dae82dfd3 | ||
|
|
0984b03805 | ||
|
|
b74c0a0e1a | ||
|
|
eaef25c3eb | ||
|
|
a8cae97da0 | ||
|
|
b2b6d9822b | ||
|
|
da7f11a826 | ||
|
|
2b1f5959bd | ||
|
|
5e2a8a86d0 | ||
|
|
d3de4435ce | ||
|
|
5510bdfd71 | ||
|
|
c67763b069 | ||
|
|
1d6ba890a8 | ||
|
|
2e90919fe6 | ||
|
|
34bc56352c | ||
|
|
e039293b7e | ||
|
|
2a0eac0ca9 | ||
|
|
6a2077642e | ||
|
|
24b0ecc657 | ||
|
|
d7364b4662 | ||
|
|
f78cddf736 | ||
|
|
45a7a18256 | ||
|
|
04d1ca4733 | ||
|
|
6d9b5bdf53 | ||
|
|
90123bac04 | ||
|
|
37b67fa4a5 | ||
|
|
48faed19f1 | ||
|
|
0e42012778 | ||
|
|
1b3ced9261 | ||
|
|
ad2e4450f2 | ||
|
|
90e2148667 | ||
|
|
2558e80f41 | ||
|
|
3c6449da89 | ||
|
|
e5417e1eb3 | ||
|
|
69f7d39717 | ||
|
|
390ea4ff54 | ||
|
|
7e868da310 | ||
|
|
03ac864dde | ||
|
|
27b70cf56e | ||
|
|
8ed3a24d49 | ||
|
|
9a990b7e89 | ||
|
|
ec80ebdf67 | ||
|
|
4dfab5d136 | ||
|
|
8f23c885b6 | ||
|
|
48a7030125 | ||
|
|
38ad19fd95 | ||
|
|
24c08922e5 | ||
|
|
323f993816 | ||
|
|
6ec53193fd | ||
|
|
559744a65e | ||
|
|
3f03097983 | ||
|
|
9928c906a2 | ||
|
|
99710b5183 | ||
|
|
42d4cfb857 | ||
|
|
751550562c | ||
|
|
72d054d772 | ||
|
|
f3dbc6f9de | ||
|
|
16cc1b15af | ||
|
|
aff3ae3f4d | ||
|
|
b8decb798a | ||
|
|
4393f7deb8 | ||
|
|
4024fe7c22 | ||
|
|
678f61b8d3 | ||
|
|
31e0f428e8 | ||
|
|
998253aa41 | ||
|
|
69d1d88807 | ||
|
|
58b0116d75 | ||
|
|
b9daeac44e | ||
|
|
a78a616566 | ||
|
|
d2b881ae4a | ||
|
|
796f72c3d4 | ||
|
|
7da3ecc08f | ||
|
|
fca42e3fd4 | ||
|
|
7f5072f286 | ||
|
|
fe6aaad4f0 | ||
|
|
90eae48c77 | ||
|
|
9895f55781 | ||
|
|
660295f656 | ||
|
|
be2c243ac8 | ||
|
|
8eac64d54c | ||
|
|
c68ec277d4 | ||
|
|
51d88197d7 | ||
|
|
4e63bc7563 | ||
|
|
19415979e8 | ||
|
|
5ac055d2a2 | ||
|
|
dcf1965c52 | ||
|
|
0a1572b9d9 | ||
|
|
853b8dddd3 | ||
|
|
9fffcd50d3 | ||
|
|
835c1fbd3c | ||
|
|
2ed67e8fac | ||
|
|
dd1547fcb4 | ||
|
|
635b9fe8fb | ||
|
|
195b238a37 | ||
|
|
cb61611816 | ||
|
|
a37a4d4073 | ||
|
|
2de6819422 | ||
|
|
df3a45ac02 | ||
|
|
f8aea44398 | ||
|
|
02de363d9c | ||
|
|
79943d8189 | ||
|
|
36f1a59c09 | ||
|
|
d5383de5c5 | ||
|
|
9cc960d07d | ||
|
|
fd78d66f4d | ||
|
|
3e6f11e08e | ||
|
|
e5b83f1d1b | ||
|
|
60adba2d5f | ||
|
|
be37e0aa3d | ||
|
|
ce35b4c484 | ||
|
|
01d00ac952 | ||
|
|
7f5ef5c087 | ||
|
|
1c9bb478e8 | ||
|
|
bc49329691 | ||
|
|
6747cfa28d | ||
|
|
5ee35ad707 | ||
|
|
908c73255e | ||
|
|
b090ee70a8 | ||
|
|
3eb442ed82 | ||
|
|
a5c6898655 | ||
|
|
ac4ae1779e | ||
|
|
d83ef2c224 | ||
|
|
9dfa9db097 | ||
|
|
20e3877633 | ||
|
|
8e00df5326 | ||
|
|
6b6512ae30 | ||
|
|
262c8151ae | ||
|
|
1bc9adb134 | ||
|
|
733350ce7c | ||
|
|
a74d8996b7 | ||
|
|
4e72dd7c55 | ||
|
|
2c4dd51e15 | ||
|
|
1e2a66d5b2 | ||
|
|
ffc3b1a11d | ||
|
|
54e2648b3f | ||
|
|
aa5d1395bc | ||
|
|
4e69156e18 | ||
|
|
9aff762531 | ||
|
|
0cfc2fd861 | ||
|
|
88ce38e450 | ||
|
|
5bd2d0d917 | ||
|
|
ebff248ba8 | ||
|
|
6f1e27e93c | ||
|
|
965c83e016 | ||
|
|
276d92d4e0 | ||
|
|
79f152c1ba | ||
|
|
a3eb4e722e | ||
|
|
3349949835 | ||
|
|
562194b35c | ||
|
|
56f8797a2b | ||
|
|
313e8bf70f | ||
|
|
786b24e2c4 | ||
|
|
69b1f0a33c | ||
|
|
e452870b0e | ||
|
|
e8e39a996e | ||
|
|
6ead52c21c | ||
|
|
9e25f93e03 | ||
|
|
3f44b85a77 | ||
|
|
ff019243a1 | ||
|
|
cd11e44d8b | ||
|
|
f420cce7a5 | ||
|
|
d39c0083ea | ||
|
|
4cd4e5e74e | ||
|
|
2e4af5fa5a | ||
|
|
e692a22b01 | ||
|
|
abbdfa26fd | ||
|
|
36345afeb2 | ||
|
|
bf035b3cb4 | ||
|
|
d58ba040e9 | ||
|
|
09b5cbdda2 | ||
|
|
c012702ce0 | ||
|
|
fb94cad141 | ||
|
|
c94874fd18 | ||
|
|
a90c54599b | ||
|
|
3b1ac4b22d | ||
|
|
0864f1ac95 | ||
|
|
671107cb10 | ||
|
|
5c2b13f07c | ||
|
|
5ca752429e | ||
|
|
1fbd63d095 | ||
|
|
07bd5e0f9e | ||
|
|
be042c4474 | ||
|
|
346a749cde | ||
|
|
04794332d2 | ||
|
|
39b9e4f8c5 | ||
|
|
0f822ff801 | ||
|
|
4d16fdcea4 | ||
|
|
c60a727524 | ||
|
|
6ec1aa1b84 | ||
|
|
a6a78ef8d6 | ||
|
|
99fea7c12e | ||
|
|
0c1eb30b4d | ||
|
|
ca6e205332 | ||
|
|
5107ec1ce3 | ||
|
|
3401d3bf92 | ||
|
|
0a0176f4fd | ||
|
|
d6ecc66216 | ||
|
|
203e07c9a4 | ||
|
|
29df864ae1 | ||
|
|
7d0820f5ca | ||
|
|
8d47727d38 | ||
|
|
3df2883a4d | ||
|
|
54b3c95e84 | ||
|
|
b8ac5ef635 | ||
|
|
986e1f8589 | ||
|
|
ea31d17f53 | ||
|
|
9567183b7c | ||
|
|
460111f7bc | ||
|
|
ac49c67403 | ||
|
|
e9811678fa | ||
|
|
061c1fc7c5 | ||
|
|
e37c3dbd40 | ||
|
|
a1bcd4246e | ||
|
|
4501ebb93f | ||
|
|
e0f4b00126 | ||
|
|
d518f13b2a | ||
|
|
c880373aae | ||
|
|
b0336b8f79 | ||
|
|
9fd0601e52 | ||
|
|
83d553ca51 | ||
|
|
d43fbe6148 | ||
|
|
1b48f363bb | ||
|
|
614c85cb72 | ||
|
|
414d9eb5db | ||
|
|
5c9a5ef9a6 | ||
|
|
d1113970cd | ||
|
|
58d82bedb8 | ||
|
|
4dd9bc7642 | ||
|
|
7f3709374b | ||
|
|
7922c77991 | ||
|
|
20f9081fb4 | ||
|
|
1d09c8c8a1 | ||
|
|
bee23628a8 | ||
|
|
f03ffb3592 | ||
|
|
d4ebcc0c15 | ||
|
|
a671cc9b23 | ||
|
|
49514c9b4c | ||
|
|
f7590fa302 | ||
|
|
8ede222650 | ||
|
|
e25123b63d | ||
|
|
d6efb9e538 | ||
|
|
9849d2743b | ||
|
|
8b2560497a | ||
|
|
2fc05c58e7 | ||
|
|
590d0cda64 | ||
|
|
fa25f278c0 | ||
|
|
127293dcc7 | ||
|
|
b39e3a8d1e | ||
|
|
531b79ad8d | ||
|
|
fb0a89864c | ||
|
|
0893d7349d | ||
|
|
a3ef471913 | ||
|
|
25fc5d2b0c | ||
|
|
c11b36d455 | ||
|
|
3ff30dfac1 | ||
|
|
1b6c8237d3 | ||
|
|
964f26901c | ||
|
|
98ca41125c | ||
|
|
2c53861edb | ||
|
|
54501c05a8 | ||
|
|
0b8d018fdf | ||
|
|
91e5f954a3 | ||
|
|
30c325d952 | ||
|
|
ede2b32aea | ||
|
|
061d5992da | ||
|
|
1203ad0660 | ||
|
|
4731c61d7a | ||
|
|
7cc360e5af | ||
|
|
df5ff1f746 | ||
|
|
6b24fcb32c | ||
|
|
114693dcc2 | ||
|
|
10fa1aa8c8 | ||
|
|
28e871e7e2 | ||
|
|
a91a6594a1 | ||
|
|
52ad029110 | ||
|
|
fd48c47cf9 | ||
|
|
b6e90ea92c | ||
|
|
c6ebc19c1f | ||
|
|
c2e93a3e95 | ||
|
|
79c75876a8 | ||
|
|
2801a11efc | ||
|
|
64198e8042 | ||
|
|
dc1d148a5d | ||
|
|
7a064000a0 | ||
|
|
96fd06165e | ||
|
|
8c6f35bf5c | ||
|
|
2ae9f43410 | ||
|
|
d14ba140d1 | ||
|
|
81ae77a4ad | ||
|
|
d33b9b98ba | ||
|
|
1df47e9aa4 | ||
|
|
44fce0e478 | ||
|
|
8f1f5c7234 | ||
|
|
6174b94d6a | ||
|
|
00ee32f7f5 | ||
|
|
f4edcf7717 | ||
|
|
6d25734852 | ||
|
|
6c299bc783 | ||
|
|
a2152a1216 | ||
|
|
d4bdabd64d | ||
|
|
6e972e436a | ||
|
|
7814790ff3 | ||
|
|
ee92cf56f2 | ||
|
|
8b567c99b4 | ||
|
|
5091f9a3f7 | ||
|
|
c7a0fa3a8c | ||
|
|
2bf3f6850e | ||
|
|
046b9e7d97 | ||
|
|
dbd8009f18 | ||
|
|
0a8a7e246f | ||
|
|
7496ede7c2 | ||
|
|
7ee59d02b6 | ||
|
|
463155b8b4 | ||
|
|
5dba0648f3 | ||
|
|
949c9eabd3 | ||
|
|
8fde8d0a4b | ||
|
|
2093179488 | ||
|
|
61b3ea1187 | ||
|
|
80a194af85 | ||
|
|
2e1e3162e5 | ||
|
|
0fa3c3d67d | ||
|
|
68c8b99270 | ||
|
|
01a882d429 | ||
|
|
edf36831ab | ||
|
|
7915002382 | ||
|
|
43ce82801d | ||
|
|
36f91a0231 | ||
|
|
47326bda6d | ||
|
|
8010b4f733 | ||
|
|
18c4d9d568 | ||
|
|
109cd6c382 | ||
|
|
e1195fd931 | ||
|
|
41a4cd3032 | ||
|
|
ee45d0fc1b | ||
|
|
d9b102bea0 | ||
|
|
fc3c929cea | ||
|
|
9472273a1b | ||
|
|
b3e72ac3e6 | ||
|
|
9050798094 | ||
|
|
23ab5fc23c | ||
|
|
30a0088c84 | ||
|
|
f532299f7c | ||
|
|
1ecc561d9e | ||
|
|
7246c0246f | ||
|
|
7bb7385e46 | ||
|
|
eb794b7db9 | ||
|
|
e691ee35b4 | ||
|
|
e89e05486d | ||
|
|
9df2fd9a67 | ||
|
|
8679549c38 | ||
|
|
ae6e35976d | ||
|
|
0e9519314b | ||
|
|
4b1bd1852f | ||
|
|
4b866292e5 | ||
|
|
5290de32b8 | ||
|
|
2c3a452fd7 | ||
|
|
ea97dec642 | ||
|
|
c0c6abedc4 | ||
|
|
b39ca155dc | ||
|
|
81ac6216a6 | ||
|
|
76f80b375c | ||
|
|
fb40acdd79 | ||
|
|
4a15925edc | ||
|
|
234c73a688 | ||
|
|
2e3f9eb1c0 | ||
|
|
5509dd8646 | ||
|
|
b9cf44b584 | ||
|
|
7fda067767 | ||
|
|
f43523a69e | ||
|
|
54a45c4f89 | ||
|
|
33959d0605 | ||
|
|
7d6604404f | ||
|
|
1be1d1c0ae | ||
|
|
ddb1021440 | ||
|
|
b8051c60a9 | ||
|
|
3bbbac4bb9 | ||
|
|
7735b743b3 | ||
|
|
ee3752df70 | ||
|
|
8ad0e22bf1 | ||
|
|
a5b4041a79 | ||
|
|
18ce5c271f | ||
|
|
10abed751e | ||
|
|
81f3843f04 | ||
|
|
65464d8b77 | ||
|
|
ad6f0afa57 | ||
|
|
6ef971330a | ||
|
|
3de278ef85 | ||
|
|
1ff739dccb | ||
|
|
2f0bde61b5 | ||
|
|
a5012404fb | ||
|
|
a2ff605212 | ||
|
|
1d44046141 | ||
|
|
d076bfa074 | ||
|
|
45f12f2676 | ||
|
|
8c23cbdf13 | ||
|
|
e5ee3e42d2 | ||
|
|
e9863708b6 | ||
|
|
c63885298c | ||
|
|
0205eafa8e | ||
|
|
b6d9d6dd33 | ||
|
|
df9ea61424 | ||
|
|
f138b35889 | ||
|
|
6dc031987f | ||
|
|
81c17cbcdd | ||
|
|
2ed3ebe807 | ||
|
|
47213a4392 | ||
|
|
cefb62292d | ||
|
|
0484f9f8a6 | ||
|
|
d9e93f43e8 | ||
|
|
fe52b4d6d9 | ||
|
|
22c11cee10 | ||
|
|
4a41a86e43 | ||
|
|
dbdc6eafdb | ||
|
|
5831c948db | ||
|
|
d2d344ba01 | ||
|
|
a823e12595 | ||
|
|
878d6f715e | ||
|
|
d69f37bb35 | ||
|
|
e6065b88e5 | ||
|
|
57b039f12c | ||
|
|
5b21f98d7c | ||
|
|
198aa8a37a | ||
|
|
9183506de8 | ||
|
|
9149021b2c | ||
|
|
722da4e70f | ||
|
|
4febded784 | ||
|
|
f9971fa370 | ||
|
|
e131d273e1 | ||
|
|
e22cd46a86 | ||
|
|
baa679d2dc | ||
|
|
96039be4e0 | ||
|
|
4905876108 | ||
|
|
17e03ff335 | ||
|
|
ce4b7a28cc | ||
|
|
18a26d624b | ||
|
|
d3223a8c9f | ||
|
|
fff55f23f1 | ||
|
|
e8f568a70d | ||
|
|
4210535a17 | ||
|
|
146a7798ed | ||
|
|
7d58ca3ef7 | ||
|
|
f4e12ad2d6 | ||
|
|
4f35f7f09a | ||
|
|
be6ba0e446 | ||
|
|
45a8c68f2a | ||
|
|
e1db296752 | ||
|
|
30ede8ac81 | ||
|
|
aefa93ff4e | ||
|
|
3e7625a255 | ||
|
|
95efc16cd8 | ||
|
|
ff7870d27c | ||
|
|
25dc9605eb | ||
|
|
a0a82a1407 | ||
|
|
309acd161c | ||
|
|
f550dc0592 | ||
|
|
90408cbafd | ||
|
|
899e9964e0 | ||
|
|
cbb76a587a | ||
|
|
17d356fe38 | ||
|
|
ec9b43f9fa | ||
|
|
f763b2a97c | ||
|
|
0707bff1c2 | ||
|
|
797f1d693b | ||
|
|
6c1cfedd14 | ||
|
|
6aa5f84631 | ||
|
|
bf488ccc4d | ||
|
|
f0d783b1e7 | ||
|
|
af3a37b053 | ||
|
|
41e7b26d03 | ||
|
|
2e4d8e58d3 | ||
|
|
0e800341bb | ||
|
|
8398380d46 | ||
|
|
32eaea7533 | ||
|
|
570b3424b0 | ||
|
|
2078f40a6f | ||
|
|
7b09aef22a | ||
|
|
b63bf8f1d4 | ||
|
|
be3efbd98f | ||
|
|
dd5a8780e1 | ||
|
|
9675487a16 | ||
|
|
db6ece9b9f | ||
|
|
02185d7056 | ||
|
|
97a38b486b | ||
|
|
76a63a1cac | ||
|
|
44294a5102 | ||
|
|
e0b278fd5d | ||
|
|
0c20652c9a | ||
|
|
299286dea1 | ||
|
|
31e8ff8759 | ||
|
|
610523b784 | ||
|
|
e28f6090d1 | ||
|
|
5a3a104793 | ||
|
|
59b9ffa094 | ||
|
|
09f874da24 | ||
|
|
f4e2c6ea3b | ||
|
|
30742f6d5d | ||
|
|
274e918fc8 | ||
|
|
2dafcba595 | ||
|
|
d284b15400 | ||
|
|
3d73f37738 | ||
|
|
bd30eea239 | ||
|
|
56c556e707 | ||
|
|
0455e840b7 | ||
|
|
e9483a6a0d | ||
|
|
992ee25bd3 | ||
|
|
d0afa89a82 | ||
|
|
bdcc705956 | ||
|
|
ff1b935905 | ||
|
|
ef07bbdea1 | ||
|
|
111da3966e | ||
|
|
6625d07859 | ||
|
|
3f3ac06fd1 | ||
|
|
bb37b10517 | ||
|
|
ceec669b0b | ||
|
|
d4a20e4136 | ||
|
|
c7fa14e3b8 | ||
|
|
6047e91d15 | ||
|
|
e578b85410 | ||
|
|
92589dea2f | ||
|
|
38e7a03c51 | ||
|
|
0b7d7df6ac | ||
|
|
a12f517ebd | ||
|
|
f31807d5da | ||
|
|
acd8844064 | ||
|
|
73f68af2a6 | ||
|
|
70fc269834 | ||
|
|
73c976003a | ||
|
|
362dc663cd | ||
|
|
410a0a54cf | ||
|
|
c37be2122b | ||
|
|
969b043503 | ||
|
|
0dfdb0a86a | ||
|
|
6cf29619ae | ||
|
|
9a738b2880 | ||
|
|
f6861aa353 | ||
|
|
11736abfb9 | ||
|
|
8950d0a46c | ||
|
|
5cd1ef9e67 | ||
|
|
53a2005b43 | ||
|
|
99faf82474 | ||
|
|
7d81d1a59e | ||
|
|
9d93eb2054 | ||
|
|
08d52f3617 | ||
|
|
d0dbafcf0d | ||
|
|
274bdcca8b | ||
|
|
8d8ca44487 | ||
|
|
ba9ad75ac7 | ||
|
|
aa7f5726e8 | ||
|
|
a7dce39444 | ||
|
|
db9c82a886 | ||
|
|
a00446565c | ||
|
|
1cd3ac511c | ||
|
|
a5626a3432 | ||
|
|
a9dff073b2 | ||
|
|
814a24e229 | ||
|
|
b2ad0122d5 | ||
|
|
89f006a351 | ||
|
|
7079a4cc7a | ||
|
|
5500a375f7 | ||
|
|
19669d2daf | ||
|
|
895dcf7fa7 | ||
|
|
ca431dc5e4 | ||
|
|
eaf83ba29d | ||
|
|
fdb035017f | ||
|
|
0ad3a2c986 | ||
|
|
f26b7cd1ac | ||
|
|
0be041a834 | ||
|
|
3a529f338d | ||
|
|
4aea93b867 | ||
|
|
0864318ac2 | ||
|
|
27ae11ac58 | ||
|
|
c8c74b5d83 | ||
|
|
d22e28a28e | ||
|
|
713f9473af | ||
|
|
04809649f8 | ||
|
|
8c3c778bee | ||
|
|
2bd2488989 | ||
|
|
86fa92d954 | ||
|
|
569f5d4ce5 | ||
|
|
4b6566a8ef | ||
|
|
bde7bcfb29 | ||
|
|
5002f8a24e | ||
|
|
23f6822cab | ||
|
|
b85fec60a5 | ||
|
|
8fd61195f0 | ||
|
|
89d1ff95cf | ||
|
|
2d2efdc48f | ||
|
|
a5b57d43a2 | ||
|
|
e97114c3cd | ||
|
|
6ce6976cf1 | ||
|
|
8c2fc74306 | ||
|
|
8e846a56f9 | ||
|
|
88ecbe2e27 | ||
|
|
a2233562df | ||
|
|
e72ebb0098 | ||
|
|
f6e15d2984 | ||
|
|
600ab18087 | ||
|
|
5e1d2b9bb7 | ||
|
|
faf0073ebc | ||
|
|
127eef95fd | ||
|
|
01e8bd1c2f | ||
|
|
1eeaab4564 | ||
|
|
923edeafa0 | ||
|
|
8cc091c3d2 | ||
|
|
580ae1db1f | ||
|
|
4d0d80731e | ||
|
|
cde5b4b7d4 | ||
|
|
8401dcead0 | ||
|
|
f54238bae4 | ||
|
|
a1fd1ba178 | ||
|
|
e4d7103508 | ||
|
|
e292cc4441 | ||
|
|
e4a8c9ee98 | ||
|
|
999dbb264e | ||
|
|
81dda34ab8 | ||
|
|
4e2d2d3459 | ||
|
|
99b8761a64 | ||
|
|
93a8014aed | ||
|
|
f7b5bef7db | ||
|
|
fc147e3e9a | ||
|
|
76d1772b5c | ||
|
|
17337de6eb | ||
|
|
a85a760ffe | ||
|
|
c5f7898382 | ||
|
|
b99e8e73c0 | ||
|
|
bc73ff9023 | ||
|
|
54d7dbff62 | ||
|
|
3a49fc87eb | ||
|
|
d016dbec4d | ||
|
|
6fb4cb56eb | ||
|
|
d0d53d3639 | ||
|
|
fa02355001 | ||
|
|
0e17da4880 | ||
|
|
bc00d08261 | ||
|
|
bd5e6af609 | ||
|
|
7a2242a6f5 | ||
|
|
765d2d36b6 | ||
|
|
2ed31a7b8c | ||
|
|
73495d0707 | ||
|
|
f63b807789 | ||
|
|
0f7da63220 | ||
|
|
8bfb4f456a | ||
|
|
5b0d76503f | ||
|
|
6bee96c375 | ||
|
|
38b4cba146 | ||
|
|
e55840a455 | ||
|
|
b3cd6fee77 | ||
|
|
c73796da90 | ||
|
|
ffe42256e9 | ||
|
|
0ec46f6857 | ||
|
|
4a987ddbf2 | ||
|
|
344b434cc5 | ||
|
|
75098e5aed | ||
|
|
d9ef10a817 | ||
|
|
06dd876038 | ||
|
|
2fbace3fe7 | ||
|
|
a1f90b5cb6 | ||
|
|
c943ad2d40 | ||
|
|
76f3e9841d | ||
|
|
f8b5b0ba54 | ||
|
|
d2da1b3f4e | ||
|
|
66d7f1b060 | ||
|
|
fe8083adf5 | ||
|
|
fe347e39f7 | ||
|
|
d171179dbd | ||
|
|
528f6ad58b | ||
|
|
77ecfe527e | ||
|
|
a628db994b | ||
|
|
881448ec76 | ||
|
|
ff9e40ef8f | ||
|
|
9c177584fa | ||
|
|
3f90f99ee8 | ||
|
|
a916289a66 | ||
|
|
6d63683bff | ||
|
|
401e835d08 | ||
|
|
432a0cdef4 | ||
|
|
b4fc122d3b | ||
|
|
98faf392a7 | ||
|
|
6340e7ebdd | ||
|
|
04186aa3e4 | ||
|
|
6d7e75d5cc | ||
|
|
545b26756d | ||
|
|
39e483ddef | ||
|
|
9f37d43672 | ||
|
|
08f34b4545 | ||
|
|
8a4ba4d2e2 | ||
|
|
f6f10c0521 | ||
|
|
dd0c300698 | ||
|
|
065e298ab4 | ||
|
|
2a50dcb6c2 | ||
|
|
a2e99a055a | ||
|
|
a445fcb4f6 | ||
|
|
20b1870d75 | ||
|
|
b0d00d521a | ||
|
|
bde63b5a5b | ||
|
|
1a25d7c52c | ||
|
|
e9ae886c3c | ||
|
|
9a8ec4aa09 | ||
|
|
9c2ffb74c9 | ||
|
|
ab337ed807 | ||
|
|
8545d214b6 | ||
|
|
bfd72b42df | ||
|
|
f044200d9e | ||
|
|
0fab3cfeec | ||
|
|
c8e1de6146 | ||
|
|
de40dc2334 | ||
|
|
9f8d68f07b | ||
|
|
fe598a831e | ||
|
|
aeb0002877 | ||
|
|
910b9518e4 | ||
|
|
2ce593b5de | ||
|
|
83257a9e73 | ||
|
|
cae840bb13 | ||
|
|
8d12e352f4 | ||
|
|
d54ae98b20 | ||
|
|
12928cbdcc | ||
|
|
9c443b6501 | ||
|
|
a45f715763 | ||
|
|
335b65baf8 | ||
|
|
f27515783a | ||
|
|
2afcb6f107 | ||
|
|
05a9171b42 | ||
|
|
ac50d8e1c1 | ||
|
|
f775e7bf28 | ||
|
|
fa0b5f56ab | ||
|
|
43e5941042 | ||
|
|
2168221092 | ||
|
|
64569e61a1 | ||
|
|
cca7485917 | ||
|
|
74b35322a2 | ||
|
|
0ed7c5618f | ||
|
|
42219fd7b7 | ||
|
|
111cf2ce9f | ||
|
|
d301b40c6b | ||
|
|
e3bd56972d | ||
|
|
922d080ae5 | ||
|
|
bd1018af5e | ||
|
|
76dbaaa3e0 | ||
|
|
376e8d5260 | ||
|
|
627da11384 | ||
|
|
75cb7fc42d | ||
|
|
a8f646e430 | ||
|
|
4202168c44 | ||
|
|
e3b333a860 | ||
|
|
cb135fdd0d | ||
|
|
3c3410cf5d | ||
|
|
5e9fc55783 | ||
|
|
9d66e89d14 | ||
|
|
14a09be652 | ||
|
|
fbd7f29056 | ||
|
|
2cefcc5a36 | ||
|
|
683e4e0d95 | ||
|
|
8d1aeb58a2 | ||
|
|
4c27f73559 | ||
|
|
dc3286730a | ||
|
|
62ec9fef36 | ||
|
|
a8b76f2951 | ||
|
|
8a752a56d6 | ||
|
|
4f179a192d | ||
|
|
270b352cbd | ||
|
|
2d994038be | ||
|
|
753f7b6e72 | ||
|
|
6959e54f06 | ||
|
|
7327093b66 | ||
|
|
74582e9965 | ||
|
|
29c69b3601 | ||
|
|
546383a8e5 | ||
|
|
0444fcdf37 | ||
|
|
527ef92c30 | ||
|
|
2731c3f18d | ||
|
|
9783c5ea24 | ||
|
|
db82e23ebb | ||
|
|
04a1608643 | ||
|
|
6b002a5106 | ||
|
|
5223ce546a | ||
|
|
95a43e0416 | ||
|
|
c6b00007f2 | ||
|
|
eb6732ec9e | ||
|
|
0b0c737af0 | ||
|
|
c0b278738c | ||
|
|
029927b25e | ||
|
|
f2490fccdd | ||
|
|
898a126f10 | ||
|
|
bfb04ddf3e | ||
|
|
416b7d0c32 | ||
|
|
561cbef5cc | ||
|
|
2bcaa19894 | ||
|
|
228b86d3b5 | ||
|
|
f542ae5158 | ||
|
|
c71dd1a9e6 | ||
|
|
f2c5093013 | ||
|
|
21ea59d600 | ||
|
|
e2e6faa3b5 | ||
|
|
42fa72d499 | ||
|
|
6f9389c2ea | ||
|
|
98cd061ac9 | ||
|
|
b9131c1802 | ||
|
|
e6f94f227c | ||
|
|
74244323f8 | ||
|
|
e38c7ae00f | ||
|
|
261137df9d | ||
|
|
c47152d017 | ||
|
|
7c7dda0e93 | ||
|
|
806c5f6b6d | ||
|
|
795bda5dd8 | ||
|
|
499f74dc27 | ||
|
|
c4183aad76 | ||
|
|
3f74b24c0a | ||
|
|
1905d127a5 | ||
|
|
07da3d41da | ||
|
|
cbef55e566 | ||
|
|
c6757573ae | ||
|
|
2b1220c600 | ||
|
|
edbce2a524 | ||
|
|
55f8a434d0 | ||
|
|
8a97c1d138 | ||
|
|
35009a7d1c | ||
|
|
94b34f88ec | ||
|
|
ce819128f9 | ||
|
|
e6e9255216 | ||
|
|
c09e6fa6ad | ||
|
|
3baca9b696 | ||
|
|
961147d387 | ||
|
|
686964daaa | ||
|
|
b88ad57986 | ||
|
|
975e966e26 | ||
|
|
49a4b7f14f | ||
|
|
644af54a7c | ||
|
|
c897e944db | ||
|
|
94d3899038 | ||
|
|
1823d8fcd2 | ||
|
|
402802b089 | ||
|
|
e784c47667 | ||
|
|
e44ae96c07 | ||
|
|
be293fe9ed | ||
|
|
e18b4d7ac8 | ||
|
|
f32f69f7d2 | ||
|
|
846503bb0e | ||
|
|
f63f5f954e | ||
|
|
8044d68492 | ||
|
|
33ad0a7ba7 | ||
|
|
0dddb9e6c8 | ||
|
|
562fa065c6 | ||
|
|
ecee599640 | ||
|
|
38b0fd5cd4 | ||
|
|
87671f55c5 | ||
|
|
1f1c33983b | ||
|
|
ee83eea784 | ||
|
|
9e6b28b8a2 | ||
|
|
84b49c84ca | ||
|
|
6c64d5ff55 | ||
|
|
7b1ed5218a | ||
|
|
079341c65c | ||
|
|
9f81cd4798 | ||
|
|
37aaeb3dd9 | ||
|
|
7870200461 | ||
|
|
167e122eba | ||
|
|
a1431bef4c | ||
|
|
073ccf7c35 | ||
|
|
feb7097fc9 | ||
|
|
976a8498d4 | ||
|
|
36d563a3c2 | ||
|
|
944f9f7b68 | ||
|
|
f78c7ee2ba | ||
|
|
c1b2f78f46 | ||
|
|
38c2b34da0 | ||
|
|
2e1db11ad9 | ||
|
|
ab05f35507 | ||
|
|
b3bf182a65 | ||
|
|
563ccb20c7 | ||
|
|
ca9da799d8 | ||
|
|
50fb7bcd21 | ||
|
|
eb6dd46e19 | ||
|
|
e14d6b7bf9 | ||
|
|
6daaac5924 | ||
|
|
65664ce5f3 | ||
|
|
8a447d9ae0 | ||
|
|
951d9d3f17 | ||
|
|
967126860c | ||
|
|
d19b02dcd0 | ||
|
|
d183d9c614 | ||
|
|
b600c5ec7d | ||
|
|
8e91ce9b2b | ||
|
|
8507a87bfa | ||
|
|
4e2902d972 | ||
|
|
928fd19593 | ||
|
|
5b79c34c72 | ||
|
|
0973409273 | ||
|
|
922dd771e3 | ||
|
|
4a1d0f3af5 | ||
|
|
d1574c204f | ||
|
|
4dcabb933e | ||
|
|
fa747f987c | ||
|
|
71c7700f2e | ||
|
|
7efd2fe2eb | ||
|
|
43822815f7 | ||
|
|
911a9c6776 | ||
|
|
73c3935031 | ||
|
|
4641e802a4 | ||
|
|
4b087089fb | ||
|
|
7d0c10e0d2 | ||
|
|
9843bfbdf8 | ||
|
|
5d147a3ae6 | ||
|
|
68d60cd092 | ||
|
|
99cd3c7650 | ||
|
|
622847bcb5 | ||
|
|
ad09b9c744 | ||
|
|
47bec8b438 | ||
|
|
95b8a4ab0b | ||
|
|
c32afb4ee2 | ||
|
|
c9c40684b7 | ||
|
|
9a3cfcb71d | ||
|
|
1b1735f5f0 | ||
|
|
854c62fc70 | ||
|
|
b4753d8d00 | ||
|
|
1d47e1c7eb | ||
|
|
88e43cb730 | ||
|
|
045a027753 | ||
|
|
b4a63aeba8 | ||
|
|
b17c5e4e2a | ||
|
|
7edaef54d4 | ||
|
|
85c6d267ba | ||
|
|
40d86e7b18 | ||
|
|
6f5537bdf1 | ||
|
|
e4311d8309 | ||
|
|
e147247aaf | ||
|
|
b9f49df757 | ||
|
|
eb8f05a9fd | ||
|
|
4e08b8dee6 | ||
|
|
7333c75327 | ||
|
|
5e5ec1b021 | ||
|
|
e8a3f7aa33 | ||
|
|
ecb9dbac42 | ||
|
|
1698370363 | ||
|
|
e3bcc48bf2 | ||
|
|
544aea18c3 | ||
|
|
75def4f2df | ||
|
|
c7e02eefdd | ||
|
|
bd9923fd75 | ||
|
|
bcf2143dfe | ||
|
|
770664411a | ||
|
|
cdb7995693 | ||
|
|
3593f24491 | ||
|
|
24893ad5e9 | ||
|
|
bf082c26c2 | ||
|
|
8f285a5dd4 | ||
|
|
35a174b956 | ||
|
|
85684a8919 | ||
|
|
27915da5b0 | ||
|
|
2cfaab335f | ||
|
|
f9ffb6ae35 | ||
|
|
91cb693164 | ||
|
|
5ef4faa8a4 | ||
|
|
f258c450b8 | ||
|
|
eed70eed6e | ||
|
|
55aab6efb6 | ||
|
|
9b91454968 | ||
|
|
9429275a91 | ||
|
|
ed3cc48847 | ||
|
|
87539aa558 | ||
|
|
c687247ce8 | ||
|
|
6f38327647 | ||
|
|
e3caebae56 | ||
|
|
7ba7fc028e | ||
|
|
1a56ea7417 | ||
|
|
9fd6beea7f | ||
|
|
3a29a745a3 | ||
|
|
606bd5b043 | ||
|
|
89c4817213 | ||
|
|
c93924b3cc | ||
|
|
f3509d2f8a | ||
|
|
3e09f946f4 | ||
|
|
d765aabc1f | ||
|
|
070287cecc | ||
|
|
d4d671316f | ||
|
|
dbf9393365 | ||
|
|
780285b86e | ||
|
|
11b866578f | ||
|
|
9b17495bc2 | ||
|
|
f548fd7a24 | ||
|
|
1ce0123fa5 | ||
|
|
3f0a1552e6 | ||
|
|
742d45a1f5 | ||
|
|
219444313a | ||
|
|
901139795d | ||
|
|
37a7231bd3 | ||
|
|
9a02f9bc2e | ||
|
|
e852142fb9 | ||
|
|
922c9e9ccc | ||
|
|
72962014b5 | ||
|
|
d23e6ac79b | ||
|
|
9a51757049 | ||
|
|
c3600d240a | ||
|
|
b77d9d9f5f | ||
|
|
2f579404f6 | ||
|
|
5d84eb4ff3 | ||
|
|
d3dda53cf6 | ||
|
|
613d06d0b1 | ||
|
|
ced84418d3 | ||
|
|
bd37458896 | ||
|
|
f6a84a470d | ||
|
|
dbdc1e737a | ||
|
|
fdf9a305b3 | ||
|
|
5207508871 | ||
|
|
2d10d4e785 | ||
|
|
4d9835927d | ||
|
|
1645d576fd | ||
|
|
77ff8f0dd4 | ||
|
|
b8508f2916 | ||
|
|
6d27928965 | ||
|
|
6c45721730 | ||
|
|
2a497ef1fd | ||
|
|
871ed62000 | ||
|
|
1a9f22625d | ||
|
|
06bd2ad10f | ||
|
|
05c76253f0 | ||
|
|
fe8bab0406 | ||
|
|
77ad48aa09 | ||
|
|
4fee6b51e0 | ||
|
|
a807c0eb69 | ||
|
|
947a7b84d7 | ||
|
|
53bc19b5f2 | ||
|
|
b04a7c101d | ||
|
|
50c49ec8f9 | ||
|
|
f39b425fac | ||
|
|
cce74a33e1 | ||
|
|
c739478b8b | ||
|
|
76d28b50dd | ||
|
|
43d2a3d937 | ||
|
|
b4912cf340 | ||
|
|
9cc52247c4 | ||
|
|
cb3024c5ff | ||
|
|
10293889b7 | ||
|
|
c80325737e | ||
|
|
0c1d218d88 | ||
|
|
9f0dc9402f | ||
|
|
41dec01c7c | ||
|
|
38ad4f4653 | ||
|
|
1bf2ab5dc0 | ||
|
|
b3e31ed1f4 | ||
|
|
e66068c11b | ||
|
|
4e5db16fbf | ||
|
|
8245cd90b3 | ||
|
|
acb47b418c | ||
|
|
5508d1feda | ||
|
|
ef55ba549b | ||
|
|
ceb758c877 | ||
|
|
4723abd0b4 | ||
|
|
8200e3d315 | ||
|
|
13235ad477 | ||
|
|
9ea979bbcd | ||
|
|
6087b4f903 | ||
|
|
33f2a6fea1 | ||
|
|
b6770bdb79 | ||
|
|
065fc31a92 | ||
|
|
db8d2f08d9 | ||
|
|
812650847c | ||
|
|
ffdbcb6f31 | ||
|
|
8bb2119a62 | ||
|
|
8a6f53d044 | ||
|
|
a415822614 | ||
|
|
9309688a8a | ||
|
|
635e592778 | ||
|
|
84f2ab850d | ||
|
|
dd366f56fa | ||
|
|
cce4a284be | ||
|
|
728b31e5e9 | ||
|
|
87a7a6eb39 | ||
|
|
afbde2a930 | ||
|
|
c6893527a7 | ||
|
|
f429352c0a | ||
|
|
644315ace1 | ||
|
|
ac8e3f01e9 | ||
|
|
812a9da62a | ||
|
|
e5df999183 | ||
|
|
cf89ee6279 | ||
|
|
f15e1bc52c | ||
|
|
80edbfed80 | ||
|
|
0b542baacb | ||
|
|
48334cfcd5 | ||
|
|
26ebb9bb5e | ||
|
|
f245e8beb5 | ||
|
|
12a4dabd1c | ||
|
|
8841b0dd1d | ||
|
|
6b57b16324 | ||
|
|
4d59154a88 | ||
|
|
e920e71cb5 | ||
|
|
d84ac3a6b2 | ||
|
|
bf2c4e866a | ||
|
|
d7ccf47057 | ||
|
|
fdf21f7763 | ||
|
|
566bc2e5e8 | ||
|
|
ab07b0aed5 | ||
|
|
34fd7296b1 | ||
|
|
0d34340c20 | ||
|
|
5e30858705 | ||
|
|
e052670c0b | ||
|
|
8ce1c33ce6 | ||
|
|
75bbb50db3 | ||
|
|
5b482d738a | ||
|
|
235470fb26 | ||
|
|
2a760e1fd1 | ||
|
|
e6b110d05b | ||
|
|
13b7b09668 | ||
|
|
f2ee9a62d2 | ||
|
|
d29124fd3a | ||
|
|
ecd0bcdd58 | ||
|
|
05cde71efc | ||
|
|
e7d909164c | ||
|
|
3639bfe700 | ||
|
|
f1083320a4 | ||
|
|
d46f224e79 | ||
|
|
1194766eba | ||
|
|
45e488657b | ||
|
|
46bfc10e4d | ||
|
|
9203dd16af | ||
|
|
0de5f60cdc | ||
|
|
1a69663a70 | ||
|
|
433b317c35 | ||
|
|
dc3f40c31e | ||
|
|
c090f849b0 | ||
|
|
a94a07ecd6 | ||
|
|
065700c5e1 | ||
|
|
8d4ae26686 | ||
|
|
8a9de6a8d3 | ||
|
|
4d17bf3d8b | ||
|
|
75b0b3c8ee | ||
|
|
bce16e9daf | ||
|
|
e878ad5f31 | ||
|
|
19eaeb9aca | ||
|
|
5cdb1a0a15 | ||
|
|
4471603de2 | ||
|
|
99efafb55a | ||
|
|
fdf539dc6a | ||
|
|
22fe65b4eb | ||
|
|
b60d28c71a |
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -5,9 +5,14 @@ docs/operator-manual/resource_actions_builtin.md linguist-generated=true
|
|||||||
docs/operator-manual/server-commands/argocd-*.md linguist-generated=true
|
docs/operator-manual/server-commands/argocd-*.md linguist-generated=true
|
||||||
docs/user-guide/commands/argocd_*.md linguist-generated=true
|
docs/user-guide/commands/argocd_*.md linguist-generated=true
|
||||||
manifests/core-install.yaml linguist-generated=true
|
manifests/core-install.yaml linguist-generated=true
|
||||||
|
manifests/core-install-with-hydrator.yaml linguist-generated=true
|
||||||
manifests/crds/*-crd.yaml linguist-generated=true
|
manifests/crds/*-crd.yaml linguist-generated=true
|
||||||
manifests/ha/install.yaml linguist-generated=true
|
manifests/ha/install.yaml linguist-generated=true
|
||||||
|
manifests/ha/install-with-hydrator.yaml linguist-generated=true
|
||||||
manifests/ha/namespace-install.yaml linguist-generated=true
|
manifests/ha/namespace-install.yaml linguist-generated=true
|
||||||
|
manifests/ha/namespace-install-with-hydrator.yaml linguist-generated=true
|
||||||
manifests/install.yaml linguist-generated=true
|
manifests/install.yaml linguist-generated=true
|
||||||
|
manifests/install-with-hydrator.yaml linguist-generated=true
|
||||||
manifests/namespace-install.yaml linguist-generated=true
|
manifests/namespace-install.yaml linguist-generated=true
|
||||||
|
manifests/namespace-install-with-hydrator.yaml linguist-generated=true
|
||||||
pkg/apis/api-rules/violation_exceptions.list linguist-generated=true
|
pkg/apis/api-rules/violation_exceptions.list linguist-generated=true
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: 'bug'
|
labels: ['bug', 'triage/pending']
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -10,9 +10,9 @@ assignees: ''
|
|||||||
|
|
||||||
Checklist:
|
Checklist:
|
||||||
|
|
||||||
* [ ] I've searched in the docs and FAQ for my answer: https://bit.ly/argocd-faq.
|
- [ ] I've searched in the docs and FAQ for my answer: https://bit.ly/argocd-faq.
|
||||||
* [ ] I've included steps to reproduce the bug.
|
- [ ] I've included steps to reproduce the bug.
|
||||||
* [ ] I've pasted the output of `argocd version`.
|
- [ ] I've pasted the output of `argocd version`.
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
name: Enhancement proposal
|
name: Enhancement proposal
|
||||||
about: Propose an enhancement for this project
|
about: Propose an enhancement for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: 'enhancement'
|
labels: ['enhancement', 'triage/pending']
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
# Summary
|
# Summary
|
||||||
|
|
||||||
What change you think needs making.
|
What change you think needs making.
|
||||||
@@ -15,4 +16,4 @@ Please give examples of your use case, e.g. when would you use this.
|
|||||||
|
|
||||||
# Proposal
|
# Proposal
|
||||||
|
|
||||||
How do you think this should be implemented?
|
How do you think this should be implemented?
|
||||||
|
|||||||
14
.github/ISSUE_TEMPLATE/new_dev_tool.md
vendored
14
.github/ISSUE_TEMPLATE/new_dev_tool.md
vendored
@@ -2,17 +2,17 @@
|
|||||||
name: New Dev Tool Request
|
name: New Dev Tool Request
|
||||||
about: This is a request for adding a new tool for setting up a dev environment.
|
about: This is a request for adding a new tool for setting up a dev environment.
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ['component:dev-env', 'triage/pending']
|
||||||
assignees: ''
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
Checklist:
|
Checklist:
|
||||||
|
|
||||||
* [ ] I am willing to maintain this tool, or have another Argo CD maintainer who is.
|
- [ ] I am willing to maintain this tool, or have another Argo CD maintainer who is.
|
||||||
* [ ] I have another Argo CD maintainer who is willing to help maintain this tool (there needs to be at least two maintainers willing to maintain this tool)
|
- [ ] I have another Argo CD maintainer who is willing to help maintain this tool (there needs to be at least two maintainers willing to maintain this tool)
|
||||||
* [ ] I have a lead sponsor who is a core Argo CD maintainer
|
- [ ] I have a lead sponsor who is a core Argo CD maintainer
|
||||||
* [ ] There is a PR which adds said tool - this is so that the maintainers can assess the impact of having this in the tree
|
- [ ] There is a PR which adds said tool - this is so that the maintainers can assess the impact of having this in the tree
|
||||||
* [ ] I have given a motivation why this should be added
|
- [ ] I have given a motivation why this should be added
|
||||||
|
|
||||||
### The proposer
|
### The proposer
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ Checklist:
|
|||||||
|
|
||||||
### Motivation
|
### Motivation
|
||||||
|
|
||||||
<!-- Why this tool would be useful to have in the tree. -->
|
<!-- Why this tool would be useful to have in the tree. -->
|
||||||
|
|
||||||
### Link to PR (Optional)
|
### Link to PR (Optional)
|
||||||
|
|
||||||
|
|||||||
8
.github/ISSUE_TEMPLATE/security_logs.md
vendored
8
.github/ISSUE_TEMPLATE/security_logs.md
vendored
@@ -1,10 +1,11 @@
|
|||||||
---
|
---
|
||||||
name: Security log
|
name: Security log
|
||||||
about: Propose adding security-related logs or tagging existing logs with security fields
|
about: Propose adding security-related logs or tagging existing logs with security fields
|
||||||
title: "seclog: [Event Description]"
|
title: 'seclog: [Event Description]'
|
||||||
labels: security-log
|
labels: ['security', 'triage/pending']
|
||||||
assignees: notfromstatefarm
|
assignees: ''
|
||||||
---
|
---
|
||||||
|
|
||||||
# Event to be logged
|
# Event to be logged
|
||||||
|
|
||||||
Specify the event that needs to be logged or existing logs that need to be tagged.
|
Specify the event that needs to be logged or existing logs that need to be tagged.
|
||||||
@@ -16,4 +17,3 @@ What security level should these events be logged under? Refer to https://argo-c
|
|||||||
# Common Weakness Enumeration
|
# Common Weakness Enumeration
|
||||||
|
|
||||||
Is there an associated [CWE](https://cwe.mitre.org/) that could be tagged as well?
|
Is there an associated [CWE](https://cwe.mitre.org/) that could be tagged as well?
|
||||||
|
|
||||||
|
|||||||
3
.github/cherry-pick-bot.yml
vendored
3
.github/cherry-pick-bot.yml
vendored
@@ -1,3 +0,0 @@
|
|||||||
enabled: true
|
|
||||||
preservePullRequestTitle: true
|
|
||||||
|
|
||||||
15
.github/configs/renovate-config.js
vendored
Normal file
15
.github/configs/renovate-config.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
module.exports = {
|
||||||
|
platform: 'github',
|
||||||
|
gitAuthor: 'renovate[bot] <renovate[bot]@users.noreply.github.com>',
|
||||||
|
autodiscover: false,
|
||||||
|
allowPostUpgradeCommandTemplating: true,
|
||||||
|
allowedPostUpgradeCommands: ["make mockgen"],
|
||||||
|
extends: [
|
||||||
|
"github>argoproj/argo-cd//renovate-presets/commons.json5",
|
||||||
|
"github>argoproj/argo-cd//renovate-presets/custom-managers/shell.json5",
|
||||||
|
"github>argoproj/argo-cd//renovate-presets/custom-managers/yaml.json5",
|
||||||
|
"github>argoproj/argo-cd//renovate-presets/fix/disable-all-updates.json5",
|
||||||
|
"github>argoproj/argo-cd//renovate-presets/devtool.json5",
|
||||||
|
"github>argoproj/argo-cd//renovate-presets/docs.json5"
|
||||||
|
]
|
||||||
|
}
|
||||||
20
.github/dependabot.yml
vendored
20
.github/dependabot.yml
vendored
@@ -10,7 +10,7 @@ updates:
|
|||||||
groups:
|
groups:
|
||||||
otel:
|
otel:
|
||||||
patterns:
|
patterns:
|
||||||
- "^go.opentelemetry.io/.*"
|
- "go.opentelemetry.io/*"
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
@@ -22,10 +22,11 @@ updates:
|
|||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
|
|
||||||
- package-ecosystem: "npm"
|
# Disabled since this code is rarely used.
|
||||||
directory: "/ui-test/"
|
# - package-ecosystem: "npm"
|
||||||
schedule:
|
# directory: "/ui-test/"
|
||||||
interval: "daily"
|
# schedule:
|
||||||
|
# interval: "daily"
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
- package-ecosystem: "docker"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
@@ -52,7 +53,8 @@ updates:
|
|||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
|
|
||||||
- package-ecosystem: "docker"
|
# Disabled since this code is rarely used.
|
||||||
directory: "/ui-test/"
|
# - package-ecosystem: "docker"
|
||||||
schedule:
|
# directory: "/ui-test/"
|
||||||
interval: "daily"
|
# schedule:
|
||||||
|
# interval: "daily"
|
||||||
|
|||||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -8,7 +8,7 @@ Checklist:
|
|||||||
|
|
||||||
* [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-cd/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
|
* [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-cd/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this does not need to be in the release notes.
|
||||||
* [ ] The title of the PR states what changed and the related issues number (used for the release note).
|
* [ ] The title of the PR states what changed and the related issues number (used for the release note).
|
||||||
* [ ] The title of the PR conforms to the [Toolchain Guide](https://argo-cd.readthedocs.io/en/latest/developer-guide/toolchain-guide/#title-of-the-pr)
|
* [ ] The title of the PR conforms to the [Title of the PR](https://argo-cd.readthedocs.io/en/latest/developer-guide/submit-your-pr/#title-of-the-pr)
|
||||||
* [ ] I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue.
|
* [ ] I've included "Closes [ISSUE #]" or "Fixes [ISSUE #]" in the description to automatically close the associated issue.
|
||||||
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
|
* [ ] I've updated both the CLI and UI to expose my feature, or I plan to submit a second PR with them.
|
||||||
* [ ] Does this PR require documentation updates?
|
* [ ] Does this PR require documentation updates?
|
||||||
|
|||||||
2
.github/workflows/README.md
vendored
2
.github/workflows/README.md
vendored
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
## image-reuse.yaml
|
## image-reuse.yaml
|
||||||
|
|
||||||
- The resuable workflow can be used to publish or build images with multiple container registries(Quay,GHCR, dockerhub), and then sign them with cosign when an image is published.
|
- The reusable workflow can be used to publish or build images with multiple container registries(Quay,GHCR, dockerhub), and then sign them with cosign when an image is published.
|
||||||
- A GO version `must` be specified e.g. 1.21
|
- A GO version `must` be specified e.g. 1.21
|
||||||
- The image name for each registry *must* contain the tag. Note: multiple tags are allowed for each registry using a CSV type.
|
- The image name for each registry *must* contain the tag. Note: multiple tags are allowed for each registry using a CSV type.
|
||||||
- Multiple platforms can be specified e.g. linux/amd64,linux/arm64
|
- Multiple platforms can be specified e.g. linux/amd64,linux/arm64
|
||||||
|
|||||||
89
.github/workflows/bump-major-version.yaml
vendored
Normal file
89
.github/workflows/bump-major-version.yaml
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
name: Bump major version
|
||||||
|
on:
|
||||||
|
workflow_dispatch: {}
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prepare-release:
|
||||||
|
permissions:
|
||||||
|
contents: write # for peter-evans/create-pull-request to create branch
|
||||||
|
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
||||||
|
name: Automatically update major version
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# Get the current major version from go.mod and save it as a variable.
|
||||||
|
- name: Get target version
|
||||||
|
id: get-target-version
|
||||||
|
run: |
|
||||||
|
set -ue
|
||||||
|
CURRENT_VERSION=$(grep 'module github.com/argoproj/argo-cd' go.mod | awk '{print $2}' | sed 's/.*\/v//')
|
||||||
|
echo "TARGET_VERSION=$((CURRENT_VERSION + 1))" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Copy source code to GOPATH
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/go/src/github.com/argoproj
|
||||||
|
cp -a ../argo-cd ~/go/src/github.com/argoproj
|
||||||
|
|
||||||
|
- name: Run script to bump the version
|
||||||
|
run: |
|
||||||
|
hack/bump-major-version.sh
|
||||||
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
|
|
||||||
|
- name: Setup Golang
|
||||||
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
|
with:
|
||||||
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
|
- name: Add ~/go/bin to PATH
|
||||||
|
run: |
|
||||||
|
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
||||||
|
- name: Add /usr/local/bin to PATH
|
||||||
|
run: |
|
||||||
|
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||||
|
- name: Download & vendor dependencies
|
||||||
|
run: |
|
||||||
|
# We need to vendor go modules for codegen yet
|
||||||
|
go mod download
|
||||||
|
go mod vendor -v
|
||||||
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
|
- name: Install toolchain for codegen
|
||||||
|
run: |
|
||||||
|
make install-codegen-tools-local
|
||||||
|
make install-go-tools-local
|
||||||
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
|
# We install kustomize in the dist directory
|
||||||
|
- name: Add dist to PATH
|
||||||
|
run: |
|
||||||
|
echo "/home/runner/work/argo-cd/argo-cd/dist" >> $GITHUB_PATH
|
||||||
|
- name: Run codegen
|
||||||
|
run: |
|
||||||
|
set -x
|
||||||
|
export GOPATH=$(go env GOPATH)
|
||||||
|
make codegen-local
|
||||||
|
working-directory: /home/runner/go/src/github.com/argoproj/argo-cd
|
||||||
|
|
||||||
|
- name: Copy changes back
|
||||||
|
run: |
|
||||||
|
# Copy the contents back, but skip the .git directory
|
||||||
|
rsync -a --exclude=.git /home/runner/go/src/github.com/argoproj/argo-cd/ ../argo-cd
|
||||||
|
|
||||||
|
- name: Create pull request
|
||||||
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
|
with:
|
||||||
|
commit-message: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
|
||||||
|
title: "Bump major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}"
|
||||||
|
body: |
|
||||||
|
Congrats! You've just bumped the major version to ${{ steps.get-target-version.outputs.TARGET_VERSION }}.
|
||||||
|
|
||||||
|
Next steps:
|
||||||
|
- [ ] Merge this PR
|
||||||
|
- [ ] Add an upgrade guide to the docs for this version
|
||||||
|
branch: bump-major-version
|
||||||
|
branch-suffix: random
|
||||||
|
signoff: true
|
||||||
114
.github/workflows/cherry-pick-single.yml
vendored
Normal file
114
.github/workflows/cherry-pick-single.yml
vendored
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
name: Cherry Pick Single
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
merge_commit_sha:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: "The merge commit SHA to cherry-pick"
|
||||||
|
version_number:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: "The version number (from cherry-pick/ label)"
|
||||||
|
pr_number:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: "The original PR number"
|
||||||
|
pr_title:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
description: "The original PR title"
|
||||||
|
secrets:
|
||||||
|
CHERRYPICK_APP_ID:
|
||||||
|
required: true
|
||||||
|
CHERRYPICK_APP_PRIVATE_KEY:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cherry-pick:
|
||||||
|
name: Cherry Pick to ${{ inputs.version_number }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Generate a token
|
||||||
|
id: generate-token
|
||||||
|
uses: actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b # v2.1.1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.CHERRYPICK_APP_ID }}
|
||||||
|
private-key: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
|
- name: Configure Git
|
||||||
|
run: |
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
- name: Cherry pick commit
|
||||||
|
id: cherry-pick
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
MERGE_COMMIT="${{ inputs.merge_commit_sha }}"
|
||||||
|
TARGET_BRANCH="release-${{ inputs.version_number }}"
|
||||||
|
|
||||||
|
echo "🍒 Cherry-picking commit $MERGE_COMMIT to branch $TARGET_BRANCH"
|
||||||
|
|
||||||
|
# Check if target branch exists
|
||||||
|
if ! git show-ref --verify --quiet "refs/remotes/origin/$TARGET_BRANCH"; then
|
||||||
|
echo "❌ Target branch '$TARGET_BRANCH' does not exist"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create new branch for cherry-pick
|
||||||
|
CHERRY_PICK_BRANCH="cherry-pick-${{ inputs.pr_number }}-to-${TARGET_BRANCH}"
|
||||||
|
git checkout -b "$CHERRY_PICK_BRANCH" "origin/$TARGET_BRANCH"
|
||||||
|
|
||||||
|
# Perform cherry-pick
|
||||||
|
if git cherry-pick -m 1 "$MERGE_COMMIT"; then
|
||||||
|
echo "✅ Cherry-pick successful"
|
||||||
|
|
||||||
|
# Extract Signed-off-by from the cherry-pick commit
|
||||||
|
SIGNOFF=$(git log -1 --pretty=format:"%B" | grep -E '^Signed-off-by:' || echo "")
|
||||||
|
|
||||||
|
# Push the new branch
|
||||||
|
git push origin "$CHERRY_PICK_BRANCH"
|
||||||
|
|
||||||
|
# Save data for PR creation
|
||||||
|
echo "branch_name=$CHERRY_PICK_BRANCH" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "signoff=$SIGNOFF" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "target_branch=$TARGET_BRANCH" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "❌ Cherry-pick failed due to conflicts"
|
||||||
|
git cherry-pick --abort
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
run: |
|
||||||
|
# Create cherry-pick PR
|
||||||
|
gh pr create \
|
||||||
|
--title "${{ inputs.pr_title }} (cherry-pick #${{ inputs.pr_number }} for ${{ inputs.version_number }})" \
|
||||||
|
--body "Cherry-picked ${{ inputs.pr_title }} (#${{ inputs.pr_number }})
|
||||||
|
|
||||||
|
${{ steps.cherry-pick.outputs.signoff }}" \
|
||||||
|
--base "${{ steps.cherry-pick.outputs.target_branch }}" \
|
||||||
|
--head "${{ steps.cherry-pick.outputs.branch_name }}"
|
||||||
|
|
||||||
|
# Comment on original PR
|
||||||
|
gh pr comment ${{ inputs.pr_number }} \
|
||||||
|
--body "🍒 Cherry-pick PR created for ${{ inputs.version_number }}: #$(gh pr list --head ${{ steps.cherry-pick.outputs.branch_name }} --json number --jq '.[0].number')"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|
||||||
|
- name: Comment on failure
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
gh pr comment ${{ inputs.pr_number }} \
|
||||||
|
--body "❌ Cherry-pick failed for ${{ inputs.version_number }}. Please check the workflow logs for details."
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
|
||||||
53
.github/workflows/cherry-pick.yml
vendored
Normal file
53
.github/workflows/cherry-pick.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
name: Cherry Pick
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
types: ["labeled", "closed"]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
find-labels:
|
||||||
|
name: Find Cherry Pick Labels
|
||||||
|
if: |
|
||||||
|
github.event.pull_request.merged == true && (
|
||||||
|
(github.event.action == 'labeled' && startsWith(github.event.label.name, 'cherry-pick/')) ||
|
||||||
|
(github.event.action == 'closed' && contains(toJSON(github.event.pull_request.labels.*.name), 'cherry-pick/'))
|
||||||
|
)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
labels: ${{ steps.extract-labels.outputs.labels }}
|
||||||
|
steps:
|
||||||
|
- name: Extract cherry-pick labels
|
||||||
|
id: extract-labels
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.event.action }}" == "labeled" ]]; then
|
||||||
|
# Label was just added - use it directly
|
||||||
|
LABEL_NAME="${{ github.event.label.name }}"
|
||||||
|
VERSION="${LABEL_NAME#cherry-pick/}"
|
||||||
|
CHERRY_PICK_DATA='[{"label":"'$LABEL_NAME'","version":"'$VERSION'"}]'
|
||||||
|
else
|
||||||
|
# PR was closed - find all cherry-pick labels
|
||||||
|
CHERRY_PICK_DATA=$(echo '${{ toJSON(github.event.pull_request.labels) }}' | jq -c '[.[] | select(.name | startswith("cherry-pick/")) | {label: .name, version: (.name | sub("cherry-pick/"; ""))}]')
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "labels=$CHERRY_PICK_DATA" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Found cherry-pick data: $CHERRY_PICK_DATA"
|
||||||
|
|
||||||
|
cherry-pick:
|
||||||
|
name: Cherry Pick
|
||||||
|
needs: find-labels
|
||||||
|
if: needs.find-labels.outputs.labels != '[]'
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include: ${{ fromJSON(needs.find-labels.outputs.labels) }}
|
||||||
|
fail-fast: false
|
||||||
|
uses: ./.github/workflows/cherry-pick-single.yml
|
||||||
|
with:
|
||||||
|
merge_commit_sha: ${{ github.event.pull_request.merge_commit_sha }}
|
||||||
|
version_number: ${{ matrix.version }}
|
||||||
|
pr_number: ${{ github.event.pull_request.number }}
|
||||||
|
pr_title: ${{ github.event.pull_request.title }}
|
||||||
|
secrets:
|
||||||
|
CHERRYPICK_APP_ID: ${{ vars.CHERRYPICK_APP_ID }}
|
||||||
|
CHERRYPICK_APP_PRIVATE_KEY: ${{ secrets.CHERRYPICK_APP_PRIVATE_KEY }}
|
||||||
127
.github/workflows/ci-build.yaml
vendored
127
.github/workflows/ci-build.yaml
vendored
@@ -14,11 +14,11 @@ on:
|
|||||||
env:
|
env:
|
||||||
# Golang version to use across CI steps
|
# Golang version to use across CI steps
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
GOLANG_VERSION: '1.23.3'
|
GOLANG_VERSION: '1.25.6'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -32,14 +32,14 @@ jobs:
|
|||||||
docs: ${{ steps.filter.outputs.docs_any_changed }}
|
docs: ${{ steps.filter.outputs.docs_any_changed }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- uses: tj-actions/changed-files@bab30c2299617f6615ec02a68b9a40d10bd21366 # v45.0.5
|
- uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0
|
||||||
id: filter
|
id: filter
|
||||||
with:
|
with:
|
||||||
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
|
# Any file which is not under docs/, ui/ or is not a markdown file is counted as a backend file
|
||||||
files_yaml: |
|
files_yaml: |
|
||||||
backend:
|
backend:
|
||||||
- '!ui/**'
|
- '!ui/**'
|
||||||
- '!**.md'
|
- '!**.md'
|
||||||
- '!**/*.md'
|
- '!**/*.md'
|
||||||
- '!docs/**'
|
- '!docs/**'
|
||||||
frontend:
|
frontend:
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Download all Go modules
|
- name: Download all Go modules
|
||||||
@@ -78,11 +78,11 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||||
@@ -94,8 +94,8 @@ jobs:
|
|||||||
|
|
||||||
lint-go:
|
lint-go:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read # for actions/checkout to fetch code
|
contents: read # for actions/checkout to fetch code
|
||||||
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
|
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
|
||||||
name: Lint Go code
|
name: Lint Go code
|
||||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@@ -105,14 +105,14 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Run golangci-lint
|
- name: Run golangci-lint
|
||||||
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1
|
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
|
||||||
with:
|
with:
|
||||||
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
# renovate: datasource=go packageName=github.com/golangci/golangci-lint versioning=regex:^v(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)?$
|
||||||
version: v1.62.2
|
version: v2.4.0
|
||||||
args: --verbose
|
args: --verbose
|
||||||
|
|
||||||
test-go:
|
test-go:
|
||||||
@@ -133,7 +133,7 @@ jobs:
|
|||||||
- name: Create symlink in GOPATH
|
- name: Create symlink in GOPATH
|
||||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Install required packages
|
- name: Install required packages
|
||||||
@@ -153,7 +153,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||||
@@ -174,7 +174,7 @@ jobs:
|
|||||||
- name: Run all unit tests
|
- name: Run all unit tests
|
||||||
run: make test-local
|
run: make test-local
|
||||||
- name: Generate test results artifacts
|
- name: Generate test results artifacts
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: test-results
|
name: test-results
|
||||||
path: test-results
|
path: test-results
|
||||||
@@ -197,7 +197,7 @@ jobs:
|
|||||||
- name: Create symlink in GOPATH
|
- name: Create symlink in GOPATH
|
||||||
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
run: ln -s $(pwd) ~/go/src/github.com/argoproj/argo-cd
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Install required packages
|
- name: Install required packages
|
||||||
@@ -217,7 +217,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||||
@@ -238,7 +238,7 @@ jobs:
|
|||||||
- name: Run all unit tests
|
- name: Run all unit tests
|
||||||
run: make test-race-local
|
run: make test-race-local
|
||||||
- name: Generate test results artifacts
|
- name: Generate test results artifacts
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: race-results
|
name: race-results
|
||||||
path: test-results/
|
path: test-results/
|
||||||
@@ -253,7 +253,7 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: Create symlink in GOPATH
|
- name: Create symlink in GOPATH
|
||||||
@@ -305,13 +305,13 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup NodeJS
|
- name: Setup NodeJS
|
||||||
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
|
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
|
||||||
with:
|
with:
|
||||||
# renovate: datasource=node-version packageName=node versioning=node
|
# renovate: datasource=node-version packageName=node versioning=node
|
||||||
node-version: '22.9.0'
|
node-version: '22.9.0'
|
||||||
- name: Restore node dependency cache
|
- name: Restore node dependency cache
|
||||||
id: cache-dependencies
|
id: cache-dependencies
|
||||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
with:
|
with:
|
||||||
path: ui/node_modules
|
path: ui/node_modules
|
||||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||||
@@ -333,6 +333,15 @@ jobs:
|
|||||||
run: yarn lint
|
run: yarn lint
|
||||||
working-directory: ui/
|
working-directory: ui/
|
||||||
|
|
||||||
|
shellcheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
|
- run: |
|
||||||
|
sudo apt-get install shellcheck
|
||||||
|
shellcheck -e SC2059 -e SC2154 -e SC2034 -e SC2016 -e SC1091 $(find . -type f -name '*.sh' | grep -v './ui/node_modules') | tee sc.log
|
||||||
|
test ! -s sc.log
|
||||||
|
|
||||||
analyze:
|
analyze:
|
||||||
name: Process & analyze test artifacts
|
name: Process & analyze test artifacts
|
||||||
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
if: ${{ needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true' }}
|
||||||
@@ -351,7 +360,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Restore node dependency cache
|
- name: Restore node dependency cache
|
||||||
id: cache-dependencies
|
id: cache-dependencies
|
||||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
with:
|
with:
|
||||||
path: ui/node_modules
|
path: ui/node_modules
|
||||||
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
key: ${{ runner.os }}-node-dep-v2-${{ hashFiles('**/yarn.lock') }}
|
||||||
@@ -359,12 +368,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
rm -rf ui/node_modules/argo-ui/node_modules
|
rm -rf ui/node_modules/argo-ui/node_modules
|
||||||
- name: Get e2e code coverage
|
- name: Get e2e code coverage
|
||||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: e2e-code-coverage
|
name: e2e-code-coverage
|
||||||
path: e2e-code-coverage
|
path: e2e-code-coverage
|
||||||
- name: Get unit test code coverage
|
- name: Get unit test code coverage
|
||||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||||
with:
|
with:
|
||||||
name: test-results
|
name: test-results
|
||||||
path: test-results
|
path: test-results
|
||||||
@@ -376,15 +385,15 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
go tool covdata percent -i=test-results,e2e-code-coverage/applicationset-controller,e2e-code-coverage/repo-server,e2e-code-coverage/app-controller,e2e-code-coverage/commit-server -o test-results/full-coverage.out
|
go tool covdata percent -i=test-results,e2e-code-coverage/applicationset-controller,e2e-code-coverage/repo-server,e2e-code-coverage/app-controller,e2e-code-coverage/commit-server -o test-results/full-coverage.out
|
||||||
- name: Upload code coverage information to codecov.io
|
- name: Upload code coverage information to codecov.io
|
||||||
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0
|
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
|
||||||
with:
|
with:
|
||||||
file: test-results/full-coverage.out
|
files: test-results/full-coverage.out
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
- name: Upload test results to Codecov
|
- name: Upload test results to Codecov
|
||||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd'
|
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && github.repository == 'argoproj/argo-cd'
|
||||||
uses: codecov/test-results-action@9739113ad922ea0a9abb4b2c0f8bf6a4aa8ef820 # v1.0.1
|
uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1
|
||||||
with:
|
with:
|
||||||
file: test-results/junit.xml
|
file: test-results/junit.xml
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
@@ -393,46 +402,54 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
uses: SonarSource/sonarqube-scan-action@1b442ee39ac3fa7c2acdd410208dcb2bcfaae6c4 # v4.1.0
|
uses: SonarSource/sonarqube-scan-action@1a6d90ebcb0e6a6b1d87e37ba693fe453195ae25 # v5.3.1
|
||||||
if: env.sonar_secret != ''
|
if: env.sonar_secret != ''
|
||||||
test-e2e:
|
test-e2e:
|
||||||
name: Run end-to-end tests
|
name: Run end-to-end tests
|
||||||
if: ${{ needs.changes.outputs.backend == 'true' }}
|
if: ${{ needs.changes.outputs.backend == 'true' }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: oracle-vm-16cpu-64gb-x86-64
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
# latest: true means that this version mush upload the coverage report to codecov.io
|
||||||
|
# We designate the latest version because we only collect code coverage for that version.
|
||||||
k3s:
|
k3s:
|
||||||
- version: v1.31.0
|
- version: v1.34.2
|
||||||
# We designate the latest version because we only collect code coverage for that version.
|
|
||||||
latest: true
|
latest: true
|
||||||
- version: v1.30.4
|
- version: v1.33.1
|
||||||
latest: false
|
latest: false
|
||||||
- version: v1.29.8
|
- version: v1.32.1
|
||||||
latest: false
|
latest: false
|
||||||
- version: v1.28.13
|
- version: v1.31.0
|
||||||
latest: false
|
latest: false
|
||||||
needs:
|
needs:
|
||||||
- build-go
|
- build-go
|
||||||
- changes
|
- changes
|
||||||
env:
|
env:
|
||||||
GOPATH: /home/runner/go
|
GOPATH: /home/ubuntu/go
|
||||||
ARGOCD_FAKE_IN_CLUSTER: "true"
|
ARGOCD_FAKE_IN_CLUSTER: 'true'
|
||||||
ARGOCD_SSH_DATA_PATH: "/tmp/argo-e2e/app/config/ssh"
|
ARGOCD_SSH_DATA_PATH: '/tmp/argo-e2e/app/config/ssh'
|
||||||
ARGOCD_TLS_DATA_PATH: "/tmp/argo-e2e/app/config/tls"
|
ARGOCD_TLS_DATA_PATH: '/tmp/argo-e2e/app/config/tls'
|
||||||
ARGOCD_E2E_SSH_KNOWN_HOSTS: "../fixture/certs/ssh_known_hosts"
|
ARGOCD_E2E_SSH_KNOWN_HOSTS: '../fixture/certs/ssh_known_hosts'
|
||||||
ARGOCD_E2E_K3S: "true"
|
ARGOCD_E2E_K3S: 'true'
|
||||||
ARGOCD_IN_CI: "true"
|
ARGOCD_IN_CI: 'true'
|
||||||
ARGOCD_E2E_APISERVER_PORT: "8088"
|
ARGOCD_E2E_APISERVER_PORT: '8088'
|
||||||
ARGOCD_APPLICATION_NAMESPACES: "argocd-e2e-external,argocd-e2e-external-2"
|
ARGOCD_APPLICATION_NAMESPACES: 'argocd-e2e-external,argocd-e2e-external-2'
|
||||||
ARGOCD_SERVER: "127.0.0.1:8088"
|
ARGOCD_SERVER: '127.0.0.1:8088'
|
||||||
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.E2E_TEST_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||||
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
GITLAB_TOKEN: ${{ secrets.E2E_TEST_GITLAB_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: Free Disk Space (Ubuntu)
|
||||||
|
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
|
||||||
|
with:
|
||||||
|
large-packages: false
|
||||||
|
docker-images: false
|
||||||
|
swap-storage: false
|
||||||
|
tool-cache: false
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
- name: GH actions workaround - Kill XSP4 process
|
- name: GH actions workaround - Kill XSP4 process
|
||||||
@@ -445,19 +462,19 @@ jobs:
|
|||||||
set -x
|
set -x
|
||||||
curl -sfL https://get.k3s.io | sh -
|
curl -sfL https://get.k3s.io | sh -
|
||||||
sudo chmod -R a+rw /etc/rancher/k3s
|
sudo chmod -R a+rw /etc/rancher/k3s
|
||||||
sudo mkdir -p $HOME/.kube && sudo chown -R runner $HOME/.kube
|
sudo mkdir -p $HOME/.kube && sudo chown -R ubuntu $HOME/.kube
|
||||||
sudo k3s kubectl config view --raw > $HOME/.kube/config
|
sudo k3s kubectl config view --raw > $HOME/.kube/config
|
||||||
sudo chown runner $HOME/.kube/config
|
sudo chown ubuntu $HOME/.kube/config
|
||||||
sudo chmod go-r $HOME/.kube/config
|
sudo chmod go-r $HOME/.kube/config
|
||||||
kubectl version
|
kubectl version
|
||||||
- name: Restore go build cache
|
- name: Restore go build cache
|
||||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/go-build
|
path: ~/.cache/go-build
|
||||||
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
key: ${{ runner.os }}-go-build-v1-${{ github.run_id }}
|
||||||
- name: Add ~/go/bin to PATH
|
- name: Add ~/go/bin to PATH
|
||||||
run: |
|
run: |
|
||||||
echo "/home/runner/go/bin" >> $GITHUB_PATH
|
echo "/home/ubuntu/go/bin" >> $GITHUB_PATH
|
||||||
- name: Add /usr/local/bin to PATH
|
- name: Add /usr/local/bin to PATH
|
||||||
run: |
|
run: |
|
||||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||||
@@ -477,13 +494,13 @@ jobs:
|
|||||||
git config --global user.email "john.doe@example.com"
|
git config --global user.email "john.doe@example.com"
|
||||||
- name: Pull Docker image required for tests
|
- name: Pull Docker image required for tests
|
||||||
run: |
|
run: |
|
||||||
docker pull ghcr.io/dexidp/dex:v2.41.1
|
docker pull ghcr.io/dexidp/dex:v2.43.0
|
||||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||||
docker pull redis:7.0.15-alpine
|
docker pull redis:8.2.2-alpine
|
||||||
- name: Create target directory for binaries in the build-process
|
- name: Create target directory for binaries in the build-process
|
||||||
run: |
|
run: |
|
||||||
mkdir -p dist
|
mkdir -p dist
|
||||||
chown runner dist
|
chown ubuntu dist
|
||||||
- name: Run E2E server and wait for it being available
|
- name: Run E2E server and wait for it being available
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
run: |
|
run: |
|
||||||
@@ -509,13 +526,13 @@ jobs:
|
|||||||
goreman run stop-all || echo "goreman trouble"
|
goreman run stop-all || echo "goreman trouble"
|
||||||
sleep 30
|
sleep 30
|
||||||
- name: Upload e2e coverage report
|
- name: Upload e2e coverage report
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: e2e-code-coverage
|
name: e2e-code-coverage
|
||||||
path: /tmp/coverage
|
path: /tmp/coverage
|
||||||
if: ${{ matrix.k3s.latest }}
|
if: ${{ matrix.k3s.latest }}
|
||||||
- name: Upload e2e-server logs
|
- name: Upload e2e-server logs
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: e2e-server-k8s${{ matrix.k3s.version }}.log
|
name: e2e-server-k8s${{ matrix.k3s.version }}.log
|
||||||
path: /tmp/e2e-server.log
|
path: /tmp/e2e-server.log
|
||||||
@@ -542,4 +559,4 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
|||||||
|
|
||||||
# Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
|
# Use correct go version. https://github.com/github/codeql-action/issues/1842#issuecomment-1704398087
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version-file: go.mod
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
|||||||
19
.github/workflows/image-reuse.yaml
vendored
19
.github/workflows/image-reuse.yaml
vendored
@@ -17,11 +17,9 @@ on:
|
|||||||
platforms:
|
platforms:
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
default: linux/amd64
|
|
||||||
push:
|
push:
|
||||||
required: true
|
required: true
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
|
||||||
target:
|
target:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
@@ -69,15 +67,16 @@ jobs:
|
|||||||
if: ${{ github.ref_type != 'tag'}}
|
if: ${{ github.ref_type != 'tag'}}
|
||||||
|
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ inputs.go-version }}
|
go-version: ${{ inputs.go-version }}
|
||||||
|
cache: false
|
||||||
|
|
||||||
- name: Install cosign
|
- name: Install cosign
|
||||||
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
|
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
|
||||||
|
|
||||||
- uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0
|
- uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||||
- uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1
|
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||||
|
|
||||||
- name: Setup tags for container image as a CSV type
|
- name: Setup tags for container image as a CSV type
|
||||||
run: |
|
run: |
|
||||||
@@ -104,7 +103,7 @@ jobs:
|
|||||||
echo 'EOF' >> $GITHUB_ENV
|
echo 'EOF' >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Login to Quay.io
|
- name: Login to Quay.io
|
||||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
registry: quay.io
|
registry: quay.io
|
||||||
username: ${{ secrets.quay_username }}
|
username: ${{ secrets.quay_username }}
|
||||||
@@ -112,7 +111,7 @@ jobs:
|
|||||||
if: ${{ inputs.quay_image_name && inputs.push }}
|
if: ${{ inputs.quay_image_name && inputs.push }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ secrets.ghcr_username }}
|
username: ${{ secrets.ghcr_username }}
|
||||||
@@ -120,7 +119,7 @@ jobs:
|
|||||||
if: ${{ inputs.ghcr_image_name && inputs.push }}
|
if: ${{ inputs.ghcr_image_name && inputs.push }}
|
||||||
|
|
||||||
- name: Login to dockerhub Container Registry
|
- name: Login to dockerhub Container Registry
|
||||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.docker_username }}
|
username: ${{ secrets.docker_username }}
|
||||||
password: ${{ secrets.docker_password }}
|
password: ${{ secrets.docker_password }}
|
||||||
@@ -143,7 +142,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push container image
|
- name: Build and push container image
|
||||||
id: image
|
id: image
|
||||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 #v6.10.0
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 #v6.18.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
platforms: ${{ inputs.platforms }}
|
platforms: ${{ inputs.platforms }}
|
||||||
|
|||||||
17
.github/workflows/image.yaml
vendored
17
.github/workflows/image.yaml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
types: [ labeled, unlabeled, opened, synchronize, reopened ]
|
types: [labeled, unlabeled, opened, synchronize, reopened]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
@@ -46,14 +46,14 @@ jobs:
|
|||||||
needs: [set-vars]
|
needs: [set-vars]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name != 'push' }}
|
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name != 'push' }}
|
||||||
uses: ./.github/workflows/image-reuse.yaml
|
uses: ./.github/workflows/image-reuse.yaml
|
||||||
with:
|
with:
|
||||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
go-version: 1.23.3
|
go-version: 1.25.6
|
||||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||||
push: false
|
push: false
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
needs: [set-vars]
|
needs: [set-vars]
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||||
uses: ./.github/workflows/image-reuse.yaml
|
uses: ./.github/workflows/image-reuse.yaml
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
|
ghcr_image_name: ghcr.io/argoproj/argo-cd/argocd:${{ needs.set-vars.outputs.image-tag }}
|
||||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
go-version: 1.23.3
|
go-version: 1.25.6
|
||||||
platforms: ${{ needs.set-vars.outputs.platforms }}
|
platforms: ${{ needs.set-vars.outputs.platforms }}
|
||||||
push: true
|
push: true
|
||||||
secrets:
|
secrets:
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||||
with:
|
with:
|
||||||
image: ghcr.io/argoproj/argo-cd/argocd
|
image: ghcr.io/argoproj/argo-cd/argocd
|
||||||
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
digest: ${{ needs.build-and-publish.outputs.image-digest }}
|
||||||
@@ -101,8 +101,8 @@ jobs:
|
|||||||
- build-and-publish
|
- build-and-publish
|
||||||
- set-vars
|
- set-vars
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # for git to push upgrade commit if not already deployed
|
contents: write # for git to push upgrade commit if not already deployed
|
||||||
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
packages: write # for pushing packages to GHCR, which is used by cd.apps.argoproj.io to avoid polluting Quay with tags
|
||||||
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
if: ${{ github.repository == 'argoproj/argo-cd' && github.event_name == 'push' }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
@@ -116,4 +116,3 @@ jobs:
|
|||||||
git config --global user.name 'CI'
|
git config --global user.name 'CI'
|
||||||
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ needs.set-vars.outputs.image-tag }}' && git push)
|
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ needs.set-vars.outputs.image-tag }}' && git push)
|
||||||
working-directory: argoproj-deployments/argocd
|
working-directory: argoproj-deployments/argocd
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/init-release.yaml
vendored
2
.github/workflows/init-release.yaml
vendored
@@ -64,7 +64,7 @@ jobs:
|
|||||||
git stash pop
|
git stash pop
|
||||||
|
|
||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
with:
|
with:
|
||||||
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
commit-message: "Bump version to ${{ inputs.TARGET_VERSION }}"
|
||||||
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
title: "Bump version to ${{ inputs.TARGET_VERSION }} on ${{ inputs.TARGET_BRANCH }} branch"
|
||||||
|
|||||||
4
.github/workflows/pr-title-check.yml
vendored
4
.github/workflows/pr-title-check.yml
vendored
@@ -12,8 +12,8 @@ permissions: {}
|
|||||||
# workflow being trigger a number of times. This limits it
|
# workflow being trigger a number of times. This limits it
|
||||||
# to one run per PR.
|
# to one run per PR.
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate:
|
validate:
|
||||||
|
|||||||
131
.github/workflows/release.yaml
vendored
131
.github/workflows/release.yaml
vendored
@@ -11,7 +11,7 @@ permissions: {}
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
GOLANG_VERSION: '1.23.3' # Note: go-version must also be set in job argocd-image.with.go-version
|
GOLANG_VERSION: '1.25.6' # Note: go-version must also be set in job argocd-image.with.go-version
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
argocd-image:
|
argocd-image:
|
||||||
@@ -25,40 +25,78 @@ jobs:
|
|||||||
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
|
quay_image_name: quay.io/argoproj/argocd:${{ github.ref_name }}
|
||||||
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
# Note: cannot use env variables to set go-version (https://docs.github.com/en/actions/using-workflows/reusing-workflows#limitations)
|
||||||
# renovate: datasource=golang-version packageName=golang
|
# renovate: datasource=golang-version packageName=golang
|
||||||
go-version: 1.23.3
|
go-version: 1.25.6
|
||||||
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
platforms: linux/amd64,linux/arm64,linux/s390x,linux/ppc64le
|
||||||
push: true
|
push: true
|
||||||
secrets:
|
secrets:
|
||||||
quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
quay_username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||||
quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
quay_password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||||
|
|
||||||
|
setup-variables:
|
||||||
|
name: Setup Release Variables
|
||||||
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
outputs:
|
||||||
|
is_pre_release: ${{ steps.var.outputs.is_pre_release }}
|
||||||
|
is_latest_release: ${{ steps.var.outputs.is_latest_release }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Setup variables
|
||||||
|
id: var
|
||||||
|
run: |
|
||||||
|
set -xue
|
||||||
|
# Fetch all tag information
|
||||||
|
git fetch --prune --tags --force
|
||||||
|
|
||||||
|
LATEST_RELEASE_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | grep -v '-' | tail -n1)
|
||||||
|
|
||||||
|
PRE_RELEASE=false
|
||||||
|
# Check if latest tag is a pre-release
|
||||||
|
if echo ${{ github.ref_name }} | grep -E -- '-rc[0-9]+$';then
|
||||||
|
PRE_RELEASE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
IS_LATEST=false
|
||||||
|
# Ensure latest release tag matches github.ref_name
|
||||||
|
if [[ $LATEST_RELEASE_TAG == ${{ github.ref_name }} ]];then
|
||||||
|
IS_LATEST=true
|
||||||
|
fi
|
||||||
|
echo "is_pre_release=$PRE_RELEASE" >> $GITHUB_OUTPUT
|
||||||
|
echo "is_latest_release=$IS_LATEST" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
argocd-image-provenance:
|
argocd-image-provenance:
|
||||||
needs: [argocd-image]
|
needs: [argocd-image]
|
||||||
permissions:
|
permissions:
|
||||||
actions: read # for detecting the Github Actions environment.
|
actions: read # for detecting the Github Actions environment.
|
||||||
id-token: write # for creating OIDC tokens for signing.
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
packages: write # for uploading attestations. (https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#known-issues)
|
||||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||||
if: github.repository == 'argoproj/argo-cd'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||||
with:
|
with:
|
||||||
image: quay.io/argoproj/argocd
|
image: quay.io/argoproj/argocd
|
||||||
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
digest: ${{ needs.argocd-image.outputs.image-digest }}
|
||||||
secrets:
|
secrets:
|
||||||
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
registry-username: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||||
registry-password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
registry-password: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||||
|
|
||||||
goreleaser:
|
goreleaser:
|
||||||
needs:
|
needs:
|
||||||
|
- setup-variables
|
||||||
- argocd-image
|
- argocd-image
|
||||||
- argocd-image-provenance
|
- argocd-image-provenance
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # used for uploading assets
|
contents: write # used for uploading assets
|
||||||
if: github.repository == 'argoproj/argo-cd'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
env:
|
||||||
|
GORELEASER_MAKE_LATEST: ${{ needs.setup-variables.outputs.is_latest_release }}
|
||||||
outputs:
|
outputs:
|
||||||
hashes: ${{ steps.hash.outputs.hashes }}
|
hashes: ${{ steps.hash.outputs.hashes }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
@@ -70,14 +108,16 @@ jobs:
|
|||||||
run: git fetch --force --tags
|
run: git fetch --force --tags
|
||||||
|
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
|
cache: false
|
||||||
|
|
||||||
- name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in release branches.
|
- name: Set GORELEASER_PREVIOUS_TAG # Workaround, GoReleaser uses 'git-describe' to determine a previous tag. Our tags are created in release branches.
|
||||||
run: |
|
run: |
|
||||||
set -xue
|
set -xue
|
||||||
echo "GORELEASER_PREVIOUS_TAG=$(go run hack/get-previous-release/get-previous-version-for-release-notes.go ${{ github.ref_name }})" >> $GITHUB_ENV
|
GORELEASER_PREVIOUS_TAG=$(go run hack/get-previous-release/get-previous-version-for-release-notes.go ${{ github.ref_name }}) || exit 1
|
||||||
|
echo "GORELEASER_PREVIOUS_TAG=$GORELEASER_PREVIOUS_TAG" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set environment variables for ldflags
|
- name: Set environment variables for ldflags
|
||||||
id: set_ldflag
|
id: set_ldflag
|
||||||
@@ -94,7 +134,7 @@ jobs:
|
|||||||
tool-cache: false
|
tool-cache: false
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||||
id: run-goreleaser
|
id: run-goreleaser
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
@@ -107,7 +147,7 @@ jobs:
|
|||||||
- name: Generate subject for provenance
|
- name: Generate subject for provenance
|
||||||
id: hash
|
id: hash
|
||||||
env:
|
env:
|
||||||
ARTIFACTS: "${{ steps.run-goreleaser.outputs.artifacts }}"
|
ARTIFACTS: '${{ steps.run-goreleaser.outputs.artifacts }}'
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
@@ -126,10 +166,10 @@ jobs:
|
|||||||
contents: write # Needed for release uploads
|
contents: write # Needed for release uploads
|
||||||
if: github.repository == 'argoproj/argo-cd'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
# Must be refernced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||||
with:
|
with:
|
||||||
base64-subjects: "${{ needs.goreleaser.outputs.hashes }}"
|
base64-subjects: '${{ needs.goreleaser.outputs.hashes }}'
|
||||||
provenance-name: "argocd-cli.intoto.jsonl"
|
provenance-name: 'argocd-cli.intoto.jsonl'
|
||||||
upload-assets: true
|
upload-assets: true
|
||||||
|
|
||||||
generate-sbom:
|
generate-sbom:
|
||||||
@@ -140,7 +180,7 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write # Needed for release uploads
|
contents: write # Needed for release uploads
|
||||||
outputs:
|
outputs:
|
||||||
hashes: ${{ steps.sbom-hash.outputs.hashes}}
|
hashes: ${{ steps.sbom-hash.outputs.hashes }}
|
||||||
if: github.repository == 'argoproj/argo-cd'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
@@ -151,9 +191,10 @@ jobs:
|
|||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Setup Golang
|
- name: Setup Golang
|
||||||
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
|
uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GOLANG_VERSION }}
|
go-version: ${{ env.GOLANG_VERSION }}
|
||||||
|
cache: false
|
||||||
|
|
||||||
- name: Generate SBOM (spdx)
|
- name: Generate SBOM (spdx)
|
||||||
id: spdx-builder
|
id: spdx-builder
|
||||||
@@ -164,7 +205,7 @@ jobs:
|
|||||||
SIGS_BOM_VERSION: v0.2.1
|
SIGS_BOM_VERSION: v0.2.1
|
||||||
# comma delimited list of project relative folders to inspect for package
|
# comma delimited list of project relative folders to inspect for package
|
||||||
# managers (gomod, yarn, npm).
|
# managers (gomod, yarn, npm).
|
||||||
PROJECT_FOLDERS: ".,./ui"
|
PROJECT_FOLDERS: '.,./ui'
|
||||||
# full qualified name of the docker image to be inspected
|
# full qualified name of the docker image to be inspected
|
||||||
DOCKER_IMAGE: quay.io/argoproj/argocd:${{ github.ref_name }}
|
DOCKER_IMAGE: quay.io/argoproj/argocd:${{ github.ref_name }}
|
||||||
run: |
|
run: |
|
||||||
@@ -195,7 +236,7 @@ jobs:
|
|||||||
echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
|
echo "hashes=$(sha256sum /tmp/sbom.tar.gz | base64 -w0)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Upload SBOM
|
- name: Upload SBOM
|
||||||
uses: softprops/action-gh-release@7b4da11513bf3f43f9999e90eabced41ab8bb048 # v2.2.0
|
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
@@ -210,14 +251,15 @@ jobs:
|
|||||||
contents: write # Needed for release uploads
|
contents: write # Needed for release uploads
|
||||||
if: github.repository == 'argoproj/argo-cd'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
# Must be referenced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
# Must be referenced by a tag. https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md#referencing-the-slsa-generator
|
||||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||||
with:
|
with:
|
||||||
base64-subjects: "${{ needs.generate-sbom.outputs.hashes }}"
|
base64-subjects: '${{ needs.generate-sbom.outputs.hashes }}'
|
||||||
provenance-name: "argocd-sbom.intoto.jsonl"
|
provenance-name: 'argocd-sbom.intoto.jsonl'
|
||||||
upload-assets: true
|
upload-assets: true
|
||||||
|
|
||||||
post-release:
|
post-release:
|
||||||
needs:
|
needs:
|
||||||
|
- setup-variables
|
||||||
- argocd-image
|
- argocd-image
|
||||||
- goreleaser
|
- goreleaser
|
||||||
- generate-sbom
|
- generate-sbom
|
||||||
@@ -226,6 +268,8 @@ jobs:
|
|||||||
pull-requests: write # Needed to create PR for VERSION update.
|
pull-requests: write # Needed to create PR for VERSION update.
|
||||||
if: github.repository == 'argoproj/argo-cd'
|
if: github.repository == 'argoproj/argo-cd'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
env:
|
||||||
|
TAG_STABLE: ${{ needs.setup-variables.outputs.is_latest_release }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
uses: actions/checkout@8410ad0602e1e429cee44a835ae9f77f654a6694 # v4.0.0
|
||||||
@@ -239,27 +283,6 @@ jobs:
|
|||||||
git config --global user.email 'ci@argoproj.com'
|
git config --global user.email 'ci@argoproj.com'
|
||||||
git config --global user.name 'CI'
|
git config --global user.name 'CI'
|
||||||
|
|
||||||
- name: Check if tag is the latest version and not a pre-release
|
|
||||||
run: |
|
|
||||||
set -xue
|
|
||||||
# Fetch all tag information
|
|
||||||
git fetch --prune --tags --force
|
|
||||||
|
|
||||||
LATEST_TAG=$(git -c 'versionsort.suffix=-rc' tag --list --sort=version:refname | tail -n1)
|
|
||||||
|
|
||||||
PRE_RELEASE=false
|
|
||||||
# Check if latest tag is a pre-release
|
|
||||||
if echo $LATEST_TAG | grep -E -- '-rc[0-9]+$';then
|
|
||||||
PRE_RELEASE=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure latest tag matches github.ref_name & not a pre-release
|
|
||||||
if [[ $LATEST_TAG == ${{ github.ref_name }} ]] && [[ $PRE_RELEASE != 'true' ]];then
|
|
||||||
echo "TAG_STABLE=true" >> $GITHUB_ENV
|
|
||||||
else
|
|
||||||
echo "TAG_STABLE=false" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Update stable tag to latest version
|
- name: Update stable tag to latest version
|
||||||
run: |
|
run: |
|
||||||
git tag -f stable ${{ github.ref_name }}
|
git tag -f stable ${{ github.ref_name }}
|
||||||
@@ -293,10 +316,10 @@ jobs:
|
|||||||
if: ${{ env.UPDATE_VERSION == 'true' }}
|
if: ${{ env.UPDATE_VERSION == 'true' }}
|
||||||
|
|
||||||
- name: Create PR to update VERSION on master branch
|
- name: Create PR to update VERSION on master branch
|
||||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5
|
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||||
with:
|
with:
|
||||||
commit-message: Bump version in master
|
commit-message: Bump version in master
|
||||||
title: "chore: Bump version in master"
|
title: 'chore: Bump version in master'
|
||||||
body: All images built from master should indicate which version we are on track for.
|
body: All images built from master should indicate which version we are on track for.
|
||||||
signoff: true
|
signoff: true
|
||||||
branch: update-version
|
branch: update-version
|
||||||
|
|||||||
38
.github/workflows/renovate.yaml
vendored
Normal file
38
.github/workflows/renovate.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: Renovate
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 * * * *'
|
||||||
|
workflow_dispatch: {}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
renovate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Get token
|
||||||
|
id: get_token
|
||||||
|
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
|
||||||
|
with:
|
||||||
|
app-id: ${{ vars.RENOVATE_APP_ID }}
|
||||||
|
private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # 6.0.1
|
||||||
|
|
||||||
|
# Some codegen commands require Go to be setup
|
||||||
|
- name: Setup Golang
|
||||||
|
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||||
|
with:
|
||||||
|
# renovate: datasource=golang-version packageName=golang
|
||||||
|
go-version: 1.25.6
|
||||||
|
|
||||||
|
- name: Self-hosted Renovate
|
||||||
|
uses: renovatebot/github-action@f8af9272cd94a4637c29f60dea8731afd3134473 #43.0.12
|
||||||
|
with:
|
||||||
|
configurationFile: .github/configs/renovate-config.js
|
||||||
|
token: '${{ steps.get_token.outputs.token }}'
|
||||||
|
env:
|
||||||
|
LOG_LEVEL: 'debug'
|
||||||
|
RENOVATE_REPOSITORIES: '${{ github.repository }}'
|
||||||
4
.github/workflows/scorecard.yaml
vendored
4
.github/workflows/scorecard.yaml
vendored
@@ -35,7 +35,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: "Run analysis"
|
- name: "Run analysis"
|
||||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
|
||||||
with:
|
with:
|
||||||
results_file: results.sarif
|
results_file: results.sarif
|
||||||
results_format: sarif
|
results_format: sarif
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -8,6 +8,7 @@ ui/dist/app/*
|
|||||||
!ui/dist/app/gitkeep
|
!ui/dist/app/gitkeep
|
||||||
site/
|
site/
|
||||||
*.iml
|
*.iml
|
||||||
|
.tilt-bin/
|
||||||
# delve debug binaries
|
# delve debug binaries
|
||||||
cmd/**/debug
|
cmd/**/debug
|
||||||
debug.test
|
debug.test
|
||||||
@@ -19,6 +20,7 @@ node_modules/
|
|||||||
.kube/
|
.kube/
|
||||||
./test/cmp/*.sock
|
./test/cmp/*.sock
|
||||||
.envrc.remote
|
.envrc.remote
|
||||||
|
.mirrord/
|
||||||
.*.swp
|
.*.swp
|
||||||
rerunreport.txt
|
rerunreport.txt
|
||||||
|
|
||||||
@@ -27,3 +29,6 @@ cmd/argocd/argocd
|
|||||||
cmd/argocd-application-controller/argocd-application-controller
|
cmd/argocd-application-controller/argocd-application-controller
|
||||||
cmd/argocd-repo-server/argocd-repo-server
|
cmd/argocd-repo-server/argocd-repo-server
|
||||||
cmd/argocd-server/argocd-server
|
cmd/argocd-server/argocd-server
|
||||||
|
|
||||||
|
# ignore generated `.argocd-helm-dep-up` marker file; this should not be committed to git
|
||||||
|
reposerver/repository/testdata/**/.argocd-helm-dep-up
|
||||||
21
.gitpod.Dockerfile
vendored
21
.gitpod.Dockerfile
vendored
@@ -1,21 +0,0 @@
|
|||||||
FROM gitpod/workspace-full@sha256:230285e0b949e6d728d384b2029a4111db7b9c87c182f22f32a0be9e36b225df
|
|
||||||
|
|
||||||
USER root
|
|
||||||
|
|
||||||
RUN curl -o /usr/local/bin/kubectl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \
|
|
||||||
chmod +x /usr/local/bin/kubectl
|
|
||||||
|
|
||||||
RUN curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v2.3.1/kubebuilder_2.3.1_$(go env GOOS)_$(go env GOARCH).tar.gz | \
|
|
||||||
tar -xz -C /tmp/ && mv /tmp/kubebuilder_2.3.1_$(go env GOOS)_$(go env GOARCH) /usr/local/kubebuilder
|
|
||||||
|
|
||||||
ENV GOCACHE=/go-build-cache
|
|
||||||
|
|
||||||
RUN apt-get install redis-server -y
|
|
||||||
RUN go install github.com/mattn/goreman@latest
|
|
||||||
|
|
||||||
RUN chown -R gitpod:gitpod /go-build-cache
|
|
||||||
|
|
||||||
USER gitpod
|
|
||||||
|
|
||||||
ENV ARGOCD_REDIS_LOCAL=true
|
|
||||||
ENV KUBECONFIG=/tmp/kubeconfig
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
image:
|
|
||||||
file: .gitpod.Dockerfile
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- init: make mod-download-local dep-ui-local && GO111MODULE=off go install github.com/mattn/goreman@latest
|
|
||||||
command: make start-test-k8s
|
|
||||||
289
.golangci.yaml
289
.golangci.yaml
@@ -1,59 +1,252 @@
|
|||||||
issues:
|
formatters:
|
||||||
exclude:
|
|
||||||
- SA5011
|
|
||||||
max-issues-per-linter: 0
|
|
||||||
max-same-issues: 0
|
|
||||||
exclude-rules:
|
|
||||||
- path: '(.+)_test\.go'
|
|
||||||
linters:
|
|
||||||
- unparam
|
|
||||||
linters:
|
|
||||||
enable:
|
enable:
|
||||||
- errcheck
|
|
||||||
- errorlint
|
|
||||||
- gocritic
|
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- goimports
|
- goimports
|
||||||
- gosimple
|
|
||||||
|
settings:
|
||||||
|
goimports:
|
||||||
|
local-prefixes:
|
||||||
|
- github.com/argoproj/argo-cd/v3
|
||||||
|
|
||||||
|
issues:
|
||||||
|
max-issues-per-linter: 0
|
||||||
|
|
||||||
|
max-same-issues: 0
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- errorlint
|
||||||
|
- exptostd
|
||||||
|
- gocritic
|
||||||
|
- gomodguard
|
||||||
- govet
|
- govet
|
||||||
- ineffassign
|
- importas
|
||||||
- misspell
|
- misspell
|
||||||
- perfsprint
|
- perfsprint
|
||||||
|
- revive
|
||||||
- staticcheck
|
- staticcheck
|
||||||
- testifylint
|
- testifylint
|
||||||
- thelper
|
- thelper
|
||||||
|
- tparallel
|
||||||
- unparam
|
- unparam
|
||||||
- unused
|
|
||||||
- usestdlibvars
|
- usestdlibvars
|
||||||
- whitespace
|
- usetesting
|
||||||
linters-settings:
|
- whitespace
|
||||||
gocritic:
|
|
||||||
disabled-checks:
|
exclusions:
|
||||||
- appendAssign
|
rules:
|
||||||
- assignOp # Keep it disabled for readability
|
- linters:
|
||||||
- badCond
|
- unparam
|
||||||
- commentFormatting
|
path: (.+)_test\.go
|
||||||
- exitAfterDefer
|
|
||||||
- ifElseChain
|
presets:
|
||||||
- mapKey
|
- comments
|
||||||
- singleCaseSwitch
|
- common-false-positives
|
||||||
- typeSwitchVar
|
- legacy
|
||||||
goimports:
|
- std-error-handling
|
||||||
local-prefixes: github.com/argoproj/argo-cd/v2
|
|
||||||
perfsprint:
|
warn-unused: true
|
||||||
# Optimizes even if it requires an int or uint type cast.
|
|
||||||
int-conversion: true
|
settings:
|
||||||
# Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.
|
gocritic:
|
||||||
err-error: false
|
enable-all: true
|
||||||
# Optimizes `fmt.Errorf`.
|
# Most of these should probably be enabled one-by-one.
|
||||||
errorf: false
|
disabled-checks:
|
||||||
# Optimizes `fmt.Sprintf` with only one argument.
|
- appendAssign
|
||||||
sprintf1: true
|
- appendCombine # Leave disabled, multi-line assigns can be more readable.
|
||||||
# Optimizes into strings concatenation.
|
- assignOp # Leave disabled, assign operations can be more confusing than helpful.
|
||||||
strconcat: false
|
- commentedOutCode
|
||||||
testifylint:
|
- deferInLoop
|
||||||
enable-all: true
|
- exitAfterDefer
|
||||||
disable:
|
- hugeParam
|
||||||
- go-require
|
- importShadow
|
||||||
run:
|
- paramTypeCombine # Leave disabled, there are too many failures to be worth fixing.
|
||||||
timeout: 50m
|
- rangeValCopy
|
||||||
|
- tooManyResultsChecker
|
||||||
|
- unnamedResult
|
||||||
|
- whyNoLint
|
||||||
|
|
||||||
|
gomodguard:
|
||||||
|
blocked:
|
||||||
|
modules:
|
||||||
|
- github.com/golang-jwt/jwt/v4:
|
||||||
|
recommendations:
|
||||||
|
- github.com/golang-jwt/jwt/v5
|
||||||
|
|
||||||
|
- github.com/imdario/mergo:
|
||||||
|
recommendations:
|
||||||
|
- dario.cat/mergo
|
||||||
|
reason: '`github.com/imdario/mergo` has been renamed.'
|
||||||
|
|
||||||
|
- github.com/pkg/errors:
|
||||||
|
recommendations:
|
||||||
|
- errors
|
||||||
|
|
||||||
|
govet:
|
||||||
|
disable:
|
||||||
|
- fieldalignment
|
||||||
|
- shadow
|
||||||
|
enable-all: true
|
||||||
|
|
||||||
|
importas:
|
||||||
|
alias:
|
||||||
|
- pkg: github.com/golang-jwt/jwt/v5
|
||||||
|
alias: jwtgo
|
||||||
|
|
||||||
|
- pkg: k8s.io/api/apps/v1
|
||||||
|
alias: appsv1
|
||||||
|
|
||||||
|
- pkg: k8s.io/api/core/v1
|
||||||
|
alias: corev1
|
||||||
|
|
||||||
|
- pkg: k8s.io/api/rbac/v1
|
||||||
|
alias: rbacv1
|
||||||
|
|
||||||
|
- pkg: k8s.io/apimachinery/pkg/api/errors
|
||||||
|
alias: apierrors
|
||||||
|
|
||||||
|
- pkg: k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
|
||||||
|
alias: apiextensionsv1
|
||||||
|
|
||||||
|
- pkg: k8s.io/apimachinery/pkg/apis/meta/v1
|
||||||
|
alias: metav1
|
||||||
|
|
||||||
|
- pkg: k8s.io/client-go/informers/core/v1
|
||||||
|
alias: informersv1
|
||||||
|
|
||||||
|
- pkg: errors
|
||||||
|
alias: stderrors
|
||||||
|
|
||||||
|
- pkg: github.com/argoproj/argo-cd/v3/util/io
|
||||||
|
alias: utilio
|
||||||
|
|
||||||
|
nolintlint:
|
||||||
|
require-specific: true
|
||||||
|
|
||||||
|
perfsprint:
|
||||||
|
# Optimizes even if it requires an int or uint type cast.
|
||||||
|
int-conversion: true
|
||||||
|
# Optimizes into `err.Error()` even if it is only equivalent for non-nil errors.
|
||||||
|
err-error: true
|
||||||
|
# Optimizes `fmt.Errorf`.
|
||||||
|
errorf: true
|
||||||
|
# Optimizes `fmt.Sprintf` with only one argument.
|
||||||
|
sprintf1: true
|
||||||
|
# Optimizes into strings concatenation.
|
||||||
|
strconcat: true
|
||||||
|
|
||||||
|
revive:
|
||||||
|
# https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md
|
||||||
|
rules:
|
||||||
|
- name: bool-literal-in-expr
|
||||||
|
|
||||||
|
- name: blank-imports
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: context-as-argument
|
||||||
|
arguments:
|
||||||
|
- allowTypesBefore: '*testing.T,testing.TB'
|
||||||
|
|
||||||
|
- name: context-keys-type
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: dot-imports
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: duplicated-imports
|
||||||
|
|
||||||
|
- name: early-return
|
||||||
|
arguments:
|
||||||
|
- preserveScope
|
||||||
|
|
||||||
|
- name: empty-block
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: error-naming
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: error-return
|
||||||
|
|
||||||
|
- name: error-strings
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: errorf
|
||||||
|
|
||||||
|
- name: identical-branches
|
||||||
|
|
||||||
|
- name: if-return
|
||||||
|
|
||||||
|
- name: increment-decrement
|
||||||
|
|
||||||
|
- name: indent-error-flow
|
||||||
|
arguments:
|
||||||
|
- preserveScope
|
||||||
|
|
||||||
|
- name: modifies-parameter
|
||||||
|
|
||||||
|
- name: optimize-operands-order
|
||||||
|
|
||||||
|
- name: range
|
||||||
|
|
||||||
|
- name: receiver-naming
|
||||||
|
|
||||||
|
- name: redefines-builtin-id
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: redundant-import-alias
|
||||||
|
|
||||||
|
- name: superfluous-else
|
||||||
|
arguments:
|
||||||
|
- preserveScope
|
||||||
|
|
||||||
|
- name: time-equal
|
||||||
|
|
||||||
|
- name: time-naming
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: unexported-return
|
||||||
|
disabled: true
|
||||||
|
|
||||||
|
- name: unnecessary-stmt
|
||||||
|
|
||||||
|
- name: unreachable-code
|
||||||
|
|
||||||
|
- name: unused-parameter
|
||||||
|
|
||||||
|
- name: use-any
|
||||||
|
|
||||||
|
- name: useless-break
|
||||||
|
|
||||||
|
- name: var-declaration
|
||||||
|
|
||||||
|
- name: var-naming
|
||||||
|
arguments:
|
||||||
|
- - ID
|
||||||
|
- - VM
|
||||||
|
- - skipPackageNameChecks: true
|
||||||
|
upperCaseConst: true
|
||||||
|
|
||||||
|
staticcheck:
|
||||||
|
checks:
|
||||||
|
- all
|
||||||
|
- -SA5011
|
||||||
|
- -ST1003
|
||||||
|
- -ST1016
|
||||||
|
|
||||||
|
testifylint:
|
||||||
|
enable-all: true
|
||||||
|
|
||||||
|
disable:
|
||||||
|
- go-require
|
||||||
|
|
||||||
|
unused:
|
||||||
|
field-writes-are-uses: false
|
||||||
|
exported-fields-are-used: false
|
||||||
|
|
||||||
|
usetesting:
|
||||||
|
os-mkdir-temp: false
|
||||||
|
|
||||||
|
output:
|
||||||
|
show-stats: false
|
||||||
|
|
||||||
|
version: "2"
|
||||||
|
|||||||
@@ -16,16 +16,16 @@ builds:
|
|||||||
flags:
|
flags:
|
||||||
- -v
|
- -v
|
||||||
ldflags:
|
ldflags:
|
||||||
- -X github.com/argoproj/argo-cd/v2/common.version={{ .Version }}
|
- -X github.com/argoproj/argo-cd/v3/common.version={{ .Version }}
|
||||||
- -X github.com/argoproj/argo-cd/v2/common.buildDate={{ .Date }}
|
- -X github.com/argoproj/argo-cd/v3/common.buildDate={{ .Date }}
|
||||||
- -X github.com/argoproj/argo-cd/v2/common.gitCommit={{ .FullCommit }}
|
- -X github.com/argoproj/argo-cd/v3/common.gitCommit={{ .FullCommit }}
|
||||||
- -X github.com/argoproj/argo-cd/v2/common.gitTreeState={{ .Env.GIT_TREE_STATE }}
|
- -X github.com/argoproj/argo-cd/v3/common.gitTreeState={{ .Env.GIT_TREE_STATE }}
|
||||||
- -X github.com/argoproj/argo-cd/v2/common.kubectlVersion={{ .Env.KUBECTL_VERSION }}
|
- -X github.com/argoproj/argo-cd/v3/common.kubectlVersion={{ .Env.KUBECTL_VERSION }}
|
||||||
- -extldflags="-static"
|
- -extldflags="-static"
|
||||||
goos:
|
goos:
|
||||||
- linux
|
- linux
|
||||||
- darwin
|
|
||||||
- windows
|
- windows
|
||||||
|
- darwin
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- arm64
|
- arm64
|
||||||
@@ -45,17 +45,18 @@ builds:
|
|||||||
|
|
||||||
archives:
|
archives:
|
||||||
- id: argocd-archive
|
- id: argocd-archive
|
||||||
builds:
|
ids:
|
||||||
- argocd-cli
|
- argocd-cli
|
||||||
name_template: |-
|
name_template: |-
|
||||||
{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}
|
{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}
|
||||||
format: binary
|
formats: [binary]
|
||||||
|
|
||||||
checksum:
|
checksum:
|
||||||
name_template: 'cli_checksums.txt'
|
name_template: 'cli_checksums.txt'
|
||||||
algorithm: sha256
|
algorithm: sha256
|
||||||
|
|
||||||
release:
|
release:
|
||||||
|
make_latest: '{{ .Env.GORELEASER_MAKE_LATEST }}'
|
||||||
prerelease: auto
|
prerelease: auto
|
||||||
draft: false
|
draft: false
|
||||||
header: |
|
header: |
|
||||||
@@ -79,25 +80,28 @@ release:
|
|||||||
|
|
||||||
All Argo CD container images are signed by cosign. A Provenance is generated for container images and CLI binaries which meet the SLSA Level 3 specifications. See the [documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/signed-release-assets) on how to verify.
|
All Argo CD container images are signed by cosign. A Provenance is generated for container images and CLI binaries which meet the SLSA Level 3 specifications. See the [documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/signed-release-assets) on how to verify.
|
||||||
|
|
||||||
|
## Release Notes Blog Post
|
||||||
|
For a detailed breakdown of the key changes and improvements in this release, check out the [official blog post](https://blog.argoproj.io/argo-cd-v3-0-release-candidate-a0b933f4e58f)
|
||||||
|
|
||||||
## Upgrading
|
## Upgrading
|
||||||
|
|
||||||
If upgrading from a different minor version, be sure to read the [upgrading](https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/) documentation.
|
If upgrading from a different minor version, be sure to read the [upgrading](https://argo-cd.readthedocs.io/en/stable/operator-manual/upgrading/overview/) documentation.
|
||||||
footer: |
|
footer: |
|
||||||
**Full Changelog**: https://github.com/argoproj/argo-cd/compare/{{ .PreviousTag }}...{{ .Tag }}
|
**Full Changelog**: https://github.com/argoproj/argo-cd/compare/{{ .PreviousTag }}...{{ .Tag }}
|
||||||
|
|
||||||
<a href="https://argoproj.github.io/cd/"><img src="https://raw.githubusercontent.com/argoproj/argo-site/master/content/pages/cd/gitops-cd.png" width="25%" ></a>
|
<a href="https://argoproj.github.io/cd/"><img src="https://raw.githubusercontent.com/argoproj/argo-site/master/content/pages/cd/gitops-cd.png" width="25%" ></a>
|
||||||
|
|
||||||
|
|
||||||
snapshot: #### To be removed for PR
|
snapshot: #### To be removed for PR
|
||||||
name_template: "2.6.0"
|
version_template: '2.6.0'
|
||||||
|
|
||||||
changelog:
|
changelog:
|
||||||
use:
|
use: github
|
||||||
github
|
|
||||||
sort: asc
|
sort: asc
|
||||||
abbrev: 0
|
abbrev: 0
|
||||||
groups: # Regex use RE2 syntax as defined here: https://github.com/google/re2/wiki/Syntax.
|
groups: # Regex use RE2 syntax as defined here: https://github.com/google/re2/wiki/Syntax.
|
||||||
|
- title: 'Breaking Changes'
|
||||||
|
regexp: '^.*?(\([[:word:]]+\))??!:.+$'
|
||||||
|
order: 0
|
||||||
- title: 'Features'
|
- title: 'Features'
|
||||||
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
|
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
|
||||||
order: 100
|
order: 100
|
||||||
@@ -117,7 +121,4 @@ changelog:
|
|||||||
- '^test:'
|
- '^test:'
|
||||||
- '^.*?Bump(\([[:word:]]+\))?.+$'
|
- '^.*?Bump(\([[:word:]]+\))?.+$'
|
||||||
- '^.*?\[Bot\](\([[:word:]]+\))?.+$'
|
- '^.*?\[Bot\](\([[:word:]]+\))?.+$'
|
||||||
|
|
||||||
|
|
||||||
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||||
|
|
||||||
|
|||||||
150
.mockery.yaml
150
.mockery.yaml
@@ -1,76 +1,88 @@
|
|||||||
# global config
|
dir: '{{.InterfaceDir}}/mocks'
|
||||||
filename: "{{.InterfaceName}}.go"
|
structname: '{{.InterfaceName}}'
|
||||||
dir: "{{.InterfaceDir}}/mocks"
|
filename: '{{.InterfaceName}}.go'
|
||||||
outpkg: "mocks"
|
pkgname: mocks
|
||||||
mockname: "{{.InterfaceName}}"
|
|
||||||
with-expecter: false
|
template-data:
|
||||||
# individual interface config
|
unroll-variadic: true
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
github.com/argoproj/argo-cd/v2/applicationset/generators:
|
github.com/argoproj/argo-cd/v3/applicationset/generators:
|
||||||
interfaces:
|
interfaces:
|
||||||
Generator:
|
Generator: {}
|
||||||
github.com/argoproj/argo-cd/v2/applicationset/services:
|
github.com/argoproj/argo-cd/v3/applicationset/services:
|
||||||
interfaces:
|
interfaces:
|
||||||
Repos:
|
Repos: {}
|
||||||
github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider:
|
github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider:
|
||||||
config:
|
config:
|
||||||
dir: "applicationset/services/scm_provider/aws_codecommit/mocks"
|
dir: applicationset/services/scm_provider/aws_codecommit/mocks
|
||||||
interfaces:
|
interfaces:
|
||||||
AWSCodeCommitClient:
|
AWSCodeCommitClient: {}
|
||||||
AWSTaggingClient:
|
AWSTaggingClient: {}
|
||||||
github.com/microsoft/azure-devops-go-api/azuredevops/git:
|
github.com/argoproj/argo-cd/v3/applicationset/utils:
|
||||||
|
interfaces:
|
||||||
|
Renderer: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/commitserver/apiclient:
|
||||||
|
interfaces:
|
||||||
|
CommitServiceClient: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/commitserver/commit:
|
||||||
|
interfaces:
|
||||||
|
RepoClientFactory: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/controller/cache:
|
||||||
|
interfaces:
|
||||||
|
LiveStateCache: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/controller/hydrator:
|
||||||
|
interfaces:
|
||||||
|
Dependencies: {}
|
||||||
|
RepoGetter: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/pkg/apiclient/cluster:
|
||||||
|
interfaces:
|
||||||
|
ClusterServiceServer: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/pkg/apiclient/session:
|
||||||
|
interfaces:
|
||||||
|
SessionServiceClient: {}
|
||||||
|
SessionServiceServer: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/typed/application/v1alpha1:
|
||||||
|
interfaces:
|
||||||
|
AppProjectInterface: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/reposerver/apiclient:
|
||||||
|
interfaces:
|
||||||
|
RepoServerService_GenerateManifestWithFilesClient: {}
|
||||||
|
RepoServerServiceClient: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/server/application:
|
||||||
|
interfaces:
|
||||||
|
Broadcaster: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/server/extension:
|
||||||
|
interfaces:
|
||||||
|
ApplicationGetter: {}
|
||||||
|
ExtensionMetricsRegistry: {}
|
||||||
|
ProjectGetter: {}
|
||||||
|
RbacEnforcer: {}
|
||||||
|
SettingsGetter: {}
|
||||||
|
UserGetter: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/util/db:
|
||||||
|
interfaces:
|
||||||
|
ArgoDB: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/util/git:
|
||||||
|
interfaces:
|
||||||
|
Client: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/util/helm:
|
||||||
|
interfaces:
|
||||||
|
Client: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/util/oci:
|
||||||
|
interfaces:
|
||||||
|
Client: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/util/io:
|
||||||
|
interfaces:
|
||||||
|
TempPaths: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/util/notification/argocd:
|
||||||
|
interfaces:
|
||||||
|
Service: {}
|
||||||
|
github.com/argoproj/argo-cd/v3/util/workloadidentity:
|
||||||
|
interfaces:
|
||||||
|
TokenProvider: {}
|
||||||
|
github.com/microsoft/azure-devops-go-api/azuredevops/v7/git:
|
||||||
config:
|
config:
|
||||||
dir: "applicationset/services/scm_provider/azure_devops/git/mocks"
|
dir: applicationset/services/scm_provider/azure_devops/git/mocks
|
||||||
interfaces:
|
interfaces:
|
||||||
Client:
|
Client: {}
|
||||||
github.com/argoproj/argo-cd/v2/applicationset/utils:
|
|
||||||
interfaces:
|
|
||||||
Renderer:
|
|
||||||
github.com/argoproj/argo-cd/v2/commitserver/commit:
|
|
||||||
interfaces:
|
|
||||||
RepoClientFactory:
|
|
||||||
github.com/argoproj/argo-cd/v2/commitserver/apiclient:
|
|
||||||
interfaces:
|
|
||||||
CommitServiceClient:
|
|
||||||
Clientset:
|
|
||||||
github.com/argoproj/argo-cd/v2/controller/cache:
|
|
||||||
interfaces:
|
|
||||||
LiveStateCache:
|
|
||||||
github.com/argoproj/argo-cd/v2/reposerver/apiclient:
|
|
||||||
interfaces:
|
|
||||||
RepoServerServiceClient:
|
|
||||||
RepoServerService_GenerateManifestWithFilesClient:
|
|
||||||
github.com/argoproj/argo-cd/v2/server/application:
|
|
||||||
interfaces:
|
|
||||||
Broadcaster:
|
|
||||||
github.com/argoproj/argo-cd/v2/server/extension:
|
|
||||||
interfaces:
|
|
||||||
ApplicationGetter:
|
|
||||||
ExtensionMetricsRegistry:
|
|
||||||
ProjectGetter:
|
|
||||||
RbacEnforcer:
|
|
||||||
SettingsGetter:
|
|
||||||
UserGetter:
|
|
||||||
github.com/argoproj/argo-cd/v2/util/db:
|
|
||||||
interfaces:
|
|
||||||
ArgoDB:
|
|
||||||
github.com/argoproj/argo-cd/v2/util/git:
|
|
||||||
interfaces:
|
|
||||||
Client:
|
|
||||||
github.com/argoproj/argo-cd/v2/util/helm:
|
|
||||||
interfaces:
|
|
||||||
Client:
|
|
||||||
github.com/argoproj/argo-cd/v2/util/io:
|
|
||||||
interfaces:
|
|
||||||
TempPaths:
|
|
||||||
github.com/argoproj/argo-cd/v2/util/notification/argocd:
|
|
||||||
interfaces:
|
|
||||||
Service:
|
|
||||||
# These mocks are not currently used, but they are part of the public API of this package.
|
|
||||||
github.com/argoproj/argo-cd/v2/pkg/apiclient/session:
|
|
||||||
interfaces:
|
|
||||||
SessionServiceServer:
|
|
||||||
SessionServiceClient:
|
|
||||||
github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster:
|
|
||||||
interfaces:
|
|
||||||
ClusterServiceServer:
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ version: 2
|
|||||||
formats: all
|
formats: all
|
||||||
mkdocs:
|
mkdocs:
|
||||||
fail_on_warning: false
|
fail_on_warning: false
|
||||||
|
configuration: mkdocs.yml
|
||||||
python:
|
python:
|
||||||
install:
|
install:
|
||||||
- requirements: docs/requirements.txt
|
- requirements: docs/requirements.txt
|
||||||
|
|||||||
@@ -12,3 +12,8 @@
|
|||||||
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
/.github/** @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||||
/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
/.goreleaser.yaml @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||||
/sonar-project.properties @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
/sonar-project.properties @argoproj/argocd-approvers @argoproj/argocd-approvers-ci
|
||||||
|
|
||||||
|
# CLI
|
||||||
|
/cmd/argocd/** @argoproj/argocd-approvers @argoproj/argocd-approvers-cli
|
||||||
|
/cmd/main.go @argoproj/argocd-approvers @argoproj/argocd-approvers-cli
|
||||||
|
/docs/operator-manual/ @argoproj/argocd-approvers @argoproj/argocd-approvers-cli
|
||||||
36
Dockerfile
36
Dockerfile
@@ -1,10 +1,12 @@
|
|||||||
ARG BASE_IMAGE=docker.io/library/ubuntu:24.04@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15
|
ARG BASE_IMAGE=docker.io/library/ubuntu:25.04@sha256:10bb10bb062de665d4dc3e0ea36715270ead632cfcb74d08ca2273712a0dfb42
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Builder image
|
# Builder image
|
||||||
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
# Initial stage which pulls prepares build dependencies and CLI tooling we need for our final image
|
||||||
# Also used as the image in CI jobs so needs all dependencies
|
# Also used as the image in CI jobs so needs all dependencies
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
FROM docker.io/library/golang:1.23.3@sha256:d56c3e08fe5b27729ee3834854ae8f7015af48fd651cd25d1e3bcf3c19830174 AS builder
|
FROM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c AS builder
|
||||||
|
|
||||||
|
WORKDIR /tmp
|
||||||
|
|
||||||
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||||
|
|
||||||
@@ -23,8 +25,6 @@ RUN apt-get update && apt-get install --no-install-recommends -y \
|
|||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
WORKDIR /tmp
|
|
||||||
|
|
||||||
COPY hack/install.sh hack/tool-versions.sh ./
|
COPY hack/install.sh hack/tool-versions.sh ./
|
||||||
COPY hack/installers installers
|
COPY hack/installers installers
|
||||||
|
|
||||||
@@ -40,8 +40,8 @@ LABEL org.opencontainers.image.source="https://github.com/argoproj/argo-cd"
|
|||||||
|
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
ENV ARGOCD_USER_ID=999
|
ENV ARGOCD_USER_ID=999 \
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
||||||
useradd -r -u $ARGOCD_USER_ID -g argocd argocd && \
|
useradd -r -u $ARGOCD_USER_ID -g argocd argocd && \
|
||||||
@@ -55,11 +55,13 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \
|
|||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
COPY hack/gpg-wrapper.sh /usr/local/bin/gpg-wrapper.sh
|
COPY hack/gpg-wrapper.sh \
|
||||||
COPY hack/git-verify-wrapper.sh /usr/local/bin/git-verify-wrapper.sh
|
hack/git-verify-wrapper.sh \
|
||||||
|
entrypoint.sh \
|
||||||
|
/usr/local/bin/
|
||||||
COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
|
COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
|
||||||
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
|
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
|
||||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
|
||||||
# keep uid_entrypoint.sh for backward compatibility
|
# keep uid_entrypoint.sh for backward compatibility
|
||||||
RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh
|
RUN ln -s /usr/local/bin/entrypoint.sh /usr/local/bin/uid_entrypoint.sh
|
||||||
|
|
||||||
@@ -101,7 +103,7 @@ RUN HOST_ARCH=$TARGETARCH NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OP
|
|||||||
####################################################################################################
|
####################################################################################################
|
||||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.23.3@sha256:d56c3e08fe5b27729ee3834854ae8f7015af48fd651cd25d1e3bcf3c19830174 AS argocd-build
|
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c AS argocd-build
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||||
|
|
||||||
@@ -111,13 +113,13 @@ RUN go mod download
|
|||||||
# Perform the build
|
# Perform the build
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=argocd-ui /src/dist/app /go/src/github.com/argoproj/argo-cd/ui/dist/app
|
COPY --from=argocd-ui /src/dist/app /go/src/github.com/argoproj/argo-cd/ui/dist/app
|
||||||
ARG TARGETOS
|
ARG TARGETOS \
|
||||||
ARG TARGETARCH
|
TARGETARCH
|
||||||
# These build args are optional; if not specified the defaults will be taken from the Makefile
|
# These build args are optional; if not specified the defaults will be taken from the Makefile
|
||||||
ARG GIT_TAG
|
ARG GIT_TAG \
|
||||||
ARG BUILD_DATE
|
BUILD_DATE \
|
||||||
ARG GIT_TREE_STATE
|
GIT_TREE_STATE \
|
||||||
ARG GIT_COMMIT
|
GIT_COMMIT
|
||||||
RUN GIT_COMMIT=$GIT_COMMIT \
|
RUN GIT_COMMIT=$GIT_COMMIT \
|
||||||
GIT_TREE_STATE=$GIT_TREE_STATE \
|
GIT_TREE_STATE=$GIT_TREE_STATE \
|
||||||
GIT_TAG=$GIT_TAG \
|
GIT_TAG=$GIT_TAG \
|
||||||
@@ -130,6 +132,7 @@ RUN GIT_COMMIT=$GIT_COMMIT \
|
|||||||
# Final image
|
# Final image
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
FROM argocd-base
|
FROM argocd-base
|
||||||
|
ENTRYPOINT ["/usr/bin/tini", "--"]
|
||||||
COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd* /usr/local/bin/
|
COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd* /usr/local/bin/
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
@@ -144,4 +147,3 @@ RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \
|
|||||||
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-commit-server
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-commit-server
|
||||||
|
|
||||||
USER $ARGOCD_USER_ID
|
USER $ARGOCD_USER_ID
|
||||||
ENTRYPOINT ["/usr/bin/tini", "--"]
|
|
||||||
|
|||||||
62
Dockerfile.tilt
Normal file
62
Dockerfile.tilt
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
FROM docker.io/library/golang:1.25.6@sha256:fc24d3881a021e7b968a4610fc024fba749f98fe5c07d4f28e6cfa14dc65a84c
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN echo 'deb http://archive.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||||
|
curl \
|
||||||
|
openssh-server \
|
||||||
|
nginx \
|
||||||
|
unzip \
|
||||||
|
fcgiwrap \
|
||||||
|
git \
|
||||||
|
git-lfs \
|
||||||
|
make \
|
||||||
|
wget \
|
||||||
|
gcc \
|
||||||
|
sudo \
|
||||||
|
zip \
|
||||||
|
tini \
|
||||||
|
gpg \
|
||||||
|
tzdata \
|
||||||
|
connect-proxy
|
||||||
|
|
||||||
|
RUN go install github.com/go-delve/delve/cmd/dlv@latest
|
||||||
|
|
||||||
|
COPY hack/install.sh hack/tool-versions.sh ./
|
||||||
|
COPY hack/installers installers
|
||||||
|
|
||||||
|
RUN ./install.sh helm && \
|
||||||
|
INSTALL_PATH=/usr/local/bin ./install.sh kustomize
|
||||||
|
|
||||||
|
COPY hack/gpg-wrapper.sh \
|
||||||
|
hack/git-verify-wrapper.sh \
|
||||||
|
entrypoint.sh \
|
||||||
|
/usr/local/bin/
|
||||||
|
|
||||||
|
# support for mounting configuration from a configmap
|
||||||
|
WORKDIR /app/config/ssh
|
||||||
|
RUN touch ssh_known_hosts && \
|
||||||
|
ln -s /app/config/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts
|
||||||
|
|
||||||
|
WORKDIR /app/config
|
||||||
|
RUN mkdir -p tls && \
|
||||||
|
mkdir -p gpg/source && \
|
||||||
|
mkdir -p gpg/keys
|
||||||
|
|
||||||
|
COPY .tilt-bin/argocd_linux /usr/local/bin/argocd
|
||||||
|
|
||||||
|
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server && \
|
||||||
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server && \
|
||||||
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller && \
|
||||||
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex && \
|
||||||
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-notifications && \
|
||||||
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-applicationset-controller && \
|
||||||
|
ln -s /usr/local/bin/argocd /usr/local/bin/argocd-commit-server
|
||||||
|
|
||||||
|
# directory for Tilt restart file
|
||||||
|
RUN mkdir -p /tilt
|
||||||
|
|
||||||
|
# overridden by Tiltfile
|
||||||
|
ENTRYPOINT ["/usr/bin/tini", "-s", "--", "dlv", "exec", "--continue", "--accept-multiclient", "--headless", "--listen=:2345", "--api-version=2"]
|
||||||
9
Dockerfile.ui.tilt
Normal file
9
Dockerfile.ui.tilt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
FROM node:20
|
||||||
|
|
||||||
|
WORKDIR /app/ui
|
||||||
|
|
||||||
|
COPY ui /app/ui
|
||||||
|
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
ENTRYPOINT ["yarn", "start"]
|
||||||
47
Makefile
47
Makefile
@@ -1,9 +1,18 @@
|
|||||||
PACKAGE=github.com/argoproj/argo-cd/v2/common
|
PACKAGE=github.com/argoproj/argo-cd/v3/common
|
||||||
CURRENT_DIR=$(shell pwd)
|
CURRENT_DIR=$(shell pwd)
|
||||||
DIST_DIR=${CURRENT_DIR}/dist
|
DIST_DIR=${CURRENT_DIR}/dist
|
||||||
CLI_NAME=argocd
|
CLI_NAME=argocd
|
||||||
BIN_NAME=argocd
|
BIN_NAME=argocd
|
||||||
CGO_FLAG=0
|
|
||||||
|
UNAME_S:=$(shell uname)
|
||||||
|
IS_DARWIN:=$(if $(filter Darwin, $(UNAME_S)),true,false)
|
||||||
|
|
||||||
|
# When using OSX/Darwin, you might need to enable CGO for local builds
|
||||||
|
DEFAULT_CGO_FLAG:=0
|
||||||
|
ifeq ($(IS_DARWIN),true)
|
||||||
|
DEFAULT_CGO_FLAG:=1
|
||||||
|
endif
|
||||||
|
CGO_FLAG?=${DEFAULT_CGO_FLAG}
|
||||||
|
|
||||||
GEN_RESOURCES_CLI_NAME=argocd-resources-gen
|
GEN_RESOURCES_CLI_NAME=argocd-resources-gen
|
||||||
|
|
||||||
@@ -34,6 +43,17 @@ endif
|
|||||||
DOCKER_SRCDIR?=$(GOPATH)/src
|
DOCKER_SRCDIR?=$(GOPATH)/src
|
||||||
DOCKER_WORKDIR?=/go/src/github.com/argoproj/argo-cd
|
DOCKER_WORKDIR?=/go/src/github.com/argoproj/argo-cd
|
||||||
|
|
||||||
|
# Allows you to control which Docker network the test-util containers attach to.
|
||||||
|
# This is particularly useful if you are running Kubernetes in Docker (e.g., k3d)
|
||||||
|
# and want the test containers to reach the Kubernetes API via an already-existing Docker network.
|
||||||
|
DOCKER_NETWORK ?= default
|
||||||
|
|
||||||
|
ifneq ($(DOCKER_NETWORK),default)
|
||||||
|
DOCKER_NETWORK_ARG := --network $(DOCKER_NETWORK)
|
||||||
|
else
|
||||||
|
DOCKER_NETWORK_ARG :=
|
||||||
|
endif
|
||||||
|
|
||||||
ARGOCD_PROCFILE?=Procfile
|
ARGOCD_PROCFILE?=Procfile
|
||||||
|
|
||||||
# pointing to python 3.7 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yml
|
# pointing to python 3.7 to match https://github.com/argoproj/argo-cd/blob/master/.readthedocs.yml
|
||||||
@@ -104,11 +124,11 @@ define run-in-test-server
|
|||||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||||
-v /tmp:/tmp${VOLUME_MOUNT} \
|
|
||||||
-w ${DOCKER_WORKDIR} \
|
-w ${DOCKER_WORKDIR} \
|
||||||
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
|
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
|
||||||
-p 4000:4000 \
|
-p 4000:4000 \
|
||||||
-p 5000:5000 \
|
-p 5000:5000 \
|
||||||
|
$(DOCKER_NETWORK_ARG)\
|
||||||
$(PODMAN_ARGS) \
|
$(PODMAN_ARGS) \
|
||||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||||
bash -c "$(1)"
|
bash -c "$(1)"
|
||||||
@@ -129,8 +149,8 @@ define run-in-test-client
|
|||||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||||
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
-v ${HOME}/.kube:/home/user/.kube${VOLUME_MOUNT} \
|
||||||
-v /tmp:/tmp${VOLUME_MOUNT} \
|
|
||||||
-w ${DOCKER_WORKDIR} \
|
-w ${DOCKER_WORKDIR} \
|
||||||
|
$(DOCKER_NETWORK_ARG)\
|
||||||
$(PODMAN_ARGS) \
|
$(PODMAN_ARGS) \
|
||||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||||
bash -c "$(1)"
|
bash -c "$(1)"
|
||||||
@@ -147,7 +167,11 @@ PATH:=$(PATH):$(PWD)/hack
|
|||||||
DOCKER_PUSH?=false
|
DOCKER_PUSH?=false
|
||||||
IMAGE_NAMESPACE?=
|
IMAGE_NAMESPACE?=
|
||||||
# perform static compilation
|
# perform static compilation
|
||||||
STATIC_BUILD?=true
|
DEFAULT_STATIC_BUILD:=true
|
||||||
|
ifeq ($(IS_DARWIN),true)
|
||||||
|
DEFAULT_STATIC_BUILD:=false
|
||||||
|
endif
|
||||||
|
STATIC_BUILD?=${DEFAULT_STATIC_BUILD}
|
||||||
# build development images
|
# build development images
|
||||||
DEV_IMAGE?=false
|
DEV_IMAGE?=false
|
||||||
ARGOCD_GPG_ENABLED?=true
|
ARGOCD_GPG_ENABLED?=true
|
||||||
@@ -355,11 +379,6 @@ mod-vendor: test-tools-image
|
|||||||
mod-vendor-local: mod-download-local
|
mod-vendor-local: mod-download-local
|
||||||
go mod vendor
|
go mod vendor
|
||||||
|
|
||||||
# Deprecated - replace by install-tools-local
|
|
||||||
.PHONY: install-lint-tools
|
|
||||||
install-lint-tools:
|
|
||||||
./hack/install.sh lint-tools
|
|
||||||
|
|
||||||
# Run linter on the code
|
# Run linter on the code
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint: test-tools-image
|
lint: test-tools-image
|
||||||
@@ -435,7 +454,7 @@ test-e2e:
|
|||||||
test-e2e-local: cli-local
|
test-e2e-local: cli-local
|
||||||
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
|
# NO_PROXY ensures all tests don't go out through a proxy if one is configured on the test system
|
||||||
export GO111MODULE=off
|
export GO111MODULE=off
|
||||||
DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results"
|
DIST_DIR=${DIST_DIR} RERUN_FAILS=5 PACKAGES="./test/e2e" ARGOCD_E2E_RECORD=${ARGOCD_E2E_RECORD} ARGOCD_CONFIG_DIR=$(HOME)/.config/argocd-e2e ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v -args -test.gocoverdir="$(PWD)/test-results"
|
||||||
|
|
||||||
# Spawns a shell in the test server container for debugging purposes
|
# Spawns a shell in the test server container for debugging purposes
|
||||||
debug-test-server: test-tools-image
|
debug-test-server: test-tools-image
|
||||||
@@ -490,6 +509,8 @@ start-e2e-local: mod-vendor-local dep-ui-local cli-local
|
|||||||
ARGOCD_APPLICATIONSET_CONTROLLER_TOKENREF_STRICT_MODE=true \
|
ARGOCD_APPLICATIONSET_CONTROLLER_TOKENREF_STRICT_MODE=true \
|
||||||
ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS=http://127.0.0.1:8341,http://127.0.0.1:8342,http://127.0.0.1:8343,http://127.0.0.1:8344 \
|
ARGOCD_APPLICATIONSET_CONTROLLER_ALLOWED_SCM_PROVIDERS=http://127.0.0.1:8341,http://127.0.0.1:8342,http://127.0.0.1:8343,http://127.0.0.1:8344 \
|
||||||
ARGOCD_E2E_TEST=true \
|
ARGOCD_E2E_TEST=true \
|
||||||
|
ARGOCD_HYDRATOR_ENABLED=true \
|
||||||
|
ARGOCD_CLUSTER_CACHE_EVENTS_PROCESSING_INTERVAL=1ms \
|
||||||
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
||||||
ls -lrt /tmp/coverage
|
ls -lrt /tmp/coverage
|
||||||
|
|
||||||
@@ -588,16 +609,19 @@ install-test-tools-local:
|
|||||||
./hack/install.sh kustomize
|
./hack/install.sh kustomize
|
||||||
./hack/install.sh helm
|
./hack/install.sh helm
|
||||||
./hack/install.sh gotestsum
|
./hack/install.sh gotestsum
|
||||||
|
./hack/install.sh oras
|
||||||
|
|
||||||
# Installs all tools required for running codegen (Linux packages)
|
# Installs all tools required for running codegen (Linux packages)
|
||||||
.PHONY: install-codegen-tools-local
|
.PHONY: install-codegen-tools-local
|
||||||
install-codegen-tools-local:
|
install-codegen-tools-local:
|
||||||
./hack/install.sh codegen-tools
|
./hack/install.sh codegen-tools
|
||||||
|
./hack/install.sh codegen-go-tools
|
||||||
|
|
||||||
# Installs all tools required for running codegen (Go packages)
|
# Installs all tools required for running codegen (Go packages)
|
||||||
.PHONY: install-go-tools-local
|
.PHONY: install-go-tools-local
|
||||||
install-go-tools-local:
|
install-go-tools-local:
|
||||||
./hack/install.sh codegen-go-tools
|
./hack/install.sh codegen-go-tools
|
||||||
|
./hack/install.sh lint-tools
|
||||||
|
|
||||||
.PHONY: dep-ui
|
.PHONY: dep-ui
|
||||||
dep-ui: test-tools-image
|
dep-ui: test-tools-image
|
||||||
@@ -678,7 +702,6 @@ help:
|
|||||||
@echo 'debug:'
|
@echo 'debug:'
|
||||||
@echo ' list -- list all make targets'
|
@echo ' list -- list all make targets'
|
||||||
@echo ' install-tools-local -- install all the tools below'
|
@echo ' install-tools-local -- install all the tools below'
|
||||||
@echo ' install-lint-tools(-local)'
|
|
||||||
@echo
|
@echo
|
||||||
@echo 'codegen:'
|
@echo 'codegen:'
|
||||||
@echo ' codegen(-local) -- if using -local, run the following targets first'
|
@echo ' codegen(-local) -- if using -local, run the following targets first'
|
||||||
|
|||||||
7
Procfile
7
Procfile
@@ -1,6 +1,6 @@
|
|||||||
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/app-controller} HOSTNAME=testappcontroller-1 FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --server-side-diff-enabled=${ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF:-'false'}"
|
controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/app-controller} HOSTNAME=testappcontroller-1 FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-application-controller $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --commit-server localhost:${ARGOCD_E2E_COMMITSERVER_PORT:-8086} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --server-side-diff-enabled=${ARGOCD_APPLICATION_CONTROLLER_SERVER_SIDE_DIFF:-'false'} --hydrator-enabled=${ARGOCD_HYDRATOR_ENABLED:='false'}"
|
||||||
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/api-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''}"
|
api-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/api-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-server $COMMAND --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --disable-auth=${ARGOCD_E2E_DISABLE_AUTH:-'true'} --insecure --dex-server http://localhost:${ARGOCD_E2E_DEX_PORT:-5556} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081} --port ${ARGOCD_E2E_APISERVER_PORT:-8080} --otlp-address=${ARGOCD_OTLP_ADDRESS} --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --hydrator-enabled=${ARGOCD_HYDRATOR_ENABLED:='false'}"
|
||||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
|
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v3/cmd gendexcfg -o `pwd`/dist/dex.yaml && (test -f dist/dex.yaml || { echo 'Failed to generate dex configuration'; exit 1; }) && docker run --rm -p ${ARGOCD_E2E_DEX_PORT:-5556}:${ARGOCD_E2E_DEX_PORT:-5556} -v `pwd`/dist/dex.yaml:/dex.yaml ghcr.io/dexidp/dex:$(grep "image: ghcr.io/dexidp/dex" manifests/base/dex/argocd-dex-server-deployment.yaml | cut -d':' -f3) dex serve /dex.yaml"
|
||||||
redis: hack/start-redis-with-password.sh
|
redis: hack/start-redis-with-password.sh
|
||||||
repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/repo-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
repo-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/repo-server} FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-repo-server ARGOCD_GPG_ENABLED=${ARGOCD_GPG_ENABLED:-false} $COMMAND --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
||||||
cmp-server: [ "$ARGOCD_E2E_TEST" = 'true' ] && exit 0 || [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
cmp-server: [ "$ARGOCD_E2E_TEST" = 'true' ] && exit 0 || [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_BINARY_NAME=argocd-cmp-server ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-./test/cmp} $COMMAND --config-dir-path ./test/cmp --loglevel debug --otlp-address=${ARGOCD_OTLP_ADDRESS}"
|
||||||
@@ -8,6 +8,7 @@ commit-server: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go
|
|||||||
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
|
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
|
||||||
git-server: test/fixture/testrepos/start-git.sh
|
git-server: test/fixture/testrepos/start-git.sh
|
||||||
helm-registry: test/fixture/testrepos/start-helm-registry.sh
|
helm-registry: test/fixture/testrepos/start-helm-registry.sh
|
||||||
|
oci-registry: test/fixture/testrepos/start-authenticated-helm-registry.sh
|
||||||
dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
|
dev-mounter: [[ "$ARGOCD_E2E_TEST" != "true" ]] && go run hack/dev-mounter/main.go --configmap argocd-ssh-known-hosts-cm=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} --configmap argocd-tls-certs-cm=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} --configmap argocd-gpg-keys-cm=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source}
|
||||||
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/applicationset-controller} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
applicationset-controller: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/applicationset-controller} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} ARGOCD_BINARY_NAME=argocd-applicationset-controller $COMMAND --loglevel debug --metrics-addr localhost:12345 --probe-addr localhost:12346 --argocd-repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
||||||
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}"
|
notification: [ "$BIN_MODE" = 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "GOCOVERDIR=${ARGOCD_COVERAGE_DIR:-/tmp/coverage/notification} FORCE_LOG_COLORS=4 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} ARGOCD_BINARY_NAME=argocd-notifications $COMMAND --loglevel debug --application-namespaces=${ARGOCD_APPLICATION_NAMESPACES:-''} --self-service-notification-enabled=${ARGOCD_NOTIFICATION_CONTROLLER_SELF_SERVICE_NOTIFICATION_ENABLED:-'false'}"
|
||||||
@@ -3,9 +3,9 @@ header:
|
|||||||
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
|
expiration-date: '2024-10-31T00:00:00.000Z' # One year from initial release.
|
||||||
last-updated: '2023-10-27'
|
last-updated: '2023-10-27'
|
||||||
last-reviewed: '2023-10-27'
|
last-reviewed: '2023-10-27'
|
||||||
commit-hash: 74a367d10e7110209610ba3ec225539ebe5f7522
|
commit-hash: 320f46f06beaf75f9c406e3a47e2e09d36e2047a
|
||||||
project-url: https://github.com/argoproj/argo-cd
|
project-url: https://github.com/argoproj/argo-cd
|
||||||
project-release: v2.14.0
|
project-release: v3.2.0
|
||||||
changelog: https://github.com/argoproj/argo-cd/releases
|
changelog: https://github.com/argoproj/argo-cd/releases
|
||||||
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
|
license: https://github.com/argoproj/argo-cd/blob/master/LICENSE
|
||||||
project-lifecycle:
|
project-lifecycle:
|
||||||
|
|||||||
291
Tiltfile
Normal file
291
Tiltfile
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
load('ext://restart_process', 'docker_build_with_restart')
|
||||||
|
load('ext://uibutton', 'cmd_button', 'location')
|
||||||
|
|
||||||
|
# add ui button in web ui to run make codegen-local (top nav)
|
||||||
|
cmd_button(
|
||||||
|
'make codegen-local',
|
||||||
|
argv=['sh', '-c', 'make codegen-local'],
|
||||||
|
location=location.NAV,
|
||||||
|
icon_name='terminal',
|
||||||
|
text='make codegen-local',
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd_button(
|
||||||
|
'make test-local',
|
||||||
|
argv=['sh', '-c', 'make test-local'],
|
||||||
|
location=location.NAV,
|
||||||
|
icon_name='science',
|
||||||
|
text='make test-local',
|
||||||
|
)
|
||||||
|
|
||||||
|
# add ui button in web ui to run make codegen-local (top nav)
|
||||||
|
cmd_button(
|
||||||
|
'make cli-local',
|
||||||
|
argv=['sh', '-c', 'make cli-local'],
|
||||||
|
location=location.NAV,
|
||||||
|
icon_name='terminal',
|
||||||
|
text='make cli-local',
|
||||||
|
)
|
||||||
|
|
||||||
|
# detect cluster architecture for build
|
||||||
|
cluster_version = decode_yaml(local('kubectl version -o yaml'))
|
||||||
|
platform = cluster_version['serverVersion']['platform']
|
||||||
|
arch = platform.split('/')[1]
|
||||||
|
|
||||||
|
# build the argocd binary on code changes
|
||||||
|
code_deps = [
|
||||||
|
'applicationset',
|
||||||
|
'cmd',
|
||||||
|
'cmpserver',
|
||||||
|
'commitserver',
|
||||||
|
'common',
|
||||||
|
'controller',
|
||||||
|
'notification-controller',
|
||||||
|
'pkg',
|
||||||
|
'reposerver',
|
||||||
|
'server',
|
||||||
|
'util',
|
||||||
|
'go.mod',
|
||||||
|
'go.sum',
|
||||||
|
]
|
||||||
|
local_resource(
|
||||||
|
'build',
|
||||||
|
'CGO_ENABLED=0 GOOS=linux GOARCH=' + arch + ' go build -gcflags="all=-N -l" -mod=readonly -o .tilt-bin/argocd_linux cmd/main.go',
|
||||||
|
deps = code_deps,
|
||||||
|
allow_parallel=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# deploy the argocd manifests
|
||||||
|
k8s_yaml(kustomize('manifests/dev-tilt'))
|
||||||
|
|
||||||
|
# build dev image
|
||||||
|
docker_build_with_restart(
|
||||||
|
'argocd',
|
||||||
|
context='.',
|
||||||
|
dockerfile='Dockerfile.tilt',
|
||||||
|
entrypoint=[
|
||||||
|
"/usr/bin/tini",
|
||||||
|
"-s",
|
||||||
|
"--",
|
||||||
|
"dlv",
|
||||||
|
"exec",
|
||||||
|
"--continue",
|
||||||
|
"--accept-multiclient",
|
||||||
|
"--headless",
|
||||||
|
"--listen=:2345",
|
||||||
|
"--api-version=2"
|
||||||
|
],
|
||||||
|
platform=platform,
|
||||||
|
live_update=[
|
||||||
|
sync('.tilt-bin/argocd_linux', '/usr/local/bin/argocd'),
|
||||||
|
],
|
||||||
|
only=[
|
||||||
|
'.tilt-bin',
|
||||||
|
'hack',
|
||||||
|
'entrypoint.sh',
|
||||||
|
],
|
||||||
|
restart_file='/tilt/.restart-proc'
|
||||||
|
)
|
||||||
|
|
||||||
|
# build image for argocd-cli jobs
|
||||||
|
docker_build(
|
||||||
|
'argocd-job',
|
||||||
|
context='.',
|
||||||
|
dockerfile='Dockerfile.tilt',
|
||||||
|
platform=platform,
|
||||||
|
only=[
|
||||||
|
'.tilt-bin',
|
||||||
|
'hack',
|
||||||
|
'entrypoint.sh',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-server resources and port forward
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-server',
|
||||||
|
objects=[
|
||||||
|
'argocd-server:serviceaccount',
|
||||||
|
'argocd-server:role',
|
||||||
|
'argocd-server:rolebinding',
|
||||||
|
'argocd-cm:configmap',
|
||||||
|
'argocd-cmd-params-cm:configmap',
|
||||||
|
'argocd-gpg-keys-cm:configmap',
|
||||||
|
'argocd-rbac-cm:configmap',
|
||||||
|
'argocd-ssh-known-hosts-cm:configmap',
|
||||||
|
'argocd-tls-certs-cm:configmap',
|
||||||
|
'argocd-secret:secret',
|
||||||
|
'argocd-server-network-policy:networkpolicy',
|
||||||
|
'argocd-server:clusterrolebinding',
|
||||||
|
'argocd-server:clusterrole',
|
||||||
|
],
|
||||||
|
port_forwards=[
|
||||||
|
'8080:8080',
|
||||||
|
'9345:2345',
|
||||||
|
'8083:8083'
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# track crds
|
||||||
|
k8s_resource(
|
||||||
|
new_name='cluster-resources',
|
||||||
|
objects=[
|
||||||
|
'applications.argoproj.io:customresourcedefinition',
|
||||||
|
'applicationsets.argoproj.io:customresourcedefinition',
|
||||||
|
'appprojects.argoproj.io:customresourcedefinition',
|
||||||
|
'argocd:namespace'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-repo-server resources and port forward
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-repo-server',
|
||||||
|
objects=[
|
||||||
|
'argocd-repo-server:serviceaccount',
|
||||||
|
'argocd-repo-server-network-policy:networkpolicy',
|
||||||
|
],
|
||||||
|
port_forwards=[
|
||||||
|
'8081:8081',
|
||||||
|
'9346:2345',
|
||||||
|
'8084:8084'
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-redis resources and port forward
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-redis',
|
||||||
|
objects=[
|
||||||
|
'argocd-redis:serviceaccount',
|
||||||
|
'argocd-redis:role',
|
||||||
|
'argocd-redis:rolebinding',
|
||||||
|
'argocd-redis-network-policy:networkpolicy',
|
||||||
|
],
|
||||||
|
port_forwards=[
|
||||||
|
'6379:6379',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-applicationset-controller resources
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-applicationset-controller',
|
||||||
|
objects=[
|
||||||
|
'argocd-applicationset-controller:serviceaccount',
|
||||||
|
'argocd-applicationset-controller-network-policy:networkpolicy',
|
||||||
|
'argocd-applicationset-controller:role',
|
||||||
|
'argocd-applicationset-controller:rolebinding',
|
||||||
|
'argocd-applicationset-controller:clusterrolebinding',
|
||||||
|
'argocd-applicationset-controller:clusterrole',
|
||||||
|
],
|
||||||
|
port_forwards=[
|
||||||
|
'9347:2345',
|
||||||
|
'8085:8080',
|
||||||
|
'7000:7000'
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-application-controller resources
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-application-controller',
|
||||||
|
objects=[
|
||||||
|
'argocd-application-controller:serviceaccount',
|
||||||
|
'argocd-application-controller-network-policy:networkpolicy',
|
||||||
|
'argocd-application-controller:role',
|
||||||
|
'argocd-application-controller:rolebinding',
|
||||||
|
'argocd-application-controller:clusterrolebinding',
|
||||||
|
'argocd-application-controller:clusterrole',
|
||||||
|
],
|
||||||
|
port_forwards=[
|
||||||
|
'9348:2345',
|
||||||
|
'8086:8082',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-notifications-controller resources
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-notifications-controller',
|
||||||
|
objects=[
|
||||||
|
'argocd-notifications-controller:serviceaccount',
|
||||||
|
'argocd-notifications-controller-network-policy:networkpolicy',
|
||||||
|
'argocd-notifications-controller:role',
|
||||||
|
'argocd-notifications-controller:rolebinding',
|
||||||
|
'argocd-notifications-cm:configmap',
|
||||||
|
'argocd-notifications-secret:secret',
|
||||||
|
],
|
||||||
|
port_forwards=[
|
||||||
|
'9349:2345',
|
||||||
|
'8087:9001',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-dex-server resources
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-dex-server',
|
||||||
|
objects=[
|
||||||
|
'argocd-dex-server:serviceaccount',
|
||||||
|
'argocd-dex-server-network-policy:networkpolicy',
|
||||||
|
'argocd-dex-server:role',
|
||||||
|
'argocd-dex-server:rolebinding',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-commit-server resources
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-commit-server',
|
||||||
|
objects=[
|
||||||
|
'argocd-commit-server:serviceaccount',
|
||||||
|
'argocd-commit-server-network-policy:networkpolicy',
|
||||||
|
],
|
||||||
|
port_forwards=[
|
||||||
|
'9350:2345',
|
||||||
|
'8088:8087',
|
||||||
|
'8089:8086',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# docker for ui
|
||||||
|
docker_build(
|
||||||
|
'argocd-ui',
|
||||||
|
context='.',
|
||||||
|
dockerfile='Dockerfile.ui.tilt',
|
||||||
|
entrypoint=['sh', '-c', 'cd /app/ui && yarn start'],
|
||||||
|
only=['ui'],
|
||||||
|
live_update=[
|
||||||
|
sync('ui', '/app/ui'),
|
||||||
|
run('sh -c "cd /app/ui && yarn install"', trigger=['/app/ui/package.json', '/app/ui/yarn.lock']),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# track argocd-ui resources and port forward
|
||||||
|
k8s_resource(
|
||||||
|
workload='argocd-ui',
|
||||||
|
port_forwards=[
|
||||||
|
'4000:4000',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# linting
|
||||||
|
local_resource(
|
||||||
|
'lint',
|
||||||
|
'make lint-local',
|
||||||
|
deps = code_deps,
|
||||||
|
allow_parallel=True,
|
||||||
|
resource_deps=['vendor']
|
||||||
|
)
|
||||||
|
|
||||||
|
local_resource(
|
||||||
|
'lint-ui',
|
||||||
|
'make lint-ui-local',
|
||||||
|
deps = [
|
||||||
|
'ui',
|
||||||
|
],
|
||||||
|
allow_parallel=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
local_resource(
|
||||||
|
'vendor',
|
||||||
|
'go mod vendor',
|
||||||
|
deps = [
|
||||||
|
'go.mod',
|
||||||
|
'go.sum',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
38
USERS.md
38
USERS.md
@@ -5,8 +5,10 @@ PR with your organization name if you are using Argo CD.
|
|||||||
|
|
||||||
Currently, the following organizations are **officially** using Argo CD:
|
Currently, the following organizations are **officially** using Argo CD:
|
||||||
|
|
||||||
|
1. [100ms](https://www.100ms.ai/)
|
||||||
1. [127Labs](https://127labs.com/)
|
1. [127Labs](https://127labs.com/)
|
||||||
1. [3Rein](https://www.3rein.com/)
|
1. [3Rein](https://www.3rein.com/)
|
||||||
|
1. [42 School](https://42.fr/)
|
||||||
1. [4data](https://4data.ch/)
|
1. [4data](https://4data.ch/)
|
||||||
1. [7shifts](https://www.7shifts.com/)
|
1. [7shifts](https://www.7shifts.com/)
|
||||||
1. [Adevinta](https://www.adevinta.com/)
|
1. [Adevinta](https://www.adevinta.com/)
|
||||||
@@ -30,7 +32,8 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Ant Group](https://www.antgroup.com/)
|
1. [Ant Group](https://www.antgroup.com/)
|
||||||
1. [AppDirect](https://www.appdirect.com)
|
1. [AppDirect](https://www.appdirect.com)
|
||||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||||
2. [Arturia](https://www.arturia.com)
|
1. [Artemis Health by Nomi Health](https://www.artemishealth.com/)
|
||||||
|
1. [Arturia](https://www.arturia.com)
|
||||||
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
1. [ARZ Allgemeines Rechenzentrum GmbH](https://www.arz.at/)
|
||||||
1. [Augury](https://www.augury.com/)
|
1. [Augury](https://www.augury.com/)
|
||||||
1. [Autodesk](https://www.autodesk.com)
|
1. [Autodesk](https://www.autodesk.com)
|
||||||
@@ -39,12 +42,14 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Back Market](https://www.backmarket.com)
|
1. [Back Market](https://www.backmarket.com)
|
||||||
1. [Bajaj Finserv Health Ltd.](https://www.bajajfinservhealth.in)
|
1. [Bajaj Finserv Health Ltd.](https://www.bajajfinservhealth.in)
|
||||||
1. [Baloise](https://www.baloise.com)
|
1. [Baloise](https://www.baloise.com)
|
||||||
|
1. [Batumbu](https://batumbu.id)
|
||||||
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
|
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
|
||||||
1. [Beat](https://thebeat.co/en/)
|
1. [Beat](https://thebeat.co/en/)
|
||||||
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
||||||
1. [Bedag Informatik AG](https://www.bedag.ch/)
|
1. [Bedag Informatik AG](https://www.bedag.ch/)
|
||||||
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
|
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
|
||||||
1. [Believable Bots](https://believablebots.io)
|
1. [Believable Bots](https://believablebots.io)
|
||||||
|
1. [Bayer AG](https://bayer.com)
|
||||||
1. [BigPanda](https://bigpanda.io)
|
1. [BigPanda](https://bigpanda.io)
|
||||||
1. [BioBox Analytics](https://biobox.io)
|
1. [BioBox Analytics](https://biobox.io)
|
||||||
1. [BMW Group](https://www.bmwgroup.com/)
|
1. [BMW Group](https://www.bmwgroup.com/)
|
||||||
@@ -67,7 +72,9 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Chainnodes](https://chainnodes.org)
|
1. [Chainnodes](https://chainnodes.org)
|
||||||
1. [Chargetrip](https://chargetrip.com)
|
1. [Chargetrip](https://chargetrip.com)
|
||||||
1. [Chime](https://www.chime.com)
|
1. [Chime](https://www.chime.com)
|
||||||
|
1. [Chronicle Labs](https://chroniclelabs.org)
|
||||||
1. [Cisco ET&I](https://eti.cisco.com/)
|
1. [Cisco ET&I](https://eti.cisco.com/)
|
||||||
|
1. [Close](https://www.close.com/)
|
||||||
1. [Cloud Posse](https://www.cloudposse.com/)
|
1. [Cloud Posse](https://www.cloudposse.com/)
|
||||||
1. [Cloud Scale](https://cloudscaleinc.com/)
|
1. [Cloud Scale](https://cloudscaleinc.com/)
|
||||||
1. [CloudScript](https://www.cloudscript.com.br/)
|
1. [CloudScript](https://www.cloudscript.com.br/)
|
||||||
@@ -92,11 +99,15 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Daydream](https://daydream.ing)
|
1. [Daydream](https://daydream.ing)
|
||||||
1. [Deloitte](https://www.deloitte.com/)
|
1. [Deloitte](https://www.deloitte.com/)
|
||||||
1. [Deutsche Telekom AG](https://telekom.com)
|
1. [Deutsche Telekom AG](https://telekom.com)
|
||||||
|
1. [Deutsche Bank AG](https://www.deutsche-bank.de/)
|
||||||
1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/)
|
1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/)
|
||||||
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
|
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
|
||||||
|
1. [DigitalEd](https://www.digitaled.com)
|
||||||
1. [DigitalOcean](https://www.digitalocean.com)
|
1. [DigitalOcean](https://www.digitalocean.com)
|
||||||
|
1. [Divar](https://divar.ir)
|
||||||
1. [Divistant](https://divistant.com)
|
1. [Divistant](https://divistant.com)
|
||||||
1. [Dott](https://ridedott.com)
|
1. [Dott](https://ridedott.com)
|
||||||
|
1. [Doubble](https://www.doubble.app)
|
||||||
1. [Doximity](https://www.doximity.com/)
|
1. [Doximity](https://www.doximity.com/)
|
||||||
1. [EDF Renewables](https://www.edf-re.com/)
|
1. [EDF Renewables](https://www.edf-re.com/)
|
||||||
1. [edX](https://edx.org)
|
1. [edX](https://edx.org)
|
||||||
@@ -108,6 +119,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Energisme](https://energisme.com/)
|
1. [Energisme](https://energisme.com/)
|
||||||
1. [enigmo](https://enigmo.co.jp/)
|
1. [enigmo](https://enigmo.co.jp/)
|
||||||
1. [Envoy](https://envoy.com/)
|
1. [Envoy](https://envoy.com/)
|
||||||
|
1. [eSave](https://esave.es/)
|
||||||
1. [Factorial](https://factorialhr.com/)
|
1. [Factorial](https://factorialhr.com/)
|
||||||
1. [Farfetch](https://www.farfetch.com)
|
1. [Farfetch](https://www.farfetch.com)
|
||||||
1. [Faro](https://www.faro.com/)
|
1. [Faro](https://www.faro.com/)
|
||||||
@@ -146,14 +158,17 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Hazelcast](https://hazelcast.com/)
|
1. [Hazelcast](https://hazelcast.com/)
|
||||||
1. [Healy](https://www.healyworld.net)
|
1. [Healy](https://www.healyworld.net)
|
||||||
1. [Helio](https://helio.exchange)
|
1. [Helio](https://helio.exchange)
|
||||||
|
1. [hetao101](https://www.hetao101.com/)
|
||||||
1. [Hetki](https://hetki.ai)
|
1. [Hetki](https://hetki.ai)
|
||||||
1. [hipages](https://hipages.com.au/)
|
1. [hipages](https://hipages.com.au/)
|
||||||
1. [Hiya](https://hiya.com)
|
1. [Hiya](https://hiya.com)
|
||||||
1. [Honestbank](https://honestbank.com)
|
1. [Honestbank](https://honestbank.com)
|
||||||
1. [Hostinger](https://www.hostinger.com)
|
1. [Hostinger](https://www.hostinger.com)
|
||||||
|
1. [Hotjar](https://www.hotjar.com)
|
||||||
1. [IABAI](https://www.iab.ai)
|
1. [IABAI](https://www.iab.ai)
|
||||||
1. [IBM](https://www.ibm.com/)
|
1. [IBM](https://www.ibm.com/)
|
||||||
1. [Ibotta](https://home.ibotta.com)
|
1. [Ibotta](https://home.ibotta.com)
|
||||||
|
1. [Icelandair](https://www.icelandair.com)
|
||||||
1. [IFS](https://www.ifs.com)
|
1. [IFS](https://www.ifs.com)
|
||||||
1. [IITS-Consulting](https://iits-consulting.de)
|
1. [IITS-Consulting](https://iits-consulting.de)
|
||||||
1. [IllumiDesk](https://www.illumidesk.com)
|
1. [IllumiDesk](https://www.illumidesk.com)
|
||||||
@@ -163,6 +178,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Info Support](https://www.infosupport.com/)
|
1. [Info Support](https://www.infosupport.com/)
|
||||||
1. [InsideBoard](https://www.insideboard.com)
|
1. [InsideBoard](https://www.insideboard.com)
|
||||||
1. [Instruqt](https://www.instruqt.com)
|
1. [Instruqt](https://www.instruqt.com)
|
||||||
|
1. [Intel](https://www.intel.com)
|
||||||
1. [Intuit](https://www.intuit.com/)
|
1. [Intuit](https://www.intuit.com/)
|
||||||
1. [Jellysmack](https://www.jellysmack.com)
|
1. [Jellysmack](https://www.jellysmack.com)
|
||||||
1. [Joblift](https://joblift.com/)
|
1. [Joblift](https://joblift.com/)
|
||||||
@@ -191,10 +207,10 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Lian Chu Securities](https://lczq.com)
|
1. [Lian Chu Securities](https://lczq.com)
|
||||||
1. [Liatrio](https://www.liatrio.com)
|
1. [Liatrio](https://www.liatrio.com)
|
||||||
1. [Lightricks](https://www.lightricks.com/)
|
1. [Lightricks](https://www.lightricks.com/)
|
||||||
1. [LINE](https://linecorp.com/en/)
|
|
||||||
1. [Loom](https://www.loom.com/)
|
1. [Loom](https://www.loom.com/)
|
||||||
1. [Lucid Motors](https://www.lucidmotors.com/)
|
1. [Lucid Motors](https://www.lucidmotors.com/)
|
||||||
1. [Lytt](https://www.lytt.co/)
|
1. [Lytt](https://www.lytt.co/)
|
||||||
|
1. [LY Corporation](https://www.lycorp.co.jp/en/)
|
||||||
1. [Magic Leap](https://www.magicleap.com/)
|
1. [Magic Leap](https://www.magicleap.com/)
|
||||||
1. [Majid Al Futtaim](https://www.majidalfuttaim.com/)
|
1. [Majid Al Futtaim](https://www.majidalfuttaim.com/)
|
||||||
1. [Major League Baseball](https://mlb.com)
|
1. [Major League Baseball](https://mlb.com)
|
||||||
@@ -257,6 +273,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Patreon](https://www.patreon.com/)
|
1. [Patreon](https://www.patreon.com/)
|
||||||
1. [PayIt](https://payitgov.com/)
|
1. [PayIt](https://payitgov.com/)
|
||||||
1. [PayPay](https://paypay.ne.jp/)
|
1. [PayPay](https://paypay.ne.jp/)
|
||||||
|
1. [Paystack](https://paystack.com/)
|
||||||
1. [Peloton Interactive](https://www.onepeloton.com/)
|
1. [Peloton Interactive](https://www.onepeloton.com/)
|
||||||
1. [Percona](https://percona.com/)
|
1. [Percona](https://percona.com/)
|
||||||
1. [PGS](https://www.pgs.com)
|
1. [PGS](https://www.pgs.com)
|
||||||
@@ -268,6 +285,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [PITS Globale Datenrettungsdienste](https://www.pitsdatenrettung.de/)
|
1. [PITS Globale Datenrettungsdienste](https://www.pitsdatenrettung.de/)
|
||||||
1. [Platform9 Systems](https://platform9.com/)
|
1. [Platform9 Systems](https://platform9.com/)
|
||||||
1. [Polarpoint.io](https://polarpoint.io)
|
1. [Polarpoint.io](https://polarpoint.io)
|
||||||
|
1. [Pollinate](https://www.pollinate.global)
|
||||||
1. [PostFinance](https://github.com/postfinance)
|
1. [PostFinance](https://github.com/postfinance)
|
||||||
1. [Preferred Networks](https://preferred.jp/en/)
|
1. [Preferred Networks](https://preferred.jp/en/)
|
||||||
1. [Previder BV](https://previder.nl)
|
1. [Previder BV](https://previder.nl)
|
||||||
@@ -302,16 +320,20 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Saloodo! GmbH](https://www.saloodo.com)
|
1. [Saloodo! GmbH](https://www.saloodo.com)
|
||||||
1. [Sap Labs](http://sap.com)
|
1. [Sap Labs](http://sap.com)
|
||||||
1. [Sauce Labs](https://saucelabs.com/)
|
1. [Sauce Labs](https://saucelabs.com/)
|
||||||
|
1. [Schneider Electric](https://www.se.com)
|
||||||
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
||||||
1. [SCRM Lidl International Hub](https://scrm.lidl)
|
1. [SCRM Lidl International Hub](https://scrm.lidl)
|
||||||
1. [SEEK](https://seek.com.au)
|
1. [SEEK](https://seek.com.au)
|
||||||
1. [SEKAI](https://www.sekai.io/)
|
1. [SEKAI](https://www.sekai.io/)
|
||||||
1. [Semgrep](https://semgrep.com)
|
1. [Semgrep](https://semgrep.com)
|
||||||
|
1. [Seznam.cz](https://o-seznam.cz/)
|
||||||
1. [Shield](https://shield.com)
|
1. [Shield](https://shield.com)
|
||||||
|
1. [Shipfox](https://www.shipfox.io)
|
||||||
|
1. [Shock Media](https://www.shockmedia.nl)
|
||||||
1. [SI Analytics](https://si-analytics.ai)
|
1. [SI Analytics](https://si-analytics.ai)
|
||||||
1. [Sidewalk Entertainment](https://sidewalkplay.com/)
|
1. [Sidewalk Entertainment](https://sidewalkplay.com/)
|
||||||
1. [Skit](https://skit.ai/)
|
1. [Skit](https://skit.ai/)
|
||||||
1. [Skribble](https://skribble.com)
|
1. [Skribble](https://skribble.com)
|
||||||
1. [Skyscanner](https://www.skyscanner.net/)
|
1. [Skyscanner](https://www.skyscanner.net/)
|
||||||
1. [Smart Pension](https://www.smartpension.co.uk/)
|
1. [Smart Pension](https://www.smartpension.co.uk/)
|
||||||
1. [Smilee.io](https://smilee.io)
|
1. [Smilee.io](https://smilee.io)
|
||||||
@@ -320,6 +342,7 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Snapp](https://snapp.ir/)
|
1. [Snapp](https://snapp.ir/)
|
||||||
1. [Snyk](https://snyk.io/)
|
1. [Snyk](https://snyk.io/)
|
||||||
1. [Softway Medical](https://www.softwaymedical.fr/)
|
1. [Softway Medical](https://www.softwaymedical.fr/)
|
||||||
|
1. [Sophotech](https://sopho.tech)
|
||||||
1. [South China Morning Post (SCMP)](https://www.scmp.com/)
|
1. [South China Morning Post (SCMP)](https://www.scmp.com/)
|
||||||
1. [Speee](https://speee.jp/)
|
1. [Speee](https://speee.jp/)
|
||||||
1. [Spendesk](https://spendesk.com/)
|
1. [Spendesk](https://spendesk.com/)
|
||||||
@@ -332,10 +355,13 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Sumo Logic](https://sumologic.com/)
|
1. [Sumo Logic](https://sumologic.com/)
|
||||||
1. [Sutpc](http://www.sutpc.com/)
|
1. [Sutpc](http://www.sutpc.com/)
|
||||||
1. [Swiss Post](https://github.com/swisspost)
|
1. [Swiss Post](https://github.com/swisspost)
|
||||||
|
1. [Swissblock Technologies](https://swissblock.net/)
|
||||||
1. [Swisscom](https://www.swisscom.ch)
|
1. [Swisscom](https://www.swisscom.ch)
|
||||||
1. [Swissquote](https://github.com/swissquote)
|
1. [Swissquote](https://github.com/swissquote)
|
||||||
1. [Syncier](https://syncier.com/)
|
1. [Syncier](https://syncier.com/)
|
||||||
|
1. [Synergy](https://synergy.net.au)
|
||||||
1. [Syself](https://syself.com)
|
1. [Syself](https://syself.com)
|
||||||
|
1. [T-ROC Global](https://trocglobal.com/)
|
||||||
1. [TableCheck](https://tablecheck.com/)
|
1. [TableCheck](https://tablecheck.com/)
|
||||||
1. [Tailor Brands](https://www.tailorbrands.com)
|
1. [Tailor Brands](https://www.tailorbrands.com)
|
||||||
1. [Tamkeen Technologies](https://tamkeentech.sa/)
|
1. [Tamkeen Technologies](https://tamkeentech.sa/)
|
||||||
@@ -373,9 +399,11 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [Vinted](https://vinted.com/)
|
1. [Vinted](https://vinted.com/)
|
||||||
1. [Virtuo](https://www.govirtuo.com/)
|
1. [Virtuo](https://www.govirtuo.com/)
|
||||||
1. [VISITS Technologies](https://visits.world/en)
|
1. [VISITS Technologies](https://visits.world/en)
|
||||||
|
1. [Viya](https://viya.me)
|
||||||
1. [Volvo Cars](https://www.volvocars.com/)
|
1. [Volvo Cars](https://www.volvocars.com/)
|
||||||
1. [Voyager Digital](https://www.investvoyager.com/)
|
1. [Voyager Digital](https://www.investvoyager.com/)
|
||||||
1. [VSHN - The DevOps Company](https://vshn.ch/)
|
1. [VSHN - The DevOps Company](https://vshn.ch/)
|
||||||
|
1. [Wakacje.pl](https://www.wakacje.pl/)
|
||||||
1. [Walkbase](https://www.walkbase.com/)
|
1. [Walkbase](https://www.walkbase.com/)
|
||||||
1. [Webstores](https://www.webstores.nl)
|
1. [Webstores](https://www.webstores.nl)
|
||||||
1. [Wehkamp](https://www.wehkamp.nl/)
|
1. [Wehkamp](https://www.wehkamp.nl/)
|
||||||
@@ -387,10 +415,12 @@ Currently, the following organizations are **officially** using Argo CD:
|
|||||||
1. [WooliesX](https://wooliesx.com.au/)
|
1. [WooliesX](https://wooliesx.com.au/)
|
||||||
1. [Woolworths Group](https://www.woolworthsgroup.com.au/)
|
1. [Woolworths Group](https://www.woolworthsgroup.com.au/)
|
||||||
1. [WSpot](https://www.wspot.com.br/)
|
1. [WSpot](https://www.wspot.com.br/)
|
||||||
|
1. [X3M ads](https://x3mads.com)
|
||||||
1. [Yieldlab](https://www.yieldlab.de/)
|
1. [Yieldlab](https://www.yieldlab.de/)
|
||||||
1. [Youverify](https://youverify.co/)
|
1. [Youverify](https://youverify.co/)
|
||||||
1. [Yubo](https://www.yubo.live/)
|
1. [Yubo](https://www.yubo.live/)
|
||||||
|
1. [Yuno](https://y.uno/)
|
||||||
1. [ZDF](https://www.zdf.de/)
|
1. [ZDF](https://www.zdf.de/)
|
||||||
1. [Zimpler](https://www.zimpler.com/)
|
1. [Zimpler](https://www.zimpler.com/)
|
||||||
1. [ZipRecuiter](https://www.ziprecruiter.com/)
|
1. [ZipRecruiter](https://www.ziprecruiter.com/)
|
||||||
1. [ZOZO](https://corp.zozo.com/)
|
1. [ZOZO](https://corp.zozo.com/)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -14,16 +14,18 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/common"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/v3/common"
|
||||||
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// clusterSecretEventHandler is used when watching Secrets to check if they are ArgoCD Cluster Secrets, and if so
|
// clusterSecretEventHandler is used when watching Secrets to check if they are ArgoCD Cluster Secrets, and if so
|
||||||
// requeue any related ApplicationSets.
|
// requeue any related ApplicationSets.
|
||||||
type clusterSecretEventHandler struct {
|
type clusterSecretEventHandler struct {
|
||||||
// handler.EnqueueRequestForOwner
|
// handler.EnqueueRequestForOwner
|
||||||
Log log.FieldLogger
|
Log log.FieldLogger
|
||||||
Client client.Client
|
Client client.Client
|
||||||
|
ApplicationSetNamespaces []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
func (h *clusterSecretEventHandler) Create(ctx context.Context, e event.CreateEvent, q workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||||
@@ -68,6 +70,10 @@ func (h *clusterSecretEventHandler) queueRelatedAppGenerators(ctx context.Contex
|
|||||||
|
|
||||||
h.Log.WithField("count", len(appSetList.Items)).Info("listed ApplicationSets")
|
h.Log.WithField("count", len(appSetList.Items)).Info("listed ApplicationSets")
|
||||||
for _, appSet := range appSetList.Items {
|
for _, appSet := range appSetList.Items {
|
||||||
|
if !utils.IsNamespaceAllowed(h.ApplicationSetNamespaces, appSet.GetNamespace()) {
|
||||||
|
// Ignore it as not part of the allowed list of namespaces in which to watch Appsets
|
||||||
|
continue
|
||||||
|
}
|
||||||
foundClusterGenerator := false
|
foundClusterGenerator := false
|
||||||
for _, generator := range appSet.Spec.Generators {
|
for _, generator := range appSet.Spec.Generators {
|
||||||
if generator.Clusters != nil {
|
if generator.Clusters != nil {
|
||||||
|
|||||||
@@ -1,26 +1,34 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
argocommon "github.com/argoproj/argo-cd/v2/common"
|
argocommon "github.com/argoproj/argo-cd/v3/common"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
ctrl "sigs.k8s.io/controller-runtime"
|
ctrl "sigs.k8s.io/controller-runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||||
|
|
||||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type mockAddRateLimitingInterface struct {
|
||||||
|
addedItems []reconcile.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add checks the type, and adds it to the internal list of received additions
|
||||||
|
func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) {
|
||||||
|
obj.addedItems = append(obj.addedItems, item)
|
||||||
|
}
|
||||||
|
|
||||||
func TestClusterEventHandler(t *testing.T) {
|
func TestClusterEventHandler(t *testing.T) {
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
err := argov1alpha1.AddToScheme(scheme)
|
err := argov1alpha1.AddToScheme(scheme)
|
||||||
@@ -39,7 +47,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "no application sets should mean no requests",
|
name: "no application sets should mean no requests",
|
||||||
items: []argov1alpha1.ApplicationSet{},
|
items: []argov1alpha1.ApplicationSet{},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -53,7 +61,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a cluster generator should produce a request",
|
name: "a cluster generator should produce a request",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -67,7 +75,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -83,7 +91,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "multiple cluster generators should produce multiple requests",
|
name: "multiple cluster generators should produce multiple requests",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -96,7 +104,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set2",
|
Name: "my-app-set2",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -110,7 +118,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -127,9 +135,9 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "non-cluster generator should not match",
|
name: "non-cluster generator should not match",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "another-namespace",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||||
@@ -140,7 +148,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "app-set-non-cluster",
|
Name: "app-set-non-cluster",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -154,7 +162,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -163,14 +171,42 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedRequests: []reconcile.Request{
|
expectedRequests: []reconcile.Request{
|
||||||
{NamespacedName: types.NamespacedName{Namespace: "another-namespace", Name: "my-app-set"}},
|
{NamespacedName: types.NamespacedName{Namespace: "argocd", Name: "my-app-set"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "cluster generators in other namespaces should not match",
|
||||||
|
items: []argov1alpha1.ApplicationSet{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "my-app-set",
|
||||||
|
Namespace: "my-namespace-not-allowed",
|
||||||
|
},
|
||||||
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
|
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||||
|
{
|
||||||
|
Clusters: &argov1alpha1.ClusterGenerator{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
secret: corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: "argocd",
|
||||||
|
Name: "my-secret",
|
||||||
|
Labels: map[string]string{
|
||||||
|
argocommon.LabelKeySecretType: argocommon.LabelValueSecretTypeCluster,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedRequests: []reconcile.Request{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "non-argo cd secret should not match",
|
name: "non-argo cd secret should not match",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "another-namespace",
|
Namespace: "another-namespace",
|
||||||
},
|
},
|
||||||
@@ -184,7 +220,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-non-argocd-secret",
|
Name: "my-non-argocd-secret",
|
||||||
},
|
},
|
||||||
@@ -195,7 +231,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a matrix generator with a cluster generator should produce a request",
|
name: "a matrix generator with a cluster generator should produce a request",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -215,7 +251,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -231,7 +267,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a matrix generator with non cluster generator should not match",
|
name: "a matrix generator with non cluster generator should not match",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -251,7 +287,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -265,7 +301,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a matrix generator with a nested matrix generator containing a cluster generator should produce a request",
|
name: "a matrix generator with a nested matrix generator containing a cluster generator should produce a request",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -301,7 +337,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -317,7 +353,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a matrix generator with a nested matrix generator containing non cluster generator should not match",
|
name: "a matrix generator with a nested matrix generator containing non cluster generator should not match",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -352,7 +388,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -366,7 +402,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a merge generator with a cluster generator should produce a request",
|
name: "a merge generator with a cluster generator should produce a request",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -386,7 +422,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -402,7 +438,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a matrix generator with non cluster generator should not match",
|
name: "a matrix generator with non cluster generator should not match",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -422,7 +458,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -436,7 +472,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a merge generator with a nested merge generator containing a cluster generator should produce a request",
|
name: "a merge generator with a nested merge generator containing a cluster generator should produce a request",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -472,7 +508,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -488,7 +524,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
name: "a merge generator with a nested merge generator containing non cluster generator should not match",
|
name: "a merge generator with a nested merge generator containing non cluster generator should not match",
|
||||||
items: []argov1alpha1.ApplicationSet{
|
items: []argov1alpha1.ApplicationSet{
|
||||||
{
|
{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-app-set",
|
Name: "my-app-set",
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
},
|
},
|
||||||
@@ -523,7 +559,7 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: corev1.Secret{
|
secret: corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: "argocd",
|
Namespace: "argocd",
|
||||||
Name: "my-secret",
|
Name: "my-secret",
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
@@ -544,28 +580,20 @@ func TestClusterEventHandler(t *testing.T) {
|
|||||||
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithLists(&appSetList).Build()
|
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithLists(&appSetList).Build()
|
||||||
|
|
||||||
handler := &clusterSecretEventHandler{
|
handler := &clusterSecretEventHandler{
|
||||||
Client: fakeClient,
|
Client: fakeClient,
|
||||||
Log: log.WithField("type", "createSecretEventHandler"),
|
Log: log.WithField("type", "createSecretEventHandler"),
|
||||||
|
ApplicationSetNamespaces: []string{"argocd"},
|
||||||
}
|
}
|
||||||
|
|
||||||
mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
|
mockAddRateLimitingInterface := mockAddRateLimitingInterface{}
|
||||||
|
|
||||||
handler.queueRelatedAppGenerators(context.Background(), &mockAddRateLimitingInterface, &test.secret)
|
handler.queueRelatedAppGenerators(t.Context(), &mockAddRateLimitingInterface, &test.secret)
|
||||||
|
|
||||||
assert.ElementsMatch(t, mockAddRateLimitingInterface.addedItems, test.expectedRequests)
|
assert.ElementsMatch(t, mockAddRateLimitingInterface.addedItems, test.expectedRequests)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add checks the type, and adds it to the internal list of received additions
|
|
||||||
func (obj *mockAddRateLimitingInterface) Add(item reconcile.Request) {
|
|
||||||
obj.addedItems = append(obj.addedItems, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockAddRateLimitingInterface struct {
|
|
||||||
addedItems []reconcile.Request
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) {
|
func TestNestedGeneratorHasClusterGenerator_NestedClusterGenerator(t *testing.T) {
|
||||||
nested := argov1alpha1.ApplicationSetNestedGenerator{
|
nested := argov1alpha1.ApplicationSetNestedGenerator{
|
||||||
Clusters: &argov1alpha1.ClusterGenerator{},
|
Clusters: &argov1alpha1.ClusterGenerator{},
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -16,15 +15,15 @@ import (
|
|||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
"github.com/argoproj/argo-cd/v3/applicationset/generators"
|
||||||
appsetmetrics "github.com/argoproj/argo-cd/v2/applicationset/metrics"
|
appsetmetrics "github.com/argoproj/argo-cd/v3/applicationset/metrics"
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
||||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRequeueAfter(t *testing.T) {
|
func TestRequeueAfter(t *testing.T) {
|
||||||
mockServer := &mocks.Repos{}
|
mockServer := &mocks.Repos{}
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
err := argov1alpha1.AddToScheme(scheme)
|
err := argov1alpha1.AddToScheme(scheme)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -36,20 +35,20 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
appClientset := kubefake.NewSimpleClientset()
|
appClientset := kubefake.NewSimpleClientset()
|
||||||
k8sClient := fake.NewClientBuilder().Build()
|
k8sClient := fake.NewClientBuilder().Build()
|
||||||
duckType := &unstructured.Unstructured{
|
duckType := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]any{
|
||||||
"apiVersion": "v2quack",
|
"apiVersion": "v2quack",
|
||||||
"kind": "Duck",
|
"kind": "Duck",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"name": "mightyduck",
|
"name": "mightyduck",
|
||||||
"namespace": "namespace",
|
"namespace": "namespace",
|
||||||
"labels": map[string]interface{}{"duck": "all-species"},
|
"labels": map[string]any{"duck": "all-species"},
|
||||||
},
|
},
|
||||||
"status": map[string]interface{}{
|
"status": map[string]any{
|
||||||
"decisions": []interface{}{
|
"decisions": []any{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"clusterName": "staging-01",
|
"clusterName": "staging-01",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"clusterName": "production-01",
|
"clusterName": "production-01",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -57,10 +56,10 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType)
|
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, duckType)
|
||||||
scmConfig := generators.NewSCMConfig("", []string{""}, true, nil, true)
|
scmConfig := generators.NewSCMConfig("", []string{""}, true, true, nil, true)
|
||||||
terminalGenerators := map[string]generators.Generator{
|
terminalGenerators := map[string]generators.Generator{
|
||||||
"List": generators.NewListGenerator(),
|
"List": generators.NewListGenerator(),
|
||||||
"Clusters": generators.NewClusterGenerator(k8sClient, ctx, appClientset, "argocd"),
|
"Clusters": generators.NewClusterGenerator(ctx, k8sClient, appClientset, "argocd"),
|
||||||
"Git": generators.NewGitGenerator(mockServer, "namespace"),
|
"Git": generators.NewGitGenerator(mockServer, "namespace"),
|
||||||
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), scmConfig),
|
"SCMProvider": generators.NewSCMProviderGenerator(fake.NewClientBuilder().WithObjects(&corev1.Secret{}).Build(), scmConfig),
|
||||||
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
|
"ClusterDecisionResource": generators.NewDuckTypeGenerator(ctx, fakeDynClient, appClientset, "argocd"),
|
||||||
@@ -90,7 +89,7 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client := fake.NewClientBuilder().WithScheme(scheme).Build()
|
client := fake.NewClientBuilder().WithScheme(scheme).Build()
|
||||||
metrics := appsetmetrics.NewFakeAppsetMetrics(client)
|
metrics := appsetmetrics.NewFakeAppsetMetrics()
|
||||||
r := ApplicationSetReconciler{
|
r := ApplicationSetReconciler{
|
||||||
Client: client,
|
Client: client,
|
||||||
Scheme: scheme,
|
Scheme: scheme,
|
||||||
@@ -130,7 +129,7 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, ""}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
}, ""}, want: generators.DefaultRequeueAfter, wantErr: assert.NoError},
|
||||||
{name: "ClusterMatrixNested", args: args{&argov1alpha1.ApplicationSet{
|
{name: "ClusterMatrixNested", args: args{&argov1alpha1.ApplicationSet{
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{
|
Generators: []argov1alpha1.ApplicationSetGenerator{
|
||||||
@@ -145,7 +144,7 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, ""}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
}, ""}, want: generators.DefaultRequeueAfter, wantErr: assert.NoError},
|
||||||
{name: "ListGenerator", args: args{appset: &argov1alpha1.ApplicationSet{
|
{name: "ListGenerator", args: args{appset: &argov1alpha1.ApplicationSet{
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{{List: &argov1alpha1.ListGenerator{}}},
|
Generators: []argov1alpha1.ApplicationSetGenerator{{List: &argov1alpha1.ListGenerator{}}},
|
||||||
@@ -155,7 +154,7 @@ func TestRequeueAfter(t *testing.T) {
|
|||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
Generators: []argov1alpha1.ApplicationSetGenerator{{ClusterDecisionResource: &argov1alpha1.DuckTypeGenerator{}}},
|
Generators: []argov1alpha1.ApplicationSetGenerator{{ClusterDecisionResource: &argov1alpha1.DuckTypeGenerator{}}},
|
||||||
},
|
},
|
||||||
}}, want: generators.DefaultRequeueAfterSeconds, wantErr: assert.NoError},
|
}}, want: generators.DefaultRequeueAfter, wantErr: assert.NoError},
|
||||||
{name: "OverrideRequeueDuck", args: args{
|
{name: "OverrideRequeueDuck", args: args{
|
||||||
appset: &argov1alpha1.ApplicationSet{
|
appset: &argov1alpha1.ApplicationSet{
|
||||||
Spec: argov1alpha1.ApplicationSetSpec{
|
Spec: argov1alpha1.ApplicationSetSpec{
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func applyTemplatePatch(app *appv1.Application, templatePatch string) (*appv1.Application, error) {
|
func applyTemplatePatch(app *appv1.Application, templatePatch string) (*appv1.Application, error) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
appv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_ApplyTemplatePatch(t *testing.T) {
|
func Test_ApplyTemplatePatch(t *testing.T) {
|
||||||
@@ -27,7 +27,7 @@ func Test_ApplyTemplatePatch(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-cluster-guestbook",
|
Name: "my-cluster-guestbook",
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
Finalizers: []string{appv1.ResourcesFinalizerName},
|
||||||
},
|
},
|
||||||
Spec: appv1.ApplicationSpec{
|
Spec: appv1.ApplicationSpec{
|
||||||
Project: "default",
|
Project: "default",
|
||||||
@@ -72,7 +72,7 @@ func Test_ApplyTemplatePatch(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-cluster-guestbook",
|
Name: "my-cluster-guestbook",
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
Finalizers: []string{appv1.ResourcesFinalizerName},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"annotation-some-key": "annotation-some-value",
|
"annotation-some-key": "annotation-some-value",
|
||||||
},
|
},
|
||||||
@@ -112,7 +112,7 @@ func Test_ApplyTemplatePatch(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-cluster-guestbook",
|
Name: "my-cluster-guestbook",
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
Finalizers: []string{appv1.ResourcesFinalizerName},
|
||||||
},
|
},
|
||||||
Spec: appv1.ApplicationSpec{
|
Spec: appv1.ApplicationSpec{
|
||||||
Project: "default",
|
Project: "default",
|
||||||
@@ -148,7 +148,7 @@ spec:
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "my-cluster-guestbook",
|
Name: "my-cluster-guestbook",
|
||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
Finalizers: []string{"resources-finalizer.argocd.argoproj.io"},
|
Finalizers: []string{appv1.ResourcesFinalizerName},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
"annotation-some-key": "annotation-some-value",
|
"annotation-some-key": "annotation-some-value",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
"github.com/argoproj/argo-cd/v3/applicationset/generators"
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
|
|
||||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.ApplicationSet, g map[string]generators.Generator, renderer utils.Renderer, client client.Client) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) {
|
func GenerateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.ApplicationSet, g map[string]generators.Generator, renderer utils.Renderer, client client.Client) ([]argov1alpha1.Application, argov1alpha1.ApplicationSetReasonType, error) {
|
||||||
@@ -20,7 +20,7 @@ func GenerateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.App
|
|||||||
var applicationSetReason argov1alpha1.ApplicationSetReasonType
|
var applicationSetReason argov1alpha1.ApplicationSetReasonType
|
||||||
|
|
||||||
for _, requestedGenerator := range applicationSetInfo.Spec.Generators {
|
for _, requestedGenerator := range applicationSetInfo.Spec.Generators {
|
||||||
t, err := generators.Transform(requestedGenerator, g, applicationSetInfo.Spec.Template, &applicationSetInfo, map[string]interface{}{}, client)
|
t, err := generators.Transform(requestedGenerator, g, applicationSetInfo.Spec.Template, &applicationSetInfo, map[string]any{}, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logCtx.WithError(err).WithField("generator", requestedGenerator).
|
logCtx.WithError(err).WithField("generator", requestedGenerator).
|
||||||
Error("error generating application from params")
|
Error("error generating application from params")
|
||||||
@@ -79,7 +79,7 @@ func GenerateApplications(logCtx *log.Entry, applicationSetInfo argov1alpha1.App
|
|||||||
return res, applicationSetReason, firstError
|
return res, applicationSetReason, firstError
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderTemplatePatch(r utils.Renderer, app *argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet, params map[string]interface{}) (*argov1alpha1.Application, error) {
|
func renderTemplatePatch(r utils.Renderer, app *argov1alpha1.Application, applicationSetInfo argov1alpha1.ApplicationSet, params map[string]any) (*argov1alpha1.Application, error) {
|
||||||
replacedTemplate, err := r.Replace(*applicationSetInfo.Spec.TemplatePatch, params, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
replacedTemplate, err := r.Replace(*applicationSetInfo.Spec.TemplatePatch, params, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error replacing values in templatePatch: %w", err)
|
return nil, fmt.Errorf("error replacing values in templatePatch: %w", err)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package template
|
package template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"maps"
|
"maps"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -13,12 +13,12 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/generators"
|
"github.com/argoproj/argo-cd/v3/applicationset/generators"
|
||||||
genmock "github.com/argoproj/argo-cd/v2/applicationset/generators/mocks"
|
genmock "github.com/argoproj/argo-cd/v3/applicationset/generators/mocks"
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
rendmock "github.com/argoproj/argo-cd/v2/applicationset/utils/mocks"
|
rendmock "github.com/argoproj/argo-cd/v3/applicationset/utils/mocks"
|
||||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
"github.com/argoproj/argo-cd/v3/pkg/apis/application"
|
||||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateApplications(t *testing.T) {
|
func TestGenerateApplications(t *testing.T) {
|
||||||
@@ -31,7 +31,7 @@ func TestGenerateApplications(t *testing.T) {
|
|||||||
|
|
||||||
for _, c := range []struct {
|
for _, c := range []struct {
|
||||||
name string
|
name string
|
||||||
params []map[string]interface{}
|
params []map[string]any
|
||||||
template v1alpha1.ApplicationSetTemplate
|
template v1alpha1.ApplicationSetTemplate
|
||||||
generateParamsError error
|
generateParamsError error
|
||||||
rendererError error
|
rendererError error
|
||||||
@@ -40,7 +40,7 @@ func TestGenerateApplications(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Generate two applications",
|
name: "Generate two applications",
|
||||||
params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}},
|
params: []map[string]any{{"name": "app1"}, {"name": "app2"}},
|
||||||
template: v1alpha1.ApplicationSetTemplate{
|
template: v1alpha1.ApplicationSetTemplate{
|
||||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
@@ -53,13 +53,13 @@ func TestGenerateApplications(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Handles error from the generator",
|
name: "Handles error from the generator",
|
||||||
generateParamsError: fmt.Errorf("error"),
|
generateParamsError: errors.New("error"),
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
expectedReason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError,
|
expectedReason: v1alpha1.ApplicationSetReasonApplicationParamsGenerationError,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Handles error from the render",
|
name: "Handles error from the render",
|
||||||
params: []map[string]interface{}{{"name": "app1"}, {"name": "app2"}},
|
params: []map[string]any{{"name": "app1"}, {"name": "app2"}},
|
||||||
template: v1alpha1.ApplicationSetTemplate{
|
template: v1alpha1.ApplicationSetTemplate{
|
||||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
@@ -68,7 +68,7 @@ func TestGenerateApplications(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Spec: v1alpha1.ApplicationSpec{},
|
Spec: v1alpha1.ApplicationSpec{},
|
||||||
},
|
},
|
||||||
rendererError: fmt.Errorf("error"),
|
rendererError: errors.New("error"),
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
expectedReason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError,
|
expectedReason: v1alpha1.ApplicationSetReasonRenderTemplateParamsError,
|
||||||
},
|
},
|
||||||
@@ -153,7 +153,7 @@ func TestGenerateApplications(t *testing.T) {
|
|||||||
func TestMergeTemplateApplications(t *testing.T) {
|
func TestMergeTemplateApplications(t *testing.T) {
|
||||||
for _, c := range []struct {
|
for _, c := range []struct {
|
||||||
name string
|
name string
|
||||||
params []map[string]interface{}
|
params []map[string]any
|
||||||
template v1alpha1.ApplicationSetTemplate
|
template v1alpha1.ApplicationSetTemplate
|
||||||
overrideTemplate v1alpha1.ApplicationSetTemplate
|
overrideTemplate v1alpha1.ApplicationSetTemplate
|
||||||
expectedMerged v1alpha1.ApplicationSetTemplate
|
expectedMerged v1alpha1.ApplicationSetTemplate
|
||||||
@@ -161,7 +161,7 @@ func TestMergeTemplateApplications(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Generate app",
|
name: "Generate app",
|
||||||
params: []map[string]interface{}{{"name": "app1"}},
|
params: []map[string]any{{"name": "app1"}},
|
||||||
template: v1alpha1.ApplicationSetTemplate{
|
template: v1alpha1.ApplicationSetTemplate{
|
||||||
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
ApplicationSetTemplateMeta: v1alpha1.ApplicationSetTemplateMeta{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
@@ -245,13 +245,13 @@ func TestMergeTemplateApplications(t *testing.T) {
|
|||||||
func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
||||||
for _, cases := range []struct {
|
for _, cases := range []struct {
|
||||||
name string
|
name string
|
||||||
params []map[string]interface{}
|
params []map[string]any
|
||||||
template v1alpha1.ApplicationSetTemplate
|
template v1alpha1.ApplicationSetTemplate
|
||||||
expectedApp []v1alpha1.Application
|
expectedApp []v1alpha1.Application
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Generate an application from a go template application set manifest using a pull request generator",
|
name: "Generate an application from a go template application set manifest using a pull request generator",
|
||||||
params: []map[string]interface{}{
|
params: []map[string]any{
|
||||||
{
|
{
|
||||||
"number": "1",
|
"number": "1",
|
||||||
"title": "title1",
|
"title": "title1",
|
||||||
@@ -341,10 +341,10 @@ func TestGenerateAppsUsingPullRequestGenerator(t *testing.T) {
|
|||||||
renderer,
|
renderer,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
assert.EqualValues(t, cases.expectedApp[0].ObjectMeta.Name, gotApp[0].ObjectMeta.Name)
|
assert.Equal(t, cases.expectedApp[0].Name, gotApp[0].Name)
|
||||||
assert.EqualValues(t, cases.expectedApp[0].Spec.Source.TargetRevision, gotApp[0].Spec.Source.TargetRevision)
|
assert.Equal(t, cases.expectedApp[0].Spec.Source.TargetRevision, gotApp[0].Spec.Source.TargetRevision)
|
||||||
assert.EqualValues(t, cases.expectedApp[0].Spec.Destination.Namespace, gotApp[0].Spec.Destination.Namespace)
|
assert.Equal(t, cases.expectedApp[0].Spec.Destination.Namespace, gotApp[0].Spec.Destination.Namespace)
|
||||||
assert.True(t, maps.Equal(cases.expectedApp[0].ObjectMeta.Labels, gotApp[0].ObjectMeta.Labels))
|
assert.True(t, maps.Equal(cases.expectedApp[0].Labels, gotApp[0].Labels))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
replicaCount: 1
|
replicaCount: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
repository: gcr.io/heptio-images/ks-guestbook-demo
|
repository: quay.io/argoprojlabs/argocd-e2e-container
|
||||||
tag: 0.1
|
tag: 0.1
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: ApplicationSet
|
||||||
|
metadata:
|
||||||
|
name: guestbook
|
||||||
|
spec:
|
||||||
|
generators:
|
||||||
|
- git:
|
||||||
|
repoURL: https://github.com/argoproj/argo-cd.git
|
||||||
|
revision: HEAD
|
||||||
|
files:
|
||||||
|
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json"
|
||||||
|
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/*/dev/config.json"
|
||||||
|
exclude: true
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: '{{cluster.name}}-guestbook'
|
||||||
|
spec:
|
||||||
|
project: default
|
||||||
|
source:
|
||||||
|
repoURL: https://github.com/argoproj/argo-cd.git
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: "applicationset/examples/git-generator-files-discovery/apps/guestbook"
|
||||||
|
destination:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
#server: '{{cluster.address}}'
|
||||||
|
namespace: guestbook
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
apiVersion: argoproj.io/v1alpha1
|
||||||
|
kind: ApplicationSet
|
||||||
|
metadata:
|
||||||
|
name: guestbook
|
||||||
|
spec:
|
||||||
|
goTemplate: true
|
||||||
|
goTemplateOptions: ["missingkey=error"]
|
||||||
|
generators:
|
||||||
|
- git:
|
||||||
|
repoURL: https://github.com/argoproj/argo-cd.git
|
||||||
|
revision: HEAD
|
||||||
|
files:
|
||||||
|
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/**/config.json"
|
||||||
|
- path: "applicationset/examples/git-generator-files-discovery/cluster-config/*/dev/config.json"
|
||||||
|
exclude: true
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
name: '{{.cluster.name}}-guestbook'
|
||||||
|
spec:
|
||||||
|
project: default
|
||||||
|
source:
|
||||||
|
repoURL: https://github.com/argoproj/argo-cd.git
|
||||||
|
targetRevision: HEAD
|
||||||
|
path: "applicationset/examples/git-generator-files-discovery/apps/guestbook"
|
||||||
|
destination:
|
||||||
|
server: https://kubernetes.default.svc
|
||||||
|
namespace: guestbook
|
||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ spec:
|
|||||||
app: guestbook-ui
|
app: guestbook-ui
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: gcr.io/heptio-images/ks-guestbook-demo:0.2
|
- image: quay.io/argoprojlabs/argocd-e2e-container:0.2
|
||||||
name: guestbook-ui
|
name: guestbook-ui
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
|
|||||||
@@ -7,16 +7,14 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
"github.com/argoproj/argo-cd/v2/common"
|
"github.com/argoproj/argo-cd/v3/common"
|
||||||
argoappsetv1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoappsetv1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*ClusterGenerator)(nil)
|
var _ Generator = (*ClusterGenerator)(nil)
|
||||||
@@ -27,21 +25,17 @@ type ClusterGenerator struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
clientset kubernetes.Interface
|
clientset kubernetes.Interface
|
||||||
// namespace is the Argo CD namespace
|
// namespace is the Argo CD namespace
|
||||||
namespace string
|
namespace string
|
||||||
settingsManager *settings.SettingsManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var render = &utils.Render{}
|
var render = &utils.Render{}
|
||||||
|
|
||||||
func NewClusterGenerator(c client.Client, ctx context.Context, clientset kubernetes.Interface, namespace string) Generator {
|
func NewClusterGenerator(ctx context.Context, c client.Client, clientset kubernetes.Interface, namespace string) Generator {
|
||||||
settingsManager := settings.NewSettingsManager(ctx, clientset, namespace)
|
|
||||||
|
|
||||||
g := &ClusterGenerator{
|
g := &ClusterGenerator{
|
||||||
Client: c,
|
Client: c,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
clientset: clientset,
|
clientset: clientset,
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
settingsManager: settingsManager,
|
|
||||||
}
|
}
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
@@ -56,21 +50,21 @@ func (g *ClusterGenerator) GetTemplate(appSetGenerator *argoappsetv1alpha1.Appli
|
|||||||
return &appSetGenerator.Clusters.Template
|
return &appSetGenerator.Clusters.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator, appSet *argoappsetv1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
logCtx := log.WithField("applicationset", appSet.GetName()).WithField("namespace", appSet.GetNamespace())
|
logCtx := log.WithField("applicationset", appSet.GetName()).WithField("namespace", appSet.GetNamespace())
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.Clusters == nil {
|
if appSetGenerator.Clusters == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not include the local cluster in the cluster parameters IF there is a non-empty selector
|
// Do not include the local cluster in the cluster parameters IF there is a non-empty selector
|
||||||
// - Since local clusters do not have secrets, they do not have labels to match against
|
// - Since local clusters do not have secrets, they do not have labels to match against
|
||||||
ignoreLocalClusters := len(appSetGenerator.Clusters.Selector.MatchExpressions) > 0 || len(appSetGenerator.Clusters.Selector.MatchLabels) > 0
|
ignoreLocalClusters := len(appSetGenerator.Clusters.Selector.MatchExpressions) > 0 || len(appSetGenerator.Clusters.Selector.MatchLabels) > 0
|
||||||
|
|
||||||
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
|
// ListCluster will include the local cluster in the list of clusters
|
||||||
clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
|
clustersFromArgoCD, err := utils.ListClusters(g.ctx, g.clientset, g.namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error listing clusters: %w", err)
|
return nil, fmt.Errorf("error listing clusters: %w", err)
|
||||||
@@ -85,22 +79,18 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
|||||||
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
|
return nil, fmt.Errorf("error getting cluster secrets: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []map[string]interface{}{}
|
paramHolder := ¶mHolder{isFlatMode: appSetGenerator.Clusters.FlatList}
|
||||||
|
logCtx.Debugf("Using flat mode = %t for cluster generator", paramHolder.isFlatMode)
|
||||||
|
|
||||||
secretsFound := []corev1.Secret{}
|
secretsFound := []corev1.Secret{}
|
||||||
|
for _, cluster := range clustersFromArgoCD {
|
||||||
isFlatMode := appSetGenerator.Clusters.FlatList
|
|
||||||
logCtx.Debugf("Using flat mode = %t for cluster generator", isFlatMode)
|
|
||||||
clustersParams := make([]map[string]interface{}, 0)
|
|
||||||
|
|
||||||
for _, cluster := range clustersFromArgoCD.Items {
|
|
||||||
// If there is a secret for this cluster, then it's a non-local cluster, so it will be
|
// If there is a secret for this cluster, then it's a non-local cluster, so it will be
|
||||||
// handled by the next step.
|
// handled by the next step.
|
||||||
if secretForCluster, exists := clusterSecrets[cluster.Name]; exists {
|
if secretForCluster, exists := clusterSecrets[cluster.Name]; exists {
|
||||||
secretsFound = append(secretsFound, secretForCluster)
|
secretsFound = append(secretsFound, secretForCluster)
|
||||||
} else if !ignoreLocalClusters {
|
} else if !ignoreLocalClusters {
|
||||||
// If there is no secret for the cluster, it's the local cluster, so handle it here.
|
// If there is no secret for the cluster, it's the local cluster, so handle it here.
|
||||||
params := map[string]interface{}{}
|
params := map[string]any{}
|
||||||
params["name"] = cluster.Name
|
params["name"] = cluster.Name
|
||||||
params["nameNormalized"] = cluster.Name
|
params["nameNormalized"] = cluster.Name
|
||||||
params["server"] = cluster.Server
|
params["server"] = cluster.Server
|
||||||
@@ -111,72 +101,80 @@ func (g *ClusterGenerator) GenerateParams(appSetGenerator *argoappsetv1alpha1.Ap
|
|||||||
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
|
return nil, fmt.Errorf("error appending templated values for local cluster: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFlatMode {
|
paramHolder.append(params)
|
||||||
clustersParams = append(clustersParams, params)
|
|
||||||
} else {
|
|
||||||
res = append(res, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
|
logCtx.WithField("cluster", "local cluster").Info("matched local cluster")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each matching cluster secret (non-local clusters only)
|
// For each matching cluster secret (non-local clusters only)
|
||||||
for _, cluster := range secretsFound {
|
for _, cluster := range secretsFound {
|
||||||
params := map[string]interface{}{}
|
params := g.getClusterParameters(cluster, appSet)
|
||||||
|
|
||||||
params["name"] = string(cluster.Data["name"])
|
|
||||||
params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
|
|
||||||
params["server"] = string(cluster.Data["server"])
|
|
||||||
|
|
||||||
project, ok := cluster.Data["project"]
|
|
||||||
if ok {
|
|
||||||
params["project"] = string(project)
|
|
||||||
} else {
|
|
||||||
params["project"] = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if appSet.Spec.GoTemplate {
|
|
||||||
meta := map[string]interface{}{}
|
|
||||||
|
|
||||||
if len(cluster.ObjectMeta.Annotations) > 0 {
|
|
||||||
meta["annotations"] = cluster.ObjectMeta.Annotations
|
|
||||||
}
|
|
||||||
if len(cluster.ObjectMeta.Labels) > 0 {
|
|
||||||
meta["labels"] = cluster.ObjectMeta.Labels
|
|
||||||
}
|
|
||||||
|
|
||||||
params["metadata"] = meta
|
|
||||||
} else {
|
|
||||||
for key, value := range cluster.ObjectMeta.Annotations {
|
|
||||||
params[fmt.Sprintf("metadata.annotations.%s", key)] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range cluster.ObjectMeta.Labels {
|
|
||||||
params[fmt.Sprintf("metadata.labels.%s", key)] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
err = appendTemplatedValues(appSetGenerator.Clusters.Values, params, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error appending templated values for cluster: %w", err)
|
return nil, fmt.Errorf("error appending templated values for cluster: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFlatMode {
|
paramHolder.append(params)
|
||||||
clustersParams = append(clustersParams, params)
|
|
||||||
} else {
|
|
||||||
res = append(res, params)
|
|
||||||
}
|
|
||||||
|
|
||||||
logCtx.WithField("cluster", cluster.Name).Debug("matched cluster secret")
|
logCtx.WithField("cluster", cluster.Name).Debug("matched cluster secret")
|
||||||
}
|
}
|
||||||
|
|
||||||
if isFlatMode {
|
return paramHolder.consolidate(), nil
|
||||||
res = append(res, map[string]interface{}{
|
}
|
||||||
"clusters": clustersParams,
|
|
||||||
})
|
type paramHolder struct {
|
||||||
|
isFlatMode bool
|
||||||
|
params []map[string]any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *paramHolder) append(params map[string]any) {
|
||||||
|
p.params = append(p.params, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *paramHolder) consolidate() []map[string]any {
|
||||||
|
if p.isFlatMode {
|
||||||
|
p.params = []map[string]any{
|
||||||
|
{"clusters": p.params},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res, nil
|
return p.params
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *ClusterGenerator) getClusterParameters(cluster corev1.Secret, appSet *argoappsetv1alpha1.ApplicationSet) map[string]any {
|
||||||
|
params := map[string]any{}
|
||||||
|
|
||||||
|
params["name"] = string(cluster.Data["name"])
|
||||||
|
params["nameNormalized"] = utils.SanitizeName(string(cluster.Data["name"]))
|
||||||
|
params["server"] = string(cluster.Data["server"])
|
||||||
|
|
||||||
|
project, ok := cluster.Data["project"]
|
||||||
|
if ok {
|
||||||
|
params["project"] = string(project)
|
||||||
|
} else {
|
||||||
|
params["project"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if appSet.Spec.GoTemplate {
|
||||||
|
meta := map[string]any{}
|
||||||
|
|
||||||
|
if len(cluster.Annotations) > 0 {
|
||||||
|
meta["annotations"] = cluster.Annotations
|
||||||
|
}
|
||||||
|
if len(cluster.Labels) > 0 {
|
||||||
|
meta["labels"] = cluster.Labels
|
||||||
|
}
|
||||||
|
|
||||||
|
params["metadata"] = meta
|
||||||
|
} else {
|
||||||
|
for key, value := range cluster.Annotations {
|
||||||
|
params["metadata.annotations."+key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range cluster.Labels {
|
||||||
|
params["metadata.labels."+key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
|
func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerator *argoappsetv1alpha1.ApplicationSetGenerator) (map[string]corev1.Secret, error) {
|
||||||
@@ -188,7 +186,7 @@ func (g *ClusterGenerator) getSecretsByClusterName(log *log.Entry, appSetGenerat
|
|||||||
return nil, fmt.Errorf("error converting label selector: %w", err)
|
return nil, fmt.Errorf("error converting label selector: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := g.Client.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
|
if err := g.List(context.Background(), clusterSecretList, client.MatchingLabelsSelector{Selector: secretSelector}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("clusters matching labels: %d", len(clusterSecretList.Items))
|
log.Debugf("clusters matching labels: %d", len(clusterSecretList.Items))
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package generators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
|
|
||||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -27,7 +27,7 @@ type possiblyErroringFakeCtrlRuntimeClient struct {
|
|||||||
|
|
||||||
func (p *possiblyErroringFakeCtrlRuntimeClient) List(ctx context.Context, secretList client.ObjectList, opts ...client.ListOption) error {
|
func (p *possiblyErroringFakeCtrlRuntimeClient) List(ctx context.Context, secretList client.ObjectList, opts ...client.ListOption) error {
|
||||||
if p.shouldError {
|
if p.shouldError {
|
||||||
return fmt.Errorf("could not list Secrets")
|
return errors.New("could not list Secrets")
|
||||||
}
|
}
|
||||||
return p.Client.List(ctx, secretList, opts...)
|
return p.Client.List(ctx, secretList, opts...)
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
selector metav1.LabelSelector
|
selector metav1.LabelSelector
|
||||||
isFlatMode bool
|
isFlatMode bool
|
||||||
values map[string]string
|
values map[string]string
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
// clientError is true if a k8s client error should be simulated
|
// clientError is true if a k8s client error should be simulated
|
||||||
clientError bool
|
clientError bool
|
||||||
expectedError error
|
expectedError error
|
||||||
@@ -106,7 +106,7 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
"bat": "{{ metadata.labels.environment }}",
|
"bat": "{{ metadata.labels.environment }}",
|
||||||
"aaa": "{{ server }}",
|
"aaa": "{{ server }}",
|
||||||
"no-op": "{{ this-does-not-exist }}",
|
"no-op": "{{ this-does-not-exist }}",
|
||||||
}, expected: []map[string]interface{}{
|
}, expected: []map[string]any{
|
||||||
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc", "project": ""},
|
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc", "project": ""},
|
||||||
{
|
{
|
||||||
"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||||
@@ -129,7 +129,7 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
"name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||||
@@ -153,7 +153,7 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||||
@@ -179,7 +179,7 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"values.foo": "bar", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
"values.foo": "bar", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
||||||
@@ -212,7 +212,7 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"name": "baz",
|
"name": "baz",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"values.name": "baz", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
"values.name": "baz", "name": "staging-01", "nameNormalized": "staging-01", "server": "https://staging-01.example.com", "metadata.labels.environment": "staging", "metadata.labels.org": "foo",
|
||||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "staging", "project": "",
|
||||||
@@ -227,7 +227,7 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
values: nil,
|
values: nil,
|
||||||
expected: nil,
|
expected: nil,
|
||||||
clientError: true,
|
clientError: true,
|
||||||
expectedError: fmt.Errorf("error getting cluster secrets: could not list Secrets"),
|
expectedError: errors.New("error getting cluster secrets: could not list Secrets"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "flat mode without selectors",
|
name: "flat mode without selectors",
|
||||||
@@ -242,9 +242,9 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
"aaa": "{{ server }}",
|
"aaa": "{{ server }}",
|
||||||
"no-op": "{{ this-does-not-exist }}",
|
"no-op": "{{ this-does-not-exist }}",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"clusters": []map[string]interface{}{
|
"clusters": []map[string]any{
|
||||||
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc", "project": ""},
|
{"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "{{ metadata.annotations.foo.argoproj.io }}", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "{{ metadata.labels.environment }}", "values.aaa": "https://kubernetes.default.svc", "nameNormalized": "in-cluster", "name": "in-cluster", "server": "https://kubernetes.default.svc", "project": ""},
|
||||||
{
|
{
|
||||||
"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
"values.lol1": "lol", "values.lol2": "{{values.lol1}}{{values.lol1}}", "values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}", "values.foo": "bar", "values.bar": "production", "values.no-op": "{{ this-does-not-exist }}", "values.bat": "production", "values.aaa": "https://production-01.example.com", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||||
@@ -280,9 +280,9 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"clusters": []map[string]interface{}{
|
"clusters": []map[string]any{
|
||||||
{
|
{
|
||||||
"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
"values.foo": "bar", "name": "production_01/west", "nameNormalized": "production-01-west", "server": "https://production-01.example.com", "metadata.labels.environment": "production", "metadata.labels.org": "bar",
|
||||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
"metadata.labels.argocd.argoproj.io/secret-type": "cluster", "metadata.annotations.foo.argoproj.io": "production", "project": "prod-project",
|
||||||
@@ -315,7 +315,7 @@ func TestGenerateParams(t *testing.T) {
|
|||||||
testCase.clientError,
|
testCase.clientError,
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -398,7 +398,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
selector metav1.LabelSelector
|
selector metav1.LabelSelector
|
||||||
values map[string]string
|
values map[string]string
|
||||||
isFlatMode bool
|
isFlatMode bool
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
// clientError is true if a k8s client error should be simulated
|
// clientError is true if a k8s client error should be simulated
|
||||||
clientError bool
|
clientError bool
|
||||||
expectedError error
|
expectedError error
|
||||||
@@ -415,13 +415,13 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
"bat": "{{ if not (empty .metadata) }}{{.metadata.labels.environment}}{{ end }}",
|
"bat": "{{ if not (empty .metadata) }}{{.metadata.labels.environment}}{{ end }}",
|
||||||
"aaa": "{{ .server }}",
|
"aaa": "{{ .server }}",
|
||||||
"no-op": "{{ .thisDoesNotExist }}",
|
"no-op": "{{ .thisDoesNotExist }}",
|
||||||
}, expected: []map[string]interface{}{
|
}, expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"name": "production_01/west",
|
"name": "production_01/west",
|
||||||
"nameNormalized": "production-01-west",
|
"nameNormalized": "production-01-west",
|
||||||
"server": "https://production-01.example.com",
|
"server": "https://production-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
@@ -447,7 +447,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
"nameNormalized": "staging-01",
|
"nameNormalized": "staging-01",
|
||||||
"server": "https://staging-01.example.com",
|
"server": "https://staging-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "staging",
|
"environment": "staging",
|
||||||
@@ -496,13 +496,13 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"name": "production_01/west",
|
"name": "production_01/west",
|
||||||
"nameNormalized": "production-01-west",
|
"nameNormalized": "production-01-west",
|
||||||
"server": "https://production-01.example.com",
|
"server": "https://production-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
@@ -518,7 +518,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
"nameNormalized": "staging-01",
|
"nameNormalized": "staging-01",
|
||||||
"server": "https://staging-01.example.com",
|
"server": "https://staging-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "staging",
|
"environment": "staging",
|
||||||
@@ -543,13 +543,13 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"name": "production_01/west",
|
"name": "production_01/west",
|
||||||
"nameNormalized": "production-01-west",
|
"nameNormalized": "production-01-west",
|
||||||
"server": "https://production-01.example.com",
|
"server": "https://production-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
@@ -584,13 +584,13 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"name": "production_01/west",
|
"name": "production_01/west",
|
||||||
"nameNormalized": "production-01-west",
|
"nameNormalized": "production-01-west",
|
||||||
"server": "https://production-01.example.com",
|
"server": "https://production-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
@@ -609,7 +609,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
"nameNormalized": "staging-01",
|
"nameNormalized": "staging-01",
|
||||||
"server": "https://staging-01.example.com",
|
"server": "https://staging-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "staging",
|
"environment": "staging",
|
||||||
@@ -647,13 +647,13 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"name": "baz",
|
"name": "baz",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"name": "staging-01",
|
"name": "staging-01",
|
||||||
"nameNormalized": "staging-01",
|
"nameNormalized": "staging-01",
|
||||||
"server": "https://staging-01.example.com",
|
"server": "https://staging-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "staging",
|
"environment": "staging",
|
||||||
@@ -677,7 +677,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
values: nil,
|
values: nil,
|
||||||
expected: nil,
|
expected: nil,
|
||||||
clientError: true,
|
clientError: true,
|
||||||
expectedError: fmt.Errorf("error getting cluster secrets: could not list Secrets"),
|
expectedError: errors.New("error getting cluster secrets: could not list Secrets"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Clusters with flat list mode and no selector",
|
name: "Clusters with flat list mode and no selector",
|
||||||
@@ -693,9 +693,9 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
"aaa": "{{ .server }}",
|
"aaa": "{{ .server }}",
|
||||||
"no-op": "{{ .thisDoesNotExist }}",
|
"no-op": "{{ .thisDoesNotExist }}",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"clusters": []map[string]interface{}{
|
"clusters": []map[string]any{
|
||||||
{
|
{
|
||||||
"nameNormalized": "in-cluster",
|
"nameNormalized": "in-cluster",
|
||||||
"name": "in-cluster",
|
"name": "in-cluster",
|
||||||
@@ -717,7 +717,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
"nameNormalized": "production-01-west",
|
"nameNormalized": "production-01-west",
|
||||||
"server": "https://production-01.example.com",
|
"server": "https://production-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
@@ -743,7 +743,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
"nameNormalized": "staging-01",
|
"nameNormalized": "staging-01",
|
||||||
"server": "https://staging-01.example.com",
|
"server": "https://staging-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "staging",
|
"environment": "staging",
|
||||||
@@ -788,15 +788,15 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"clusters": []map[string]interface{}{
|
"clusters": []map[string]any{
|
||||||
{
|
{
|
||||||
"name": "production_01/west",
|
"name": "production_01/west",
|
||||||
"nameNormalized": "production-01-west",
|
"nameNormalized": "production-01-west",
|
||||||
"server": "https://production-01.example.com",
|
"server": "https://production-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "production",
|
"environment": "production",
|
||||||
@@ -815,7 +815,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
"nameNormalized": "staging-01",
|
"nameNormalized": "staging-01",
|
||||||
"server": "https://staging-01.example.com",
|
"server": "https://staging-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
"environment": "staging",
|
"environment": "staging",
|
||||||
@@ -853,7 +853,7 @@ func TestGenerateParamsGoTemplate(t *testing.T) {
|
|||||||
testCase.clientError,
|
testCase.clientError,
|
||||||
}
|
}
|
||||||
|
|
||||||
clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package generators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -9,38 +10,33 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*DuckTypeGenerator)(nil)
|
var _ Generator = (*DuckTypeGenerator)(nil)
|
||||||
|
|
||||||
// DuckTypeGenerator generates Applications for some or all clusters registered with ArgoCD.
|
// DuckTypeGenerator generates Applications for some or all clusters registered with ArgoCD.
|
||||||
type DuckTypeGenerator struct {
|
type DuckTypeGenerator struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
dynClient dynamic.Interface
|
dynClient dynamic.Interface
|
||||||
clientset kubernetes.Interface
|
clientset kubernetes.Interface
|
||||||
namespace string // namespace is the Argo CD namespace
|
namespace string // namespace is the Argo CD namespace
|
||||||
settingsManager *settings.SettingsManager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string) Generator {
|
func NewDuckTypeGenerator(ctx context.Context, dynClient dynamic.Interface, clientset kubernetes.Interface, namespace string) Generator {
|
||||||
settingsManager := settings.NewSettingsManager(ctx, clientset, namespace)
|
|
||||||
|
|
||||||
g := &DuckTypeGenerator{
|
g := &DuckTypeGenerator{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
dynClient: dynClient,
|
dynClient: dynClient,
|
||||||
clientset: clientset,
|
clientset: clientset,
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
settingsManager: settingsManager,
|
|
||||||
}
|
}
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
@@ -59,14 +55,14 @@ func (g *DuckTypeGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Appl
|
|||||||
return &appSetGenerator.ClusterDecisionResource.Template
|
return &appSetGenerator.ClusterDecisionResource.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not likely to happen
|
// Not likely to happen
|
||||||
if appSetGenerator.ClusterDecisionResource == nil {
|
if appSetGenerator.ClusterDecisionResource == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
|
// ListCluster from Argo CD's util/db package will include the local cluster in the list of clusters
|
||||||
@@ -96,13 +92,13 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
|||||||
// Validate the fields
|
// Validate the fields
|
||||||
if kind == "" || versionIdx < 1 {
|
if kind == "" || versionIdx < 1 {
|
||||||
log.Warningf("kind=%v, resourceName=%v, versionIdx=%v", kind, resourceName, versionIdx)
|
log.Warningf("kind=%v, resourceName=%v, versionIdx=%v", kind, resourceName, versionIdx)
|
||||||
return nil, fmt.Errorf("There is a problem with the apiVersion, kind or resourceName provided")
|
return nil, errors.New("there is a problem with the apiVersion, kind or resourceName provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resourceName == "" && labelSelector.MatchLabels == nil && labelSelector.MatchExpressions == nil) ||
|
if (resourceName == "" && labelSelector.MatchLabels == nil && labelSelector.MatchExpressions == nil) ||
|
||||||
(resourceName != "" && (labelSelector.MatchExpressions != nil || labelSelector.MatchLabels != nil)) {
|
(resourceName != "" && (labelSelector.MatchExpressions != nil || labelSelector.MatchLabels != nil)) {
|
||||||
log.Warningf("You must choose either resourceName=%v, labelSelector.matchLabels=%v or labelSelect.matchExpressions=%v", resourceName, labelSelector.MatchLabels, labelSelector.MatchExpressions)
|
log.Warningf("You must choose either resourceName=%v, labelSelector.matchLabels=%v or labelSelect.matchExpressions=%v", resourceName, labelSelector.MatchLabels, labelSelector.MatchExpressions)
|
||||||
return nil, fmt.Errorf("There is a problem with the definition of the ClusterDecisionResource generator")
|
return nil, errors.New("there is a problem with the definition of the ClusterDecisionResource generator")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split up the apiVersion
|
// Split up the apiVersion
|
||||||
@@ -130,97 +126,104 @@ func (g *DuckTypeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.A
|
|||||||
|
|
||||||
if len(duckResources.Items) == 0 {
|
if len(duckResources.Items) == 0 {
|
||||||
log.Warning("no resource found, make sure you clusterDecisionResource is defined correctly")
|
log.Warning("no resource found, make sure you clusterDecisionResource is defined correctly")
|
||||||
return nil, fmt.Errorf("no clusterDecisionResources found")
|
return nil, errors.New("no clusterDecisionResources found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override the duck type in the status of the resource
|
// Override the duck type in the status of the resource
|
||||||
statusListKey := "clusters"
|
statusListKey := "clusters"
|
||||||
|
|
||||||
matchKey := cm.Data["matchKey"]
|
|
||||||
|
|
||||||
if cm.Data["statusListKey"] != "" {
|
if cm.Data["statusListKey"] != "" {
|
||||||
statusListKey = cm.Data["statusListKey"]
|
statusListKey = cm.Data["statusListKey"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matchKey := cm.Data["matchKey"]
|
||||||
if matchKey == "" {
|
if matchKey == "" {
|
||||||
log.WithField("matchKey", matchKey).Warning("matchKey not found in " + cm.Name)
|
log.WithField("matchKey", matchKey).Warning("matchKey not found in " + cm.Name)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []map[string]interface{}{}
|
clusterDecisions := buildClusterDecisions(duckResources, statusListKey)
|
||||||
clusterDecisions := []interface{}{}
|
if len(clusterDecisions) == 0 {
|
||||||
|
log.Warningf("clusterDecisionResource status.%s missing", statusListKey)
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res := []map[string]any{}
|
||||||
|
for _, clusterDecision := range clusterDecisions {
|
||||||
|
cluster := findCluster(clustersFromArgoCD, clusterDecision, matchKey, statusListKey)
|
||||||
|
// if no cluster is found, move to the next cluster
|
||||||
|
if cluster == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// generated instance of cluster params
|
||||||
|
params := map[string]any{
|
||||||
|
"name": cluster.Name,
|
||||||
|
"server": cluster.Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range clusterDecision.(map[string]any) {
|
||||||
|
params[key] = value.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range appSetGenerator.ClusterDecisionResource.Values {
|
||||||
|
collectParams(appSet, params, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildClusterDecisions(duckResources *unstructured.UnstructuredList, statusListKey string) []any {
|
||||||
|
clusterDecisions := []any{}
|
||||||
|
|
||||||
// Build the decision slice
|
// Build the decision slice
|
||||||
for _, duckResource := range duckResources.Items {
|
for _, duckResource := range duckResources.Items {
|
||||||
log.WithField("duckResourceName", duckResource.GetName()).Debug("found resource")
|
log.WithField("duckResourceName", duckResource.GetName()).Debug("found resource")
|
||||||
|
|
||||||
if duckResource.Object["status"] == nil || len(duckResource.Object["status"].(map[string]interface{})) == 0 {
|
if duckResource.Object["status"] == nil || len(duckResource.Object["status"].(map[string]any)) == 0 {
|
||||||
log.Warningf("clusterDecisionResource: %s, has no status", duckResource.GetName())
|
log.Warningf("clusterDecisionResource: %s, has no status", duckResource.GetName())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.WithField("duckResourceStatus", duckResource.Object["status"]).Debug("found resource")
|
log.WithField("duckResourceStatus", duckResource.Object["status"]).Debug("found resource")
|
||||||
|
|
||||||
clusterDecisions = append(clusterDecisions, duckResource.Object["status"].(map[string]interface{})[statusListKey].([]interface{})...)
|
clusterDecisions = append(clusterDecisions, duckResource.Object["status"].(map[string]any)[statusListKey].([]any)...)
|
||||||
}
|
}
|
||||||
log.Infof("Number of decisions found: %v", len(clusterDecisions))
|
log.Infof("Number of decisions found: %v", len(clusterDecisions))
|
||||||
|
return clusterDecisions
|
||||||
|
}
|
||||||
|
|
||||||
// Read this outside the loop to improve performance
|
func findCluster(clustersFromArgoCD []utils.ClusterSpecifier, cluster any, matchKey string, statusListKey string) *utils.ClusterSpecifier {
|
||||||
argoClusters := clustersFromArgoCD.Items
|
log.Infof("cluster: %v", cluster)
|
||||||
|
matchValue := cluster.(map[string]any)[matchKey]
|
||||||
if len(clusterDecisions) > 0 {
|
if matchValue == nil || matchValue.(string) == "" {
|
||||||
for _, cluster := range clusterDecisions {
|
log.Warningf("matchKey=%v not found in \"%v\" list: %v\n", matchKey, statusListKey, cluster.(map[string]any))
|
||||||
// generated instance of cluster params
|
return nil // no match
|
||||||
params := map[string]interface{}{}
|
|
||||||
|
|
||||||
log.Infof("cluster: %v", cluster)
|
|
||||||
matchValue := cluster.(map[string]interface{})[matchKey]
|
|
||||||
if matchValue == nil || matchValue.(string) == "" {
|
|
||||||
log.Warningf("matchKey=%v not found in \"%v\" list: %v\n", matchKey, statusListKey, cluster.(map[string]interface{}))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
strMatchValue := matchValue.(string)
|
|
||||||
log.WithField(matchKey, strMatchValue).Debug("validate against ArgoCD")
|
|
||||||
|
|
||||||
found := false
|
|
||||||
|
|
||||||
for _, argoCluster := range argoClusters {
|
|
||||||
if argoCluster.Name == strMatchValue {
|
|
||||||
log.WithField(matchKey, argoCluster.Name).Info("matched cluster in ArgoCD")
|
|
||||||
params["name"] = argoCluster.Name
|
|
||||||
params["server"] = argoCluster.Server
|
|
||||||
|
|
||||||
found = true
|
|
||||||
break // Stop looking
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
log.WithField(matchKey, strMatchValue).Warning("unmatched cluster in ArgoCD")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range cluster.(map[string]interface{}) {
|
|
||||||
params[key] = value.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range appSetGenerator.ClusterDecisionResource.Values {
|
|
||||||
if appSet.Spec.GoTemplate {
|
|
||||||
if params["values"] == nil {
|
|
||||||
params["values"] = map[string]string{}
|
|
||||||
}
|
|
||||||
params["values"].(map[string]string)[key] = value
|
|
||||||
} else {
|
|
||||||
params[fmt.Sprintf("values.%s", key)] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, params)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warningf("clusterDecisionResource status.%s missing", statusListKey)
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
strMatchValue := matchValue.(string)
|
||||||
|
log.WithField(matchKey, strMatchValue).Debug("validate against ArgoCD")
|
||||||
|
|
||||||
|
for _, argoCluster := range clustersFromArgoCD {
|
||||||
|
if argoCluster.Name == strMatchValue {
|
||||||
|
log.WithField(matchKey, argoCluster.Name).Info("matched cluster in ArgoCD")
|
||||||
|
return &argoCluster
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithField(matchKey, strMatchValue).Warning("unmatched cluster in ArgoCD")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectParams(appSet *argoprojiov1alpha1.ApplicationSet, params map[string]any, key string, value string) {
|
||||||
|
if appSet.Spec.GoTemplate {
|
||||||
|
if params["values"] == nil {
|
||||||
|
params["values"] = map[string]string{}
|
||||||
|
}
|
||||||
|
params["values"].(map[string]string)[key] = value
|
||||||
|
} else {
|
||||||
|
params["values."+key] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package generators
|
package generators
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -16,11 +15,11 @@ import (
|
|||||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
resourceApiVersion = "mallard.io/v1"
|
resourceAPIVersion = "mallard.io/v1"
|
||||||
resourceKind = "ducks"
|
resourceKind = "ducks"
|
||||||
resourceName = "quak"
|
resourceName = "quak"
|
||||||
)
|
)
|
||||||
@@ -78,20 +77,20 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
duckType := &unstructured.Unstructured{
|
duckType := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]any{
|
||||||
"apiVersion": resourceApiVersion,
|
"apiVersion": resourceAPIVersion,
|
||||||
"kind": "Duck",
|
"kind": "Duck",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"name": resourceName,
|
"name": resourceName,
|
||||||
"namespace": "namespace",
|
"namespace": "namespace",
|
||||||
"labels": map[string]interface{}{"duck": "all-species"},
|
"labels": map[string]any{"duck": "all-species"},
|
||||||
},
|
},
|
||||||
"status": map[string]interface{}{
|
"status": map[string]any{
|
||||||
"decisions": []interface{}{
|
"decisions": []any{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"clusterName": "staging-01",
|
"clusterName": "staging-01",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"clusterName": "production-01",
|
"clusterName": "production-01",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -100,17 +99,17 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
duckTypeProdOnly := &unstructured.Unstructured{
|
duckTypeProdOnly := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]any{
|
||||||
"apiVersion": resourceApiVersion,
|
"apiVersion": resourceAPIVersion,
|
||||||
"kind": "Duck",
|
"kind": "Duck",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"name": resourceName,
|
"name": resourceName,
|
||||||
"namespace": "namespace",
|
"namespace": "namespace",
|
||||||
"labels": map[string]interface{}{"duck": "spotted"},
|
"labels": map[string]any{"duck": "spotted"},
|
||||||
},
|
},
|
||||||
"status": map[string]interface{}{
|
"status": map[string]any{
|
||||||
"decisions": []interface{}{
|
"decisions": []any{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"clusterName": "production-01",
|
"clusterName": "production-01",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -119,15 +118,15 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
duckTypeEmpty := &unstructured.Unstructured{
|
duckTypeEmpty := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]any{
|
||||||
"apiVersion": resourceApiVersion,
|
"apiVersion": resourceAPIVersion,
|
||||||
"kind": "Duck",
|
"kind": "Duck",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"name": resourceName,
|
"name": resourceName,
|
||||||
"namespace": "namespace",
|
"namespace": "namespace",
|
||||||
"labels": map[string]interface{}{"duck": "canvasback"},
|
"labels": map[string]any{"duck": "canvasback"},
|
||||||
},
|
},
|
||||||
"status": map[string]interface{}{},
|
"status": map[string]any{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +136,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
},
|
},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"apiVersion": resourceApiVersion,
|
"apiVersion": resourceAPIVersion,
|
||||||
"kind": resourceKind,
|
"kind": resourceKind,
|
||||||
"statusListKey": "decisions",
|
"statusListKey": "decisions",
|
||||||
"matchKey": "clusterName",
|
"matchKey": "clusterName",
|
||||||
@@ -151,7 +150,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
labelSelector metav1.LabelSelector
|
labelSelector metav1.LabelSelector
|
||||||
resource *unstructured.Unstructured
|
resource *unstructured.Unstructured
|
||||||
values map[string]string
|
values map[string]string
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
expectedError error
|
expectedError error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -159,8 +158,8 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
resourceName: "",
|
resourceName: "",
|
||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{},
|
expected: []map[string]any{},
|
||||||
expectedError: fmt.Errorf("There is a problem with the definition of the ClusterDecisionResource generator"),
|
expectedError: errors.New("there is a problem with the definition of the ClusterDecisionResource generator"),
|
||||||
},
|
},
|
||||||
/*** This does not work with the FAKE runtime client, fieldSelectors are broken.
|
/*** This does not work with the FAKE runtime client, fieldSelectors are broken.
|
||||||
{
|
{
|
||||||
@@ -177,7 +176,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
resourceName: resourceName,
|
resourceName: resourceName,
|
||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
|
|
||||||
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
||||||
@@ -191,7 +190,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "values.foo": "bar", "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "values.foo": "bar", "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
},
|
},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@@ -219,7 +218,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
labelSelector: metav1.LabelSelector{MatchLabels: map[string]string{"duck": "all-species"}},
|
labelSelector: metav1.LabelSelector{MatchLabels: map[string]string{"duck": "all-species"}},
|
||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
|
|
||||||
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
||||||
@@ -234,7 +233,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "values.foo": "bar", "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "values.foo": "bar", "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
},
|
},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@@ -251,7 +250,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
|
|
||||||
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
||||||
@@ -271,7 +270,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: nil,
|
expected: nil,
|
||||||
expectedError: fmt.Errorf("There is a problem with the definition of the ClusterDecisionResource generator"),
|
expectedError: errors.New("there is a problem with the definition of the ClusterDecisionResource generator"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,7 +292,7 @@ func TestGenerateParamsForDuckType(t *testing.T) {
|
|||||||
|
|
||||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
||||||
|
|
||||||
duckTypeGenerator := NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace")
|
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace")
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -374,20 +373,20 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
duckType := &unstructured.Unstructured{
|
duckType := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]any{
|
||||||
"apiVersion": resourceApiVersion,
|
"apiVersion": resourceAPIVersion,
|
||||||
"kind": "Duck",
|
"kind": "Duck",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"name": resourceName,
|
"name": resourceName,
|
||||||
"namespace": "namespace",
|
"namespace": "namespace",
|
||||||
"labels": map[string]interface{}{"duck": "all-species"},
|
"labels": map[string]any{"duck": "all-species"},
|
||||||
},
|
},
|
||||||
"status": map[string]interface{}{
|
"status": map[string]any{
|
||||||
"decisions": []interface{}{
|
"decisions": []any{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"clusterName": "staging-01",
|
"clusterName": "staging-01",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"clusterName": "production-01",
|
"clusterName": "production-01",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -396,17 +395,17 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
duckTypeProdOnly := &unstructured.Unstructured{
|
duckTypeProdOnly := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]any{
|
||||||
"apiVersion": resourceApiVersion,
|
"apiVersion": resourceAPIVersion,
|
||||||
"kind": "Duck",
|
"kind": "Duck",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"name": resourceName,
|
"name": resourceName,
|
||||||
"namespace": "namespace",
|
"namespace": "namespace",
|
||||||
"labels": map[string]interface{}{"duck": "spotted"},
|
"labels": map[string]any{"duck": "spotted"},
|
||||||
},
|
},
|
||||||
"status": map[string]interface{}{
|
"status": map[string]any{
|
||||||
"decisions": []interface{}{
|
"decisions": []any{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"clusterName": "production-01",
|
"clusterName": "production-01",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -415,15 +414,15 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
duckTypeEmpty := &unstructured.Unstructured{
|
duckTypeEmpty := &unstructured.Unstructured{
|
||||||
Object: map[string]interface{}{
|
Object: map[string]any{
|
||||||
"apiVersion": resourceApiVersion,
|
"apiVersion": resourceAPIVersion,
|
||||||
"kind": "Duck",
|
"kind": "Duck",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"name": resourceName,
|
"name": resourceName,
|
||||||
"namespace": "namespace",
|
"namespace": "namespace",
|
||||||
"labels": map[string]interface{}{"duck": "canvasback"},
|
"labels": map[string]any{"duck": "canvasback"},
|
||||||
},
|
},
|
||||||
"status": map[string]interface{}{},
|
"status": map[string]any{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,7 +432,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
Namespace: "namespace",
|
Namespace: "namespace",
|
||||||
},
|
},
|
||||||
Data: map[string]string{
|
Data: map[string]string{
|
||||||
"apiVersion": resourceApiVersion,
|
"apiVersion": resourceAPIVersion,
|
||||||
"kind": resourceKind,
|
"kind": resourceKind,
|
||||||
"statusListKey": "decisions",
|
"statusListKey": "decisions",
|
||||||
"matchKey": "clusterName",
|
"matchKey": "clusterName",
|
||||||
@@ -447,7 +446,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
labelSelector metav1.LabelSelector
|
labelSelector metav1.LabelSelector
|
||||||
resource *unstructured.Unstructured
|
resource *unstructured.Unstructured
|
||||||
values map[string]string
|
values map[string]string
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
expectedError error
|
expectedError error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -455,8 +454,8 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
resourceName: "",
|
resourceName: "",
|
||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{},
|
expected: []map[string]any{},
|
||||||
expectedError: fmt.Errorf("There is a problem with the definition of the ClusterDecisionResource generator"),
|
expectedError: errors.New("there is a problem with the definition of the ClusterDecisionResource generator"),
|
||||||
},
|
},
|
||||||
/*** This does not work with the FAKE runtime client, fieldSelectors are broken.
|
/*** This does not work with the FAKE runtime client, fieldSelectors are broken.
|
||||||
{
|
{
|
||||||
@@ -473,7 +472,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
resourceName: resourceName,
|
resourceName: resourceName,
|
||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
|
|
||||||
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
||||||
@@ -487,7 +486,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "values": map[string]string{"foo": "bar"}, "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "values": map[string]string{"foo": "bar"}, "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
},
|
},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@@ -515,7 +514,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
labelSelector: metav1.LabelSelector{MatchLabels: map[string]string{"duck": "all-species"}},
|
labelSelector: metav1.LabelSelector{MatchLabels: map[string]string{"duck": "all-species"}},
|
||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
|
|
||||||
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
||||||
@@ -530,7 +529,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "values": map[string]string{"foo": "bar"}, "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "values": map[string]string{"foo": "bar"}, "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
},
|
},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
@@ -547,7 +546,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
{"clusterName": "production-01", "name": "production-01", "server": "https://production-01.example.com"},
|
||||||
|
|
||||||
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
{"clusterName": "staging-01", "name": "staging-01", "server": "https://staging-01.example.com"},
|
||||||
@@ -567,7 +566,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
resource: duckType,
|
resource: duckType,
|
||||||
values: nil,
|
values: nil,
|
||||||
expected: nil,
|
expected: nil,
|
||||||
expectedError: fmt.Errorf("There is a problem with the definition of the ClusterDecisionResource generator"),
|
expectedError: errors.New("there is a problem with the definition of the ClusterDecisionResource generator"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,7 +588,7 @@ func TestGenerateParamsForDuckTypeGoTemplate(t *testing.T) {
|
|||||||
|
|
||||||
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
fakeDynClient := dynfake.NewSimpleDynamicClientWithCustomListKinds(runtime.NewScheme(), gvrToListKind, testCase.resource)
|
||||||
|
|
||||||
duckTypeGenerator := NewDuckTypeGenerator(context.Background(), fakeDynClient, appClientset, "namespace")
|
duckTypeGenerator := NewDuckTypeGenerator(t.Context(), fakeDynClient, appClientset, "namespace")
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import (
|
|||||||
"github.com/jeremywohl/flatten"
|
"github.com/jeremywohl/flatten"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
"dario.cat/mergo"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,12 +22,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TransformResult struct {
|
type TransformResult struct {
|
||||||
Params []map[string]interface{}
|
Params []map[string]any
|
||||||
Template argoprojiov1alpha1.ApplicationSetTemplate
|
Template argoprojiov1alpha1.ApplicationSetTemplate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform a spec generator to list of paramSets and a template
|
// Transform a spec generator to list of paramSets and a template
|
||||||
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]interface{}, client client.Client) ([]TransformResult, error) {
|
func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, allGenerators map[string]Generator, baseTemplate argoprojiov1alpha1.ApplicationSetTemplate, appSet *argoprojiov1alpha1.ApplicationSet, genParams map[string]any, client client.Client) ([]TransformResult, error) {
|
||||||
// This is a custom version of the `LabelSelectorAsSelector` that is in k8s.io/apimachinery. This has been copied
|
// This is a custom version of the `LabelSelectorAsSelector` that is in k8s.io/apimachinery. This has been copied
|
||||||
// verbatim from that package, with the difference that we do not have any restrictions on label values. This is done
|
// verbatim from that package, with the difference that we do not have any restrictions on label values. This is done
|
||||||
// so that, among other things, we can match on cluster urls.
|
// so that, among other things, we can match on cluster urls.
|
||||||
@@ -52,7 +52,7 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var params []map[string]interface{}
|
var params []map[string]any
|
||||||
if len(genParams) != 0 {
|
if len(genParams) != 0 {
|
||||||
tempInterpolatedGenerator, err := InterpolateGenerator(&requestedGenerator, genParams, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
tempInterpolatedGenerator, err := InterpolateGenerator(&requestedGenerator, genParams, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
||||||
interpolatedGenerator = &tempInterpolatedGenerator
|
interpolatedGenerator = &tempInterpolatedGenerator
|
||||||
@@ -74,7 +74,7 @@ func Transform(requestedGenerator argoprojiov1alpha1.ApplicationSetGenerator, al
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var filterParams []map[string]interface{}
|
var filterParams []map[string]any
|
||||||
for _, param := range params {
|
for _, param := range params {
|
||||||
flatParam, err := flattenParameters(param)
|
flatParam, err := flattenParameters(param)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -123,7 +123,7 @@ func GetRelevantGenerators(requestedGenerator *argoprojiov1alpha1.ApplicationSet
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func flattenParameters(in map[string]interface{}) (map[string]string, error) {
|
func flattenParameters(in map[string]any) (map[string]string, error) {
|
||||||
flat, err := flatten.Flatten(in, "", flatten.DotStyle)
|
flat, err := flatten.Flatten(in, "", flatten.DotStyle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error flatenning parameters: %w", err)
|
return nil, fmt.Errorf("error flatenning parameters: %w", err)
|
||||||
@@ -149,7 +149,7 @@ func mergeGeneratorTemplate(g Generator, requestedGenerator *argoprojiov1alpha1.
|
|||||||
|
|
||||||
// InterpolateGenerator allows interpolating the matrix's 2nd child generator with values from the 1st child generator
|
// InterpolateGenerator allows interpolating the matrix's 2nd child generator with values from the 1st child generator
|
||||||
// "params" parameter is an array, where each index corresponds to a generator. Each index contains a map w/ that generator's parameters.
|
// "params" parameter is an array, where each index corresponds to a generator. Each index contains a map w/ that generator's parameters.
|
||||||
func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (argoprojiov1alpha1.ApplicationSetGenerator, error) {
|
func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetGenerator, params map[string]any, useGoTemplate bool, goTemplateOptions []string) (argoprojiov1alpha1.ApplicationSetGenerator, error) {
|
||||||
render := utils.Render{}
|
render := utils.Render{}
|
||||||
interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate, goTemplateOptions)
|
interpolatedGenerator, err := render.RenderGeneratorParams(requestedGenerator, params, useGoTemplate, goTemplateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -159,16 +159,3 @@ func InterpolateGenerator(requestedGenerator *argoprojiov1alpha1.ApplicationSetG
|
|||||||
|
|
||||||
return *interpolatedGenerator, nil
|
return *interpolatedGenerator, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fixes https://github.com/argoproj/argo-cd/issues/11982 while ensuring backwards compatibility.
|
|
||||||
// This is only a short-term solution and should be removed in a future major version.
|
|
||||||
func dropDisabledNestedSelectors(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator) bool {
|
|
||||||
var foundSelector bool
|
|
||||||
for i := range generators {
|
|
||||||
if generators[i].Selector != nil {
|
|
||||||
foundSelector = true
|
|
||||||
generators[i].Selector = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return foundSelector
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
||||||
|
|
||||||
argov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -27,19 +27,19 @@ func TestMatchValues(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
elements []apiextensionsv1.JSON
|
elements []apiextensionsv1.JSON
|
||||||
selector *metav1.LabelSelector
|
selector *metav1.LabelSelector
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no filter",
|
name: "no filter",
|
||||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
||||||
selector: &metav1.LabelSelector{},
|
selector: &metav1.LabelSelector{},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nil",
|
name: "nil",
|
||||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
||||||
selector: nil,
|
selector: nil,
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "values.foo should be foo but is ignore element",
|
name: "values.foo should be foo but is ignore element",
|
||||||
@@ -49,7 +49,7 @@ func TestMatchValues(t *testing.T) {
|
|||||||
"values.foo": "foo",
|
"values.foo": "foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{},
|
expected: []map[string]any{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "values.foo should be bar",
|
name: "values.foo should be bar",
|
||||||
@@ -59,7 +59,7 @@ func TestMatchValues(t *testing.T) {
|
|||||||
"values.foo": "bar",
|
"values.foo": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values.foo": "bar"}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url", "values.foo": "bar"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,19 +101,19 @@ func TestMatchValuesGoTemplate(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
elements []apiextensionsv1.JSON
|
elements []apiextensionsv1.JSON
|
||||||
selector *metav1.LabelSelector
|
selector *metav1.LabelSelector
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no filter",
|
name: "no filter",
|
||||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
||||||
selector: &metav1.LabelSelector{},
|
selector: &metav1.LabelSelector{},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "nil",
|
name: "nil",
|
||||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
||||||
selector: nil,
|
selector: nil,
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url"}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "values.foo should be foo but is ignore element",
|
name: "values.foo should be foo but is ignore element",
|
||||||
@@ -123,7 +123,7 @@ func TestMatchValuesGoTemplate(t *testing.T) {
|
|||||||
"values.foo": "foo",
|
"values.foo": "foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{},
|
expected: []map[string]any{},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "values.foo should be bar",
|
name: "values.foo should be bar",
|
||||||
@@ -133,7 +133,7 @@ func TestMatchValuesGoTemplate(t *testing.T) {
|
|||||||
"values.foo": "bar",
|
"values.foo": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": map[string]interface{}{"foo": "bar"}}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url", "values": map[string]any{"foo": "bar"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "values.0 should be bar",
|
name: "values.0 should be bar",
|
||||||
@@ -143,7 +143,7 @@ func TestMatchValuesGoTemplate(t *testing.T) {
|
|||||||
"values.0": "bar",
|
"values.0": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": []interface{}{"bar"}}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url", "values": []any{"bar"}}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,14 +184,14 @@ func TestTransForm(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
selector *metav1.LabelSelector
|
selector *metav1.LabelSelector
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "server filter",
|
name: "server filter",
|
||||||
selector: &metav1.LabelSelector{
|
selector: &metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{"server": "https://production-01.example.com"},
|
MatchLabels: map[string]string{"server": "https://production-01.example.com"},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{{
|
expected: []map[string]any{{
|
||||||
"metadata.annotations.foo.argoproj.io": "production",
|
"metadata.annotations.foo.argoproj.io": "production",
|
||||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster",
|
"metadata.labels.argocd.argoproj.io/secret-type": "cluster",
|
||||||
"metadata.labels.environment": "production",
|
"metadata.labels.environment": "production",
|
||||||
@@ -207,7 +207,7 @@ func TestTransForm(t *testing.T) {
|
|||||||
selector: &metav1.LabelSelector{
|
selector: &metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{"server": "https://some-really-long-url-that-will-exceed-63-characters.com"},
|
MatchLabels: map[string]string{"server": "https://some-really-long-url-that-will-exceed-63-characters.com"},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{{
|
expected: []map[string]any{{
|
||||||
"metadata.annotations.foo.argoproj.io": "production",
|
"metadata.annotations.foo.argoproj.io": "production",
|
||||||
"metadata.labels.argocd.argoproj.io/secret-type": "cluster",
|
"metadata.labels.argocd.argoproj.io/secret-type": "cluster",
|
||||||
"metadata.labels.environment": "production",
|
"metadata.labels.environment": "production",
|
||||||
@@ -342,7 +342,7 @@ func getMockClusterGenerator() Generator {
|
|||||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||||
|
|
||||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||||
return NewClusterGenerator(fakeClient, context.Background(), appClientset, "namespace")
|
return NewClusterGenerator(context.Background(), fakeClient, appClientset, "namespace")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMockGitGenerator() Generator {
|
func getMockGitGenerator() Generator {
|
||||||
@@ -413,7 +413,7 @@ func TestInterpolateGenerator(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
gitGeneratorParams := map[string]interface{}{
|
gitGeneratorParams := map[string]any{
|
||||||
"path": "p1/p2/app3",
|
"path": "p1/p2/app3",
|
||||||
"path.basename": "app3",
|
"path.basename": "app3",
|
||||||
"path[0]": "p1",
|
"path[0]": "p1",
|
||||||
@@ -442,7 +442,7 @@ func TestInterpolateGenerator(t *testing.T) {
|
|||||||
Template: argov1alpha1.ApplicationSetTemplate{},
|
Template: argov1alpha1.ApplicationSetTemplate{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
clusterGeneratorParams := map[string]interface{}{
|
clusterGeneratorParams := map[string]any{
|
||||||
"name": "production_01/west", "server": "https://production-01.example.com",
|
"name": "production_01/west", "server": "https://production-01.example.com",
|
||||||
}
|
}
|
||||||
interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, false, nil)
|
interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, false, nil)
|
||||||
@@ -468,8 +468,8 @@ func TestInterpolateGenerator_go(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
gitGeneratorParams := map[string]interface{}{
|
gitGeneratorParams := map[string]any{
|
||||||
"path": map[string]interface{}{
|
"path": map[string]any{
|
||||||
"path": "p1/p2/app3",
|
"path": "p1/p2/app3",
|
||||||
"segments": []string{"p1", "p2", "app3"},
|
"segments": []string{"p1", "p2", "app3"},
|
||||||
},
|
},
|
||||||
@@ -497,7 +497,7 @@ func TestInterpolateGenerator_go(t *testing.T) {
|
|||||||
Template: argov1alpha1.ApplicationSetTemplate{},
|
Template: argov1alpha1.ApplicationSetTemplate{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
clusterGeneratorParams := map[string]interface{}{
|
clusterGeneratorParams := map[string]any{
|
||||||
"name": "production_01/west", "server": "https://production-01.example.com",
|
"name": "production_01/west", "server": "https://production-01.example.com",
|
||||||
}
|
}
|
||||||
interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, true, nil)
|
interpolatedGenerator, err = InterpolateGenerator(requestedGenerator, clusterGeneratorParams, true, nil)
|
||||||
@@ -512,7 +512,7 @@ func TestInterpolateGenerator_go(t *testing.T) {
|
|||||||
func TestInterpolateGeneratorError(t *testing.T) {
|
func TestInterpolateGeneratorError(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
requestedGenerator *argov1alpha1.ApplicationSetGenerator
|
requestedGenerator *argov1alpha1.ApplicationSetGenerator
|
||||||
params map[string]interface{}
|
params map[string]any
|
||||||
useGoTemplate bool
|
useGoTemplate bool
|
||||||
goTemplateOptions []string
|
goTemplateOptions []string
|
||||||
}
|
}
|
||||||
@@ -530,7 +530,7 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
|||||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "generator is empty"},
|
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: "generator is empty"},
|
||||||
{name: "No Params", args: args{
|
{name: "No Params", args: args{
|
||||||
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{},
|
requestedGenerator: &argov1alpha1.ApplicationSetGenerator{},
|
||||||
params: map[string]interface{}{},
|
params: map[string]any{},
|
||||||
useGoTemplate: false,
|
useGoTemplate: false,
|
||||||
goTemplateOptions: nil,
|
goTemplateOptions: nil,
|
||||||
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: ""},
|
}, want: argov1alpha1.ApplicationSetGenerator{}, expectedErrStr: ""},
|
||||||
@@ -545,7 +545,7 @@ func TestInterpolateGeneratorError(t *testing.T) {
|
|||||||
"resolved": "{{ index .rmap (default .override .test) }}",
|
"resolved": "{{ index .rmap (default .override .test) }}",
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
params: map[string]interface{}{
|
params: map[string]any{
|
||||||
"name": "in-cluster",
|
"name": "in-cluster",
|
||||||
"override": "foo",
|
"override": "foo",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services"
|
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
"github.com/argoproj/argo-cd/v3/util/gpg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*GitGenerator)(nil)
|
var _ Generator = (*GitGenerator)(nil)
|
||||||
@@ -28,22 +28,27 @@ type GitGenerator struct {
|
|||||||
namespace string
|
namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGitGenerator(repos services.Repos, namespace string) Generator {
|
// NewGitGenerator creates a new instance of Git Generator
|
||||||
|
func NewGitGenerator(repos services.Repos, controllerNamespace string) Generator {
|
||||||
g := &GitGenerator{
|
g := &GitGenerator{
|
||||||
repos: repos,
|
repos: repos,
|
||||||
namespace: namespace,
|
namespace: controllerNamespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTemplate returns the ApplicationSetTemplate associated with the Git generator
|
||||||
|
// from the provided ApplicationSetGenerator. This template defines how each
|
||||||
|
// generated Argo CD Application should be rendered.
|
||||||
func (g *GitGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *GitGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||||
return &appSetGenerator.Git.Template
|
return &appSetGenerator.Git.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRequeueAfter returns the duration after which the Git generator should be
|
||||||
|
// requeued for reconciliation. If RequeueAfterSeconds is set in the generator spec,
|
||||||
|
// it uses that value. Otherwise, it falls back to a default requeue interval (3 minutes).
|
||||||
func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
||||||
// Return a requeue default of 3 minutes, if no default is specified.
|
|
||||||
|
|
||||||
if appSetGenerator.Git.RequeueAfterSeconds != nil {
|
if appSetGenerator.Git.RequeueAfterSeconds != nil {
|
||||||
return time.Duration(*appSetGenerator.Git.RequeueAfterSeconds) * time.Second
|
return time.Duration(*appSetGenerator.Git.RequeueAfterSeconds) * time.Second
|
||||||
}
|
}
|
||||||
@@ -51,13 +56,15 @@ func (g *GitGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Appli
|
|||||||
return getDefaultRequeueAfter()
|
return getDefaultRequeueAfter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) {
|
// GenerateParams generates a list of parameter maps for the ApplicationSet by evaluating the Git generator's configuration.
|
||||||
|
// It supports both directory-based and file-based Git generators.
|
||||||
|
func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.Git == nil {
|
if appSetGenerator.Git == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
noRevisionCache := appSet.RefreshRequired()
|
noRevisionCache := appSet.RefreshRequired()
|
||||||
@@ -67,28 +74,34 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
|||||||
// When the project field is templated, the contents of the git repo are required to run the git generator and get the templated value,
|
// When the project field is templated, the contents of the git repo are required to run the git generator and get the templated value,
|
||||||
// but git generator cannot be called without verifying the commit signature.
|
// but git generator cannot be called without verifying the commit signature.
|
||||||
// In this case, we skip the signature verification.
|
// In this case, we skip the signature verification.
|
||||||
|
// If the project is templated, we skip the commit verification
|
||||||
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
if !strings.Contains(appSet.Spec.Template.Spec.Project, "{{") {
|
||||||
project := appSet.Spec.Template.Spec.Project
|
project := appSet.Spec.Template.Spec.Project
|
||||||
appProject := &argoprojiov1alpha1.AppProject{}
|
appProject := &argoprojiov1alpha1.AppProject{}
|
||||||
namespace := g.namespace
|
controllerNamespace := g.namespace
|
||||||
if namespace == "" {
|
if controllerNamespace == "" {
|
||||||
namespace = appSet.Namespace
|
controllerNamespace = appSet.Namespace
|
||||||
}
|
}
|
||||||
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: namespace}, appProject); err != nil {
|
if err := client.Get(context.TODO(), types.NamespacedName{Name: project, Namespace: controllerNamespace}, appProject); err != nil {
|
||||||
return nil, fmt.Errorf("error getting project %s: %w", project, err)
|
return nil, fmt.Errorf("error getting project %s: %w", project, err)
|
||||||
}
|
}
|
||||||
// we need to verify the signature on the Git revision if GPG is enabled
|
// we need to verify the signature on the Git revision if GPG is enabled
|
||||||
verifyCommit = len(appProject.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled()
|
verifyCommit = len(appProject.Spec.SignatureKeys) > 0 && gpg.IsGPGEnabled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the project field is templated, we cannot resolve the project name, so we pass an empty string to the repo-server.
|
||||||
|
// This means only "globally-scoped" repo credentials can be used for such appsets.
|
||||||
|
project := resolveProjectName(appSet.Spec.Template.Spec.Project)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var res []map[string]interface{}
|
var res []map[string]any
|
||||||
if len(appSetGenerator.Git.Directories) != 0 {
|
switch {
|
||||||
res, err = g.generateParamsForGitDirectories(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
case len(appSetGenerator.Git.Directories) != 0:
|
||||||
} else if len(appSetGenerator.Git.Files) != 0 {
|
res, err = g.generateParamsForGitDirectories(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, project, appSet.Spec.GoTemplateOptions)
|
||||||
res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, appSet.Spec.GoTemplateOptions)
|
case len(appSetGenerator.Git.Files) != 0:
|
||||||
} else {
|
res, err = g.generateParamsForGitFiles(appSetGenerator, noRevisionCache, verifyCommit, appSet.Spec.GoTemplate, project, appSet.Spec.GoTemplateOptions)
|
||||||
return nil, EmptyAppSetGeneratorError
|
default:
|
||||||
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error generating params from git: %w", err)
|
return nil, fmt.Errorf("error generating params from git: %w", err)
|
||||||
@@ -97,9 +110,11 @@ func (g *GitGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Applic
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
// generateParamsForGitDirectories generates parameters for an ApplicationSet using a directory-based Git generator.
|
||||||
// Directories, not files
|
// It fetches all directories from the given Git repository and revision, optionally using a revision cache and verifying commits.
|
||||||
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, noRevisionCache, verifyCommit)
|
// It then filters the directories based on the generator's configuration and renders parameters for the resulting applications
|
||||||
|
func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit, useGoTemplate bool, project string, goTemplateOptions []string) ([]map[string]any, error) {
|
||||||
|
allPaths, err := g.repos.GetDirectories(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, project, noRevisionCache, verifyCommit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting directories from repo: %w", err)
|
return nil, fmt.Errorf("error getting directories from repo: %w", err)
|
||||||
}
|
}
|
||||||
@@ -122,70 +137,116 @@ func (g *GitGenerator) generateParamsForGitDirectories(appSetGenerator *argoproj
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit bool, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
// generateParamsForGitFiles generates parameters for an ApplicationSet using a file-based Git generator.
|
||||||
// Get all files that match the requested path string, removing duplicates
|
// It retrieves and processes specified files from the Git repository, supporting both YAML and JSON formats,
|
||||||
allFiles := make(map[string][]byte)
|
// and returns a list of parameter maps extracted from the content.
|
||||||
for _, requestedPath := range appSetGenerator.Git.Files {
|
func (g *GitGenerator) generateParamsForGitFiles(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, noRevisionCache, verifyCommit, useGoTemplate bool, project string, goTemplateOptions []string) ([]map[string]any, error) {
|
||||||
files, err := g.repos.GetFiles(context.TODO(), appSetGenerator.Git.RepoURL, appSetGenerator.Git.Revision, requestedPath.Path, noRevisionCache, verifyCommit)
|
// fileContentMap maps absolute file paths to their byte content
|
||||||
|
fileContentMap := make(map[string][]byte)
|
||||||
|
var includePatterns []string
|
||||||
|
var excludePatterns []string
|
||||||
|
|
||||||
|
for _, req := range appSetGenerator.Git.Files {
|
||||||
|
if req.Exclude {
|
||||||
|
excludePatterns = append(excludePatterns, req.Path)
|
||||||
|
} else {
|
||||||
|
includePatterns = append(includePatterns, req.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch all files from include patterns
|
||||||
|
for _, includePattern := range includePatterns {
|
||||||
|
retrievedFiles, err := g.repos.GetFiles(
|
||||||
|
context.TODO(),
|
||||||
|
appSetGenerator.Git.RepoURL,
|
||||||
|
appSetGenerator.Git.Revision,
|
||||||
|
project,
|
||||||
|
includePattern,
|
||||||
|
noRevisionCache,
|
||||||
|
verifyCommit,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for filePath, content := range files {
|
for absPath, content := range retrievedFiles {
|
||||||
allFiles[filePath] = content
|
fileContentMap[absPath] = content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the unduplicated map into a list, and sort by path to ensure a deterministic
|
// Now remove files matching any exclude pattern
|
||||||
// processing order in the subsequent step
|
for _, excludePattern := range excludePatterns {
|
||||||
allPaths := []string{}
|
matchingFiles, err := g.repos.GetFiles(
|
||||||
for path := range allFiles {
|
context.TODO(),
|
||||||
allPaths = append(allPaths, path)
|
appSetGenerator.Git.RepoURL,
|
||||||
}
|
appSetGenerator.Git.Revision,
|
||||||
sort.Strings(allPaths)
|
project,
|
||||||
|
excludePattern,
|
||||||
// Generate params from each path, and return
|
noRevisionCache,
|
||||||
res := []map[string]interface{}{}
|
verifyCommit,
|
||||||
for _, path := range allPaths {
|
)
|
||||||
// A JSON / YAML file path can contain multiple sets of parameters (ie it is an array)
|
|
||||||
paramsArray, err := g.generateParamsFromGitFile(path, allFiles[path], appSetGenerator.Git.Values, useGoTemplate, goTemplateOptions, appSetGenerator.Git.PathParamPrefix)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to process file '%s': %w", path, err)
|
return nil, err
|
||||||
|
}
|
||||||
|
for absPath := range matchingFiles {
|
||||||
|
// if the file doesn't exist already and you try to delete it from the map
|
||||||
|
// the operation is a no-op. It’s safe and doesn't return an error or panic.
|
||||||
|
// Hence, we can simply try to delete the file from the path without checking
|
||||||
|
// if that file already exists in the map.
|
||||||
|
delete(fileContentMap, absPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, paramsArray...)
|
|
||||||
}
|
}
|
||||||
return res, nil
|
|
||||||
|
// Get a sorted list of file paths to ensure deterministic processing order
|
||||||
|
var filePaths []string
|
||||||
|
for path := range fileContentMap {
|
||||||
|
filePaths = append(filePaths, path)
|
||||||
|
}
|
||||||
|
sort.Strings(filePaths)
|
||||||
|
|
||||||
|
var allParams []map[string]any
|
||||||
|
for _, filePath := range filePaths {
|
||||||
|
// A JSON / YAML file path can contain multiple sets of parameters (ie it is an array)
|
||||||
|
paramsFromFileArray, err := g.generateParamsFromGitFile(filePath, fileContentMap[filePath], appSetGenerator.Git.Values, useGoTemplate, goTemplateOptions, appSetGenerator.Git.PathParamPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to process file '%s': %w", filePath, err)
|
||||||
|
}
|
||||||
|
allParams = append(allParams, paramsFromFileArray...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allParams, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, values map[string]string, useGoTemplate bool, goTemplateOptions []string, pathParamPrefix string) ([]map[string]interface{}, error) {
|
// generateParamsFromGitFile parses the content of a Git-tracked file and generates a slice of parameter maps.
|
||||||
objectsFound := []map[string]interface{}{}
|
// The file can contain a single YAML/JSON object or an array of such objects. Depending on the useGoTemplate flag,
|
||||||
|
// it either preserves structure for Go templating or flattens the objects for use as plain key-value parameters.
|
||||||
|
func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []byte, values map[string]string, useGoTemplate bool, goTemplateOptions []string, pathParamPrefix string) ([]map[string]any, error) {
|
||||||
|
objectsFound := []map[string]any{}
|
||||||
|
|
||||||
// First, we attempt to parse as an array
|
// First, we attempt to parse as a single object.
|
||||||
err := yaml.Unmarshal(fileContent, &objectsFound)
|
// This will also succeed for empty files.
|
||||||
if err != nil {
|
singleObj := map[string]any{}
|
||||||
// If unable to parse as an array, attempt to parse as a single object
|
err := yaml.Unmarshal(fileContent, &singleObj)
|
||||||
singleObj := make(map[string]interface{})
|
if err == nil {
|
||||||
err = yaml.Unmarshal(fileContent, &singleObj)
|
objectsFound = append(objectsFound, singleObj)
|
||||||
|
} else {
|
||||||
|
// If unable to parse as an object, try to parse as an array
|
||||||
|
err = yaml.Unmarshal(fileContent, &objectsFound)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to parse file: %w", err)
|
return nil, fmt.Errorf("unable to parse file: %w", err)
|
||||||
}
|
}
|
||||||
objectsFound = append(objectsFound, singleObj)
|
|
||||||
} else if len(objectsFound) == 0 {
|
|
||||||
// If file is valid but empty, add a default empty item
|
|
||||||
objectsFound = append(objectsFound, map[string]interface{}{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []map[string]interface{}{}
|
res := []map[string]any{}
|
||||||
|
|
||||||
for _, objectFound := range objectsFound {
|
for _, objectFound := range objectsFound {
|
||||||
params := map[string]interface{}{}
|
params := map[string]any{}
|
||||||
|
|
||||||
if useGoTemplate {
|
if useGoTemplate {
|
||||||
for k, v := range objectFound {
|
for k, v := range objectFound {
|
||||||
params[k] = v
|
params[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
paramPath := map[string]interface{}{}
|
paramPath := map[string]any{}
|
||||||
|
|
||||||
paramPath["path"] = path.Dir(filePath)
|
paramPath["path"] = path.Dir(filePath)
|
||||||
paramPath["basename"] = path.Base(paramPath["path"].(string))
|
paramPath["basename"] = path.Base(paramPath["path"].(string))
|
||||||
@@ -194,7 +255,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
|||||||
paramPath["filenameNormalized"] = utils.SanitizeName(path.Base(paramPath["filename"].(string)))
|
paramPath["filenameNormalized"] = utils.SanitizeName(path.Base(paramPath["filename"].(string)))
|
||||||
paramPath["segments"] = strings.Split(paramPath["path"].(string), "/")
|
paramPath["segments"] = strings.Split(paramPath["path"].(string), "/")
|
||||||
if pathParamPrefix != "" {
|
if pathParamPrefix != "" {
|
||||||
params[pathParamPrefix] = map[string]interface{}{"path": paramPath}
|
params[pathParamPrefix] = map[string]any{"path": paramPath}
|
||||||
} else {
|
} else {
|
||||||
params["path"] = paramPath
|
params["path"] = paramPath
|
||||||
}
|
}
|
||||||
@@ -216,7 +277,7 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
|||||||
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName].(string)))
|
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName].(string)))
|
||||||
params[pathParamName+".filenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName+".filename"].(string)))
|
params[pathParamName+".filenameNormalized"] = utils.SanitizeName(path.Base(params[pathParamName+".filename"].(string)))
|
||||||
for k, v := range strings.Split(params[pathParamName].(string), "/") {
|
for k, v := range strings.Split(params[pathParamName].(string), "/") {
|
||||||
if len(v) > 0 {
|
if v != "" {
|
||||||
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
|
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,8 +294,10 @@ func (g *GitGenerator) generateParamsFromGitFile(filePath string, fileContent []
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// filterApps filters the list of all application paths based on inclusion and exclusion rules
|
||||||
|
// defined in GitDirectoryGeneratorItems. Each item can either include or exclude matching paths.
|
||||||
func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string {
|
func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryGeneratorItem, allPaths []string) []string {
|
||||||
res := []string{}
|
var res []string
|
||||||
for _, appPath := range allPaths {
|
for _, appPath := range allPaths {
|
||||||
appInclude := false
|
appInclude := false
|
||||||
appExclude := false
|
appExclude := false
|
||||||
@@ -261,19 +324,22 @@ func (g *GitGenerator) filterApps(directories []argoprojiov1alpha1.GitDirectoryG
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]interface{}, error) {
|
// generateParamsFromApps generates a list of parameter maps based on the given app paths.
|
||||||
res := make([]map[string]interface{}, len(requestedApps))
|
// Each app path is converted into a parameter object with path metadata (basename, segments, etc.).
|
||||||
|
// It supports both Go templates and flat key-value parameters.
|
||||||
|
func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, useGoTemplate bool, goTemplateOptions []string) ([]map[string]any, error) {
|
||||||
|
res := make([]map[string]any, len(requestedApps))
|
||||||
for i, a := range requestedApps {
|
for i, a := range requestedApps {
|
||||||
params := make(map[string]interface{}, 5)
|
params := make(map[string]any, 5)
|
||||||
|
|
||||||
if useGoTemplate {
|
if useGoTemplate {
|
||||||
paramPath := map[string]interface{}{}
|
paramPath := map[string]any{}
|
||||||
paramPath["path"] = a
|
paramPath["path"] = a
|
||||||
paramPath["basename"] = path.Base(a)
|
paramPath["basename"] = path.Base(a)
|
||||||
paramPath["basenameNormalized"] = utils.SanitizeName(path.Base(a))
|
paramPath["basenameNormalized"] = utils.SanitizeName(path.Base(a))
|
||||||
paramPath["segments"] = strings.Split(paramPath["path"].(string), "/")
|
paramPath["segments"] = strings.Split(paramPath["path"].(string), "/")
|
||||||
if appSetGenerator.Git.PathParamPrefix != "" {
|
if appSetGenerator.Git.PathParamPrefix != "" {
|
||||||
params[appSetGenerator.Git.PathParamPrefix] = map[string]interface{}{"path": paramPath}
|
params[appSetGenerator.Git.PathParamPrefix] = map[string]any{"path": paramPath}
|
||||||
} else {
|
} else {
|
||||||
params["path"] = paramPath
|
params["path"] = paramPath
|
||||||
}
|
}
|
||||||
@@ -286,7 +352,7 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGene
|
|||||||
params[pathParamName+".basename"] = path.Base(a)
|
params[pathParamName+".basename"] = path.Base(a)
|
||||||
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(a))
|
params[pathParamName+".basenameNormalized"] = utils.SanitizeName(path.Base(a))
|
||||||
for k, v := range strings.Split(params[pathParamName].(string), "/") {
|
for k, v := range strings.Split(params[pathParamName].(string), "/") {
|
||||||
if len(v) > 0 {
|
if v != "" {
|
||||||
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
|
params[pathParamName+"["+strconv.Itoa(k)+"]"] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -302,3 +368,12 @@ func (g *GitGenerator) generateParamsFromApps(requestedApps []string, appSetGene
|
|||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveProjectName resolves a project name whether templated or not
|
||||||
|
func resolveProjectName(project string) string {
|
||||||
|
if strings.Contains(project, "{{") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return project
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
|||||||
package generators
|
package generators
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
"github.com/argoproj/argo-cd/v2/util/env"
|
"github.com/argoproj/argo-cd/v3/util/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generator defines the interface implemented by all ApplicationSet generators.
|
// Generator defines the interface implemented by all ApplicationSet generators.
|
||||||
@@ -15,7 +15,7 @@ type Generator interface {
|
|||||||
// GenerateParams interprets the ApplicationSet and generates all relevant parameters for the application template.
|
// GenerateParams interprets the ApplicationSet and generates all relevant parameters for the application template.
|
||||||
// The expected / desired list of parameters is returned, it then will be render and reconciled
|
// The expected / desired list of parameters is returned, it then will be render and reconciled
|
||||||
// against the current state of the Applications in the cluster.
|
// against the current state of the Applications in the cluster.
|
||||||
GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error)
|
GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error)
|
||||||
|
|
||||||
// GetRequeueAfter is the generator can controller the next reconciled loop
|
// GetRequeueAfter is the generator can controller the next reconciled loop
|
||||||
// In case there is more then one generator the time will be the minimum of the times.
|
// In case there is more then one generator the time will be the minimum of the times.
|
||||||
@@ -27,15 +27,15 @@ type Generator interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
EmptyAppSetGeneratorError = fmt.Errorf("ApplicationSet is empty")
|
ErrEmptyAppSetGenerator = errors.New("ApplicationSet is empty")
|
||||||
NoRequeueAfter time.Duration
|
NoRequeueAfter time.Duration
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultRequeueAfterSeconds = 3 * time.Minute
|
DefaultRequeueAfter = 3 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
func getDefaultRequeueAfter() time.Duration {
|
func getDefaultRequeueAfter() time.Duration {
|
||||||
// Default is 3 minutes, min is 1 second, max is 1 year
|
// Default is 3 minutes, min is 1 second, max is 1 year
|
||||||
return env.ParseDurationFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REQUEUE_AFTER", DefaultRequeueAfterSeconds, 1*time.Second, 8760*time.Hour)
|
return env.ParseDurationFromEnv("ARGOCD_APPLICATIONSET_CONTROLLER_REQUEUE_AFTER", DefaultRequeueAfter, 1*time.Second, 8760*time.Hour)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ func Test_getDefaultRequeueAfter(t *testing.T) {
|
|||||||
requeueAfterEnv string
|
requeueAfterEnv string
|
||||||
want time.Duration
|
want time.Duration
|
||||||
}{
|
}{
|
||||||
{name: "Default", requeueAfterEnv: "", want: DefaultRequeueAfterSeconds},
|
{name: "Default", requeueAfterEnv: "", want: DefaultRequeueAfter},
|
||||||
{name: "Min", requeueAfterEnv: "1s", want: 1 * time.Second},
|
{name: "Min", requeueAfterEnv: "1s", want: 1 * time.Second},
|
||||||
{name: "Max", requeueAfterEnv: "8760h", want: 8760 * time.Hour},
|
{name: "Max", requeueAfterEnv: "8760h", want: 8760 * time.Hour},
|
||||||
{name: "Override", requeueAfterEnv: "10m", want: 10 * time.Minute},
|
{name: "Override", requeueAfterEnv: "10m", want: 10 * time.Minute},
|
||||||
{name: "LessThanMin", requeueAfterEnv: "1ms", want: DefaultRequeueAfterSeconds},
|
{name: "LessThanMin", requeueAfterEnv: "1ms", want: DefaultRequeueAfter},
|
||||||
{name: "MoreThanMax", requeueAfterEnv: "8761h", want: DefaultRequeueAfterSeconds},
|
{name: "MoreThanMax", requeueAfterEnv: "8761h", want: DefaultRequeueAfter},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -2,13 +2,14 @@ package generators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*ListGenerator)(nil)
|
var _ Generator = (*ListGenerator)(nil)
|
||||||
@@ -20,7 +21,7 @@ func NewListGenerator() Generator {
|
|||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ListGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
func (g *ListGenerator) GetRequeueAfter(_ *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
||||||
return NoRequeueAfter
|
return NoRequeueAfter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,20 +29,20 @@ func (g *ListGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.Applicat
|
|||||||
return &appSetGenerator.List.Template
|
return &appSetGenerator.List.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.List == nil {
|
if appSetGenerator.List == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
res := make([]map[string]interface{}, len(appSetGenerator.List.Elements))
|
res := make([]map[string]any, len(appSetGenerator.List.Elements))
|
||||||
|
|
||||||
for i, tmpItem := range appSetGenerator.List.Elements {
|
for i, tmpItem := range appSetGenerator.List.Elements {
|
||||||
params := map[string]interface{}{}
|
params := map[string]any{}
|
||||||
var element map[string]interface{}
|
var element map[string]any
|
||||||
err := json.Unmarshal(tmpItem.Raw, &element)
|
err := json.Unmarshal(tmpItem.Raw, &element)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error unmarshling list element %w", err)
|
return nil, fmt.Errorf("error unmarshling list element %w", err)
|
||||||
@@ -52,16 +53,16 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli
|
|||||||
} else {
|
} else {
|
||||||
for key, value := range element {
|
for key, value := range element {
|
||||||
if key == "values" {
|
if key == "values" {
|
||||||
values, ok := (value).(map[string]interface{})
|
values, ok := (value).(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("error parsing values map")
|
return nil, errors.New("error parsing values map")
|
||||||
}
|
}
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
value, ok := v.(string)
|
value, ok := v.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("error parsing value as string %w", err)
|
return nil, fmt.Errorf("error parsing value as string %w", err)
|
||||||
}
|
}
|
||||||
params[fmt.Sprintf("values.%s", k)] = value
|
params["values."+k] = value
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
v, ok := value.(string)
|
v, ok := value.(string)
|
||||||
@@ -76,8 +77,8 @@ func (g *ListGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appli
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Append elements from ElementsYaml to the response
|
// Append elements from ElementsYaml to the response
|
||||||
if len(appSetGenerator.List.ElementsYaml) > 0 {
|
if appSetGenerator.List.ElementsYaml != "" {
|
||||||
var yamlElements []map[string]interface{}
|
var yamlElements []map[string]any
|
||||||
err := yaml.Unmarshal([]byte(appSetGenerator.List.ElementsYaml), &yamlElements)
|
err := yaml.Unmarshal([]byte(appSetGenerator.List.ElementsYaml), &yamlElements)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error unmarshling decoded ElementsYaml %w", err)
|
return nil, fmt.Errorf("error unmarshling decoded ElementsYaml %w", err)
|
||||||
|
|||||||
@@ -8,20 +8,20 @@ import (
|
|||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateListParams(t *testing.T) {
|
func TestGenerateListParams(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
elements []apiextensionsv1.JSON
|
elements []apiextensionsv1.JSON
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url"}},
|
||||||
}, {
|
}, {
|
||||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
|
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values.foo": "bar"}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url", "values.foo": "bar"}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,14 +49,14 @@ func TestGenerateListParams(t *testing.T) {
|
|||||||
func TestGenerateListParamsGoTemplate(t *testing.T) {
|
func TestGenerateListParamsGoTemplate(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
elements []apiextensionsv1.JSON
|
elements []apiextensionsv1.JSON
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url"}`)}},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url"}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url"}},
|
||||||
}, {
|
}, {
|
||||||
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
|
elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "cluster","url": "url","values":{"foo":"bar"}}`)}},
|
||||||
expected: []map[string]interface{}{{"cluster": "cluster", "url": "url", "values": map[string]interface{}{"foo": "bar"}}},
|
expected: []map[string]any{{"cluster": "cluster", "url": "url", "values": map[string]any{"foo": "bar"}}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
package generators
|
package generators
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
"dario.cat/mergo"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*MatrixGenerator)(nil)
|
var _ Generator = (*MatrixGenerator)(nil)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrMoreThanTwoGenerators = fmt.Errorf("found more than two generators, Matrix support only two")
|
ErrMoreThanTwoGenerators = errors.New("found more than two generators, Matrix support only two")
|
||||||
ErrLessThanTwoGenerators = fmt.Errorf("found less than two generators, Matrix support only two")
|
ErrLessThanTwoGenerators = errors.New("found less than two generators, Matrix support only two")
|
||||||
ErrMoreThenOneInnerGenerators = fmt.Errorf("found more than one generator in matrix.Generators")
|
ErrMoreThenOneInnerGenerators = errors.New("found more than one generator in matrix.Generators")
|
||||||
)
|
)
|
||||||
|
|
||||||
type MatrixGenerator struct {
|
type MatrixGenerator struct {
|
||||||
@@ -33,9 +32,9 @@ func NewMatrixGenerator(supportedGenerators map[string]Generator) Generator {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) {
|
func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator.Matrix == nil {
|
if appSetGenerator.Matrix == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(appSetGenerator.Matrix.Generators) < 2 {
|
if len(appSetGenerator.Matrix.Generators) < 2 {
|
||||||
@@ -46,7 +45,7 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
|||||||
return nil, ErrMoreThanTwoGenerators
|
return nil, ErrMoreThanTwoGenerators
|
||||||
}
|
}
|
||||||
|
|
||||||
res := []map[string]interface{}{}
|
res := []map[string]any{}
|
||||||
|
|
||||||
g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil, client)
|
g0, err := m.getParams(appSetGenerator.Matrix.Generators[0], appSet, nil, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -59,7 +58,7 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
|||||||
}
|
}
|
||||||
for _, b := range g1 {
|
for _, b := range g1 {
|
||||||
if appSet.Spec.GoTemplate {
|
if appSet.Spec.GoTemplate {
|
||||||
tmp := map[string]interface{}{}
|
tmp := map[string]any{}
|
||||||
if err := mergo.Merge(&tmp, b, mergo.WithOverride); err != nil {
|
if err := mergo.Merge(&tmp, b, mergo.WithOverride); err != nil {
|
||||||
return nil, fmt.Errorf("failed to merge params from the second generator in the matrix generator with temp map: %w", err)
|
return nil, fmt.Errorf("failed to merge params from the second generator in the matrix generator with temp map: %w", err)
|
||||||
}
|
}
|
||||||
@@ -72,7 +71,7 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to combine string maps with merging params for the matrix generator: %w", err)
|
return nil, fmt.Errorf("failed to combine string maps with merging params for the matrix generator: %w", err)
|
||||||
}
|
}
|
||||||
res = append(res, utils.ConvertToMapStringInterface(val))
|
res = append(res, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,27 +79,15 @@ func (m *MatrixGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.App
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, params map[string]interface{}, client client.Client) ([]map[string]interface{}, error) {
|
func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, params map[string]any, client client.Client) ([]map[string]any, error) {
|
||||||
matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
|
matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if matrixGen != nil && !appSet.Spec.ApplyNestedSelectors {
|
|
||||||
foundSelector := dropDisabledNestedSelectors(matrixGen.Generators)
|
|
||||||
if foundSelector {
|
|
||||||
log.Warnf("AppSet '%v' defines selector on nested matrix generator's generator without enabling them via 'spec.applyNestedSelectors', ignoring nested selectors", appSet.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mergeGen, err := getMergeGenerator(appSetBaseGenerator)
|
mergeGen, err := getMergeGenerator(appSetBaseGenerator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error retrieving merge generator: %w", err)
|
return nil, fmt.Errorf("error retrieving merge generator: %w", err)
|
||||||
}
|
}
|
||||||
if mergeGen != nil && !appSet.Spec.ApplyNestedSelectors {
|
|
||||||
foundSelector := dropDisabledNestedSelectors(mergeGen.Generators)
|
|
||||||
if foundSelector {
|
|
||||||
log.Warnf("AppSet '%v' defines selector on nested merge generator's generator without enabling them via 'spec.applyNestedSelectors', ignoring nested selectors", appSet.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := Transform(
|
t, err := Transform(
|
||||||
argoprojiov1alpha1.ApplicationSetGenerator{
|
argoprojiov1alpha1.ApplicationSetGenerator{
|
||||||
@@ -125,7 +112,7 @@ func (m *MatrixGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Appli
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(t) == 0 {
|
if len(t) == 0 {
|
||||||
return nil, fmt.Errorf("child generator generated no parameters")
|
return nil, errors.New("child generator generated no parameters")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(t) > 1 {
|
if len(t) > 1 {
|
||||||
@@ -168,9 +155,8 @@ func (m *MatrixGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap
|
|||||||
|
|
||||||
if found {
|
if found {
|
||||||
return res
|
return res
|
||||||
} else {
|
|
||||||
return NoRequeueAfter
|
|
||||||
}
|
}
|
||||||
|
return NoRequeueAfter
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMatrixGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MatrixGenerator, error) {
|
func getMatrixGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MatrixGenerator, error) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package generators
|
package generators
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -13,36 +12,35 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/mocks"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/mocks"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMatrixGenerate(t *testing.T) {
|
func TestMatrixGenerate(t *testing.T) {
|
||||||
gitGenerator := &argoprojiov1alpha1.GitGenerator{
|
gitGenerator := &v1alpha1.GitGenerator{
|
||||||
RepoURL: "RepoURL",
|
RepoURL: "RepoURL",
|
||||||
Revision: "Revision",
|
Revision: "Revision",
|
||||||
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
Directories: []v1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
listGenerator := &argoprojiov1alpha1.ListGenerator{
|
listGenerator := &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url", "templated": "test-{{path.basenameNormalized}}"}`)}},
|
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url", "templated": "test-{{path.basenameNormalized}}"}`)}},
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
baseGenerators []v1alpha1.ApplicationSetNestedGenerator
|
||||||
expectedErr error
|
expectedErr error
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "happy flow - generate params",
|
name: "happy flow - generate params",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -50,16 +48,16 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url", "templated": "test-app1"},
|
{"path": "app1", "path.basename": "app1", "path.basenameNormalized": "app1", "cluster": "Cluster", "url": "Url", "templated": "test-app1"},
|
||||||
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url", "templated": "test-app2"},
|
{"path": "app2", "path.basename": "app2", "path.basenameNormalized": "app2", "cluster": "Cluster", "url": "Url", "templated": "test-app2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "happy flow - generate params from two lists",
|
name: "happy flow - generate params from two lists",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
List: &argoprojiov1alpha1.ListGenerator{
|
List: &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{
|
Elements: []apiextensionsv1.JSON{
|
||||||
{Raw: []byte(`{"a": "1"}`)},
|
{Raw: []byte(`{"a": "1"}`)},
|
||||||
{Raw: []byte(`{"a": "2"}`)},
|
{Raw: []byte(`{"a": "2"}`)},
|
||||||
@@ -67,7 +65,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
List: &argoprojiov1alpha1.ListGenerator{
|
List: &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{
|
Elements: []apiextensionsv1.JSON{
|
||||||
{Raw: []byte(`{"b": "1"}`)},
|
{Raw: []byte(`{"b": "1"}`)},
|
||||||
{Raw: []byte(`{"b": "2"}`)},
|
{Raw: []byte(`{"b": "2"}`)},
|
||||||
@@ -75,7 +73,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"a": "1", "b": "1"},
|
{"a": "1", "b": "1"},
|
||||||
{"a": "1", "b": "2"},
|
{"a": "1", "b": "2"},
|
||||||
{"a": "2", "b": "1"},
|
{"a": "2", "b": "1"},
|
||||||
@@ -84,7 +82,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns error if there is less than two base generators",
|
name: "returns error if there is less than two base generators",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -93,7 +91,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns error if there is more than two base generators",
|
name: "returns error if there is more than two base generators",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
},
|
},
|
||||||
@@ -108,7 +106,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns error if there is more than one inner generator in the first base generator",
|
name: "returns error if there is more than one inner generator in the first base generator",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
@@ -121,7 +119,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns error if there is more than one inner generator in the second base generator",
|
name: "returns error if there is more than one inner generator in the second base generator",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
},
|
},
|
||||||
@@ -139,19 +137,19 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorMock{}
|
genMock := &generatorMock{}
|
||||||
appSet := &argoprojiov1alpha1.ApplicationSet{
|
appSet := &v1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "set",
|
Name: "set",
|
||||||
},
|
},
|
||||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{},
|
Spec: v1alpha1.ApplicationSetSpec{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, g := range testCaseCopy.baseGenerators {
|
for _, g := range testCaseCopy.baseGenerators {
|
||||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
List: g.List,
|
List: g.List,
|
||||||
}
|
}
|
||||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]interface{}{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{
|
||||||
{
|
{
|
||||||
"path": "app1",
|
"path": "app1",
|
||||||
"path.basename": "app1",
|
"path.basename": "app1",
|
||||||
@@ -165,7 +163,7 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixGenerator := NewMatrixGenerator(
|
matrixGenerator := NewMatrixGenerator(
|
||||||
@@ -175,10 +173,10 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
got, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
got, err := matrixGenerator.GenerateParams(&v1alpha1.ApplicationSetGenerator{
|
||||||
Matrix: &argoprojiov1alpha1.MatrixGenerator{
|
Matrix: &v1alpha1.MatrixGenerator{
|
||||||
Generators: testCaseCopy.baseGenerators,
|
Generators: testCaseCopy.baseGenerators,
|
||||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
Template: v1alpha1.ApplicationSetTemplate{},
|
||||||
},
|
},
|
||||||
}, appSet, nil)
|
}, appSet, nil)
|
||||||
|
|
||||||
@@ -193,25 +191,25 @@ func TestMatrixGenerate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMatrixGenerateGoTemplate(t *testing.T) {
|
func TestMatrixGenerateGoTemplate(t *testing.T) {
|
||||||
gitGenerator := &argoprojiov1alpha1.GitGenerator{
|
gitGenerator := &v1alpha1.GitGenerator{
|
||||||
RepoURL: "RepoURL",
|
RepoURL: "RepoURL",
|
||||||
Revision: "Revision",
|
Revision: "Revision",
|
||||||
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
Directories: []v1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
listGenerator := &argoprojiov1alpha1.ListGenerator{
|
listGenerator := &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
|
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
baseGenerators []v1alpha1.ApplicationSetNestedGenerator
|
||||||
expectedErr error
|
expectedErr error
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "happy flow - generate params",
|
name: "happy flow - generate params",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -219,7 +217,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"path": map[string]string{
|
"path": map[string]string{
|
||||||
"path": "app1",
|
"path": "app1",
|
||||||
@@ -242,9 +240,9 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "happy flow - generate params from two lists",
|
name: "happy flow - generate params from two lists",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
List: &argoprojiov1alpha1.ListGenerator{
|
List: &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{
|
Elements: []apiextensionsv1.JSON{
|
||||||
{Raw: []byte(`{"a": "1"}`)},
|
{Raw: []byte(`{"a": "1"}`)},
|
||||||
{Raw: []byte(`{"a": "2"}`)},
|
{Raw: []byte(`{"a": "2"}`)},
|
||||||
@@ -252,7 +250,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
List: &argoprojiov1alpha1.ListGenerator{
|
List: &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{
|
Elements: []apiextensionsv1.JSON{
|
||||||
{Raw: []byte(`{"b": "1"}`)},
|
{Raw: []byte(`{"b": "1"}`)},
|
||||||
{Raw: []byte(`{"b": "2"}`)},
|
{Raw: []byte(`{"b": "2"}`)},
|
||||||
@@ -260,7 +258,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"a": "1", "b": "1"},
|
{"a": "1", "b": "1"},
|
||||||
{"a": "1", "b": "2"},
|
{"a": "1", "b": "2"},
|
||||||
{"a": "2", "b": "1"},
|
{"a": "2", "b": "1"},
|
||||||
@@ -269,29 +267,29 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "parameter override: first list elements take precedence",
|
name: "parameter override: first list elements take precedence",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
List: &argoprojiov1alpha1.ListGenerator{
|
List: &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{
|
Elements: []apiextensionsv1.JSON{
|
||||||
{Raw: []byte(`{"booleanFalse": false, "booleanTrue": true, "stringFalse": "false", "stringTrue": "true"}`)},
|
{Raw: []byte(`{"booleanFalse": false, "booleanTrue": true, "stringFalse": "false", "stringTrue": "true"}`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
List: &argoprojiov1alpha1.ListGenerator{
|
List: &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{
|
Elements: []apiextensionsv1.JSON{
|
||||||
{Raw: []byte(`{"booleanFalse": true, "booleanTrue": false, "stringFalse": "true", "stringTrue": "false"}`)},
|
{Raw: []byte(`{"booleanFalse": true, "booleanTrue": false, "stringFalse": "true", "stringTrue": "false"}`)},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"booleanFalse": false, "booleanTrue": true, "stringFalse": "false", "stringTrue": "true"},
|
{"booleanFalse": false, "booleanTrue": true, "stringFalse": "false", "stringTrue": "true"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns error if there is less than two base generators",
|
name: "returns error if there is less than two base generators",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -300,7 +298,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns error if there is more than two base generators",
|
name: "returns error if there is more than two base generators",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
},
|
},
|
||||||
@@ -315,7 +313,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns error if there is more than one inner generator in the first base generator",
|
name: "returns error if there is more than one inner generator in the first base generator",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
@@ -328,7 +326,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns error if there is more than one inner generator in the second base generator",
|
name: "returns error if there is more than one inner generator in the second base generator",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
},
|
},
|
||||||
@@ -346,21 +344,21 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorMock{}
|
genMock := &generatorMock{}
|
||||||
appSet := &argoprojiov1alpha1.ApplicationSet{
|
appSet := &v1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "set",
|
Name: "set",
|
||||||
},
|
},
|
||||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
Spec: v1alpha1.ApplicationSetSpec{
|
||||||
GoTemplate: true,
|
GoTemplate: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, g := range testCaseCopy.baseGenerators {
|
for _, g := range testCaseCopy.baseGenerators {
|
||||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
List: g.List,
|
List: g.List,
|
||||||
}
|
}
|
||||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]interface{}{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet, mock.Anything).Return([]map[string]any{
|
||||||
{
|
{
|
||||||
"path": map[string]string{
|
"path": map[string]string{
|
||||||
"path": "app1",
|
"path": "app1",
|
||||||
@@ -378,7 +376,7 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixGenerator := NewMatrixGenerator(
|
matrixGenerator := NewMatrixGenerator(
|
||||||
@@ -388,10 +386,10 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
got, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
got, err := matrixGenerator.GenerateParams(&v1alpha1.ApplicationSetGenerator{
|
||||||
Matrix: &argoprojiov1alpha1.MatrixGenerator{
|
Matrix: &v1alpha1.MatrixGenerator{
|
||||||
Generators: testCaseCopy.baseGenerators,
|
Generators: testCaseCopy.baseGenerators,
|
||||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
Template: v1alpha1.ApplicationSetTemplate{},
|
||||||
},
|
},
|
||||||
}, appSet, nil)
|
}, appSet, nil)
|
||||||
|
|
||||||
@@ -406,31 +404,31 @@ func TestMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMatrixGetRequeueAfter(t *testing.T) {
|
func TestMatrixGetRequeueAfter(t *testing.T) {
|
||||||
gitGenerator := &argoprojiov1alpha1.GitGenerator{
|
gitGenerator := &v1alpha1.GitGenerator{
|
||||||
RepoURL: "RepoURL",
|
RepoURL: "RepoURL",
|
||||||
Revision: "Revision",
|
Revision: "Revision",
|
||||||
Directories: []argoprojiov1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
Directories: []v1alpha1.GitDirectoryGeneratorItem{{Path: "*"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
listGenerator := &argoprojiov1alpha1.ListGenerator{
|
listGenerator := &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
|
Elements: []apiextensionsv1.JSON{{Raw: []byte(`{"cluster": "Cluster","url": "Url"}`)}},
|
||||||
}
|
}
|
||||||
|
|
||||||
pullRequestGenerator := &argoprojiov1alpha1.PullRequestGenerator{}
|
pullRequestGenerator := &v1alpha1.PullRequestGenerator{}
|
||||||
|
|
||||||
scmGenerator := &argoprojiov1alpha1.SCMProviderGenerator{}
|
scmGenerator := &v1alpha1.SCMProviderGenerator{}
|
||||||
|
|
||||||
duckTypeGenerator := &argoprojiov1alpha1.DuckTypeGenerator{}
|
duckTypeGenerator := &v1alpha1.DuckTypeGenerator{}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
baseGenerators []v1alpha1.ApplicationSetNestedGenerator
|
||||||
gitGetRequeueAfter time.Duration
|
gitGetRequeueAfter time.Duration
|
||||||
expected time.Duration
|
expected time.Duration
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "return NoRequeueAfter if all the inner baseGenerators returns it",
|
name: "return NoRequeueAfter if all the inner baseGenerators returns it",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -443,7 +441,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns the minimal time",
|
name: "returns the minimal time",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -456,7 +454,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns the minimal time for pull request",
|
name: "returns the minimal time for pull request",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -469,7 +467,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns the default time if no requeueAfterSeconds is provided",
|
name: "returns the default time if no requeueAfterSeconds is provided",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -481,7 +479,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns the default time for duck type generator",
|
name: "returns the default time for duck type generator",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -493,7 +491,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "returns the default time for scm generator",
|
name: "returns the default time for scm generator",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -512,7 +510,7 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
mock := &generatorMock{}
|
mock := &generatorMock{}
|
||||||
|
|
||||||
for _, g := range testCaseCopy.baseGenerators {
|
for _, g := range testCaseCopy.baseGenerators {
|
||||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
List: g.List,
|
List: g.List,
|
||||||
PullRequest: g.PullRequest,
|
PullRequest: g.PullRequest,
|
||||||
@@ -532,10 +530,10 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
got := matrixGenerator.GetRequeueAfter(&argoprojiov1alpha1.ApplicationSetGenerator{
|
got := matrixGenerator.GetRequeueAfter(&v1alpha1.ApplicationSetGenerator{
|
||||||
Matrix: &argoprojiov1alpha1.MatrixGenerator{
|
Matrix: &v1alpha1.MatrixGenerator{
|
||||||
Generators: testCaseCopy.baseGenerators,
|
Generators: testCaseCopy.baseGenerators,
|
||||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
Template: v1alpha1.ApplicationSetTemplate{},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -545,16 +543,16 @@ func TestMatrixGetRequeueAfter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInterpolatedMatrixGenerate(t *testing.T) {
|
func TestInterpolatedMatrixGenerate(t *testing.T) {
|
||||||
interpolatedGitGenerator := &argoprojiov1alpha1.GitGenerator{
|
interpolatedGitGenerator := &v1alpha1.GitGenerator{
|
||||||
RepoURL: "RepoURL",
|
RepoURL: "RepoURL",
|
||||||
Revision: "Revision",
|
Revision: "Revision",
|
||||||
Files: []argoprojiov1alpha1.GitFileGeneratorItem{
|
Files: []v1alpha1.GitFileGeneratorItem{
|
||||||
{Path: "examples/git-generator-files-discovery/cluster-config/dev/config.json"},
|
{Path: "examples/git-generator-files-discovery/cluster-config/dev/config.json"},
|
||||||
{Path: "examples/git-generator-files-discovery/cluster-config/prod/config.json"},
|
{Path: "examples/git-generator-files-discovery/cluster-config/prod/config.json"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
interpolatedClusterGenerator := &argoprojiov1alpha1.ClusterGenerator{
|
interpolatedClusterGenerator := &v1alpha1.ClusterGenerator{
|
||||||
Selector: metav1.LabelSelector{
|
Selector: metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{"environment": "{{path.basename}}"},
|
MatchLabels: map[string]string{"environment": "{{path.basename}}"},
|
||||||
MatchExpressions: nil,
|
MatchExpressions: nil,
|
||||||
@@ -562,14 +560,14 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
baseGenerators []v1alpha1.ApplicationSetNestedGenerator
|
||||||
expectedErr error
|
expectedErr error
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
clientError bool
|
clientError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "happy flow - generate interpolated params",
|
name: "happy flow - generate interpolated params",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: interpolatedGitGenerator,
|
Git: interpolatedGitGenerator,
|
||||||
},
|
},
|
||||||
@@ -577,7 +575,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
Clusters: interpolatedClusterGenerator,
|
Clusters: interpolatedClusterGenerator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json", "path.basename": "dev", "path.basenameNormalized": "dev", "name": "dev-01", "nameNormalized": "dev-01", "server": "https://dev-01.example.com", "metadata.labels.environment": "dev", "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "project": ""},
|
{"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json", "path.basename": "dev", "path.basenameNormalized": "dev", "name": "dev-01", "nameNormalized": "dev-01", "server": "https://dev-01.example.com", "metadata.labels.environment": "dev", "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "project": ""},
|
||||||
{"path": "examples/git-generator-files-discovery/cluster-config/prod/config.json", "path.basename": "prod", "path.basenameNormalized": "prod", "name": "prod-01", "nameNormalized": "prod-01", "server": "https://prod-01.example.com", "metadata.labels.environment": "prod", "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "project": ""},
|
{"path": "examples/git-generator-files-discovery/cluster-config/prod/config.json", "path.basename": "prod", "path.basenameNormalized": "prod", "name": "prod-01", "nameNormalized": "prod-01", "server": "https://prod-01.example.com", "metadata.labels.environment": "prod", "metadata.labels.argocd.argoproj.io/secret-type": "cluster", "project": ""},
|
||||||
},
|
},
|
||||||
@@ -637,7 +635,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorMock{}
|
genMock := &generatorMock{}
|
||||||
appSet := &argoprojiov1alpha1.ApplicationSet{}
|
appSet := &v1alpha1.ApplicationSet{}
|
||||||
|
|
||||||
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
appClientset := kubefake.NewSimpleClientset(runtimeClusters...)
|
||||||
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
fakeClient := fake.NewClientBuilder().WithObjects(clusters...).Build()
|
||||||
@@ -645,14 +643,14 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
fakeClient,
|
fakeClient,
|
||||||
testCase.clientError,
|
testCase.clientError,
|
||||||
}
|
}
|
||||||
clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
|
||||||
|
|
||||||
for _, g := range testCaseCopy.baseGenerators {
|
for _, g := range testCaseCopy.baseGenerators {
|
||||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
Clusters: g.Clusters,
|
Clusters: g.Clusters,
|
||||||
}
|
}
|
||||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]interface{}{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{
|
||||||
{
|
{
|
||||||
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
||||||
"path.basename": "dev",
|
"path.basename": "dev",
|
||||||
@@ -665,7 +663,7 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
matrixGenerator := NewMatrixGenerator(
|
matrixGenerator := NewMatrixGenerator(
|
||||||
map[string]Generator{
|
map[string]Generator{
|
||||||
@@ -674,10 +672,10 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
got, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
got, err := matrixGenerator.GenerateParams(&v1alpha1.ApplicationSetGenerator{
|
||||||
Matrix: &argoprojiov1alpha1.MatrixGenerator{
|
Matrix: &v1alpha1.MatrixGenerator{
|
||||||
Generators: testCaseCopy.baseGenerators,
|
Generators: testCaseCopy.baseGenerators,
|
||||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
Template: v1alpha1.ApplicationSetTemplate{},
|
||||||
},
|
},
|
||||||
}, appSet, nil)
|
}, appSet, nil)
|
||||||
|
|
||||||
@@ -692,16 +690,16 @@ func TestInterpolatedMatrixGenerate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
||||||
interpolatedGitGenerator := &argoprojiov1alpha1.GitGenerator{
|
interpolatedGitGenerator := &v1alpha1.GitGenerator{
|
||||||
RepoURL: "RepoURL",
|
RepoURL: "RepoURL",
|
||||||
Revision: "Revision",
|
Revision: "Revision",
|
||||||
Files: []argoprojiov1alpha1.GitFileGeneratorItem{
|
Files: []v1alpha1.GitFileGeneratorItem{
|
||||||
{Path: "examples/git-generator-files-discovery/cluster-config/dev/config.json"},
|
{Path: "examples/git-generator-files-discovery/cluster-config/dev/config.json"},
|
||||||
{Path: "examples/git-generator-files-discovery/cluster-config/prod/config.json"},
|
{Path: "examples/git-generator-files-discovery/cluster-config/prod/config.json"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
interpolatedClusterGenerator := &argoprojiov1alpha1.ClusterGenerator{
|
interpolatedClusterGenerator := &v1alpha1.ClusterGenerator{
|
||||||
Selector: metav1.LabelSelector{
|
Selector: metav1.LabelSelector{
|
||||||
MatchLabels: map[string]string{"environment": "{{.path.basename}}"},
|
MatchLabels: map[string]string{"environment": "{{.path.basename}}"},
|
||||||
MatchExpressions: nil,
|
MatchExpressions: nil,
|
||||||
@@ -709,14 +707,14 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
baseGenerators []v1alpha1.ApplicationSetNestedGenerator
|
||||||
expectedErr error
|
expectedErr error
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
clientError bool
|
clientError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "happy flow - generate interpolated params",
|
name: "happy flow - generate interpolated params",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: interpolatedGitGenerator,
|
Git: interpolatedGitGenerator,
|
||||||
},
|
},
|
||||||
@@ -724,7 +722,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
Clusters: interpolatedClusterGenerator,
|
Clusters: interpolatedClusterGenerator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"path": map[string]string{
|
"path": map[string]string{
|
||||||
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
||||||
@@ -735,7 +733,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
"nameNormalized": "dev-01",
|
"nameNormalized": "dev-01",
|
||||||
"server": "https://dev-01.example.com",
|
"server": "https://dev-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"environment": "dev",
|
"environment": "dev",
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
@@ -752,7 +750,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
"nameNormalized": "prod-01",
|
"nameNormalized": "prod-01",
|
||||||
"server": "https://prod-01.example.com",
|
"server": "https://prod-01.example.com",
|
||||||
"project": "",
|
"project": "",
|
||||||
"metadata": map[string]interface{}{
|
"metadata": map[string]any{
|
||||||
"labels": map[string]string{
|
"labels": map[string]string{
|
||||||
"environment": "prod",
|
"environment": "prod",
|
||||||
"argocd.argoproj.io/secret-type": "cluster",
|
"argocd.argoproj.io/secret-type": "cluster",
|
||||||
@@ -816,8 +814,8 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorMock{}
|
genMock := &generatorMock{}
|
||||||
appSet := &argoprojiov1alpha1.ApplicationSet{
|
appSet := &v1alpha1.ApplicationSet{
|
||||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
Spec: v1alpha1.ApplicationSetSpec{
|
||||||
GoTemplate: true,
|
GoTemplate: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -828,14 +826,14 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
fakeClient,
|
fakeClient,
|
||||||
testCase.clientError,
|
testCase.clientError,
|
||||||
}
|
}
|
||||||
clusterGenerator := NewClusterGenerator(cl, context.Background(), appClientset, "namespace")
|
clusterGenerator := NewClusterGenerator(t.Context(), cl, appClientset, "namespace")
|
||||||
|
|
||||||
for _, g := range testCaseCopy.baseGenerators {
|
for _, g := range testCaseCopy.baseGenerators {
|
||||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
Clusters: g.Clusters,
|
Clusters: g.Clusters,
|
||||||
}
|
}
|
||||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]interface{}{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{
|
||||||
{
|
{
|
||||||
"path": map[string]string{
|
"path": map[string]string{
|
||||||
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
"path": "examples/git-generator-files-discovery/cluster-config/dev/config.json",
|
||||||
@@ -852,7 +850,7 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
matrixGenerator := NewMatrixGenerator(
|
matrixGenerator := NewMatrixGenerator(
|
||||||
map[string]Generator{
|
map[string]Generator{
|
||||||
@@ -861,10 +859,10 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
got, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
got, err := matrixGenerator.GenerateParams(&v1alpha1.ApplicationSetGenerator{
|
||||||
Matrix: &argoprojiov1alpha1.MatrixGenerator{
|
Matrix: &v1alpha1.MatrixGenerator{
|
||||||
Generators: testCaseCopy.baseGenerators,
|
Generators: testCaseCopy.baseGenerators,
|
||||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
Template: v1alpha1.ApplicationSetTemplate{},
|
||||||
},
|
},
|
||||||
}, appSet, nil)
|
}, appSet, nil)
|
||||||
|
|
||||||
@@ -879,28 +877,28 @@ func TestInterpolatedMatrixGenerateGoTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
||||||
gitGenerator := &argoprojiov1alpha1.GitGenerator{
|
gitGenerator := &v1alpha1.GitGenerator{
|
||||||
RepoURL: "RepoURL",
|
RepoURL: "RepoURL",
|
||||||
Revision: "Revision",
|
Revision: "Revision",
|
||||||
Files: []argoprojiov1alpha1.GitFileGeneratorItem{
|
Files: []v1alpha1.GitFileGeneratorItem{
|
||||||
{Path: "config.yaml"},
|
{Path: "config.yaml"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
listGenerator := &argoprojiov1alpha1.ListGenerator{
|
listGenerator := &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{},
|
Elements: []apiextensionsv1.JSON{},
|
||||||
ElementsYaml: "{{ .foo.bar | toJson }}",
|
ElementsYaml: "{{ .foo.bar | toJson }}",
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
baseGenerators []v1alpha1.ApplicationSetNestedGenerator
|
||||||
expectedErr error
|
expectedErr error
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "happy flow - generate params",
|
name: "happy flow - generate params",
|
||||||
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
baseGenerators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
Git: gitGenerator,
|
Git: gitGenerator,
|
||||||
},
|
},
|
||||||
@@ -908,23 +906,23 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
List: listGenerator,
|
List: listGenerator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"chart": "a",
|
"chart": "a",
|
||||||
"version": "1",
|
"version": "1",
|
||||||
"foo": map[string]interface{}{
|
"foo": map[string]any{
|
||||||
"bar": []interface{}{
|
"bar": []any{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"chart": "a",
|
"chart": "a",
|
||||||
"version": "1",
|
"version": "1",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"chart": "b",
|
"chart": "b",
|
||||||
"version": "2",
|
"version": "2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"path": map[string]interface{}{
|
"path": map[string]any{
|
||||||
"basename": "dir",
|
"basename": "dir",
|
||||||
"basenameNormalized": "dir",
|
"basenameNormalized": "dir",
|
||||||
"filename": "file_name.yaml",
|
"filename": "file_name.yaml",
|
||||||
@@ -939,19 +937,19 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"chart": "b",
|
"chart": "b",
|
||||||
"version": "2",
|
"version": "2",
|
||||||
"foo": map[string]interface{}{
|
"foo": map[string]any{
|
||||||
"bar": []interface{}{
|
"bar": []any{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"chart": "a",
|
"chart": "a",
|
||||||
"version": "1",
|
"version": "1",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"chart": "b",
|
"chart": "b",
|
||||||
"version": "2",
|
"version": "2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"path": map[string]interface{}{
|
"path": map[string]any{
|
||||||
"basename": "dir",
|
"basename": "dir",
|
||||||
"basenameNormalized": "dir",
|
"basenameNormalized": "dir",
|
||||||
"filename": "file_name.yaml",
|
"filename": "file_name.yaml",
|
||||||
@@ -972,34 +970,34 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
|
|
||||||
t.Run(testCaseCopy.name, func(t *testing.T) {
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
||||||
genMock := &generatorMock{}
|
genMock := &generatorMock{}
|
||||||
appSet := &argoprojiov1alpha1.ApplicationSet{
|
appSet := &v1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "set",
|
Name: "set",
|
||||||
},
|
},
|
||||||
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
Spec: v1alpha1.ApplicationSetSpec{
|
||||||
GoTemplate: true,
|
GoTemplate: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, g := range testCaseCopy.baseGenerators {
|
for _, g := range testCaseCopy.baseGenerators {
|
||||||
gitGeneratorSpec := argoprojiov1alpha1.ApplicationSetGenerator{
|
gitGeneratorSpec := v1alpha1.ApplicationSetGenerator{
|
||||||
Git: g.Git,
|
Git: g.Git,
|
||||||
List: g.List,
|
List: g.List,
|
||||||
}
|
}
|
||||||
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{{
|
genMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), appSet).Return([]map[string]any{{
|
||||||
"foo": map[string]interface{}{
|
"foo": map[string]any{
|
||||||
"bar": []interface{}{
|
"bar": []any{
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"chart": "a",
|
"chart": "a",
|
||||||
"version": "1",
|
"version": "1",
|
||||||
},
|
},
|
||||||
map[string]interface{}{
|
map[string]any{
|
||||||
"chart": "b",
|
"chart": "b",
|
||||||
"version": "2",
|
"version": "2",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"path": map[string]interface{}{
|
"path": map[string]any{
|
||||||
"basename": "dir",
|
"basename": "dir",
|
||||||
"basenameNormalized": "dir",
|
"basenameNormalized": "dir",
|
||||||
"filename": "file_name.yaml",
|
"filename": "file_name.yaml",
|
||||||
@@ -1012,7 +1010,7 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}}, nil)
|
}}, nil)
|
||||||
genMock.On("GetTemplate", &gitGeneratorSpec).
|
genMock.On("GetTemplate", &gitGeneratorSpec).
|
||||||
Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
}
|
}
|
||||||
|
|
||||||
matrixGenerator := NewMatrixGenerator(
|
matrixGenerator := NewMatrixGenerator(
|
||||||
@@ -1022,10 +1020,10 @@ func TestMatrixGenerateListElementsYaml(t *testing.T) {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
got, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
got, err := matrixGenerator.GenerateParams(&v1alpha1.ApplicationSetGenerator{
|
||||||
Matrix: &argoprojiov1alpha1.MatrixGenerator{
|
Matrix: &v1alpha1.MatrixGenerator{
|
||||||
Generators: testCaseCopy.baseGenerators,
|
Generators: testCaseCopy.baseGenerators,
|
||||||
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
Template: v1alpha1.ApplicationSetTemplate{},
|
||||||
},
|
},
|
||||||
}, appSet, nil)
|
}, appSet, nil)
|
||||||
|
|
||||||
@@ -1043,19 +1041,19 @@ type generatorMock struct {
|
|||||||
mock.Mock
|
mock.Mock
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *generatorMock) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *generatorMock) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate {
|
||||||
args := g.Called(appSetGenerator)
|
args := g.Called(appSetGenerator)
|
||||||
|
|
||||||
return args.Get(0).(*argoprojiov1alpha1.ApplicationSetTemplate)
|
return args.Get(0).(*v1alpha1.ApplicationSetTemplate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *generatorMock) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
func (g *generatorMock) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, appSet *v1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
args := g.Called(appSetGenerator, appSet)
|
args := g.Called(appSetGenerator, appSet)
|
||||||
|
|
||||||
return args.Get(0).([]map[string]interface{}), args.Error(1)
|
return args.Get(0).([]map[string]any), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *generatorMock) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) time.Duration {
|
func (g *generatorMock) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration {
|
||||||
args := g.Called(appSetGenerator)
|
args := g.Called(appSetGenerator)
|
||||||
|
|
||||||
return args.Get(0).(time.Duration)
|
return args.Get(0).(time.Duration)
|
||||||
@@ -1075,20 +1073,20 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
|||||||
// of that bug.
|
// of that bug.
|
||||||
|
|
||||||
listGeneratorMock := &generatorMock{}
|
listGeneratorMock := &generatorMock{}
|
||||||
listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]interface{}{
|
listGeneratorMock.On("GenerateParams", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator"), mock.AnythingOfType("*v1alpha1.ApplicationSet"), mock.Anything).Return([]map[string]any{
|
||||||
{"some": "value"},
|
{"some": "value"},
|
||||||
}, nil)
|
}, nil)
|
||||||
listGeneratorMock.On("GetTemplate", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&argoprojiov1alpha1.ApplicationSetTemplate{})
|
listGeneratorMock.On("GetTemplate", mock.AnythingOfType("*v1alpha1.ApplicationSetGenerator")).Return(&v1alpha1.ApplicationSetTemplate{})
|
||||||
|
|
||||||
gitGeneratorSpec := &argoprojiov1alpha1.GitGenerator{
|
gitGeneratorSpec := &v1alpha1.GitGenerator{
|
||||||
RepoURL: "https://git.example.com",
|
RepoURL: "https://git.example.com",
|
||||||
Files: []argoprojiov1alpha1.GitFileGeneratorItem{
|
Files: []v1alpha1.GitFileGeneratorItem{
|
||||||
{Path: "some/path.json"},
|
{Path: "some/path.json"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
repoServiceMock := &mocks.Repos{}
|
repoServiceMock := &mocks.Repos{}
|
||||||
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
repoServiceMock.On("GetFiles", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(map[string][]byte{
|
||||||
"some/path.json": []byte("test: content"),
|
"some/path.json": []byte("test: content"),
|
||||||
}, nil)
|
}, nil)
|
||||||
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
gitGenerator := NewGitGenerator(repoServiceMock, "")
|
||||||
@@ -1098,10 +1096,10 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
|||||||
"Git": gitGenerator,
|
"Git": gitGenerator,
|
||||||
})
|
})
|
||||||
|
|
||||||
matrixGeneratorSpec := &argoprojiov1alpha1.MatrixGenerator{
|
matrixGeneratorSpec := &v1alpha1.MatrixGenerator{
|
||||||
Generators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
Generators: []v1alpha1.ApplicationSetNestedGenerator{
|
||||||
{
|
{
|
||||||
List: &argoprojiov1alpha1.ListGenerator{
|
List: &v1alpha1.ListGenerator{
|
||||||
Elements: []apiextensionsv1.JSON{
|
Elements: []apiextensionsv1.JSON{
|
||||||
{
|
{
|
||||||
Raw: []byte(`{"some": "value"}`),
|
Raw: []byte(`{"some": "value"}`),
|
||||||
@@ -1118,15 +1116,15 @@ func TestGitGenerator_GenerateParams_list_x_git_matrix_generator(t *testing.T) {
|
|||||||
scheme := runtime.NewScheme()
|
scheme := runtime.NewScheme()
|
||||||
err := v1alpha1.AddToScheme(scheme)
|
err := v1alpha1.AddToScheme(scheme)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
appProject := argoprojiov1alpha1.AppProject{}
|
appProject := v1alpha1.AppProject{}
|
||||||
|
|
||||||
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&appProject).Build()
|
||||||
|
|
||||||
params, err := matrixGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
params, err := matrixGenerator.GenerateParams(&v1alpha1.ApplicationSetGenerator{
|
||||||
Matrix: matrixGeneratorSpec,
|
Matrix: matrixGeneratorSpec,
|
||||||
}, &argoprojiov1alpha1.ApplicationSet{}, client)
|
}, &v1alpha1.ApplicationSet{}, client)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, []map[string]interface{}{{
|
assert.Equal(t, []map[string]any{{
|
||||||
"path": "some",
|
"path": "some",
|
||||||
"path.basename": "some",
|
"path.basename": "some",
|
||||||
"path.basenameNormalized": "some",
|
"path.basenameNormalized": "some",
|
||||||
|
|||||||
@@ -2,24 +2,23 @@ package generators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"maps"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
"dario.cat/mergo"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*MergeGenerator)(nil)
|
var _ Generator = (*MergeGenerator)(nil)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrLessThanTwoGeneratorsInMerge = fmt.Errorf("found less than two generators, Merge requires two or more")
|
ErrLessThanTwoGeneratorsInMerge = errors.New("found less than two generators, Merge requires two or more")
|
||||||
ErrNoMergeKeys = fmt.Errorf("no merge keys were specified, Merge requires at least one")
|
ErrNoMergeKeys = errors.New("no merge keys were specified, Merge requires at least one")
|
||||||
ErrNonUniqueParamSets = fmt.Errorf("the parameters from a generator were not unique by the given mergeKeys, Merge requires all param sets to be unique")
|
ErrNonUniqueParamSets = errors.New("the parameters from a generator were not unique by the given mergeKeys, Merge requires all param sets to be unique")
|
||||||
)
|
)
|
||||||
|
|
||||||
type MergeGenerator struct {
|
type MergeGenerator struct {
|
||||||
@@ -37,8 +36,8 @@ func NewMergeGenerator(supportedGenerators map[string]Generator) Generator {
|
|||||||
|
|
||||||
// getParamSetsForAllGenerators generates params for each child generator in a MergeGenerator. Param sets are returned
|
// getParamSetsForAllGenerators generates params for each child generator in a MergeGenerator. Param sets are returned
|
||||||
// in slices ordered according to the order of the given generators.
|
// in slices ordered according to the order of the given generators.
|
||||||
func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([][]map[string]interface{}, error) {
|
func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([][]map[string]any, error) {
|
||||||
var paramSets [][]map[string]interface{}
|
var paramSets [][]map[string]any
|
||||||
for i, generator := range generators {
|
for i, generator := range generators {
|
||||||
generatorParamSets, err := m.getParams(generator, appSet, client)
|
generatorParamSets, err := m.getParams(generator, appSet, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -51,9 +50,9 @@ func (m *MergeGenerator) getParamSetsForAllGenerators(generators []argoprojiov1a
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GenerateParams gets the params produced by the MergeGenerator.
|
// GenerateParams gets the params produced by the MergeGenerator.
|
||||||
func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) {
|
func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator.Merge == nil {
|
if appSetGenerator.Merge == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(appSetGenerator.Merge.Generators) < 2 {
|
if len(appSetGenerator.Merge.Generators) < 2 {
|
||||||
@@ -84,21 +83,18 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl
|
|||||||
}
|
}
|
||||||
baseParamSetsByMergeKey[mergeKeyValue] = baseParamSet
|
baseParamSetsByMergeKey[mergeKeyValue] = baseParamSet
|
||||||
} else {
|
} else {
|
||||||
overriddenParamSet, err := utils.CombineStringMapsAllowDuplicates(baseParamSet, overrideParamSet)
|
maps.Copy(baseParamSet, overrideParamSet)
|
||||||
if err != nil {
|
baseParamSetsByMergeKey[mergeKeyValue] = baseParamSet
|
||||||
return nil, fmt.Errorf("error combining string maps: %w", err)
|
|
||||||
}
|
|
||||||
baseParamSetsByMergeKey[mergeKeyValue] = utils.ConvertToMapStringInterface(overriddenParamSet)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mergedParamSets := make([]map[string]interface{}, len(baseParamSetsByMergeKey))
|
mergedParamSets := make([]map[string]any, len(baseParamSetsByMergeKey))
|
||||||
i := 0
|
i := 0
|
||||||
for _, mergedParamSet := range baseParamSetsByMergeKey {
|
for _, mergedParamSet := range baseParamSetsByMergeKey {
|
||||||
mergedParamSets[i] = mergedParamSet
|
mergedParamSets[i] = mergedParamSet
|
||||||
i += 1
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
return mergedParamSets, nil
|
return mergedParamSets, nil
|
||||||
@@ -107,7 +103,7 @@ func (m *MergeGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.Appl
|
|||||||
// getParamSetsByMergeKey converts the given list of parameter sets to a map of parameter sets where the key is the
|
// getParamSetsByMergeKey converts the given list of parameter sets to a map of parameter sets where the key is the
|
||||||
// unique key of the parameter set as determined by the given mergeKeys. If any two parameter sets share the same merge
|
// unique key of the parameter set as determined by the given mergeKeys. If any two parameter sets share the same merge
|
||||||
// key, getParamSetsByMergeKey will throw NonUniqueParamSets.
|
// key, getParamSetsByMergeKey will throw NonUniqueParamSets.
|
||||||
func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]interface{}) (map[string]map[string]interface{}, error) {
|
func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]any) (map[string]map[string]any, error) {
|
||||||
if len(mergeKeys) < 1 {
|
if len(mergeKeys) < 1 {
|
||||||
return nil, ErrNoMergeKeys
|
return nil, ErrNoMergeKeys
|
||||||
}
|
}
|
||||||
@@ -117,17 +113,17 @@ func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]interface
|
|||||||
deDuplicatedMergeKeys[mergeKey] = false
|
deDuplicatedMergeKeys[mergeKey] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
paramSetsByMergeKey := make(map[string]map[string]interface{}, len(paramSets))
|
paramSetsByMergeKey := make(map[string]map[string]any, len(paramSets))
|
||||||
for _, paramSet := range paramSets {
|
for _, paramSet := range paramSets {
|
||||||
paramSetKey := make(map[string]interface{})
|
paramSetKey := make(map[string]any)
|
||||||
for mergeKey := range deDuplicatedMergeKeys {
|
for mergeKey := range deDuplicatedMergeKeys {
|
||||||
paramSetKey[mergeKey] = paramSet[mergeKey]
|
paramSetKey[mergeKey] = paramSet[mergeKey]
|
||||||
}
|
}
|
||||||
paramSetKeyJson, err := json.Marshal(paramSetKey)
|
paramSetKeyJSON, err := json.Marshal(paramSetKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error marshalling param set key json: %w", err)
|
return nil, fmt.Errorf("error marshalling param set key json: %w", err)
|
||||||
}
|
}
|
||||||
paramSetKeyString := string(paramSetKeyJson)
|
paramSetKeyString := string(paramSetKeyJSON)
|
||||||
if _, exists := paramSetsByMergeKey[paramSetKeyString]; exists {
|
if _, exists := paramSetsByMergeKey[paramSetKeyString]; exists {
|
||||||
return nil, fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, paramSetKeyString)
|
return nil, fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, paramSetKeyString)
|
||||||
}
|
}
|
||||||
@@ -138,27 +134,15 @@ func getParamSetsByMergeKey(mergeKeys []string, paramSets []map[string]interface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getParams get the parameters generated by this generator.
|
// getParams get the parameters generated by this generator.
|
||||||
func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]interface{}, error) {
|
func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.ApplicationSetNestedGenerator, appSet *argoprojiov1alpha1.ApplicationSet, client client.Client) ([]map[string]any, error) {
|
||||||
matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
|
matrixGen, err := getMatrixGenerator(appSetBaseGenerator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if matrixGen != nil && !appSet.Spec.ApplyNestedSelectors {
|
|
||||||
foundSelector := dropDisabledNestedSelectors(matrixGen.Generators)
|
|
||||||
if foundSelector {
|
|
||||||
log.Warnf("AppSet '%v' defines selector on nested matrix generator's generator without enabling them via 'spec.applyNestedSelectors', ignoring nested selector", appSet.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mergeGen, err := getMergeGenerator(appSetBaseGenerator)
|
mergeGen, err := getMergeGenerator(appSetBaseGenerator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if mergeGen != nil && !appSet.Spec.ApplyNestedSelectors {
|
|
||||||
foundSelector := dropDisabledNestedSelectors(mergeGen.Generators)
|
|
||||||
if foundSelector {
|
|
||||||
log.Warnf("AppSet '%v' defines selector on nested merge generator's generator without enabling them via 'spec.applyNestedSelectors', ignoring nested selector", appSet.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t, err := Transform(
|
t, err := Transform(
|
||||||
argoprojiov1alpha1.ApplicationSetGenerator{
|
argoprojiov1alpha1.ApplicationSetGenerator{
|
||||||
@@ -176,13 +160,13 @@ func (m *MergeGenerator) getParams(appSetBaseGenerator argoprojiov1alpha1.Applic
|
|||||||
m.supportedGenerators,
|
m.supportedGenerators,
|
||||||
argoprojiov1alpha1.ApplicationSetTemplate{},
|
argoprojiov1alpha1.ApplicationSetTemplate{},
|
||||||
appSet,
|
appSet,
|
||||||
map[string]interface{}{}, client)
|
map[string]any{}, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("child generator returned an error on parameter generation: %w", err)
|
return nil, fmt.Errorf("child generator returned an error on parameter generation: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(t) == 0 {
|
if len(t) == 0 {
|
||||||
return nil, fmt.Errorf("child generator generated no parameters")
|
return nil, errors.New("child generator generated no parameters")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(t) > 1 {
|
if len(t) > 1 {
|
||||||
@@ -223,9 +207,8 @@ func (m *MergeGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.App
|
|||||||
|
|
||||||
if found {
|
if found {
|
||||||
return res
|
return res
|
||||||
} else {
|
|
||||||
return NoRequeueAfter
|
|
||||||
}
|
}
|
||||||
|
return NoRequeueAfter
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMergeGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MergeGenerator, error) {
|
func getMergeGenerator(r argoprojiov1alpha1.ApplicationSetNestedGenerator) (*argoprojiov1alpha1.MergeGenerator, error) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getNestedListGenerator(json string) *argoprojiov1alpha1.ApplicationSetNestedGenerator {
|
func getNestedListGenerator(json string) *argoprojiov1alpha1.ApplicationSetNestedGenerator {
|
||||||
@@ -36,26 +36,28 @@ func getTerminalListGeneratorMultiple(jsons []string) argoprojiov1alpha1.Applica
|
|||||||
return generator
|
return generator
|
||||||
}
|
}
|
||||||
|
|
||||||
func listOfMapsToSet(maps []map[string]interface{}) (map[string]bool, error) {
|
func listOfMapsToSet(maps []map[string]any) (map[string]bool, error) {
|
||||||
set := make(map[string]bool, len(maps))
|
set := make(map[string]bool, len(maps))
|
||||||
for _, paramMap := range maps {
|
for _, paramMap := range maps {
|
||||||
paramMapAsJson, err := json.Marshal(paramMap)
|
paramMapAsJSON, err := json.Marshal(paramMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
set[string(paramMapAsJson)] = false
|
set[string(paramMapAsJSON)] = false
|
||||||
}
|
}
|
||||||
return set, nil
|
return set, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMergeGenerate(t *testing.T) {
|
func TestMergeGenerate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
||||||
mergeKeys []string
|
mergeKeys []string
|
||||||
expectedErr error
|
expectedErr error
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no generators",
|
name: "no generators",
|
||||||
@@ -79,7 +81,7 @@ func TestMergeGenerate(t *testing.T) {
|
|||||||
*getNestedListGenerator(`{"a": "3_1","b": "different","c": "3_3"}`), // gets ignored because its merge key value isn't in the base params set
|
*getNestedListGenerator(`{"a": "3_1","b": "different","c": "3_3"}`), // gets ignored because its merge key value isn't in the base params set
|
||||||
},
|
},
|
||||||
mergeKeys: []string{"b"},
|
mergeKeys: []string{"b"},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"a": "2_1", "b": "same", "c": "1_3"},
|
{"a": "2_1", "b": "same", "c": "1_3"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -90,7 +92,7 @@ func TestMergeGenerate(t *testing.T) {
|
|||||||
*getNestedListGenerator(`{"a": "a"}`),
|
*getNestedListGenerator(`{"a": "a"}`),
|
||||||
},
|
},
|
||||||
mergeKeys: []string{"b"},
|
mergeKeys: []string{"b"},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"a": "a"},
|
{"a": "a"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -101,7 +103,7 @@ func TestMergeGenerate(t *testing.T) {
|
|||||||
*getNestedListGenerator(`{"b": "b"}`),
|
*getNestedListGenerator(`{"b": "b"}`),
|
||||||
},
|
},
|
||||||
mergeKeys: []string{"b"},
|
mergeKeys: []string{"b"},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"a": "a"},
|
{"a": "a"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -119,7 +121,7 @@ func TestMergeGenerate(t *testing.T) {
|
|||||||
*getNestedListGenerator(`{"a": "1", "b": "1", "c": "added"}`),
|
*getNestedListGenerator(`{"a": "1", "b": "1", "c": "added"}`),
|
||||||
},
|
},
|
||||||
mergeKeys: []string{"a", "b"},
|
mergeKeys: []string{"a", "b"},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"a": "1", "b": "1", "c": "added"},
|
{"a": "1", "b": "1", "c": "added"},
|
||||||
{"a": "1", "b": "2"},
|
{"a": "1", "b": "2"},
|
||||||
{"a": "2", "b": "1"},
|
{"a": "2", "b": "1"},
|
||||||
@@ -141,7 +143,7 @@ func TestMergeGenerate(t *testing.T) {
|
|||||||
*getNestedListGenerator(`{"a": "1", "b": "3", "d": "added"}`),
|
*getNestedListGenerator(`{"a": "1", "b": "3", "d": "added"}`),
|
||||||
},
|
},
|
||||||
mergeKeys: []string{"a", "b"},
|
mergeKeys: []string{"a", "b"},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{"a": "1", "b": "3", "c": "added", "d": "added"},
|
{"a": "1", "b": "3", "c": "added", "d": "added"},
|
||||||
{"a": "2", "b": "2"},
|
{"a": "2", "b": "2"},
|
||||||
},
|
},
|
||||||
@@ -196,7 +198,7 @@ func TestMergeGenerate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toAPIExtensionsJSON(t *testing.T, g interface{}) *apiextensionsv1.JSON {
|
func toAPIExtensionsJSON(t *testing.T, g any) *apiextensionsv1.JSON {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
resVal, err := json.Marshal(g)
|
resVal, err := json.Marshal(g)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -210,12 +212,14 @@ func toAPIExtensionsJSON(t *testing.T, g interface{}) *apiextensionsv1.JSON {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
mergeKeys []string
|
mergeKeys []string
|
||||||
paramSets []map[string]interface{}
|
paramSets []map[string]any
|
||||||
expectedErr error
|
expectedErr error
|
||||||
expected map[string]map[string]interface{}
|
expected map[string]map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no merge keys",
|
name: "no merge keys",
|
||||||
@@ -225,13 +229,13 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "no paramSets",
|
name: "no paramSets",
|
||||||
mergeKeys: []string{"key"},
|
mergeKeys: []string{"key"},
|
||||||
expected: make(map[string]map[string]interface{}),
|
expected: make(map[string]map[string]any),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple key, unique paramSets",
|
name: "simple key, unique paramSets",
|
||||||
mergeKeys: []string{"key"},
|
mergeKeys: []string{"key"},
|
||||||
paramSets: []map[string]interface{}{{"key": "a"}, {"key": "b"}},
|
paramSets: []map[string]any{{"key": "a"}, {"key": "b"}},
|
||||||
expected: map[string]map[string]interface{}{
|
expected: map[string]map[string]any{
|
||||||
`{"key":"a"}`: {"key": "a"},
|
`{"key":"a"}`: {"key": "a"},
|
||||||
`{"key":"b"}`: {"key": "b"},
|
`{"key":"b"}`: {"key": "b"},
|
||||||
},
|
},
|
||||||
@@ -239,23 +243,23 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "simple key object, unique paramSets",
|
name: "simple key object, unique paramSets",
|
||||||
mergeKeys: []string{"key"},
|
mergeKeys: []string{"key"},
|
||||||
paramSets: []map[string]interface{}{{"key": map[string]interface{}{"hello": "world"}}, {"key": "b"}},
|
paramSets: []map[string]any{{"key": map[string]any{"hello": "world"}}, {"key": "b"}},
|
||||||
expected: map[string]map[string]interface{}{
|
expected: map[string]map[string]any{
|
||||||
`{"key":{"hello":"world"}}`: {"key": map[string]interface{}{"hello": "world"}},
|
`{"key":{"hello":"world"}}`: {"key": map[string]any{"hello": "world"}},
|
||||||
`{"key":"b"}`: {"key": "b"},
|
`{"key":"b"}`: {"key": "b"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple key, non-unique paramSets",
|
name: "simple key, non-unique paramSets",
|
||||||
mergeKeys: []string{"key"},
|
mergeKeys: []string{"key"},
|
||||||
paramSets: []map[string]interface{}{{"key": "a"}, {"key": "b"}, {"key": "b"}},
|
paramSets: []map[string]any{{"key": "a"}, {"key": "b"}, {"key": "b"}},
|
||||||
expectedErr: fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, `{"key":"b"}`),
|
expectedErr: fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, `{"key":"b"}`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple key, duplicated key name, unique paramSets",
|
name: "simple key, duplicated key name, unique paramSets",
|
||||||
mergeKeys: []string{"key", "key"},
|
mergeKeys: []string{"key", "key"},
|
||||||
paramSets: []map[string]interface{}{{"key": "a"}, {"key": "b"}},
|
paramSets: []map[string]any{{"key": "a"}, {"key": "b"}},
|
||||||
expected: map[string]map[string]interface{}{
|
expected: map[string]map[string]any{
|
||||||
`{"key":"a"}`: {"key": "a"},
|
`{"key":"a"}`: {"key": "a"},
|
||||||
`{"key":"b"}`: {"key": "b"},
|
`{"key":"b"}`: {"key": "b"},
|
||||||
},
|
},
|
||||||
@@ -263,18 +267,18 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "simple key, duplicated key name, non-unique paramSets",
|
name: "simple key, duplicated key name, non-unique paramSets",
|
||||||
mergeKeys: []string{"key", "key"},
|
mergeKeys: []string{"key", "key"},
|
||||||
paramSets: []map[string]interface{}{{"key": "a"}, {"key": "b"}, {"key": "b"}},
|
paramSets: []map[string]any{{"key": "a"}, {"key": "b"}, {"key": "b"}},
|
||||||
expectedErr: fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, `{"key":"b"}`),
|
expectedErr: fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, `{"key":"b"}`),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "compound key, unique paramSets",
|
name: "compound key, unique paramSets",
|
||||||
mergeKeys: []string{"key1", "key2"},
|
mergeKeys: []string{"key1", "key2"},
|
||||||
paramSets: []map[string]interface{}{
|
paramSets: []map[string]any{
|
||||||
{"key1": "a", "key2": "a"},
|
{"key1": "a", "key2": "a"},
|
||||||
{"key1": "a", "key2": "b"},
|
{"key1": "a", "key2": "b"},
|
||||||
{"key1": "b", "key2": "a"},
|
{"key1": "b", "key2": "a"},
|
||||||
},
|
},
|
||||||
expected: map[string]map[string]interface{}{
|
expected: map[string]map[string]any{
|
||||||
`{"key1":"a","key2":"a"}`: {"key1": "a", "key2": "a"},
|
`{"key1":"a","key2":"a"}`: {"key1": "a", "key2": "a"},
|
||||||
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
||||||
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
||||||
@@ -283,13 +287,13 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "compound key object, unique paramSets",
|
name: "compound key object, unique paramSets",
|
||||||
mergeKeys: []string{"key1", "key2"},
|
mergeKeys: []string{"key1", "key2"},
|
||||||
paramSets: []map[string]interface{}{
|
paramSets: []map[string]any{
|
||||||
{"key1": "a", "key2": map[string]interface{}{"hello": "world"}},
|
{"key1": "a", "key2": map[string]any{"hello": "world"}},
|
||||||
{"key1": "a", "key2": "b"},
|
{"key1": "a", "key2": "b"},
|
||||||
{"key1": "b", "key2": "a"},
|
{"key1": "b", "key2": "a"},
|
||||||
},
|
},
|
||||||
expected: map[string]map[string]interface{}{
|
expected: map[string]map[string]any{
|
||||||
`{"key1":"a","key2":{"hello":"world"}}`: {"key1": "a", "key2": map[string]interface{}{"hello": "world"}},
|
`{"key1":"a","key2":{"hello":"world"}}`: {"key1": "a", "key2": map[string]any{"hello": "world"}},
|
||||||
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
||||||
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
||||||
},
|
},
|
||||||
@@ -297,12 +301,12 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "compound key, duplicate key names, unique paramSets",
|
name: "compound key, duplicate key names, unique paramSets",
|
||||||
mergeKeys: []string{"key1", "key1", "key2"},
|
mergeKeys: []string{"key1", "key1", "key2"},
|
||||||
paramSets: []map[string]interface{}{
|
paramSets: []map[string]any{
|
||||||
{"key1": "a", "key2": "a"},
|
{"key1": "a", "key2": "a"},
|
||||||
{"key1": "a", "key2": "b"},
|
{"key1": "a", "key2": "b"},
|
||||||
{"key1": "b", "key2": "a"},
|
{"key1": "b", "key2": "a"},
|
||||||
},
|
},
|
||||||
expected: map[string]map[string]interface{}{
|
expected: map[string]map[string]any{
|
||||||
`{"key1":"a","key2":"a"}`: {"key1": "a", "key2": "a"},
|
`{"key1":"a","key2":"a"}`: {"key1": "a", "key2": "a"},
|
||||||
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
||||||
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
||||||
@@ -311,7 +315,7 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "compound key, non-unique paramSets",
|
name: "compound key, non-unique paramSets",
|
||||||
mergeKeys: []string{"key1", "key2"},
|
mergeKeys: []string{"key1", "key2"},
|
||||||
paramSets: []map[string]interface{}{
|
paramSets: []map[string]any{
|
||||||
{"key1": "a", "key2": "a"},
|
{"key1": "a", "key2": "a"},
|
||||||
{"key1": "a", "key2": "a"},
|
{"key1": "a", "key2": "a"},
|
||||||
{"key1": "b", "key2": "a"},
|
{"key1": "b", "key2": "a"},
|
||||||
@@ -321,7 +325,7 @@ func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "compound key, duplicate key names, non-unique paramSets",
|
name: "compound key, duplicate key names, non-unique paramSets",
|
||||||
mergeKeys: []string{"key1", "key1", "key2"},
|
mergeKeys: []string{"key1", "key1", "key2"},
|
||||||
paramSets: []map[string]interface{}{
|
paramSets: []map[string]any{
|
||||||
{"key1": "a", "key2": "a"},
|
{"key1": "a", "key2": "a"},
|
||||||
{"key1": "a", "key2": "a"},
|
{"key1": "a", "key2": "a"},
|
||||||
{"key1": "b", "key2": "a"},
|
{"key1": "b", "key2": "a"},
|
||||||
|
|||||||
276
applicationset/generators/mocks/Generator.go
generated
276
applicationset/generators/mocks/Generator.go
generated
@@ -1,90 +1,17 @@
|
|||||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
// Code generated by mockery; DO NOT EDIT.
|
||||||
|
// github.com/vektra/mockery
|
||||||
|
// template: testify
|
||||||
|
|
||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
client "sigs.k8s.io/controller-runtime/pkg/client"
|
"time"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
time "time"
|
|
||||||
|
|
||||||
v1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generator is an autogenerated mock type for the Generator type
|
|
||||||
type Generator struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateParams provides a mock function with given fields: appSetGenerator, applicationSetInfo, _a2
|
|
||||||
func (_m *Generator) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, _a2 client.Client) ([]map[string]interface{}, error) {
|
|
||||||
ret := _m.Called(appSetGenerator, applicationSetInfo, _a2)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GenerateParams")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 []map[string]interface{}
|
|
||||||
var r1 error
|
|
||||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) ([]map[string]interface{}, error)); ok {
|
|
||||||
return rf(appSetGenerator, applicationSetInfo, _a2)
|
|
||||||
}
|
|
||||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) []map[string]interface{}); ok {
|
|
||||||
r0 = rf(appSetGenerator, applicationSetInfo, _a2)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).([]map[string]interface{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rf, ok := ret.Get(1).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) error); ok {
|
|
||||||
r1 = rf(appSetGenerator, applicationSetInfo, _a2)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRequeueAfter provides a mock function with given fields: appSetGenerator
|
|
||||||
func (_m *Generator) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration {
|
|
||||||
ret := _m.Called(appSetGenerator)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GetRequeueAfter")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 time.Duration
|
|
||||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) time.Duration); ok {
|
|
||||||
r0 = rf(appSetGenerator)
|
|
||||||
} else {
|
|
||||||
r0 = ret.Get(0).(time.Duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTemplate provides a mock function with given fields: appSetGenerator
|
|
||||||
func (_m *Generator) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate {
|
|
||||||
ret := _m.Called(appSetGenerator)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GetTemplate")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 *v1alpha1.ApplicationSetTemplate
|
|
||||||
if rf, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate); ok {
|
|
||||||
r0 = rf(appSetGenerator)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).(*v1alpha1.ApplicationSetTemplate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGenerator creates a new instance of Generator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
// NewGenerator creates a new instance of Generator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
// The first argument is typically a *testing.T value.
|
// The first argument is typically a *testing.T value.
|
||||||
func NewGenerator(t interface {
|
func NewGenerator(t interface {
|
||||||
@@ -98,3 +25,194 @@ func NewGenerator(t interface {
|
|||||||
|
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generator is an autogenerated mock type for the Generator type
|
||||||
|
type Generator struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
type Generator_Expecter struct {
|
||||||
|
mock *mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_m *Generator) EXPECT() *Generator_Expecter {
|
||||||
|
return &Generator_Expecter{mock: &_m.Mock}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateParams provides a mock function for the type Generator
|
||||||
|
func (_mock *Generator) GenerateParams(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, client1 client.Client) ([]map[string]any, error) {
|
||||||
|
ret := _mock.Called(appSetGenerator, applicationSetInfo, client1)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GenerateParams")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []map[string]any
|
||||||
|
var r1 error
|
||||||
|
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) ([]map[string]any, error)); ok {
|
||||||
|
return returnFunc(appSetGenerator, applicationSetInfo, client1)
|
||||||
|
}
|
||||||
|
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) []map[string]any); ok {
|
||||||
|
r0 = returnFunc(appSetGenerator, applicationSetInfo, client1)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]map[string]any)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if returnFunc, ok := ret.Get(1).(func(*v1alpha1.ApplicationSetGenerator, *v1alpha1.ApplicationSet, client.Client) error); ok {
|
||||||
|
r1 = returnFunc(appSetGenerator, applicationSetInfo, client1)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generator_GenerateParams_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GenerateParams'
|
||||||
|
type Generator_GenerateParams_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateParams is a helper method to define mock.On call
|
||||||
|
// - appSetGenerator *v1alpha1.ApplicationSetGenerator
|
||||||
|
// - applicationSetInfo *v1alpha1.ApplicationSet
|
||||||
|
// - client1 client.Client
|
||||||
|
func (_e *Generator_Expecter) GenerateParams(appSetGenerator interface{}, applicationSetInfo interface{}, client1 interface{}) *Generator_GenerateParams_Call {
|
||||||
|
return &Generator_GenerateParams_Call{Call: _e.mock.On("GenerateParams", appSetGenerator, applicationSetInfo, client1)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GenerateParams_Call) Run(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, client1 client.Client)) *Generator_GenerateParams_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
var arg0 *v1alpha1.ApplicationSetGenerator
|
||||||
|
if args[0] != nil {
|
||||||
|
arg0 = args[0].(*v1alpha1.ApplicationSetGenerator)
|
||||||
|
}
|
||||||
|
var arg1 *v1alpha1.ApplicationSet
|
||||||
|
if args[1] != nil {
|
||||||
|
arg1 = args[1].(*v1alpha1.ApplicationSet)
|
||||||
|
}
|
||||||
|
var arg2 client.Client
|
||||||
|
if args[2] != nil {
|
||||||
|
arg2 = args[2].(client.Client)
|
||||||
|
}
|
||||||
|
run(
|
||||||
|
arg0,
|
||||||
|
arg1,
|
||||||
|
arg2,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GenerateParams_Call) Return(stringToVs []map[string]any, err error) *Generator_GenerateParams_Call {
|
||||||
|
_c.Call.Return(stringToVs, err)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GenerateParams_Call) RunAndReturn(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator, applicationSetInfo *v1alpha1.ApplicationSet, client1 client.Client) ([]map[string]any, error)) *Generator_GenerateParams_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequeueAfter provides a mock function for the type Generator
|
||||||
|
func (_mock *Generator) GetRequeueAfter(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration {
|
||||||
|
ret := _mock.Called(appSetGenerator)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetRequeueAfter")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 time.Duration
|
||||||
|
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) time.Duration); ok {
|
||||||
|
r0 = returnFunc(appSetGenerator)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Get(0).(time.Duration)
|
||||||
|
}
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generator_GetRequeueAfter_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetRequeueAfter'
|
||||||
|
type Generator_GetRequeueAfter_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRequeueAfter is a helper method to define mock.On call
|
||||||
|
// - appSetGenerator *v1alpha1.ApplicationSetGenerator
|
||||||
|
func (_e *Generator_Expecter) GetRequeueAfter(appSetGenerator interface{}) *Generator_GetRequeueAfter_Call {
|
||||||
|
return &Generator_GetRequeueAfter_Call{Call: _e.mock.On("GetRequeueAfter", appSetGenerator)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GetRequeueAfter_Call) Run(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator)) *Generator_GetRequeueAfter_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
var arg0 *v1alpha1.ApplicationSetGenerator
|
||||||
|
if args[0] != nil {
|
||||||
|
arg0 = args[0].(*v1alpha1.ApplicationSetGenerator)
|
||||||
|
}
|
||||||
|
run(
|
||||||
|
arg0,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GetRequeueAfter_Call) Return(duration time.Duration) *Generator_GetRequeueAfter_Call {
|
||||||
|
_c.Call.Return(duration)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GetRequeueAfter_Call) RunAndReturn(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator) time.Duration) *Generator_GetRequeueAfter_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTemplate provides a mock function for the type Generator
|
||||||
|
func (_mock *Generator) GetTemplate(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate {
|
||||||
|
ret := _mock.Called(appSetGenerator)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetTemplate")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *v1alpha1.ApplicationSetTemplate
|
||||||
|
if returnFunc, ok := ret.Get(0).(func(*v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate); ok {
|
||||||
|
r0 = returnFunc(appSetGenerator)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*v1alpha1.ApplicationSetTemplate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generator_GetTemplate_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTemplate'
|
||||||
|
type Generator_GetTemplate_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTemplate is a helper method to define mock.On call
|
||||||
|
// - appSetGenerator *v1alpha1.ApplicationSetGenerator
|
||||||
|
func (_e *Generator_Expecter) GetTemplate(appSetGenerator interface{}) *Generator_GetTemplate_Call {
|
||||||
|
return &Generator_GetTemplate_Call{Call: _e.mock.On("GetTemplate", appSetGenerator)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GetTemplate_Call) Run(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator)) *Generator_GetTemplate_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
var arg0 *v1alpha1.ApplicationSetGenerator
|
||||||
|
if args[0] != nil {
|
||||||
|
arg0 = args[0].(*v1alpha1.ApplicationSetGenerator)
|
||||||
|
}
|
||||||
|
run(
|
||||||
|
arg0,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GetTemplate_Call) Return(applicationSetTemplate *v1alpha1.ApplicationSetTemplate) *Generator_GetTemplate_Call {
|
||||||
|
_c.Call.Return(applicationSetTemplate)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Generator_GetTemplate_Call) RunAndReturn(run func(appSetGenerator *v1alpha1.ApplicationSetGenerator) *v1alpha1.ApplicationSetTemplate) *Generator_GetTemplate_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package generators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -9,33 +10,28 @@ import (
|
|||||||
|
|
||||||
"github.com/jeremywohl/flatten"
|
"github.com/jeremywohl/flatten"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
"github.com/argoproj/argo-cd/v3/util/settings"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/plugin"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/plugin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultPluginRequeueAfterSeconds = 30 * time.Minute
|
DefaultPluginRequeueAfter = 30 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*PluginGenerator)(nil)
|
var _ Generator = (*PluginGenerator)(nil)
|
||||||
|
|
||||||
type PluginGenerator struct {
|
type PluginGenerator struct {
|
||||||
client client.Client
|
client client.Client
|
||||||
ctx context.Context
|
|
||||||
clientset kubernetes.Interface
|
|
||||||
namespace string
|
namespace string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPluginGenerator(client client.Client, ctx context.Context, clientset kubernetes.Interface, namespace string) Generator {
|
func NewPluginGenerator(client client.Client, namespace string) Generator {
|
||||||
g := &PluginGenerator{
|
g := &PluginGenerator{
|
||||||
client: client,
|
client: client,
|
||||||
ctx: ctx,
|
|
||||||
clientset: clientset,
|
|
||||||
namespace: namespace,
|
namespace: namespace,
|
||||||
}
|
}
|
||||||
return g
|
return g
|
||||||
@@ -48,20 +44,20 @@ func (g *PluginGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alpha1.Ap
|
|||||||
return time.Duration(*appSetGenerator.Plugin.RequeueAfterSeconds) * time.Second
|
return time.Duration(*appSetGenerator.Plugin.RequeueAfterSeconds) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefaultPluginRequeueAfterSeconds
|
return DefaultPluginRequeueAfter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *PluginGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||||
return &appSetGenerator.Plugin.Template
|
return &appSetGenerator.Plugin.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
func (g *PluginGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.Plugin == nil {
|
if appSetGenerator.Plugin == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -105,18 +101,18 @@ func (g *PluginGenerator) getPluginFromGenerator(ctx context.Context, appSetName
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pluginClient, err := plugin.NewPluginService(ctx, appSetName, cm["baseUrl"], token, requestTimeout)
|
pluginClient, err := plugin.NewPluginService(appSetName, cm["baseUrl"], token, requestTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error initializing plugin client: %w", err)
|
return nil, fmt.Errorf("error initializing plugin client: %w", err)
|
||||||
}
|
}
|
||||||
return pluginClient, nil
|
return pluginClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, objectsFound []map[string]interface{}, pluginParams argoprojiov1alpha1.PluginParameters, useGoTemplate bool) ([]map[string]interface{}, error) {
|
func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, appSet *argoprojiov1alpha1.ApplicationSet, objectsFound []map[string]any, pluginParams argoprojiov1alpha1.PluginParameters, useGoTemplate bool) ([]map[string]any, error) {
|
||||||
res := []map[string]interface{}{}
|
res := []map[string]any{}
|
||||||
|
|
||||||
for _, objectFound := range objectsFound {
|
for _, objectFound := range objectsFound {
|
||||||
params := map[string]interface{}{}
|
params := map[string]any{}
|
||||||
|
|
||||||
if useGoTemplate {
|
if useGoTemplate {
|
||||||
for k, v := range objectFound {
|
for k, v := range objectFound {
|
||||||
@@ -132,7 +128,7 @@ func (g *PluginGenerator) generateParams(appSetGenerator *argoprojiov1alpha1.App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params["generator"] = map[string]interface{}{
|
params["generator"] = map[string]any{
|
||||||
"input": map[string]argoprojiov1alpha1.PluginParameters{
|
"input": map[string]argoprojiov1alpha1.PluginParameters{
|
||||||
"parameters": pluginParams,
|
"parameters": pluginParams,
|
||||||
},
|
},
|
||||||
@@ -192,14 +188,14 @@ func (g *PluginGenerator) getConfigMap(ctx context.Context, configMapRef string)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
baseUrl, ok := cm.Data["baseUrl"]
|
baseURL, ok := cm.Data["baseUrl"]
|
||||||
if !ok || baseUrl == "" {
|
if !ok || baseURL == "" {
|
||||||
return nil, fmt.Errorf("baseUrl not found in ConfigMap")
|
return nil, errors.New("baseUrl not found in ConfigMap")
|
||||||
}
|
}
|
||||||
|
|
||||||
token, ok := cm.Data["token"]
|
token, ok := cm.Data["token"]
|
||||||
if !ok || token == "" {
|
if !ok || token == "" {
|
||||||
return nil, fmt.Errorf("token not found in ConfigMap")
|
return nil, errors.New("token not found in ConfigMap")
|
||||||
}
|
}
|
||||||
|
|
||||||
return cm.Data, nil
|
return cm.Data, nil
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package generators
|
package generators
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@@ -11,33 +11,31 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
v1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/plugin"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/plugin"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPluginGenerateParams(t *testing.T) {
|
func TestPluginGenerateParams(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
configmap *v1.ConfigMap
|
configmap *corev1.ConfigMap
|
||||||
secret *v1.Secret
|
secret *corev1.Secret
|
||||||
inputParameters map[string]apiextensionsv1.JSON
|
inputParameters map[string]apiextensionsv1.JSON
|
||||||
values map[string]string
|
values map[string]string
|
||||||
gotemplate bool
|
gotemplate bool
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
content []byte
|
content []byte
|
||||||
expectedError error
|
expectedError error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "simple case",
|
name: "simple case",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -47,7 +45,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -73,13 +71,13 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123
|
"key3": 123
|
||||||
}]
|
}]
|
||||||
}}`),
|
}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
"key2.key2_2.key2_2_1": "val2_2_1",
|
"key2.key2_2.key2_2_1": "val2_2_1",
|
||||||
"key3": "123",
|
"key3": "123",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -93,7 +91,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple case with values",
|
name: "simple case with values",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -103,7 +101,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -133,7 +131,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123
|
"key3": 123
|
||||||
}]
|
}]
|
||||||
}}`),
|
}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
@@ -141,7 +139,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": "123",
|
"key3": "123",
|
||||||
"values.valuekey1": "valuevalue1",
|
"values.valuekey1": "valuevalue1",
|
||||||
"values.valuekey2": "templated-val1",
|
"values.valuekey2": "templated-val1",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -155,7 +153,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple case with gotemplate",
|
name: "simple case with gotemplate",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -165,7 +163,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -191,17 +189,17 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123
|
"key3": 123
|
||||||
}]
|
}]
|
||||||
}}`),
|
}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2": map[string]interface{}{
|
"key2": map[string]any{
|
||||||
"key2_1": "val2_1",
|
"key2_1": "val2_1",
|
||||||
"key2_2": map[string]interface{}{
|
"key2_2": map[string]any{
|
||||||
"key2_2_1": "val2_2_1",
|
"key2_2_1": "val2_2_1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"key3": float64(123),
|
"key3": float64(123),
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -215,7 +213,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple case with appended params",
|
name: "simple case with appended params",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -225,7 +223,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -250,14 +248,14 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123,
|
"key3": 123,
|
||||||
"pkey2": "valplugin"
|
"pkey2": "valplugin"
|
||||||
}]}}`),
|
}]}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
"key2.key2_2.key2_2_1": "val2_2_1",
|
"key2.key2_2.key2_2_1": "val2_2_1",
|
||||||
"key3": "123",
|
"key3": "123",
|
||||||
"pkey2": "valplugin",
|
"pkey2": "valplugin",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -271,7 +269,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no params",
|
name: "no params",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -281,7 +279,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -304,14 +302,14 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123
|
"key3": 123
|
||||||
}]
|
}]
|
||||||
}}`),
|
}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
"key2.key2_2.key2_2_1": "val2_2_1",
|
"key2.key2_2.key2_2_1": "val2_2_1",
|
||||||
"key3": "123",
|
"key3": "123",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": map[string]map[string]interface{}{
|
"input": map[string]map[string]any{
|
||||||
"parameters": {},
|
"parameters": {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -321,7 +319,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty return",
|
name: "empty return",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -331,7 +329,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -343,12 +341,12 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
inputParameters: map[string]apiextensionsv1.JSON{},
|
inputParameters: map[string]apiextensionsv1.JSON{},
|
||||||
gotemplate: false,
|
gotemplate: false,
|
||||||
content: []byte(`{"input": {"parameters": []}}`),
|
content: []byte(`{"input": {"parameters": []}}`),
|
||||||
expected: []map[string]interface{}{},
|
expected: []map[string]any{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wrong return",
|
name: "wrong return",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -358,7 +356,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -370,12 +368,12 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
inputParameters: map[string]apiextensionsv1.JSON{},
|
inputParameters: map[string]apiextensionsv1.JSON{},
|
||||||
gotemplate: false,
|
gotemplate: false,
|
||||||
content: []byte(`wrong body ...`),
|
content: []byte(`wrong body ...`),
|
||||||
expected: []map[string]interface{}{},
|
expected: []map[string]any{},
|
||||||
expectedError: fmt.Errorf("error listing params: error get api 'set': invalid character 'w' looking for beginning of value: wrong body ..."),
|
expectedError: errors.New("error listing params: error get api 'set': invalid character 'w' looking for beginning of value: wrong body ..."),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "external secret",
|
name: "external secret",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -385,7 +383,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin-secret:plugin.token",
|
"token": "$plugin-secret:plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "plugin-secret",
|
Name: "plugin-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -410,14 +408,14 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123,
|
"key3": 123,
|
||||||
"pkey2": "valplugin"
|
"pkey2": "valplugin"
|
||||||
}]}}`),
|
}]}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
"key2.key2_2.key2_2_1": "val2_2_1",
|
"key2.key2_2.key2_2_1": "val2_2_1",
|
||||||
"key3": "123",
|
"key3": "123",
|
||||||
"pkey2": "valplugin",
|
"pkey2": "valplugin",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -431,7 +429,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no secret",
|
name: "no secret",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -441,7 +439,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{},
|
secret: &corev1.Secret{},
|
||||||
inputParameters: map[string]apiextensionsv1.JSON{
|
inputParameters: map[string]apiextensionsv1.JSON{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
"pkey2": {Raw: []byte(`"val2"`)},
|
"pkey2": {Raw: []byte(`"val2"`)},
|
||||||
@@ -459,13 +457,13 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123
|
"key3": 123
|
||||||
}]
|
}]
|
||||||
}}`),
|
}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
"key2.key2_2.key2_2_1": "val2_2_1",
|
"key2.key2_2.key2_2_1": "val2_2_1",
|
||||||
"key3": "123",
|
"key3": "123",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -475,12 +473,12 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: fmt.Errorf("error getting plugin from generator: error fetching Secret token: error fetching secret default/argocd-secret: secrets \"argocd-secret\" not found"),
|
expectedError: errors.New("error getting plugin from generator: error fetching Secret token: error fetching secret default/argocd-secret: secrets \"argocd-secret\" not found"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no configmap",
|
name: "no configmap",
|
||||||
configmap: &v1.ConfigMap{},
|
configmap: &corev1.ConfigMap{},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -506,13 +504,13 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123
|
"key3": 123
|
||||||
}]
|
}]
|
||||||
}}`),
|
}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
"key2.key2_2.key2_2_1": "val2_2_1",
|
"key2.key2_2.key2_2_1": "val2_2_1",
|
||||||
"key3": "123",
|
"key3": "123",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -522,11 +520,11 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: configmaps \"\" not found"),
|
expectedError: errors.New("error getting plugin from generator: error fetching ConfigMap: configmaps \"\" not found"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no baseUrl",
|
name: "no baseUrl",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -535,7 +533,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"token": "$plugin.token",
|
"token": "$plugin.token",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{
|
secret: &corev1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "argocd-secret",
|
Name: "argocd-secret",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -561,13 +559,13 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123
|
"key3": 123
|
||||||
}]
|
}]
|
||||||
}}`),
|
}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
"key2.key2_2.key2_2_1": "val2_2_1",
|
"key2.key2_2.key2_2_1": "val2_2_1",
|
||||||
"key3": "123",
|
"key3": "123",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -577,11 +575,11 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: baseUrl not found in ConfigMap"),
|
expectedError: errors.New("error getting plugin from generator: error fetching ConfigMap: baseUrl not found in ConfigMap"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no token",
|
name: "no token",
|
||||||
configmap: &v1.ConfigMap{
|
configmap: &corev1.ConfigMap{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "first-plugin-cm",
|
Name: "first-plugin-cm",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
@@ -590,7 +588,7 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"baseUrl": "http://127.0.0.1",
|
"baseUrl": "http://127.0.0.1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
secret: &v1.Secret{},
|
secret: &corev1.Secret{},
|
||||||
inputParameters: map[string]apiextensionsv1.JSON{
|
inputParameters: map[string]apiextensionsv1.JSON{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
"pkey2": {Raw: []byte(`"val2"`)},
|
"pkey2": {Raw: []byte(`"val2"`)},
|
||||||
@@ -608,13 +606,13 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
"key3": 123
|
"key3": 123
|
||||||
}]
|
}]
|
||||||
}}`),
|
}}`),
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2.key2_1": "val2_1",
|
"key2.key2_1": "val2_1",
|
||||||
"key2.key2_2.key2_2_1": "val2_2_1",
|
"key2.key2_2.key2_2_1": "val2_2_1",
|
||||||
"key3": "123",
|
"key3": "123",
|
||||||
"generator": map[string]interface{}{
|
"generator": map[string]any{
|
||||||
"input": argoprojiov1alpha1.PluginInput{
|
"input": argoprojiov1alpha1.PluginInput{
|
||||||
Parameters: argoprojiov1alpha1.PluginParameters{
|
Parameters: argoprojiov1alpha1.PluginParameters{
|
||||||
"pkey1": {Raw: []byte(`"val1"`)},
|
"pkey1": {Raw: []byte(`"val1"`)},
|
||||||
@@ -624,12 +622,10 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedError: fmt.Errorf("error getting plugin from generator: error fetching ConfigMap: token not found in ConfigMap"),
|
expectedError: errors.New("error getting plugin from generator: error fetching ConfigMap: token not found in ConfigMap"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||||
@@ -666,11 +662,9 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
testCase.configmap.Data["baseUrl"] = fakeServer.URL
|
testCase.configmap.Data["baseUrl"] = fakeServer.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
fakeClient := kubefake.NewSimpleClientset(append([]runtime.Object{}, testCase.configmap, testCase.secret)...)
|
|
||||||
|
|
||||||
fakeClientWithCache := fake.NewClientBuilder().WithObjects([]client.Object{testCase.configmap, testCase.secret}...).Build()
|
fakeClientWithCache := fake.NewClientBuilder().WithObjects([]client.Object{testCase.configmap, testCase.secret}...).Build()
|
||||||
|
|
||||||
pluginGenerator := NewPluginGenerator(fakeClientWithCache, ctx, fakeClient, "default")
|
pluginGenerator := NewPluginGenerator(fakeClientWithCache, "default")
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -690,11 +684,11 @@ func TestPluginGenerateParams(t *testing.T) {
|
|||||||
require.EqualError(t, err, testCase.expectedError.Error())
|
require.EqualError(t, err, testCase.expectedError.Error())
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expectedJson, err := json.Marshal(testCase.expected)
|
expectedJSON, err := json.Marshal(testCase.expected)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
gotJson, err := json.Marshal(got)
|
gotJSON, err := json.Marshal(got)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.JSONEq(t, string(expectedJson), string(gotJson))
|
assert.JSONEq(t, string(expectedJSON), string(gotJSON))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,25 @@ package generators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/gosimple/slug"
|
"github.com/gosimple/slug"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
pullrequest "github.com/argoproj/argo-cd/v2/applicationset/services/pull_request"
|
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
pullrequest "github.com/argoproj/argo-cd/v3/applicationset/services/pull_request"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*PullRequestGenerator)(nil)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultPullRequestRequeueAfterSeconds = 30 * time.Minute
|
DefaultPullRequestRequeueAfter = 30 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
type PullRequestGenerator struct {
|
type PullRequestGenerator struct {
|
||||||
@@ -43,20 +45,24 @@ func (g *PullRequestGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alph
|
|||||||
return time.Duration(*appSetGenerator.PullRequest.RequeueAfterSeconds) * time.Second
|
return time.Duration(*appSetGenerator.PullRequest.RequeueAfterSeconds) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefaultPullRequestRequeueAfterSeconds
|
return DefaultPullRequestRequeueAfter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *PullRequestGenerator) GetContinueOnRepoNotFoundError(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) bool {
|
||||||
|
return appSetGenerator.PullRequest.ContinueOnRepoNotFoundError
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *PullRequestGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||||
return &appSetGenerator.PullRequest.Template
|
return &appSetGenerator.PullRequest.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.PullRequest == nil {
|
if appSetGenerator.PullRequest == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -66,10 +72,15 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
}
|
}
|
||||||
|
|
||||||
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
|
pulls, err := pullrequest.ListPullRequests(ctx, svc, appSetGenerator.PullRequest.Filters)
|
||||||
|
params := make([]map[string]any, 0, len(pulls))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if pullrequest.IsRepositoryNotFoundError(err) && g.GetContinueOnRepoNotFoundError(appSetGenerator) {
|
||||||
|
log.WithError(err).WithField("generator", g).
|
||||||
|
Warn("Skipping params generation for this repository since it was not found.")
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("error listing repos: %w", err)
|
return nil, fmt.Errorf("error listing repos: %w", err)
|
||||||
}
|
}
|
||||||
params := make([]map[string]interface{}, 0, len(pulls))
|
|
||||||
|
|
||||||
// In order to follow the DNS label standard as defined in RFC 1123,
|
// In order to follow the DNS label standard as defined in RFC 1123,
|
||||||
// we need to limit the 'branch' to 50 to give room to append/suffix-ing it
|
// we need to limit the 'branch' to 50 to give room to append/suffix-ing it
|
||||||
@@ -95,7 +106,7 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
shortSHALength7 = len(pull.HeadSHA)
|
shortSHALength7 = len(pull.HeadSHA)
|
||||||
}
|
}
|
||||||
|
|
||||||
paramMap := map[string]interface{}{
|
paramMap := map[string]any{
|
||||||
"number": strconv.Itoa(pull.Number),
|
"number": strconv.Itoa(pull.Number),
|
||||||
"title": pull.Title,
|
"title": pull.Title,
|
||||||
"branch": pull.Branch,
|
"branch": pull.Branch,
|
||||||
@@ -112,6 +123,11 @@ func (g *PullRequestGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
if applicationSetInfo != nil && applicationSetInfo.Spec.GoTemplate {
|
if applicationSetInfo != nil && applicationSetInfo.Spec.GoTemplate {
|
||||||
paramMap["labels"] = pull.Labels
|
paramMap["labels"] = pull.Labels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := appendTemplatedValues(appSetGenerator.PullRequest.Values, paramMap, applicationSetInfo.Spec.GoTemplate, applicationSetInfo.Spec.GoTemplateOptions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to append templated values: %w", err)
|
||||||
|
}
|
||||||
params = append(params, paramMap)
|
params = append(params, paramMap)
|
||||||
}
|
}
|
||||||
return params, nil
|
return params, nil
|
||||||
@@ -143,7 +159,7 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
return pullrequest.NewGitLabService(token, providerConfig.API, providerConfig.Project, providerConfig.Labels, providerConfig.PullRequestState, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||||
}
|
}
|
||||||
if generatorConfig.Gitea != nil {
|
if generatorConfig.Gitea != nil {
|
||||||
providerConfig := generatorConfig.Gitea
|
providerConfig := generatorConfig.Gitea
|
||||||
@@ -151,7 +167,8 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
return pullrequest.NewGiteaService(ctx, token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Insecure)
|
|
||||||
|
return pullrequest.NewGiteaService(token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Labels, providerConfig.Insecure)
|
||||||
}
|
}
|
||||||
if generatorConfig.BitbucketServer != nil {
|
if generatorConfig.BitbucketServer != nil {
|
||||||
providerConfig := generatorConfig.BitbucketServer
|
providerConfig := generatorConfig.BitbucketServer
|
||||||
@@ -175,9 +192,8 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
|||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
return pullrequest.NewBitbucketServiceBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
return pullrequest.NewBitbucketServiceBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||||
} else {
|
|
||||||
return pullrequest.NewBitbucketServiceNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
|
||||||
}
|
}
|
||||||
|
return pullrequest.NewBitbucketServiceNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.Repo, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||||
}
|
}
|
||||||
if generatorConfig.Bitbucket != nil {
|
if generatorConfig.Bitbucket != nil {
|
||||||
providerConfig := generatorConfig.Bitbucket
|
providerConfig := generatorConfig.Bitbucket
|
||||||
@@ -193,9 +209,8 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
|||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
return pullrequest.NewBitbucketCloudServiceBasicAuth(providerConfig.API, providerConfig.BasicAuth.Username, password, providerConfig.Owner, providerConfig.Repo)
|
return pullrequest.NewBitbucketCloudServiceBasicAuth(providerConfig.API, providerConfig.BasicAuth.Username, password, providerConfig.Owner, providerConfig.Repo)
|
||||||
} else {
|
|
||||||
return pullrequest.NewBitbucketCloudServiceNoAuth(providerConfig.API, providerConfig.Owner, providerConfig.Repo)
|
|
||||||
}
|
}
|
||||||
|
return pullrequest.NewBitbucketCloudServiceNoAuth(providerConfig.API, providerConfig.Owner, providerConfig.Repo)
|
||||||
}
|
}
|
||||||
if generatorConfig.AzureDevOps != nil {
|
if generatorConfig.AzureDevOps != nil {
|
||||||
providerConfig := generatorConfig.AzureDevOps
|
providerConfig := generatorConfig.AzureDevOps
|
||||||
@@ -203,18 +218,33 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
return pullrequest.NewAzureDevOpsService(ctx, token, providerConfig.API, providerConfig.Organization, providerConfig.Project, providerConfig.Repo, providerConfig.Labels)
|
return pullrequest.NewAzureDevOpsService(token, providerConfig.API, providerConfig.Organization, providerConfig.Project, providerConfig.Repo, providerConfig.Labels)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("no Pull Request provider implementation configured")
|
return nil, errors.New("no Pull Request provider implementation configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alpha1.PullRequestGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alpha1.PullRequestGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
|
var metricsCtx *services.MetricsContext
|
||||||
|
var httpClient *http.Client
|
||||||
|
|
||||||
|
if g.enableGitHubAPIMetrics {
|
||||||
|
metricsCtx = &services.MetricsContext{
|
||||||
|
AppSetNamespace: applicationSetInfo.Namespace,
|
||||||
|
AppSetName: applicationSetInfo.Name,
|
||||||
|
}
|
||||||
|
httpClient = services.NewGitHubMetricsClient(metricsCtx)
|
||||||
|
}
|
||||||
|
|
||||||
// use an app if it was configured
|
// use an app if it was configured
|
||||||
if cfg.AppSecretName != "" {
|
if cfg.AppSecretName != "" {
|
||||||
auth, err := g.GitHubApps.GetAuthSecret(ctx, cfg.AppSecretName)
|
auth, err := g.GitHubApps.GetAuthSecret(ctx, cfg.AppSecretName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting GitHub App secret: %w", err)
|
return nil, fmt.Errorf("error getting GitHub App secret: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if g.enableGitHubAPIMetrics {
|
||||||
|
return pullrequest.NewGithubAppService(*auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels, httpClient)
|
||||||
|
}
|
||||||
return pullrequest.NewGithubAppService(*auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
return pullrequest.NewGithubAppService(*auth, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,5 +253,9 @@ func (g *PullRequestGenerator) github(ctx context.Context, cfg *argoprojiov1alph
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
return pullrequest.NewGithubService(ctx, token, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
|
||||||
|
if g.enableGitHubAPIMetrics {
|
||||||
|
return pullrequest.NewGithubService(token, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels, httpClient)
|
||||||
|
}
|
||||||
|
return pullrequest.NewGithubService(token, cfg.API, cfg.Owner, cfg.Repo, cfg.Labels)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,26 @@ package generators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
pullrequest "github.com/argoproj/argo-cd/v2/applicationset/services/pull_request"
|
pullrequest "github.com/argoproj/argo-cd/v3/applicationset/services/pull_request"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPullRequestGithubGenerateParams(t *testing.T) {
|
func TestPullRequestGithubGenerateParams(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
|
selectFunc func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
|
||||||
expected []map[string]interface{}
|
values map[string]string
|
||||||
expectedErr error
|
expected []map[string]any
|
||||||
applicationSet argoprojiov1alpha1.ApplicationSet
|
expectedErr error
|
||||||
|
applicationSet argoprojiov1alpha1.ApplicationSet
|
||||||
|
continueOnRepoNotFoundError bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
@@ -38,7 +40,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"number": "1",
|
"number": "1",
|
||||||
"title": "title1",
|
"title": "title1",
|
||||||
@@ -71,7 +73,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"number": "2",
|
"number": "2",
|
||||||
"title": "title2",
|
"title": "title2",
|
||||||
@@ -104,7 +106,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"number": "1",
|
"number": "1",
|
||||||
"title": "title1",
|
"title": "title1",
|
||||||
@@ -124,12 +126,75 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
return pullrequest.NewFakeService(
|
return pullrequest.NewFakeService(
|
||||||
ctx,
|
ctx,
|
||||||
|
[]*pullrequest.PullRequest{
|
||||||
|
{
|
||||||
|
Number: 1,
|
||||||
|
Title: "title1",
|
||||||
|
Branch: "my_branch",
|
||||||
|
TargetBranch: "master",
|
||||||
|
HeadSHA: "abcd",
|
||||||
|
Author: "testName",
|
||||||
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
fmt.Errorf("fake error"),
|
)
|
||||||
|
},
|
||||||
|
values: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"pr_branch": "{{ branch }}",
|
||||||
|
},
|
||||||
|
expected: []map[string]any{
|
||||||
|
{
|
||||||
|
"number": "1",
|
||||||
|
"title": "title1",
|
||||||
|
"branch": "my_branch",
|
||||||
|
"branch_slug": "my-branch",
|
||||||
|
"target_branch": "master",
|
||||||
|
"target_branch_slug": "master",
|
||||||
|
"head_sha": "abcd",
|
||||||
|
"head_short_sha": "abcd",
|
||||||
|
"head_short_sha_7": "abcd",
|
||||||
|
"author": "testName",
|
||||||
|
"values.foo": "bar",
|
||||||
|
"values.pr_branch": "my_branch",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
|
return pullrequest.NewFakeService(
|
||||||
|
ctx,
|
||||||
|
nil,
|
||||||
|
errors.New("fake error"),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
expectedErr: fmt.Errorf("error listing repos: fake error"),
|
expectedErr: errors.New("error listing repos: fake error"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
|
return pullrequest.NewFakeService(
|
||||||
|
ctx,
|
||||||
|
nil,
|
||||||
|
pullrequest.NewRepositoryNotFoundError(errors.New("repository not found")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
expected: []map[string]any{},
|
||||||
|
expectedErr: nil,
|
||||||
|
continueOnRepoNotFoundError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
|
return pullrequest.NewFakeService(
|
||||||
|
ctx,
|
||||||
|
nil,
|
||||||
|
pullrequest.NewRepositoryNotFoundError(errors.New("repository not found")),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
expectedErr: errors.New("error listing repos: repository not found"),
|
||||||
|
continueOnRepoNotFoundError: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
@@ -149,7 +214,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"number": "1",
|
"number": "1",
|
||||||
"title": "title1",
|
"title": "title1",
|
||||||
@@ -190,7 +255,7 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"number": "1",
|
"number": "1",
|
||||||
"title": "title1",
|
"title": "title1",
|
||||||
@@ -212,6 +277,51 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
|
||||||
|
return pullrequest.NewFakeService(
|
||||||
|
ctx,
|
||||||
|
[]*pullrequest.PullRequest{
|
||||||
|
{
|
||||||
|
Number: 1,
|
||||||
|
Title: "title1",
|
||||||
|
Branch: "my_branch",
|
||||||
|
TargetBranch: "master",
|
||||||
|
HeadSHA: "abcd",
|
||||||
|
Author: "testName",
|
||||||
|
Labels: []string{"preview", "preview:team1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
values: map[string]string{
|
||||||
|
"preview_env": "{{ regexFind \"(team1|team2)\" (.labels | join \",\") }}",
|
||||||
|
},
|
||||||
|
expected: []map[string]any{
|
||||||
|
{
|
||||||
|
"number": "1",
|
||||||
|
"title": "title1",
|
||||||
|
"branch": "my_branch",
|
||||||
|
"branch_slug": "my-branch",
|
||||||
|
"target_branch": "master",
|
||||||
|
"target_branch_slug": "master",
|
||||||
|
"head_sha": "abcd",
|
||||||
|
"head_short_sha": "abcd",
|
||||||
|
"head_short_sha_7": "abcd",
|
||||||
|
"author": "testName",
|
||||||
|
"labels": []string{"preview", "preview:team1"},
|
||||||
|
"values": map[string]string{"preview_env": "team1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedErr: nil,
|
||||||
|
applicationSet: argoprojiov1alpha1.ApplicationSet{
|
||||||
|
Spec: argoprojiov1alpha1.ApplicationSetSpec{
|
||||||
|
// Application set is using fasttemplate.
|
||||||
|
GoTemplate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@@ -219,12 +329,15 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
selectServiceProviderFunc: c.selectFunc,
|
selectServiceProviderFunc: c.selectFunc,
|
||||||
}
|
}
|
||||||
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
|
||||||
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{},
|
PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
|
||||||
|
Values: c.values,
|
||||||
|
ContinueOnRepoNotFoundError: c.continueOnRepoNotFoundError,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
got, gotErr := gen.GenerateParams(&generatorConfig, &c.applicationSet, nil)
|
got, gotErr := gen.GenerateParams(&generatorConfig, &c.applicationSet, nil)
|
||||||
if c.expectedErr != nil {
|
if c.expectedErr != nil {
|
||||||
assert.Equal(t, c.expectedErr.Error(), gotErr.Error())
|
require.EqualError(t, gotErr, c.expectedErr.Error())
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, gotErr)
|
require.NoError(t, gotErr)
|
||||||
}
|
}
|
||||||
@@ -233,6 +346,8 @@ func TestPullRequestGithubGenerateParams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
providerConfig *argoprojiov1alpha1.PullRequestGenerator
|
providerConfig *argoprojiov1alpha1.PullRequestGenerator
|
||||||
@@ -283,7 +398,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
|||||||
"gitea.myorg.com",
|
"gitea.myorg.com",
|
||||||
"bitbucket.myorg.com",
|
"bitbucket.myorg.com",
|
||||||
"azuredevops.myorg.com",
|
"azuredevops.myorg.com",
|
||||||
}, true, nil, true))
|
}, true, true, nil, true))
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@@ -306,7 +421,7 @@ func TestAllowedSCMProviderPullRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
|
func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
|
||||||
generator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{}, false, nil, true))
|
generator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{}, false, true, nil, true))
|
||||||
|
|
||||||
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,17 +12,18 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/github_app_auth"
|
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider"
|
||||||
"github.com/argoproj/argo-cd/v2/common"
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/v3/common"
|
||||||
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ Generator = (*SCMProviderGenerator)(nil)
|
var _ Generator = (*SCMProviderGenerator)(nil)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DefaultSCMProviderRequeueAfterSeconds = 30 * time.Minute
|
DefaultSCMProviderRequeueAfter = 30 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
type SCMProviderGenerator struct {
|
type SCMProviderGenerator struct {
|
||||||
@@ -31,20 +33,22 @@ type SCMProviderGenerator struct {
|
|||||||
SCMConfig
|
SCMConfig
|
||||||
}
|
}
|
||||||
type SCMConfig struct {
|
type SCMConfig struct {
|
||||||
scmRootCAPath string
|
scmRootCAPath string
|
||||||
allowedSCMProviders []string
|
allowedSCMProviders []string
|
||||||
enableSCMProviders bool
|
enableSCMProviders bool
|
||||||
GitHubApps github_app_auth.Credentials
|
enableGitHubAPIMetrics bool
|
||||||
tokenRefStrictMode bool
|
GitHubApps github_app_auth.Credentials
|
||||||
|
tokenRefStrictMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSCMConfig(scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool, gitHubApps github_app_auth.Credentials, tokenRefStrictMode bool) SCMConfig {
|
func NewSCMConfig(scmRootCAPath string, allowedSCMProviders []string, enableSCMProviders bool, enableGitHubAPIMetrics bool, gitHubApps github_app_auth.Credentials, tokenRefStrictMode bool) SCMConfig {
|
||||||
return SCMConfig{
|
return SCMConfig{
|
||||||
scmRootCAPath: scmRootCAPath,
|
scmRootCAPath: scmRootCAPath,
|
||||||
allowedSCMProviders: allowedSCMProviders,
|
allowedSCMProviders: allowedSCMProviders,
|
||||||
enableSCMProviders: enableSCMProviders,
|
enableSCMProviders: enableSCMProviders,
|
||||||
GitHubApps: gitHubApps,
|
enableGitHubAPIMetrics: enableGitHubAPIMetrics,
|
||||||
tokenRefStrictMode: tokenRefStrictMode,
|
GitHubApps: gitHubApps,
|
||||||
|
tokenRefStrictMode: tokenRefStrictMode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +73,7 @@ func (g *SCMProviderGenerator) GetRequeueAfter(appSetGenerator *argoprojiov1alph
|
|||||||
return time.Duration(*appSetGenerator.SCMProvider.RequeueAfterSeconds) * time.Second
|
return time.Duration(*appSetGenerator.SCMProvider.RequeueAfterSeconds) * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefaultSCMProviderRequeueAfterSeconds
|
return DefaultSCMProviderRequeueAfter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
func (g *SCMProviderGenerator) GetTemplate(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator) *argoprojiov1alpha1.ApplicationSetTemplate {
|
||||||
@@ -116,13 +120,13 @@ func ScmProviderAllowed(applicationSetInfo *argoprojiov1alpha1.ApplicationSet, g
|
|||||||
return NewErrDisallowedSCMProvider(url, allowedScmProviders)
|
return NewErrDisallowedSCMProvider(url, allowedScmProviders)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]interface{}, error) {
|
func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha1.ApplicationSetGenerator, applicationSetInfo *argoprojiov1alpha1.ApplicationSet, _ client.Client) ([]map[string]any, error) {
|
||||||
if appSetGenerator == nil {
|
if appSetGenerator == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if appSetGenerator.SCMProvider == nil {
|
if appSetGenerator.SCMProvider == nil {
|
||||||
return nil, EmptyAppSetGeneratorError
|
return nil, ErrEmptyAppSetGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
if !g.enableSCMProviders {
|
if !g.enableSCMProviders {
|
||||||
@@ -138,15 +142,16 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
var provider scm_provider.SCMProviderService
|
var provider scm_provider.SCMProviderService
|
||||||
if g.overrideProvider != nil {
|
switch {
|
||||||
|
case g.overrideProvider != nil:
|
||||||
provider = g.overrideProvider
|
provider = g.overrideProvider
|
||||||
} else if providerConfig.Github != nil {
|
case providerConfig.Github != nil:
|
||||||
var err error
|
var err error
|
||||||
provider, err = g.githubProvider(ctx, providerConfig.Github, applicationSetInfo)
|
provider, err = g.githubProvider(ctx, providerConfig.Github, applicationSetInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("scm provider: %w", err)
|
return nil, fmt.Errorf("scm provider: %w", err)
|
||||||
}
|
}
|
||||||
} else if providerConfig.Gitlab != nil {
|
case providerConfig.Gitlab != nil:
|
||||||
providerConfig := providerConfig.Gitlab
|
providerConfig := providerConfig.Gitlab
|
||||||
var caCerts []byte
|
var caCerts []byte
|
||||||
var scmError error
|
var scmError error
|
||||||
@@ -160,20 +165,20 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Gitlab token: %w", err)
|
return nil, fmt.Errorf("error fetching Gitlab token: %w", err)
|
||||||
}
|
}
|
||||||
provider, err = scm_provider.NewGitlabProvider(ctx, providerConfig.Group, token, providerConfig.API, providerConfig.AllBranches, providerConfig.IncludeSubgroups, providerConfig.WillIncludeSharedProjects(), providerConfig.Insecure, g.scmRootCAPath, providerConfig.Topic, caCerts)
|
provider, err = scm_provider.NewGitlabProvider(providerConfig.Group, token, providerConfig.API, providerConfig.AllBranches, providerConfig.IncludeSubgroups, providerConfig.WillIncludeSharedProjects(), providerConfig.Insecure, g.scmRootCAPath, providerConfig.Topic, caCerts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error initializing Gitlab service: %w", err)
|
return nil, fmt.Errorf("error initializing Gitlab service: %w", err)
|
||||||
}
|
}
|
||||||
} else if providerConfig.Gitea != nil {
|
case providerConfig.Gitea != nil:
|
||||||
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.Gitea.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Gitea token: %w", err)
|
return nil, fmt.Errorf("error fetching Gitea token: %w", err)
|
||||||
}
|
}
|
||||||
provider, err = scm_provider.NewGiteaProvider(ctx, providerConfig.Gitea.Owner, token, providerConfig.Gitea.API, providerConfig.Gitea.AllBranches, providerConfig.Gitea.Insecure)
|
provider, err = scm_provider.NewGiteaProvider(providerConfig.Gitea.Owner, token, providerConfig.Gitea.API, providerConfig.Gitea.AllBranches, providerConfig.Gitea.Insecure)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error initializing Gitea service: %w", err)
|
return nil, fmt.Errorf("error initializing Gitea service: %w", err)
|
||||||
}
|
}
|
||||||
} else if providerConfig.BitbucketServer != nil {
|
case providerConfig.BitbucketServer != nil:
|
||||||
providerConfig := providerConfig.BitbucketServer
|
providerConfig := providerConfig.BitbucketServer
|
||||||
var caCerts []byte
|
var caCerts []byte
|
||||||
var scmError error
|
var scmError error
|
||||||
@@ -183,50 +188,51 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", scmError)
|
return nil, fmt.Errorf("error fetching CA certificates from ConfigMap: %w", scmError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if providerConfig.BearerToken != nil {
|
switch {
|
||||||
|
case providerConfig.BearerToken != nil:
|
||||||
appToken, err := utils.GetSecretRef(ctx, g.client, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
appToken, err := utils.GetSecretRef(ctx, g.client, providerConfig.BearerToken.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Secret Bearer token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret Bearer token: %w", err)
|
||||||
}
|
}
|
||||||
provider, scmError = scm_provider.NewBitbucketServerProviderBearerToken(ctx, appToken, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
provider, scmError = scm_provider.NewBitbucketServerProviderBearerToken(ctx, appToken, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||||
} else if providerConfig.BasicAuth != nil {
|
case providerConfig.BasicAuth != nil:
|
||||||
password, err := utils.GetSecretRef(ctx, g.client, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
password, err := utils.GetSecretRef(ctx, g.client, providerConfig.BasicAuth.PasswordRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
return nil, fmt.Errorf("error fetching Secret token: %w", err)
|
||||||
}
|
}
|
||||||
provider, scmError = scm_provider.NewBitbucketServerProviderBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
provider, scmError = scm_provider.NewBitbucketServerProviderBasicAuth(ctx, providerConfig.BasicAuth.Username, password, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||||
} else {
|
default:
|
||||||
provider, scmError = scm_provider.NewBitbucketServerProviderNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
provider, scmError = scm_provider.NewBitbucketServerProviderNoAuth(ctx, providerConfig.API, providerConfig.Project, providerConfig.AllBranches, g.scmRootCAPath, providerConfig.Insecure, caCerts)
|
||||||
}
|
}
|
||||||
if scmError != nil {
|
if scmError != nil {
|
||||||
return nil, fmt.Errorf("error initializing Bitbucket Server service: %w", scmError)
|
return nil, fmt.Errorf("error initializing Bitbucket Server service: %w", scmError)
|
||||||
}
|
}
|
||||||
} else if providerConfig.AzureDevOps != nil {
|
case providerConfig.AzureDevOps != nil:
|
||||||
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
token, err := utils.GetSecretRef(ctx, g.client, providerConfig.AzureDevOps.AccessTokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Azure Devops access token: %w", err)
|
return nil, fmt.Errorf("error fetching Azure Devops access token: %w", err)
|
||||||
}
|
}
|
||||||
provider, err = scm_provider.NewAzureDevOpsProvider(ctx, token, providerConfig.AzureDevOps.Organization, providerConfig.AzureDevOps.API, providerConfig.AzureDevOps.TeamProject, providerConfig.AzureDevOps.AllBranches)
|
provider, err = scm_provider.NewAzureDevOpsProvider(token, providerConfig.AzureDevOps.Organization, providerConfig.AzureDevOps.API, providerConfig.AzureDevOps.TeamProject, providerConfig.AzureDevOps.AllBranches)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error initializing Azure Devops service: %w", err)
|
return nil, fmt.Errorf("error initializing Azure Devops service: %w", err)
|
||||||
}
|
}
|
||||||
} else if providerConfig.Bitbucket != nil {
|
case providerConfig.Bitbucket != nil:
|
||||||
appPassword, err := utils.GetSecretRef(ctx, g.client, providerConfig.Bitbucket.AppPasswordRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
appPassword, err := utils.GetSecretRef(ctx, g.client, providerConfig.Bitbucket.AppPasswordRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Bitbucket cloud appPassword: %w", err)
|
return nil, fmt.Errorf("error fetching Bitbucket cloud appPassword: %w", err)
|
||||||
}
|
}
|
||||||
provider, err = scm_provider.NewBitBucketCloudProvider(ctx, providerConfig.Bitbucket.Owner, providerConfig.Bitbucket.User, appPassword, providerConfig.Bitbucket.AllBranches)
|
provider, err = scm_provider.NewBitBucketCloudProvider(providerConfig.Bitbucket.Owner, providerConfig.Bitbucket.User, appPassword, providerConfig.Bitbucket.AllBranches)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error initializing Bitbucket cloud service: %w", err)
|
return nil, fmt.Errorf("error initializing Bitbucket cloud service: %w", err)
|
||||||
}
|
}
|
||||||
} else if providerConfig.AWSCodeCommit != nil {
|
case providerConfig.AWSCodeCommit != nil:
|
||||||
var awsErr error
|
var awsErr error
|
||||||
provider, awsErr = scm_provider.NewAWSCodeCommitProvider(ctx, providerConfig.AWSCodeCommit.TagFilters, providerConfig.AWSCodeCommit.Role, providerConfig.AWSCodeCommit.Region, providerConfig.AWSCodeCommit.AllBranches)
|
provider, awsErr = scm_provider.NewAWSCodeCommitProvider(ctx, providerConfig.AWSCodeCommit.TagFilters, providerConfig.AWSCodeCommit.Role, providerConfig.AWSCodeCommit.Region, providerConfig.AWSCodeCommit.AllBranches)
|
||||||
if awsErr != nil {
|
if awsErr != nil {
|
||||||
return nil, fmt.Errorf("error initializing AWS codecommit service: %w", awsErr)
|
return nil, fmt.Errorf("error initializing AWS codecommit service: %w", awsErr)
|
||||||
}
|
}
|
||||||
} else {
|
default:
|
||||||
return nil, fmt.Errorf("no SCM provider implementation configured")
|
return nil, errors.New("no SCM provider implementation configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all the available repos.
|
// Find all the available repos.
|
||||||
@@ -234,7 +240,7 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error listing repos: %w", err)
|
return nil, fmt.Errorf("error listing repos: %w", err)
|
||||||
}
|
}
|
||||||
paramsArray := make([]map[string]interface{}, 0, len(repos))
|
paramsArray := make([]map[string]any, 0, len(repos))
|
||||||
var shortSHALength int
|
var shortSHALength int
|
||||||
var shortSHALength7 int
|
var shortSHALength7 int
|
||||||
for _, repo := range repos {
|
for _, repo := range repos {
|
||||||
@@ -248,9 +254,10 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
shortSHALength7 = len(repo.SHA)
|
shortSHALength7 = len(repo.SHA)
|
||||||
}
|
}
|
||||||
|
|
||||||
params := map[string]interface{}{
|
params := map[string]any{
|
||||||
"organization": repo.Organization,
|
"organization": repo.Organization,
|
||||||
"repository": repo.Repository,
|
"repository": repo.Repository,
|
||||||
|
"repository_id": repo.RepositoryId,
|
||||||
"url": repo.URL,
|
"url": repo.URL,
|
||||||
"branch": repo.Branch,
|
"branch": repo.Branch,
|
||||||
"sha": repo.SHA,
|
"sha": repo.SHA,
|
||||||
@@ -271,23 +278,36 @@ func (g *SCMProviderGenerator) GenerateParams(appSetGenerator *argoprojiov1alpha
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argoprojiov1alpha1.SCMProviderGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (scm_provider.SCMProviderService, error) {
|
func (g *SCMProviderGenerator) githubProvider(ctx context.Context, github *argoprojiov1alpha1.SCMProviderGeneratorGithub, applicationSetInfo *argoprojiov1alpha1.ApplicationSet) (scm_provider.SCMProviderService, error) {
|
||||||
|
var metricsCtx *services.MetricsContext
|
||||||
|
var httpClient *http.Client
|
||||||
|
|
||||||
|
if g.enableGitHubAPIMetrics {
|
||||||
|
metricsCtx = &services.MetricsContext{
|
||||||
|
AppSetNamespace: applicationSetInfo.Namespace,
|
||||||
|
AppSetName: applicationSetInfo.Name,
|
||||||
|
}
|
||||||
|
httpClient = services.NewGitHubMetricsClient(metricsCtx)
|
||||||
|
}
|
||||||
|
|
||||||
if github.AppSecretName != "" {
|
if github.AppSecretName != "" {
|
||||||
auth, err := g.GitHubApps.GetAuthSecret(ctx, github.AppSecretName)
|
auth, err := g.GitHubApps.GetAuthSecret(ctx, github.AppSecretName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Github app secret: %w", err)
|
return nil, fmt.Errorf("error fetching Github app secret: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return scm_provider.NewGithubAppProviderFor(
|
if g.enableGitHubAPIMetrics {
|
||||||
*auth,
|
return scm_provider.NewGithubAppProviderFor(*auth, github.Organization, github.API, github.AllBranches, httpClient)
|
||||||
github.Organization,
|
}
|
||||||
github.API,
|
return scm_provider.NewGithubAppProviderFor(*auth, github.Organization, github.API, github.AllBranches)
|
||||||
github.AllBranches,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := utils.GetSecretRef(ctx, g.client, github.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
token, err := utils.GetSecretRef(ctx, g.client, github.TokenRef, applicationSetInfo.Namespace, g.tokenRefStrictMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching Github token: %w", err)
|
return nil, fmt.Errorf("error fetching Github token: %w", err)
|
||||||
}
|
}
|
||||||
return scm_provider.NewGithubProvider(ctx, github.Organization, token, github.API, github.AllBranches)
|
|
||||||
|
if g.enableGitHubAPIMetrics {
|
||||||
|
return scm_provider.NewGithubProvider(github.Organization, token, github.API, github.AllBranches, httpClient)
|
||||||
|
}
|
||||||
|
return scm_provider.NewGithubProvider(github.Organization, token, github.API, github.AllBranches)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,18 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider"
|
||||||
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSCMProviderGenerateParams(t *testing.T) {
|
func TestSCMProviderGenerateParams(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
repos []*scm_provider.Repository
|
repos []*scm_provider.Repository
|
||||||
values map[string]string
|
values map[string]string
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
expectedError error
|
expectedError error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -25,6 +27,7 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Organization: "myorg",
|
Organization: "myorg",
|
||||||
Repository: "repo1",
|
Repository: "repo1",
|
||||||
|
RepositoryId: 190320251,
|
||||||
URL: "git@github.com:myorg/repo1.git",
|
URL: "git@github.com:myorg/repo1.git",
|
||||||
Branch: "main",
|
Branch: "main",
|
||||||
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||||
@@ -33,15 +36,17 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Organization: "myorg",
|
Organization: "myorg",
|
||||||
Repository: "repo2",
|
Repository: "repo2",
|
||||||
|
RepositoryId: 190320252,
|
||||||
URL: "git@github.com:myorg/repo2.git",
|
URL: "git@github.com:myorg/repo2.git",
|
||||||
Branch: "main",
|
Branch: "main",
|
||||||
SHA: "59d0",
|
SHA: "59d0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"organization": "myorg",
|
"organization": "myorg",
|
||||||
"repository": "repo1",
|
"repository": "repo1",
|
||||||
|
"repository_id": 190320251,
|
||||||
"url": "git@github.com:myorg/repo1.git",
|
"url": "git@github.com:myorg/repo1.git",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"branchNormalized": "main",
|
"branchNormalized": "main",
|
||||||
@@ -53,6 +58,7 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"organization": "myorg",
|
"organization": "myorg",
|
||||||
"repository": "repo2",
|
"repository": "repo2",
|
||||||
|
"repository_id": 190320252,
|
||||||
"url": "git@github.com:myorg/repo2.git",
|
"url": "git@github.com:myorg/repo2.git",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"branchNormalized": "main",
|
"branchNormalized": "main",
|
||||||
@@ -69,6 +75,7 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
{
|
{
|
||||||
Organization: "myorg",
|
Organization: "myorg",
|
||||||
Repository: "repo3",
|
Repository: "repo3",
|
||||||
|
RepositoryId: 190320253,
|
||||||
URL: "git@github.com:myorg/repo3.git",
|
URL: "git@github.com:myorg/repo3.git",
|
||||||
Branch: "main",
|
Branch: "main",
|
||||||
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||||
@@ -79,10 +86,11 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
"should_i_force_push_to": "{{ branch }}?",
|
"should_i_force_push_to": "{{ branch }}?",
|
||||||
},
|
},
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"organization": "myorg",
|
"organization": "myorg",
|
||||||
"repository": "repo3",
|
"repository": "repo3",
|
||||||
|
"repository_id": 190320253,
|
||||||
"url": "git@github.com:myorg/repo3.git",
|
"url": "git@github.com:myorg/repo3.git",
|
||||||
"branch": "main",
|
"branch": "main",
|
||||||
"branchNormalized": "main",
|
"branchNormalized": "main",
|
||||||
@@ -95,6 +103,52 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Repos with and without id",
|
||||||
|
repos: []*scm_provider.Repository{
|
||||||
|
{
|
||||||
|
Organization: "myorg",
|
||||||
|
Repository: "repo4",
|
||||||
|
RepositoryId: "idaz09",
|
||||||
|
URL: "git@github.com:myorg/repo4.git",
|
||||||
|
Branch: "main",
|
||||||
|
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Organization: "myorg",
|
||||||
|
Repository: "repo5",
|
||||||
|
URL: "git@github.com:myorg/repo5.git",
|
||||||
|
Branch: "main",
|
||||||
|
SHA: "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []map[string]any{
|
||||||
|
{
|
||||||
|
"organization": "myorg",
|
||||||
|
"repository": "repo4",
|
||||||
|
"repository_id": "idaz09",
|
||||||
|
"url": "git@github.com:myorg/repo4.git",
|
||||||
|
"branch": "main",
|
||||||
|
"branchNormalized": "main",
|
||||||
|
"sha": "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||||
|
"short_sha": "0bc57212",
|
||||||
|
"short_sha_7": "0bc5721",
|
||||||
|
"labels": "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"organization": "myorg",
|
||||||
|
"repository": "repo5",
|
||||||
|
"repository_id": nil,
|
||||||
|
"url": "git@github.com:myorg/repo5.git",
|
||||||
|
"branch": "main",
|
||||||
|
"branchNormalized": "main",
|
||||||
|
"sha": "0bc57212c3cbbec69d20b34c507284bd300def5b",
|
||||||
|
"short_sha": "0bc57212",
|
||||||
|
"short_sha_7": "0bc5721",
|
||||||
|
"labels": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range cases {
|
for _, testCase := range cases {
|
||||||
@@ -133,6 +187,8 @@ func TestSCMProviderGenerateParams(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAllowedSCMProvider(t *testing.T) {
|
func TestAllowedSCMProvider(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
providerConfig *argoprojiov1alpha1.SCMProviderGenerator
|
providerConfig *argoprojiov1alpha1.SCMProviderGenerator
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
package generators
|
package generators
|
||||||
|
|
||||||
type SCMGeneratorWithCustomApiUrl interface {
|
type SCMGeneratorWithCustomApiUrl interface { //nolint:revive //FIXME(var-naming)
|
||||||
CustomApiUrl() string
|
CustomApiUrl() string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,18 @@ import (
|
|||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services"
|
"github.com/argoproj/argo-cd/v3/applicationset/services"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, namespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
|
func GetGenerators(ctx context.Context, c client.Client, k8sClient kubernetes.Interface, controllerNamespace string, argoCDService services.Repos, dynamicClient dynamic.Interface, scmConfig SCMConfig) map[string]Generator {
|
||||||
terminalGenerators := map[string]Generator{
|
terminalGenerators := map[string]Generator{
|
||||||
"List": NewListGenerator(),
|
"List": NewListGenerator(),
|
||||||
"Clusters": NewClusterGenerator(c, ctx, k8sClient, namespace),
|
"Clusters": NewClusterGenerator(ctx, c, k8sClient, controllerNamespace),
|
||||||
"Git": NewGitGenerator(argoCDService, namespace),
|
"Git": NewGitGenerator(argoCDService, controllerNamespace),
|
||||||
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
|
"SCMProvider": NewSCMProviderGenerator(c, scmConfig),
|
||||||
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, namespace),
|
"ClusterDecisionResource": NewDuckTypeGenerator(ctx, dynamicClient, k8sClient, controllerNamespace),
|
||||||
"PullRequest": NewPullRequestGenerator(c, scmConfig),
|
"PullRequest": NewPullRequestGenerator(c, scmConfig),
|
||||||
"Plugin": NewPluginGenerator(c, ctx, k8sClient, namespace),
|
"Plugin": NewPluginGenerator(c, controllerNamespace),
|
||||||
}
|
}
|
||||||
|
|
||||||
nestedGenerators := map[string]Generator{
|
nestedGenerators := map[string]Generator{
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func appendTemplatedValues(values map[string]string, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) error {
|
func appendTemplatedValues(values map[string]string, params map[string]any, useGoTemplate bool, goTemplateOptions []string) error {
|
||||||
// We create a local map to ensure that we do not fall victim to a billion-laughs attack. We iterate through the
|
// We create a local map to ensure that we do not fall victim to a billion-laughs attack. We iterate through the
|
||||||
// cluster values map and only replace values in said map if it has already been allowlisted in the params map.
|
// cluster values map and only replace values in said map if it has already been allowlisted in the params map.
|
||||||
// Once we iterate through all the cluster values we can then safely merge the `tmp` map into the main params map.
|
// Once we iterate through all the cluster values we can then safely merge the `tmp` map into the main params map.
|
||||||
tmp := map[string]interface{}{}
|
tmp := map[string]any{}
|
||||||
|
|
||||||
for key, value := range values {
|
for key, value := range values {
|
||||||
result, err := replaceTemplatedString(value, params, useGoTemplate, goTemplateOptions)
|
result, err := replaceTemplatedString(value, params, useGoTemplate, goTemplateOptions)
|
||||||
@@ -22,7 +22,7 @@ func appendTemplatedValues(values map[string]string, params map[string]interface
|
|||||||
}
|
}
|
||||||
tmp["values"].(map[string]string)[key] = result
|
tmp["values"].(map[string]string)[key] = result
|
||||||
} else {
|
} else {
|
||||||
tmp[fmt.Sprintf("values.%s", key)] = result
|
tmp["values."+key] = result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ func appendTemplatedValues(values map[string]string, params map[string]interface
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func replaceTemplatedString(value string, params map[string]interface{}, useGoTemplate bool, goTemplateOptions []string) (string, error) {
|
func replaceTemplatedString(value string, params map[string]any, useGoTemplate bool, goTemplateOptions []string) (string, error) {
|
||||||
replacedTmplStr, err := render.Replace(value, params, useGoTemplate, goTemplateOptions)
|
replacedTmplStr, err := render.Replace(value, params, useGoTemplate, goTemplateOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to replace templated string with rendered values: %w", err)
|
return "", fmt.Errorf("failed to replace templated string with rendered values: %w", err)
|
||||||
|
|||||||
@@ -11,18 +11,18 @@ func TestValueInterpolation(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
values map[string]string
|
values map[string]string
|
||||||
params map[string]interface{}
|
params map[string]any
|
||||||
expected map[string]interface{}
|
expected map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Simple interpolation",
|
name: "Simple interpolation",
|
||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"hello": "{{ world }}",
|
"hello": "{{ world }}",
|
||||||
},
|
},
|
||||||
params: map[string]interface{}{
|
params: map[string]any{
|
||||||
"world": "world!",
|
"world": "world!",
|
||||||
},
|
},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]any{
|
||||||
"world": "world!",
|
"world": "world!",
|
||||||
"values.hello": "world!",
|
"values.hello": "world!",
|
||||||
},
|
},
|
||||||
@@ -32,8 +32,8 @@ func TestValueInterpolation(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"non-existent": "{{ non-existent }}",
|
"non-existent": "{{ non-existent }}",
|
||||||
},
|
},
|
||||||
params: map[string]interface{}{},
|
params: map[string]any{},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]any{
|
||||||
"values.non-existent": "{{ non-existent }}",
|
"values.non-existent": "{{ non-existent }}",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -44,8 +44,8 @@ func TestValueInterpolation(t *testing.T) {
|
|||||||
"lol2": "{{values.lol1}}{{values.lol1}}",
|
"lol2": "{{values.lol1}}{{values.lol1}}",
|
||||||
"lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}",
|
"lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}",
|
||||||
},
|
},
|
||||||
params: map[string]interface{}{},
|
params: map[string]any{},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]any{
|
||||||
"values.lol1": "lol",
|
"values.lol1": "lol",
|
||||||
"values.lol2": "{{values.lol1}}{{values.lol1}}",
|
"values.lol2": "{{values.lol1}}{{values.lol1}}",
|
||||||
"values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}",
|
"values.lol3": "{{values.lol2}}{{values.lol2}}{{values.lol2}}",
|
||||||
@@ -57,7 +57,7 @@ func TestValueInterpolation(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
err := appendTemplatedValues(testCase.values, testCase.params, false, nil)
|
err := appendTemplatedValues(testCase.values, testCase.params, false, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.EqualValues(t, testCase.expected, testCase.params)
|
assert.Equal(t, testCase.expected, testCase.params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,18 +66,18 @@ func TestValueInterpolationWithGoTemplating(t *testing.T) {
|
|||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
values map[string]string
|
values map[string]string
|
||||||
params map[string]interface{}
|
params map[string]any
|
||||||
expected map[string]interface{}
|
expected map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Simple interpolation",
|
name: "Simple interpolation",
|
||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"hello": "{{ .world }}",
|
"hello": "{{ .world }}",
|
||||||
},
|
},
|
||||||
params: map[string]interface{}{
|
params: map[string]any{
|
||||||
"world": "world!",
|
"world": "world!",
|
||||||
},
|
},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]any{
|
||||||
"world": "world!",
|
"world": "world!",
|
||||||
"values": map[string]string{
|
"values": map[string]string{
|
||||||
"hello": "world!",
|
"hello": "world!",
|
||||||
@@ -89,8 +89,8 @@ func TestValueInterpolationWithGoTemplating(t *testing.T) {
|
|||||||
values: map[string]string{
|
values: map[string]string{
|
||||||
"non_existent": "{{ default \"bar\" .non_existent }}",
|
"non_existent": "{{ default \"bar\" .non_existent }}",
|
||||||
},
|
},
|
||||||
params: map[string]interface{}{},
|
params: map[string]any{},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]any{
|
||||||
"values": map[string]string{
|
"values": map[string]string{
|
||||||
"non_existent": "bar",
|
"non_existent": "bar",
|
||||||
},
|
},
|
||||||
@@ -103,8 +103,8 @@ func TestValueInterpolationWithGoTemplating(t *testing.T) {
|
|||||||
"lol2": "{{.values.lol1}}{{.values.lol1}}",
|
"lol2": "{{.values.lol1}}{{.values.lol1}}",
|
||||||
"lol3": "{{.values.lol2}}{{.values.lol2}}{{.values.lol2}}",
|
"lol3": "{{.values.lol2}}{{.values.lol2}}{{.values.lol2}}",
|
||||||
},
|
},
|
||||||
params: map[string]interface{}{},
|
params: map[string]any{},
|
||||||
expected: map[string]interface{}{
|
expected: map[string]any{
|
||||||
"values": map[string]string{
|
"values": map[string]string{
|
||||||
"lol1": "lol",
|
"lol1": "lol",
|
||||||
"lol2": "<no value><no value>",
|
"lol2": "<no value><no value>",
|
||||||
@@ -118,7 +118,7 @@ func TestValueInterpolationWithGoTemplating(t *testing.T) {
|
|||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
err := appendTemplatedValues(testCase.values, testCase.params, true, nil)
|
err := appendTemplatedValues(testCase.values, testCase.params, true, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.EqualValues(t, testCase.expected, testCase.params)
|
assert.Equal(t, testCase.expected, testCase.params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ package metrics
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fake implementation for testing
|
// Fake implementation for testing
|
||||||
func NewFakeAppsetMetrics(client ctrlclient.WithWatch) *ApplicationsetMetrics {
|
func NewFakeAppsetMetrics() *ApplicationsetMetrics {
|
||||||
reconcileHistogram := prometheus.NewHistogramVec(
|
reconcileHistogram := prometheus.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
prometheus.HistogramOpts{
|
||||||
Name: "argocd_appset_reconcile",
|
Name: "argocd_appset_reconcile",
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||||
|
|
||||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
argoappv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
|
applisters "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1"
|
||||||
metricsutil "github.com/argoproj/argo-cd/v2/util/metrics"
|
metricsutil "github.com/argoproj/argo-cd/v3/util/metrics"
|
||||||
|
"github.com/argoproj/argo-cd/v3/util/metrics/kubectl"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -57,6 +58,9 @@ func NewApplicationsetMetrics(appsetLister applisters.ApplicationSetLister, apps
|
|||||||
metrics.Registry.MustRegister(reconcileHistogram)
|
metrics.Registry.MustRegister(reconcileHistogram)
|
||||||
metrics.Registry.MustRegister(appsetCollector)
|
metrics.Registry.MustRegister(appsetCollector)
|
||||||
|
|
||||||
|
kubectl.RegisterWithClientGo()
|
||||||
|
kubectl.RegisterWithPrometheus(metrics.Registry)
|
||||||
|
|
||||||
return ApplicationsetMetrics{
|
return ApplicationsetMetrics{
|
||||||
reconcileHistogram: reconcileHistogram,
|
reconcileHistogram: reconcileHistogram,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,23 +7,19 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/utils"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
fake "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
prometheus "github.com/prometheus/client_golang/prometheus"
|
|
||||||
|
|
||||||
metricsutil "github.com/argoproj/argo-cd/v2/util/metrics"
|
|
||||||
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||||
|
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
|
"github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
|
argoappv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
|
metricsutil "github.com/argoproj/argo-cd/v3/util/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -178,7 +174,7 @@ func TestApplicationsetCollector(t *testing.T) {
|
|||||||
appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter)
|
appsetCollector := newAppsetCollector(utils.NewAppsetLister(client), collectedLabels, filter)
|
||||||
|
|
||||||
metrics.Registry.MustRegister(appsetCollector)
|
metrics.Registry.MustRegister(appsetCollector)
|
||||||
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
req, err := http.NewRequest(http.MethodGet, "/metrics", http.NoBody)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
||||||
@@ -220,7 +216,7 @@ func TestObserveReconcile(t *testing.T) {
|
|||||||
|
|
||||||
appsetMetrics := NewApplicationsetMetrics(utils.NewAppsetLister(client), collectedLabels, filter)
|
appsetMetrics := NewApplicationsetMetrics(utils.NewAppsetLister(client), collectedLabels, filter)
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodGet, "/metrics", nil)
|
req, err := http.NewRequest(http.MethodGet, "/metrics", http.NoBody)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
handler := promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})
|
||||||
|
|||||||
241
applicationset/services/github_metrics.go
Normal file
241
applicationset/services/github_metrics.go
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Doc for the GitHub API rate limit headers:
|
||||||
|
// https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#checking-the-status-of-your-rate-limit
|
||||||
|
|
||||||
|
// Metric names as constants
|
||||||
|
const (
|
||||||
|
githubAPIRequestTotalMetricName = "argocd_github_api_requests_total"
|
||||||
|
githubAPIRequestDurationMetricName = "argocd_github_api_request_duration_seconds"
|
||||||
|
githubAPIRateLimitRemainingMetricName = "argocd_github_api_rate_limit_remaining"
|
||||||
|
githubAPIRateLimitLimitMetricName = "argocd_github_api_rate_limit_limit"
|
||||||
|
githubAPIRateLimitResetMetricName = "argocd_github_api_rate_limit_reset_seconds"
|
||||||
|
githubAPIRateLimitUsedMetricName = "argocd_github_api_rate_limit_used"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitHubMetrics groups all metric vectors for easier injection and registration
|
||||||
|
type GitHubMetrics struct {
|
||||||
|
RequestTotal *prometheus.CounterVec
|
||||||
|
RequestDuration *prometheus.HistogramVec
|
||||||
|
RateLimitRemaining *prometheus.GaugeVec
|
||||||
|
RateLimitLimit *prometheus.GaugeVec
|
||||||
|
RateLimitReset *prometheus.GaugeVec
|
||||||
|
RateLimitUsed *prometheus.GaugeVec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory for a new set of GitHub metrics (for tests or custom registries)
|
||||||
|
func NewGitHubMetrics() *GitHubMetrics {
|
||||||
|
return &GitHubMetrics{
|
||||||
|
RequestTotal: NewGitHubAPIRequestTotal(),
|
||||||
|
RequestDuration: NewGitHubAPIRequestDuration(),
|
||||||
|
RateLimitRemaining: NewGitHubAPIRateLimitRemaining(),
|
||||||
|
RateLimitLimit: NewGitHubAPIRateLimitLimit(),
|
||||||
|
RateLimitReset: NewGitHubAPIRateLimitReset(),
|
||||||
|
RateLimitUsed: NewGitHubAPIRateLimitUsed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory functions for each metric vector
|
||||||
|
func NewGitHubAPIRequestTotal() *prometheus.CounterVec {
|
||||||
|
return prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: githubAPIRequestTotalMetricName,
|
||||||
|
Help: "Total number of GitHub API requests",
|
||||||
|
},
|
||||||
|
[]string{"method", "endpoint", "status", "appset_namespace", "appset_name"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitHubAPIRequestDuration() *prometheus.HistogramVec {
|
||||||
|
return prometheus.NewHistogramVec(
|
||||||
|
prometheus.HistogramOpts{
|
||||||
|
Name: githubAPIRequestDurationMetricName,
|
||||||
|
Help: "GitHub API request duration in seconds",
|
||||||
|
Buckets: prometheus.DefBuckets,
|
||||||
|
},
|
||||||
|
[]string{"method", "endpoint", "appset_namespace", "appset_name"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitHubAPIRateLimitRemaining() *prometheus.GaugeVec {
|
||||||
|
return prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: githubAPIRateLimitRemainingMetricName,
|
||||||
|
Help: "The number of requests remaining in the current rate limit window",
|
||||||
|
},
|
||||||
|
[]string{"endpoint", "appset_namespace", "appset_name", "resource"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitHubAPIRateLimitLimit() *prometheus.GaugeVec {
|
||||||
|
return prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: githubAPIRateLimitLimitMetricName,
|
||||||
|
Help: "The maximum number of requests that you can make per hour",
|
||||||
|
},
|
||||||
|
[]string{"endpoint", "appset_namespace", "appset_name", "resource"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitHubAPIRateLimitReset() *prometheus.GaugeVec {
|
||||||
|
return prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: githubAPIRateLimitResetMetricName,
|
||||||
|
Help: "The time left till the current rate limit window resets, in seconds",
|
||||||
|
},
|
||||||
|
[]string{"endpoint", "appset_namespace", "appset_name", "resource"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGitHubAPIRateLimitUsed() *prometheus.GaugeVec {
|
||||||
|
return prometheus.NewGaugeVec(
|
||||||
|
prometheus.GaugeOpts{
|
||||||
|
Name: githubAPIRateLimitUsedMetricName,
|
||||||
|
Help: "The number of requests used in the current rate limit window",
|
||||||
|
},
|
||||||
|
[]string{"endpoint", "appset_namespace", "appset_name", "resource"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global metrics (registered with the default registry)
|
||||||
|
var globalGitHubMetrics = NewGitHubMetrics()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.Debug("Registering GitHub API AppSet metrics")
|
||||||
|
metrics.Registry.MustRegister(globalGitHubMetrics.RequestTotal)
|
||||||
|
metrics.Registry.MustRegister(globalGitHubMetrics.RequestDuration)
|
||||||
|
metrics.Registry.MustRegister(globalGitHubMetrics.RateLimitRemaining)
|
||||||
|
metrics.Registry.MustRegister(globalGitHubMetrics.RateLimitLimit)
|
||||||
|
metrics.Registry.MustRegister(globalGitHubMetrics.RateLimitReset)
|
||||||
|
metrics.Registry.MustRegister(globalGitHubMetrics.RateLimitUsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetricsContext struct {
|
||||||
|
AppSetNamespace string
|
||||||
|
AppSetName string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitHubMetricsTransport is a custom http.RoundTripper that collects GitHub API metrics
|
||||||
|
type GitHubMetricsTransport struct {
|
||||||
|
transport http.RoundTripper
|
||||||
|
metricsContext *MetricsContext
|
||||||
|
metrics *GitHubMetrics
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoundTrip implements http.RoundTripper interface and collects metrics along with debug logging
|
||||||
|
func (t *GitHubMetricsTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
endpoint := req.URL.Path
|
||||||
|
method := req.Method
|
||||||
|
|
||||||
|
appsetNamespace := "unknown"
|
||||||
|
appsetName := "unknown"
|
||||||
|
|
||||||
|
if t.metricsContext != nil {
|
||||||
|
appsetNamespace = t.metricsContext.AppSetNamespace
|
||||||
|
appsetName = t.metricsContext.AppSetName
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"method": method,
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"applicationset": map[string]string{"name": appsetName, "namespace": appsetNamespace},
|
||||||
|
}).Debugf("Invoking GitHub API")
|
||||||
|
|
||||||
|
startTime := time.Now()
|
||||||
|
resp, err := t.transport.RoundTrip(req)
|
||||||
|
duration := time.Since(startTime)
|
||||||
|
|
||||||
|
// Record metrics
|
||||||
|
t.metrics.RequestDuration.WithLabelValues(method, endpoint, appsetNamespace, appsetName).Observe(duration.Seconds())
|
||||||
|
|
||||||
|
status := "0"
|
||||||
|
if resp != nil {
|
||||||
|
status = strconv.Itoa(resp.StatusCode)
|
||||||
|
}
|
||||||
|
t.metrics.RequestTotal.WithLabelValues(method, endpoint, status, appsetNamespace, appsetName).Inc()
|
||||||
|
|
||||||
|
if resp != nil {
|
||||||
|
resetHumanReadableTime := ""
|
||||||
|
remainingInt := 0
|
||||||
|
limitInt := 0
|
||||||
|
usedInt := 0
|
||||||
|
resource := resp.Header.Get("X-RateLimit-Resource")
|
||||||
|
|
||||||
|
// Record rate limit metrics if available
|
||||||
|
if resetTime := resp.Header.Get("X-RateLimit-Reset"); resetTime != "" {
|
||||||
|
if resetUnix, err := strconv.ParseInt(resetTime, 10, 64); err == nil {
|
||||||
|
// Calculate seconds until reset (reset timestamp - current time)
|
||||||
|
secondsUntilReset := resetUnix - time.Now().Unix()
|
||||||
|
t.metrics.RateLimitReset.WithLabelValues(endpoint, appsetNamespace, appsetName, resource).Set(float64(secondsUntilReset))
|
||||||
|
resetHumanReadableTime = time.Unix(resetUnix, 0).Local().Format("2006-01-02 15:04:05 MST")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if remaining := resp.Header.Get("X-RateLimit-Remaining"); remaining != "" {
|
||||||
|
if remainingInt, err = strconv.Atoi(remaining); err == nil {
|
||||||
|
t.metrics.RateLimitRemaining.WithLabelValues(endpoint, appsetNamespace, appsetName, resource).Set(float64(remainingInt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if limit := resp.Header.Get("X-RateLimit-Limit"); limit != "" {
|
||||||
|
if limitInt, err = strconv.Atoi(limit); err == nil {
|
||||||
|
t.metrics.RateLimitLimit.WithLabelValues(endpoint, appsetNamespace, appsetName, resource).Set(float64(limitInt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if used := resp.Header.Get("X-RateLimit-Used"); used != "" {
|
||||||
|
if usedInt, err = strconv.Atoi(used); err == nil {
|
||||||
|
t.metrics.RateLimitUsed.WithLabelValues(endpoint, appsetNamespace, appsetName, resource).Set(float64(usedInt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"reset": resetHumanReadableTime,
|
||||||
|
"remaining": remainingInt,
|
||||||
|
"limit": limitInt,
|
||||||
|
"used": usedInt,
|
||||||
|
"resource": resource,
|
||||||
|
"applicationset": map[string]string{"name": appsetName, "namespace": appsetNamespace},
|
||||||
|
}).Debugf("GitHub API rate limit info")
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full constructor (for tests and advanced use)
|
||||||
|
func NewGitHubMetricsTransport(
|
||||||
|
transport http.RoundTripper,
|
||||||
|
metricsContext *MetricsContext,
|
||||||
|
metrics *GitHubMetrics,
|
||||||
|
) *GitHubMetricsTransport {
|
||||||
|
return &GitHubMetricsTransport{
|
||||||
|
transport: transport,
|
||||||
|
metricsContext: metricsContext,
|
||||||
|
metrics: metrics,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default constructor
|
||||||
|
func NewDefaultGitHubMetricsTransport(transport http.RoundTripper, metricsContext *MetricsContext) *GitHubMetricsTransport {
|
||||||
|
return NewGitHubMetricsTransport(
|
||||||
|
transport,
|
||||||
|
metricsContext,
|
||||||
|
globalGitHubMetrics,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGitHubMetricsClient wraps an http.Client with metrics middleware
|
||||||
|
func NewGitHubMetricsClient(metricsContext *MetricsContext) *http.Client {
|
||||||
|
log.Debug("Creating new GitHub metrics client")
|
||||||
|
return &http.Client{
|
||||||
|
Transport: NewDefaultGitHubMetricsTransport(http.DefaultTransport, metricsContext),
|
||||||
|
}
|
||||||
|
}
|
||||||
221
applicationset/services/github_metrics_test.go
Normal file
221
applicationset/services/github_metrics_test.go
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Metric struct {
|
||||||
|
name string
|
||||||
|
labels []string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
endpointLabel = "endpoint=\"/api/test\""
|
||||||
|
URL = "/api/test"
|
||||||
|
appsetNamespaceLabel = "appset_namespace=\"test-ns\""
|
||||||
|
appsetNamespace = "test-ns"
|
||||||
|
appsetName = "test-appset"
|
||||||
|
appsetNameLabel = "appset_name=\"test-appset\""
|
||||||
|
resourceLabel = "resource=\"core\""
|
||||||
|
|
||||||
|
rateLimitMetrics = []Metric{
|
||||||
|
{
|
||||||
|
name: githubAPIRateLimitRemainingMetricName,
|
||||||
|
labels: []string{endpointLabel, appsetNamespaceLabel, appsetNameLabel, resourceLabel},
|
||||||
|
value: "42",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: githubAPIRateLimitLimitMetricName,
|
||||||
|
labels: []string{endpointLabel, appsetNamespaceLabel, appsetNameLabel, resourceLabel},
|
||||||
|
value: "100",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: githubAPIRateLimitUsedMetricName,
|
||||||
|
labels: []string{endpointLabel, appsetNamespaceLabel, appsetNameLabel, resourceLabel},
|
||||||
|
value: "58",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: githubAPIRateLimitResetMetricName,
|
||||||
|
labels: []string{endpointLabel, appsetNamespaceLabel, appsetNameLabel, resourceLabel},
|
||||||
|
value: "1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
successRequestMetrics = Metric{
|
||||||
|
name: githubAPIRequestTotalMetricName,
|
||||||
|
labels: []string{"method=\"GET\"", endpointLabel, "status=\"201\"", appsetNamespaceLabel, appsetNameLabel},
|
||||||
|
value: "1",
|
||||||
|
}
|
||||||
|
failureRequestMetrics = Metric{
|
||||||
|
name: githubAPIRequestTotalMetricName,
|
||||||
|
labels: []string{"method=\"GET\"", endpointLabel, "status=\"0\"", appsetNamespaceLabel, appsetNameLabel},
|
||||||
|
value: "1",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGitHubMetrics_CollectorApproach_Success(t *testing.T) {
|
||||||
|
metrics := NewGitHubMetrics()
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
reg.MustRegister(
|
||||||
|
metrics.RequestTotal,
|
||||||
|
metrics.RequestDuration,
|
||||||
|
metrics.RateLimitRemaining,
|
||||||
|
metrics.RateLimitLimit,
|
||||||
|
metrics.RateLimitReset,
|
||||||
|
metrics.RateLimitUsed,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Setup a fake HTTP server
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Unix()+1, 10))
|
||||||
|
w.Header().Set("X-RateLimit-Remaining", "42")
|
||||||
|
w.Header().Set("X-RateLimit-Limit", "100")
|
||||||
|
w.Header().Set("X-RateLimit-Used", "58")
|
||||||
|
w.Header().Set("X-RateLimit-Resource", "core")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
_, _ = w.Write([]byte("ok"))
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
metricsCtx := &MetricsContext{AppSetNamespace: appsetNamespace, AppSetName: appsetName}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: NewGitHubMetricsTransport(
|
||||||
|
http.DefaultTransport,
|
||||||
|
metricsCtx,
|
||||||
|
metrics,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, ts.URL+URL, http.NoBody)
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
// Expose and scrape metrics
|
||||||
|
handler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
|
||||||
|
server := httptest.NewServer(handler)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
resp, err = http.Get(server.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to scrape metrics: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
metricsOutput := string(body)
|
||||||
|
|
||||||
|
sort.Strings(successRequestMetrics.labels)
|
||||||
|
assert.Contains(t, metricsOutput, successRequestMetrics.name+"{"+strings.Join(successRequestMetrics.labels, ",")+"} "+successRequestMetrics.value)
|
||||||
|
|
||||||
|
for _, metric := range rateLimitMetrics {
|
||||||
|
sort.Strings(metric.labels)
|
||||||
|
assert.Contains(t, metricsOutput, metric.name+"{"+strings.Join(metric.labels, ",")+"} "+metric.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoundTripperFunc func(*http.Request) (*http.Response, error)
|
||||||
|
|
||||||
|
func (f RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
|
||||||
|
|
||||||
|
func TestGitHubMetrics_CollectorApproach_NoRateLimitMetricsOnNilResponse(t *testing.T) {
|
||||||
|
metrics := NewGitHubMetrics()
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
reg.MustRegister(
|
||||||
|
metrics.RequestTotal,
|
||||||
|
metrics.RequestDuration,
|
||||||
|
metrics.RateLimitRemaining,
|
||||||
|
metrics.RateLimitLimit,
|
||||||
|
metrics.RateLimitReset,
|
||||||
|
metrics.RateLimitUsed,
|
||||||
|
)
|
||||||
|
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: &GitHubMetricsTransport{
|
||||||
|
transport: RoundTripperFunc(func(*http.Request) (*http.Response, error) {
|
||||||
|
return nil, http.ErrServerClosed
|
||||||
|
}),
|
||||||
|
metricsContext: &MetricsContext{AppSetNamespace: appsetNamespace, AppSetName: appsetName},
|
||||||
|
metrics: metrics,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, URL, http.NoBody)
|
||||||
|
_, _ = client.Do(req)
|
||||||
|
|
||||||
|
handler := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
|
||||||
|
server := httptest.NewServer(handler)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
resp, err := http.Get(server.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to scrape metrics: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
metricsOutput := string(body)
|
||||||
|
|
||||||
|
// Verify request metric exists with status "0"
|
||||||
|
sort.Strings(failureRequestMetrics.labels)
|
||||||
|
assert.Contains(t, metricsOutput, failureRequestMetrics.name+"{"+strings.Join(failureRequestMetrics.labels, ",")+"} "+failureRequestMetrics.value)
|
||||||
|
|
||||||
|
// Verify rate limit metrics don't exist
|
||||||
|
for _, metric := range rateLimitMetrics {
|
||||||
|
sort.Strings(metric.labels)
|
||||||
|
assert.NotContains(t, metricsOutput, metric.name+"{"+strings.Join(metric.labels, ",")+"} "+metric.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewGitHubMetricsClient(t *testing.T) {
|
||||||
|
// Test cases
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
metricsCtx *MetricsContext
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "with metrics context",
|
||||||
|
metricsCtx: &MetricsContext{
|
||||||
|
AppSetNamespace: appsetNamespace,
|
||||||
|
AppSetName: appsetName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with nil metrics context",
|
||||||
|
metricsCtx: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
// Create client
|
||||||
|
client := NewGitHubMetricsClient(tc.metricsCtx)
|
||||||
|
|
||||||
|
// Assert client is not nil
|
||||||
|
assert.NotNil(t, client)
|
||||||
|
|
||||||
|
// Assert transport is properly configured
|
||||||
|
transport, ok := client.Transport.(*GitHubMetricsTransport)
|
||||||
|
assert.True(t, ok, "Transport should be GitHubMetricsTransport")
|
||||||
|
|
||||||
|
// Verify transport configuration
|
||||||
|
assert.Equal(t, tc.metricsCtx, transport.metricsContext)
|
||||||
|
assert.NotNil(t, transport.metrics, "Metrics should not be nil")
|
||||||
|
assert.Equal(t, http.DefaultTransport, transport.transport, "Base transport should be http.DefaultTransport")
|
||||||
|
|
||||||
|
// Verify metrics are global metrics
|
||||||
|
assert.Equal(t, globalGitHubMetrics, transport.metrics, "Should use global metrics")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,14 +5,27 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/bradleyfalzon/ghinstallation/v2"
|
"github.com/bradleyfalzon/ghinstallation/v2"
|
||||||
"github.com/google/go-github/v66/github"
|
"github.com/google/go-github/v69/github"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/applicationset/services/github_app_auth"
|
"github.com/argoproj/argo-cd/v3/applicationset/services/github_app_auth"
|
||||||
|
appsetutils "github.com/argoproj/argo-cd/v3/applicationset/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getOptionalHTTPClientAndTransport(optionalHTTPClient ...*http.Client) (*http.Client, http.RoundTripper) {
|
||||||
|
httpClient := appsetutils.GetOptionalHTTPClient(optionalHTTPClient...)
|
||||||
|
if len(optionalHTTPClient) > 0 && optionalHTTPClient[0] != nil && optionalHTTPClient[0].Transport != nil {
|
||||||
|
// will either use the provided custom httpClient and it's transport
|
||||||
|
return httpClient, optionalHTTPClient[0].Transport
|
||||||
|
}
|
||||||
|
// or the default httpClient and transport
|
||||||
|
return httpClient, http.DefaultTransport
|
||||||
|
}
|
||||||
|
|
||||||
// Client builds a github client for the given app authentication.
|
// Client builds a github client for the given app authentication.
|
||||||
func Client(g github_app_auth.Authentication, url string) (*github.Client, error) {
|
func Client(g github_app_auth.Authentication, url string, optionalHTTPClient ...*http.Client) (*github.Client, error) {
|
||||||
rt, err := ghinstallation.New(http.DefaultTransport, g.Id, g.InstallationId, []byte(g.PrivateKey))
|
httpClient, transport := getOptionalHTTPClientAndTransport(optionalHTTPClient...)
|
||||||
|
|
||||||
|
rt, err := ghinstallation.New(transport, g.Id, g.InstallationId, []byte(g.PrivateKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create github app install: %w", err)
|
return nil, fmt.Errorf("failed to create github app install: %w", err)
|
||||||
}
|
}
|
||||||
@@ -20,13 +33,12 @@ func Client(g github_app_auth.Authentication, url string) (*github.Client, error
|
|||||||
url = g.EnterpriseBaseURL
|
url = g.EnterpriseBaseURL
|
||||||
}
|
}
|
||||||
var client *github.Client
|
var client *github.Client
|
||||||
|
httpClient.Transport = rt
|
||||||
if url == "" {
|
if url == "" {
|
||||||
httpClient := http.Client{Transport: rt}
|
client = github.NewClient(httpClient)
|
||||||
client = github.NewClient(&httpClient)
|
|
||||||
} else {
|
} else {
|
||||||
rt.BaseURL = url
|
rt.BaseURL = url
|
||||||
httpClient := http.Client{Transport: rt}
|
client, err = github.NewClient(httpClient).WithEnterpriseURLs(url, url)
|
||||||
client, err = github.NewClient(&httpClient).WithEnterpriseURLs(url, url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create github enterprise client: %w", err)
|
return nil, fmt.Errorf("failed to create github enterprise client: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func newClient(baseURL string, options ...ClientOptionFunc) (*Client, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) NewRequest(method, path string, body interface{}, options []ClientOptionFunc) (*http.Request, error) {
|
func (c *Client) NewRequestWithContext(ctx context.Context, method, path string, body any) (*http.Request, error) {
|
||||||
// Make sure the given URL end with a slash
|
// Make sure the given URL end with a slash
|
||||||
if !strings.HasSuffix(c.baseURL, "/") {
|
if !strings.HasSuffix(c.baseURL, "/") {
|
||||||
c.baseURL += "/"
|
c.baseURL += "/"
|
||||||
@@ -82,7 +82,7 @@ func (c *Client) NewRequest(method, path string, body interface{}, options []Cli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(method, c.baseURL+path, buf)
|
req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -91,7 +91,7 @@ func (c *Client) NewRequest(method, path string, body interface{}, options []Cli
|
|||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.token) != 0 {
|
if c.token != "" {
|
||||||
req.Header.Set("Authorization", "Bearer "+c.token)
|
req.Header.Set("Authorization", "Bearer "+c.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ func (c *Client) NewRequest(method, path string, body interface{}, options []Cli
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*http.Response, error) {
|
func (c *Client) Do(req *http.Request, v any) (*http.Response, error) {
|
||||||
resp, err := c.client.Do(req)
|
resp, err := c.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -143,7 +143,7 @@ func CheckResponse(resp *http.Response) error {
|
|||||||
return fmt.Errorf("API error with status code %d: %w", resp.StatusCode, err)
|
return fmt.Errorf("API error with status code %d: %w", resp.StatusCode, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var raw map[string]interface{}
|
var raw map[string]any
|
||||||
if err := json.Unmarshal(data, &raw); err != nil {
|
if err := json.Unmarshal(data, &raw); err != nil {
|
||||||
return fmt.Errorf("API error with status code %d: %s", resp.StatusCode, string(data))
|
return fmt.Errorf("API error with status code %d: %s", resp.StatusCode, string(data))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestClient(t *testing.T) {
|
func TestClient(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_, err := w.Write([]byte("Hello, World!"))
|
_, err := w.Write([]byte("Hello, World!"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -29,15 +29,13 @@ func TestClient(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestClientDo(t *testing.T) {
|
func TestClientDo(t *testing.T) {
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
for _, c := range []struct {
|
for _, c := range []struct {
|
||||||
name string
|
name string
|
||||||
params map[string]string
|
params map[string]string
|
||||||
content []byte
|
content []byte
|
||||||
fakeServer *httptest.Server
|
fakeServer *httptest.Server
|
||||||
clientOptionFns []ClientOptionFunc
|
clientOptionFns []ClientOptionFunc
|
||||||
expected []map[string]interface{}
|
expected []map[string]any
|
||||||
expectedCode int
|
expectedCode int
|
||||||
expectedError error
|
expectedError error
|
||||||
}{
|
}{
|
||||||
@@ -47,7 +45,7 @@ func TestClientDo(t *testing.T) {
|
|||||||
"pkey1": "val1",
|
"pkey1": "val1",
|
||||||
"pkey2": "val2",
|
"pkey2": "val2",
|
||||||
},
|
},
|
||||||
fakeServer: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
fakeServer: httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
_, err := w.Write([]byte(`[{
|
_, err := w.Write([]byte(`[{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
@@ -64,12 +62,12 @@ func TestClientDo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
clientOptionFns: nil,
|
clientOptionFns: nil,
|
||||||
expected: []map[string]interface{}{
|
expected: []map[string]any{
|
||||||
{
|
{
|
||||||
"key1": "val1",
|
"key1": "val1",
|
||||||
"key2": map[string]interface{}{
|
"key2": map[string]any{
|
||||||
"key2_1": "val2_1",
|
"key2_1": "val2_1",
|
||||||
"key2_2": map[string]interface{}{
|
"key2_2": map[string]any{
|
||||||
"key2_2_1": "val2_2_1",
|
"key2_2_1": "val2_2_1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -107,9 +105,9 @@ func TestClientDo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
clientOptionFns: nil,
|
clientOptionFns: nil,
|
||||||
expected: []map[string]interface{}(nil),
|
expected: []map[string]any(nil),
|
||||||
expectedCode: http.StatusUnauthorized,
|
expectedCode: http.StatusUnauthorized,
|
||||||
expectedError: fmt.Errorf("API error with status code 401: "),
|
expectedError: errors.New("API error with status code 401: "),
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
cc := c
|
cc := c
|
||||||
@@ -119,12 +117,12 @@ func TestClientDo(t *testing.T) {
|
|||||||
client, err := NewClient(cc.fakeServer.URL, cc.clientOptionFns...)
|
client, err := NewClient(cc.fakeServer.URL, cc.clientOptionFns...)
|
||||||
require.NoError(t, err, "NewClient returned unexpected error")
|
require.NoError(t, err, "NewClient returned unexpected error")
|
||||||
|
|
||||||
req, err := client.NewRequest("POST", "", cc.params, nil)
|
req, err := client.NewRequestWithContext(t.Context(), http.MethodPost, "", cc.params)
|
||||||
require.NoError(t, err, "NewRequest returned unexpected error")
|
require.NoError(t, err, "NewRequest returned unexpected error")
|
||||||
|
|
||||||
var data []map[string]interface{}
|
var data []map[string]any
|
||||||
|
|
||||||
resp, err := client.Do(ctx, req, &data)
|
resp, err := client.Do(req, &data)
|
||||||
|
|
||||||
if cc.expectedError != nil {
|
if cc.expectedError != nil {
|
||||||
assert.EqualError(t, err, cc.expectedError.Error())
|
assert.EqualError(t, err, cc.expectedError.Error())
|
||||||
|
|||||||
274
applicationset/services/mocks/Repos.go
generated
274
applicationset/services/mocks/Repos.go
generated
@@ -1,78 +1,15 @@
|
|||||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
// Code generated by mockery; DO NOT EDIT.
|
||||||
|
// github.com/vektra/mockery
|
||||||
|
// template: testify
|
||||||
|
|
||||||
package mocks
|
package mocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
"context"
|
||||||
|
|
||||||
mock "github.com/stretchr/testify/mock"
|
mock "github.com/stretchr/testify/mock"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repos is an autogenerated mock type for the Repos type
|
|
||||||
type Repos struct {
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDirectories provides a mock function with given fields: ctx, repoURL, revision, noRevisionCache, verifyCommit
|
|
||||||
func (_m *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, noRevisionCache bool, verifyCommit bool) ([]string, error) {
|
|
||||||
ret := _m.Called(ctx, repoURL, revision, noRevisionCache, verifyCommit)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GetDirectories")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 []string
|
|
||||||
var r1 error
|
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool, bool) ([]string, error)); ok {
|
|
||||||
return rf(ctx, repoURL, revision, noRevisionCache, verifyCommit)
|
|
||||||
}
|
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, bool, bool) []string); ok {
|
|
||||||
r0 = rf(ctx, repoURL, revision, noRevisionCache, verifyCommit)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).([]string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, bool, bool) error); ok {
|
|
||||||
r1 = rf(ctx, repoURL, revision, noRevisionCache, verifyCommit)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFiles provides a mock function with given fields: ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit
|
|
||||||
func (_m *Repos) GetFiles(ctx context.Context, repoURL string, revision string, pattern string, noRevisionCache bool, verifyCommit bool) (map[string][]byte, error) {
|
|
||||||
ret := _m.Called(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit)
|
|
||||||
|
|
||||||
if len(ret) == 0 {
|
|
||||||
panic("no return value specified for GetFiles")
|
|
||||||
}
|
|
||||||
|
|
||||||
var r0 map[string][]byte
|
|
||||||
var r1 error
|
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) (map[string][]byte, error)); ok {
|
|
||||||
return rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit)
|
|
||||||
}
|
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) map[string][]byte); ok {
|
|
||||||
r0 = rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit)
|
|
||||||
} else {
|
|
||||||
if ret.Get(0) != nil {
|
|
||||||
r0 = ret.Get(0).(map[string][]byte)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rf, ok := ret.Get(1).(func(context.Context, string, string, string, bool, bool) error); ok {
|
|
||||||
r1 = rf(ctx, repoURL, revision, pattern, noRevisionCache, verifyCommit)
|
|
||||||
} else {
|
|
||||||
r1 = ret.Error(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return r0, r1
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
// NewRepos creates a new instance of Repos. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||||
// The first argument is typically a *testing.T value.
|
// The first argument is typically a *testing.T value.
|
||||||
func NewRepos(t interface {
|
func NewRepos(t interface {
|
||||||
@@ -86,3 +23,206 @@ func NewRepos(t interface {
|
|||||||
|
|
||||||
return mock
|
return mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Repos is an autogenerated mock type for the Repos type
|
||||||
|
type Repos struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
type Repos_Expecter struct {
|
||||||
|
mock *mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_m *Repos) EXPECT() *Repos_Expecter {
|
||||||
|
return &Repos_Expecter{mock: &_m.Mock}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDirectories provides a mock function for the type Repos
|
||||||
|
func (_mock *Repos) GetDirectories(ctx context.Context, repoURL string, revision string, project string, noRevisionCache bool, verifyCommit bool) ([]string, error) {
|
||||||
|
ret := _mock.Called(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetDirectories")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 []string
|
||||||
|
var r1 error
|
||||||
|
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) ([]string, error)); ok {
|
||||||
|
return returnFunc(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
||||||
|
}
|
||||||
|
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, bool, bool) []string); ok {
|
||||||
|
r0 = returnFunc(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, string, bool, bool) error); ok {
|
||||||
|
r1 = returnFunc(ctx, repoURL, revision, project, noRevisionCache, verifyCommit)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repos_GetDirectories_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetDirectories'
|
||||||
|
type Repos_GetDirectories_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDirectories is a helper method to define mock.On call
|
||||||
|
// - ctx context.Context
|
||||||
|
// - repoURL string
|
||||||
|
// - revision string
|
||||||
|
// - project string
|
||||||
|
// - noRevisionCache bool
|
||||||
|
// - verifyCommit bool
|
||||||
|
func (_e *Repos_Expecter) GetDirectories(ctx interface{}, repoURL interface{}, revision interface{}, project interface{}, noRevisionCache interface{}, verifyCommit interface{}) *Repos_GetDirectories_Call {
|
||||||
|
return &Repos_GetDirectories_Call{Call: _e.mock.On("GetDirectories", ctx, repoURL, revision, project, noRevisionCache, verifyCommit)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Repos_GetDirectories_Call) Run(run func(ctx context.Context, repoURL string, revision string, project string, noRevisionCache bool, verifyCommit bool)) *Repos_GetDirectories_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
var arg0 context.Context
|
||||||
|
if args[0] != nil {
|
||||||
|
arg0 = args[0].(context.Context)
|
||||||
|
}
|
||||||
|
var arg1 string
|
||||||
|
if args[1] != nil {
|
||||||
|
arg1 = args[1].(string)
|
||||||
|
}
|
||||||
|
var arg2 string
|
||||||
|
if args[2] != nil {
|
||||||
|
arg2 = args[2].(string)
|
||||||
|
}
|
||||||
|
var arg3 string
|
||||||
|
if args[3] != nil {
|
||||||
|
arg3 = args[3].(string)
|
||||||
|
}
|
||||||
|
var arg4 bool
|
||||||
|
if args[4] != nil {
|
||||||
|
arg4 = args[4].(bool)
|
||||||
|
}
|
||||||
|
var arg5 bool
|
||||||
|
if args[5] != nil {
|
||||||
|
arg5 = args[5].(bool)
|
||||||
|
}
|
||||||
|
run(
|
||||||
|
arg0,
|
||||||
|
arg1,
|
||||||
|
arg2,
|
||||||
|
arg3,
|
||||||
|
arg4,
|
||||||
|
arg5,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Repos_GetDirectories_Call) Return(strings []string, err error) *Repos_GetDirectories_Call {
|
||||||
|
_c.Call.Return(strings, err)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Repos_GetDirectories_Call) RunAndReturn(run func(ctx context.Context, repoURL string, revision string, project string, noRevisionCache bool, verifyCommit bool) ([]string, error)) *Repos_GetDirectories_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFiles provides a mock function for the type Repos
|
||||||
|
func (_mock *Repos) GetFiles(ctx context.Context, repoURL string, revision string, project string, pattern string, noRevisionCache bool, verifyCommit bool) (map[string][]byte, error) {
|
||||||
|
ret := _mock.Called(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for GetFiles")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 map[string][]byte
|
||||||
|
var r1 error
|
||||||
|
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, string, bool, bool) (map[string][]byte, error)); ok {
|
||||||
|
return returnFunc(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
||||||
|
}
|
||||||
|
if returnFunc, ok := ret.Get(0).(func(context.Context, string, string, string, string, bool, bool) map[string][]byte); ok {
|
||||||
|
r0 = returnFunc(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(map[string][]byte)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if returnFunc, ok := ret.Get(1).(func(context.Context, string, string, string, string, bool, bool) error); ok {
|
||||||
|
r1 = returnFunc(ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repos_GetFiles_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetFiles'
|
||||||
|
type Repos_GetFiles_Call struct {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFiles is a helper method to define mock.On call
|
||||||
|
// - ctx context.Context
|
||||||
|
// - repoURL string
|
||||||
|
// - revision string
|
||||||
|
// - project string
|
||||||
|
// - pattern string
|
||||||
|
// - noRevisionCache bool
|
||||||
|
// - verifyCommit bool
|
||||||
|
func (_e *Repos_Expecter) GetFiles(ctx interface{}, repoURL interface{}, revision interface{}, project interface{}, pattern interface{}, noRevisionCache interface{}, verifyCommit interface{}) *Repos_GetFiles_Call {
|
||||||
|
return &Repos_GetFiles_Call{Call: _e.mock.On("GetFiles", ctx, repoURL, revision, project, pattern, noRevisionCache, verifyCommit)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Repos_GetFiles_Call) Run(run func(ctx context.Context, repoURL string, revision string, project string, pattern string, noRevisionCache bool, verifyCommit bool)) *Repos_GetFiles_Call {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
var arg0 context.Context
|
||||||
|
if args[0] != nil {
|
||||||
|
arg0 = args[0].(context.Context)
|
||||||
|
}
|
||||||
|
var arg1 string
|
||||||
|
if args[1] != nil {
|
||||||
|
arg1 = args[1].(string)
|
||||||
|
}
|
||||||
|
var arg2 string
|
||||||
|
if args[2] != nil {
|
||||||
|
arg2 = args[2].(string)
|
||||||
|
}
|
||||||
|
var arg3 string
|
||||||
|
if args[3] != nil {
|
||||||
|
arg3 = args[3].(string)
|
||||||
|
}
|
||||||
|
var arg4 string
|
||||||
|
if args[4] != nil {
|
||||||
|
arg4 = args[4].(string)
|
||||||
|
}
|
||||||
|
var arg5 bool
|
||||||
|
if args[5] != nil {
|
||||||
|
arg5 = args[5].(bool)
|
||||||
|
}
|
||||||
|
var arg6 bool
|
||||||
|
if args[6] != nil {
|
||||||
|
arg6 = args[6].(bool)
|
||||||
|
}
|
||||||
|
run(
|
||||||
|
arg0,
|
||||||
|
arg1,
|
||||||
|
arg2,
|
||||||
|
arg3,
|
||||||
|
arg4,
|
||||||
|
arg5,
|
||||||
|
arg6,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Repos_GetFiles_Call) Return(stringToBytes map[string][]byte, err error) *Repos_GetFiles_Call {
|
||||||
|
_c.Call.Return(stringToBytes, err)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *Repos_GetFiles_Call) RunAndReturn(run func(ctx context.Context, repoURL string, revision string, project string, pattern string, noRevisionCache bool, verifyCommit bool) (map[string][]byte, error)) *Repos_GetFiles_Call {
|
||||||
|
_c.Call.Return(run)
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
internalhttp "github.com/argoproj/argo-cd/v2/applicationset/services/internal/http"
|
internalhttp "github.com/argoproj/argo-cd/v3/applicationset/services/internal/http"
|
||||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServiceRequest is the request object sent to the plugin service.
|
// ServiceRequest is the request object sent to the plugin service.
|
||||||
@@ -20,7 +20,7 @@ type ServiceRequest struct {
|
|||||||
|
|
||||||
type Output struct {
|
type Output struct {
|
||||||
// Parameters is the list of parameter sets returned by the plugin.
|
// Parameters is the list of parameter sets returned by the plugin.
|
||||||
Parameters []map[string]interface{} `json:"parameters"`
|
Parameters []map[string]any `json:"parameters"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceResponse is the response object returned by the plugin service.
|
// ServiceResponse is the response object returned by the plugin service.
|
||||||
@@ -34,7 +34,7 @@ type Service struct {
|
|||||||
appSetName string
|
appSetName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPluginService(ctx context.Context, appSetName string, baseURL string, token string, requestTimeout int) (*Service, error) {
|
func NewPluginService(appSetName string, baseURL string, token string, requestTimeout int) (*Service, error) {
|
||||||
var clientOptionFns []internalhttp.ClientOptionFunc
|
var clientOptionFns []internalhttp.ClientOptionFunc
|
||||||
|
|
||||||
clientOptionFns = append(clientOptionFns, internalhttp.WithToken(token))
|
clientOptionFns = append(clientOptionFns, internalhttp.WithToken(token))
|
||||||
@@ -55,14 +55,14 @@ func NewPluginService(ctx context.Context, appSetName string, baseURL string, to
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Service) List(ctx context.Context, parameters v1alpha1.PluginParameters) (*ServiceResponse, error) {
|
func (p *Service) List(ctx context.Context, parameters v1alpha1.PluginParameters) (*ServiceResponse, error) {
|
||||||
req, err := p.client.NewRequest(http.MethodPost, "api/v1/getparams.execute", ServiceRequest{ApplicationSetName: p.appSetName, Input: v1alpha1.PluginInput{Parameters: parameters}}, nil)
|
req, err := p.client.NewRequestWithContext(ctx, http.MethodPost, "api/v1/getparams.execute", ServiceRequest{ApplicationSetName: p.appSetName, Input: v1alpha1.PluginInput{Parameters: parameters}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("NewRequest returned unexpected error: %w", err)
|
return nil, fmt.Errorf("NewRequest returned unexpected error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var data ServiceResponse
|
var data ServiceResponse
|
||||||
|
|
||||||
_, err = p.client.Do(ctx, req, &data)
|
_, err = p.client.Do(req, &data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error get api '%s': %w", p.appSetName, err)
|
return nil, fmt.Errorf("error get api '%s': %w", p.appSetName, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -31,10 +30,10 @@ func TestPlugin(t *testing.T) {
|
|||||||
ts := httptest.NewServer(handler)
|
ts := httptest.NewServer(handler)
|
||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
client, err := NewPluginService(context.Background(), "plugin-test", ts.URL, token, 0)
|
client, err := NewPluginService("plugin-test", ts.URL, token, 0)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
data, err := client.List(context.Background(), nil)
|
data, err := client.List(t.Context(), nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var expectedData ServiceResponse
|
var expectedData ServiceResponse
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/argoproj/argo-cd/v2/common"
|
"github.com/argoproj/argo-cd/v3/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseSecretKey retrieves secret appSetName if different from common ArgoCDSecretName.
|
// ParseSecretKey retrieves secret appSetName if different from common ArgoCDSecretName.
|
||||||
@@ -12,7 +11,7 @@ func ParseSecretKey(key string) (secretName string, tokenKey string) {
|
|||||||
if strings.Contains(key, ":") {
|
if strings.Contains(key, ":") {
|
||||||
parts := strings.Split(key, ":")
|
parts := strings.Split(key, ":")
|
||||||
secretName = parts[0][1:]
|
secretName = parts[0][1:]
|
||||||
tokenKey = fmt.Sprintf("$%s", parts[1])
|
tokenKey = "$" + parts[1]
|
||||||
} else {
|
} else {
|
||||||
secretName = common.ArgoCDSecretName
|
secretName = common.ArgoCDSecretName
|
||||||
tokenKey = key
|
tokenKey = key
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/v7"
|
||||||
core "github.com/microsoft/azure-devops-go-api/azuredevops/core"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/core"
|
||||||
git "github.com/microsoft/azure-devops-go-api/azuredevops/git"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
const AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com"
|
const (
|
||||||
|
AZURE_DEVOPS_DEFAULT_URL = "https://dev.azure.com"
|
||||||
|
AZURE_DEVOPS_PROJECT_NOT_FOUND_ERROR = "The following project does not exist"
|
||||||
|
)
|
||||||
|
|
||||||
type AzureDevOpsClientFactory interface {
|
type AzureDevOpsClientFactory interface {
|
||||||
// Returns an Azure Devops Client interface.
|
// Returns an Azure Devops Client interface.
|
||||||
@@ -41,14 +44,14 @@ var (
|
|||||||
_ AzureDevOpsClientFactory = &devopsFactoryImpl{}
|
_ AzureDevOpsClientFactory = &devopsFactoryImpl{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAzureDevOpsService(ctx context.Context, token, url, organization, project, repo string, labels []string) (PullRequestService, error) {
|
func NewAzureDevOpsService(token, url, organization, project, repo string, labels []string) (PullRequestService, error) {
|
||||||
organizationUrl := buildURL(url, organization)
|
organizationURL := buildURL(url, organization)
|
||||||
|
|
||||||
var connection *azuredevops.Connection
|
var connection *azuredevops.Connection
|
||||||
if token == "" {
|
if token == "" {
|
||||||
connection = azuredevops.NewAnonymousConnection(organizationUrl)
|
connection = azuredevops.NewAnonymousConnection(organizationURL)
|
||||||
} else {
|
} else {
|
||||||
connection = azuredevops.NewPatConnection(organizationUrl, token)
|
connection = azuredevops.NewPatConnection(organizationURL, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AzureDevOpsService{
|
return &AzureDevOpsService{
|
||||||
@@ -70,13 +73,22 @@ func (a *AzureDevOpsService) List(ctx context.Context) ([]*PullRequest, error) {
|
|||||||
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pullRequests := []*PullRequest{}
|
||||||
|
|
||||||
azurePullRequests, err := client.GetPullRequestsByProject(ctx, args)
|
azurePullRequests, err := client.GetPullRequestsByProject(ctx, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// A standard Http 404 error is not returned for Azure DevOps,
|
||||||
|
// so checking the error message for a specific pattern.
|
||||||
|
// NOTE: Since the repos are filtered later, only existence of the project
|
||||||
|
// is relevant for AzureDevOps
|
||||||
|
if strings.Contains(err.Error(), AZURE_DEVOPS_PROJECT_NOT_FOUND_ERROR) {
|
||||||
|
// return a custom error indicating that the repository is not found,
|
||||||
|
// but also return the empty result since the decision to continue or not in this case is made by the caller
|
||||||
|
return pullRequests, NewRepositoryNotFoundError(err)
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("failed to get pull requests by project: %w", err)
|
return nil, fmt.Errorf("failed to get pull requests by project: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pullRequests := []*PullRequest{}
|
|
||||||
|
|
||||||
for _, pr := range *azurePullRequests {
|
for _, pr := range *azurePullRequests {
|
||||||
if pr.Repository == nil ||
|
if pr.Repository == nil ||
|
||||||
pr.Repository.Name == nil ||
|
pr.Repository.Name == nil ||
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ package pull_request
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/webapi"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/core"
|
||||||
|
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/git"
|
||||||
"github.com/microsoft/azure-devops-go-api/azuredevops/core"
|
"github.com/microsoft/azure-devops-go-api/azuredevops/v7/webapi"
|
||||||
git "github.com/microsoft/azure-devops-go-api/azuredevops/git"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
azureMock "github.com/argoproj/argo-cd/v2/applicationset/services/scm_provider/azure_devops/git/mocks"
|
azureMock "github.com/argoproj/argo-cd/v3/applicationset/services/scm_provider/azure_devops/git/mocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
func createBoolPtr(x bool) *bool {
|
func createBoolPtr(x bool) *bool {
|
||||||
@@ -61,20 +61,20 @@ func (m *AzureClientFactoryMock) GetClient(ctx context.Context) (git.Client, err
|
|||||||
func TestListPullRequest(t *testing.T) {
|
func TestListPullRequest(t *testing.T) {
|
||||||
teamProject := "myorg_project"
|
teamProject := "myorg_project"
|
||||||
repoName := "myorg_project_repo"
|
repoName := "myorg_project_repo"
|
||||||
pr_id := 123
|
prID := 123
|
||||||
pr_title := "feat(123)"
|
prTitle := "feat(123)"
|
||||||
pr_head_sha := "cd4973d9d14a08ffe6b641a89a68891d6aac8056"
|
prHeadSha := "cd4973d9d14a08ffe6b641a89a68891d6aac8056"
|
||||||
ctx := context.Background()
|
ctx := t.Context()
|
||||||
uniqueName := "testName"
|
uniqueName := "testName"
|
||||||
|
|
||||||
pullRequestMock := []git.GitPullRequest{
|
pullRequestMock := []git.GitPullRequest{
|
||||||
{
|
{
|
||||||
PullRequestId: createIntPtr(pr_id),
|
PullRequestId: createIntPtr(prID),
|
||||||
Title: createStringPtr(pr_title),
|
Title: createStringPtr(prTitle),
|
||||||
SourceRefName: createStringPtr("refs/heads/feature-branch"),
|
SourceRefName: createStringPtr("refs/heads/feature-branch"),
|
||||||
TargetRefName: createStringPtr("refs/heads/main"),
|
TargetRefName: createStringPtr("refs/heads/main"),
|
||||||
LastMergeSourceCommit: &git.GitCommitRef{
|
LastMergeSourceCommit: &git.GitCommitRef{
|
||||||
CommitId: createStringPtr(pr_head_sha),
|
CommitId: createStringPtr(prHeadSha),
|
||||||
},
|
},
|
||||||
Labels: &[]core.WebApiTagDefinition{},
|
Labels: &[]core.WebApiTagDefinition{},
|
||||||
Repository: &git.GitRepository{
|
Repository: &git.GitRepository{
|
||||||
@@ -108,9 +108,9 @@ func TestListPullRequest(t *testing.T) {
|
|||||||
assert.Len(t, list, 1)
|
assert.Len(t, list, 1)
|
||||||
assert.Equal(t, "feature-branch", list[0].Branch)
|
assert.Equal(t, "feature-branch", list[0].Branch)
|
||||||
assert.Equal(t, "main", list[0].TargetBranch)
|
assert.Equal(t, "main", list[0].TargetBranch)
|
||||||
assert.Equal(t, pr_head_sha, list[0].HeadSHA)
|
assert.Equal(t, prHeadSha, list[0].HeadSHA)
|
||||||
assert.Equal(t, "feat(123)", list[0].Title)
|
assert.Equal(t, "feat(123)", list[0].Title)
|
||||||
assert.Equal(t, pr_id, list[0].Number)
|
assert.Equal(t, prID, list[0].Number)
|
||||||
assert.Equal(t, uniqueName, list[0].Author)
|
assert.Equal(t, uniqueName, list[0].Author)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,3 +236,36 @@ func TestBuildURL(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAzureDevOpsListReturnsRepositoryNotFoundError(t *testing.T) {
|
||||||
|
args := git.GetPullRequestsByProjectArgs{
|
||||||
|
Project: createStringPtr("nonexistent"),
|
||||||
|
SearchCriteria: &git.GitPullRequestSearchCriteria{},
|
||||||
|
}
|
||||||
|
|
||||||
|
pullRequestMock := []git.GitPullRequest{}
|
||||||
|
|
||||||
|
gitClientMock := azureMock.Client{}
|
||||||
|
clientFactoryMock := &AzureClientFactoryMock{mock: &mock.Mock{}}
|
||||||
|
clientFactoryMock.mock.On("GetClient", mock.Anything).Return(&gitClientMock, nil)
|
||||||
|
|
||||||
|
// Mock the GetPullRequestsByProject to return an error containing "404"
|
||||||
|
gitClientMock.On("GetPullRequestsByProject", t.Context(), args).Return(&pullRequestMock,
|
||||||
|
errors.New("The following project does not exist:"))
|
||||||
|
|
||||||
|
provider := AzureDevOpsService{
|
||||||
|
clientFactory: clientFactoryMock,
|
||||||
|
project: "nonexistent",
|
||||||
|
repo: "nonexistent",
|
||||||
|
labels: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
prs, err := provider.List(t.Context())
|
||||||
|
|
||||||
|
// Should return empty pull requests list
|
||||||
|
assert.Empty(t, prs)
|
||||||
|
|
||||||
|
// Should return RepositoryNotFoundError
|
||||||
|
require.Error(t, err)
|
||||||
|
assert.True(t, IsRepositoryNotFoundError(err), "Expected RepositoryNotFoundError but got: %v", err)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package pull_request
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ktrysmt/go-bitbucket"
|
"github.com/ktrysmt/go-bitbucket"
|
||||||
)
|
)
|
||||||
@@ -16,10 +18,19 @@ type BitbucketCloudService struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BitbucketCloudPullRequest struct {
|
type BitbucketCloudPullRequest struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Source BitbucketCloudPullRequestSource `json:"source"`
|
Source BitbucketCloudPullRequestSource `json:"source"`
|
||||||
Author BitbucketCloudPullRequestAuthor `json:"author"`
|
Author BitbucketCloudPullRequestAuthor `json:"author"`
|
||||||
|
Destination BitbucketCloudPullRequestDestination `json:"destination"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BitbucketCloudPullRequestDestination struct {
|
||||||
|
Branch BitbucketCloudPullRequestDestinationBranch `json:"branch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BitbucketCloudPullRequestDestinationBranch struct {
|
||||||
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BitbucketCloudPullRequestSource struct {
|
type BitbucketCloudPullRequestSource struct {
|
||||||
@@ -51,7 +62,7 @@ type PullRequestResponse struct {
|
|||||||
|
|
||||||
var _ PullRequestService = (*BitbucketCloudService)(nil)
|
var _ PullRequestService = (*BitbucketCloudService)(nil)
|
||||||
|
|
||||||
func parseUrl(uri string) (*url.URL, error) {
|
func parseURL(uri string) (*url.URL, error) {
|
||||||
if uri == "" {
|
if uri == "" {
|
||||||
uri = "https://api.bitbucket.org/2.0"
|
uri = "https://api.bitbucket.org/2.0"
|
||||||
}
|
}
|
||||||
@@ -64,10 +75,10 @@ func parseUrl(uri string) (*url.URL, error) {
|
|||||||
return url, nil
|
return url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBitbucketCloudServiceBasicAuth(baseUrl, username, password, owner, repositorySlug string) (PullRequestService, error) {
|
func NewBitbucketCloudServiceBasicAuth(baseURL, username, password, owner, repositorySlug string) (PullRequestService, error) {
|
||||||
url, err := parseUrl(baseUrl)
|
url, err := parseURL(baseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseUrl, owner, repositorySlug, err)
|
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseURL, owner, repositorySlug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bitbucketClient := bitbucket.NewBasicAuth(username, password)
|
bitbucketClient := bitbucket.NewBasicAuth(username, password)
|
||||||
@@ -80,10 +91,10 @@ func NewBitbucketCloudServiceBasicAuth(baseUrl, username, password, owner, repos
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBitbucketCloudServiceBearerToken(baseUrl, bearerToken, owner, repositorySlug string) (PullRequestService, error) {
|
func NewBitbucketCloudServiceBearerToken(baseURL, bearerToken, owner, repositorySlug string) (PullRequestService, error) {
|
||||||
url, err := parseUrl(baseUrl)
|
url, err := parseURL(baseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseUrl, owner, repositorySlug, err)
|
return nil, fmt.Errorf("error parsing base url of %s for %s/%s: %w", baseURL, owner, repositorySlug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bitbucketClient := bitbucket.NewOAuthbearerToken(bearerToken)
|
bitbucketClient := bitbucket.NewOAuthbearerToken(bearerToken)
|
||||||
@@ -96,9 +107,9 @@ func NewBitbucketCloudServiceBearerToken(baseUrl, bearerToken, owner, repository
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBitbucketCloudServiceNoAuth(baseUrl, owner, repositorySlug string) (PullRequestService, error) {
|
func NewBitbucketCloudServiceNoAuth(baseURL, owner, repositorySlug string) (PullRequestService, error) {
|
||||||
// There is currently no method to explicitly not require auth
|
// There is currently no method to explicitly not require auth
|
||||||
return NewBitbucketCloudServiceBearerToken(baseUrl, "", owner, repositorySlug)
|
return NewBitbucketCloudServiceBearerToken(baseURL, "", owner, repositorySlug)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error) {
|
func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error) {
|
||||||
@@ -107,19 +118,28 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
|
|||||||
RepoSlug: b.repositorySlug,
|
RepoSlug: b.repositorySlug,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pullRequests := []*PullRequest{}
|
||||||
|
|
||||||
response, err := b.client.Repositories.PullRequests.Gets(opts)
|
response, err := b.client.Repositories.PullRequests.Gets(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// A standard Http 404 error is not returned for Bitbucket Cloud,
|
||||||
|
// so checking the error message for a specific pattern
|
||||||
|
if strings.Contains(err.Error(), "404 Not Found") {
|
||||||
|
// return a custom error indicating that the repository is not found,
|
||||||
|
// but also return the empty result since the decision to continue or not in this case is made by the caller
|
||||||
|
return pullRequests, NewRepositoryNotFoundError(err)
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.owner, b.repositorySlug, err)
|
return nil, fmt.Errorf("error listing pull requests for %s/%s: %w", b.owner, b.repositorySlug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, ok := response.(map[string]interface{})
|
resp, ok := response.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown type returned from bitbucket pull requests")
|
return nil, errors.New("unknown type returned from bitbucket pull requests")
|
||||||
}
|
}
|
||||||
|
|
||||||
repoArray, ok := resp["values"].([]interface{})
|
repoArray, ok := resp["values"].([]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown type returned from response values")
|
return nil, errors.New("unknown type returned from response values")
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonStr, err := json.Marshal(repoArray)
|
jsonStr, err := json.Marshal(repoArray)
|
||||||
@@ -132,14 +152,14 @@ func (b *BitbucketCloudService) List(_ context.Context) ([]*PullRequest, error)
|
|||||||
return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %w", err)
|
return nil, fmt.Errorf("error unmarshalling json to type '[]BitbucketCloudPullRequest': %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pullRequests := []*PullRequest{}
|
|
||||||
for _, pull := range pulls {
|
for _, pull := range pulls {
|
||||||
pullRequests = append(pullRequests, &PullRequest{
|
pullRequests = append(pullRequests, &PullRequest{
|
||||||
Number: pull.ID,
|
Number: pull.ID,
|
||||||
Title: pull.Title,
|
Title: pull.Title,
|
||||||
Branch: pull.Source.Branch.Name,
|
Branch: pull.Source.Branch.Name,
|
||||||
HeadSHA: pull.Source.Commit.Hash,
|
TargetBranch: pull.Destination.Branch.Name,
|
||||||
Author: pull.Author.Nickname,
|
HeadSHA: pull.Source.Commit.Hash,
|
||||||
|
Author: pull.Author.Nickname,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user