mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 17:48:47 +01:00
Compare commits
935 Commits
refresh-do
...
v2.3.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa54aa7eda | ||
|
|
4bbd0e57dd | ||
|
|
2d6b619a86 | ||
|
|
39487ef7c7 | ||
|
|
fcc7c0b080 | ||
|
|
ed734fedbb | ||
|
|
7414f2d42c | ||
|
|
8139df8983 | ||
|
|
f364330de2 | ||
|
|
4ec67d8b08 | ||
|
|
3d3f81df4a | ||
|
|
37f01f6f32 | ||
|
|
36eab6b82c | ||
|
|
793acc147f | ||
|
|
b50609c0e6 | ||
|
|
5f48ce96c6 | ||
|
|
c32460a2bc | ||
|
|
7eb1aba99b | ||
|
|
1a8139f4d6 | ||
|
|
02c03c3b26 | ||
|
|
cdb20d5060 | ||
|
|
7d7eed4932 | ||
|
|
af8c5eb07a | ||
|
|
1a476f7564 | ||
|
|
7f15389c72 | ||
|
|
1a3556e1cc | ||
|
|
4aa614dafb | ||
|
|
6abccea3f0 | ||
|
|
5ffc60f88d | ||
|
|
04e05c5106 | ||
|
|
f387ab846b | ||
|
|
6dc544db99 | ||
|
|
061e575d82 | ||
|
|
d9c1af9f25 | ||
|
|
6abc85a3a5 | ||
|
|
a16471b8a5 | ||
|
|
ecc3ab3cab | ||
|
|
0aeda4366d | ||
|
|
cf601c96d3 | ||
|
|
96f95ca1c1 | ||
|
|
0b2e585373 | ||
|
|
83458e0bd9 | ||
|
|
4aa7cbfb91 | ||
|
|
26db08931e | ||
|
|
3ae9bffa30 | ||
|
|
4c7651b5f8 | ||
|
|
feaf949968 | ||
|
|
b7bdb8ffe7 | ||
|
|
c3b3521f59 | ||
|
|
285454f011 | ||
|
|
134f880aae | ||
|
|
9ac935d59f | ||
|
|
8c2cef6f28 | ||
|
|
26b9ba3dc6 | ||
|
|
de496d6b4b | ||
|
|
ae71475869 | ||
|
|
ef8ae570bf | ||
|
|
6962fc22ee | ||
|
|
25698fd9f5 | ||
|
|
44d596f0ee | ||
|
|
1547b44ffc | ||
|
|
9155d14f9a | ||
|
|
2fc0a25c0a | ||
|
|
988d760474 | ||
|
|
a17c77e238 | ||
|
|
dc24d05a31 | ||
|
|
113b3da691 | ||
|
|
8608a3192a | ||
|
|
2fb3986386 | ||
|
|
8b95052720 | ||
|
|
010411012f | ||
|
|
22f4a936cc | ||
|
|
41fc70d17e | ||
|
|
303cc4613b | ||
|
|
2072d1a4b0 | ||
|
|
d33caacb9b | ||
|
|
3c5033c6bf | ||
|
|
978162fdee | ||
|
|
4f62aa4398 | ||
|
|
6f59fe660f | ||
|
|
0dfc125d95 | ||
|
|
f65289748a | ||
|
|
47187bd30e | ||
|
|
4f155b539b | ||
|
|
cb47740d62 | ||
|
|
0b9b7c31f8 | ||
|
|
35f5b32f53 | ||
|
|
2f83312800 | ||
|
|
074db0cf28 | ||
|
|
502762acc0 | ||
|
|
55ea34292a | ||
|
|
7ff5f0755b | ||
|
|
4a04bb9096 | ||
|
|
fb30f1bd03 | ||
|
|
d6f3f87c69 | ||
|
|
6240ef00e7 | ||
|
|
5c85df90d5 | ||
|
|
07cc533896 | ||
|
|
d511fb8deb | ||
|
|
99583c74c3 | ||
|
|
c92922e3d6 | ||
|
|
5e6aee5961 | ||
|
|
57e0ffceb7 | ||
|
|
c8bcabeba5 | ||
|
|
4b5df692d7 | ||
|
|
5d770d6c54 | ||
|
|
dfd40efb93 | ||
|
|
27af0b4924 | ||
|
|
5d1fb9f775 | ||
|
|
03c5d20d58 | ||
|
|
3fe924ebd6 | ||
|
|
5da1522b17 | ||
|
|
8b57bc990c | ||
|
|
37851b165e | ||
|
|
722fe9273a | ||
|
|
f1d4166a7c | ||
|
|
d00e112791 | ||
|
|
6cd0d97e23 | ||
|
|
2547176b29 | ||
|
|
9530b38f7a | ||
|
|
4a55b13d66 | ||
|
|
c0349f3795 | ||
|
|
48d46e395f | ||
|
|
9cb9a380da | ||
|
|
399d2a830b | ||
|
|
3247090838 | ||
|
|
99d1dcad03 | ||
|
|
8e6fcde4d6 | ||
|
|
160a4fda5e | ||
|
|
cb1f06c72a | ||
|
|
62f29865e7 | ||
|
|
ae9abe5175 | ||
|
|
b558579f28 | ||
|
|
b3ea0a38bb | ||
|
|
32838b1f71 | ||
|
|
e822437eb3 | ||
|
|
de582e0bd5 | ||
|
|
26f83501ad | ||
|
|
000f951b9c | ||
|
|
129e9ea89e | ||
|
|
d1d82c6d6f | ||
|
|
d8c3113c06 | ||
|
|
7bac2c151a | ||
|
|
800b1093e7 | ||
|
|
18f7b416a8 | ||
|
|
63dfc78917 | ||
|
|
487db973d3 | ||
|
|
fcaa8abd01 | ||
|
|
e32c07007c | ||
|
|
e50d03eb61 | ||
|
|
e15c8a58a6 | ||
|
|
3bd8688004 | ||
|
|
ad1ed1d51b | ||
|
|
914fb8337e | ||
|
|
0676b65f7e | ||
|
|
2e494689cd | ||
|
|
561a44d0a8 | ||
|
|
63e70a9f71 | ||
|
|
656f790aed | ||
|
|
7f09331e3f | ||
|
|
f9ba9e9811 | ||
|
|
17c6daa9ea | ||
|
|
f89d40c9e0 | ||
|
|
6a1472371f | ||
|
|
3ea2ba06f5 | ||
|
|
29a6be4afe | ||
|
|
bed36e2027 | ||
|
|
9ff7c0b9c7 | ||
|
|
cf78fe429a | ||
|
|
5b17fc36d4 | ||
|
|
5978dfa167 | ||
|
|
ddb90ab496 | ||
|
|
0a792fefac | ||
|
|
e48350756b | ||
|
|
6d8ff61b16 | ||
|
|
238ff275c4 | ||
|
|
7eccd21b47 | ||
|
|
c9ddaabf08 | ||
|
|
a51169e8c0 | ||
|
|
bea379b036 | ||
|
|
b813301ed6 | ||
|
|
458a10310c | ||
|
|
6221ef20af | ||
|
|
2087eaa4c7 | ||
|
|
7d59cbb5be | ||
|
|
dae43a23f9 | ||
|
|
78e256eef3 | ||
|
|
ccd15d1151 | ||
|
|
05935a9d7e | ||
|
|
891d9b2f38 | ||
|
|
0f845a0416 | ||
|
|
a456a92493 | ||
|
|
e967f8836a | ||
|
|
a8ed010921 | ||
|
|
0c352f8bd6 | ||
|
|
e81f25026d | ||
|
|
cd497c4eec | ||
|
|
7490ee483e | ||
|
|
ced5c1deed | ||
|
|
b977813e9a | ||
|
|
a2f97af2e4 | ||
|
|
6b4a13c4bd | ||
|
|
d64014364c | ||
|
|
0f75eecbd2 | ||
|
|
335becebb1 | ||
|
|
d89c708399 | ||
|
|
d8cfafbd92 | ||
|
|
394a2c8318 | ||
|
|
faee047081 | ||
|
|
a05a2633ba | ||
|
|
0f2f9a97e3 | ||
|
|
d346fceea9 | ||
|
|
b40508323b | ||
|
|
8851842d8e | ||
|
|
70a08a0aa6 | ||
|
|
9cb9ef6752 | ||
|
|
23967f8717 | ||
|
|
475bea599d | ||
|
|
8e8a1ded27 | ||
|
|
0c0bc1f769 | ||
|
|
8bbf8887d2 | ||
|
|
012268d619 | ||
|
|
af3fb9a4d0 | ||
|
|
2370164c98 | ||
|
|
18a1b07d1a | ||
|
|
fafa79fac1 | ||
|
|
8a5b34581a | ||
|
|
27928d0dc6 | ||
|
|
0c91f65e67 | ||
|
|
09c5d5f42a | ||
|
|
caa246a38d | ||
|
|
ebf27dea3e | ||
|
|
98fcb417eb | ||
|
|
a3517d594b | ||
|
|
e0c9827a37 | ||
|
|
375e27bd7a | ||
|
|
46fdb6f364 | ||
|
|
2e84638827 | ||
|
|
0a64781616 | ||
|
|
43a29e6b2d | ||
|
|
b77f947e1d | ||
|
|
dc24380065 | ||
|
|
b0278c4493 | ||
|
|
1431e04b90 | ||
|
|
5bb45435e0 | ||
|
|
691b77ff9e | ||
|
|
b52793ab07 | ||
|
|
fc13eda8d6 | ||
|
|
ff45418948 | ||
|
|
0d4e40ed6a | ||
|
|
fe3cc7218c | ||
|
|
513d8a4ae0 | ||
|
|
6036e9b4c9 | ||
|
|
8a6b759c4a | ||
|
|
c2b3e74089 | ||
|
|
8b10b64dfa | ||
|
|
e5c34f472e | ||
|
|
49b71522c4 | ||
|
|
7a6b2ffe56 | ||
|
|
c7792522c5 | ||
|
|
f4fd836a1b | ||
|
|
2770c690a5 | ||
|
|
5b914750a4 | ||
|
|
43cba9cc0b | ||
|
|
0dbffb8557 | ||
|
|
7b0cf773cc | ||
|
|
d3bec0faaa | ||
|
|
7750f579bb | ||
|
|
5aeb113968 | ||
|
|
4522a88289 | ||
|
|
5f4ecf1ef2 | ||
|
|
7ced59f346 | ||
|
|
a912611b7a | ||
|
|
a21b0363e3 | ||
|
|
07b8a1f63c | ||
|
|
1804d771c7 | ||
|
|
7fe8e5ff37 | ||
|
|
b8510f35f1 | ||
|
|
aa6aed3c04 | ||
|
|
d6d620e98c | ||
|
|
3e17a69df3 | ||
|
|
da63fb26d1 | ||
|
|
401b266235 | ||
|
|
f46ac844b8 | ||
|
|
046840dcf7 | ||
|
|
d460f844a4 | ||
|
|
970bb80b28 | ||
|
|
81e801d8ee | ||
|
|
872eff292b | ||
|
|
00874af5f2 | ||
|
|
ad97ed52d5 | ||
|
|
7ea35924c9 | ||
|
|
c7074fe67a | ||
|
|
9a11790df2 | ||
|
|
695699d08e | ||
|
|
374965a606 | ||
|
|
ddc9f56d05 | ||
|
|
3312dc0037 | ||
|
|
67dd012b87 | ||
|
|
10bb1c4e1f | ||
|
|
f3730da01e | ||
|
|
d8f6ee2d72 | ||
|
|
b5d1433a1f | ||
|
|
83ff035df7 | ||
|
|
1d4b91efa2 | ||
|
|
43bcbfb093 | ||
|
|
f0cd21a9f3 | ||
|
|
d7d8fe3da2 | ||
|
|
c80c1d4f95 | ||
|
|
6644cbe452 | ||
|
|
ae02bc27fc | ||
|
|
5faea8f339 | ||
|
|
678d35d850 | ||
|
|
363e1d2d49 | ||
|
|
b2c70df619 | ||
|
|
3c874ae065 | ||
|
|
4dd570eb11 | ||
|
|
1bc231dd99 | ||
|
|
8a8373faba | ||
|
|
384f5b2bae | ||
|
|
b0736219b6 | ||
|
|
4399348ae5 | ||
|
|
df5cce866f | ||
|
|
07eeddf5f1 | ||
|
|
0c757534ed | ||
|
|
d8d7b30f58 | ||
|
|
9d88e614cd | ||
|
|
4e025838c0 | ||
|
|
d0c6280d38 | ||
|
|
e084a110e4 | ||
|
|
df7e5ffd96 | ||
|
|
6f8b10c324 | ||
|
|
eb70a1e3fa | ||
|
|
35bd9e1ed6 | ||
|
|
778be231b9 | ||
|
|
d790e96aa9 | ||
|
|
a6039e1be9 | ||
|
|
3d4deeb4f0 | ||
|
|
ebd87b77ed | ||
|
|
31b2cbee70 | ||
|
|
a2677f5537 | ||
|
|
65d669565b | ||
|
|
07f4034aaa | ||
|
|
459833d7c3 | ||
|
|
bbdbf81108 | ||
|
|
1842c98374 | ||
|
|
94372adcd4 | ||
|
|
e3df023310 | ||
|
|
6f5f7ffeee | ||
|
|
97302103d9 | ||
|
|
380037b63d | ||
|
|
0d68194205 | ||
|
|
20313adbe2 | ||
|
|
a731997c59 | ||
|
|
bb88a1c975 | ||
|
|
917d0797fa | ||
|
|
993cb838a9 | ||
|
|
eb2c69d45e | ||
|
|
65012f502b | ||
|
|
2f6e91ad22 | ||
|
|
b264729bfb | ||
|
|
3952f66fc7 | ||
|
|
b1f979aad6 | ||
|
|
2a579a6c05 | ||
|
|
9e234b9dec | ||
|
|
4f69e93802 | ||
|
|
3799d706df | ||
|
|
344353cb16 | ||
|
|
d698e5d68a | ||
|
|
5f9993a75b | ||
|
|
f613fab6f3 | ||
|
|
183d729d40 | ||
|
|
b26e92873f | ||
|
|
36ab4dadda | ||
|
|
91fea1470f | ||
|
|
17e6ebdce1 | ||
|
|
6f794d0dc9 | ||
|
|
9cf71bef90 | ||
|
|
25db5038c7 | ||
|
|
b6c458e8f4 | ||
|
|
2147ed3aea | ||
|
|
3df6be73e7 | ||
|
|
d823efee5d | ||
|
|
1817b6b4d4 | ||
|
|
7ed06cc9f6 | ||
|
|
49378a95c4 | ||
|
|
a38f3176e2 | ||
|
|
08d1cf0e55 | ||
|
|
85c45ca76e | ||
|
|
626c963b1c | ||
|
|
fc49eb2e46 | ||
|
|
e0c07b8027 | ||
|
|
7122b83fc3 | ||
|
|
c64e8df84c | ||
|
|
be884d2172 | ||
|
|
d2100a30a0 | ||
|
|
9559613c8f | ||
|
|
bd7f8c8eda | ||
|
|
a90ab1a2e1 | ||
|
|
a5126648e1 | ||
|
|
d9e8a0fc31 | ||
|
|
b559bf88d1 | ||
|
|
c0f2bf55a7 | ||
|
|
4b92f96034 | ||
|
|
24054820da | ||
|
|
22600228c2 | ||
|
|
9025318adf | ||
|
|
5d9aa989f3 | ||
|
|
9613b240fb | ||
|
|
8fbcc1c7fc | ||
|
|
d2ff65ae39 | ||
|
|
dd26a48f8a | ||
|
|
8c19c7864d | ||
|
|
a9f009cb10 | ||
|
|
ce1d8031ae | ||
|
|
dd2900eaeb | ||
|
|
c0bcd6b255 | ||
|
|
6897aa4ed2 | ||
|
|
42903044f3 | ||
|
|
700806f408 | ||
|
|
b271d6ac5c | ||
|
|
a940cb5914 | ||
|
|
87de4edd15 | ||
|
|
abba8dddce | ||
|
|
44520ea479 | ||
|
|
ae803a2f1e | ||
|
|
49a854a738 | ||
|
|
81155ccb0c | ||
|
|
18057fa5ee | ||
|
|
ca5003f3ba | ||
|
|
1d3d03f5d3 | ||
|
|
a894d4b128 | ||
|
|
440e4dc5d9 | ||
|
|
d6973c7b8a | ||
|
|
34e904dd7b | ||
|
|
c7e532941f | ||
|
|
613db278a7 | ||
|
|
91c883668e | ||
|
|
d12eafa826 | ||
|
|
0dcac9a8d9 | ||
|
|
0078a4d9ee | ||
|
|
8ceceb8b55 | ||
|
|
2e3405134b | ||
|
|
5b1906d2a7 | ||
|
|
0093bddc39 | ||
|
|
788613cb14 | ||
|
|
53dd0da8c3 | ||
|
|
8f21138d54 | ||
|
|
676a8714bf | ||
|
|
4ae1d876fd | ||
|
|
efc8ec1d78 | ||
|
|
e3b7a2be13 | ||
|
|
44ec4e0a82 | ||
|
|
24ad4618c2 | ||
|
|
d3fb6fb7db | ||
|
|
95d9016ed4 | ||
|
|
e195995790 | ||
|
|
124995c275 | ||
|
|
7b89c4e53c | ||
|
|
1ab85de378 | ||
|
|
eb76649ef5 | ||
|
|
3887289090 | ||
|
|
d842e8333a | ||
|
|
311926b8c9 | ||
|
|
63d809614c | ||
|
|
741329afb6 | ||
|
|
d36cf3e2bb | ||
|
|
53bbb8ecf4 | ||
|
|
86ca66b2d6 | ||
|
|
6ac9753b63 | ||
|
|
afb1c8b210 | ||
|
|
117eadaceb | ||
|
|
10b4e44c42 | ||
|
|
8cf8a8cf81 | ||
|
|
8a29cf8fbf | ||
|
|
4a3dd76f19 | ||
|
|
b91732a8eb | ||
|
|
97e8c7d00d | ||
|
|
e5ee00f4d0 | ||
|
|
aab0b173d5 | ||
|
|
50993206a5 | ||
|
|
4164db88f5 | ||
|
|
c5eb25ceba | ||
|
|
685b07ed34 | ||
|
|
3091bc5b6f | ||
|
|
810a977846 | ||
|
|
adade448a8 | ||
|
|
c22d8321cc | ||
|
|
819225c709 | ||
|
|
dd50b88cd1 | ||
|
|
8b1c364f58 | ||
|
|
d74ce38c8c | ||
|
|
826288f861 | ||
|
|
e86c5ea17c | ||
|
|
f7614e0c4e | ||
|
|
2130c8a949 | ||
|
|
9d6ccee305 | ||
|
|
9c0495d9a3 | ||
|
|
ef653cac57 | ||
|
|
9099d0ba83 | ||
|
|
50b0010470 | ||
|
|
6b0d449997 | ||
|
|
c607a3b294 | ||
|
|
4fef2114fe | ||
|
|
8c216ed7a8 | ||
|
|
d404f0e425 | ||
|
|
c5ebc584d0 | ||
|
|
241a6fb1ba | ||
|
|
93e624d590 | ||
|
|
8f4e002ae4 | ||
|
|
3abf8c6735 | ||
|
|
3ff8481694 | ||
|
|
0d23207c59 | ||
|
|
fcd28b6eb1 | ||
|
|
7e27d1072f | ||
|
|
1e5c763d6f | ||
|
|
7fbf50493b | ||
|
|
f170d24ed1 | ||
|
|
bcfc112d82 | ||
|
|
66acd16bce | ||
|
|
bee20c2154 | ||
|
|
17bef1c2c6 | ||
|
|
2abf284f81 | ||
|
|
221d0c048d | ||
|
|
cd9dcecccd | ||
|
|
52090425c6 | ||
|
|
228c465393 | ||
|
|
4a69cce7b1 | ||
|
|
c7738a0cae | ||
|
|
369385388d | ||
|
|
321a9734ec | ||
|
|
0c644e0df7 | ||
|
|
bd1390c182 | ||
|
|
ba5c300cc5 | ||
|
|
1b99ce2bf3 | ||
|
|
e1e0f27a7b | ||
|
|
62104da946 | ||
|
|
d6a5d7700f | ||
|
|
f1d740c4df | ||
|
|
fb357defe3 | ||
|
|
75a5f79ed2 | ||
|
|
8c973a40e3 | ||
|
|
30ede1c4bf | ||
|
|
af516e9f0d | ||
|
|
c3abe77bd5 | ||
|
|
02bf2bfc20 | ||
|
|
f66a38875b | ||
|
|
7c3c3528b6 | ||
|
|
eff2d2f816 | ||
|
|
84c70b776a | ||
|
|
e68618d168 | ||
|
|
6262a8c750 | ||
|
|
e058bc4228 | ||
|
|
082e66bd5b | ||
|
|
ba221b9cee | ||
|
|
2497679dcb | ||
|
|
5f5f0285e9 | ||
|
|
561452ac94 | ||
|
|
d7a8a877ad | ||
|
|
9c690a10e2 | ||
|
|
03dcff6659 | ||
|
|
3220b7613d | ||
|
|
fbe2649857 | ||
|
|
c33e850d38 | ||
|
|
b627435996 | ||
|
|
e251b14d6b | ||
|
|
58ac345f2b | ||
|
|
ef744e3c15 | ||
|
|
a9a69b74c9 | ||
|
|
e17565ae81 | ||
|
|
1838fc0587 | ||
|
|
90bacef5bb | ||
|
|
2ca14c6310 | ||
|
|
af7f3fbd48 | ||
|
|
30d2ba1f1c | ||
|
|
2b3601cfa3 | ||
|
|
1b46143d07 | ||
|
|
3bba77c36f | ||
|
|
1602ec992d | ||
|
|
45fbca510b | ||
|
|
c8eab2c15d | ||
|
|
083390ef87 | ||
|
|
d6dd6afd05 | ||
|
|
9a788c3470 | ||
|
|
dec710e8cb | ||
|
|
2693b5480e | ||
|
|
da3f21b62d | ||
|
|
17d698d77f | ||
|
|
5f5b788202 | ||
|
|
040202e7d0 | ||
|
|
7e68b1ef71 | ||
|
|
abdb4c0679 | ||
|
|
0ff1ec511a | ||
|
|
8030f4202a | ||
|
|
b893eccea0 | ||
|
|
e0db23b63d | ||
|
|
ef7ee088f5 | ||
|
|
8cecf1eee6 | ||
|
|
f12650c32b | ||
|
|
ba4c65540b | ||
|
|
2ab10516be | ||
|
|
7149e35822 | ||
|
|
e59ed2208f | ||
|
|
8b40f96584 | ||
|
|
6d1b789b53 | ||
|
|
2a41018756 | ||
|
|
9f1af587ee | ||
|
|
4a4b43f1d2 | ||
|
|
5bd2f61c56 | ||
|
|
1d0b5fc7ec | ||
|
|
76632d927e | ||
|
|
9b867ff075 | ||
|
|
f81ed952cb | ||
|
|
ef910c8458 | ||
|
|
e5d9c39308 | ||
|
|
46418f7a45 | ||
|
|
379298337f | ||
|
|
d47f466198 | ||
|
|
f74d8072c5 | ||
|
|
0e9aa8e6b6 | ||
|
|
ce1bcb3e4b | ||
|
|
ee8393dba5 | ||
|
|
33a8d80a42 | ||
|
|
a80656d345 | ||
|
|
4d9e8bc2e6 | ||
|
|
31be4d8f19 | ||
|
|
6736a3d7d7 | ||
|
|
63eb5de233 | ||
|
|
9815490edb | ||
|
|
766a7ba2b1 | ||
|
|
7d32c2735a | ||
|
|
4ae5cdf7a4 | ||
|
|
7f5c0142ff | ||
|
|
c5b8aa765c | ||
|
|
1593f8d797 | ||
|
|
f3b79aef25 | ||
|
|
c4f69ac144 | ||
|
|
dff84a7d95 | ||
|
|
bf9622a2c4 | ||
|
|
2541fa0332 | ||
|
|
0f6c1ab01e | ||
|
|
170b657a79 | ||
|
|
9476ab5e18 | ||
|
|
e58d94f430 | ||
|
|
2c966f24a5 | ||
|
|
15a3c29b01 | ||
|
|
4485025d86 | ||
|
|
a647ca960a | ||
|
|
c62012d526 | ||
|
|
a0f887e854 | ||
|
|
e415d6ee90 | ||
|
|
6f959506ab | ||
|
|
fba25d8229 | ||
|
|
2da0e81b05 | ||
|
|
f533ed17f0 | ||
|
|
aa86dbabbf | ||
|
|
af338ddd80 | ||
|
|
e865504a98 | ||
|
|
1f9fc2cc53 | ||
|
|
8c9674b272 | ||
|
|
2f9683aac7 | ||
|
|
3dd5dc44ac | ||
|
|
c618c1fc76 | ||
|
|
9adba69e88 | ||
|
|
faba995bd7 | ||
|
|
124cc8a07e | ||
|
|
33d691eccd | ||
|
|
f2093ecbf3 | ||
|
|
12c95d64a7 | ||
|
|
e729acd075 | ||
|
|
e444575c15 | ||
|
|
2eb6b3bf80 | ||
|
|
abb0c4b260 | ||
|
|
c3b12202d7 | ||
|
|
118c298752 | ||
|
|
86d0ee6982 | ||
|
|
2ba6223b6c | ||
|
|
16349b8819 | ||
|
|
bffdd0b6f3 | ||
|
|
e9ce61d662 | ||
|
|
d6f579b8f6 | ||
|
|
f43a6ad2aa | ||
|
|
3fbe818bea | ||
|
|
dfa5fef8c6 | ||
|
|
701bc5b709 | ||
|
|
0f6a7d5f34 | ||
|
|
e44fa434ab | ||
|
|
e3392eeb57 | ||
|
|
4a3c94b20c | ||
|
|
2a7745b41b | ||
|
|
e23f3f495d | ||
|
|
1708a7154d | ||
|
|
a6d04469c5 | ||
|
|
3604a588bb | ||
|
|
a61052ba1a | ||
|
|
c5b25c067f | ||
|
|
54ec2fcadb | ||
|
|
5fd7ffdb7a | ||
|
|
aad1a26144 | ||
|
|
32f60d3504 | ||
|
|
8cb589859f | ||
|
|
62aa859f29 | ||
|
|
ae5388dd79 | ||
|
|
ffb22ff1ff | ||
|
|
857d448a5c | ||
|
|
20ba86ba5f | ||
|
|
35a5f19d08 | ||
|
|
cc98954847 | ||
|
|
696610688e | ||
|
|
0e9823efa9 | ||
|
|
40629e5715 | ||
|
|
b57ba42b9d | ||
|
|
b2f547e5ab | ||
|
|
da222e9621 | ||
|
|
cc4eea0d69 | ||
|
|
9b32e01104 | ||
|
|
44dccdbba0 | ||
|
|
cf78189678 | ||
|
|
da9cf5a4b6 | ||
|
|
e84f3c6b3f | ||
|
|
e959c3b2a0 | ||
|
|
748126e95f | ||
|
|
3f32a750a0 | ||
|
|
7d12385aed | ||
|
|
4159ebc6ce | ||
|
|
434af15d5b | ||
|
|
5c41377ca6 | ||
|
|
f1d72d2b86 | ||
|
|
5aa29ce405 | ||
|
|
a1419c2276 | ||
|
|
9bf83b4695 | ||
|
|
5934094330 | ||
|
|
456a2bc934 | ||
|
|
32c73ad8bd | ||
|
|
d3fffc2b49 | ||
|
|
ac2491d37d | ||
|
|
31d234560d | ||
|
|
4761656306 | ||
|
|
ce28ea33c9 | ||
|
|
29b4c4deff | ||
|
|
6c51082d40 | ||
|
|
5ec7d6e33e | ||
|
|
09c5698c11 | ||
|
|
1797aa5324 | ||
|
|
8b6af8b3d3 | ||
|
|
82b177fd02 | ||
|
|
deac72f05f | ||
|
|
6acac42412 | ||
|
|
1e517e71e9 | ||
|
|
ddfd78f872 | ||
|
|
75a201e45b | ||
|
|
b4507d800d | ||
|
|
5981bd18be | ||
|
|
4c012245c4 | ||
|
|
2f1357a2b5 | ||
|
|
5c3f1d4b17 | ||
|
|
a125794dba | ||
|
|
c46c95bb4a | ||
|
|
9ef9be41d5 | ||
|
|
11489822d0 | ||
|
|
070fe0a9d8 | ||
|
|
45713b1ffc | ||
|
|
d9bc6cf699 | ||
|
|
fa9440f885 | ||
|
|
1ea0b373d2 | ||
|
|
fad9030ded | ||
|
|
bfd0b155ef | ||
|
|
050e63fded | ||
|
|
b6d5c23bce | ||
|
|
1bffd827aa | ||
|
|
5ca09481e0 | ||
|
|
411f9c44df | ||
|
|
81d5b9388b | ||
|
|
dee71db556 | ||
|
|
31d66bcef8 | ||
|
|
9c5ed913d0 | ||
|
|
66f36ff002 | ||
|
|
3e43ecaa2d | ||
|
|
6fb395f8af | ||
|
|
376e8a7beb | ||
|
|
70d7f2bcf5 | ||
|
|
3d3a250072 | ||
|
|
6093797498 | ||
|
|
887242c341 | ||
|
|
62a6c7ae55 | ||
|
|
6aee524e0a | ||
|
|
d78af0b463 | ||
|
|
aa829c2c81 | ||
|
|
5bc7297934 | ||
|
|
8f53bd53ec | ||
|
|
8fd6f13855 | ||
|
|
8a2897d783 | ||
|
|
c847bd9f4d | ||
|
|
61080b3313 | ||
|
|
8301d39487 | ||
|
|
12cabdfb4e | ||
|
|
9da9514ba8 | ||
|
|
5e34a8a9b8 | ||
|
|
2f92777f71 | ||
|
|
ae2d0ff0a6 | ||
|
|
b003f70e26 | ||
|
|
445872f558 | ||
|
|
9afa8333b4 | ||
|
|
4f9c5007a7 | ||
|
|
7c88ac861c | ||
|
|
834a5fa854 | ||
|
|
2a2b81f6b9 | ||
|
|
f06fc0d982 | ||
|
|
92c7ca04c7 | ||
|
|
1bc3348553 | ||
|
|
a686f12f4d | ||
|
|
ed5c5b639a | ||
|
|
ac3d4cc761 | ||
|
|
24b2caca91 | ||
|
|
b37eee1054 | ||
|
|
dc4eaff6b3 | ||
|
|
8eeb9a074a | ||
|
|
7945286434 | ||
|
|
de522bf43b | ||
|
|
0a53fb1f2f | ||
|
|
ae49b45249 | ||
|
|
a2ebe15895 | ||
|
|
7f5a23d89d | ||
|
|
24218b4808 | ||
|
|
d085636fd7 | ||
|
|
75a1ea0c61 | ||
|
|
11c730c202 | ||
|
|
7cab755d1a | ||
|
|
12bec2f4f3 | ||
|
|
8bfe41b44b | ||
|
|
a8c211e0b2 | ||
|
|
9590dc9740 | ||
|
|
8808bb7b7f | ||
|
|
6f5f87b730 | ||
|
|
3ce367462a | ||
|
|
2f785cde06 | ||
|
|
719d6a9c25 | ||
|
|
1c318f3959 | ||
|
|
4ee30a1cff | ||
|
|
8d066d3007 | ||
|
|
26af455a6c | ||
|
|
86494a2800 | ||
|
|
a7ab7dd969 | ||
|
|
d297bc3241 | ||
|
|
65b4b5887b | ||
|
|
f3883944e5 | ||
|
|
2faa08e710 | ||
|
|
6cc8ab025a | ||
|
|
733587e78a | ||
|
|
bde4ad4d82 | ||
|
|
53fabec845 | ||
|
|
ccfa058cf7 | ||
|
|
14a71f3f92 | ||
|
|
aff71203e8 | ||
|
|
84f0594568 | ||
|
|
796a20ca9a | ||
|
|
21b7679ba7 | ||
|
|
439963f4c9 | ||
|
|
b2c54f8215 | ||
|
|
ecd865803e | ||
|
|
ed0cbdd8fa | ||
|
|
9ba4019898 | ||
|
|
3d05a20454 | ||
|
|
036346b604 | ||
|
|
4417b95c80 | ||
|
|
d82c770531 | ||
|
|
14083aa631 | ||
|
|
6b065717c6 | ||
|
|
d1b2d57467 | ||
|
|
8eef06671a | ||
|
|
416be6011e | ||
|
|
91c20b7ca5 | ||
|
|
38864e0f1b | ||
|
|
e8b0d34ea3 | ||
|
|
7a68880e2e | ||
|
|
9c849e63f7 | ||
|
|
b728505c9f | ||
|
|
d1fa6f8380 | ||
|
|
8cb2faf6a9 | ||
|
|
96a23450ab | ||
|
|
89f5a71e06 | ||
|
|
6757395b9c | ||
|
|
d957ab467e | ||
|
|
f08a859822 | ||
|
|
a532af7c74 | ||
|
|
73af758724 | ||
|
|
1b7441d62b | ||
|
|
1c15e1845b | ||
|
|
b2fead6a84 | ||
|
|
4112b55e06 | ||
|
|
50066eb1aa | ||
|
|
0da05bb13b | ||
|
|
e49d3f7dca | ||
|
|
ec314207de | ||
|
|
1f140678e8 | ||
|
|
33b570c652 | ||
|
|
9c3df13672 | ||
|
|
445d091d49 | ||
|
|
27519b08e2 | ||
|
|
a0a54555eb | ||
|
|
90602a7ad9 | ||
|
|
e3163e317a | ||
|
|
c0985fb0c9 | ||
|
|
df29371ee9 | ||
|
|
022a130a3e | ||
|
|
0259ed7f97 | ||
|
|
d8cb02bddb | ||
|
|
415034c493 | ||
|
|
b268ef4db9 | ||
|
|
8562a4ea47 | ||
|
|
8569105d81 | ||
|
|
892712a7cb | ||
|
|
39d42ecfbf | ||
|
|
ac1808b1be | ||
|
|
28b0b193c5 | ||
|
|
212a92cdfb | ||
|
|
5889bbb330 | ||
|
|
a3d2b1aa96 | ||
|
|
ced0e8c114 | ||
|
|
b0f952011a | ||
|
|
7abfbe8178 | ||
|
|
0ccd573229 | ||
|
|
cdabf31119 | ||
|
|
4d0d1435fe | ||
|
|
5fa10a3526 | ||
|
|
e33330fce1 | ||
|
|
519ac0d291 | ||
|
|
734f5c424b | ||
|
|
d4a514c390 | ||
|
|
8595c2f0a9 | ||
|
|
d3f624c6d7 | ||
|
|
8c97aded11 | ||
|
|
c6d37289c0 | ||
|
|
cbc04c7897 | ||
|
|
086683ab31 |
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -6,7 +6,7 @@ labels: 'bug'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
If you are trying to resolve an environment-specific issue or have a one-off question about the edge case that does not require a feature then please consider asking a question in argocd slack [channel](https://argoproj.github.io/community/join-slack).
|
||||
<!-- If you are trying to resolve an environment-specific issue or have a one-off question about the edge case that does not require a feature then please consider asking a question in argocd slack [channel](https://argoproj.github.io/community/join-slack). -->
|
||||
|
||||
Checklist:
|
||||
|
||||
@@ -16,19 +16,19 @@ Checklist:
|
||||
|
||||
**Describe the bug**
|
||||
|
||||
A clear and concise description of what the bug is.
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
**To Reproduce**
|
||||
|
||||
A list of the steps required to reproduce the issue. Best of all, give us the URL to a repository that exhibits this issue.
|
||||
<!-- A list of the steps required to reproduce the issue. Best of all, give us the URL to a repository that exhibits this issue. -->
|
||||
|
||||
**Expected behavior**
|
||||
|
||||
A clear and concise description of what you expected to happen.
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Screenshots**
|
||||
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
<!-- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Version**
|
||||
|
||||
|
||||
58
.github/workflows/ci-build.yaml
vendored
58
.github/workflows/ci-build.yaml
vendored
@@ -10,17 +10,11 @@ on:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
env:
|
||||
# Golang version to use across CI steps
|
||||
GOLANG_VERSION: '1.17.6'
|
||||
|
||||
jobs:
|
||||
build-docker:
|
||||
name: Build Docker image
|
||||
runs-on: ubuntu-latest
|
||||
if: github.head_ref != ''
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
make image
|
||||
check-go:
|
||||
name: Ensure Go modules synchronicity
|
||||
runs-on: ubuntu-latest
|
||||
@@ -30,7 +24,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Download all Go modules
|
||||
run: |
|
||||
go mod download
|
||||
@@ -48,7 +42,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
@@ -69,8 +63,8 @@ jobs:
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v1.29
|
||||
args: --timeout 5m --exclude SA5011
|
||||
version: v1.38.0
|
||||
args: --timeout 10m --exclude SA5011
|
||||
|
||||
test-go:
|
||||
name: Run unit tests for Go packages
|
||||
@@ -87,7 +81,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
run: |
|
||||
sudo apt-get install git -y
|
||||
@@ -147,7 +141,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Install required packages
|
||||
run: |
|
||||
sudo apt-get install git -y
|
||||
@@ -196,7 +190,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Create symlink in GOPATH
|
||||
run: |
|
||||
mkdir -p ~/go/src/github.com/argoproj
|
||||
@@ -259,6 +253,8 @@ jobs:
|
||||
yarn build
|
||||
env:
|
||||
NODE_ENV: production
|
||||
NODE_ONLINE_ENV: online
|
||||
HOST_ARCH: amd64
|
||||
working-directory: ui/
|
||||
- name: Run ESLint
|
||||
run: yarn lint
|
||||
@@ -335,7 +331,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
k3s-version: [v1.20.2, v1.19.2, v1.18.9, v1.17.11, v1.16.15]
|
||||
k3s-version: [v1.21.2, v1.20.2, v1.19.2, v1.18.9, v1.17.11]
|
||||
needs:
|
||||
- build-go
|
||||
env:
|
||||
@@ -354,7 +350,10 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: GH actions workaround - Kill XSP4 process
|
||||
run: |
|
||||
sudo pkill mono || true
|
||||
- name: Install K3S
|
||||
env:
|
||||
INSTALL_K3S_VERSION: ${{ matrix.k3s-version }}+k3s1
|
||||
@@ -380,7 +379,7 @@ jobs:
|
||||
- name: Download Go dependencies
|
||||
run: |
|
||||
go mod download
|
||||
go get github.com/mattn/goreman
|
||||
go install github.com/mattn/goreman@latest
|
||||
- name: Install all tools required for building & testing
|
||||
run: |
|
||||
make install-test-tools-local
|
||||
@@ -392,7 +391,7 @@ jobs:
|
||||
run: |
|
||||
docker pull quay.io/dexidp/dex:v2.25.0
|
||||
docker pull argoproj/argo-cd-ci-builder:v1.0.0
|
||||
docker pull redis:5.0.10-alpine
|
||||
docker pull redis:6.2.6-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
run: |
|
||||
mkdir -p dist
|
||||
@@ -425,3 +424,20 @@ jobs:
|
||||
name: e2e-server-k8s${{ matrix.k3s-version }}.log
|
||||
path: /tmp/e2e-server.log
|
||||
if: ${{ failure() }}
|
||||
|
||||
lint-docs:
|
||||
name: Lint docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r docs/requirements.txt
|
||||
- name: Lint docs
|
||||
run: |
|
||||
make lint-docs
|
||||
|
||||
1
.github/workflows/codeql.yml
vendored
1
.github/workflows/codeql.yml
vendored
@@ -8,6 +8,7 @@ on:
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
|
||||
# CodeQL runs on ubuntu-latest and windows-latest
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
30
.github/workflows/gh-pages.yaml
vendored
30
.github/workflows/gh-pages.yaml
vendored
@@ -1,30 +0,0 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: build
|
||||
run: |
|
||||
pip install -r docs/requirements.txt
|
||||
mkdocs build
|
||||
- name: deploy
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: peaceiris/actions-gh-pages@v2.5.0
|
||||
env:
|
||||
PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}
|
||||
PUBLISH_BRANCH: gh-pages
|
||||
PUBLISH_DIR: ./site
|
||||
47
.github/workflows/image.yaml
vendored
47
.github/workflows/image.yaml
vendored
@@ -4,16 +4,24 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
types: [ labeled, unlabeled, opened, synchronize, reopened ]
|
||||
|
||||
env:
|
||||
GOLANG_VERSION: '1.17.6'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOPATH: /home/runner/work/argo-cd/argo-cd
|
||||
steps:
|
||||
- uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
path: src/github.com/argoproj/argo-cd
|
||||
@@ -23,28 +31,43 @@ jobs:
|
||||
working-directory: ./src/github.com/argoproj/argo-cd
|
||||
id: image
|
||||
|
||||
# build
|
||||
# login
|
||||
- run: |
|
||||
docker images -a --format "{{.ID}}" | xargs -I {} docker rmi {}
|
||||
make image DEV_IMAGE=true DOCKER_PUSH=false IMAGE_NAMESPACE=docker.pkg.github.com/argoproj/argo-cd IMAGE_TAG=${{ steps.image.outputs.tag }}
|
||||
working-directory: ./src/github.com/argoproj/argo-cd
|
||||
|
||||
# publish
|
||||
- run: |
|
||||
docker login docker.pkg.github.com --username $USERNAME --password $PASSWORD
|
||||
docker push docker.pkg.github.com/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }}
|
||||
docker login ghcr.io --username $USERNAME --password $PASSWORD
|
||||
docker login quay.io --username "${DOCKER_USERNAME}" --password "${DOCKER_TOKEN}"
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
USERNAME: ${{ secrets.USERNAME }}
|
||||
PASSWORD: ${{ secrets.TOKEN }}
|
||||
DOCKER_USERNAME: ${{ secrets.RELEASE_QUAY_USERNAME }}
|
||||
DOCKER_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||
|
||||
# build
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
- run: |
|
||||
IMAGE_PLATFORMS=linux/amd64
|
||||
if [[ "${{ github.event_name }}" == "push" || "${{ contains(github.event.pull_request.labels.*.name, 'test-arm-image') }}" == "true" ]]
|
||||
then
|
||||
IMAGE_PLATFORMS=linux/amd64,linux/arm64
|
||||
fi
|
||||
echo "Building image for platforms: $IMAGE_PLATFORMS"
|
||||
docker buildx build --platform $IMAGE_PLATFORMS --push="${{ github.event_name == 'push' }}" \
|
||||
-t ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} \
|
||||
-t quay.io/argoproj/argocd:latest .
|
||||
working-directory: ./src/github.com/argoproj/argo-cd
|
||||
|
||||
|
||||
# deploy
|
||||
- run: git clone "https://$TOKEN@github.com/argoproj/argoproj-deployments"
|
||||
if: github.event_name == 'push'
|
||||
env:
|
||||
TOKEN: ${{ secrets.TOKEN }}
|
||||
- run: |
|
||||
docker run -v $(pwd):/src -w /src --rm -t lyft/kustomizer:v3.3.0 kustomize edit set image quay.io/argoproj/argocd=docker.pkg.github.com/argoproj/argo-cd/argocd:${{ steps.image.outputs.tag }}
|
||||
docker run -u $(id -u):$(id -g) -v $(pwd):/src -w /src --rm -t ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }} kustomize edit set image quay.io/argoproj/argocd=ghcr.io/argoproj/argocd:${{ steps.image.outputs.tag }}
|
||||
git config --global user.email 'ci@argoproj.com'
|
||||
git config --global user.name 'CI'
|
||||
git diff --exit-code && echo 'Already deployed' || (git commit -am 'Upgrade argocd to ${{ steps.image.outputs.tag }}' && git push)
|
||||
if: github.event_name == 'push'
|
||||
working-directory: argoproj-deployments/argocd
|
||||
# TODO: clean up old images once github supports it: https://github.community/t5/How-to-use-Git-and-GitHub/Deleting-images-from-Github-Package-Registry/m-p/41202/thread-id/9811
|
||||
# TODO: clean up old images once github supports it: https://github.community/t5/How-to-use-Git-and-GitHub/Deleting-images-from-GitHub-Package-Registry/m-p/41202/thread-id/9811
|
||||
|
||||
123
.github/workflows/release.yaml
vendored
123
.github/workflows/release.yaml
vendored
@@ -10,15 +10,20 @@ on:
|
||||
- '!release-v1.1*'
|
||||
- '!release-v1.0*'
|
||||
- '!release-v0*'
|
||||
|
||||
env:
|
||||
GOLANG_VERSION: '1.17.6'
|
||||
|
||||
jobs:
|
||||
prepare-release:
|
||||
name: Perform automatic release on trigger ${{ github.ref }}
|
||||
if: github.repository == 'argoproj/argo-cd'
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# The name of the tag as supplied by the GitHub event
|
||||
SOURCE_TAG: ${{ github.ref }}
|
||||
# The image namespace where Docker image will be published to
|
||||
IMAGE_NAMESPACE: argoproj
|
||||
IMAGE_NAMESPACE: quay.io/argoproj
|
||||
# Whether to create & push image and release assets
|
||||
DRY_RUN: false
|
||||
# Whether a draft release should be created, instead of public one
|
||||
@@ -90,7 +95,7 @@ jobs:
|
||||
echo "=========== BEGIN COMMIT MESSAGE ============="
|
||||
git show ${SOURCE_TAG}
|
||||
echo "============ END COMMIT MESSAGE =============="
|
||||
|
||||
|
||||
# Quite dirty hack to get the release notes from the annotated tag
|
||||
# into a temporary file.
|
||||
RELEASE_NOTES=$(mktemp -p /tmp release-notes.XXXXXX)
|
||||
@@ -137,9 +142,9 @@ jobs:
|
||||
echo "RELEASE_NOTES=${RELEASE_NOTES}" >> $GITHUB_ENV
|
||||
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.12'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
|
||||
- name: Setup Git author information
|
||||
run: |
|
||||
@@ -178,18 +183,7 @@ jobs:
|
||||
echo "Creating release ${RELEASE_TAG}"
|
||||
git tag ${RELEASE_TAG}
|
||||
|
||||
- name: Build Docker image for release
|
||||
run: |
|
||||
set -ue
|
||||
git clean -fd
|
||||
mkdir -p dist/
|
||||
make image IMAGE_TAG="${TARGET_VERSION}" DOCKER_PUSH=false
|
||||
make release-cli
|
||||
chmod +x ./dist/argocd-linux-amd64
|
||||
./dist/argocd-linux-amd64 version --client
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Push docker image to repository
|
||||
- name: Login to docker repositories
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.RELEASE_DOCKERHUB_USERNAME }}
|
||||
DOCKER_TOKEN: ${{ secrets.RELEASE_DOCKERHUB_TOKEN }}
|
||||
@@ -197,11 +191,23 @@ jobs:
|
||||
QUAY_TOKEN: ${{ secrets.RELEASE_QUAY_TOKEN }}
|
||||
run: |
|
||||
set -ue
|
||||
docker login --username "${DOCKER_USERNAME}" --password "${DOCKER_TOKEN}"
|
||||
docker push ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
|
||||
docker login quay.io --username "${QUAY_USERNAME}" --password "${QUAY_TOKEN}"
|
||||
docker tag ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} quay.io/${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
|
||||
docker push quay.io/${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION}
|
||||
# Remove the following when Docker Hub is gone
|
||||
docker login --username "${DOCKER_USERNAME}" --password "${DOCKER_TOKEN}"
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
|
||||
- uses: docker/setup-qemu-action@v1
|
||||
- uses: docker/setup-buildx-action@v1
|
||||
- name: Build and push Docker image for release
|
||||
run: |
|
||||
set -ue
|
||||
git clean -fd
|
||||
mkdir -p dist/
|
||||
docker buildx build --platform linux/amd64,linux/arm64 --push -t ${IMAGE_NAMESPACE}/argocd:v${TARGET_VERSION} -t argoproj/argocd:v${TARGET_VERSION} .
|
||||
make release-cli
|
||||
chmod +x ./dist/argocd-linux-amd64
|
||||
./dist/argocd-linux-amd64 version --client
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Read release notes file
|
||||
@@ -239,6 +245,17 @@ jobs:
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Upload argocd-linux-arm64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dist/argocd-linux-arm64
|
||||
asset_name: argocd-linux-arm64
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Upload argocd-darwin-amd64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -250,6 +267,17 @@ jobs:
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Upload argocd-darwin-arm64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dist/argocd-darwin-arm64
|
||||
asset_name: argocd-darwin-arm64
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Upload argocd-windows-amd64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
@@ -261,37 +289,45 @@ jobs:
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
# include argocd-util as part of release artifacts (argoproj/argo-cd#5174)
|
||||
- name: Upload argocd-util-linux-amd64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
- name: Generate SBOM (spdx)
|
||||
id: spdx-builder
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dist/argocd-linux-amd64
|
||||
asset_name: argocd-util-linux-amd64
|
||||
asset_content_type: application/octet-stream
|
||||
# defines the spdx/spdx-sbom-generator version to use.
|
||||
SPDX_GEN_VERSION: v0.0.13
|
||||
# defines the sigs.k8s.io/bom version to use.
|
||||
SIGS_BOM_VERSION: v0.2.1
|
||||
# comma delimited list of project relative folders to inspect for package
|
||||
# managers (gomod, yarn, npm).
|
||||
PROJECT_FOLDERS: ".,./ui"
|
||||
# full qualified name of the docker image to be inspected
|
||||
DOCKER_IMAGE: ${{env.IMAGE_NAMESPACE}}/argocd:v${{env.TARGET_VERSION}}
|
||||
run: |
|
||||
yarn install --cwd ./ui
|
||||
go install github.com/spdx/spdx-sbom-generator/cmd/generator@$SPDX_GEN_VERSION
|
||||
go install sigs.k8s.io/bom/cmd/bom@$SIGS_BOM_VERSION
|
||||
|
||||
# Generate SPDX for project dependencies analyzing package managers
|
||||
for folder in $(echo $PROJECT_FOLDERS | sed "s/,/ /g")
|
||||
do
|
||||
generator -p $folder -o /tmp
|
||||
done
|
||||
|
||||
# Generate SPDX for binaries analyzing the docker image
|
||||
if [[ ! -z $DOCKER_IMAGE ]]; then
|
||||
bom generate -o /tmp/bom-docker-image.spdx -i $DOCKER_IMAGE
|
||||
fi
|
||||
|
||||
cd /tmp && tar -zcf sbom.tar.gz *.spdx
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Upload argocd-util-darwin-amd64 binary to release assets
|
||||
- name: Upload SBOM to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dist/argocd-darwin-amd64
|
||||
asset_name: argocd-util-darwin-amd64
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
- name: Upload argocd-util-windows-amd64 binary to release assets
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dist/argocd-windows-amd64.exe
|
||||
asset_name: argocd-util-windows-amd64.exe
|
||||
asset_path: /tmp/sbom.tar.gz
|
||||
asset_name: sbom.tar.gz
|
||||
asset_content_type: application/octet-stream
|
||||
if: ${{ env.DRY_RUN != 'true' }}
|
||||
|
||||
@@ -309,3 +345,4 @@ jobs:
|
||||
set -ue
|
||||
git push --delete origin ${SOURCE_TAG}
|
||||
if: ${{ always() }}
|
||||
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -2,7 +2,9 @@
|
||||
.idea/
|
||||
.DS_Store
|
||||
vendor/
|
||||
dist/
|
||||
dist/*
|
||||
ui/dist/app/*
|
||||
!ui/dist/app/gitkeep
|
||||
site/
|
||||
*.iml
|
||||
# delve debug binaries
|
||||
@@ -13,10 +15,10 @@ test-results
|
||||
.scannerwork
|
||||
.scratch
|
||||
node_modules/
|
||||
.kube/
|
||||
|
||||
# ignore built binaries
|
||||
cmd/argocd/argocd
|
||||
cmd/argocd-application-controller/argocd-application-controller
|
||||
cmd/argocd-repo-server/argocd-repo-server
|
||||
cmd/argocd-server/argocd-server
|
||||
cmd/argocd-util/argocd-util
|
||||
cmd/argocd-server/argocd-server
|
||||
17
.gitpod.Dockerfile
vendored
Normal file
17
.gitpod.Dockerfile
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
FROM gitpod/workspace-full
|
||||
|
||||
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://go.kubebuilder.io/dl/2.3.1/$(go env GOOS)/$(go env GOARCH) | \
|
||||
tar -xz -C /tmp/ && mv /tmp/kubebuilder_2.3.1_$(go env GOOS)_$(go env GOARCH) /usr/local/kubebuilder
|
||||
|
||||
RUN apt-get install redis-server -y
|
||||
RUN go install github.com/mattn/goreman@latest
|
||||
|
||||
USER gitpod
|
||||
|
||||
ENV ARGOCD_REDIS_LOCAL=true
|
||||
ENV KUBECONFIG=/tmp/kubeconfig
|
||||
6
.gitpod.yml
Normal file
6
.gitpod.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
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
|
||||
310
CHANGELOG.md
310
CHANGELOG.md
@@ -1,6 +1,284 @@
|
||||
# Changelog
|
||||
|
||||
## v1.8.0 (Unreleased)
|
||||
## v2.2.0 (2021-12-14)
|
||||
|
||||
> [Upgrade instructions](./docs/operator-manual/upgrading/2.1-2.2.md)
|
||||
|
||||
### Project Scoped repositories and clusters
|
||||
|
||||
The project scoped repositories and clusters is a feature that simplifies registering the repositories and cluster credentials.
|
||||
Instead of requiring operators to set up in advance all clusters and git repositories that can be used, developers can now do
|
||||
this on their own in a self-service manner.
|
||||
|
||||
### Config Management Plugins V2
|
||||
|
||||
The Config Management Plugins V2 is set of enhancement of the existing config management plugins feature.
|
||||
The list includes improved installation experience, ability to package plugin into a separate image and
|
||||
improved plugin manifests discovery.
|
||||
|
||||
### Resource tracking
|
||||
|
||||
Argo CD has traditionally tracked the resources it manages by the well-known "app.kubernetes.io/instance" property.
|
||||
While using this property works ok in simple scenarios, it also has several limitations. ArgoCD now allows you to use
|
||||
a new annotation (argocd.argoproj.io/tracking-id) for tracking your resources. Using this annotation is a much more flexible approach
|
||||
as there are no conflicts with other Kubernetes tools, and you can easily install multiple Argo CD instances on the same clusters.
|
||||
|
||||
### Bug Fixes and Performance Enhancements
|
||||
|
||||
* Argo CD API server caches RBAC checks that significantly improves the GET /api/v1/applications API performance (#7587)
|
||||
* Argo CD RBAC supports regex matches (#7165)
|
||||
* Health check support for KubeVirt (#7176), Cassandra (#7017), Openshift Route (#7112), DeploymentConfig (#7114), Confluent (#6957) and SparkApplication (#7434) CRDs.
|
||||
* Persistent banner (#7312) with custom positioning (#7462)
|
||||
* Cluster name support in project destinations (#7198)
|
||||
* around 30 more features and a total of 84 bug fixes
|
||||
|
||||
## v2.1.7 (2021-12-14)
|
||||
|
||||
- fix: issue with keepalive (#7861)
|
||||
- fix nil pointer dereference error (#7905)
|
||||
- fix: env vars to tune cluster cache were broken (#7779)
|
||||
- fix: upgraded gitops engine to v0.4.2 (fixes #7561)
|
||||
|
||||
|
||||
## v2.1.6 (2021-11-16)
|
||||
|
||||
- fix: don't use revision caching during app creation (#7508)
|
||||
- fix: supporting OCI dependencies. Fixes #6062 (#6994)
|
||||
|
||||
## v2.1.5 (2021-11-16)
|
||||
|
||||
- fix: Invalid memory address or nil pointer dereference in processRequestedAppOperation (#7501)
|
||||
|
||||
## v2.1.4 (2021-11-15)
|
||||
|
||||
- fix: Operation has completed with phase: Running (#7482)
|
||||
- fix: Application status panel shows Syncing instead of Deleting (#7486)
|
||||
- fix(ui): Add Error Boundary around Extensions and comply with new Extensions API (#7215)
|
||||
|
||||
|
||||
## v2.1.3 (2021-10-29)
|
||||
|
||||
- fix: core-install.yaml always refers to latest argocd image (#7321)
|
||||
- fix: handle applicationset backup forbidden error (#7306)
|
||||
- fix: Argo CD should not use cached git/helm revision during app creation/update validation (#7244)
|
||||
|
||||
## v2.1.2 (2021-10-02)
|
||||
|
||||
- fix: cluster filter popping out of box (#7135)
|
||||
- fix: gracefully shutdown metrics server when dex config changes (#7138)
|
||||
- fix: upgrade gitops engine version to v0.4.1 (#7088)
|
||||
- fix: repository name already exists when multiple helm dependencies (#7096)
|
||||
|
||||
|
||||
## v2.1.1 (2021-08-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fix: password reset requirements (#7071)
|
||||
- fix: Custom Styles feature is broken (#7067)
|
||||
- fix(ui): Add State to props passed to Extensions (#7045)
|
||||
- fix: keep uid_entrypoint.sh for backward compatibility (#7047)
|
||||
|
||||
## v2.1.0 (2021-08-20)
|
||||
|
||||
> [Upgrade instructions](./docs/operator-manual/upgrading/2.0-2.1.md)
|
||||
|
||||
### Argo CD Core
|
||||
|
||||
Argo CD Core - lightweight Argo CD distribution that packages only core GitOps features and relies
|
||||
on Kubernetes API/RBAC to power UI and CLI.
|
||||
|
||||
### Core Features
|
||||
|
||||
* The synchronization process became much much faster and requires significantly less memory.
|
||||
* An additional caching that ensures that each repository's target revisions are queried only once per
|
||||
reconciliation cycle. This dramatically reduces the number of Git requests.
|
||||
* Improved Diffing Customizations: use JQ path expressions to exclude required fields from the diffing.
|
||||
* Health assessment support for new CRDs: introduced health assessment of CRDs from trident.netapp.io,
|
||||
elasticsearch.k8s.elastic.co, cluster.x-k8s.io, and minio.min.io API groups.
|
||||
|
||||
### Improved Settings
|
||||
|
||||
A set of changes had been implemented to simplify configuring Argo CD.
|
||||
|
||||
* Simplified Repository Registration: you no longer need to modify the argocd-cm ConfigMap to register a
|
||||
new Git or Helm repository.
|
||||
* Enhanced Resource Customizations: the resource.customizations key has been deprecated in favor of
|
||||
a separate ConfigMap key per resource.
|
||||
* Reference secret values from any Kubernetes secret: starting v2.1 you can use sensitive data stored in
|
||||
any Kubernetes secret to configure Argo CD.
|
||||
* Simplify parametrization of Argo CD server processes: an additional optional ConfigMap argocd-cmd-params-cm
|
||||
has been introduced.
|
||||
|
||||
### Refreshed User Interface
|
||||
|
||||
* Enhanced and more consistent filters on Applications List and Applications Details pages.
|
||||
* Status bar on the Application List page.
|
||||
* The redesigned search box on the Application List page and more.
|
||||
|
||||
### The argocd-util CLI deprecation
|
||||
|
||||
The argocd CLI and now available under argocd admin subcommand.
|
||||
|
||||
## v2.0.5 (2021-07-22)
|
||||
|
||||
* fix: allow argocd-notification ingress to repo-server (#6746)
|
||||
* fix: argocd-server crashes due to nil pointer dereference (#6757)
|
||||
* fix: WebUI failure when loading pod view 't.parentRefs is undefined' (#6490) (#6535)
|
||||
* fix: prevent 'cannot read property "filter" of undefined' during nodes filtering (#6453)
|
||||
* fix: download Pod Logs button not honouring argocd-server rootpath (#6548) (#6627)
|
||||
* fix: Version warning banner in docs (#6682)
|
||||
* fix: upgrade gitops engine to fix workflow health check
|
||||
|
||||
## v2.0.4 (2021-06-22)
|
||||
|
||||
* fix: typo in networkPolicy definition in manifests (#6532)
|
||||
* fix: Update redis to 6.2.4 (#6475)
|
||||
* fix: allows access to dex metrics from any pod (#6420)
|
||||
* fix: add client side retry to prevent 'transport is closing' errors (#6402)
|
||||
* fix: Update documentation Argocd app CRD health with app of apps (#6281)
|
||||
* fix(ui): Crash on application pod view (#6384)
|
||||
* chore: pin mkdocs version to fix docs build (#6421)
|
||||
* chore: regenerate manifests using codegen (#6422)
|
||||
* refactor: use RLock and RUnlock for project to improve performance (#6225)
|
||||
* chore: Update Golang to v1.16.4 (#6358)
|
||||
|
||||
## v2.0.3 (2021-05-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix: add missing --container flag to 'argocd app logs' command (#6320)
|
||||
* fix: grpc web proxy must ensure to read full header (#6319)
|
||||
* fix: controller should refresh app before running sync operation (#6294)
|
||||
|
||||
## v2.0.2 (2021-05-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix: enable access to metrics port in embedded network policies (#6277)
|
||||
* fix: display log streaming error in logs viewer (#6100) (#6273)
|
||||
* fix: Don't count errored or completed neighbor pods toward resource consumption (#6259)
|
||||
* fix: Enable kex algo diffie-hellman-group-exchange-sha256 for go-git ssh (#6256)
|
||||
* fix: copy github app key from repocreds (#6140, #6197)
|
||||
* fix(ui): UI crashes after reinstalling ArgoCD (#6218)
|
||||
* fix: add network policies to restrict traffic flow between argocd components (#6156)
|
||||
* fix: Revert "feat: Add health checks for kubernetes-external-secrets (#5435)"
|
||||
* chore: Allow ingress traffic to argocd-server by default (#6179)
|
||||
|
||||
## v2.0.1 (2021-04-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* fix: spark application check fails on missing section (#6036)
|
||||
* fix: Adding explicit bind to redis and sentinel for IPv4 clusters #5957 (#6005)
|
||||
* fix: fix: use correct field for evaluating whether or not GitHub Enterprise is selected (#5987)
|
||||
|
||||
## v2.0.0 (2021-04-07)
|
||||
|
||||
> [Upgrade instructions](./docs/operator-manual/upgrading/1.8-2.0.md)
|
||||
|
||||
### Pods View
|
||||
|
||||
Pods View is particularly useful for applications that have hundreds of pods. Instead of visualizing all Kubernetes
|
||||
resources for the application, it only shows Kubernetes pods and closely related resources. The Pods View supports
|
||||
grouping related resources by Parent Resource, Top Level Parent, or by Node. Each way of grouping solves a particular
|
||||
use case. For example grouping by Top Level Parent allows you to quickly find how many pods your application is running
|
||||
and which resources created them. Grouping by Node allows to see how Pods are spread across the nodes and how many
|
||||
resources they requested.
|
||||
|
||||
|
||||
### Logs Viewer
|
||||
|
||||
Argo CD provides a way to see live logs of pods, which is very useful for debugging and troubleshooting. In the v2.0
|
||||
release, the log visualization has been rewritten to support pagination, filtering, the ability to disable/enable log
|
||||
streaming, and even a dark mode for terminal lovers. Do you want to see aggregated logs of multiple deployment pods?
|
||||
Not a problem! Just click on the parent resource such as Deployment, ReplicaSet, or StatefulSet and navigate
|
||||
to the Logs tab.
|
||||
|
||||
### Banner Feature
|
||||
|
||||
Want to notify your Argo CD users of upcoming changes? Just specify the notification message and optional URL using the
|
||||
`ui.bannercontent` and `ui.bannerurl` attributes in the `argocd-cm` ConfigMap.
|
||||
|
||||
### Core Features
|
||||
|
||||
* The new sync option `PrunePropagationPolicy=background` allows using background deletion during syncing
|
||||
* New application finalizer `resources-finalizer.argocd.argoproj.io:background` allows using background deletion when the application is deleted
|
||||
* The new sync option `ApplyOutOfSyncOnly=true` allows skipping syncing resources that are already in the desired state.
|
||||
* The new sync option `PruneLast=true` allows deferring resource pruning until the last synchronization phase after all other resources are synced and healthy.
|
||||
|
||||
### The argocd-util CLI
|
||||
|
||||
Argo CD Util is a CLI tool that contains useful commands for operators who manage Argo CD. Starting from this release
|
||||
the Argo CD Utility is published with every Argo CD release as a Homebrew installation.
|
||||
|
||||
## v1.8.7 (2021-02-26)
|
||||
|
||||
### Important note
|
||||
This release fixed a regression regarding which cluster resources are permitted on the AppProject level.
|
||||
Previous to this fix, after #3960 has been merged, all cluster resources were allowed on project level when neither of
|
||||
the allow or deny lists was defined. However, the correct behavior is to block all resources in this case.
|
||||
|
||||
If you have Projects with empty allow and deny lists, but want the associated applications be able to sync cluster
|
||||
resources, you will have to adapt your cluster resources allow lists to explicitly allow the resources.
|
||||
|
||||
- fix: redact sensitive data in logs (#5662)
|
||||
- fix: Properly escape HTML for error message from CLI SSO (#5563)
|
||||
- fix: Empty resource whitelist allowed all resources (#5540) (#5551)
|
||||
|
||||
## v1.8.6 (2021-02-26)
|
||||
|
||||
- fix: Properly escape HTML for error message from CLI SSO (#5563)
|
||||
- fix: API server should not print resource body when resource update fails (#5617)
|
||||
- fix: fix memory leak in application controller (#5604)
|
||||
|
||||
## v1.8.5 (2021-02-19)
|
||||
|
||||
- fix: 'argocd app wait --suspended' stuck if operation is in progress (#5511)
|
||||
- fix: Presync hooks stop working after namespace resource is added in a Helm chart #5522
|
||||
- docs: add the missing rbac resources to the documentation (#5476)
|
||||
- refactor: optimize argocd-application-controller redis usage (#5345)
|
||||
|
||||
## v1.8.4 (2021-02-05)
|
||||
|
||||
- feat: set X-XSS-Protection while serving static content (#5412)
|
||||
- fix: version info should be avaialble if anonymous access is enabled (#5422)
|
||||
- fix: disable jwt claim audience validation #5381 (#5413)
|
||||
- fix: /api/version should not return tools version for unauthenticated requests (#5415)
|
||||
- fix: account tokens should be rejected if required capability is disabled (#5414)
|
||||
- fix: tokens keep working after account is deactivated (#5402)
|
||||
- fix: a request which was using a revoked project token, would still be allowed to perform requests allowed by default policy (#5378)
|
||||
|
||||
## v1.8.3 (2021-01-21)
|
||||
|
||||
- fix: make sure JWT token time fields contain only integer values (#5228)
|
||||
|
||||
## v1.8.2 (2021-01-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fix: updating cluster drops secret (#5220)
|
||||
- fix: remove invalid assumption about OCI helm chart path (#5179)
|
||||
- fix: Possible nil pointer dereference in repository API (#5128)
|
||||
- fix: Possible nil pointer dereference in repocreds API (#5130)
|
||||
- fix: use json serialization to store cache instead of github.com/vmihailenco/msgpack (#4965)
|
||||
- fix: add liveness probe to restart repo server if it fails to server tls requests (#5110) (#5119)
|
||||
- fix: Allow correct SSO redirect URL for CLI static client (#5098)
|
||||
- fix: add grpc health check (#5060)
|
||||
- fix: setting 'revision history limit' errors in UI (#5035)
|
||||
- fix: add api-server liveness probe that catches bad data in informer (#5026)
|
||||
|
||||
### Refactoring
|
||||
|
||||
- chore: Update Dex to v2.27.0 (#5058)
|
||||
- chore: Upgrade gorilla/handlers and gorilla/websocket (#5186)
|
||||
- chore: Upgrade jwt-go to 4.0.0-preview1 (#5184)
|
||||
|
||||
## v1.8.1 (2020-12-09)
|
||||
|
||||
- fix: sync retry is broken for multi-phase syncs (#5017)
|
||||
|
||||
## v1.8.0 (2020-12-09)
|
||||
|
||||
### Mono-Repository Improvements
|
||||
|
||||
@@ -93,7 +371,7 @@ In addition to new features and enhancements, we’ve fixed more than 50 bugs an
|
||||
## v1.7.5 (2020-09-15)
|
||||
|
||||
- fix: app create with -f should not ignore other options (#4322)
|
||||
- fix: limit concurrent list requests accross all clusters (#4328)
|
||||
- fix: limit concurrent list requests across all clusters (#4328)
|
||||
- fix: fix possible deadlock in /v1/api/stream/applications and /v1/api/application APIs (#4315)
|
||||
- fix: WatchResourceTree does not enforce RBAC (#4311)
|
||||
- fix: app refresh API should use app resource version (#4303)
|
||||
@@ -227,7 +505,7 @@ use cases, such as bootstrapping a Kubernetes cluster, or decentralized manageme
|
||||
|
||||
#### Other
|
||||
|
||||
- refactoring: Gitops engine (#3066)
|
||||
- refactoring: GitOps engine (#3066)
|
||||
|
||||
## v1.5.8 (2020-06-16)
|
||||
|
||||
@@ -290,7 +568,7 @@ customizations, custom resource health checks, and more.
|
||||
### Other
|
||||
|
||||
* New Project and Application CRD settings ([#2900](https://github.com/argoproj/argo-cd/issues/2900), [#2873](https://github.com/argoproj/argo-cd/issues/2873)) that allows customizing Argo CD behavior.
|
||||
* Upgraded Dex (v2.22.0) enables seamless [SSO integration](https://www.openshift.com/blog/openshift-authentication-integration-with-argocd) with Openshift.
|
||||
* Upgraded Dex (v2.22.0) enables seamless [SSO integration](https://www.openshift.com/blog/openshift-authentication-integration-with-argocd) with OpenShift.
|
||||
|
||||
|
||||
#### Enhancements
|
||||
@@ -322,7 +600,7 @@ customizations, custom resource health checks, and more.
|
||||
* fix for helm repo add with flag --insecure-skip-server-verification (#3420)
|
||||
* fix: app diff --local support for helm repo. #3151 (#3407)
|
||||
* fix: Syncing apps incorrectly states "app synced", but this is not true (#3286)
|
||||
* fix: for jsonnet when it is localed in nested subdirectory and uses import (#3372)
|
||||
* fix: for jsonnet when it is located in nested subdirectory and uses import (#3372)
|
||||
* fix: Update 4.5.3 redis-ha helm manifest (#3370)
|
||||
* fix: return 401 error code if username does not exist (#3369)
|
||||
* fix: Do not panic while running hooks with short revision (#3368)
|
||||
@@ -438,7 +716,7 @@ Last-minute bugs that will be addressed in 1.5.1 shortly:
|
||||
- fix: argocd-util backup produced truncated backups. import app status (#3096)
|
||||
- fix: upgrade redis-ha chart and enable haproxy (#3147)
|
||||
- fix: make dex server deployment init container resilient to restarts (#3136)
|
||||
- fix: reduct secret values of manifests stored in git (#3088)
|
||||
- fix: redact secret values of manifests stored in git (#3088)
|
||||
- fix: labels not being deleted via UI (#3081)
|
||||
- fix: HTTP|HTTPS|NO_PROXY env variable reading #3055 (#3063)
|
||||
- fix: Correct usage text for repo add command regarding insecure repos (#3068)
|
||||
@@ -545,10 +823,10 @@ More documentation and tools are coming in patch releases.
|
||||
The Argo CD deletes all **in-flight** hooks if you terminate running sync operation. The hook state assessment change implemented in this release the Argo CD enables detection of
|
||||
an in-flight state for all Kubernetes resources including `Deployment`, `PVC`, `StatefulSet`, `ReplicaSet` etc. So if you terminate the sync operation that has, for example,
|
||||
`StatefulSet` hook that is `Progressing` it will be deleted. The long-running jobs are not supposed to be used as a sync hook and you should consider using
|
||||
[Sync Waves](https://argoproj.github.io/argo-cd/user-guide/sync-waves/) instead.
|
||||
[Sync Waves](https://argo-cd.readthedocs.io/en/stable/user-guide/sync-waves/) instead.
|
||||
|
||||
#### Enhancements
|
||||
* feat: Add custom healthchecks for cert-manager v0.11.0 (#2689)
|
||||
* feat: Add custom health checks for cert-manager v0.11.0 (#2689)
|
||||
* feat: add git submodule support (#2495)
|
||||
* feat: Add repository credential management API and CLI (addresses #2136) (#2207)
|
||||
* feat: add support for --additional-headers cli flag (#2467)
|
||||
@@ -733,7 +1011,7 @@ There may be instances when you want to control the times during which an Argo C
|
||||
#### Bug Fixes
|
||||
|
||||
- failed parsing on parameters with comma (#1660)
|
||||
- Statefulset with OnDelete Update Strategy stuck progressing (#1881)
|
||||
- StatefulSet with OnDelete Update Strategy stuck progressing (#1881)
|
||||
- Warning during secret diffing (#1923)
|
||||
- Error message "Unable to load data: key is missing" is confusing (#1944)
|
||||
- OIDC group bindings are truncated (#2006)
|
||||
@@ -815,7 +1093,7 @@ There may be instances when you want to control the times during which an Argo C
|
||||
## v1.2.3 (2019-10-1)
|
||||
* Make argo-cd docker images openshift friendly (#2362) (@duboisf)
|
||||
* Add dest-server and dest-namespace field to reconciliation logs (#2354)
|
||||
- Stop loggin /repository.RepositoryService/ValidateAccess parameters (#2386)
|
||||
- Stop logging /repository.RepositoryService/ValidateAccess parameters (#2386)
|
||||
|
||||
## v1.2.2 (2019-09-26)
|
||||
+ Resource action equivalent to `kubectl rollout restart` (#2177)
|
||||
@@ -900,7 +1178,7 @@ Support for Git LFS enabled repositories - now you can store Helm charts as tar
|
||||
- Wait for CRD creation during sync process (#1940)
|
||||
- Added a button to select out of sync items in the sync panel (#1902)
|
||||
- Proper handling of an excluded resource in an application (#1621)
|
||||
- Stop repeating logs on stoped container (#1614)
|
||||
- Stop repeating logs on stopped container (#1614)
|
||||
- Fix git repo url parsing on application list view (#2174)
|
||||
- Fix nil pointer dereference error during app reconciliation (#2146)
|
||||
- Fix history api fallback implementation to support app names with dots (#2114)
|
||||
@@ -956,7 +1234,7 @@ optimized which significantly reduced the number of Git requests. With v1.1 rele
|
||||
#### User Defined Application Metadata
|
||||
|
||||
User-defined Application metadata enables the user to define a list of useful URLs for their specific application and expose those links on the UI
|
||||
(e.g. reference tp a CI pipeline or an application-specific management tool). These links should provide helpful shortcuts that make easier to integrate Argo CD into existing
|
||||
(e.g. reference to a CI pipeline or an application-specific management tool). These links should provide helpful shortcuts that make easier to integrate Argo CD into existing
|
||||
systems by making it easier to find other components inside and outside Argo CD.
|
||||
|
||||
### Deprecation Notice
|
||||
@@ -1320,7 +1598,7 @@ has a minimum client version of v0.12.0. Older CLI clients will be rejected.
|
||||
* Deprecate componentParameterOverrides in favor of source specific config (#1207)
|
||||
* Support talking to Dex using local cluster address instead of public address (#1211)
|
||||
* Use Recreate deployment strategy for controller (#1315)
|
||||
* Honor os environment variables for helm commands (#1306) (@1337andre)
|
||||
* Honor OS environment variables for helm commands (#1306) (@1337andre)
|
||||
* Disable CGO_ENABLED for server/controller binaries (#1286)
|
||||
* Documentation fixes and improvements (@twz123, @yann-soubeyrand, @OmerKahani, @dulltz)
|
||||
- Fix CRD creation/deletion handling (#1249)
|
||||
@@ -1410,7 +1688,7 @@ running Dex (e.g. Okta, OneLogin, Auth0, Microsoft, etc...)
|
||||
The optional, [Dex IDP OIDC provider](https://github.com/dexidp/dex) is still bundled as part of the
|
||||
default installation, in order to provide a seamless out-of-box experience, enabling Argo CD to
|
||||
integrate with non-OIDC providers, and to benefit from Dex's full range of
|
||||
[connectors](https://github.com/dexidp/dex/tree/master/Documentation/connectors).
|
||||
[connectors](https://dexidp.io/docs/connectors/).
|
||||
|
||||
#### OIDC group bindings to Project Roles
|
||||
OIDC group claims from an OAuth2 provider can now be bound to a Argo CD project roles. Previously,
|
||||
@@ -1812,8 +2090,8 @@ RBAC policy rules, need to be rewritten to include one extra column with the eff
|
||||
+ Override parameters
|
||||
|
||||
## v0.1.0 (2018-03-12)
|
||||
+ Define app in Github with dev and preprod environment using KSonnet
|
||||
+ Define app in GitHub with dev and preprod environment using KSonnet
|
||||
+ Add cluster Diff App with a cluster Deploy app in a cluster
|
||||
+ Deploy a new version of the app in the cluster
|
||||
+ App sync based on Github app config change - polling only
|
||||
+ App sync based on GitHub app config change - polling only
|
||||
+ Basic UI: App diff between Git and k8s cluster for all environments Basic GUI
|
||||
|
||||
42
Dockerfile
42
Dockerfile
@@ -1,10 +1,10 @@
|
||||
ARG BASE_IMAGE=ubuntu:20.10
|
||||
ARG BASE_IMAGE=docker.io/library/ubuntu:21.10
|
||||
####################################################################################################
|
||||
# Builder 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
|
||||
####################################################################################################
|
||||
FROM golang:1.14.12 as builder
|
||||
FROM docker.io/library/golang:1.17.6 as builder
|
||||
|
||||
RUN echo 'deb http://deb.debian.org/debian buster-backports main' >> /etc/apt/sources.list
|
||||
|
||||
@@ -17,6 +17,7 @@ RUN apt-get update && apt-get install -y \
|
||||
make \
|
||||
wget \
|
||||
gcc \
|
||||
sudo \
|
||||
zip && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
@@ -27,11 +28,11 @@ ADD hack/install.sh .
|
||||
ADD hack/installers installers
|
||||
ADD hack/tool-versions.sh .
|
||||
|
||||
RUN ./install.sh packr-linux
|
||||
RUN ./install.sh ksonnet-linux
|
||||
RUN ./install.sh helm2-linux
|
||||
RUN ./install.sh helm-linux
|
||||
RUN ./install.sh kustomize-linux
|
||||
RUN ./install.sh awscli-linux
|
||||
|
||||
####################################################################################################
|
||||
# Argo CD Base - used as the base for both the release and dev argocd images
|
||||
@@ -47,12 +48,10 @@ RUN groupadd -g 999 argocd && \
|
||||
mkdir -p /home/argocd && \
|
||||
chown argocd:0 /home/argocd && \
|
||||
chmod g=u /home/argocd && \
|
||||
chmod g=u /etc/passwd && \
|
||||
apt-get update && \
|
||||
apt-get dist-upgrade -y && \
|
||||
apt-get install -y git git-lfs python3-pip tini gpg && \
|
||||
apt-get install -y git git-lfs tini gpg tzdata && \
|
||||
apt-get clean && \
|
||||
pip3 install awscli==1.18.80 && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY hack/git-ask-pass.sh /usr/local/bin/git-ask-pass.sh
|
||||
@@ -62,9 +61,11 @@ COPY --from=builder /usr/local/bin/ks /usr/local/bin/ks
|
||||
COPY --from=builder /usr/local/bin/helm2 /usr/local/bin/helm2
|
||||
COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
|
||||
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
|
||||
# script to add current (possibly arbitrary) user to /etc/passwd at runtime
|
||||
# (if it's not already there, to be openshift friendly)
|
||||
COPY uid_entrypoint.sh /usr/local/bin/uid_entrypoint.sh
|
||||
COPY --from=builder /usr/local/aws-cli/v2/current/dist /usr/local/aws-cli/v2/current/dist
|
||||
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
# 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/aws-cli/v2/current/dist/aws /usr/local/bin/aws
|
||||
|
||||
# support for mounting configuration from a configmap
|
||||
RUN mkdir -p /app/config/ssh && \
|
||||
@@ -86,25 +87,23 @@ WORKDIR /home/argocd
|
||||
####################################################################################################
|
||||
# Argo CD UI stage
|
||||
####################################################################################################
|
||||
FROM node:12.18.4 as argocd-ui
|
||||
FROM docker.io/library/node:12.18.4 as argocd-ui
|
||||
|
||||
WORKDIR /src
|
||||
ADD ["ui/package.json", "ui/yarn.lock", "./"]
|
||||
|
||||
RUN yarn install
|
||||
RUN yarn install --network-timeout 100000
|
||||
|
||||
ADD ["ui/", "."]
|
||||
|
||||
ARG ARGO_VERSION=latest
|
||||
ENV ARGO_VERSION=$ARGO_VERSION
|
||||
RUN NODE_ENV='production' yarn build
|
||||
RUN HOST_ARCH='amd64' NODE_ENV='production' NODE_ONLINE_ENV='online' NODE_OPTIONS=--max_old_space_size=8192 yarn build
|
||||
|
||||
####################################################################################################
|
||||
# Argo CD Build stage which performs the actual build of Argo CD binaries
|
||||
####################################################################################################
|
||||
FROM golang:1.14.12 as argocd-build
|
||||
|
||||
COPY --from=builder /usr/local/bin/packr /usr/local/bin/packr
|
||||
FROM docker.io/library/golang:1.17.6 as argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
@@ -115,26 +114,21 @@ RUN go mod download
|
||||
|
||||
# Perform the build
|
||||
COPY . .
|
||||
COPY --from=argocd-ui /src/dist/app /go/src/github.com/argoproj/argo-cd/ui/dist/app
|
||||
RUN make argocd-all
|
||||
|
||||
ARG BUILD_ALL_CLIS=true
|
||||
RUN if [ "$BUILD_ALL_CLIS" = "true" ] ; then \
|
||||
make BIN_NAME=argocd-darwin-amd64 GOOS=darwin argocd-all && \
|
||||
make BIN_NAME=argocd-windows-amd64.exe GOOS=windows argocd-all \
|
||||
; fi
|
||||
|
||||
####################################################################################################
|
||||
# Final image
|
||||
####################################################################################################
|
||||
FROM argocd-base
|
||||
COPY --from=argocd-build /go/src/github.com/argoproj/argo-cd/dist/argocd* /usr/local/bin/
|
||||
COPY --from=argocd-ui ./src/dist/app /shared/app
|
||||
|
||||
USER root
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-util
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-cmp-server
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-notifications
|
||||
|
||||
USER 999
|
||||
USER 999
|
||||
|
||||
@@ -3,17 +3,11 @@
|
||||
####################################################################################################
|
||||
FROM argocd-base
|
||||
COPY argocd /usr/local/bin/
|
||||
COPY argocd-darwin-amd64 /usr/local/bin/
|
||||
COPY argocd-windows-amd64.exe /usr/local/bin/
|
||||
|
||||
USER root
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-server
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-repo-server
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-application-controller
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-dex
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-util
|
||||
RUN ln -s /usr/local/bin/argocd-darwin-amd64 /usr/local/bin/argocd-util-darwin-amd64
|
||||
RUN ln -s /usr/local/bin/argocd-windows-amd64.exe /usr/local/bin/argocd-util-windows-amd64.exe
|
||||
RUN ln -s /usr/local/bin/argocd /usr/local/bin/argocd-notifications
|
||||
USER 999
|
||||
|
||||
COPY --from=argocd-ui ./src/dist/app /shared/app
|
||||
|
||||
129
Makefile
129
Makefile
@@ -1,10 +1,11 @@
|
||||
PACKAGE=github.com/argoproj/argo-cd/common
|
||||
PACKAGE=github.com/argoproj/argo-cd/v2/common
|
||||
CURRENT_DIR=$(shell pwd)
|
||||
DIST_DIR=${CURRENT_DIR}/dist
|
||||
CLI_NAME=argocd
|
||||
UTIL_CLI_NAME=argocd-util
|
||||
BIN_NAME=argocd
|
||||
|
||||
GEN_RESOURCES_CLI_NAME=argocd-resources-gen
|
||||
|
||||
HOST_OS:=$(shell go env GOOS)
|
||||
HOST_ARCH:=$(shell go env GOARCH)
|
||||
|
||||
@@ -13,9 +14,8 @@ BUILD_DATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
GIT_COMMIT=$(shell git rev-parse HEAD)
|
||||
GIT_TAG=$(shell if [ -z "`git status --porcelain`" ]; then git describe --exact-match --tags HEAD 2>/dev/null; fi)
|
||||
GIT_TREE_STATE=$(shell if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)
|
||||
PACKR_CMD=$(shell if [ "`which packr`" ]; then echo "packr"; else echo "go run github.com/gobuffalo/packr/packr"; fi)
|
||||
VOLUME_MOUNT=$(shell if test "$(go env GOOS)" = "darwin"; then echo ":delegated"; elif test selinuxenabled; then echo ":delegated"; else echo ""; fi)
|
||||
KUBECTL_VERSION=$(shell go list -m all | grep k8s.io/client-go | cut -d ' ' -f5)
|
||||
KUBECTL_VERSION=$(shell go list -m k8s.io/client-go | head -n 1 | rev | cut -d' ' -f1 | rev)
|
||||
|
||||
GOPATH?=$(shell if test -x `which go`; then go env GOPATH; else echo "$(HOME)/go"; fi)
|
||||
GOCACHE?=$(HOME)/.cache/go-build
|
||||
@@ -45,6 +45,9 @@ ARGOCD_E2E_REPOSERVER_PORT?=8081
|
||||
ARGOCD_E2E_REDIS_PORT?=6379
|
||||
ARGOCD_E2E_DEX_PORT?=5556
|
||||
ARGOCD_E2E_YARN_HOST?=localhost
|
||||
ARGOCD_E2E_DISABLE_AUTH?=
|
||||
|
||||
ARGOCD_E2E_TEST_TIMEOUT?=30m
|
||||
|
||||
ARGOCD_IN_CI?=false
|
||||
ARGOCD_TEST_E2E?=true
|
||||
@@ -73,6 +76,10 @@ define run-in-test-server
|
||||
-e ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
|
||||
-e ARGOCD_E2E_TEST=$(ARGOCD_E2E_TEST) \
|
||||
-e ARGOCD_E2E_YARN_HOST=$(ARGOCD_E2E_YARN_HOST) \
|
||||
-e ARGOCD_E2E_DISABLE_AUTH=$(ARGOCD_E2E_DISABLE_AUTH) \
|
||||
-e ARGOCD_TLS_DATA_PATH=${ARGOCD_TLS_DATA_PATH:-/tmp/argocd-local/tls} \
|
||||
-e ARGOCD_SSH_DATA_PATH=${ARGOCD_SSH_DATA_PATH:-/tmp/argocd-local/ssh} \
|
||||
-e ARGOCD_GPG_DATA_PATH=${ARGOCD_GPG_DATA_PATH:-/tmp/argocd-local/gpg/source} \
|
||||
-v ${DOCKER_SRC_MOUNT} \
|
||||
-v ${GOPATH}/pkg/mod:/go/pkg/mod${VOLUME_MOUNT} \
|
||||
-v ${GOCACHE}:/tmp/go-build-cache${VOLUME_MOUNT} \
|
||||
@@ -81,6 +88,7 @@ define run-in-test-server
|
||||
-w ${DOCKER_WORKDIR} \
|
||||
-p ${ARGOCD_E2E_APISERVER_PORT}:8080 \
|
||||
-p 4000:4000 \
|
||||
-p 5000:5000 \
|
||||
$(TEST_TOOLS_PREFIX)$(TEST_TOOLS_IMAGE):$(TEST_TOOLS_TAG) \
|
||||
bash -c "$(1)"
|
||||
endef
|
||||
@@ -152,7 +160,7 @@ IMAGE_PREFIX=${IMAGE_NAMESPACE}/
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: cli image argocd-util
|
||||
all: cli image
|
||||
|
||||
# We have some legacy requirements for being checked out within $GOPATH.
|
||||
# The ensure-gopath target can be used as dependency to ensure we are running
|
||||
@@ -180,6 +188,16 @@ openapigen: ensure-gopath
|
||||
export GO111MODULE=off
|
||||
./hack/update-openapi.sh
|
||||
|
||||
.PHONY: notification-catalog
|
||||
notification-catalog:
|
||||
go run ./hack/gen-catalog catalog
|
||||
|
||||
.PHONY: notification-docs
|
||||
notification-docs:
|
||||
go run ./hack/gen-docs
|
||||
go run ./hack/gen-catalog docs
|
||||
|
||||
|
||||
.PHONY: clientgen
|
||||
clientgen: ensure-gopath
|
||||
export GO111MODULE=off
|
||||
@@ -189,8 +207,9 @@ clientgen: ensure-gopath
|
||||
clidocsgen: ensure-gopath
|
||||
go run tools/cmd-docs/main.go
|
||||
|
||||
|
||||
.PHONY: codegen-local
|
||||
codegen-local: ensure-gopath mod-vendor-local gogen protogen clientgen openapigen clidocsgen manifests-local
|
||||
codegen-local: ensure-gopath mod-vendor-local notification-docs notification-catalog gogen protogen clientgen openapigen clidocsgen manifests-local
|
||||
rm -rf vendor/
|
||||
|
||||
.PHONY: codegen
|
||||
@@ -203,29 +222,19 @@ cli: test-tools-image
|
||||
|
||||
.PHONY: cli-local
|
||||
cli-local: clean-debug
|
||||
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
|
||||
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
|
||||
|
||||
.PHONY: cli-argocd
|
||||
cli-argocd:
|
||||
go build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${CLI_NAME} ./cmd
|
||||
.PHONY: gen-resources-cli-local
|
||||
gen-resources-cli-local: clean-debug
|
||||
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${GEN_RESOURCES_CLI_NAME} ./hack/gen-resources/cmd
|
||||
|
||||
.PHONY: release-cli
|
||||
release-cli: clean-debug image
|
||||
docker create --name tmp-argocd-linux $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)
|
||||
docker cp tmp-argocd-linux:/usr/local/bin/argocd ${DIST_DIR}/argocd-linux-amd64
|
||||
docker cp tmp-argocd-linux:/usr/local/bin/argocd-darwin-amd64 ${DIST_DIR}/argocd-darwin-amd64
|
||||
docker cp tmp-argocd-linux:/usr/local/bin/argocd-windows-amd64.exe ${DIST_DIR}/argocd-windows-amd64.exe
|
||||
docker rm tmp-argocd-linux
|
||||
|
||||
.PHONY: argocd-util
|
||||
argocd-util: clean-debug
|
||||
# Build argocd-util as a statically linked binary, so it could run within the alpine-based dex container (argoproj/argo-cd#844)
|
||||
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${UTIL_CLI_NAME} ./cmd
|
||||
|
||||
# .PHONY: dev-tools-image
|
||||
# dev-tools-image:
|
||||
# docker build -t $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE) . -f hack/Dockerfile.dev-tools
|
||||
# docker tag $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE) $(DEV_TOOLS_PREFIX)$(DEV_TOOLS_IMAGE):$(DEV_TOOLS_VERSION)
|
||||
release-cli: clean-debug
|
||||
make BIN_NAME=argocd-darwin-amd64 GOOS=darwin argocd-all
|
||||
make BIN_NAME=argocd-darwin-arm64 GOOS=darwin GOARCH=arm64 argocd-all
|
||||
make BIN_NAME=argocd-linux-amd64 GOOS=linux argocd-all
|
||||
make BIN_NAME=argocd-linux-arm64 GOOS=linux GOARCH=arm64 argocd-all
|
||||
make BIN_NAME=argocd-windows-amd64.exe GOOS=windows argocd-all
|
||||
|
||||
.PHONY: test-tools-image
|
||||
test-tools-image:
|
||||
@@ -243,25 +252,19 @@ manifests: test-tools-image
|
||||
# consolidated binary for cli, util, server, repo-server, controller
|
||||
.PHONY: argocd-all
|
||||
argocd-all: clean-debug
|
||||
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
|
||||
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/${BIN_NAME} ./cmd
|
||||
|
||||
# NOTE: we use packr to do the build instead of go, since we embed swagger files and policy.csv
|
||||
# files into the go binary
|
||||
.PHONY: server
|
||||
server: clean-debug
|
||||
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
|
||||
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-server ./cmd
|
||||
|
||||
.PHONY: repo-server
|
||||
repo-server:
|
||||
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
|
||||
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-repo-server ./cmd
|
||||
|
||||
.PHONY: controller
|
||||
controller:
|
||||
CGO_ENABLED=0 ${PACKR_CMD} build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
|
||||
|
||||
.PHONY: packr
|
||||
packr:
|
||||
go build -o ${DIST_DIR}/packr github.com/gobuffalo/packr/packr/
|
||||
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
|
||||
|
||||
.PHONY: image
|
||||
ifeq ($(DEV_IMAGE), true)
|
||||
@@ -269,19 +272,17 @@ ifeq ($(DEV_IMAGE), true)
|
||||
# which speeds up builds. Dockerfile.dev needs to be copied into dist to perform the build, since
|
||||
# the dist directory is under .dockerignore.
|
||||
IMAGE_TAG="dev-$(shell git describe --always --dirty)"
|
||||
image: packr
|
||||
image:
|
||||
docker build -t argocd-base --target argocd-base .
|
||||
docker build -t argocd-ui --target argocd-ui .
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 dist/packr build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 dist/packr build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-darwin-amd64 ./cmd
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 dist/packr build -v -i -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-windows-amd64.exe ./cmd
|
||||
find ./ui/dist -type f -not -name gitkeep -delete
|
||||
docker run -v ${CURRENT_DIR}/ui/dist/app:/tmp/app --rm -t argocd-ui sh -c 'cp -r ./dist/app/* /tmp/app/'
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd ./cmd
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-application-controller
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-repo-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-cmp-server
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-dex
|
||||
ln -sfn ${DIST_DIR}/argocd ${DIST_DIR}/argocd-util
|
||||
ln -sfn ${DIST_DIR}/argocd-darwin-amd64 ${DIST_DIR}/argocd-util-darwin-amd64
|
||||
ln -sfn ${DIST_DIR}/argocd-windows-amd64.exe ${DIST_DIR}/argocd-util-windows-amd64.exe
|
||||
cp Dockerfile.dev dist
|
||||
docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) -f dist/Dockerfile.dev dist
|
||||
else
|
||||
@@ -291,10 +292,8 @@ endif
|
||||
@if [ "$(DOCKER_PUSH)" = "true" ] ; then docker push $(IMAGE_PREFIX)argocd:$(IMAGE_TAG) ; fi
|
||||
|
||||
.PHONY: armimage
|
||||
# The "BUILD_ALL_CLIS" argument is to skip building the CLIs for darwin and windows
|
||||
# which would take a really long time.
|
||||
armimage:
|
||||
docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm . --build-arg BUILD_ALL_CLIS="false"
|
||||
docker build -t $(IMAGE_PREFIX)argocd:$(IMAGE_TAG)-arm .
|
||||
|
||||
.PHONY: builder-image
|
||||
builder-image:
|
||||
@@ -307,7 +306,7 @@ mod-download: test-tools-image
|
||||
|
||||
.PHONY: mod-download-local
|
||||
mod-download-local:
|
||||
go mod download
|
||||
go mod download && go mod tidy # go mod download changes go.sum https://github.com/golang/go/issues/42970
|
||||
|
||||
.PHONY: mod-vendor
|
||||
mod-vendor: test-tools-image
|
||||
@@ -397,7 +396,7 @@ test-e2e:
|
||||
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
|
||||
export GO111MODULE=off
|
||||
ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout 20m -v ./test/e2e
|
||||
ARGOCD_GPG_ENABLED=true NO_PROXY=* ./hack/test.sh -timeout $(ARGOCD_E2E_TEST_TIMEOUT) -v ./test/e2e
|
||||
|
||||
# Spawns a shell in the test server container for debugging purposes
|
||||
debug-test-server: test-tools-image
|
||||
@@ -424,19 +423,21 @@ start-e2e-local:
|
||||
if test -d /tmp/argo-e2e/app/config/gpg; then rm -rf /tmp/argo-e2e/app/config/gpg/*; fi
|
||||
mkdir -p /tmp/argo-e2e/app/config/gpg/keys && chmod 0700 /tmp/argo-e2e/app/config/gpg/keys
|
||||
mkdir -p /tmp/argo-e2e/app/config/gpg/source && chmod 0700 /tmp/argo-e2e/app/config/gpg/source
|
||||
mkdir -p /tmp/argo-e2e/app/config/plugin && chmod 0700 /tmp/argo-e2e/app/config/plugin
|
||||
# set paths for locally managed ssh known hosts and tls certs data
|
||||
ARGOCD_SSH_DATA_PATH=/tmp/argo-e2e/app/config/ssh \
|
||||
ARGOCD_TLS_DATA_PATH=/tmp/argo-e2e/app/config/tls \
|
||||
ARGOCD_GPG_DATA_PATH=/tmp/argo-e2e/app/config/gpg/source \
|
||||
ARGOCD_GNUPGHOME=/tmp/argo-e2e/app/config/gpg/keys \
|
||||
ARGOCD_GPG_ENABLED=true \
|
||||
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
|
||||
ARGOCD_PLUGINCONFIGFILEPATH=/tmp/argo-e2e/app/config/plugin \
|
||||
ARGOCD_E2E_DISABLE_AUTH=false \
|
||||
ARGOCD_ZJWT_FEATURE_FLAG=always \
|
||||
ARGOCD_IN_CI=$(ARGOCD_IN_CI) \
|
||||
ARGOCD_E2E_TEST=true \
|
||||
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
||||
|
||||
# Cleans VSCode debug.test files from sub-dirs to prevent them from being included in packr boxes
|
||||
# Cleans VSCode debug.test files from sub-dirs to prevent them from being included in by golang embed
|
||||
.PHONY: clean-debug
|
||||
clean-debug:
|
||||
-find ${CURRENT_DIR} -name debug.test | xargs rm -f
|
||||
@@ -462,10 +463,16 @@ start-local: mod-vendor-local dep-ui-local
|
||||
mkdir -p /tmp/argocd-local/gpg/source
|
||||
ARGOCD_ZJWT_FEATURE_FLAG=always \
|
||||
ARGOCD_IN_CI=false \
|
||||
ARGOCD_GPG_ENABLED=true \
|
||||
ARGOCD_GPG_ENABLED=$(ARGOCD_GPG_ENABLED) \
|
||||
ARGOCD_E2E_TEST=false \
|
||||
goreman -f $(ARGOCD_PROCFILE) start ${ARGOCD_START}
|
||||
|
||||
# Run goreman start with exclude option , provide exclude env variable with list of services
|
||||
.PHONY: run
|
||||
run:
|
||||
bash ./hack/goreman-start.sh
|
||||
|
||||
|
||||
# Runs pre-commit validation with the virtualized toolchain
|
||||
.PHONY: pre-commit
|
||||
pre-commit: codegen build lint test
|
||||
@@ -502,7 +509,7 @@ serve-docs:
|
||||
.PHONY: lint-docs
|
||||
lint-docs:
|
||||
# https://github.com/dkhamsing/awesome_bot
|
||||
find docs -name '*.md' -exec grep -l http {} + | xargs docker run --rm -v $(PWD):/mnt:ro dkhamsing/awesome_bot -t 3 --allow-dupe --allow-redirect --white-list `cat white-list | grep -v "#" | tr "\n" ','` --skip-save-results --
|
||||
find docs -name '*.md' -exec grep -l http {} + | xargs docker run --rm -v $(PWD):/mnt:ro dkhamsing/awesome_bot -t 3 --allow-dupe --allow-redirect --allow-timeout --allow-ssl --allow 502,500,429,400 --white-list `cat docs/url-allow-list | grep -v "#" | tr "\n" ','` --skip-save-results --
|
||||
|
||||
# Verify that kubectl can connect to your K8s cluster from Docker
|
||||
.PHONY: verify-kube-connect
|
||||
@@ -524,16 +531,15 @@ install-tools-local: install-test-tools-local install-codegen-tools-local instal
|
||||
# Installs all tools required for running unit & end-to-end tests (Linux packages)
|
||||
.PHONY: install-test-tools-local
|
||||
install-test-tools-local:
|
||||
sudo ./hack/install.sh packr-linux
|
||||
sudo ./hack/install.sh kustomize-linux
|
||||
sudo ./hack/install.sh ksonnet-linux
|
||||
sudo ./hack/install.sh helm2-linux
|
||||
sudo ./hack/install.sh helm-linux
|
||||
./hack/install.sh kustomize-linux
|
||||
./hack/install.sh ksonnet-linux
|
||||
./hack/install.sh helm2-linux
|
||||
./hack/install.sh helm-linux
|
||||
|
||||
# Installs all tools required for running codegen (Linux packages)
|
||||
.PHONY: install-codegen-tools-local
|
||||
install-codegen-tools-local:
|
||||
sudo ./hack/install.sh codegen-tools
|
||||
./hack/install.sh codegen-tools
|
||||
|
||||
# Installs all tools required for running codegen (Go packages)
|
||||
.PHONY: install-go-tools-local
|
||||
@@ -546,3 +552,10 @@ dep-ui: test-tools-image
|
||||
|
||||
dep-ui-local:
|
||||
cd ui && yarn install
|
||||
|
||||
start-test-k8s:
|
||||
go run ./hack/k8s
|
||||
|
||||
.PHONY: list
|
||||
list:
|
||||
@LC_ALL=C $(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | egrep -v -e '^[^[:alnum:]]' -e '^$@$$'
|
||||
|
||||
18
OWNERS
18
OWNERS
@@ -5,13 +5,23 @@ owners:
|
||||
approvers:
|
||||
- alexec
|
||||
- alexmt
|
||||
- dthomson25
|
||||
- jannfis
|
||||
- jessesuen
|
||||
- jgwest
|
||||
- mayzhang2000
|
||||
- rachelwang20
|
||||
- rbreeze
|
||||
|
||||
reviewers:
|
||||
- jgwest
|
||||
- wtam2018
|
||||
- dthomson25
|
||||
- tetchel
|
||||
- wtam2018
|
||||
- ishitasequeira
|
||||
- reginapizza
|
||||
- hblixt
|
||||
- chetan-rns
|
||||
- wanghong230
|
||||
- pasha-codefresh
|
||||
- ciiay
|
||||
- leoluz
|
||||
- crenshaw-dev
|
||||
- saumeya
|
||||
|
||||
11
Procfile
11
Procfile
@@ -1,8 +1,9 @@
|
||||
controller: sh -c "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 go run ./cmd/main.go --loglevel debug --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379} --repo-server localhost:${ARGOCD_E2E_REPOSERVER_PORT:-8081}"
|
||||
api-server: sh -c "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 go run ./cmd/main.go --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} --staticassets ui/dist/app"
|
||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/cmd gendexcfg -o `pwd`/dist/dex.yaml && 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:v2.27.0 serve /dex.yaml"
|
||||
redis: docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:5.0.10-alpine --save "" --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}
|
||||
repo-server: sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} 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 go run ./cmd/main.go --loglevel debug --port ${ARGOCD_E2E_REPOSERVER_PORT:-8081} --redis localhost:${ARGOCD_E2E_REDIS_PORT:-6379}"
|
||||
controller: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "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}"
|
||||
api-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "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} "
|
||||
dex: sh -c "ARGOCD_BINARY_NAME=argocd-dex go run github.com/argoproj/argo-cd/v2/cmd gendexcfg -o `pwd`/dist/dex.yaml && 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:v2.30.2 dex serve /dex.yaml"
|
||||
redis: bash -c "if [ \"$ARGOCD_REDIS_LOCAL\" == 'true' ]; then redis-server --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; else docker run --rm --name argocd-redis -i -p ${ARGOCD_E2E_REDIS_PORT:-6379}:${ARGOCD_E2E_REDIS_PORT:-6379} redis:6.2.6-alpine --save '' --appendonly no --port ${ARGOCD_E2E_REDIS_PORT:-6379}; fi"
|
||||
repo-server: [ "$BIN_MODE" == 'true' ] && COMMAND=./dist/argocd || COMMAND='go run ./cmd/main.go' && sh -c "FORCE_LOG_COLORS=1 ARGOCD_FAKE_IN_CLUSTER=true ARGOCD_GNUPGHOME=${ARGOCD_GNUPGHOME:-/tmp/argocd-local/gpg/keys} ARGOCD_PLUGINSOCKFILEPATH=${ARGOCD_PLUGINSOCKFILEPATH:-/tmp/argo-e2e/app/config/plugin} 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}"
|
||||
ui: sh -c 'cd ui && ${ARGOCD_E2E_YARN_CMD:-yarn} start'
|
||||
git-server: test/fixture/testrepos/start-git.sh
|
||||
helm-registry: test/fixture/testrepos/start-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}
|
||||
|
||||
34
README.md
34
README.md
@@ -28,26 +28,48 @@ Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.
|
||||
To learn more about Argo CD [go to the complete documentation](https://argo-cd.readthedocs.io/).
|
||||
Check live demo at https://cd.apps.argoproj.io/.
|
||||
|
||||
## Community Blogs and Presentations
|
||||
## Community
|
||||
|
||||
### Contribution, Discussion and Support
|
||||
|
||||
You can reach the Argo CD community and developers via the following channels:
|
||||
|
||||
* Q & A : [Github Discussions](https://github.com/argoproj/argo-cd/discussions)
|
||||
* Chat : [The #argo-cd Slack channel](https://argoproj.github.io/community/join-slack)
|
||||
* Contributors Office Hours: [Every Thursday](https://calendar.google.com/calendar/u/0/embed?src=argoproj@gmail.com) | [Agenda](https://docs.google.com/document/d/1ttgw98MO45Dq7ZUHpIiOIEfbyeitKHNfMjbY5dLLMKQ)
|
||||
* User Community meeting: [Every other Wednesday](https://calendar.google.com/calendar/u/0/embed?src=argoproj@gmail.com) | [Agenda](https://docs.google.com/document/d/1xkoFkVviB70YBzSEa4bDnu-rUZ1sIFtwKKG1Uw8XsY8)
|
||||
|
||||
|
||||
Participation in the Argo CD project is governed by the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md)
|
||||
|
||||
|
||||
### Blogs and Presentations
|
||||
|
||||
1. [Awesome-Argo: A Curated List of Awesome Projects and Resources Related to Argo](https://github.com/terrytangyuan/awesome-argo)
|
||||
1. [Unveil the Secret Ingredients of Continuous Delivery at Enterprise Scale with Argo CD](https://blog.akuity.io/unveil-the-secret-ingredients-of-continuous-delivery-at-enterprise-scale-with-argo-cd-7c5b4057ee49)
|
||||
1. [GitOps Without Pipelines With ArgoCD Image Updater](https://youtu.be/avPUQin9kzU)
|
||||
1. [Combining Argo CD (GitOps), Crossplane (Control Plane), And KubeVela (OAM)](https://youtu.be/eEcgn_gU3SM)
|
||||
1. [How to Apply GitOps to Everything - Combining Argo CD and Crossplane](https://youtu.be/yrj4lmScKHQ)
|
||||
1. [Couchbase - How To Run a Database Cluster in Kubernetes Using Argo CD](https://youtu.be/nkPoPaVzExY)
|
||||
1. [Automation of Everything - How To Combine Argo Events, Workflows & Pipelines, CD, and Rollouts](https://youtu.be/XNXJtxkUKeY)
|
||||
1. [Environments Based On Pull Requests (PRs): Using Argo CD To Apply GitOps Principles On Previews](https://youtu.be/cpAaI8p4R60)
|
||||
1. [Argo CD: Applying GitOps Principles To Manage Production Environment In Kubernetes](https://youtu.be/vpWQeoaiRM4)
|
||||
1. [Creating Temporary Preview Environments Based On Pull Requests With Argo CD And Codefresh](https://codefresh.io/continuous-deployment/creating-temporary-preview-environments-based-pull-requests-argo-cd-codefresh/)
|
||||
1. [Tutorial: Everything You Need To Become A GitOps Ninja](https://www.youtube.com/watch?v=r50tRQjisxw) 90m tutorial on GitOps and Argo CD.
|
||||
1. [Comparison of Argo CD, Spinnaker, Jenkins X, and Tekton](https://www.inovex.de/blog/spinnaker-vs-argo-cd-vs-tekton-vs-jenkins-x/)
|
||||
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager 3.1.2](https://medium.com/ibm-cloud/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2-4395af317359)
|
||||
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager 3.1.2](https://www.ibm.com/cloud/blog/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2)
|
||||
1. [GitOps for Kubeflow using Argo CD](https://v0-6.kubeflow.org/docs/use-cases/gitops-for-kubeflow/)
|
||||
1. [GitOps Toolsets on Kubernetes with CircleCI and Argo CD](https://www.digitalocean.com/community/tutorials/webinar-series-gitops-tool-sets-on-kubernetes-with-circleci-and-argo-cd)
|
||||
1. [Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager](https://www.ibm.com/blogs/bluemix/2019/02/simplify-and-automate-deployments-using-gitops-with-ibm-multicloud-manager-3-1-2/)
|
||||
1. [CI/CD in Light Speed with K8s and Argo CD](https://www.youtube.com/watch?v=OdzH82VpMwI&feature=youtu.be)
|
||||
1. [Machine Learning as Code](https://www.youtube.com/watch?v=VXrGp5er1ZE&t=0s&index=135&list=PLj6h78yzYM2PZf9eA7bhWnIh_mK1vyOfU). Among other things, describes how Kubeflow uses Argo CD to implement GitOPs for ML
|
||||
1. [Argo CD - GitOps Continuous Delivery for Kubernetes](https://www.youtube.com/watch?v=aWDIQMbp1cc&feature=youtu.be&t=1m4s)
|
||||
1. [Introduction to Argo CD : Kubernetes DevOps CI/CD](https://www.youtube.com/watch?v=2WSJF7d8dUg&feature=youtu.be)
|
||||
1. [GitOps Deployment and Kubernetes - using ArgoCD](https://medium.com/riskified-technology/gitops-deployment-and-kubernetes-f1ab289efa4b)
|
||||
1. [GitOps Deployment and Kubernetes - using Argo CD](https://medium.com/riskified-technology/gitops-deployment-and-kubernetes-f1ab289efa4b)
|
||||
1. [Deploy Argo CD with Ingress and TLS in Three Steps: No YAML Yak Shaving Required](https://itnext.io/deploy-argo-cd-with-ingress-and-tls-in-three-steps-no-yaml-yak-shaving-required-bc536d401491)
|
||||
1. [GitOps Continuous Delivery with Argo and Codefresh](https://codefresh.io/events/cncf-member-webinar-gitops-continuous-delivery-argo-codefresh/)
|
||||
1. [Stay up to date with ArgoCD and Renovate](https://mjpitz.com/blog/2020/12/03/renovate-your-gitops/)
|
||||
1. [Stay up to date with Argo CD and Renovate](https://mjpitz.com/blog/2020/12/03/renovate-your-gitops/)
|
||||
1. [Setting up Argo CD with Helm](https://www.arthurkoziel.com/setting-up-argocd-with-helm/)
|
||||
1. [Applied GitOps with ArgoCD](https://thenewstack.io/applied-gitops-with-argocd/)
|
||||
1. [Applied GitOps with Argo CD](https://thenewstack.io/applied-gitops-with-argocd/)
|
||||
1. [Solving configuration drift using GitOps with Argo CD](https://www.cncf.io/blog/2020/12/17/solving-configuration-drift-using-gitops-with-argo-cd/)
|
||||
1. [Decentralized GitOps over environments](https://blogs.sap.com/2021/05/06/decentralized-gitops-over-environments/)
|
||||
1. [How GitOps and Operators mark the rise of Infrastructure-As-Software](https://paytmlabs.com/blog/2021/10/how-to-improve-operational-work-with-operators-and-gitops/)
|
||||
|
||||
47
SECURITY.md
47
SECURITY.md
@@ -1,6 +1,6 @@
|
||||
# Security Policy for Argo CD
|
||||
|
||||
Version: **v1.0 (2020-02-26)**
|
||||
Version: **v1.4 (2022-01-23)**
|
||||
|
||||
## Preface
|
||||
|
||||
@@ -8,6 +8,31 @@ As a deployment tool, Argo CD needs to have production access which makes
|
||||
security a very important topic. The Argoproj team takes security very
|
||||
seriously and is continuously working on improving it.
|
||||
|
||||
## A word about security scanners
|
||||
|
||||
Many organisations these days employ security scanners to validate their
|
||||
container images before letting them on their clusters, and that is a good
|
||||
thing. However, the quality and results of these scanners vary greatly,
|
||||
many of them produce false positives and require people to look at the
|
||||
issues reported and validate them for correctness. A great example of that
|
||||
is, that some scanners report kernel vulnerabilities for container images
|
||||
just because they are derived from some distribution.
|
||||
|
||||
We kindly ask you to not raise issues or contact us regarding any issues
|
||||
that are found by your security scanner. Many of those produce a lot of false
|
||||
positives, and many of these issues don't affect Argo CD. We do have scanners
|
||||
in place for our code, dependencies and container images that we publish. We
|
||||
are well aware of the issues that may affect Argo CD and are constantly
|
||||
working on the remediation of those that affect Argo CD and our users.
|
||||
|
||||
If you believe that we might have missed an issue that we should take a look
|
||||
at (that can happen), then please discuss it with us. If there is a CVE
|
||||
assigned to the issue, please do open an issue on our GitHub tracker instead
|
||||
of writing to the security contact e-mail, since things reported by scanners
|
||||
are public already and the discussion that might emerge is of benefit to the
|
||||
general community. However, please validate your scanner results and its
|
||||
impact on Argo CD before opening an issue at least roughly.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We currently support the most recent release (`N`, e.g. `1.8`) and the release
|
||||
@@ -35,13 +60,17 @@ We will do our best to react quickly on your inquiry, and to coordinate a fix
|
||||
and disclosure with you. Sometimes, it might take a little longer for us to
|
||||
react (e.g. out of office conditions), so please bear with us in these cases.
|
||||
|
||||
We will publish security advisiories using the Git Hub SA feature to keep our
|
||||
community well informed, and will credit you for your findings (unless you
|
||||
prefer to stay anonymous, of course).
|
||||
We will publish security advisiories using the
|
||||
[Git Hub Security Advisories](https://github.com/argoproj/argo-cd/security/advisories)
|
||||
feature to keep our community well informed, and will credit you for your
|
||||
findings (unless you prefer to stay anonymous, of course).
|
||||
|
||||
Please report vulnerabilities by e-mail to all of the following people:
|
||||
Please report vulnerabilities by e-mail to the following address:
|
||||
|
||||
* jfischer@redhat.com
|
||||
* Jesse_Suen@intuit.com
|
||||
* Alexander_Matyushentsev@intuit.com
|
||||
* Edward_Lee@intuit.com
|
||||
* cncf-argo-security@lists.cncf.io
|
||||
|
||||
## Securing your Argo CD Instance
|
||||
|
||||
See the [operator manual security page](docs/operator-manual/security.md) for
|
||||
additional information about Argo CD's security features and how to make your
|
||||
Argo CD production ready.
|
||||
|
||||
79
USERS.md
79
USERS.md
@@ -8,25 +8,40 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [3Rein](https://www.3rein.com/)
|
||||
1. [7shifts](https://www.7shifts.com/)
|
||||
1. [Adevinta](https://www.adevinta.com/)
|
||||
1. [Adventure](https://jp.adventurekk.com/)
|
||||
1. [Akuity](https://akuity.io/)
|
||||
1. [Alibaba Group](https://www.alibabagroup.com/)
|
||||
1. [Allianz Direct](https://www.allianzdirect.de/)
|
||||
1. [Ambassador Labs](https://www.getambassador.io/)
|
||||
1. [Ant Group](https://www.antgroup.com/)
|
||||
1. [ANSTO - Australian Synchrotron](https://www.synchrotron.org.au/)
|
||||
1. [AppDirect](https://www.appdirect.com)
|
||||
1. [Arctiq Inc.](https://www.arctiq.ca)
|
||||
1. [ARZ Allgemeines Rechenzentrum GmbH ](https://www.arz.at/)
|
||||
1. [Axual B.V.](https://axual.com)
|
||||
1. [Baloise](https://www.baloise.com)
|
||||
1. [BCDevExchange DevOps Platform](https://bcdevexchange.org/DevOpsPlatform)
|
||||
1. [Beat](https://thebeat.co/en/)
|
||||
1. [Beez Innovation Labs](https://www.beezlabs.com/)
|
||||
1. [BioBox Analytics](https://biobox.io)
|
||||
1. [BigPanda](https://bigpanda.io)
|
||||
1. [BMW Group](https://www.bmwgroup.com/)
|
||||
1. [Camptocamp](https://camptocamp.com)
|
||||
1. [Capital One](https://www.capitalone.com)
|
||||
1. [CARFAX](https://www.carfax.com)
|
||||
1. [Celonis](https://www.celonis.com/)
|
||||
1. [Chime](https://www.chime.com)
|
||||
1. [Cisco ET&I](https://eti.cisco.com/)
|
||||
1. [Codefresh](https://www.codefresh.io/)
|
||||
1. [Codility](https://www.codility.com/)
|
||||
1. [Commonbond](https://commonbond.co/)
|
||||
1. [Crédit Agricole CIB](https://www.ca-cib.com)
|
||||
1. [CROZ d.o.o.](https://croz.net/)
|
||||
1. [CyberAgent](https://www.cyberagent.co.jp/en/)
|
||||
1. [Cybozu](https://cybozu-global.com)
|
||||
1. [Chargetrip](https://chargetrip.com)
|
||||
1. [D2iQ](https://www.d2iq.com)
|
||||
1. [Deloitte](https://www.deloitte.com/)
|
||||
1. [Devtron Labs](https://github.com/devtron-labs/devtron)
|
||||
1. [EDF Renewables](https://www.edf-re.com/)
|
||||
1. [edX](https://edx.org)
|
||||
@@ -35,17 +50,34 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [END.](https://www.endclothing.com/)
|
||||
1. [Energisme](https://energisme.com/)
|
||||
1. [Fave](https://myfave.com)
|
||||
1. [Flip](https://flip.id)
|
||||
1. [Fonoa](https://www.fonoa.com/)
|
||||
1. [Future PLC](https://www.futureplc.com/)
|
||||
1. [Garner](https://www.garnercorp.com)
|
||||
1. [G DATA CyberDefense AG](https://www.gdata-software.com/)
|
||||
1. [Generali Deutschland AG](https://www.generali.de/)
|
||||
1. [Gitpod](https://www.gitpod.io)
|
||||
1. [Glovo](https://www.glovoapp.com)
|
||||
1. [Gllue](https://gllue.com)
|
||||
1. [GMETRI](https://gmetri.com/)
|
||||
1. [Gojek](https://www.gojek.io/)
|
||||
1. [Greenpass](https://www.greenpass.com.br/)
|
||||
1. [Handelsbanken](https://www.handelsbanken.se)
|
||||
1. [Healy](https://www.healyworld.net)
|
||||
1. [Helio](https://helio.exchange)
|
||||
1. [hipages](https://hipages.com.au/)
|
||||
1. [Hiya](https://hiya.com)
|
||||
1. [Honestbank](https://honestbank.com)
|
||||
1. [IBM](https://www.ibm.com/)
|
||||
1. [Ibotta](https://home.ibotta.com)
|
||||
1. [IITS-Consulting](https://iits-consulting.de)
|
||||
1. [Index Exchange](https://www.indexexchange.com/)
|
||||
1. [InsideBoard](https://www.insideboard.com)
|
||||
1. [Intuit](https://www.intuit.com/)
|
||||
1. [Joblift](https://joblift.com/)
|
||||
1. [JovianX](https://www.jovianx.com/)
|
||||
1. [Karrot](https://www.daangn.com/)
|
||||
1. [KarrotPay](https://www.daangnpay.com/)
|
||||
1. [Kasa](https://kasa.co.kr/)
|
||||
1. [Keptn](https://keptn.sh)
|
||||
1. [Kinguin](https://www.kinguin.net/)
|
||||
@@ -56,17 +88,22 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Lytt](https://www.lytt.co/)
|
||||
1. [Major League Baseball](https://mlb.com)
|
||||
1. [Mambu](https://www.mambu.com/)
|
||||
1. [Mattermost](https://www.mattermost.com)
|
||||
1. [Max Kelsen](https://www.maxkelsen.com/)
|
||||
1. [MindSpore](https://mindspore.cn)
|
||||
1. [Mirantis](https://mirantis.com/)
|
||||
1. [mixi Group](https://mixi.co.jp/)
|
||||
1. [Moengage](https://www.moengage.com/)
|
||||
1. [Money Forward](https://corp.moneyforward.com/en/)
|
||||
1. [MOO Print](https://www.moo.com/)
|
||||
1. [MTN Group](https://www.mtn.com/)
|
||||
1. [Natura &Co](https://naturaeco.com/)
|
||||
1. [New Relic](https://newrelic.com/)
|
||||
1. [Nextdoor](https://nextdoor.com/)
|
||||
1. [Nikkei](https://www.nikkei.co.jp/nikkeiinfo/en/)
|
||||
1. [Nitro](https://gonitro.com)
|
||||
1. [Octadesk](https://octadesk.com)
|
||||
1. [omegaUp](https://omegaUp.com)
|
||||
1. [openEuler](https://openeuler.org)
|
||||
1. [openGauss](https://opengauss.org/)
|
||||
1. [openLooKeng](https://openlookeng.io)
|
||||
@@ -74,9 +111,11 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Opensurvey](https://www.opensurvey.co.kr/)
|
||||
1. [Optoro](https://www.optoro.com/)
|
||||
1. [Orbital Insight](https://orbitalinsight.com/)
|
||||
1. [Packlink](https://www.packlink.com/)
|
||||
1. [PayPay](https://paypay.ne.jp/)
|
||||
1. [Peloton Interactive](https://www.onepeloton.com/)
|
||||
1. [Pipefy](https://www.pipefy.com/)
|
||||
1. [Polarpoint.io](https://polarpoint.io)
|
||||
1. [Preferred Networks](https://preferred.jp/en/)
|
||||
1. [Prudential](https://prudential.com.sg)
|
||||
1. [PUBG](https://www.pubg.com)
|
||||
@@ -89,9 +128,14 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Robotinfra](https://www.robotinfra.com)
|
||||
1. [Saildrone](https://www.saildrone.com/)
|
||||
1. [Saloodo! GmbH](https://www.saloodo.com)
|
||||
1. [Schwarz IT](https://jobs.schwarz/it-mission)
|
||||
1. [Skit](https://skit.ai/)
|
||||
1. [Snyk](https://snyk.io/)
|
||||
1. [Speee](https://speee.jp/)
|
||||
1. [Spendesk](https://spendesk.com/)
|
||||
1. [Sumo Logic](https://sumologic.com/)
|
||||
1. [Sutpc](http://www.sutpc.com/)
|
||||
1. [Swiss Post](https://github.com/swisspost)
|
||||
1. [Swisscom](https://www.swisscom.ch)
|
||||
1. [Swissquote](https://github.com/swissquote)
|
||||
1. [Syncier](https://syncier.com/)
|
||||
@@ -101,10 +145,12 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [ThousandEyes](https://www.thousandeyes.com/)
|
||||
1. [Ticketmaster](https://ticketmaster.com)
|
||||
1. [Tiger Analytics](https://www.tigeranalytics.com/)
|
||||
1. [Tigera](https://www.tigera.io/)
|
||||
1. [Toss](https://toss.im/en)
|
||||
1. [tru.ID](https://tru.id)
|
||||
1. [Twilio SendGrid](https://sendgrid.com)
|
||||
1. [tZERO](https://www.tzero.com/)
|
||||
1. [ungleich.ch](https://ungleich.ch/)
|
||||
1. [UBIO](https://ub.io/)
|
||||
1. [UFirstGroup](https://www.ufirstgroup.com/en/)
|
||||
1. [Universidad Mesoamericana](https://www.umes.edu.gt/)
|
||||
@@ -114,7 +160,38 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
1. [Volvo Cars](https://www.volvocars.com/)
|
||||
1. [VSHN - The DevOps Company](https://vshn.ch/)
|
||||
1. [Walkbase](https://www.walkbase.com/)
|
||||
1. [Wehkamp](https://www.wehkamp.nl/)
|
||||
1. [WeMo Scooter](https://www.wemoscooter.com/)
|
||||
1. [Webstores](https://www.webstores.nl)
|
||||
1. [Whitehat Berlin](https://whitehat.berlin) by Guido Maria Serra +Fenaroli
|
||||
1. [Witick](https://witick.io/)
|
||||
1. [WooliesX](https://wooliesx.com.au/)
|
||||
1. [Woolworths Group](https://www.woolworthsgroup.com.au/)
|
||||
1. [WSpot](https://www.wspot.com.br/)
|
||||
1. [Yieldlab](https://www.yieldlab.de/)
|
||||
1. [Sap Labs] (http://sap.com)
|
||||
1. [Zimpler](https://www.zimpler.com/)
|
||||
1. [Sap Labs](http://sap.com)
|
||||
1. [Smilee.io](https://smilee.io)
|
||||
1. [Metanet](http://www.metanet.co.kr/en/)
|
||||
1. [Unifonic Inc](https://www.unifonic.com/)
|
||||
1. [Tamkeen Technologies](https://tamkeentech.sa/)
|
||||
1. [Kaltura](https://corp.kaltura.com/)
|
||||
1. [Boticario](https://www.boticario.com.br/)
|
||||
1. [Beleza Na Web](https://www.belezanaweb.com.br/)
|
||||
1. [MariaDB](https://mariadb.com)
|
||||
1. [Lightricks](https://www.lightricks.com/)
|
||||
1. [RightRev](https://rightrev.com/)
|
||||
1. [MeDirect](https://medirect.com.mt/)
|
||||
1. [Snapp](https://snapp.ir/)
|
||||
1. [Technacy](https://www.technacy.it/)
|
||||
1. [freee](https://corp.freee.co.jp/en/company/)
|
||||
1. [Youverify](https://youverify.co/)
|
||||
1. [Keeeb](https://www.keeeb.com/)
|
||||
1. [p3r](https://www.p3r.one/)
|
||||
1. [Faro](https://www.faro.com/)
|
||||
1. [Rise](https://www.risecard.eu/)
|
||||
1. [Devopsi - Poland Software/DevOps Consulting](https://devopsi.pl/)
|
||||
1. [Skyscanner](https://www.skyscanner.net/)
|
||||
1. [Casavo](https://casavo.com)
|
||||
1. [Majid Al Futtaim](https://www.majidalfuttaim.com/)
|
||||
1. [ZOZO](https://corp.zozo.com/)
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 26 KiB |
7
assets/embed.go
Normal file
7
assets/embed.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package assets
|
||||
|
||||
import "embed"
|
||||
|
||||
// Embedded contains embedded assets
|
||||
//go:embed *
|
||||
var Embedded embed.FS
|
||||
@@ -11,4 +11,4 @@ g = _, _
|
||||
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
|
||||
|
||||
[matchers]
|
||||
m = g(r.sub, p.sub) && globMatch(r.res, p.res) && globMatch(r.act, p.act) && globMatch(r.obj, p.obj)
|
||||
m = g(r.sub, p.sub) && globOrRegexMatch(r.res, p.res) && globOrRegexMatch(r.act, p.act) && globOrRegexMatch(r.obj, p.obj)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
@@ -12,20 +13,21 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller"
|
||||
"github.com/argoproj/argo-cd/controller/sharding"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
cacheutil "github.com/argoproj/argo-cd/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/env"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
kubeutil "github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/controller"
|
||||
"github.com/argoproj/argo-cd/v2/controller/sharding"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/tls"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -47,9 +49,12 @@ func NewCommand() *cobra.Command {
|
||||
glogLevel int
|
||||
metricsPort int
|
||||
metricsCacheExpiration time.Duration
|
||||
metricsAplicationLabels []string
|
||||
kubectlParallelismLimit int64
|
||||
cacheSrc func() (*appstatecache.Cache, error)
|
||||
redisClient *redis.Client
|
||||
repoServerPlaintext bool
|
||||
repoServerStrictTLS bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -71,8 +76,34 @@ func NewCommand() *cobra.Command {
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
resyncDuration := time.Duration(appResyncPeriod) * time.Second
|
||||
repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
|
||||
var resyncDuration time.Duration
|
||||
if appResyncPeriod == 0 {
|
||||
// Re-sync should be disabled if period is 0. Set duration to a very long duration
|
||||
resyncDuration = time.Hour * 24 * 365 * 100
|
||||
} else {
|
||||
resyncDuration = time.Duration(appResyncPeriod) * time.Second
|
||||
}
|
||||
|
||||
tlsConfig := apiclient.TLSConfiguration{
|
||||
DisableTLS: repoServerPlaintext,
|
||||
StrictValidation: repoServerStrictTLS,
|
||||
}
|
||||
|
||||
// Load CA information to use for validating connections to the
|
||||
// repository server, if strict TLS validation was requested.
|
||||
if !repoServerPlaintext && repoServerStrictTLS {
|
||||
pool, err := tls.LoadX509CertPool(
|
||||
fmt.Sprintf("%s/controller/tls/tls.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
|
||||
fmt.Sprintf("%s/controller/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
tlsConfig.Certificates = pool
|
||||
}
|
||||
|
||||
repoClientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds, tlsConfig)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -80,10 +111,14 @@ func NewCommand() *cobra.Command {
|
||||
errors.CheckError(err)
|
||||
cache.Cache.SetClient(cacheutil.NewTwoLevelClient(cache.Cache.GetClient(), 10*time.Minute))
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace)
|
||||
var appController *controller.ApplicationController
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(ctx, kubeClient, namespace, settings.WithRepoOrClusterChangedHandler(func() {
|
||||
appController.InvalidateProjectsCache()
|
||||
}))
|
||||
kubectl := kubeutil.NewKubectl()
|
||||
clusterFilter := getClusterFilter()
|
||||
appController, err := controller.NewApplicationController(
|
||||
appController, err = controller.NewApplicationController(
|
||||
namespace,
|
||||
settingsMgr,
|
||||
kubeClient,
|
||||
@@ -95,6 +130,7 @@ func NewCommand() *cobra.Command {
|
||||
time.Duration(selfHealTimeoutSeconds)*time.Second,
|
||||
metricsPort,
|
||||
metricsCacheExpiration,
|
||||
metricsAplicationLabels,
|
||||
kubectlParallelismLimit,
|
||||
clusterFilter)
|
||||
errors.CheckError(err)
|
||||
@@ -114,18 +150,21 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().Int64Var(&appResyncPeriod, "app-resync", defaultAppResyncPeriod, "Time period in seconds for application resync.")
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address.")
|
||||
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
|
||||
command.Flags().IntVar(&statusProcessors, "status-processors", 1, "Number of application status processors")
|
||||
command.Flags().IntVar(&operationProcessors, "operation-processors", 1, "Number of application operation processors")
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().Int64Var(&appResyncPeriod, "app-resync", int64(env.ParseDurationFromEnv("ARGOCD_RECONCILIATION_TIMEOUT", defaultAppResyncPeriod*time.Second, 0, math.MaxInt64).Seconds()), "Time period in seconds for application resync.")
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address.")
|
||||
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
|
||||
command.Flags().IntVar(&statusProcessors, "status-processors", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_STATUS_PROCESSORS", 20, 0, math.MaxInt32), "Number of application status processors")
|
||||
command.Flags().IntVar(&operationProcessors, "operation-processors", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_OPERATION_PROCESSORS", 10, 0, math.MaxInt32), "Number of application operation processors")
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_APPLICATION_CONTROLLER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDMetrics, "Start metrics server on given port")
|
||||
command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", 0*time.Second, "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)")
|
||||
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", 5, "Specifies timeout between application self heal attempts")
|
||||
command.Flags().DurationVar(&metricsCacheExpiration, "metrics-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APPLICATION_CONTROLLER_METRICS_CACHE_EXPIRATION", 0*time.Second, 0, math.MaxInt64), "Prometheus metrics cache expiration (disabled by default. e.g. 24h0m0s)")
|
||||
command.Flags().IntVar(&selfHealTimeoutSeconds, "self-heal-timeout-seconds", env.ParseNumFromEnv("ARGOCD_APPLICATION_CONTROLLER_SELF_HEAL_TIMEOUT_SECONDS", 5, 0, math.MaxInt32), "Specifies timeout between application self heal attempts")
|
||||
command.Flags().Int64Var(&kubectlParallelismLimit, "kubectl-parallelism-limit", 20, "Number of allowed concurrent kubectl fork/execs. Any value less the 1 means no limit.")
|
||||
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_PLAINTEXT", false), "Disable TLS on connections to repo server")
|
||||
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_APPLICATION_CONTROLLER_REPO_SERVER_STRICT_TLS", false), "Whether to use strict validation of the TLS cert presented by the repo server")
|
||||
command.Flags().StringSliceVar(&metricsAplicationLabels, "metrics-application-labels", []string{}, "List of Application labels that will be added to the argocd_application_labels metric")
|
||||
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
})
|
||||
|
||||
58
cmd/argocd-cmp-server/commands/argocd_cmp_server.go
Normal file
58
cmd/argocd-cmp-server/commands/argocd_cmp_server.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/stats"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/cmpserver"
|
||||
"github.com/argoproj/argo-cd/v2/cmpserver/plugin"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = "argocd-cmp-server"
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
configFilePath string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
Short: "Run ArgoCD ConfigManagementPlugin Server",
|
||||
Long: "ArgoCD ConfigManagementPlugin Server is an internal service which runs as sidecar container in reposerver deployment. It can be configured by following options.",
|
||||
DisableAutoGenTag: true,
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
cli.SetLogFormat(cmdutil.LogFormat)
|
||||
cli.SetLogLevel(cmdutil.LogLevel)
|
||||
|
||||
config, err := plugin.ReadPluginConfig(configFilePath)
|
||||
errors.CheckError(err)
|
||||
|
||||
server, err := cmpserver.NewServer(plugin.CMPServerInitConstants{
|
||||
PluginConfig: *config,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
|
||||
// register dumper
|
||||
stats.RegisterStackDumper()
|
||||
stats.StartStatsTicker(10 * time.Minute)
|
||||
stats.RegisterHeapDumper("memprofile")
|
||||
|
||||
// run argocd-cmp-server server
|
||||
server.Run()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().StringVar(&configFilePath, "config-dir-path", common.DefaultPluginConfigFilePath, "Config management plugin configuration file location, Default is '/home/argocd/cmp-server/config/'")
|
||||
return &command
|
||||
}
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/dex"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/dex"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
132
cmd/argocd-notification/commands/controller.go
Normal file
132
cmd/argocd-notification/commands/controller.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
service "github.com/argoproj/argo-cd/v2/util/notification/argocd"
|
||||
|
||||
notificationscontroller "github.com/argoproj/argo-cd/v2/notification_controller/controller"
|
||||
|
||||
controller "github.com/argoproj/notifications-engine/pkg/controller"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMetricsPort = 9001
|
||||
)
|
||||
|
||||
func addK8SFlagsToCmd(cmd *cobra.Command) clientcmd.ClientConfig {
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
|
||||
overrides := clientcmd.ConfigOverrides{}
|
||||
kflags := clientcmd.RecommendedConfigOverrideFlags("")
|
||||
cmd.PersistentFlags().StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to a kube config. Only required if out-of-cluster")
|
||||
clientcmd.BindOverrideFlags(&overrides, cmd.PersistentFlags(), kflags)
|
||||
return clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, &overrides, os.Stdin)
|
||||
}
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
processorsCount int
|
||||
namespace string
|
||||
appLabelSelector string
|
||||
logLevel string
|
||||
logFormat string
|
||||
metricsPort int
|
||||
argocdRepoServer string
|
||||
argocdRepoServerPlaintext bool
|
||||
argocdRepoServerStrictTLS bool
|
||||
configMapName string
|
||||
secretName string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "controller",
|
||||
Short: "Starts Argo CD Notifications controller",
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
restConfig, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dynamicClient, err := dynamic.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k8sClient, err := kubernetes.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if namespace == "" {
|
||||
namespace, _, err = clientConfig.Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
level, err := log.ParseLevel(logLevel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.SetLevel(level)
|
||||
|
||||
switch strings.ToLower(logFormat) {
|
||||
case "json":
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
case "text":
|
||||
if os.Getenv("FORCE_LOG_COLORS") == "1" {
|
||||
log.SetFormatter(&log.TextFormatter{ForceColors: true})
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unknown log format '%s'", logFormat)
|
||||
}
|
||||
|
||||
argocdService, err := service.NewArgoCDService(k8sClient, namespace, argocdRepoServer, argocdRepoServerPlaintext, argocdRepoServerStrictTLS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer argocdService.Close()
|
||||
|
||||
registry := controller.NewMetricsRegistry("argocd")
|
||||
http.Handle("/metrics", promhttp.HandlerFor(prometheus.Gatherers{registry, prometheus.DefaultGatherer}, promhttp.HandlerOpts{}))
|
||||
|
||||
go func() {
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", metricsPort), http.DefaultServeMux))
|
||||
}()
|
||||
log.Infof("serving metrics on port %d", metricsPort)
|
||||
log.Infof("loading configuration %d", metricsPort)
|
||||
|
||||
ctrl := notificationscontroller.NewController(k8sClient, dynamicClient, argocdService, namespace, appLabelSelector, registry, secretName, configMapName)
|
||||
err = ctrl.Init(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go ctrl.Run(context.Background(), processorsCount)
|
||||
<-context.Background().Done()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
clientConfig = addK8SFlagsToCmd(&command)
|
||||
command.Flags().IntVar(&processorsCount, "processors-count", 1, "Processors count.")
|
||||
command.Flags().StringVar(&appLabelSelector, "app-label-selector", "", "App label selector.")
|
||||
command.Flags().StringVar(&namespace, "namespace", "", "Namespace which controller handles. Current namespace if empty.")
|
||||
command.Flags().StringVar(&logLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().StringVar(&logFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", defaultMetricsPort, "Metrics port")
|
||||
command.Flags().StringVar(&argocdRepoServer, "argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address")
|
||||
command.Flags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server")
|
||||
command.Flags().BoolVar(&argocdRepoServerStrictTLS, "argocd-repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server")
|
||||
command.Flags().StringVar(&configMapName, "config-map-name", "argocd-notifications-cm", "Set notifications ConfigMap name")
|
||||
command.Flags().StringVar(&secretName, "secret-name", "argocd-notifications-secret", "Set notifications Secret name")
|
||||
return &command
|
||||
}
|
||||
@@ -14,21 +14,21 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/reposerver"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
reposervercache "github.com/argoproj/argo-cd/reposerver/cache"
|
||||
"github.com/argoproj/argo-cd/reposerver/metrics"
|
||||
"github.com/argoproj/argo-cd/reposerver/repository"
|
||||
cacheutil "github.com/argoproj/argo-cd/util/cache"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/env"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/gpg"
|
||||
"github.com/argoproj/argo-cd/util/healthz"
|
||||
ioutil "github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/tls"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
reposervercache "github.com/argoproj/argo-cd/v2/reposerver/cache"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/metrics"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/repository"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
"github.com/argoproj/argo-cd/v2/util/healthz"
|
||||
ioutil "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/tls"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -67,8 +67,10 @@ func NewCommand() *cobra.Command {
|
||||
listenPort int
|
||||
metricsPort int
|
||||
cacheSrc func() (*reposervercache.Cache, error)
|
||||
tlsConfigCustomizer tls.ConfigCustomizer
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
redisClient *redis.Client
|
||||
disableTLS bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -79,8 +81,11 @@ func NewCommand() *cobra.Command {
|
||||
cli.SetLogFormat(cmdutil.LogFormat)
|
||||
cli.SetLogLevel(cmdutil.LogLevel)
|
||||
|
||||
tlsConfigCustomizer, err := tlsConfigCustomizerSrc()
|
||||
errors.CheckError(err)
|
||||
if !disableTLS {
|
||||
var err error
|
||||
tlsConfigCustomizer, err = tlsConfigCustomizerSrc()
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
cache, err := cacheSrc()
|
||||
errors.CheckError(err)
|
||||
@@ -104,7 +109,7 @@ func NewCommand() *cobra.Command {
|
||||
// connect to itself to make sure repo server is able to serve connection
|
||||
// used by liveness probe to auto restart repo server
|
||||
// see https://github.com/argoproj/argo-cd/issues/5110 for more information
|
||||
conn, err := apiclient.NewConnection(fmt.Sprintf("localhost:%d", listenPort), 60)
|
||||
conn, err := apiclient.NewConnection(fmt.Sprintf("localhost:%d", listenPort), 60, &apiclient.TLSConfiguration{DisableTLS: disableTLS})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -146,12 +151,15 @@ func NewCommand() *cobra.Command {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().Int64Var(¶llelismLimit, "parallelismlimit", 0, "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.")
|
||||
if cmdutil.LogFormat == "" {
|
||||
cmdutil.LogFormat = os.Getenv("ARGOCD_REPO_SERVER_LOGLEVEL")
|
||||
}
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().Int64Var(¶llelismLimit, "parallelismlimit", int64(env.ParseNumFromEnv("ARGOCD_REPO_SERVER_PARALLELISM_LIMIT", 0, 0, math.MaxInt32)), "Limit on number of concurrent manifests generate requests. Any value less the 1 means no limit.")
|
||||
command.Flags().IntVar(&listenPort, "port", common.DefaultPortRepoServer, "Listen on given port for incoming connections")
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortRepoServerMetrics, "Start metrics server on given port")
|
||||
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_TLS", false), "Disable TLS on the gRPC endpoint")
|
||||
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
|
||||
@@ -2,6 +2,8 @@ package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/stats"
|
||||
@@ -11,18 +13,18 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/server"
|
||||
servercache "github.com/argoproj/argo-cd/server/cache"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/env"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/tls"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/server"
|
||||
servercache "github.com/argoproj/argo-cd/v2/server/cache"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/env"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/kube"
|
||||
"github.com/argoproj/argo-cd/v2/util/tls"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -50,7 +52,6 @@ func NewCommand() *cobra.Command {
|
||||
glogLevel int
|
||||
clientConfig clientcmd.ClientConfig
|
||||
repoServerTimeoutSeconds int
|
||||
staticAssetsDir string
|
||||
baseHRef string
|
||||
rootPath string
|
||||
repoServerAddress string
|
||||
@@ -60,6 +61,9 @@ func NewCommand() *cobra.Command {
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
cacheSrc func() (*servercache.Cache, error)
|
||||
frameOptions string
|
||||
repoServerPlaintext bool
|
||||
repoServerStrictTLS bool
|
||||
staticAssetsDir string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -93,8 +97,25 @@ func NewCommand() *cobra.Command {
|
||||
appclientsetConfig = kube.AddFailureRetryWrapper(appclientsetConfig, failureRetryCount, failureRetryPeriodMilliSeconds)
|
||||
}
|
||||
appclientset := appclientset.NewForConfigOrDie(appclientsetConfig)
|
||||
repoclientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds)
|
||||
tlsConfig := apiclient.TLSConfiguration{
|
||||
DisableTLS: repoServerPlaintext,
|
||||
StrictValidation: repoServerStrictTLS,
|
||||
}
|
||||
|
||||
// Load CA information to use for validating connections to the
|
||||
// repository server, if strict TLS validation was requested.
|
||||
if !repoServerPlaintext && repoServerStrictTLS {
|
||||
pool, err := tls.LoadX509CertPool(
|
||||
fmt.Sprintf("%s/server/tls/tls.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
|
||||
fmt.Sprintf("%s/server/tls/ca.crt", env.StringFromEnv(common.EnvAppConfigPath, common.DefaultAppConfigPath)),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
tlsConfig.Certificates = pool
|
||||
}
|
||||
|
||||
repoclientset := apiclient.NewRepoServerClientset(repoServerAddress, repoServerTimeoutSeconds, tlsConfig)
|
||||
if rootPath != "" {
|
||||
if baseHRef != "" && baseHRef != rootPath {
|
||||
log.Warnf("--basehref and --rootpath had conflict: basehref: %s rootpath: %s", baseHRef, rootPath)
|
||||
@@ -107,7 +128,6 @@ func NewCommand() *cobra.Command {
|
||||
ListenPort: listenPort,
|
||||
MetricsPort: metricsPort,
|
||||
Namespace: namespace,
|
||||
StaticAssetsDir: staticAssetsDir,
|
||||
BaseHRef: baseHRef,
|
||||
RootPath: rootPath,
|
||||
KubeClientset: kubeclientset,
|
||||
@@ -120,6 +140,7 @@ func NewCommand() *cobra.Command {
|
||||
Cache: cache,
|
||||
XFrameOptions: frameOptions,
|
||||
RedisClient: redisClient,
|
||||
StaticAssetsDir: staticAssetsDir,
|
||||
}
|
||||
|
||||
stats.RegisterStackDumper()
|
||||
@@ -137,22 +158,24 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
command.Flags().BoolVar(&insecure, "insecure", false, "Run server without TLS")
|
||||
command.Flags().StringVar(&staticAssetsDir, "staticassets", "", "Static assets directory path")
|
||||
command.Flags().StringVar(&baseHRef, "basehref", "/", "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /")
|
||||
command.Flags().StringVar(&rootPath, "rootpath", "", "Used if Argo CD is running behind reverse proxy under subpath different from /")
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().BoolVar(&insecure, "insecure", env.ParseBoolFromEnv("ARGOCD_SERVER_INSECURE", false), "Run server without TLS")
|
||||
command.Flags().StringVar(&staticAssetsDir, "staticassets", env.StringFromEnv("ARGOCD_SERVER_STATIC_ASSETS", "/shared/app"), "Directory path that contains additional static assets")
|
||||
command.Flags().StringVar(&baseHRef, "basehref", env.StringFromEnv("ARGOCD_SERVER_BASEHREF", "/"), "Value for base href in index.html. Used if Argo CD is running behind reverse proxy under subpath different from /")
|
||||
command.Flags().StringVar(&rootPath, "rootpath", env.StringFromEnv("ARGOCD_SERVER_ROOTPATH", ""), "Used if Argo CD is running behind reverse proxy under subpath different from /")
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_SERVER_LOGFORMAT", "text"), "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_REPO_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error")
|
||||
command.Flags().IntVar(&glogLevel, "gloglevel", 0, "Set the glog logging level")
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", common.DefaultRepoServerAddr, "Repo server address")
|
||||
command.Flags().StringVar(&dexServerAddress, "dex-server", common.DefaultDexServerAddr, "Dex server address")
|
||||
command.Flags().BoolVar(&disableAuth, "disable-auth", false, "Disable client authentication")
|
||||
command.Flags().BoolVar(&enableGZip, "enable-gzip", false, "Enable GZIP compression")
|
||||
command.Flags().StringVar(&repoServerAddress, "repo-server", env.StringFromEnv("ARGOCD_SERVER_REPO_SERVER", common.DefaultRepoServerAddr), "Repo server address")
|
||||
command.Flags().StringVar(&dexServerAddress, "dex-server", env.StringFromEnv("ARGOCD_SERVER_DEX_SERVER", common.DefaultDexServerAddr), "Dex server address")
|
||||
command.Flags().BoolVar(&disableAuth, "disable-auth", env.ParseBoolFromEnv("ARGOCD_SERVER_DISABLE_AUTH", false), "Disable client authentication")
|
||||
command.Flags().BoolVar(&enableGZip, "enable-gzip", env.ParseBoolFromEnv("ARGOCD_SERVER_ENABLE_GZIP", false), "Enable GZIP compression")
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
command.Flags().IntVar(&listenPort, "port", common.DefaultPortAPIServer, "Listen on given port")
|
||||
command.Flags().IntVar(&metricsPort, "metrics-port", common.DefaultPortArgoCDAPIServerMetrics, "Start metrics on given port")
|
||||
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", 60, "Repo server RPC call timeout seconds.")
|
||||
command.Flags().StringVar(&frameOptions, "x-frame-options", "sameorigin", "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".")
|
||||
command.Flags().IntVar(&repoServerTimeoutSeconds, "repo-server-timeout-seconds", env.ParseNumFromEnv("ARGOCD_SERVER_REPO_SERVER_TIMEOUT_SECONDS", 60, 0, math.MaxInt64), "Repo server RPC call timeout seconds.")
|
||||
command.Flags().StringVar(&frameOptions, "x-frame-options", env.StringFromEnv("ARGOCD_SERVER_X_FRAME_OPTIONS", "sameorigin"), "Set X-Frame-Options header in HTTP responses to `value`. To disable, set to \"\".")
|
||||
command.Flags().BoolVar(&repoServerPlaintext, "repo-server-plaintext", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_PLAINTEXT", false), "Use a plaintext client (non-TLS) to connect to repository server")
|
||||
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", env.ParseBoolFromEnv("ARGOCD_SERVER_REPO_SERVER_STRICT_TLS", false), "Perform strict validation of TLS certificates when connecting to repo server")
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(command)
|
||||
cacheSrc = servercache.AddCacheFlagsToCmd(command, func(client *redis.Client) {
|
||||
redisClient = client
|
||||
|
||||
@@ -1,547 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
const (
|
||||
// CLIName is the name of the CLI
|
||||
cliName = "argocd-util"
|
||||
// YamlSeparator separates sections of a YAML file
|
||||
yamlSeparator = "---\n"
|
||||
)
|
||||
|
||||
var (
|
||||
configMapResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}
|
||||
secretResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
|
||||
applicationsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
|
||||
appprojectsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "appprojects"}
|
||||
)
|
||||
|
||||
// NewCommand returns a new instance of an argocd command
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
pathOpts = clientcmd.NewDefaultPathOptions()
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: cliName,
|
||||
Short: "argocd-util tools used by Argo CD",
|
||||
Long: "argocd-util has internal utility tools used by Argo CD",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(cli.NewVersionCmd(cliName))
|
||||
command.AddCommand(NewImportCommand())
|
||||
command.AddCommand(NewExportCommand())
|
||||
command.AddCommand(NewClusterConfig())
|
||||
command.AddCommand(NewProjectsCommand())
|
||||
command.AddCommand(NewSettingsCommand())
|
||||
command.AddCommand(NewAppsCommand())
|
||||
command.AddCommand(NewRBACCommand())
|
||||
command.AddCommand(NewGenerateConfigCommand(pathOpts))
|
||||
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
return command
|
||||
}
|
||||
|
||||
// NewImportCommand defines a new command for exporting Kubernetes and Argo CD resources.
|
||||
func NewImportCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
prune bool
|
||||
dryRun bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "import SOURCE",
|
||||
Short: "Import Argo CD data from stdin (specify `-') or a file",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
config.QPS = 100
|
||||
config.Burst = 50
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
acdClients := newArgoCDClientsets(config, namespace)
|
||||
|
||||
var input []byte
|
||||
if in := args[0]; in == "-" {
|
||||
input, err = ioutil.ReadAll(os.Stdin)
|
||||
} else {
|
||||
input, err = ioutil.ReadFile(in)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
var dryRunMsg string
|
||||
if dryRun {
|
||||
dryRunMsg = " (dry run)"
|
||||
}
|
||||
|
||||
// pruneObjects tracks live objects and it's current resource version. any remaining
|
||||
// items in this map indicates the resource should be pruned since it no longer appears
|
||||
// in the backup
|
||||
pruneObjects := make(map[kube.ResourceKey]unstructured.Unstructured)
|
||||
configMaps, err := acdClients.configMaps.List(context.Background(), metav1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
// referencedSecrets holds any secrets referenced in the argocd-cm configmap. These
|
||||
// secrets need to be imported too
|
||||
var referencedSecrets map[string]bool
|
||||
for _, cm := range configMaps.Items {
|
||||
if isArgoCDConfigMap(cm.GetName()) {
|
||||
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName()}] = cm
|
||||
}
|
||||
if cm.GetName() == common.ArgoCDConfigMapName {
|
||||
referencedSecrets = getReferencedSecrets(cm)
|
||||
}
|
||||
}
|
||||
|
||||
secrets, err := acdClients.secrets.List(context.Background(), metav1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, secret := range secrets.Items {
|
||||
if isArgoCDSecret(referencedSecrets, secret) {
|
||||
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName()}] = secret
|
||||
}
|
||||
}
|
||||
applications, err := acdClients.applications.List(context.Background(), metav1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, app := range applications.Items {
|
||||
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "Application", Name: app.GetName()}] = app
|
||||
}
|
||||
projects, err := acdClients.projects.List(context.Background(), metav1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, proj := range projects.Items {
|
||||
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj
|
||||
}
|
||||
|
||||
// Create or replace existing object
|
||||
backupObjects, err := kube.SplitYAML(input)
|
||||
errors.CheckError(err)
|
||||
for _, bakObj := range backupObjects {
|
||||
gvk := bakObj.GroupVersionKind()
|
||||
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()}
|
||||
liveObj, exists := pruneObjects[key]
|
||||
delete(pruneObjects, key)
|
||||
var dynClient dynamic.ResourceInterface
|
||||
switch bakObj.GetKind() {
|
||||
case "Secret":
|
||||
dynClient = acdClients.secrets
|
||||
case "ConfigMap":
|
||||
dynClient = acdClients.configMaps
|
||||
case "AppProject":
|
||||
dynClient = acdClients.projects
|
||||
case "Application":
|
||||
dynClient = acdClients.applications
|
||||
}
|
||||
if !exists {
|
||||
if !dryRun {
|
||||
_, err = dynClient.Create(context.Background(), bakObj, metav1.CreateOptions{})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
} else if specsEqual(*bakObj, liveObj) {
|
||||
fmt.Printf("%s/%s %s unchanged%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
} else {
|
||||
if !dryRun {
|
||||
newLive := updateLive(bakObj, &liveObj)
|
||||
_, err = dynClient.Update(context.Background(), newLive, metav1.UpdateOptions{})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// Delete objects not in backup
|
||||
for key := range pruneObjects {
|
||||
if prune {
|
||||
var dynClient dynamic.ResourceInterface
|
||||
switch key.Kind {
|
||||
case "Secret":
|
||||
dynClient = acdClients.secrets
|
||||
case "AppProject":
|
||||
dynClient = acdClients.projects
|
||||
case "Application":
|
||||
dynClient = acdClients.applications
|
||||
default:
|
||||
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
|
||||
}
|
||||
if !dryRun {
|
||||
err = dynClient.Delete(context.Background(), key.Name, metav1.DeleteOptions{})
|
||||
errors.CheckError(err)
|
||||
}
|
||||
fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg)
|
||||
} else {
|
||||
fmt.Printf("%s/%s %s needs pruning\n", key.Group, key.Kind, key.Name)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", false, "Print what will be performed")
|
||||
command.Flags().BoolVar(&prune, "prune", false, "Prune secrets, applications and projects which do not appear in the backup")
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
type argoCDClientsets struct {
|
||||
configMaps dynamic.ResourceInterface
|
||||
secrets dynamic.ResourceInterface
|
||||
applications dynamic.ResourceInterface
|
||||
projects dynamic.ResourceInterface
|
||||
}
|
||||
|
||||
func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientsets {
|
||||
dynamicIf, err := dynamic.NewForConfig(config)
|
||||
errors.CheckError(err)
|
||||
return &argoCDClientsets{
|
||||
configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace),
|
||||
secrets: dynamicIf.Resource(secretResource).Namespace(namespace),
|
||||
applications: dynamicIf.Resource(applicationsResource).Namespace(namespace),
|
||||
projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace),
|
||||
}
|
||||
}
|
||||
|
||||
// NewExportCommand defines a new command for exporting Kubernetes and Argo CD resources.
|
||||
func NewExportCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
out string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Export all Argo CD data to stdout (default) or a file",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
var writer io.Writer
|
||||
if out == "-" {
|
||||
writer = os.Stdout
|
||||
} else {
|
||||
f, err := os.Create(out)
|
||||
errors.CheckError(err)
|
||||
bw := bufio.NewWriter(f)
|
||||
writer = bw
|
||||
defer func() {
|
||||
err = bw.Flush()
|
||||
errors.CheckError(err)
|
||||
err = f.Close()
|
||||
errors.CheckError(err)
|
||||
}()
|
||||
}
|
||||
|
||||
acdClients := newArgoCDClientsets(config, namespace)
|
||||
acdConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDConfigMapName, metav1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdConfigMap)
|
||||
acdRBACConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDRBACConfigMapName, metav1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdRBACConfigMap)
|
||||
acdKnownHostsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDKnownHostsConfigMapName, metav1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdKnownHostsConfigMap)
|
||||
acdTLSCertsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDTLSCertsConfigMapName, metav1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdTLSCertsConfigMap)
|
||||
|
||||
referencedSecrets := getReferencedSecrets(*acdConfigMap)
|
||||
secrets, err := acdClients.secrets.List(context.Background(), metav1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, secret := range secrets.Items {
|
||||
if isArgoCDSecret(referencedSecrets, secret) {
|
||||
export(writer, secret)
|
||||
}
|
||||
}
|
||||
projects, err := acdClients.projects.List(context.Background(), metav1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, proj := range projects.Items {
|
||||
export(writer, proj)
|
||||
}
|
||||
applications, err := acdClients.applications.List(context.Background(), metav1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, app := range applications.Items {
|
||||
export(writer, app)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout")
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
// getReferencedSecrets examines the argocd-cm config for any referenced repo secrets and returns a
|
||||
// map of all referenced secrets.
|
||||
func getReferencedSecrets(un unstructured.Unstructured) map[string]bool {
|
||||
var cm apiv1.ConfigMap
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm)
|
||||
errors.CheckError(err)
|
||||
referencedSecrets := make(map[string]bool)
|
||||
|
||||
// Referenced repository secrets
|
||||
if reposRAW, ok := cm.Data["repositories"]; ok {
|
||||
repos := make([]settings.Repository, 0)
|
||||
err := yaml.Unmarshal([]byte(reposRAW), &repos)
|
||||
errors.CheckError(err)
|
||||
for _, cred := range repos {
|
||||
if cred.PasswordSecret != nil {
|
||||
referencedSecrets[cred.PasswordSecret.Name] = true
|
||||
}
|
||||
if cred.SSHPrivateKeySecret != nil {
|
||||
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
|
||||
}
|
||||
if cred.UsernameSecret != nil {
|
||||
referencedSecrets[cred.UsernameSecret.Name] = true
|
||||
}
|
||||
if cred.TLSClientCertDataSecret != nil {
|
||||
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
|
||||
}
|
||||
if cred.TLSClientCertKeySecret != nil {
|
||||
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Referenced repository credentials secrets
|
||||
if reposRAW, ok := cm.Data["repository.credentials"]; ok {
|
||||
creds := make([]settings.RepositoryCredentials, 0)
|
||||
err := yaml.Unmarshal([]byte(reposRAW), &creds)
|
||||
errors.CheckError(err)
|
||||
for _, cred := range creds {
|
||||
if cred.PasswordSecret != nil {
|
||||
referencedSecrets[cred.PasswordSecret.Name] = true
|
||||
}
|
||||
if cred.SSHPrivateKeySecret != nil {
|
||||
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
|
||||
}
|
||||
if cred.UsernameSecret != nil {
|
||||
referencedSecrets[cred.UsernameSecret.Name] = true
|
||||
}
|
||||
if cred.TLSClientCertDataSecret != nil {
|
||||
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
|
||||
}
|
||||
if cred.TLSClientCertKeySecret != nil {
|
||||
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return referencedSecrets
|
||||
}
|
||||
|
||||
// isArgoCDSecret returns whether or not the given secret is a part of Argo CD configuration
|
||||
// (e.g. argocd-secret, repo credentials, or cluster credentials)
|
||||
func isArgoCDSecret(repoSecretRefs map[string]bool, un unstructured.Unstructured) bool {
|
||||
secretName := un.GetName()
|
||||
if secretName == common.ArgoCDSecretName {
|
||||
return true
|
||||
}
|
||||
if repoSecretRefs != nil {
|
||||
if _, ok := repoSecretRefs[secretName]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if labels := un.GetLabels(); labels != nil {
|
||||
if _, ok := labels[common.LabelKeySecretType]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if annotations := un.GetAnnotations(); annotations != nil {
|
||||
if annotations[common.AnnotationKeyManagedBy] == common.AnnotationValueManagedByArgoCD {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isArgoCDConfigMap returns true if the configmap name is one of argo cd's well known configmaps
|
||||
func isArgoCDConfigMap(name string) bool {
|
||||
switch name {
|
||||
case common.ArgoCDConfigMapName, common.ArgoCDRBACConfigMapName, common.ArgoCDKnownHostsConfigMapName, common.ArgoCDTLSCertsConfigMapName:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
// specsEqual returns if the spec, data, labels, annotations, and finalizers of the two
|
||||
// supplied objects are equal, indicating that no update is necessary during importing
|
||||
func specsEqual(left, right unstructured.Unstructured) bool {
|
||||
if !reflect.DeepEqual(left.GetAnnotations(), right.GetAnnotations()) {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(left.GetLabels(), right.GetLabels()) {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(left.GetFinalizers(), right.GetFinalizers()) {
|
||||
return false
|
||||
}
|
||||
switch left.GetKind() {
|
||||
case "Secret", "ConfigMap":
|
||||
leftData, _, _ := unstructured.NestedMap(left.Object, "data")
|
||||
rightData, _, _ := unstructured.NestedMap(right.Object, "data")
|
||||
return reflect.DeepEqual(leftData, rightData)
|
||||
case "AppProject":
|
||||
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
|
||||
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
|
||||
return reflect.DeepEqual(leftSpec, rightSpec)
|
||||
case "Application":
|
||||
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
|
||||
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
|
||||
leftStatus, _, _ := unstructured.NestedMap(left.Object, "status")
|
||||
rightStatus, _, _ := unstructured.NestedMap(right.Object, "status")
|
||||
// reconciledAt and observedAt are constantly changing and we ignore any diff there
|
||||
delete(leftStatus, "reconciledAt")
|
||||
delete(rightStatus, "reconciledAt")
|
||||
delete(leftStatus, "observedAt")
|
||||
delete(rightStatus, "observedAt")
|
||||
return reflect.DeepEqual(leftSpec, rightSpec) && reflect.DeepEqual(leftStatus, rightStatus)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// updateLive replaces the live object's finalizers, spec, annotations, labels, and data from the
|
||||
// backup object but leaves all other fields intact (status, other metadata, etc...)
|
||||
func updateLive(bak, live *unstructured.Unstructured) *unstructured.Unstructured {
|
||||
newLive := live.DeepCopy()
|
||||
newLive.SetAnnotations(bak.GetAnnotations())
|
||||
newLive.SetLabels(bak.GetLabels())
|
||||
newLive.SetFinalizers(bak.GetFinalizers())
|
||||
switch live.GetKind() {
|
||||
case "Secret", "ConfigMap":
|
||||
newLive.Object["data"] = bak.Object["data"]
|
||||
case "AppProject":
|
||||
newLive.Object["spec"] = bak.Object["spec"]
|
||||
case "Application":
|
||||
newLive.Object["spec"] = bak.Object["spec"]
|
||||
if _, ok := bak.Object["status"]; ok {
|
||||
newLive.Object["status"] = bak.Object["status"]
|
||||
}
|
||||
}
|
||||
return newLive
|
||||
}
|
||||
|
||||
// export writes the unstructured object and removes extraneous cruft from output before writing
|
||||
func export(w io.Writer, un unstructured.Unstructured) {
|
||||
name := un.GetName()
|
||||
finalizers := un.GetFinalizers()
|
||||
apiVersion := un.GetAPIVersion()
|
||||
kind := un.GetKind()
|
||||
labels := un.GetLabels()
|
||||
annotations := un.GetAnnotations()
|
||||
unstructured.RemoveNestedField(un.Object, "metadata")
|
||||
un.SetName(name)
|
||||
un.SetFinalizers(finalizers)
|
||||
un.SetAPIVersion(apiVersion)
|
||||
un.SetKind(kind)
|
||||
un.SetLabels(labels)
|
||||
un.SetAnnotations(annotations)
|
||||
data, err := yaml.Marshal(un.Object)
|
||||
errors.CheckError(err)
|
||||
_, err = w.Write(data)
|
||||
errors.CheckError(err)
|
||||
_, err = w.Write([]byte(yamlSeparator))
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
// NewClusterConfig returns a new instance of `argocd-util kubeconfig` command
|
||||
func NewClusterConfig() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "kubeconfig CLUSTER_URL OUTPUT_PATH",
|
||||
Short: "Generates kubeconfig for the specified cluster",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
serverUrl := args[0]
|
||||
output := args[1]
|
||||
conf, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
kubeclientset, err := kubernetes.NewForConfig(conf)
|
||||
errors.CheckError(err)
|
||||
|
||||
cluster, err := db.NewDB(namespace, settings.NewSettingsManager(context.Background(), kubeclientset, namespace), kubeclientset).GetCluster(context.Background(), serverUrl)
|
||||
errors.CheckError(err)
|
||||
err = kube.WriteKubeConfig(cluster.RawRestConfig(), namespace, output)
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
return command
|
||||
}
|
||||
|
||||
func iterateStringFields(obj interface{}, callback func(name string, val string) string) {
|
||||
if mapField, ok := obj.(map[string]interface{}); ok {
|
||||
for field, val := range mapField {
|
||||
if strVal, ok := val.(string); ok {
|
||||
mapField[field] = callback(field, strVal)
|
||||
} else {
|
||||
iterateStringFields(val, callback)
|
||||
}
|
||||
}
|
||||
} else if arrayField, ok := obj.([]interface{}); ok {
|
||||
for i := range arrayField {
|
||||
iterateStringFields(arrayField[i], callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func redactor(dirtyString string) string {
|
||||
config := make(map[string]interface{})
|
||||
err := yaml.Unmarshal([]byte(dirtyString), &config)
|
||||
errors.CheckError(err)
|
||||
iterateStringFields(config, func(name string, val string) string {
|
||||
if name == "clientSecret" || name == "secret" || name == "bindPW" {
|
||||
return "********"
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
})
|
||||
data, err := yaml.Marshal(config)
|
||||
errors.CheckError(err)
|
||||
return string(data)
|
||||
}
|
||||
@@ -1,359 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
const (
|
||||
ArgoCDNamespace = "argocd"
|
||||
repoSecretPrefix = "repo"
|
||||
)
|
||||
|
||||
func NewGenerateConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Generate declarative configuration files",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
command.AddCommand(NewGenAppConfigCommand())
|
||||
command.AddCommand(NewGenProjectConfigCommand())
|
||||
command.AddCommand(NewGenClusterConfigCommand(pathOpts))
|
||||
command.AddCommand(NewGenRepoConfigCommand())
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
// NewGenAppConfigCommand generates declarative configuration file for given application
|
||||
func NewGenAppConfigCommand() *cobra.Command {
|
||||
var (
|
||||
appOpts cmdutil.AppOptions
|
||||
fileURL string
|
||||
appName string
|
||||
labels []string
|
||||
outputFormat string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "app APPNAME",
|
||||
Short: "Generate declarative config for an application",
|
||||
Example: `
|
||||
# Generate declarative config for a directory app
|
||||
argocd-util config app guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse
|
||||
|
||||
# Generate declarative config for a Jsonnet app
|
||||
argocd-util config app jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2
|
||||
|
||||
# Generate declarative config for a Helm app
|
||||
argocd-util config app helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2
|
||||
|
||||
# Generate declarative config for a Helm app from a Helm repo
|
||||
argocd-util config app nginx-ingress --repo https://kubernetes-charts.storage.googleapis.com --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
|
||||
|
||||
# Generate declarative config for a Kustomize app
|
||||
argocd-util config app kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||
|
||||
# Generate declarative config for a app using a custom tool:
|
||||
argocd-util config app ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
app, err := cmdutil.ConstructApp(fileURL, appName, labels, args, appOpts, c.Flags())
|
||||
errors.CheckError(err)
|
||||
|
||||
if app.Name == "" {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var printResources []interface{}
|
||||
printResources = append(printResources, app)
|
||||
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&appName, "name", "", "A name for the app, ignored if a file is set (DEPRECATED)")
|
||||
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the app")
|
||||
command.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "Labels to apply to the app")
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
|
||||
// Only complete files with appropriate extension.
|
||||
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
|
||||
errors.CheckError(err)
|
||||
|
||||
cmdutil.AddAppFlags(command, &appOpts)
|
||||
return command
|
||||
}
|
||||
|
||||
// NewGenProjectConfigCommand generates declarative configuration file for given project
|
||||
func NewGenProjectConfigCommand() *cobra.Command {
|
||||
var (
|
||||
opts cmdutil.ProjectOpts
|
||||
fileURL string
|
||||
outputFormat string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "proj PROJECT",
|
||||
Short: "Generate declarative config for a project",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c)
|
||||
errors.CheckError(err)
|
||||
|
||||
var printResources []interface{}
|
||||
printResources = append(printResources, proj)
|
||||
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the project")
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cmdutil.AddProjFlags(command, &opts)
|
||||
return command
|
||||
}
|
||||
|
||||
func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var (
|
||||
clusterOpts cmdutil.ClusterOptions
|
||||
bearerToken string
|
||||
outputFormat string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "cluster CONTEXT",
|
||||
Short: "Generate declarative config for a cluster",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
var configAccess clientcmd.ConfigAccess = pathOpts
|
||||
if len(args) == 0 {
|
||||
log.Error("Choose a context name from:")
|
||||
cmdutil.PrintKubeContexts(configAccess)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfgAccess, err := configAccess.GetStartingConfig()
|
||||
errors.CheckError(err)
|
||||
contextName := args[0]
|
||||
clstContext := cfgAccess.Contexts[contextName]
|
||||
if clstContext == nil {
|
||||
log.Fatalf("Context %s does not exist in kubeconfig", contextName)
|
||||
}
|
||||
|
||||
overrides := clientcmd.ConfigOverrides{
|
||||
Context: *clstContext,
|
||||
}
|
||||
clientConfig := clientcmd.NewDefaultClientConfig(*cfgAccess, &overrides)
|
||||
conf, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
kubeClientset := fake.NewSimpleClientset()
|
||||
|
||||
var awsAuthConf *argoappv1.AWSAuthConfig
|
||||
var execProviderConf *argoappv1.ExecProviderConfig
|
||||
if clusterOpts.AwsClusterName != "" {
|
||||
awsAuthConf = &argoappv1.AWSAuthConfig{
|
||||
ClusterName: clusterOpts.AwsClusterName,
|
||||
RoleARN: clusterOpts.AwsRoleArn,
|
||||
}
|
||||
} else if clusterOpts.ExecProviderCommand != "" {
|
||||
execProviderConf = &argoappv1.ExecProviderConfig{
|
||||
Command: clusterOpts.ExecProviderCommand,
|
||||
Args: clusterOpts.ExecProviderArgs,
|
||||
Env: clusterOpts.ExecProviderEnv,
|
||||
APIVersion: clusterOpts.ExecProviderAPIVersion,
|
||||
InstallHint: clusterOpts.ExecProviderInstallHint,
|
||||
}
|
||||
} else if bearerToken == "" {
|
||||
bearerToken = "bearer-token"
|
||||
}
|
||||
if clusterOpts.Name != "" {
|
||||
contextName = clusterOpts.Name
|
||||
}
|
||||
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, conf, bearerToken, awsAuthConf, execProviderConf)
|
||||
if clusterOpts.InCluster {
|
||||
clst.Server = common.KubernetesInternalAPIServerAddr
|
||||
}
|
||||
if clusterOpts.Shard >= 0 {
|
||||
clst.Shard = &clusterOpts.Shard
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace)
|
||||
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
|
||||
|
||||
_, err = argoDB.CreateCluster(context.Background(), clst)
|
||||
errors.CheckError(err)
|
||||
|
||||
secName, err := db.ServerToSecretName(clst.Server)
|
||||
errors.CheckError(err)
|
||||
|
||||
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), secName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
|
||||
cmdutil.ConvertSecretData(secret)
|
||||
var printResources []interface{}
|
||||
printResources = append(printResources, secret)
|
||||
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
|
||||
},
|
||||
}
|
||||
command.PersistentFlags().StringVar(&pathOpts.LoadingRules.ExplicitPath, pathOpts.ExplicitFileFlag, pathOpts.LoadingRules.ExplicitPath, "use a particular kubeconfig file")
|
||||
command.Flags().StringVar(&bearerToken, "bearer-token", "", "Authentication token that should be used to access K8S API server")
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
cmdutil.AddClusterFlags(command, &clusterOpts)
|
||||
return command
|
||||
}
|
||||
|
||||
func NewGenRepoConfigCommand() *cobra.Command {
|
||||
var (
|
||||
repoOpts cmdutil.RepoOptions
|
||||
outputFormat string
|
||||
)
|
||||
|
||||
// For better readability and easier formatting
|
||||
var repoAddExamples = `
|
||||
# Add a Git repository via SSH using a private key for authentication, ignoring the server's host key:
|
||||
argocd-util config repo git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
|
||||
|
||||
# Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here
|
||||
argocd-util config repo ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa
|
||||
|
||||
# Add a private Git repository via HTTPS using username/password and TLS client certificates:
|
||||
argocd-util config repo https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key
|
||||
|
||||
# Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate
|
||||
argocd-util config repo https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
|
||||
|
||||
# Add a public Helm repository named 'stable' via HTTPS
|
||||
argocd-util config repo https://kubernetes-charts.storage.googleapis.com --type helm --name stable
|
||||
|
||||
# Add a private Helm repository named 'stable' via HTTPS
|
||||
argocd-util config repo https://kubernetes-charts.storage.googleapis.com --type helm --name stable --username test --password test
|
||||
|
||||
# Add a private Helm OCI-based repository named 'stable' via HTTPS
|
||||
argocd-util config repo helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
|
||||
`
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: "repo REPOURL",
|
||||
Short: "Generate declarative config for a repo",
|
||||
Example: repoAddExamples,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Repository URL
|
||||
repoOpts.Repo.Repo = args[0]
|
||||
|
||||
// Specifying ssh-private-key-path is only valid for SSH repositories
|
||||
if repoOpts.SshPrivateKeyPath != "" {
|
||||
if ok, _ := git.IsSSHURL(repoOpts.Repo.Repo); ok {
|
||||
keyData, err := ioutil.ReadFile(repoOpts.SshPrivateKeyPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
repoOpts.Repo.SSHPrivateKey = string(keyData)
|
||||
} else {
|
||||
err := fmt.Errorf("--ssh-private-key-path is only supported for SSH repositories.")
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// tls-client-cert-path and tls-client-cert-key-key-path must always be
|
||||
// specified together
|
||||
if (repoOpts.TlsClientCertPath != "" && repoOpts.TlsClientCertKeyPath == "") || (repoOpts.TlsClientCertPath == "" && repoOpts.TlsClientCertKeyPath != "") {
|
||||
err := fmt.Errorf("--tls-client-cert-path and --tls-client-cert-key-path must be specified together")
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
// Specifying tls-client-cert-path is only valid for HTTPS repositories
|
||||
if repoOpts.TlsClientCertPath != "" {
|
||||
if git.IsHTTPSURL(repoOpts.Repo.Repo) {
|
||||
tlsCertData, err := ioutil.ReadFile(repoOpts.TlsClientCertPath)
|
||||
errors.CheckError(err)
|
||||
tlsCertKey, err := ioutil.ReadFile(repoOpts.TlsClientCertKeyPath)
|
||||
errors.CheckError(err)
|
||||
repoOpts.Repo.TLSClientCertData = string(tlsCertData)
|
||||
repoOpts.Repo.TLSClientCertKey = string(tlsCertKey)
|
||||
} else {
|
||||
err := fmt.Errorf("--tls-client-cert-path is only supported for HTTPS repositories")
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set repository connection properties only when creating repository, not
|
||||
// when creating repository credentials.
|
||||
// InsecureIgnoreHostKey is deprecated and only here for backwards compat
|
||||
repoOpts.Repo.InsecureIgnoreHostKey = repoOpts.InsecureIgnoreHostKey
|
||||
repoOpts.Repo.Insecure = repoOpts.InsecureSkipServerVerification
|
||||
repoOpts.Repo.EnableLFS = repoOpts.EnableLfs
|
||||
repoOpts.Repo.EnableOCI = repoOpts.EnableOci
|
||||
|
||||
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
|
||||
errors.CheckError(fmt.Errorf("must specify --name for repos of type 'helm'"))
|
||||
}
|
||||
|
||||
// If the user set a username, but didn't supply password via --password,
|
||||
// then we prompt for it
|
||||
if repoOpts.Repo.Username != "" && repoOpts.Repo.Password == "" {
|
||||
repoOpts.Repo.Password = cli.PromptPassword(repoOpts.Repo.Password)
|
||||
}
|
||||
|
||||
argoCDCM := &apiv1.ConfigMap{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: "ConfigMap",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: ArgoCDNamespace,
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
}
|
||||
kubeClientset := fake.NewSimpleClientset(argoCDCM)
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace)
|
||||
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
|
||||
|
||||
var printResources []interface{}
|
||||
_, err := argoDB.CreateRepository(context.Background(), &repoOpts.Repo)
|
||||
errors.CheckError(err)
|
||||
|
||||
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), db.RepoURLToSecretName(repoSecretPrefix, repoOpts.Repo.Repo), v1.GetOptions{})
|
||||
if err != nil {
|
||||
if !apierr.IsNotFound(err) {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
} else {
|
||||
cmdutil.ConvertSecretData(secret)
|
||||
printResources = append(printResources, secret)
|
||||
}
|
||||
|
||||
cm, err := kubeClientset.CoreV1().ConfigMaps(ArgoCDNamespace).Get(context.Background(), common.ArgoCDConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
|
||||
printResources = append(printResources, cm)
|
||||
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
cmdutil.AddRepoFlags(command, &repoOpts)
|
||||
return command
|
||||
}
|
||||
@@ -14,17 +14,17 @@ import (
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"golang.org/x/term"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
accountpkg "github.com/argoproj/argo-cd/pkg/apiclient/account"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/session"
|
||||
"github.com/argoproj/argo-cd/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
sessionutil "github.com/argoproj/argo-cd/util/session"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
accountpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/account"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient/session"
|
||||
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
sessionutil "github.com/argoproj/argo-cd/v2/util/session"
|
||||
)
|
||||
|
||||
func NewAccountCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
@@ -54,7 +54,19 @@ func NewAccountUpdatePasswordCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "update-password",
|
||||
Short: "Update password",
|
||||
Short: "Update an account's password",
|
||||
Long: `
|
||||
This command can be used to update the password of the currently logged on
|
||||
user, or an arbitrary local user account when the currently logged on user
|
||||
has appropriate RBAC permissions to change other accounts.
|
||||
`,
|
||||
Example: `
|
||||
# Update the current user's password
|
||||
argocd account update-password
|
||||
|
||||
# Update the password for user foobar
|
||||
argocd account update-password --account foobar
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -67,16 +79,20 @@ func NewAccountUpdatePasswordCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
userInfo := getCurrentAccount(acdClient)
|
||||
|
||||
if userInfo.Iss == sessionutil.SessionManagerClaimsIssuer && currentPassword == "" {
|
||||
fmt.Print("*** Enter current password: ")
|
||||
password, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
||||
fmt.Printf("*** Enter password of currently logged in user (%s): ", userInfo.Username)
|
||||
password, err := term.ReadPassword(int(os.Stdin.Fd()))
|
||||
errors.CheckError(err)
|
||||
currentPassword = string(password)
|
||||
fmt.Print("\n")
|
||||
}
|
||||
|
||||
if account == "" {
|
||||
account = userInfo.Username
|
||||
}
|
||||
|
||||
if newPassword == "" {
|
||||
var err error
|
||||
newPassword, err = cli.ReadAndConfirmPassword()
|
||||
newPassword, err = cli.ReadAndConfirmPassword(account)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
@@ -99,7 +115,7 @@ func NewAccountUpdatePasswordCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
errors.CheckError(err)
|
||||
claims, err := configCtx.User.Claims()
|
||||
errors.CheckError(err)
|
||||
tokenString := passwordLogin(acdClient, claims.Subject, newPassword)
|
||||
tokenString := passwordLogin(acdClient, localconfig.GetUsername(claims.Subject), newPassword)
|
||||
localCfg.UpsertUser(localconfig.User{
|
||||
Name: localCfg.CurrentContext,
|
||||
AuthToken: tokenString,
|
||||
@@ -111,7 +127,7 @@ func NewAccountUpdatePasswordCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
},
|
||||
}
|
||||
|
||||
command.Flags().StringVar(¤tPassword, "current-password", "", "current password you wish to change")
|
||||
command.Flags().StringVar(¤tPassword, "current-password", "", "password of the currently logged on user")
|
||||
command.Flags().StringVar(&newPassword, "new-password", "", "new password you want to update to")
|
||||
command.Flags().StringVar(&account, "account", "", "an account name that should be updated. Defaults to current user account")
|
||||
return command
|
||||
@@ -362,7 +378,7 @@ argocd account generate-token --account <account-name>`,
|
||||
}
|
||||
cmd.Flags().StringVarP(&account, "account", "a", "", "Account name. Defaults to the current account.")
|
||||
cmd.Flags().StringVarP(&expiresIn, "expires-in", "e", "0s", "Duration before the token will expire. (Default: No expiration)")
|
||||
cmd.Flags().StringVar(&id, "id", "", "Optional token id. Fallback to uuid if not value specified.")
|
||||
cmd.Flags().StringVar(&id, "id", "", "Optional token id. Fall back to uuid if not value specified.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -377,7 +393,7 @@ func NewAccountDeleteTokenCommand(clientOpts *argocdclient.ClientOptions) *cobra
|
||||
argocd account delete-token ID
|
||||
|
||||
# Delete token of the account with the specified name
|
||||
argocd account generate-token --account <account-name>`,
|
||||
argocd account delete-token --account <account-name> ID`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
|
||||
244
cmd/argocd/commands/admin/admin.go
Normal file
244
cmd/argocd/commands/admin/admin.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
const (
|
||||
// YamlSeparator separates sections of a YAML file
|
||||
yamlSeparator = "---\n"
|
||||
)
|
||||
|
||||
var (
|
||||
configMapResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"}
|
||||
secretResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "secrets"}
|
||||
applicationsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
|
||||
appprojectsResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "appprojects"}
|
||||
appplicationSetResource = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applicationsets"}
|
||||
)
|
||||
|
||||
// NewAdminCommand returns a new instance of an argocd command
|
||||
func NewAdminCommand() *cobra.Command {
|
||||
var (
|
||||
pathOpts = clientcmd.NewDefaultPathOptions()
|
||||
)
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: "admin",
|
||||
Short: "Contains a set of commands useful for Argo CD administrators and requires direct Kubernetes access",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewClusterCommand(pathOpts))
|
||||
command.AddCommand(NewProjectsCommand())
|
||||
command.AddCommand(NewSettingsCommand())
|
||||
command.AddCommand(NewAppCommand())
|
||||
command.AddCommand(NewRepoCommand())
|
||||
command.AddCommand(NewImportCommand())
|
||||
command.AddCommand(NewExportCommand())
|
||||
command.AddCommand(NewDashboardCommand())
|
||||
command.AddCommand(NewNotificationsCommand())
|
||||
|
||||
command.Flags().StringVar(&cmdutil.LogFormat, "logformat", "text", "Set the logging format. One of: text|json")
|
||||
command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", "info", "Set the logging level. One of: debug|info|warn|error")
|
||||
return command
|
||||
}
|
||||
|
||||
type argoCDClientsets struct {
|
||||
configMaps dynamic.ResourceInterface
|
||||
secrets dynamic.ResourceInterface
|
||||
applications dynamic.ResourceInterface
|
||||
projects dynamic.ResourceInterface
|
||||
applicationSets dynamic.ResourceInterface
|
||||
}
|
||||
|
||||
func newArgoCDClientsets(config *rest.Config, namespace string) *argoCDClientsets {
|
||||
dynamicIf, err := dynamic.NewForConfig(config)
|
||||
errors.CheckError(err)
|
||||
return &argoCDClientsets{
|
||||
configMaps: dynamicIf.Resource(configMapResource).Namespace(namespace),
|
||||
secrets: dynamicIf.Resource(secretResource).Namespace(namespace),
|
||||
applications: dynamicIf.Resource(applicationsResource).Namespace(namespace),
|
||||
projects: dynamicIf.Resource(appprojectsResource).Namespace(namespace),
|
||||
applicationSets: dynamicIf.Resource(appplicationSetResource).Namespace(namespace),
|
||||
}
|
||||
}
|
||||
|
||||
// getReferencedSecrets examines the argocd-cm config for any referenced repo secrets and returns a
|
||||
// map of all referenced secrets.
|
||||
func getReferencedSecrets(un unstructured.Unstructured) map[string]bool {
|
||||
var cm apiv1.ConfigMap
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(un.Object, &cm)
|
||||
errors.CheckError(err)
|
||||
referencedSecrets := make(map[string]bool)
|
||||
|
||||
// Referenced repository secrets
|
||||
if reposRAW, ok := cm.Data["repositories"]; ok {
|
||||
repos := make([]settings.Repository, 0)
|
||||
err := yaml.Unmarshal([]byte(reposRAW), &repos)
|
||||
errors.CheckError(err)
|
||||
for _, cred := range repos {
|
||||
if cred.PasswordSecret != nil {
|
||||
referencedSecrets[cred.PasswordSecret.Name] = true
|
||||
}
|
||||
if cred.SSHPrivateKeySecret != nil {
|
||||
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
|
||||
}
|
||||
if cred.UsernameSecret != nil {
|
||||
referencedSecrets[cred.UsernameSecret.Name] = true
|
||||
}
|
||||
if cred.TLSClientCertDataSecret != nil {
|
||||
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
|
||||
}
|
||||
if cred.TLSClientCertKeySecret != nil {
|
||||
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Referenced repository credentials secrets
|
||||
if reposRAW, ok := cm.Data["repository.credentials"]; ok {
|
||||
creds := make([]settings.RepositoryCredentials, 0)
|
||||
err := yaml.Unmarshal([]byte(reposRAW), &creds)
|
||||
errors.CheckError(err)
|
||||
for _, cred := range creds {
|
||||
if cred.PasswordSecret != nil {
|
||||
referencedSecrets[cred.PasswordSecret.Name] = true
|
||||
}
|
||||
if cred.SSHPrivateKeySecret != nil {
|
||||
referencedSecrets[cred.SSHPrivateKeySecret.Name] = true
|
||||
}
|
||||
if cred.UsernameSecret != nil {
|
||||
referencedSecrets[cred.UsernameSecret.Name] = true
|
||||
}
|
||||
if cred.TLSClientCertDataSecret != nil {
|
||||
referencedSecrets[cred.TLSClientCertDataSecret.Name] = true
|
||||
}
|
||||
if cred.TLSClientCertKeySecret != nil {
|
||||
referencedSecrets[cred.TLSClientCertKeySecret.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return referencedSecrets
|
||||
}
|
||||
|
||||
// isArgoCDSecret returns whether or not the given secret is a part of Argo CD configuration
|
||||
// (e.g. argocd-secret, repo credentials, or cluster credentials)
|
||||
func isArgoCDSecret(repoSecretRefs map[string]bool, un unstructured.Unstructured) bool {
|
||||
secretName := un.GetName()
|
||||
if secretName == common.ArgoCDSecretName {
|
||||
return true
|
||||
}
|
||||
if repoSecretRefs != nil {
|
||||
if _, ok := repoSecretRefs[secretName]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if labels := un.GetLabels(); labels != nil {
|
||||
if _, ok := labels[common.LabelKeySecretType]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if annotations := un.GetAnnotations(); annotations != nil {
|
||||
if annotations[common.AnnotationKeyManagedBy] == common.AnnotationValueManagedByArgoCD {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isArgoCDConfigMap returns true if the configmap name is one of argo cd's well known configmaps
|
||||
func isArgoCDConfigMap(name string) bool {
|
||||
switch name {
|
||||
case common.ArgoCDConfigMapName, common.ArgoCDRBACConfigMapName, common.ArgoCDKnownHostsConfigMapName, common.ArgoCDTLSCertsConfigMapName:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
// specsEqual returns if the spec, data, labels, annotations, and finalizers of the two
|
||||
// supplied objects are equal, indicating that no update is necessary during importing
|
||||
func specsEqual(left, right unstructured.Unstructured) bool {
|
||||
if !reflect.DeepEqual(left.GetAnnotations(), right.GetAnnotations()) {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(left.GetLabels(), right.GetLabels()) {
|
||||
return false
|
||||
}
|
||||
if !reflect.DeepEqual(left.GetFinalizers(), right.GetFinalizers()) {
|
||||
return false
|
||||
}
|
||||
switch left.GetKind() {
|
||||
case "Secret", "ConfigMap":
|
||||
leftData, _, _ := unstructured.NestedMap(left.Object, "data")
|
||||
rightData, _, _ := unstructured.NestedMap(right.Object, "data")
|
||||
return reflect.DeepEqual(leftData, rightData)
|
||||
case "AppProject":
|
||||
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
|
||||
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
|
||||
return reflect.DeepEqual(leftSpec, rightSpec)
|
||||
case "Application":
|
||||
leftSpec, _, _ := unstructured.NestedMap(left.Object, "spec")
|
||||
rightSpec, _, _ := unstructured.NestedMap(right.Object, "spec")
|
||||
leftStatus, _, _ := unstructured.NestedMap(left.Object, "status")
|
||||
rightStatus, _, _ := unstructured.NestedMap(right.Object, "status")
|
||||
// reconciledAt and observedAt are constantly changing and we ignore any diff there
|
||||
delete(leftStatus, "reconciledAt")
|
||||
delete(rightStatus, "reconciledAt")
|
||||
delete(leftStatus, "observedAt")
|
||||
delete(rightStatus, "observedAt")
|
||||
return reflect.DeepEqual(leftSpec, rightSpec) && reflect.DeepEqual(leftStatus, rightStatus)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func iterateStringFields(obj interface{}, callback func(name string, val string) string) {
|
||||
if mapField, ok := obj.(map[string]interface{}); ok {
|
||||
for field, val := range mapField {
|
||||
if strVal, ok := val.(string); ok {
|
||||
mapField[field] = callback(field, strVal)
|
||||
} else {
|
||||
iterateStringFields(val, callback)
|
||||
}
|
||||
}
|
||||
} else if arrayField, ok := obj.([]interface{}); ok {
|
||||
for i := range arrayField {
|
||||
iterateStringFields(arrayField[i], callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func redactor(dirtyString string) string {
|
||||
config := make(map[string]interface{})
|
||||
err := yaml.Unmarshal([]byte(dirtyString), &config)
|
||||
errors.CheckError(err)
|
||||
iterateStringFields(config, func(name string, val string) string {
|
||||
if name == "clientSecret" || name == "secret" || name == "bindPW" {
|
||||
return "********"
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
})
|
||||
data, err := yaml.Marshal(config)
|
||||
errors.CheckError(err)
|
||||
return string(data)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -22,37 +22,107 @@ import (
|
||||
kubecache "k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller"
|
||||
"github.com/argoproj/argo-cd/controller/cache"
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
appinformers "github.com/argoproj/argo-cd/pkg/client/informers/externalversions"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
cacheutil "github.com/argoproj/argo-cd/util/cache"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
kubeutil "github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/controller"
|
||||
"github.com/argoproj/argo-cd/v2/controller/cache"
|
||||
"github.com/argoproj/argo-cd/v2/controller/metrics"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
appinformers "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/config"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
func NewAppsCommand() *cobra.Command {
|
||||
func NewAppCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "apps",
|
||||
Short: "Utility commands operate on ArgoCD applications",
|
||||
Use: "app",
|
||||
Short: "Manage applications configuration",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewGenAppSpecCommand())
|
||||
command.AddCommand(NewReconcileCommand())
|
||||
command.AddCommand(NewDiffReconcileResults())
|
||||
return command
|
||||
}
|
||||
|
||||
// NewGenAppSpecCommand generates declarative configuration file for given application
|
||||
func NewGenAppSpecCommand() *cobra.Command {
|
||||
var (
|
||||
appOpts cmdutil.AppOptions
|
||||
fileURL string
|
||||
appName string
|
||||
labels []string
|
||||
outputFormat string
|
||||
annotations []string
|
||||
inline bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "generate-spec APPNAME",
|
||||
Short: "Generate declarative config for an application",
|
||||
Example: `
|
||||
# Generate declarative config for a directory app
|
||||
argocd admin app generate-spec guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --directory-recurse
|
||||
|
||||
# Generate declarative config for a Jsonnet app
|
||||
argocd admin app generate-spec jsonnet-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path jsonnet-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --jsonnet-ext-str replicas=2
|
||||
|
||||
# Generate declarative config for a Helm app
|
||||
argocd admin app generate-spec helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2
|
||||
|
||||
# Generate declarative config for a Helm app from a Helm repo
|
||||
argocd admin app generate-spec nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
|
||||
|
||||
# Generate declarative config for a Kustomize app
|
||||
argocd admin app generate-spec kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||
|
||||
# Generate declarative config for a app using a custom tool:
|
||||
argocd admin app generate-spec ksane --repo https://github.com/argoproj/argocd-example-apps.git --path plugins/kasane --dest-namespace default --dest-server https://kubernetes.default.svc --config-management-plugin kasane
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
apps, err := cmdutil.ConstructApps(fileURL, appName, labels, annotations, args, appOpts, c.Flags())
|
||||
errors.CheckError(err)
|
||||
if len(apps) > 1 {
|
||||
errors.CheckError(fmt.Errorf("failed to generate spec, more than one application is not supported"))
|
||||
}
|
||||
app := apps[0]
|
||||
if app.Name == "" {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
out, closer, err := getOutWriter(inline, fileURL)
|
||||
errors.CheckError(err)
|
||||
defer io.Close(closer)
|
||||
|
||||
errors.CheckError(PrintResources(outputFormat, out, app))
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&appName, "name", "", "A name for the app, ignored if a file is set (DEPRECATED)")
|
||||
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the app")
|
||||
command.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "Labels to apply to the app")
|
||||
command.Flags().StringArrayVarP(&annotations, "annotations", "", []string{}, "Set metadata annotations (e.g. example=value)")
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
command.Flags().BoolVarP(&inline, "inline", "i", false, "If set then generated resource is written back to the file specified in --file flag")
|
||||
|
||||
// Only complete files with appropriate extension.
|
||||
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
|
||||
errors.CheckError(err)
|
||||
|
||||
cmdutil.AddAppFlags(command, &appOpts)
|
||||
return command
|
||||
}
|
||||
|
||||
type appReconcileResult struct {
|
||||
Name string `json:"name"`
|
||||
Health *v1alpha1.HealthStatus `json:"health"`
|
||||
@@ -178,7 +248,7 @@ func NewReconcileCommand() *cobra.Command {
|
||||
}
|
||||
outputPath := args[0]
|
||||
|
||||
errors.CheckError(os.Setenv(common.EnvVarFakeInClusterConfig, "true"))
|
||||
errors.CheckError(os.Setenv(v1alpha1.EnvVarFakeInClusterConfig, "true"))
|
||||
cfg, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
@@ -188,11 +258,12 @@ func NewReconcileCommand() *cobra.Command {
|
||||
if refresh {
|
||||
if repoServerAddress == "" {
|
||||
printLine("Repo server is not provided, trying to port-forward to argocd-repo-server pod.")
|
||||
repoServerPort, err := kubeutil.PortForward("app.kubernetes.io/name=argocd-repo-server", 8081, namespace)
|
||||
overrides := clientcmd.ConfigOverrides{}
|
||||
repoServerPort, err := kubeutil.PortForward(8081, namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server")
|
||||
errors.CheckError(err)
|
||||
repoServerAddress = fmt.Sprintf("localhost:%d", repoServerPort)
|
||||
}
|
||||
repoServerClient := apiclient.NewRepoServerClientset(repoServerAddress, 60)
|
||||
repoServerClient := apiclient.NewRepoServerClientset(repoServerAddress, 60, apiclient.TLSConfiguration{DisableTLS: false, StrictValidation: false})
|
||||
|
||||
appClientset := appclientset.NewForConfigOrDie(cfg)
|
||||
kubeClientset := kubernetes.NewForConfigOrDie(cfg)
|
||||
@@ -263,11 +334,11 @@ func reconcileApplications(
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, namespace)
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClientset)
|
||||
appInformerFactory := appinformers.NewFilteredSharedInformerFactory(
|
||||
appInformerFactory := appinformers.NewSharedInformerFactoryWithOptions(
|
||||
appClientset,
|
||||
1*time.Hour,
|
||||
namespace,
|
||||
func(options *v1.ListOptions) {},
|
||||
appinformers.WithNamespace(namespace),
|
||||
appinformers.WithTweakListOptions(func(options *v1.ListOptions) {}),
|
||||
)
|
||||
|
||||
appInformer := appInformerFactory.Argoproj().V1alpha1().Applications().Informer()
|
||||
@@ -284,7 +355,7 @@ func reconcileApplications(
|
||||
return true
|
||||
}, func(r *http.Request) error {
|
||||
return nil
|
||||
})
|
||||
}, []string{})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -300,7 +371,7 @@ func reconcileApplications(
|
||||
)
|
||||
|
||||
appStateManager := controller.NewAppStateManager(
|
||||
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second)
|
||||
argoDB, appClientset, repoServerClient, namespace, kubeutil.NewKubectl(), settingsMgr, stateCache, projInformer, server, cache, time.Second, argo.NewResourceTracking())
|
||||
|
||||
appsList, err := appClientset.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{LabelSelector: selector})
|
||||
if err != nil {
|
||||
@@ -330,7 +401,7 @@ func reconcileApplications(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := appStateManager.CompareAppState(&app, proj, app.Spec.Source.TargetRevision, app.Spec.Source, false, nil)
|
||||
res := appStateManager.CompareAppState(&app, proj, app.Spec.Source.TargetRevision, app.Spec.Source, false, false, nil)
|
||||
items = append(items, appReconcileResult{
|
||||
Name: app.Name,
|
||||
Conditions: app.Status.Conditions,
|
||||
@@ -342,5 +413,5 @@ func reconcileApplications(
|
||||
}
|
||||
|
||||
func newLiveStateCache(argoDB db.ArgoDB, appInformer kubecache.SharedIndexInformer, settingsMgr *settings.SettingsManager, server *metrics.MetricsServer) cache.LiveStateCache {
|
||||
return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, nil)
|
||||
return cache.NewLiveStateCache(argoDB, appInformer, settingsMgr, kubeutil.NewKubectl(), server, func(managedByApp map[string]bool, ref apiv1.ObjectReference) {}, nil, argo.NewResourceTracking())
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/test"
|
||||
"github.com/argoproj/argo-cd/v2/test"
|
||||
|
||||
clustermocks "github.com/argoproj/gitops-engine/pkg/cache/mocks"
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
@@ -16,16 +16,15 @@ import (
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
statecache "github.com/argoproj/argo-cd/controller/cache"
|
||||
cachemocks "github.com/argoproj/argo-cd/controller/cache/mocks"
|
||||
"github.com/argoproj/argo-cd/controller/metrics"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appfake "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/reposerver/apiclient/mocks"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
statecache "github.com/argoproj/argo-cd/v2/controller/cache"
|
||||
cachemocks "github.com/argoproj/argo-cd/v2/controller/cache/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/controller/metrics"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appfake "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
func TestGetReconcileResults(t *testing.T) {
|
||||
@@ -79,7 +78,7 @@ func TestGetReconcileResults_Refresh(t *testing.T) {
|
||||
Spec: v1alpha1.ApplicationSpec{
|
||||
Project: "default",
|
||||
Destination: v1alpha1.ApplicationDestination{
|
||||
Server: common.KubernetesInternalAPIServerAddr,
|
||||
Server: v1alpha1.KubernetesInternalAPIServerAddr,
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
369
cmd/argocd/commands/admin/backup.go
Normal file
369
cmd/argocd/commands/admin/backup.go
Normal file
@@ -0,0 +1,369 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
)
|
||||
|
||||
// NewExportCommand defines a new command for exporting Kubernetes and Argo CD resources.
|
||||
func NewExportCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
out string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Export all Argo CD data to stdout (default) or a file",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
var writer io.Writer
|
||||
if out == "-" {
|
||||
writer = os.Stdout
|
||||
} else {
|
||||
f, err := os.Create(out)
|
||||
errors.CheckError(err)
|
||||
bw := bufio.NewWriter(f)
|
||||
writer = bw
|
||||
defer func() {
|
||||
err = bw.Flush()
|
||||
errors.CheckError(err)
|
||||
err = f.Close()
|
||||
errors.CheckError(err)
|
||||
}()
|
||||
}
|
||||
|
||||
acdClients := newArgoCDClientsets(config, namespace)
|
||||
acdConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdConfigMap)
|
||||
acdRBACConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDRBACConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdRBACConfigMap)
|
||||
acdKnownHostsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDKnownHostsConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdKnownHostsConfigMap)
|
||||
acdTLSCertsConfigMap, err := acdClients.configMaps.Get(context.Background(), common.ArgoCDTLSCertsConfigMapName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
export(writer, *acdTLSCertsConfigMap)
|
||||
|
||||
referencedSecrets := getReferencedSecrets(*acdConfigMap)
|
||||
secrets, err := acdClients.secrets.List(context.Background(), v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, secret := range secrets.Items {
|
||||
if isArgoCDSecret(referencedSecrets, secret) {
|
||||
export(writer, secret)
|
||||
}
|
||||
}
|
||||
projects, err := acdClients.projects.List(context.Background(), v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, proj := range projects.Items {
|
||||
export(writer, proj)
|
||||
}
|
||||
applications, err := acdClients.applications.List(context.Background(), v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, app := range applications.Items {
|
||||
export(writer, app)
|
||||
}
|
||||
applicationSets, err := acdClients.applicationSets.List(context.Background(), v1.ListOptions{})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
if apierr.IsForbidden(err) {
|
||||
log.Warn(err)
|
||||
} else {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
if applicationSets != nil {
|
||||
for _, appSet := range applicationSets.Items {
|
||||
export(writer, appSet)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().StringVarP(&out, "out", "o", "-", "Output to the specified file instead of stdout")
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
// NewImportCommand defines a new command for exporting Kubernetes and Argo CD resources.
|
||||
func NewImportCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
prune bool
|
||||
dryRun bool
|
||||
verbose bool
|
||||
stopOperation bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "import SOURCE",
|
||||
Short: "Import Argo CD data from stdin (specify `-') or a file",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
config.QPS = 100
|
||||
config.Burst = 50
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
acdClients := newArgoCDClientsets(config, namespace)
|
||||
|
||||
var input []byte
|
||||
if in := args[0]; in == "-" {
|
||||
input, err = ioutil.ReadAll(os.Stdin)
|
||||
} else {
|
||||
input, err = ioutil.ReadFile(in)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
var dryRunMsg string
|
||||
if dryRun {
|
||||
dryRunMsg = " (dry run)"
|
||||
}
|
||||
|
||||
// pruneObjects tracks live objects and it's current resource version. any remaining
|
||||
// items in this map indicates the resource should be pruned since it no longer appears
|
||||
// in the backup
|
||||
pruneObjects := make(map[kube.ResourceKey]unstructured.Unstructured)
|
||||
configMaps, err := acdClients.configMaps.List(context.Background(), v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
// referencedSecrets holds any secrets referenced in the argocd-cm configmap. These
|
||||
// secrets need to be imported too
|
||||
var referencedSecrets map[string]bool
|
||||
for _, cm := range configMaps.Items {
|
||||
if isArgoCDConfigMap(cm.GetName()) {
|
||||
pruneObjects[kube.ResourceKey{Group: "", Kind: "ConfigMap", Name: cm.GetName()}] = cm
|
||||
}
|
||||
if cm.GetName() == common.ArgoCDConfigMapName {
|
||||
referencedSecrets = getReferencedSecrets(cm)
|
||||
}
|
||||
}
|
||||
|
||||
secrets, err := acdClients.secrets.List(context.Background(), v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, secret := range secrets.Items {
|
||||
if isArgoCDSecret(referencedSecrets, secret) {
|
||||
pruneObjects[kube.ResourceKey{Group: "", Kind: "Secret", Name: secret.GetName()}] = secret
|
||||
}
|
||||
}
|
||||
applications, err := acdClients.applications.List(context.Background(), v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, app := range applications.Items {
|
||||
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "Application", Name: app.GetName()}] = app
|
||||
}
|
||||
projects, err := acdClients.projects.List(context.Background(), v1.ListOptions{})
|
||||
errors.CheckError(err)
|
||||
for _, proj := range projects.Items {
|
||||
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "AppProject", Name: proj.GetName()}] = proj
|
||||
}
|
||||
applicationSets, err := acdClients.applicationSets.List(context.Background(), v1.ListOptions{})
|
||||
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
|
||||
log.Warnf("argoproj.io/ApplicationSet: %v\n", err)
|
||||
} else {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
if applicationSets != nil {
|
||||
for _, appSet := range applicationSets.Items {
|
||||
pruneObjects[kube.ResourceKey{Group: "argoproj.io", Kind: "ApplicationSet", Name: appSet.GetName()}] = appSet
|
||||
}
|
||||
}
|
||||
|
||||
// Create or replace existing object
|
||||
backupObjects, err := kube.SplitYAML(input)
|
||||
errors.CheckError(err)
|
||||
for _, bakObj := range backupObjects {
|
||||
gvk := bakObj.GroupVersionKind()
|
||||
key := kube.ResourceKey{Group: gvk.Group, Kind: gvk.Kind, Name: bakObj.GetName()}
|
||||
liveObj, exists := pruneObjects[key]
|
||||
delete(pruneObjects, key)
|
||||
var dynClient dynamic.ResourceInterface
|
||||
switch bakObj.GetKind() {
|
||||
case "Secret":
|
||||
dynClient = acdClients.secrets
|
||||
case "ConfigMap":
|
||||
dynClient = acdClients.configMaps
|
||||
case "AppProject":
|
||||
dynClient = acdClients.projects
|
||||
case "Application":
|
||||
dynClient = acdClients.applications
|
||||
case "ApplicationSet":
|
||||
dynClient = acdClients.applicationSets
|
||||
}
|
||||
if !exists {
|
||||
isForbidden := false
|
||||
if !dryRun {
|
||||
_, err = dynClient.Create(context.Background(), bakObj, v1.CreateOptions{})
|
||||
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
|
||||
isForbidden = true
|
||||
log.Warnf("%s/%s %s: %v", gvk.Group, gvk.Kind, bakObj.GetName(), err)
|
||||
} else {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
if !isForbidden {
|
||||
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
}
|
||||
|
||||
} else if specsEqual(*bakObj, liveObj) && checkAppHasNoNeedToStopOperation(liveObj, stopOperation) {
|
||||
if verbose {
|
||||
fmt.Printf("%s/%s %s unchanged%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
}
|
||||
} else {
|
||||
isForbidden := false
|
||||
if !dryRun {
|
||||
newLive := updateLive(bakObj, &liveObj, stopOperation)
|
||||
_, err = dynClient.Update(context.Background(), newLive, v1.UpdateOptions{})
|
||||
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
|
||||
isForbidden = true
|
||||
log.Warnf("%s/%s %s: %v", gvk.Group, gvk.Kind, bakObj.GetName(), err)
|
||||
} else {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
if !isForbidden {
|
||||
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete objects not in backup
|
||||
for key, liveObj := range pruneObjects {
|
||||
if prune {
|
||||
var dynClient dynamic.ResourceInterface
|
||||
switch key.Kind {
|
||||
case "Secret":
|
||||
dynClient = acdClients.secrets
|
||||
case "AppProject":
|
||||
dynClient = acdClients.projects
|
||||
case "Application":
|
||||
dynClient = acdClients.applications
|
||||
if !dryRun {
|
||||
if finalizers := liveObj.GetFinalizers(); len(finalizers) > 0 {
|
||||
newLive := liveObj.DeepCopy()
|
||||
newLive.SetFinalizers(nil)
|
||||
_, err = dynClient.Update(context.Background(), newLive, v1.UpdateOptions{})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
case "ApplicationSet":
|
||||
dynClient = acdClients.applicationSets
|
||||
default:
|
||||
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
|
||||
}
|
||||
isForbidden := false
|
||||
if !dryRun {
|
||||
err = dynClient.Delete(context.Background(), key.Name, v1.DeleteOptions{})
|
||||
if apierr.IsForbidden(err) || apierr.IsNotFound(err) {
|
||||
isForbidden = true
|
||||
log.Warnf("%s/%s %s: %v\n", key.Group, key.Kind, key.Name, err)
|
||||
} else {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
if !isForbidden {
|
||||
fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("%s/%s %s needs pruning\n", key.Group, key.Kind, key.Name)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", false, "Print what will be performed")
|
||||
command.Flags().BoolVar(&prune, "prune", false, "Prune secrets, applications and projects which do not appear in the backup")
|
||||
command.Flags().BoolVar(&verbose, "verbose", false, "Verbose output (versus only changed output)")
|
||||
command.Flags().BoolVar(&stopOperation, "stop-operation", false, "Stop any existing operations")
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
// check app has no need to stop operation.
|
||||
func checkAppHasNoNeedToStopOperation(liveObj unstructured.Unstructured, stopOperation bool) bool {
|
||||
if !stopOperation {
|
||||
return true
|
||||
}
|
||||
switch liveObj.GetKind() {
|
||||
case "Application":
|
||||
return liveObj.Object["operation"] == nil
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// export writes the unstructured object and removes extraneous cruft from output before writing
|
||||
func export(w io.Writer, un unstructured.Unstructured) {
|
||||
name := un.GetName()
|
||||
finalizers := un.GetFinalizers()
|
||||
apiVersion := un.GetAPIVersion()
|
||||
kind := un.GetKind()
|
||||
labels := un.GetLabels()
|
||||
annotations := un.GetAnnotations()
|
||||
unstructured.RemoveNestedField(un.Object, "metadata")
|
||||
un.SetName(name)
|
||||
un.SetFinalizers(finalizers)
|
||||
un.SetAPIVersion(apiVersion)
|
||||
un.SetKind(kind)
|
||||
un.SetLabels(labels)
|
||||
un.SetAnnotations(annotations)
|
||||
data, err := yaml.Marshal(un.Object)
|
||||
errors.CheckError(err)
|
||||
_, err = w.Write(data)
|
||||
errors.CheckError(err)
|
||||
_, err = w.Write([]byte(yamlSeparator))
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
// updateLive replaces the live object's finalizers, spec, annotations, labels, and data from the
|
||||
// backup object but leaves all other fields intact (status, other metadata, etc...)
|
||||
func updateLive(bak, live *unstructured.Unstructured, stopOperation bool) *unstructured.Unstructured {
|
||||
newLive := live.DeepCopy()
|
||||
newLive.SetAnnotations(bak.GetAnnotations())
|
||||
newLive.SetLabels(bak.GetLabels())
|
||||
newLive.SetFinalizers(bak.GetFinalizers())
|
||||
switch live.GetKind() {
|
||||
case "Secret", "ConfigMap":
|
||||
newLive.Object["data"] = bak.Object["data"]
|
||||
case "AppProject":
|
||||
newLive.Object["spec"] = bak.Object["spec"]
|
||||
case "Application":
|
||||
newLive.Object["spec"] = bak.Object["spec"]
|
||||
if _, ok := bak.Object["status"]; ok {
|
||||
newLive.Object["status"] = bak.Object["status"]
|
||||
}
|
||||
if stopOperation {
|
||||
newLive.Object["operation"] = nil
|
||||
}
|
||||
|
||||
case "ApplicationSet":
|
||||
newLive.Object["spec"] = bak.Object["spec"]
|
||||
}
|
||||
return newLive
|
||||
}
|
||||
617
cmd/argocd/commands/admin/cluster.go
Normal file
617
cmd/argocd/commands/admin/cluster.go
Normal file
@@ -0,0 +1,617 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/go-redis/redis/v8"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/controller/sharding"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/clusterauth"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/glob"
|
||||
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/text/label"
|
||||
)
|
||||
|
||||
func NewClusterCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "cluster",
|
||||
Short: "Manage clusters configuration",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewClusterConfig())
|
||||
command.AddCommand(NewGenClusterConfigCommand(pathOpts))
|
||||
command.AddCommand(NewClusterStatsCommand())
|
||||
command.AddCommand(NewClusterShardsCommand())
|
||||
namespacesCommand := NewClusterNamespacesCommand()
|
||||
namespacesCommand.AddCommand(NewClusterEnableNamespacedMode())
|
||||
namespacesCommand.AddCommand(NewClusterDisableNamespacedMode())
|
||||
command.AddCommand(namespacesCommand)
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
type ClusterWithInfo struct {
|
||||
argoappv1.Cluster
|
||||
// Shard holds controller shard number that handles the cluster
|
||||
Shard int
|
||||
// Namespaces holds list of namespaces managed by Argo CD in the cluster
|
||||
Namespaces []string
|
||||
}
|
||||
|
||||
func loadClusters(kubeClient *kubernetes.Clientset, appClient *versioned.Clientset, replicas int, namespace string, portForwardRedis bool, cacheSrc func() (*appstatecache.Cache, error), shard int) ([]ClusterWithInfo, error) {
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, namespace)
|
||||
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
|
||||
clustersList, err := argoDB.ListClusters(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var cache *appstatecache.Cache
|
||||
if portForwardRedis {
|
||||
overrides := clientcmd.ConfigOverrides{}
|
||||
port, err := kubeutil.PortForward(6379, namespace, &overrides,
|
||||
"app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", port)})
|
||||
cache = appstatecache.NewCache(cacheutil.NewCache(cacheutil.NewRedisCache(client, time.Hour)), time.Hour)
|
||||
} else {
|
||||
cache, err = cacheSrc()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apps := appItems.Items
|
||||
for i, app := range apps {
|
||||
err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, argoDB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apps[i] = app
|
||||
}
|
||||
clusters := make([]ClusterWithInfo, len(clustersList.Items))
|
||||
batchSize := 10
|
||||
batchesCount := int(math.Ceil(float64(len(clusters)) / float64(batchSize)))
|
||||
for batchNum := 0; batchNum < batchesCount; batchNum++ {
|
||||
batchStart := batchSize * batchNum
|
||||
batchEnd := batchSize * (batchNum + 1)
|
||||
if batchEnd > len(clustersList.Items) {
|
||||
batchEnd = len(clustersList.Items)
|
||||
}
|
||||
batch := clustersList.Items[batchStart:batchEnd]
|
||||
_ = kube.RunAllAsync(len(batch), func(i int) error {
|
||||
cluster := batch[i]
|
||||
clusterShard := 0
|
||||
if replicas > 0 {
|
||||
clusterShard = sharding.GetShardByID(cluster.ID, replicas)
|
||||
}
|
||||
|
||||
if shard != -1 && clusterShard != shard {
|
||||
return nil
|
||||
}
|
||||
nsSet := map[string]bool{}
|
||||
for _, app := range apps {
|
||||
if app.Spec.Destination.Server == cluster.Server {
|
||||
nsSet[app.Spec.Destination.Namespace] = true
|
||||
}
|
||||
}
|
||||
var namespaces []string
|
||||
for ns := range nsSet {
|
||||
namespaces = append(namespaces, ns)
|
||||
}
|
||||
_ = cache.GetClusterInfo(cluster.Server, &cluster.Info)
|
||||
clusters[batchStart+i] = ClusterWithInfo{cluster, clusterShard, namespaces}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return clusters, nil
|
||||
}
|
||||
|
||||
func getControllerReplicas(kubeClient *kubernetes.Clientset, namespace string) (int, error) {
|
||||
controllerPods, err := kubeClient.CoreV1().Pods(namespace).List(context.Background(), v1.ListOptions{
|
||||
LabelSelector: "app.kubernetes.io/name=argocd-application-controller"})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(controllerPods.Items), nil
|
||||
}
|
||||
|
||||
func NewClusterShardsCommand() *cobra.Command {
|
||||
var (
|
||||
shard int
|
||||
replicas int
|
||||
clientConfig clientcmd.ClientConfig
|
||||
cacheSrc func() (*appstatecache.Cache, error)
|
||||
portForwardRedis bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "shards",
|
||||
Short: "Print information about each controller shard and portion of Kubernetes resources it is responsible for.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.WarnLevel)
|
||||
|
||||
clientCfg, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
kubeClient := kubernetes.NewForConfigOrDie(clientCfg)
|
||||
appClient := versioned.NewForConfigOrDie(clientCfg)
|
||||
|
||||
if replicas == 0 {
|
||||
replicas, err = getControllerReplicas(kubeClient, namespace)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
if replicas == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
clusters, err := loadClusters(kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard)
|
||||
errors.CheckError(err)
|
||||
if len(clusters) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
printStatsSummary(clusters)
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().IntVar(&shard, "shard", -1, "Cluster shard filter")
|
||||
command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified")
|
||||
command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?")
|
||||
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command)
|
||||
return &command
|
||||
}
|
||||
|
||||
func printStatsSummary(clusters []ClusterWithInfo) {
|
||||
totalResourcesCount := int64(0)
|
||||
resourcesCountByShard := map[int]int64{}
|
||||
for _, c := range clusters {
|
||||
totalResourcesCount += c.Info.CacheInfo.ResourcesCount
|
||||
resourcesCountByShard[c.Shard] += c.Info.CacheInfo.ResourcesCount
|
||||
}
|
||||
|
||||
avgResourcesByShard := totalResourcesCount / int64(len(resourcesCountByShard))
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "SHARD\tRESOURCES COUNT\n")
|
||||
for shard := 0; shard < len(resourcesCountByShard); shard++ {
|
||||
cnt := resourcesCountByShard[shard]
|
||||
percent := (float64(cnt) / float64(avgResourcesByShard)) * 100.0
|
||||
_, _ = fmt.Fprintf(w, "%d\t%s\n", shard, fmt.Sprintf("%d (%.0f%%)", cnt, percent))
|
||||
}
|
||||
_ = w.Flush()
|
||||
}
|
||||
|
||||
func runClusterNamespacesCommand(clientConfig clientcmd.ClientConfig, action func(appClient *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error) error {
|
||||
clientCfg, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kubeClient := kubernetes.NewForConfigOrDie(clientCfg)
|
||||
appClient := versioned.NewForConfigOrDie(clientCfg)
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, namespace)
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
|
||||
clustersList, err := argoDB.ListClusters(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appItems, err := appClient.ArgoprojV1alpha1().Applications(namespace).List(context.Background(), v1.ListOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apps := appItems.Items
|
||||
for i, app := range apps {
|
||||
err := argo.ValidateDestination(context.Background(), &app.Spec.Destination, argoDB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apps[i] = app
|
||||
}
|
||||
|
||||
clusters := map[string][]string{}
|
||||
for _, cluster := range clustersList.Items {
|
||||
nsSet := map[string]bool{}
|
||||
for _, app := range apps {
|
||||
if app.Spec.Destination.Server != cluster.Server {
|
||||
continue
|
||||
}
|
||||
// Use namespaces of actually deployed resources, since some application use dummy target namespace
|
||||
// If resources list is empty then use target namespace
|
||||
if len(app.Status.Resources) != 0 {
|
||||
for _, res := range app.Status.Resources {
|
||||
if res.Namespace != "" {
|
||||
nsSet[res.Namespace] = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if app.Spec.Destination.Server == cluster.Server {
|
||||
nsSet[app.Spec.Destination.Namespace] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
var namespaces []string
|
||||
for ns := range nsSet {
|
||||
namespaces = append(namespaces, ns)
|
||||
}
|
||||
clusters[cluster.Server] = namespaces
|
||||
}
|
||||
return action(appClient, argoDB, clusters)
|
||||
}
|
||||
|
||||
func NewClusterNamespacesCommand() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "namespaces",
|
||||
Short: "Print information namespaces which Argo CD manages in each cluster.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.WarnLevel)
|
||||
|
||||
err := runClusterNamespacesCommand(clientConfig, func(appClient *versioned.Clientset, _ db.ArgoDB, clusters map[string][]string) error {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "CLUSTER\tNAMESPACES\n")
|
||||
|
||||
for cluster, namespaces := range clusters {
|
||||
// print shortest namespace names first
|
||||
sort.Slice(namespaces, func(i, j int) bool {
|
||||
return len(namespaces[j]) > len(namespaces[i])
|
||||
})
|
||||
namespacesStr := ""
|
||||
if len(namespaces) > 4 {
|
||||
namespacesStr = fmt.Sprintf("%s (total %d)", strings.Join(namespaces[:4], ","), len(namespaces))
|
||||
} else {
|
||||
namespacesStr = strings.Join(namespaces, ",")
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\n", cluster, namespacesStr)
|
||||
}
|
||||
_ = w.Flush()
|
||||
return nil
|
||||
})
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
return &command
|
||||
}
|
||||
|
||||
func NewClusterEnableNamespacedMode() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
dryRun bool
|
||||
clusterResources bool
|
||||
namespacesCount int
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "enable-namespaced-mode PATTERN",
|
||||
Short: "Enable namespaced mode for clusters which name matches to the specified pattern.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.WarnLevel)
|
||||
|
||||
if len(args) == 0 {
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
pattern := args[0]
|
||||
|
||||
errors.CheckError(runClusterNamespacesCommand(clientConfig, func(_ *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error {
|
||||
for server, namespaces := range clusters {
|
||||
if len(namespaces) == 0 || len(namespaces) > namespacesCount || !glob.Match(pattern, server) {
|
||||
continue
|
||||
}
|
||||
|
||||
cluster, err := argoDB.GetCluster(context.Background(), server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cluster.Namespaces = namespaces
|
||||
cluster.ClusterResources = clusterResources
|
||||
fmt.Printf("Setting cluster %s namespaces to %v...", server, namespaces)
|
||||
if !dryRun {
|
||||
_, err = argoDB.UpdateCluster(context.Background(), cluster)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("done")
|
||||
} else {
|
||||
fmt.Println("done (dry run)")
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", true, "Print what will be performed")
|
||||
command.Flags().BoolVar(&clusterResources, "cluster-resources", false, "Indicates if cluster level resources should be managed.")
|
||||
command.Flags().IntVar(&namespacesCount, "max-namespace-count", 0, "Max number of namespaces that cluster should managed managed namespaces is less or equal to specified count")
|
||||
|
||||
return &command
|
||||
}
|
||||
|
||||
func NewClusterDisableNamespacedMode() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
dryRun bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "disable-namespaced-mode PATTERN",
|
||||
Short: "Disable namespaced mode for clusters which name matches to the specified pattern.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.WarnLevel)
|
||||
|
||||
if len(args) == 0 {
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
pattern := args[0]
|
||||
|
||||
errors.CheckError(runClusterNamespacesCommand(clientConfig, func(_ *versioned.Clientset, argoDB db.ArgoDB, clusters map[string][]string) error {
|
||||
for server := range clusters {
|
||||
if !glob.Match(pattern, server) {
|
||||
continue
|
||||
}
|
||||
|
||||
cluster, err := argoDB.GetCluster(context.Background(), server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cluster.Namespaces) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cluster.Namespaces = nil
|
||||
fmt.Printf("Disabling namespaced mode for cluster %s...", server)
|
||||
if !dryRun {
|
||||
_, err = argoDB.UpdateCluster(context.Background(), cluster)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("done")
|
||||
} else {
|
||||
fmt.Println("done (dry run)")
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().BoolVar(&dryRun, "dry-run", true, "Print what will be performed")
|
||||
return &command
|
||||
}
|
||||
|
||||
func NewClusterStatsCommand() *cobra.Command {
|
||||
var (
|
||||
shard int
|
||||
replicas int
|
||||
clientConfig clientcmd.ClientConfig
|
||||
cacheSrc func() (*appstatecache.Cache, error)
|
||||
portForwardRedis bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "stats",
|
||||
Short: "Prints information cluster statistics and inferred shard number",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.SetLevel(log.WarnLevel)
|
||||
|
||||
clientCfg, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
kubeClient := kubernetes.NewForConfigOrDie(clientCfg)
|
||||
appClient := versioned.NewForConfigOrDie(clientCfg)
|
||||
if replicas == 0 {
|
||||
replicas, err = getControllerReplicas(kubeClient, namespace)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
clusters, err := loadClusters(kubeClient, appClient, replicas, namespace, portForwardRedis, cacheSrc, shard)
|
||||
errors.CheckError(err)
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "SERVER\tSHARD\tCONNECTION\tNAMESPACES COUNT\tAPPS COUNT\tRESOURCES COUNT\n")
|
||||
for _, cluster := range clusters {
|
||||
_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%d\t%d\t%d\n", cluster.Server, cluster.Shard, cluster.Info.ConnectionState.Status, len(cluster.Namespaces), cluster.Info.ApplicationsCount, cluster.Info.CacheInfo.ResourcesCount)
|
||||
}
|
||||
_ = w.Flush()
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(&command)
|
||||
command.Flags().IntVar(&shard, "shard", -1, "Cluster shard filter")
|
||||
command.Flags().IntVar(&replicas, "replicas", 0, "Application controller replicas count. Inferred from number of running controller pods if not specified")
|
||||
command.Flags().BoolVar(&portForwardRedis, "port-forward-redis", true, "Automatically port-forward ha proxy redis from current namespace?")
|
||||
cacheSrc = appstatecache.AddCacheFlagsToCmd(&command)
|
||||
return &command
|
||||
}
|
||||
|
||||
// NewClusterConfig returns a new instance of `argocd admin kubeconfig` command
|
||||
func NewClusterConfig() *cobra.Command {
|
||||
var (
|
||||
clientConfig clientcmd.ClientConfig
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "kubeconfig CLUSTER_URL OUTPUT_PATH",
|
||||
Short: "Generates kubeconfig for the specified cluster",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
serverUrl := args[0]
|
||||
output := args[1]
|
||||
conf, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
kubeclientset, err := kubernetes.NewForConfig(conf)
|
||||
errors.CheckError(err)
|
||||
|
||||
cluster, err := db.NewDB(namespace, settings.NewSettingsManager(context.Background(), kubeclientset, namespace), kubeclientset).GetCluster(context.Background(), serverUrl)
|
||||
errors.CheckError(err)
|
||||
err = kube.WriteKubeConfig(cluster.RawRestConfig(), namespace, output)
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
return command
|
||||
}
|
||||
|
||||
func NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var (
|
||||
clusterOpts cmdutil.ClusterOptions
|
||||
bearerToken string
|
||||
generateToken bool
|
||||
outputFormat string
|
||||
labels []string
|
||||
annotations []string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "generate-spec CONTEXT",
|
||||
Short: "Generate declarative config for a cluster",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
log.SetLevel(log.WarnLevel)
|
||||
var configAccess clientcmd.ConfigAccess = pathOpts
|
||||
if len(args) == 0 {
|
||||
log.Error("Choose a context name from:")
|
||||
cmdutil.PrintKubeContexts(configAccess)
|
||||
os.Exit(1)
|
||||
}
|
||||
cfgAccess, err := configAccess.GetStartingConfig()
|
||||
errors.CheckError(err)
|
||||
contextName := args[0]
|
||||
clstContext := cfgAccess.Contexts[contextName]
|
||||
if clstContext == nil {
|
||||
log.Fatalf("Context %s does not exist in kubeconfig", contextName)
|
||||
return
|
||||
}
|
||||
|
||||
overrides := clientcmd.ConfigOverrides{
|
||||
Context: *clstContext,
|
||||
}
|
||||
clientConfig := clientcmd.NewDefaultClientConfig(*cfgAccess, &overrides)
|
||||
conf, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
kubeClientset := fake.NewSimpleClientset()
|
||||
|
||||
var awsAuthConf *argoappv1.AWSAuthConfig
|
||||
var execProviderConf *argoappv1.ExecProviderConfig
|
||||
if clusterOpts.AwsClusterName != "" {
|
||||
awsAuthConf = &argoappv1.AWSAuthConfig{
|
||||
ClusterName: clusterOpts.AwsClusterName,
|
||||
RoleARN: clusterOpts.AwsRoleArn,
|
||||
}
|
||||
} else if clusterOpts.ExecProviderCommand != "" {
|
||||
execProviderConf = &argoappv1.ExecProviderConfig{
|
||||
Command: clusterOpts.ExecProviderCommand,
|
||||
Args: clusterOpts.ExecProviderArgs,
|
||||
Env: clusterOpts.ExecProviderEnv,
|
||||
APIVersion: clusterOpts.ExecProviderAPIVersion,
|
||||
InstallHint: clusterOpts.ExecProviderInstallHint,
|
||||
}
|
||||
} else if generateToken {
|
||||
bearerToken, err = GenerateToken(clusterOpts, conf)
|
||||
errors.CheckError(err)
|
||||
} else if bearerToken == "" {
|
||||
bearerToken = "bearer-token"
|
||||
}
|
||||
if clusterOpts.Name != "" {
|
||||
contextName = clusterOpts.Name
|
||||
}
|
||||
|
||||
labelsMap, err := label.Parse(labels)
|
||||
errors.CheckError(err)
|
||||
annotationsMap, err := label.Parse(annotations)
|
||||
errors.CheckError(err)
|
||||
|
||||
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, bearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap)
|
||||
if clusterOpts.InCluster {
|
||||
clst.Server = argoappv1.KubernetesInternalAPIServerAddr
|
||||
}
|
||||
if clusterOpts.Shard >= 0 {
|
||||
clst.Shard = &clusterOpts.Shard
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace)
|
||||
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
|
||||
|
||||
_, err = argoDB.CreateCluster(context.Background(), clst)
|
||||
errors.CheckError(err)
|
||||
|
||||
secName, err := db.URIToSecretName("cluster", clst.Server)
|
||||
errors.CheckError(err)
|
||||
|
||||
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), secName, v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
|
||||
errors.CheckError(PrintResources(outputFormat, os.Stdout, secret))
|
||||
},
|
||||
}
|
||||
command.PersistentFlags().StringVar(&pathOpts.LoadingRules.ExplicitPath, pathOpts.ExplicitFileFlag, pathOpts.LoadingRules.ExplicitPath, "use a particular kubeconfig file")
|
||||
command.Flags().StringVar(&bearerToken, "bearer-token", "", "Authentication token that should be used to access K8S API server")
|
||||
command.Flags().BoolVar(&generateToken, "generate-bearer-token", false, "Generate authentication token that should be used to access K8S API server")
|
||||
command.Flags().StringVar(&clusterOpts.ServiceAccount, "service-account", "argocd-manager", fmt.Sprintf("System namespace service account to use for kubernetes resource management. If not set then default \"%s\" SA will be used", clusterauth.ArgoCDManagerServiceAccount))
|
||||
command.Flags().StringVar(&clusterOpts.SystemNamespace, "system-namespace", common.DefaultSystemNamespace, "Use different system namespace")
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
command.Flags().StringArrayVar(&labels, "label", nil, "Set metadata labels (e.g. --label key=value)")
|
||||
command.Flags().StringArrayVar(&annotations, "annotation", nil, "Set metadata annotations (e.g. --annotation key=value)")
|
||||
cmdutil.AddClusterFlags(command, &clusterOpts)
|
||||
return command
|
||||
}
|
||||
|
||||
func GenerateToken(clusterOpts cmdutil.ClusterOptions, conf *rest.Config) (string, error) {
|
||||
clientset, err := kubernetes.NewForConfig(conf)
|
||||
errors.CheckError(err)
|
||||
|
||||
bearerToken, err := clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return bearerToken, nil
|
||||
}
|
||||
32
cmd/argocd/commands/admin/dashboard.go
Normal file
32
cmd/argocd/commands/admin/dashboard.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
)
|
||||
|
||||
func NewDashboardCommand() *cobra.Command {
|
||||
var (
|
||||
port int
|
||||
address string
|
||||
)
|
||||
cmd := &cobra.Command{
|
||||
Use: "dashboard",
|
||||
Short: "Starts Argo CD Web UI locally",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
println(fmt.Sprintf("Argo CD UI is available at http://%s:%d", address, port))
|
||||
<-context.Background().Done()
|
||||
},
|
||||
}
|
||||
clientOpts := &apiclient.ClientOptions{Core: true}
|
||||
headless.InitCommand(cmd, clientOpts, &port, &address)
|
||||
cmd.Flags().IntVar(&port, "port", common.DefaultPortAPIServer, "Listen on given port")
|
||||
cmd.Flags().StringVar(&address, "address", common.DefaultAddressAPIServer, "Listen on given address")
|
||||
return cmd
|
||||
}
|
||||
108
cmd/argocd/commands/admin/generatespec_utils.go
Normal file
108
cmd/argocd/commands/admin/generatespec_utils.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
ioutil "github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
func getOutWriter(inline bool, filePath string) (io.Writer, io.Closer, error) {
|
||||
if !inline {
|
||||
return os.Stdout, ioutil.NopCloser, nil
|
||||
}
|
||||
|
||||
if filePath == "" {
|
||||
return nil, nil, errors.New("The file path must be specified using flag '--file'")
|
||||
}
|
||||
|
||||
err := os.Rename(filePath, fmt.Sprintf("%s.back", filePath))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
fileOut, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return fileOut, fileOut, nil
|
||||
}
|
||||
|
||||
// PrintResources prints a single resource in YAML or JSON format to stdout according to the output format
|
||||
func PrintResources(output string, out io.Writer, resources ...interface{}) error {
|
||||
for i, resource := range resources {
|
||||
if secret, ok := resource.(*v1.Secret); ok {
|
||||
convertSecretData(secret)
|
||||
}
|
||||
filteredResource, err := omitFields(resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resources[i] = filteredResource
|
||||
}
|
||||
var obj interface{} = resources
|
||||
if len(resources) == 1 {
|
||||
obj = resources[0]
|
||||
}
|
||||
|
||||
switch output {
|
||||
case "json":
|
||||
jsonBytes, err := json.MarshalIndent(obj, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(out, string(jsonBytes))
|
||||
case "yaml":
|
||||
yamlBytes, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// marshaled YAML already ends with the new line character
|
||||
_, _ = fmt.Fprint(out, string(yamlBytes))
|
||||
default:
|
||||
return fmt.Errorf("unknown output format: %s", output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// omit fields such as status, creationTimestamp and metadata.namespace in k8s objects
|
||||
func omitFields(resource interface{}) (interface{}, error) {
|
||||
jsonBytes, err := json.Marshal(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toMap := make(map[string]interface{})
|
||||
err = json.Unmarshal(jsonBytes, &toMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(toMap, "status")
|
||||
if v, ok := toMap["metadata"]; ok {
|
||||
if metadata, ok := v.(map[string]interface{}); ok {
|
||||
delete(metadata, "creationTimestamp")
|
||||
delete(metadata, "namespace")
|
||||
}
|
||||
}
|
||||
return toMap, nil
|
||||
}
|
||||
|
||||
// convertSecretData converts kubernetes secret's data to stringData
|
||||
func convertSecretData(secret *v1.Secret) {
|
||||
secret.Kind = kube.SecretKind
|
||||
secret.APIVersion = "v1"
|
||||
secret.StringData = map[string]string{}
|
||||
for k, v := range secret.Data {
|
||||
secret.StringData[k] = string(v)
|
||||
}
|
||||
secret.Data = map[string][]byte{}
|
||||
}
|
||||
58
cmd/argocd/commands/admin/generatespec_utils_test.go
Normal file
58
cmd/argocd/commands/admin/generatespec_utils_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestGetOutWriter_InlineOff(t *testing.T) {
|
||||
out, closer, err := getOutWriter(false, "")
|
||||
require.NoError(t, err)
|
||||
defer io.Close(closer)
|
||||
|
||||
assert.Equal(t, os.Stdout, out)
|
||||
}
|
||||
|
||||
func TestGetOutWriter_InlineOn(t *testing.T) {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = os.Remove(tmpFile.Name())
|
||||
_ = os.Remove(fmt.Sprintf("%s.back", tmpFile.Name()))
|
||||
}()
|
||||
|
||||
out, closer, err := getOutWriter(true, tmpFile.Name())
|
||||
require.NoError(t, err)
|
||||
defer io.Close(closer)
|
||||
|
||||
assert.Equal(t, tmpFile.Name(), out.(*os.File).Name())
|
||||
_, err = os.Stat(fmt.Sprintf("%s.back", tmpFile.Name()))
|
||||
assert.NoError(t, err, "Back file must be created")
|
||||
}
|
||||
|
||||
func TestPrintResources_Secret_YAML(t *testing.T) {
|
||||
out := bytes.Buffer{}
|
||||
err := PrintResources("yaml", &out, &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "my-secret"},
|
||||
Data: map[string][]byte{"my-secret-key": []byte("my-secret-data")},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, `apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: my-secret
|
||||
stringData:
|
||||
my-secret-key: my-secret-data
|
||||
`, out.String())
|
||||
}
|
||||
51
cmd/argocd/commands/admin/notifications.go
Normal file
51
cmd/argocd/commands/admin/notifications.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
service "github.com/argoproj/argo-cd/v2/util/notification/argocd"
|
||||
settings "github.com/argoproj/argo-cd/v2/util/notification/settings"
|
||||
|
||||
"github.com/argoproj/notifications-engine/pkg/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
applications = schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "applications"}
|
||||
)
|
||||
|
||||
func NewNotificationsCommand() *cobra.Command {
|
||||
var (
|
||||
argocdRepoServer string
|
||||
argocdRepoServerPlaintext bool
|
||||
argocdRepoServerStrictTLS bool
|
||||
)
|
||||
|
||||
var argocdService service.Service
|
||||
toolsCommand := cmd.NewToolsCommand(
|
||||
"notifications",
|
||||
"notifications",
|
||||
applications,
|
||||
settings.GetFactorySettings(argocdService, "argocd-notifications-secret", "argocd-notifications-cm"), func(clientConfig clientcmd.ClientConfig) {
|
||||
k8sCfg, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse k8s config: %v", err)
|
||||
}
|
||||
ns, _, err := clientConfig.Namespace()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse k8s config: %v", err)
|
||||
}
|
||||
argocdService, err = service.NewArgoCDService(kubernetes.NewForConfigOrDie(k8sCfg), ns, argocdRepoServer, argocdRepoServerPlaintext, argocdRepoServerStrictTLS)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to initalize Argo CD service: %v", err)
|
||||
}
|
||||
})
|
||||
toolsCommand.PersistentFlags().StringVar(&argocdRepoServer, "argocd-repo-server", "argocd-repo-server:8081", "Argo CD repo server address")
|
||||
toolsCommand.PersistentFlags().BoolVar(&argocdRepoServerPlaintext, "argocd-repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server")
|
||||
toolsCommand.PersistentFlags().BoolVar(&argocdRepoServerStrictTLS, "argocd-repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server")
|
||||
return toolsCommand
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,11 +7,13 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned"
|
||||
appclient "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/typed/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
appclient "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/typed/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -21,18 +23,53 @@ import (
|
||||
|
||||
func NewProjectsCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "projects",
|
||||
Short: "Utility commands operate on ArgoCD Projects",
|
||||
Use: "proj",
|
||||
Short: "Manage projects configuration",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.AddCommand(NewGenProjectSpecCommand())
|
||||
command.AddCommand(NewUpdatePolicyRuleCommand())
|
||||
command.AddCommand(NewProjectAllowListGenCommand())
|
||||
return command
|
||||
}
|
||||
|
||||
// NewGenProjectSpecCommand generates declarative configuration file for given project
|
||||
func NewGenProjectSpecCommand() *cobra.Command {
|
||||
var (
|
||||
opts cmdutil.ProjectOpts
|
||||
fileURL string
|
||||
outputFormat string
|
||||
inline bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "generate-spec PROJECT",
|
||||
Short: "Generate declarative config for a project",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c)
|
||||
errors.CheckError(err)
|
||||
|
||||
out, closer, err := getOutWriter(inline, fileURL)
|
||||
errors.CheckError(err)
|
||||
defer io.Close(closer)
|
||||
|
||||
errors.CheckError(PrintResources(outputFormat, out, proj))
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the project")
|
||||
command.Flags().BoolVarP(&inline, "inline", "i", false, "If set then generated resource is written back to the file specified in --file flag")
|
||||
|
||||
// Only complete files with appropriate extension.
|
||||
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
|
||||
errors.CheckError(err)
|
||||
|
||||
cmdutil.AddProjFlags(command, &opts)
|
||||
return command
|
||||
}
|
||||
|
||||
func globMatch(pattern string, val string) bool {
|
||||
if pattern == "*" {
|
||||
return true
|
||||
@@ -106,10 +143,10 @@ func NewUpdatePolicyRuleCommand() *cobra.Command {
|
||||
Use: "update-role-policy PROJECT_GLOB MODIFICATION ACTION",
|
||||
Short: "Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions.",
|
||||
Example: ` # Add policy that allows executing any action (action/*) to roles which name matches to *deployer* in all projects
|
||||
argocd-util projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow
|
||||
argocd admin projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow
|
||||
|
||||
# Remove policy that which manages running (action/*) from all roles which name matches *deployer* in all projects
|
||||
argocd-util projects update-role-policy '*' remove override --role '*deployer*'
|
||||
argocd admin projects update-role-policy '*' remove override --role '*deployer*'
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -17,10 +17,10 @@ import (
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
|
||||
// load the gcp plugin (required to authenticate against GKE clusters).
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -30,9 +30,9 @@ func TestProjectAllowListGen(t *testing.T) {
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
var patchSeverPreferedResources *mpatch.Patch
|
||||
var patchSeverPreferredResources *mpatch.Patch
|
||||
discoClient := &discovery.DiscoveryClient{}
|
||||
patchSeverPreferedResources, err = mpatch.PatchInstanceMethodByName(reflect.TypeOf(discoClient), "ServerPreferredResources", func(*discovery.DiscoveryClient) ([]*metav1.APIResourceList, error) {
|
||||
patchSeverPreferredResources, err = mpatch.PatchInstanceMethodByName(reflect.TypeOf(discoClient), "ServerPreferredResources", func(*discovery.DiscoveryClient) ([]*metav1.APIResourceList, error) {
|
||||
res := metav1.APIResource{
|
||||
Name: "services",
|
||||
Kind: "Service",
|
||||
@@ -47,7 +47,7 @@ func TestProjectAllowListGen(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
err = patch.Unpatch()
|
||||
assert.NoError(t, err)
|
||||
err = patchSeverPreferedResources.Unpatch()
|
||||
err = patchSeverPreferredResources.Unpatch()
|
||||
err = patch.Unpatch()
|
||||
}()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
|
||||
)
|
||||
|
||||
const (
|
||||
169
cmd/argocd/commands/admin/repo.go
Normal file
169
cmd/argocd/commands/admin/repo.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/db"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
const (
|
||||
ArgoCDNamespace = "argocd"
|
||||
repoSecretPrefix = "repo"
|
||||
)
|
||||
|
||||
func NewRepoCommand() *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "repo",
|
||||
Short: "Manage repositories configuration",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
c.HelpFunc()(c, args)
|
||||
},
|
||||
}
|
||||
command.AddCommand(NewGenRepoSpecCommand())
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func NewGenRepoSpecCommand() *cobra.Command {
|
||||
var (
|
||||
repoOpts cmdutil.RepoOptions
|
||||
outputFormat string
|
||||
)
|
||||
|
||||
// For better readability and easier formatting
|
||||
var repoAddExamples = `
|
||||
# Add a Git repository via SSH using a private key for authentication, ignoring the server's host key:
|
||||
argocd admin repo generate-spec git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
|
||||
|
||||
# Add a Git repository via SSH on a non-default port - need to use ssh:// style URLs here
|
||||
argocd admin repo generate-spec ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa
|
||||
|
||||
# Add a private Git repository via HTTPS using username/password and TLS client certificates:
|
||||
argocd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --tls-client-cert-path ~/mycert.crt --tls-client-cert-key-path ~/mycert.key
|
||||
|
||||
# Add a private Git repository via HTTPS using username/password without verifying the server's TLS certificate
|
||||
argocd admin repo generate-spec https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
|
||||
|
||||
# Add a public Helm repository named 'stable' via HTTPS
|
||||
argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable
|
||||
|
||||
# Add a private Helm repository named 'stable' via HTTPS
|
||||
argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable --username test --password test
|
||||
|
||||
# Add a private Helm OCI-based repository named 'stable' via HTTPS
|
||||
argocd admin repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
|
||||
`
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: "generate-spec REPOURL",
|
||||
Short: "Generate declarative config for a repo",
|
||||
Example: repoAddExamples,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
log.SetLevel(log.WarnLevel)
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Repository URL
|
||||
repoOpts.Repo.Repo = args[0]
|
||||
|
||||
// Specifying ssh-private-key-path is only valid for SSH repositories
|
||||
if repoOpts.SshPrivateKeyPath != "" {
|
||||
if ok, _ := git.IsSSHURL(repoOpts.Repo.Repo); ok {
|
||||
keyData, err := ioutil.ReadFile(repoOpts.SshPrivateKeyPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
repoOpts.Repo.SSHPrivateKey = string(keyData)
|
||||
} else {
|
||||
err := fmt.Errorf("--ssh-private-key-path is only supported for SSH repositories.")
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// tls-client-cert-path and tls-client-cert-key-key-path must always be
|
||||
// specified together
|
||||
if (repoOpts.TlsClientCertPath != "" && repoOpts.TlsClientCertKeyPath == "") || (repoOpts.TlsClientCertPath == "" && repoOpts.TlsClientCertKeyPath != "") {
|
||||
err := fmt.Errorf("--tls-client-cert-path and --tls-client-cert-key-path must be specified together")
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
// Specifying tls-client-cert-path is only valid for HTTPS repositories
|
||||
if repoOpts.TlsClientCertPath != "" {
|
||||
if git.IsHTTPSURL(repoOpts.Repo.Repo) {
|
||||
tlsCertData, err := ioutil.ReadFile(repoOpts.TlsClientCertPath)
|
||||
errors.CheckError(err)
|
||||
tlsCertKey, err := ioutil.ReadFile(repoOpts.TlsClientCertKeyPath)
|
||||
errors.CheckError(err)
|
||||
repoOpts.Repo.TLSClientCertData = string(tlsCertData)
|
||||
repoOpts.Repo.TLSClientCertKey = string(tlsCertKey)
|
||||
} else {
|
||||
err := fmt.Errorf("--tls-client-cert-path is only supported for HTTPS repositories")
|
||||
errors.CheckError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set repository connection properties only when creating repository, not
|
||||
// when creating repository credentials.
|
||||
// InsecureIgnoreHostKey is deprecated and only here for backwards compat
|
||||
repoOpts.Repo.InsecureIgnoreHostKey = repoOpts.InsecureIgnoreHostKey
|
||||
repoOpts.Repo.Insecure = repoOpts.InsecureSkipServerVerification
|
||||
repoOpts.Repo.EnableLFS = repoOpts.EnableLfs
|
||||
repoOpts.Repo.EnableOCI = repoOpts.EnableOci
|
||||
|
||||
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
|
||||
errors.CheckError(fmt.Errorf("must specify --name for repos of type 'helm'"))
|
||||
}
|
||||
|
||||
// If the user set a username, but didn't supply password via --password,
|
||||
// then we prompt for it
|
||||
if repoOpts.Repo.Username != "" && repoOpts.Repo.Password == "" {
|
||||
repoOpts.Repo.Password = cli.PromptPassword(repoOpts.Repo.Password)
|
||||
}
|
||||
|
||||
argoCDCM := &apiv1.ConfigMap{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: "ConfigMap",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: common.ArgoCDConfigMapName,
|
||||
Namespace: ArgoCDNamespace,
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/part-of": "argocd",
|
||||
},
|
||||
},
|
||||
}
|
||||
kubeClientset := fake.NewSimpleClientset(argoCDCM)
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClientset, ArgoCDNamespace)
|
||||
argoDB := db.NewDB(ArgoCDNamespace, settingsMgr, kubeClientset)
|
||||
|
||||
_, err := argoDB.CreateRepository(context.Background(), &repoOpts.Repo)
|
||||
errors.CheckError(err)
|
||||
|
||||
secret, err := kubeClientset.CoreV1().Secrets(ArgoCDNamespace).Get(context.Background(), db.RepoURLToSecretName(repoSecretPrefix, repoOpts.Repo.Repo), v1.GetOptions{})
|
||||
errors.CheckError(err)
|
||||
|
||||
errors.CheckError(PrintResources(outputFormat, os.Stdout, secret))
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
cmdutil.AddRepoFlags(command, &repoOpts)
|
||||
return command
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -23,13 +23,13 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/lua"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo/normalizers"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/lua"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
)
|
||||
|
||||
type settingsOpts struct {
|
||||
@@ -162,6 +162,7 @@ func NewSettingsCommand() *cobra.Command {
|
||||
|
||||
command.AddCommand(NewValidateSettingsCommand(&opts))
|
||||
command.AddCommand(NewResourceOverridesCommand(&opts))
|
||||
command.AddCommand(NewRBACCommand())
|
||||
|
||||
opts.clientConfig = cli.AddKubectlFlagsToCmd(command)
|
||||
command.PersistentFlags().StringVar(&opts.argocdCMPath, "argocd-cm-path", "", "Path to local argocd-cm.yaml file")
|
||||
@@ -302,10 +303,10 @@ func NewValidateSettingsCommand(cmdCtx commandContext) *cobra.Command {
|
||||
Long: "Validates settings specified in 'argocd-cm' ConfigMap and 'argocd-secret' Secret",
|
||||
Example: `
|
||||
#Validates all settings in the specified YAML file
|
||||
argocd-util settings validate --argocd-cm-path ./argocd-cm.yaml
|
||||
argocd admin settings validate --argocd-cm-path ./argocd-cm.yaml
|
||||
|
||||
#Validates accounts and plugins settings in Kubernetes cluster of current kubeconfig context
|
||||
argocd-util settings validate --group accounts --group plugins --load-cluster-settings`,
|
||||
argocd admin settings validate --group accounts --group plugins --load-cluster-settings`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
settingsManager, err := cmdCtx.createSettingsManager()
|
||||
errors.CheckError(err)
|
||||
@@ -391,7 +392,7 @@ func NewResourceIgnoreDifferencesCommand(cmdCtx commandContext) *cobra.Command {
|
||||
Short: "Renders fields excluded from diffing",
|
||||
Long: "Renders ignored fields using the 'ignoreDifferences' setting specified in the 'resource.customizations' field of 'argocd-cm' ConfigMap",
|
||||
Example: `
|
||||
argocd-util settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
|
||||
argocd admin settings resource-overrides ignore-differences ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) < 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -400,11 +401,15 @@ argocd-util settings resource-overrides ignore-differences ./deploy.yaml --argoc
|
||||
|
||||
executeResourceOverrideCommand(cmdCtx, args, func(res unstructured.Unstructured, override v1alpha1.ResourceOverride, overrides map[string]v1alpha1.ResourceOverride) {
|
||||
gvk := res.GroupVersionKind()
|
||||
if len(override.IgnoreDifferences.JSONPointers) == 0 {
|
||||
if len(override.IgnoreDifferences.JSONPointers) == 0 && len(override.IgnoreDifferences.JQPathExpressions) == 0 {
|
||||
_, _ = fmt.Printf("Ignore differences are not configured for '%s/%s'\n", gvk.Group, gvk.Kind)
|
||||
return
|
||||
}
|
||||
|
||||
// This normalizer won't verify 'managedFieldsManagers' ignore difference
|
||||
// configurations. This requires access to live resources which is not the
|
||||
// purpose of this command. This will just apply jsonPointers and
|
||||
// jqPathExpressions configurations.
|
||||
normalizer, err := normalizers.NewIgnoreNormalizer(nil, overrides)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -435,7 +440,7 @@ func NewResourceHealthCommand(cmdCtx commandContext) *cobra.Command {
|
||||
Short: "Assess resource health",
|
||||
Long: "Assess resource health using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap",
|
||||
Example: `
|
||||
argocd-util settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
|
||||
argocd admin settings resource-overrides health ./deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) < 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -466,7 +471,7 @@ func NewResourceActionListCommand(cmdCtx commandContext) *cobra.Command {
|
||||
Short: "List available resource actions",
|
||||
Long: "List actions available for given resource action using the lua scripts configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields",
|
||||
Example: `
|
||||
argocd-util settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
|
||||
argocd admin settings resource-overrides action list /tmp/deploy.yaml --argocd-cm-path ./argocd-cm.yaml`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) < 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -509,7 +514,7 @@ func NewResourceActionRunCommand(cmdCtx commandContext) *cobra.Command {
|
||||
Short: "Executes resource action",
|
||||
Long: "Executes resource action using the lua script configured in the 'resource.customizations' field of 'argocd-cm' ConfigMap and outputs updated fields",
|
||||
Example: `
|
||||
argocd-util settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml`,
|
||||
argocd admin settings resource-overrides action run /tmp/deploy.yaml restart --argocd-cm-path ./argocd-cm.yaml`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) < 2 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/util/assets"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/rbac"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/rbac"
|
||||
)
|
||||
|
||||
// Provide a mapping of short-hand resource names to their RBAC counterparts
|
||||
@@ -101,19 +101,19 @@ something.
|
||||
Example: `
|
||||
# Check whether role some:role has permissions to create an application in the
|
||||
# 'default' project, using a local policy.csv file
|
||||
argocd-util rbac can some:role create application 'default/app' --policy-file policy.csv
|
||||
argocd admin settings rbac can some:role create application 'default/app' --policy-file policy.csv
|
||||
|
||||
# Policy file can also be K8s config map with data keys like argocd-rbac-cm,
|
||||
# i.e. 'policy.csv' and (optionally) 'policy.default'
|
||||
argocd-util rbac can some:role create application 'default/app' --policy-file argocd-rbac-cm.yaml
|
||||
argocd admin settings rbac can some:role create application 'default/app' --policy-file argocd-rbac-cm.yaml
|
||||
|
||||
# If --policy-file is not given, the ConfigMap 'argocd-rbac-cm' from K8s is
|
||||
# used. You need to specify the argocd namespace, and make sure that your
|
||||
# current Kubernetes context is pointing to the cluster Argo CD is running in
|
||||
argocd-util rbac can some:role create application 'default/app' --namespace argocd
|
||||
argocd admin settings rbac can some:role create application 'default/app' --namespace argocd
|
||||
|
||||
# You can override a possibly configured default role
|
||||
argocd-util rbac can someuser create application 'default/app' --default-role role:readonly
|
||||
argocd admin settings rbac can someuser create application 'default/app' --default-role role:readonly
|
||||
|
||||
`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
"github.com/argoproj/argo-cd/util/assets"
|
||||
"github.com/argoproj/argo-cd/v2/util/assets"
|
||||
)
|
||||
|
||||
func Test_isValidRBACAction(t *testing.T) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
utils "github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
utils "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/settings"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -240,6 +240,7 @@ func tempFile(content string) (string, io.Closer, error) {
|
||||
_ = os.Remove(f.Name())
|
||||
return "", nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return f.Name(), utils.NewCloser(func() error {
|
||||
return os.Remove(f.Name())
|
||||
}), nil
|
||||
@@ -14,7 +14,8 @@ import (
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/diff"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/common"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/health"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/hook"
|
||||
"github.com/argoproj/gitops-engine/pkg/sync/ignore"
|
||||
@@ -30,28 +31,27 @@ import (
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/application"
|
||||
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
|
||||
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
|
||||
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/settings"
|
||||
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
repoapiclient "github.com/argoproj/argo-cd/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/reposerver/repository"
|
||||
"github.com/argoproj/argo-cd/util/argo"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
argoio "github.com/argoproj/argo-cd/util/io"
|
||||
argokube "github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/templates"
|
||||
"github.com/argoproj/argo-cd/util/text/label"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/controller"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
|
||||
applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
|
||||
clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
|
||||
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
|
||||
settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
repoapiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/repository"
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/templates"
|
||||
"github.com/argoproj/argo-cd/v2/util/text/label"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -93,6 +93,7 @@ func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
command.AddCommand(NewApplicationEditCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationPatchCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationPatchResourceCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationDeleteResourceCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationResourceActionsCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationListResourcesCommand(clientOpts))
|
||||
command.AddCommand(NewApplicationLogsCommand(clientOpts))
|
||||
@@ -102,11 +103,12 @@ func NewApplicationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
// NewApplicationCreateCommand returns a new instance of an `argocd app create` command
|
||||
func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
appOpts cmdutil.AppOptions
|
||||
fileURL string
|
||||
appName string
|
||||
upsert bool
|
||||
labels []string
|
||||
appOpts cmdutil.AppOptions
|
||||
fileURL string
|
||||
appName string
|
||||
upsert bool
|
||||
labels []string
|
||||
annotations []string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "create APPNAME",
|
||||
@@ -122,7 +124,7 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
argocd app create helm-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path helm-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --helm-set replicaCount=2
|
||||
|
||||
# Create a Helm app from a Helm repo
|
||||
argocd app create nginx-ingress --repo https://kubernetes-charts.storage.googleapis.com --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
|
||||
argocd app create nginx-ingress --repo https://charts.helm.sh/stable --helm-chart nginx-ingress --revision 1.24.3 --dest-namespace default --dest-server https://kubernetes.default.svc
|
||||
|
||||
# Create a Kustomize app
|
||||
argocd app create kustomize-guestbook --repo https://github.com/argoproj/argocd-example-apps.git --path kustomize-guestbook --dest-namespace default --dest-server https://kubernetes.default.svc --kustomize-image gcr.io/heptio-images/ks-guestbook-demo:0.1
|
||||
@@ -133,30 +135,34 @@ func NewApplicationCreateCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
argocdClient := argocdclient.NewClientOrDie(clientOpts)
|
||||
|
||||
app, err := cmdutil.ConstructApp(fileURL, appName, labels, args, appOpts, c.Flags())
|
||||
apps, err := cmdutil.ConstructApps(fileURL, appName, labels, annotations, args, appOpts, c.Flags())
|
||||
errors.CheckError(err)
|
||||
|
||||
if app.Name == "" {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
for _, app := range apps {
|
||||
if app.Name == "" {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
conn, appIf := argocdClient.NewApplicationClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
appCreateRequest := applicationpkg.ApplicationCreateRequest{
|
||||
Application: *app,
|
||||
Upsert: &upsert,
|
||||
Validate: &appOpts.Validate,
|
||||
}
|
||||
created, err := appIf.Create(context.Background(), &appCreateRequest)
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("application '%s' created\n", created.ObjectMeta.Name)
|
||||
}
|
||||
|
||||
conn, appIf := argocdClient.NewApplicationClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
appCreateRequest := applicationpkg.ApplicationCreateRequest{
|
||||
Application: *app,
|
||||
Upsert: &upsert,
|
||||
Validate: &appOpts.Validate,
|
||||
}
|
||||
created, err := appIf.Create(context.Background(), &appCreateRequest)
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("application '%s' created\n", created.ObjectMeta.Name)
|
||||
},
|
||||
}
|
||||
command.Flags().StringVar(&appName, "name", "", "A name for the app, ignored if a file is set (DEPRECATED)")
|
||||
command.Flags().BoolVar(&upsert, "upsert", false, "Allows to override application with the same name even if supplied application spec is different from existing spec")
|
||||
command.Flags().StringVarP(&fileURL, "file", "f", "", "Filename or URL to Kubernetes manifests for the app")
|
||||
command.Flags().StringArrayVarP(&labels, "label", "l", []string{}, "Labels to apply to the app")
|
||||
command.Flags().StringArrayVarP(&annotations, "annotations", "", []string{}, "Set metadata annotations (e.g. example=value)")
|
||||
// Only complete files with appropriate extension.
|
||||
err := command.Flags().SetAnnotation("file", cobra.BashCompFilenameExt, []string{"json", "yaml", "yml"})
|
||||
if err != nil {
|
||||
@@ -276,6 +282,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
sinceSeconds int64
|
||||
untilTime string
|
||||
filter string
|
||||
container string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "logs APPNAME",
|
||||
@@ -304,6 +311,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
SinceSeconds: sinceSeconds,
|
||||
UntilTime: &untilTime,
|
||||
Filter: &filter,
|
||||
Container: container,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get pod logs: %v", err)
|
||||
@@ -344,6 +352,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().Int64Var(&sinceSeconds, "since-seconds", 0, "A relative time in seconds before the current time from which to show logs")
|
||||
command.Flags().StringVar(&untilTime, "until-time", "", "Show logs until this time")
|
||||
command.Flags().StringVar(&filter, "filter", "", "Show logs contain this string")
|
||||
command.Flags().StringVar(&container, "container", "", "Optional container name")
|
||||
|
||||
return command
|
||||
}
|
||||
@@ -549,14 +558,16 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
|
||||
// NewApplicationUnsetCommand returns a new instance of an `argocd app unset` command
|
||||
func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
parameters []string
|
||||
valuesLiteral bool
|
||||
valuesFiles []string
|
||||
nameSuffix bool
|
||||
namePrefix bool
|
||||
kustomizeVersion bool
|
||||
kustomizeImages []string
|
||||
appOpts cmdutil.AppOptions
|
||||
parameters []string
|
||||
valuesLiteral bool
|
||||
valuesFiles []string
|
||||
ignoreMissingValueFiles bool
|
||||
nameSuffix bool
|
||||
namePrefix bool
|
||||
kustomizeVersion bool
|
||||
kustomizeImages []string
|
||||
pluginEnvs []string
|
||||
appOpts cmdutil.AppOptions
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "unset APPNAME parameters",
|
||||
@@ -633,7 +644,7 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
}
|
||||
}
|
||||
if app.Spec.Source.Helm != nil {
|
||||
if len(parameters) == 0 && len(valuesFiles) == 0 && !valuesLiteral {
|
||||
if len(parameters) == 0 && len(valuesFiles) == 0 && !valuesLiteral && !ignoreMissingValueFiles {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -661,9 +672,31 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
}
|
||||
}
|
||||
}
|
||||
if !updated {
|
||||
return
|
||||
if ignoreMissingValueFiles {
|
||||
app.Spec.Source.Helm.IgnoreMissingValueFiles = false
|
||||
updated = true
|
||||
}
|
||||
if app.Spec.Source.Helm.PassCredentials {
|
||||
app.Spec.Source.Helm.PassCredentials = false
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
|
||||
if app.Spec.Source.Plugin != nil {
|
||||
if len(pluginEnvs) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, env := range pluginEnvs {
|
||||
err = app.Spec.Source.Plugin.RemoveEnvEntry(env)
|
||||
if err == nil {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !updated {
|
||||
return
|
||||
}
|
||||
|
||||
cmdutil.SetAppSpecOptions(c.Flags(), &app.Spec, &appOpts)
|
||||
@@ -678,10 +711,12 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
command.Flags().StringArrayVarP(¶meters, "parameter", "p", []string{}, "Unset a parameter override (e.g. -p guestbook=image)")
|
||||
command.Flags().StringArrayVar(&valuesFiles, "values", []string{}, "Unset one or more Helm values files")
|
||||
command.Flags().BoolVar(&valuesLiteral, "values-literal", false, "Unset literal Helm values block")
|
||||
command.Flags().BoolVar(&ignoreMissingValueFiles, "ignore-missing-value-files", false, "Unset the helm ignore-missing-value-files option (revert to false)")
|
||||
command.Flags().BoolVar(&nameSuffix, "namesuffix", false, "Kustomize namesuffix")
|
||||
command.Flags().BoolVar(&namePrefix, "nameprefix", false, "Kustomize nameprefix")
|
||||
command.Flags().BoolVar(&kustomizeVersion, "kustomize-version", false, "Kustomize version")
|
||||
command.Flags().StringArrayVar(&kustomizeImages, "kustomize-image", []string{}, "Kustomize images name (e.g. --kustomize-image node --kustomize-image mysql)")
|
||||
command.Flags().StringArrayVar(&pluginEnvs, "plugin-env", []string{}, "Unset plugin env variables (e.g --plugin-env name)")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -712,8 +747,8 @@ func liveObjects(resources []*argoappv1.ResourceDiff) ([]*unstructured.Unstructu
|
||||
}
|
||||
|
||||
func getLocalObjects(app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
configManagementPlugins []*argoappv1.ConfigManagementPlugin) []*unstructured.Unstructured {
|
||||
manifestStrings := getLocalObjectsString(app, local, localRepoRoot, appLabelKey, kubeVersion, kustomizeOptions, configManagementPlugins)
|
||||
configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []*unstructured.Unstructured {
|
||||
manifestStrings := getLocalObjectsString(app, local, localRepoRoot, appLabelKey, kubeVersion, kustomizeOptions, configManagementPlugins, trackingMethod)
|
||||
objs := make([]*unstructured.Unstructured, len(manifestStrings))
|
||||
for i := range manifestStrings {
|
||||
obj := unstructured.Unstructured{}
|
||||
@@ -725,9 +760,9 @@ func getLocalObjects(app *argoappv1.Application, local, localRepoRoot, appLabelK
|
||||
}
|
||||
|
||||
func getLocalObjectsString(app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
configManagementPlugins []*argoappv1.ConfigManagementPlugin) []string {
|
||||
configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []string {
|
||||
|
||||
res, err := repository.GenerateManifests(local, localRepoRoot, app.Spec.Source.TargetRevision, &repoapiclient.ManifestRequest{
|
||||
res, err := repository.GenerateManifests(context.Background(), local, localRepoRoot, app.Spec.Source.TargetRevision, &repoapiclient.ManifestRequest{
|
||||
Repo: &argoappv1.Repository{Repo: app.Spec.Source.RepoURL},
|
||||
AppLabelKey: appLabelKey,
|
||||
AppName: app.Name,
|
||||
@@ -736,6 +771,7 @@ func getLocalObjectsString(app *argoappv1.Application, local, localRepoRoot, app
|
||||
KustomizeOptions: kustomizeOptions,
|
||||
KubeVersion: kubeVersion,
|
||||
Plugins: configManagementPlugins,
|
||||
TrackingMethod: trackingMethod,
|
||||
}, true)
|
||||
errors.CheckError(err)
|
||||
|
||||
@@ -783,6 +819,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
var (
|
||||
refresh bool
|
||||
hardRefresh bool
|
||||
exitCode bool
|
||||
local string
|
||||
revision string
|
||||
localRepoRoot string
|
||||
@@ -820,7 +857,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
defer argoio.Close(conn)
|
||||
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server})
|
||||
errors.CheckError(err)
|
||||
localObjs := groupObjsByKey(getLocalObjects(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins), liveObjs, app.Spec.Destination.Namespace)
|
||||
localObjs := groupObjsByKey(getLocalObjects(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod), liveObjs, app.Spec.Destination.Namespace)
|
||||
items = groupObjsForDiff(resources, localObjs, items, argoSettings, appName)
|
||||
} else if revision != "" {
|
||||
var unstructureds []*unstructured.Unstructured
|
||||
@@ -862,10 +899,18 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
val := argoSettings.ResourceOverrides[k]
|
||||
overrides[k] = *val
|
||||
}
|
||||
normalizer, err := argo.NewDiffNormalizer(app.Spec.IgnoreDifferences, overrides)
|
||||
|
||||
// TODO remove hardcoded IgnoreAggregatedRoles and retrieve the
|
||||
// compareOptions in the protobuf
|
||||
ignoreAggregatedRoles := false
|
||||
diffConfig, err := argodiff.NewDiffConfigBuilder().
|
||||
WithDiffSettings(app.Spec.IgnoreDifferences, overrides, ignoreAggregatedRoles).
|
||||
WithTracking(argoSettings.AppLabelKey, argoSettings.TrackingMethod).
|
||||
WithNoCache().
|
||||
Build()
|
||||
errors.CheckError(err)
|
||||
|
||||
diffRes, err := diff.Diff(item.target, item.live, diff.WithNormalizer(normalizer))
|
||||
diffRes, err := argodiff.StateDiff(item.live, item.target, diffConfig)
|
||||
errors.CheckError(err)
|
||||
|
||||
if diffRes.Modified || item.target == nil || item.live == nil {
|
||||
@@ -886,7 +931,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
_ = cli.PrintDiff(item.key.Name, live, target)
|
||||
}
|
||||
}
|
||||
if foundDiffs {
|
||||
if foundDiffs && exitCode {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -894,6 +939,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
command.Flags().BoolVar(&refresh, "refresh", false, "Refresh application data when retrieving")
|
||||
command.Flags().BoolVar(&hardRefresh, "hard-refresh", false, "Refresh application data as well as target manifests cache")
|
||||
command.Flags().BoolVar(&exitCode, "exit-code", true, "Return non-zero exit code when there is a diff")
|
||||
command.Flags().StringVar(&local, "local", "", "Compare live app to a local manifests")
|
||||
command.Flags().StringVar(&revision, "revision", "", "Compare live app to a particular revision")
|
||||
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
|
||||
@@ -901,6 +947,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
}
|
||||
|
||||
func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[kube.ResourceKey]*unstructured.Unstructured, items []objKeyLiveTarget, argoSettings *settings.Settings, appName string) []objKeyLiveTarget {
|
||||
resourceTracking := argo.NewResourceTracking()
|
||||
for _, res := range resources.Items {
|
||||
var live = &unstructured.Unstructured{}
|
||||
err := json.Unmarshal([]byte(res.NormalizedLiveState), &live)
|
||||
@@ -914,7 +961,7 @@ func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[
|
||||
}
|
||||
if local, ok := objs[key]; ok || live != nil {
|
||||
if local != nil && !kube.IsCRD(local) {
|
||||
err = argokube.SetAppInstanceLabel(local, argoSettings.AppLabelKey, appName)
|
||||
err = resourceTracking.SetAppInstance(local, argoSettings.AppLabelKey, appName, "", argoappv1.TrackingMethod(argoSettings.GetTrackingMethod()))
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
@@ -936,8 +983,9 @@ func groupObjsForDiff(resources *application.ManagedResourcesResponse, objs map[
|
||||
// NewApplicationDeleteCommand returns a new instance of an `argocd app delete` command
|
||||
func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
cascade bool
|
||||
noPrompt bool
|
||||
cascade bool
|
||||
noPrompt bool
|
||||
propagationPolicy string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "delete APPNAME",
|
||||
@@ -963,6 +1011,9 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
if c.Flag("cascade").Changed {
|
||||
appDeleteReq.Cascade = &cascade
|
||||
}
|
||||
if c.Flag("propagation-policy").Changed {
|
||||
appDeleteReq.PropagationPolicy = &propagationPolicy
|
||||
}
|
||||
if cascade && isTerminal && !noPrompt {
|
||||
var confirmAnswer string = "n"
|
||||
var lowercaseAnswer string
|
||||
@@ -997,6 +1048,7 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra.
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&cascade, "cascade", true, "Perform a cascaded deletion of all application resources")
|
||||
command.Flags().StringVarP(&propagationPolicy, "propagation-policy", "p", "foreground", "Specify propagation policy for deletion of application's resources. One of: foreground|background")
|
||||
command.Flags().BoolVarP(&noPrompt, "yes", "y", false, "Turn off prompting to confirm cascaded deletion of application resources")
|
||||
return command
|
||||
}
|
||||
@@ -1045,6 +1097,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
output string
|
||||
selector string
|
||||
projects []string
|
||||
repo string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "list",
|
||||
@@ -1063,6 +1116,9 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
if len(projects) != 0 {
|
||||
appList = argo.FilterByProjects(appList, projects)
|
||||
}
|
||||
if repo != "" {
|
||||
appList = argo.FilterByRepo(appList, repo)
|
||||
}
|
||||
switch output {
|
||||
case "yaml", "json":
|
||||
err := PrintResourceList(appList, output, false)
|
||||
@@ -1079,6 +1135,7 @@ func NewApplicationListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().StringVarP(&output, "output", "o", "wide", "Output format. One of: wide|name|json|yaml")
|
||||
command.Flags().StringVarP(&selector, "selector", "l", "", "List apps by label")
|
||||
command.Flags().StringArrayVarP(&projects, "project", "p", []string{}, "Filter by project name")
|
||||
command.Flags().StringVarP(&repo, "repo", "r", "", "List apps by source repo URL")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -1237,6 +1294,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
timeout uint
|
||||
strategy string
|
||||
force bool
|
||||
replace bool
|
||||
async bool
|
||||
retryLimit int64
|
||||
retryBackoffDuration time.Duration
|
||||
@@ -1344,18 +1402,35 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
cluster, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Name: app.Spec.Destination.Name, Server: app.Spec.Destination.Server})
|
||||
errors.CheckError(err)
|
||||
argoio.Close(conn)
|
||||
localObjsStrings = getLocalObjectsString(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins)
|
||||
localObjsStrings = getLocalObjectsString(app, local, localRepoRoot, argoSettings.AppLabelKey, cluster.ServerVersion, argoSettings.KustomizeOptions, argoSettings.ConfigManagementPlugins, argoSettings.TrackingMethod)
|
||||
}
|
||||
|
||||
syncOptionsFactory := func() *applicationpkg.SyncOptions {
|
||||
syncOptions := applicationpkg.SyncOptions{}
|
||||
items := make([]string, 0)
|
||||
if replace {
|
||||
items = append(items, common.SyncOptionReplace)
|
||||
}
|
||||
|
||||
if len(items) == 0 {
|
||||
// for prevent send even empty array if not need
|
||||
return nil
|
||||
}
|
||||
syncOptions.Items = items
|
||||
return &syncOptions
|
||||
}
|
||||
|
||||
syncReq := applicationpkg.ApplicationSyncRequest{
|
||||
Name: &appName,
|
||||
DryRun: dryRun,
|
||||
Revision: revision,
|
||||
Resources: selectedResources,
|
||||
Prune: prune,
|
||||
Manifests: localObjsStrings,
|
||||
Infos: getInfos(infos),
|
||||
Name: &appName,
|
||||
DryRun: dryRun,
|
||||
Revision: revision,
|
||||
Resources: selectedResources,
|
||||
Prune: prune,
|
||||
Manifests: localObjsStrings,
|
||||
Infos: getInfos(infos),
|
||||
SyncOptions: syncOptionsFactory(),
|
||||
}
|
||||
|
||||
switch strategy {
|
||||
case "apply":
|
||||
syncReq.Strategy = &argoappv1.SyncStrategy{Apply: &argoappv1.SyncStrategyApply{}}
|
||||
@@ -1407,11 +1482,12 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
command.Flags().StringArrayVar(&labels, "label", []string{}, "Sync only specific resources with a label. This option may be specified repeatedly.")
|
||||
command.Flags().UintVar(&timeout, "timeout", defaultCheckTimeoutSeconds, "Time out after this many seconds")
|
||||
command.Flags().Int64Var(&retryLimit, "retry-limit", 0, "Max number of allowed sync retries")
|
||||
command.Flags().DurationVar(&retryBackoffDuration, "retry-backoff-duration", common.DefaultSyncRetryDuration, "Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().DurationVar(&retryBackoffMaxDuration, "retry-backoff-max-duration", common.DefaultSyncRetryMaxDuration, "Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().Int64Var(&retryBackoffFactor, "retry-backoff-factor", common.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed retry")
|
||||
command.Flags().DurationVar(&retryBackoffDuration, "retry-backoff-duration", argoappv1.DefaultSyncRetryDuration, "Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().DurationVar(&retryBackoffMaxDuration, "retry-backoff-max-duration", argoappv1.DefaultSyncRetryMaxDuration, "Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().Int64Var(&retryBackoffFactor, "retry-backoff-factor", argoappv1.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed retry")
|
||||
command.Flags().StringVar(&strategy, "strategy", "", "Sync strategy (one of: apply|hook)")
|
||||
command.Flags().BoolVar(&force, "force", false, "Use a force apply")
|
||||
command.Flags().BoolVar(&replace, "replace", false, "Use a kubectl create/replace instead apply")
|
||||
command.Flags().BoolVar(&async, "async", false, "Do not wait for application to sync before continuing")
|
||||
command.Flags().StringVar(&local, "local", "", "Path to a local directory. When this flag is present no git queries will be made")
|
||||
command.Flags().StringVar(&localRepoRoot, "local-repo-root", "/", "Path to the repository root. Used together with --local allows setting the repository root")
|
||||
@@ -1782,6 +1858,23 @@ func NewApplicationHistoryCommand(clientOpts *argocdclient.ClientOptions) *cobra
|
||||
return command
|
||||
}
|
||||
|
||||
func findRevisionHistory(application *argoappv1.Application, historyId int64) (*argoappv1.RevisionHistory, error) {
|
||||
// in case if history id not passed and need fetch previous history revision
|
||||
if historyId == -1 {
|
||||
l := len(application.Status.History)
|
||||
if l < 2 {
|
||||
return nil, fmt.Errorf("Application '%s' should have at least two successful deployments", application.ObjectMeta.Name)
|
||||
}
|
||||
return &application.Status.History[l-2], nil
|
||||
}
|
||||
for _, di := range application.Status.History {
|
||||
if di.ID == historyId {
|
||||
return &di, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Application '%s' does not have deployment id '%d' in history\n", application.ObjectMeta.Name, historyId)
|
||||
}
|
||||
|
||||
// NewApplicationRollbackCommand returns a new instance of an `argocd app rollback` command
|
||||
func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var (
|
||||
@@ -1789,36 +1882,33 @@ func NewApplicationRollbackCommand(clientOpts *argocdclient.ClientOptions) *cobr
|
||||
timeout uint
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "rollback APPNAME ID",
|
||||
Short: "Rollback application to a previous deployed version by History ID",
|
||||
Use: "rollback APPNAME [ID]",
|
||||
Short: "Rollback application to a previous deployed version by History ID, omitted will Rollback to the previous version",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 2 {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
appName := args[0]
|
||||
depID, err := strconv.Atoi(args[1])
|
||||
errors.CheckError(err)
|
||||
var err error
|
||||
depID := -1
|
||||
if len(args) > 1 {
|
||||
depID, err = strconv.Atoi(args[1])
|
||||
errors.CheckError(err)
|
||||
}
|
||||
acdClient := argocdclient.NewClientOrDie(clientOpts)
|
||||
conn, appIf := acdClient.NewApplicationClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
ctx := context.Background()
|
||||
app, err := appIf.Get(ctx, &applicationpkg.ApplicationQuery{Name: &appName})
|
||||
errors.CheckError(err)
|
||||
var depInfo *argoappv1.RevisionHistory
|
||||
for _, di := range app.Status.History {
|
||||
if di.ID == int64(depID) {
|
||||
depInfo = &di
|
||||
break
|
||||
}
|
||||
}
|
||||
if depInfo == nil {
|
||||
log.Fatalf("Application '%s' does not have deployment id '%d' in history\n", app.ObjectMeta.Name, depID)
|
||||
}
|
||||
|
||||
depInfo, err := findRevisionHistory(app, int64(depID))
|
||||
errors.CheckError(err)
|
||||
|
||||
_, err = appIf.Rollback(ctx, &applicationpkg.ApplicationRollbackRequest{
|
||||
Name: &appName,
|
||||
ID: int64(depID),
|
||||
ID: depInfo.ID,
|
||||
Prune: prune,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
@@ -2156,3 +2246,59 @@ func NewApplicationPatchResourceCommand(clientOpts *argocdclient.ClientOptions)
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
func NewApplicationDeleteResourceCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var resourceName string
|
||||
var namespace string
|
||||
var kind string
|
||||
var group string
|
||||
var force bool
|
||||
var orphan bool
|
||||
var all bool
|
||||
command := &cobra.Command{
|
||||
Use: "delete-resource APPNAME",
|
||||
Short: "Delete resource in an application",
|
||||
}
|
||||
|
||||
command.Flags().StringVar(&resourceName, "resource-name", "", "Name of resource")
|
||||
command.Flags().StringVar(&kind, "kind", "", "Kind")
|
||||
err := command.MarkFlagRequired("kind")
|
||||
errors.CheckError(err)
|
||||
command.Flags().StringVar(&group, "group", "", "Group")
|
||||
command.Flags().StringVar(&namespace, "namespace", "", "Namespace")
|
||||
command.Flags().BoolVar(&force, "force", false, "Indicates whether to orphan the dependents of the deleted resource")
|
||||
command.Flags().BoolVar(&orphan, "orphan", false, "Indicates whether to force delete the resource")
|
||||
command.Flags().BoolVar(&all, "all", false, "Indicates whether to patch multiple matching of resources")
|
||||
command.Run = func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
appName := args[0]
|
||||
|
||||
conn, appIf := argocdclient.NewClientOrDie(clientOpts).NewApplicationClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
ctx := context.Background()
|
||||
resources, err := appIf.ManagedResources(ctx, &applicationpkg.ResourcesQuery{ApplicationName: &appName})
|
||||
errors.CheckError(err)
|
||||
objectsToDelete := filterResources(command, resources.Items, group, kind, namespace, resourceName, all)
|
||||
for i := range objectsToDelete {
|
||||
obj := objectsToDelete[i]
|
||||
gvk := obj.GroupVersionKind()
|
||||
_, err = appIf.DeleteResource(ctx, &applicationpkg.ApplicationResourceDeleteRequest{
|
||||
Name: &appName,
|
||||
Namespace: obj.GetNamespace(),
|
||||
ResourceName: obj.GetName(),
|
||||
Version: gvk.Version,
|
||||
Group: gvk.Group,
|
||||
Kind: gvk.Kind,
|
||||
Force: &force,
|
||||
Orphan: &orphan,
|
||||
})
|
||||
errors.CheckError(err)
|
||||
log.Infof("Resource '%s' deleted", obj.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
type DisplayedAction struct {
|
||||
|
||||
163
cmd/argocd/commands/app_test.go
Normal file
163
cmd/argocd/commands/app_test.go
Normal file
@@ -0,0 +1,163 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func TestFindRevisionHistoryWithoutPassedId(t *testing.T) {
|
||||
|
||||
histories := v1alpha1.RevisionHistories{}
|
||||
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 1})
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 2})
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 3})
|
||||
|
||||
status := v1alpha1.ApplicationStatus{
|
||||
Resources: nil,
|
||||
Sync: v1alpha1.SyncStatus{},
|
||||
Health: v1alpha1.HealthStatus{},
|
||||
History: histories,
|
||||
Conditions: nil,
|
||||
ReconciledAt: nil,
|
||||
OperationState: nil,
|
||||
ObservedAt: nil,
|
||||
SourceType: "",
|
||||
Summary: v1alpha1.ApplicationSummary{},
|
||||
}
|
||||
|
||||
application := v1alpha1.Application{
|
||||
Status: status,
|
||||
}
|
||||
|
||||
history, err := findRevisionHistory(&application, -1)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Find revision history should fail without errors")
|
||||
}
|
||||
|
||||
if history == nil {
|
||||
t.Fatal("History should be found")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFindRevisionHistoryWithoutPassedIdAndEmptyHistoryList(t *testing.T) {
|
||||
|
||||
histories := v1alpha1.RevisionHistories{}
|
||||
|
||||
status := v1alpha1.ApplicationStatus{
|
||||
Resources: nil,
|
||||
Sync: v1alpha1.SyncStatus{},
|
||||
Health: v1alpha1.HealthStatus{},
|
||||
History: histories,
|
||||
Conditions: nil,
|
||||
ReconciledAt: nil,
|
||||
OperationState: nil,
|
||||
ObservedAt: nil,
|
||||
SourceType: "",
|
||||
Summary: v1alpha1.ApplicationSummary{},
|
||||
}
|
||||
|
||||
application := v1alpha1.Application{
|
||||
Status: status,
|
||||
}
|
||||
|
||||
history, err := findRevisionHistory(&application, -1)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Find revision history should fail with errors")
|
||||
}
|
||||
|
||||
if history != nil {
|
||||
t.Fatal("History should be empty")
|
||||
}
|
||||
|
||||
if err.Error() != "Application '' should have at least two successful deployments" {
|
||||
t.Fatal("Find revision history should fail with correct error message")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFindRevisionHistoryWithPassedId(t *testing.T) {
|
||||
|
||||
histories := v1alpha1.RevisionHistories{}
|
||||
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 1})
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 2})
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 3, Revision: "123"})
|
||||
|
||||
status := v1alpha1.ApplicationStatus{
|
||||
Resources: nil,
|
||||
Sync: v1alpha1.SyncStatus{},
|
||||
Health: v1alpha1.HealthStatus{},
|
||||
History: histories,
|
||||
Conditions: nil,
|
||||
ReconciledAt: nil,
|
||||
OperationState: nil,
|
||||
ObservedAt: nil,
|
||||
SourceType: "",
|
||||
Summary: v1alpha1.ApplicationSummary{},
|
||||
}
|
||||
|
||||
application := v1alpha1.Application{
|
||||
Status: status,
|
||||
}
|
||||
|
||||
history, err := findRevisionHistory(&application, 3)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Find revision history should fail without errors")
|
||||
}
|
||||
|
||||
if history == nil {
|
||||
t.Fatal("History should be found")
|
||||
}
|
||||
|
||||
if history.Revision != "123" {
|
||||
t.Fatal("Failed to find correct history with correct revision")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestFindRevisionHistoryWithPassedIdThatNotExist(t *testing.T) {
|
||||
|
||||
histories := v1alpha1.RevisionHistories{}
|
||||
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 1})
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 2})
|
||||
histories = append(histories, v1alpha1.RevisionHistory{ID: 3, Revision: "123"})
|
||||
|
||||
status := v1alpha1.ApplicationStatus{
|
||||
Resources: nil,
|
||||
Sync: v1alpha1.SyncStatus{},
|
||||
Health: v1alpha1.HealthStatus{},
|
||||
History: histories,
|
||||
Conditions: nil,
|
||||
ReconciledAt: nil,
|
||||
OperationState: nil,
|
||||
ObservedAt: nil,
|
||||
SourceType: "",
|
||||
Summary: v1alpha1.ApplicationSummary{},
|
||||
}
|
||||
|
||||
application := v1alpha1.Application{
|
||||
Status: status,
|
||||
}
|
||||
|
||||
history, err := findRevisionHistory(&application, 4)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Find revision history should fail with errors")
|
||||
}
|
||||
|
||||
if history != nil {
|
||||
t.Fatal("History should be not found")
|
||||
}
|
||||
|
||||
if err.Error() != "Application '' does not have deployment id '4' in history\n" {
|
||||
t.Fatal("Find revision history should fail with correct error message")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,12 +11,12 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
certificatepkg "github.com/argoproj/argo-cd/pkg/apiclient/certificate"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
certutil "github.com/argoproj/argo-cd/util/cert"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
certificatepkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/certificate"
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
certutil "github.com/argoproj/argo-cd/v2/util/cert"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
// NewCertCommand returns a new instance of an `argocd repo` command
|
||||
|
||||
@@ -4,22 +4,28 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/text/label"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/clusterauth"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
clusterpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/cluster"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/clusterauth"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
// NewClusterCommand returns a new instance of an `argocd cluster` command
|
||||
@@ -56,7 +62,10 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc
|
||||
// NewClusterAddCommand returns a new instance of an `argocd cluster add` command
|
||||
func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var (
|
||||
clusterOpts cmdutil.ClusterOptions
|
||||
clusterOpts cmdutil.ClusterOptions
|
||||
skipConfirmation bool
|
||||
labels []string
|
||||
annotations []string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "add CONTEXT",
|
||||
@@ -106,22 +115,39 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
|
||||
if clusterOpts.ServiceAccount != "" {
|
||||
managerBearerToken, err = clusterauth.GetServiceAccountBearerToken(clientset, clusterOpts.SystemNamespace, clusterOpts.ServiceAccount)
|
||||
} else {
|
||||
isTerminal := isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
|
||||
|
||||
if isTerminal && !skipConfirmation {
|
||||
message := fmt.Sprintf("WARNING: This will create a service account `argocd-manager` on the cluster referenced by context `%s` with full cluster level admin privileges. Do you want to continue [y/N]? ", contextName)
|
||||
if !cli.AskToProceed(message) {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
managerBearerToken, err = clusterauth.InstallClusterManagerRBAC(clientset, clusterOpts.SystemNamespace, clusterOpts.Namespaces)
|
||||
}
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
labelsMap, err := label.Parse(labels)
|
||||
errors.CheckError(err)
|
||||
annotationsMap, err := label.Parse(annotations)
|
||||
errors.CheckError(err)
|
||||
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer io.Close(conn)
|
||||
if clusterOpts.Name != "" {
|
||||
contextName = clusterOpts.Name
|
||||
}
|
||||
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, conf, managerBearerToken, awsAuthConf, execProviderConf)
|
||||
clst := cmdutil.NewCluster(contextName, clusterOpts.Namespaces, clusterOpts.ClusterResources, conf, managerBearerToken, awsAuthConf, execProviderConf, labelsMap, annotationsMap)
|
||||
if clusterOpts.InCluster {
|
||||
clst.Server = common.KubernetesInternalAPIServerAddr
|
||||
clst.Server = argoappv1.KubernetesInternalAPIServerAddr
|
||||
}
|
||||
if clusterOpts.Shard >= 0 {
|
||||
clst.Shard = &clusterOpts.Shard
|
||||
}
|
||||
if clusterOpts.Project != "" {
|
||||
clst.Project = clusterOpts.Project
|
||||
}
|
||||
clstCreateReq := clusterpkg.ClusterCreateRequest{
|
||||
Cluster: clst,
|
||||
Upsert: clusterOpts.Upsert,
|
||||
@@ -135,6 +161,9 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
|
||||
command.Flags().BoolVar(&clusterOpts.Upsert, "upsert", false, "Override an existing cluster with the same name even if the spec differs")
|
||||
command.Flags().StringVar(&clusterOpts.ServiceAccount, "service-account", "", fmt.Sprintf("System namespace service account to use for kubernetes resource management. If not set then default \"%s\" SA will be created", clusterauth.ArgoCDManagerServiceAccount))
|
||||
command.Flags().StringVar(&clusterOpts.SystemNamespace, "system-namespace", common.DefaultSystemNamespace, "Use different system namespace")
|
||||
command.Flags().BoolVarP(&skipConfirmation, "yes", "y", false, "Skip explicit confirmation")
|
||||
command.Flags().StringArrayVar(&labels, "label", nil, "Set metadata labels (e.g. --label key=value)")
|
||||
command.Flags().StringArrayVar(&annotations, "annotation", nil, "Set metadata annotations (e.g. --annotation key=value)")
|
||||
cmdutil.AddClusterFlags(command, &clusterOpts)
|
||||
return command
|
||||
}
|
||||
@@ -145,9 +174,10 @@ func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
output string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "get SERVER",
|
||||
Short: "Get cluster information",
|
||||
Example: `argocd cluster get https://12.34.567.89`,
|
||||
Use: "get SERVER/NAME",
|
||||
Short: "Get cluster information",
|
||||
Example: `argocd cluster get https://12.34.567.89
|
||||
argocd cluster get in-cluster`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
c.HelpFunc()(c, args)
|
||||
@@ -156,8 +186,8 @@ func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer io.Close(conn)
|
||||
clusters := make([]argoappv1.Cluster, 0)
|
||||
for _, clusterName := range args {
|
||||
clst, err := clusterIf.Get(context.Background(), &clusterpkg.ClusterQuery{Server: clusterName})
|
||||
for _, clusterSelector := range args {
|
||||
clst, err := clusterIf.Get(context.Background(), getQueryBySelector(clusterSelector))
|
||||
errors.CheckError(err)
|
||||
clusters = append(clusters, *clst)
|
||||
}
|
||||
@@ -244,17 +274,29 @@ func NewClusterRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
// Print table of cluster information
|
||||
func printClusterTable(clusters []argoappv1.Cluster) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "SERVER\tNAME\tVERSION\tSTATUS\tMESSAGE\n")
|
||||
_, _ = fmt.Fprintf(w, "SERVER\tNAME\tVERSION\tSTATUS\tMESSAGE\tPROJECT\n")
|
||||
for _, c := range clusters {
|
||||
server := c.Server
|
||||
if len(c.Namespaces) > 0 {
|
||||
server = fmt.Sprintf("%s (%d namespaces)", c.Server, len(c.Namespaces))
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", server, c.Name, c.ServerVersion, c.ConnectionState.Status, c.ConnectionState.Message)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", server, c.Name, c.ServerVersion, c.ConnectionState.Status, c.ConnectionState.Message, c.Project)
|
||||
}
|
||||
_ = w.Flush()
|
||||
}
|
||||
|
||||
// Returns cluster query for getting cluster depending on the cluster selector
|
||||
func getQueryBySelector(clusterSelector string) *clusterpkg.ClusterQuery {
|
||||
var query clusterpkg.ClusterQuery
|
||||
isServer, err := regexp.MatchString(`^https?://`, clusterSelector)
|
||||
if isServer || err != nil {
|
||||
query.Server = clusterSelector
|
||||
} else {
|
||||
query.Name = clusterSelector
|
||||
}
|
||||
return &query
|
||||
}
|
||||
|
||||
// Print list of cluster servers
|
||||
func printClusterServers(clusters []argoappv1.Cluster) {
|
||||
for _, c := range clusters {
|
||||
|
||||
@@ -5,9 +5,25 @@ import (
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_getQueryBySelector(t *testing.T) {
|
||||
query := getQueryBySelector("my-cluster")
|
||||
assert.Equal(t, query.Name, "my-cluster")
|
||||
assert.Equal(t, query.Server, "")
|
||||
|
||||
query = getQueryBySelector("http://my-server")
|
||||
assert.Equal(t, query.Name, "")
|
||||
assert.Equal(t, query.Server, "http://my-server")
|
||||
|
||||
query = getQueryBySelector("https://my-server")
|
||||
assert.Equal(t, query.Name, "")
|
||||
assert.Equal(t, query.Server, "https://my-server")
|
||||
}
|
||||
|
||||
func Test_printClusterTable(t *testing.T) {
|
||||
printClusterTable([]v1alpha1.Cluster{
|
||||
{
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
)
|
||||
|
||||
// NewContextCommand returns a new instance of an `argocd ctx` command
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
)
|
||||
|
||||
const testConfig = `contexts:
|
||||
|
||||
@@ -10,11 +10,11 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
gpgkeypkg "github.com/argoproj/argo-cd/pkg/apiclient/gpgkey"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/util/io"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
gpgkeypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/gpgkey"
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
// NewGPGCommand returns a new instance of an `argocd repo` command
|
||||
|
||||
103
cmd/argocd/commands/headless/forward.go
Normal file
103
cmd/argocd/commands/headless/forward.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package headless
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
repoapiclient "github.com/argoproj/argo-cd/v2/reposerver/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/cache"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
kubeutil "github.com/argoproj/argo-cd/v2/util/kube"
|
||||
)
|
||||
|
||||
type forwardCacheClient struct {
|
||||
namespace string
|
||||
context string
|
||||
init sync.Once
|
||||
client cache.CacheClient
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) doLazy(action func(client cache.CacheClient) error) error {
|
||||
c.init.Do(func() {
|
||||
overrides := clientcmd.ConfigOverrides{
|
||||
CurrentContext: c.context,
|
||||
}
|
||||
redisPort, err := kubeutil.PortForward(6379, c.namespace, &overrides,
|
||||
"app.kubernetes.io/name=argocd-redis-ha-haproxy", "app.kubernetes.io/name=argocd-redis")
|
||||
if err != nil {
|
||||
c.err = err
|
||||
return
|
||||
}
|
||||
|
||||
redisClient := redis.NewClient(&redis.Options{Addr: fmt.Sprintf("localhost:%d", redisPort)})
|
||||
c.client = cache.NewRedisCache(redisClient, time.Hour)
|
||||
})
|
||||
if c.err != nil {
|
||||
return c.err
|
||||
}
|
||||
return action(c.client)
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) Set(item *cache.Item) error {
|
||||
return c.doLazy(func(client cache.CacheClient) error {
|
||||
return client.Set(item)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) Get(key string, obj interface{}) error {
|
||||
return c.doLazy(func(client cache.CacheClient) error {
|
||||
return client.Get(key, obj)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) Delete(key string) error {
|
||||
return c.doLazy(func(client cache.CacheClient) error {
|
||||
return client.Delete(key)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) OnUpdated(ctx context.Context, key string, callback func() error) error {
|
||||
return c.doLazy(func(client cache.CacheClient) error {
|
||||
return client.OnUpdated(ctx, key, callback)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *forwardCacheClient) NotifyUpdated(key string) error {
|
||||
return c.doLazy(func(client cache.CacheClient) error {
|
||||
return client.NotifyUpdated(key)
|
||||
})
|
||||
}
|
||||
|
||||
type forwardRepoClientset struct {
|
||||
namespace string
|
||||
context string
|
||||
init sync.Once
|
||||
repoClientset repoapiclient.Clientset
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *forwardRepoClientset) NewRepoServerClient() (io.Closer, repoapiclient.RepoServerServiceClient, error) {
|
||||
c.init.Do(func() {
|
||||
overrides := clientcmd.ConfigOverrides{
|
||||
CurrentContext: c.context,
|
||||
}
|
||||
repoServerPort, err := kubeutil.PortForward(8081, c.namespace, &overrides, "app.kubernetes.io/name=argocd-repo-server")
|
||||
if err != nil {
|
||||
c.err = err
|
||||
return
|
||||
}
|
||||
c.repoClientset = apiclient.NewRepoServerClientset(fmt.Sprintf("localhost:%d", repoServerPort), 60, apiclient.TLSConfiguration{
|
||||
DisableTLS: false, StrictValidation: false})
|
||||
})
|
||||
if c.err != nil {
|
||||
return nil, nil, c.err
|
||||
}
|
||||
return c.repoClientset.NewRepoServerClient()
|
||||
}
|
||||
167
cmd/argocd/commands/headless/headless.go
Normal file
167
cmd/argocd/commands/headless/headless.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package headless
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/alicebob/miniredis/v2"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
argoapi "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
appclientset "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned"
|
||||
"github.com/argoproj/argo-cd/v2/server"
|
||||
servercache "github.com/argoproj/argo-cd/v2/server/cache"
|
||||
cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
|
||||
appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func testAPI(clientOpts *argoapi.ClientOptions) error {
|
||||
apiClient, err := argoapi.NewClient(clientOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
closer, versionClient, err := apiClient.NewVersionClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer io.Close(closer)
|
||||
_, err = versionClient.Version(context.Background(), &empty.Empty{})
|
||||
return err
|
||||
}
|
||||
|
||||
func retrieveContextIfChanged(contextFlag *flag.Flag) string {
|
||||
if contextFlag != nil && contextFlag.Changed {
|
||||
return contextFlag.Value.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// InitCommand allows executing command in a headless mode: on the fly starts Argo CD API server and
|
||||
// changes provided client options to use started API server port
|
||||
func InitCommand(cmd *cobra.Command, clientOpts *argoapi.ClientOptions, port *int, address *string) *cobra.Command {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
flags := pflag.NewFlagSet("tmp", pflag.ContinueOnError)
|
||||
clientConfig := cli.AddKubectlFlagsToSet(flags)
|
||||
// copy k8s persistent flags into argocd command flags
|
||||
flags.VisitAll(func(flag *pflag.Flag) {
|
||||
// skip Kubernetes server flags since argocd has it's own server flag
|
||||
if flag.Name == "server" {
|
||||
return
|
||||
}
|
||||
cmd.Flags().AddFlag(flag)
|
||||
})
|
||||
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
|
||||
startInProcessAPI := clientOpts.Core
|
||||
if !startInProcessAPI {
|
||||
localCfg, err := localconfig.ReadLocalConfig(clientOpts.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if localCfg != nil {
|
||||
configCtx, err := localCfg.ResolveContext(clientOpts.Context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
startInProcessAPI = configCtx.Server.Core
|
||||
}
|
||||
}
|
||||
if !startInProcessAPI {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get rid of logging error handler
|
||||
runtime.ErrorHandlers = runtime.ErrorHandlers[1:]
|
||||
cli.SetLogLevel(log.ErrorLevel.String())
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
os.Setenv(v1alpha1.EnvVarFakeInClusterConfig, "true")
|
||||
if address == nil {
|
||||
address = pointer.String("localhost")
|
||||
}
|
||||
if port == nil || *port == 0 {
|
||||
addr := fmt.Sprintf("%s:0", *address)
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
port = &ln.Addr().(*net.TCPAddr).Port
|
||||
io.Close(ln)
|
||||
}
|
||||
|
||||
restConfig, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
appClientset, err := appclientset.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
kubeClientset, err := kubernetes.NewForConfig(restConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context := retrieveContextIfChanged(cmd.Flag("context"))
|
||||
|
||||
mr, err := miniredis.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appstateCache := appstatecache.NewCache(cacheutil.NewCache(&forwardCacheClient{namespace: namespace, context: context}), time.Hour)
|
||||
srv := server.NewServer(ctx, server.ArgoCDServerOpts{
|
||||
EnableGZip: false,
|
||||
Namespace: namespace,
|
||||
ListenPort: *port,
|
||||
AppClientset: appClientset,
|
||||
DisableAuth: true,
|
||||
RedisClient: redis.NewClient(&redis.Options{Addr: mr.Addr()}),
|
||||
Cache: servercache.NewCache(appstateCache, 0, 0, 0),
|
||||
KubeClientset: kubeClientset,
|
||||
Insecure: true,
|
||||
ListenHost: *address,
|
||||
RepoClientset: &forwardRepoClientset{namespace: namespace, context: context},
|
||||
})
|
||||
|
||||
go srv.Run(ctx, *port, 0)
|
||||
clientOpts.ServerAddr = fmt.Sprintf("%s:%d", *address, *port)
|
||||
clientOpts.PlainText = true
|
||||
if !cache.WaitForCacheSync(ctx.Done(), srv.Initialized) {
|
||||
log.Fatal("Timed out waiting for project cache to sync")
|
||||
}
|
||||
tries := 5
|
||||
for i := 0; i < tries; i++ {
|
||||
err = testAPI(clientOpts)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
return err
|
||||
}
|
||||
cmd.PostRun = func(cmd *cobra.Command, args []string) {
|
||||
cancel()
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
80
cmd/argocd/commands/headless/headless_test.go
Normal file
80
cmd/argocd/commands/headless/headless_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package headless
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type StringFlag struct {
|
||||
// The exact value provided on the flag
|
||||
value string
|
||||
}
|
||||
|
||||
func (f StringFlag) String() string {
|
||||
return f.value
|
||||
}
|
||||
|
||||
func (f *StringFlag) Set(value string) error {
|
||||
f.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *StringFlag) Type() string {
|
||||
return "string"
|
||||
}
|
||||
|
||||
func Test_FlagContextNotChanged(t *testing.T) {
|
||||
res := retrieveContextIfChanged(&flag.Flag{
|
||||
Name: "",
|
||||
Shorthand: "",
|
||||
Usage: "",
|
||||
Value: &StringFlag{value: "test"},
|
||||
DefValue: "",
|
||||
Changed: false,
|
||||
NoOptDefVal: "",
|
||||
Deprecated: "",
|
||||
Hidden: false,
|
||||
ShorthandDeprecated: "",
|
||||
Annotations: nil,
|
||||
})
|
||||
|
||||
assert.Equal(t, "", res)
|
||||
}
|
||||
|
||||
func Test_FlagContextChanged(t *testing.T) {
|
||||
res := retrieveContextIfChanged(&flag.Flag{
|
||||
Name: "",
|
||||
Shorthand: "",
|
||||
Usage: "",
|
||||
Value: &StringFlag{value: "test"},
|
||||
DefValue: "",
|
||||
Changed: true,
|
||||
NoOptDefVal: "",
|
||||
Deprecated: "",
|
||||
Hidden: false,
|
||||
ShorthandDeprecated: "",
|
||||
Annotations: nil,
|
||||
})
|
||||
|
||||
assert.Equal(t, "test", res)
|
||||
}
|
||||
|
||||
func Test_FlagContextNil(t *testing.T) {
|
||||
res := retrieveContextIfChanged(&flag.Flag{
|
||||
Name: "",
|
||||
Shorthand: "",
|
||||
Usage: "",
|
||||
Value: nil,
|
||||
DefValue: "",
|
||||
Changed: false,
|
||||
NoOptDefVal: "",
|
||||
Deprecated: "",
|
||||
Hidden: false,
|
||||
ShorthandDeprecated: "",
|
||||
Annotations: nil,
|
||||
})
|
||||
|
||||
assert.Equal(t, "", res)
|
||||
}
|
||||
@@ -13,23 +13,23 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc"
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
sessionpkg "github.com/argoproj/argo-cd/pkg/apiclient/session"
|
||||
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
grpc_util "github.com/argoproj/argo-cd/util/grpc"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
jwtutil "github.com/argoproj/argo-cd/util/jwt"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
oidcutil "github.com/argoproj/argo-cd/util/oidc"
|
||||
"github.com/argoproj/argo-cd/util/rand"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
sessionpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/session"
|
||||
settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
grpc_util "github.com/argoproj/argo-cd/v2/util/grpc"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
jwtutil "github.com/argoproj/argo-cd/v2/util/jwt"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
oidcutil "github.com/argoproj/argo-cd/v2/util/oidc"
|
||||
"github.com/argoproj/argo-cd/v2/util/rand"
|
||||
)
|
||||
|
||||
// NewLoginCommand returns a new instance of `argocd login` command
|
||||
@@ -45,16 +45,26 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
Use: "login SERVER",
|
||||
Short: "Log in to Argo CD",
|
||||
Long: "Log in to Argo CD",
|
||||
Example: `# Login to Argo CD using a username and password
|
||||
argocd login cd.argoproj.io
|
||||
|
||||
# Login to Argo CD using SSO
|
||||
argocd login cd.argoproj.io --sso
|
||||
|
||||
# Configure direct access using Kubernetes API server
|
||||
argocd login cd.argoproj.io --core`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
var server string
|
||||
|
||||
if len(args) != 1 && !globalClientOpts.PortForward {
|
||||
if len(args) != 1 && !globalClientOpts.PortForward && !globalClientOpts.Core {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if globalClientOpts.PortForward {
|
||||
server = "port-forward"
|
||||
} else if globalClientOpts.Core {
|
||||
server = "kubernetes"
|
||||
} else {
|
||||
server = args[0]
|
||||
tlsTestResult, err := grpc_util.TestTLS(server)
|
||||
@@ -80,15 +90,14 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
ServerAddr: server,
|
||||
Insecure: globalClientOpts.Insecure,
|
||||
PlainText: globalClientOpts.PlainText,
|
||||
ClientCertFile: globalClientOpts.ClientCertFile,
|
||||
ClientCertKeyFile: globalClientOpts.ClientCertKeyFile,
|
||||
GRPCWeb: globalClientOpts.GRPCWeb,
|
||||
GRPCWebRootPath: globalClientOpts.GRPCWebRootPath,
|
||||
PortForward: globalClientOpts.PortForward,
|
||||
PortForwardNamespace: globalClientOpts.PortForwardNamespace,
|
||||
Headers: globalClientOpts.Headers,
|
||||
}
|
||||
acdClient := argocdclient.NewClientOrDie(&clientOpts)
|
||||
setConn, setIf := acdClient.NewSettingsClientOrDie()
|
||||
defer io.Close(setConn)
|
||||
|
||||
if ctxName == "" {
|
||||
ctxName = server
|
||||
@@ -101,28 +110,30 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
// Perform the login
|
||||
var tokenString string
|
||||
var refreshToken string
|
||||
if !sso {
|
||||
tokenString = passwordLogin(acdClient, username, password)
|
||||
} else {
|
||||
ctx := context.Background()
|
||||
httpClient, err := acdClient.HTTPClient()
|
||||
if !globalClientOpts.Core {
|
||||
acdClient := argocdclient.NewClientOrDie(&clientOpts)
|
||||
setConn, setIf := acdClient.NewSettingsClientOrDie()
|
||||
defer io.Close(setConn)
|
||||
if !sso {
|
||||
tokenString = passwordLogin(acdClient, username, password)
|
||||
} else {
|
||||
ctx := context.Background()
|
||||
httpClient, err := acdClient.HTTPClient()
|
||||
errors.CheckError(err)
|
||||
ctx = oidc.ClientContext(ctx, httpClient)
|
||||
acdSet, err := setIf.Get(ctx, &settingspkg.SettingsQuery{})
|
||||
errors.CheckError(err)
|
||||
oauth2conf, provider, err := acdClient.OIDCConfig(ctx, acdSet)
|
||||
errors.CheckError(err)
|
||||
tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider)
|
||||
}
|
||||
parser := jwt.NewParser(jwt.WithoutClaimsValidation())
|
||||
claims := jwt.MapClaims{}
|
||||
_, _, err := parser.ParseUnverified(tokenString, &claims)
|
||||
errors.CheckError(err)
|
||||
ctx = oidc.ClientContext(ctx, httpClient)
|
||||
acdSet, err := setIf.Get(ctx, &settingspkg.SettingsQuery{})
|
||||
errors.CheckError(err)
|
||||
oauth2conf, provider, err := acdClient.OIDCConfig(ctx, acdSet)
|
||||
errors.CheckError(err)
|
||||
tokenString, refreshToken = oauth2Login(ctx, ssoPort, acdSet.GetOIDCConfig(), oauth2conf, provider)
|
||||
fmt.Printf("'%s' logged in successfully\n", userDisplayName(claims))
|
||||
}
|
||||
|
||||
parser := &jwt.Parser{
|
||||
ValidationHelper: jwt.NewValidationHelper(jwt.WithoutClaimsValidation(), jwt.WithoutAudienceValidation()),
|
||||
}
|
||||
claims := jwt.MapClaims{}
|
||||
_, _, err := parser.ParseUnverified(tokenString, &claims)
|
||||
errors.CheckError(err)
|
||||
|
||||
fmt.Printf("'%s' logged in successfully\n", userDisplayName(claims))
|
||||
// login successful. Persist the config
|
||||
localCfg, err := localconfig.ReadLocalConfig(globalClientOpts.ConfigPath)
|
||||
errors.CheckError(err)
|
||||
@@ -135,6 +146,7 @@ func NewLoginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
Insecure: globalClientOpts.Insecure,
|
||||
GRPCWeb: globalClientOpts.GRPCWeb,
|
||||
GRPCWebRootPath: globalClientOpts.GRPCWebRootPath,
|
||||
Core: globalClientOpts.Core,
|
||||
})
|
||||
localCfg.UpsertUser(localconfig.User{
|
||||
Name: ctxName,
|
||||
|
||||
@@ -3,7 +3,7 @@ package commands
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/dgrijalva/jwt-go/v4"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
)
|
||||
|
||||
// NewLogoutCommand returns a new instance of `argocd logout` command
|
||||
|
||||
@@ -5,11 +5,11 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
)
|
||||
|
||||
func TestLogout(t *testing.T) {
|
||||
|
||||
@@ -14,19 +14,18 @@ import (
|
||||
"github.com/ghodss/yaml"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/gpg"
|
||||
argoio "github.com/argoproj/argo-cd/util/io"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
type policyOpts struct {
|
||||
@@ -129,23 +128,7 @@ func NewProjectSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
|
||||
visited := 0
|
||||
c.Flags().Visit(func(f *pflag.Flag) {
|
||||
visited++
|
||||
switch f.Name {
|
||||
case "description":
|
||||
proj.Spec.Description = opts.Description
|
||||
case "dest":
|
||||
proj.Spec.Destinations = opts.GetDestinations()
|
||||
case "src":
|
||||
proj.Spec.SourceRepos = opts.Sources
|
||||
case "signature-keys":
|
||||
proj.Spec.SignatureKeys = opts.GetSignatureKeys()
|
||||
case "orphaned-resources", "orphaned-resources-warn":
|
||||
proj.Spec.OrphanedResources = cmdutil.GetOrphanedResourcesSettings(c, opts)
|
||||
}
|
||||
})
|
||||
if visited == 0 {
|
||||
if visited := cmdutil.SetProjSpecOptions(c.Flags(), &proj.Spec, &opts); visited == 0 {
|
||||
log.Error("Please set at least one option to update")
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
@@ -236,8 +219,17 @@ func NewProjectRemoveSignatureKeyCommand(clientOpts *argocdclient.ClientOptions)
|
||||
|
||||
// NewProjectAddDestinationCommand returns a new instance of an `argocd proj add-destination` command
|
||||
func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var nameInsteadServer bool
|
||||
|
||||
buildApplicationDestination := func(destination string, namespace string, nameInsteadServer bool) v1alpha1.ApplicationDestination {
|
||||
if nameInsteadServer {
|
||||
return v1alpha1.ApplicationDestination{Name: destination, Namespace: namespace}
|
||||
}
|
||||
return v1alpha1.ApplicationDestination{Server: destination, Namespace: namespace}
|
||||
}
|
||||
|
||||
var command = &cobra.Command{
|
||||
Use: "add-destination PROJECT SERVER NAMESPACE",
|
||||
Use: "add-destination PROJECT SERVER/NAME NAMESPACE",
|
||||
Short: "Add project destination",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 3 {
|
||||
@@ -245,8 +237,8 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
os.Exit(1)
|
||||
}
|
||||
projName := args[0]
|
||||
server := args[1]
|
||||
namespace := args[2]
|
||||
destination := buildApplicationDestination(args[1], namespace, nameInsteadServer)
|
||||
conn, projIf := argocdclient.NewClientOrDie(clientOpts).NewProjectClientOrDie()
|
||||
defer argoio.Close(conn)
|
||||
|
||||
@@ -254,15 +246,18 @@ func NewProjectAddDestinationCommand(clientOpts *argocdclient.ClientOptions) *co
|
||||
errors.CheckError(err)
|
||||
|
||||
for _, dest := range proj.Spec.Destinations {
|
||||
if dest.Namespace == namespace && dest.Server == server {
|
||||
dstServerExist := destination.Server != "" && dest.Server == destination.Server
|
||||
dstNameExist := destination.Name != "" && dest.Name == destination.Name
|
||||
if dest.Namespace == namespace && (dstServerExist || dstNameExist) {
|
||||
log.Fatal("Specified destination is already defined in project")
|
||||
}
|
||||
}
|
||||
proj.Spec.Destinations = append(proj.Spec.Destinations, v1alpha1.ApplicationDestination{Server: server, Namespace: namespace})
|
||||
proj.Spec.Destinations = append(proj.Spec.Destinations, destination)
|
||||
_, err = projIf.Update(context.Background(), &projectpkg.ProjectUpdateRequest{Project: proj})
|
||||
errors.CheckError(err)
|
||||
},
|
||||
}
|
||||
command.Flags().BoolVar(&nameInsteadServer, "name", false, "Use name as destination instead server")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -9,15 +9,15 @@ import (
|
||||
"time"
|
||||
|
||||
timeutil "github.com/argoproj/pkg/time"
|
||||
jwtgo "github.com/dgrijalva/jwt-go/v4"
|
||||
jwtgo "github.com/golang-jwt/jwt/v4"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/jwt"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/jwt"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -265,7 +265,7 @@ func NewProjectRoleCreateTokenCommand(clientOpts *argocdclient.ClientOptions) *c
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&expiresIn, "expires-in", "e", "",
|
||||
"Duration before the token will expire, eg \"12h\", \"7d\". (Default: No expiration)",
|
||||
"Duration before the token will expire, e.g. \"12h\", \"7d\". (Default: No expiration)",
|
||||
)
|
||||
command.Flags().StringVarP(&tokenID, "id", "i", "", "Token unique identifier. (Default: Random UUID)")
|
||||
command.Flags().BoolVarP(&outputTokenOnly, "token-only", "t", false, "Output token only - for use in scripts.")
|
||||
|
||||
@@ -10,11 +10,11 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
projectpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/project"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
// NewProjectWindowsCommand returns a new instance of the `argocd proj windows` command
|
||||
@@ -116,6 +116,7 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) *
|
||||
namespaces []string
|
||||
clusters []string
|
||||
manualSync bool
|
||||
timeZone string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "add PROJECT",
|
||||
@@ -132,7 +133,7 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) *
|
||||
proj, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
|
||||
errors.CheckError(err)
|
||||
|
||||
err = proj.Spec.AddWindow(kind, schedule, duration, applications, namespaces, clusters, manualSync)
|
||||
err = proj.Spec.AddWindow(kind, schedule, duration, applications, namespaces, clusters, manualSync, timeZone)
|
||||
errors.CheckError(err)
|
||||
|
||||
_, err = projIf.Update(context.Background(), &projectpkg.ProjectUpdateRequest{Project: proj})
|
||||
@@ -146,6 +147,7 @@ func NewProjectWindowsAddWindowCommand(clientOpts *argocdclient.ClientOptions) *
|
||||
command.Flags().StringSliceVar(&namespaces, "namespaces", []string{}, "Namespaces that the schedule will be applied to. Comma separated, wildcards supported (e.g. --namespaces default,\\*-prod)")
|
||||
command.Flags().StringSliceVar(&clusters, "clusters", []string{}, "Clusters that the schedule will be applied to. Comma separated, wildcards supported (e.g. --clusters prod,staging)")
|
||||
command.Flags().BoolVar(&manualSync, "manual-sync", false, "Allow manual syncs for both deny and allow windows")
|
||||
command.Flags().StringVar(&timeZone, "time-zone", "UTC", "Time zone of the sync window")
|
||||
|
||||
return command
|
||||
}
|
||||
@@ -189,6 +191,7 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
applications []string
|
||||
namespaces []string
|
||||
clusters []string
|
||||
timeZone string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "update PROJECT ID",
|
||||
@@ -212,7 +215,7 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
|
||||
for i, window := range proj.Spec.SyncWindows {
|
||||
if id == i {
|
||||
err := window.Update(schedule, duration, applications, namespaces, clusters)
|
||||
err := window.Update(schedule, duration, applications, namespaces, clusters, timeZone)
|
||||
if err != nil {
|
||||
errors.CheckError(err)
|
||||
}
|
||||
@@ -228,6 +231,7 @@ func NewProjectWindowsUpdateCommand(clientOpts *argocdclient.ClientOptions) *cob
|
||||
command.Flags().StringSliceVar(&applications, "applications", []string{}, "Applications that the schedule will be applied to. Comma separated, wildcards supported (e.g. --applications prod-\\*,website)")
|
||||
command.Flags().StringSliceVar(&namespaces, "namespaces", []string{}, "Namespaces that the schedule will be applied to. Comma separated, wildcards supported (e.g. --namespaces default,\\*-prod)")
|
||||
command.Flags().StringSliceVar(&clusters, "clusters", []string{}, "Clusters that the schedule will be applied to. Comma separated, wildcards supported (e.g. --clusters prod,staging)")
|
||||
command.Flags().StringVar(&timeZone, "time-zone", "UTC", "Time zone of the sync window. (e.g. --time-zone \"America/New_York\")")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
settingspkg "github.com/argoproj/argo-cd/pkg/apiclient/settings"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
"github.com/argoproj/argo-cd/util/session"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
settingspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/settings"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
"github.com/argoproj/argo-cd/v2/util/session"
|
||||
)
|
||||
|
||||
// NewReloginCommand returns a new instance of `argocd relogin` command
|
||||
@@ -43,20 +43,22 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
var tokenString string
|
||||
var refreshToken string
|
||||
clientOpts := argocdclient.ClientOptions{
|
||||
ConfigPath: "",
|
||||
ServerAddr: configCtx.Server.Server,
|
||||
Insecure: configCtx.Server.Insecure,
|
||||
GRPCWeb: globalClientOpts.GRPCWeb,
|
||||
GRPCWebRootPath: globalClientOpts.GRPCWebRootPath,
|
||||
PlainText: configCtx.Server.PlainText,
|
||||
Headers: globalClientOpts.Headers,
|
||||
ConfigPath: "",
|
||||
ServerAddr: configCtx.Server.Server,
|
||||
Insecure: configCtx.Server.Insecure,
|
||||
ClientCertFile: globalClientOpts.ClientCertFile,
|
||||
ClientCertKeyFile: globalClientOpts.ClientCertKeyFile,
|
||||
GRPCWeb: globalClientOpts.GRPCWeb,
|
||||
GRPCWebRootPath: globalClientOpts.GRPCWebRootPath,
|
||||
PlainText: configCtx.Server.PlainText,
|
||||
Headers: globalClientOpts.Headers,
|
||||
}
|
||||
acdClient := argocdclient.NewClientOrDie(&clientOpts)
|
||||
claims, err := configCtx.User.Claims()
|
||||
errors.CheckError(err)
|
||||
if claims.Issuer == session.SessionManagerClaimsIssuer {
|
||||
fmt.Printf("Relogging in as '%s'\n", claims.Subject)
|
||||
tokenString = passwordLogin(acdClient, claims.Subject, password)
|
||||
fmt.Printf("Relogging in as '%s'\n", localconfig.GetUsername(claims.Subject))
|
||||
tokenString = passwordLogin(acdClient, localconfig.GetUsername(claims.Subject), password)
|
||||
} else {
|
||||
fmt.Println("Reinitiating SSO login")
|
||||
setConn, setIf := acdClient.NewSettingsClientOrDie()
|
||||
|
||||
@@ -10,14 +10,14 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
// NewRepoCommand returns a new instance of an `argocd repo` command
|
||||
@@ -58,10 +58,10 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
argocd repo add https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
|
||||
|
||||
# Add a public Helm repository named 'stable' via HTTPS
|
||||
argocd repo add https://kubernetes-charts.storage.googleapis.com --type helm --name stable
|
||||
argocd repo add https://charts.helm.sh/stable --type helm --name stable
|
||||
|
||||
# Add a private Helm repository named 'stable' via HTTPS
|
||||
argocd repo add https://kubernetes-charts.storage.googleapis.com --type helm --name stable --username test --password test
|
||||
argocd repo add https://charts.helm.sh/stable --type helm --name stable --username test --password test
|
||||
|
||||
# Add a private Helm OCI-based repository named 'stable' via HTTPS
|
||||
argocd repo add helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
|
||||
@@ -144,6 +144,7 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
repoOpts.Repo.GithubAppId = repoOpts.GithubAppId
|
||||
repoOpts.Repo.GithubAppInstallationId = repoOpts.GithubAppInstallationId
|
||||
repoOpts.Repo.GitHubAppEnterpriseBaseURL = repoOpts.GitHubAppEnterpriseBaseURL
|
||||
repoOpts.Repo.Proxy = repoOpts.Proxy
|
||||
|
||||
if repoOpts.Repo.Type == "helm" && repoOpts.Repo.Name == "" {
|
||||
errors.CheckError(fmt.Errorf("Must specify --name for repos of type 'helm'"))
|
||||
@@ -180,6 +181,8 @@ func NewRepoAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
GithubAppID: repoOpts.Repo.GithubAppId,
|
||||
GithubAppInstallationID: repoOpts.Repo.GithubAppInstallationId,
|
||||
GithubAppEnterpriseBaseUrl: repoOpts.Repo.GitHubAppEnterpriseBaseURL,
|
||||
Proxy: repoOpts.Proxy,
|
||||
Project: repoOpts.Repo.Project,
|
||||
}
|
||||
_, err := repoIf.ValidateAccess(context.Background(), &repoAccessReq)
|
||||
errors.CheckError(err)
|
||||
@@ -224,7 +227,7 @@ func NewRepoRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
// Print table of repo info
|
||||
func printRepoTable(repos appsv1.Repositories) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tOCI\tLFS\tCREDS\tSTATUS\tMESSAGE\n")
|
||||
_, _ = fmt.Fprintf(w, "TYPE\tNAME\tREPO\tINSECURE\tOCI\tLFS\tCREDS\tSTATUS\tMESSAGE\tPROJECT\n")
|
||||
for _, r := range repos {
|
||||
var hasCreds string
|
||||
if !r.HasCredentials() {
|
||||
@@ -236,7 +239,7 @@ func printRepoTable(repos appsv1.Repositories) {
|
||||
hasCreds = "true"
|
||||
}
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%v\t%s\t%s\t%s\n", r.Type, r.Name, r.Repo, r.IsInsecure(), r.EnableOCI, r.EnableLFS, hasCreds, r.ConnectionState.Status, r.ConnectionState.Message)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%v\t%v\t%s\t%s\t%s\t%s\n", r.Type, r.Name, r.Repo, r.IsInsecure(), r.EnableOCI, r.EnableLFS, hasCreds, r.ConnectionState.Status, r.ConnectionState.Message, r.Project)
|
||||
}
|
||||
_ = w.Flush()
|
||||
}
|
||||
|
||||
@@ -10,13 +10,14 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
repocredspkg "github.com/argoproj/argo-cd/pkg/apiclient/repocreds"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
repocredspkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repocreds"
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
// NewRepoCredsCommand returns a new instance of an `argocd repocreds` command
|
||||
@@ -59,6 +60,9 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
|
||||
|
||||
# Add credentials with GitHub App authentication to use for all repositories under https://ghe.example.com/repos
|
||||
argocd repocreds add https://ghe.example.com/repos/ --github-app-id 1 --github-app-installation-id 2 --github-app-private-key-path test.private-key.pem --github-app-enterprise-base-url https://ghe.example.com/api/v3
|
||||
|
||||
# Add credentials with helm oci registry so that these oci registry urls do not need to be added as repos individually.
|
||||
argocd repocreds add localhost:5000/myrepo --enable-oci --type helm
|
||||
`
|
||||
|
||||
var command = &cobra.Command{
|
||||
@@ -151,6 +155,8 @@ func NewRepoCredsAddCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comma
|
||||
command.Flags().StringVar(&githubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
|
||||
command.Flags().StringVar(&repo.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
|
||||
command.Flags().BoolVar(&upsert, "upsert", false, "Override an existing repository with the same name even if the spec differs")
|
||||
command.Flags().BoolVar(&repo.EnableOCI, "enable-oci", false, "Specifies whether helm-oci support should be enabled for this repo")
|
||||
command.Flags().StringVar(&repo.Type, "type", common.DefaultRepoType, "type of the repository, \"git\" or \"helm\"")
|
||||
return command
|
||||
}
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/localconfig"
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/admin"
|
||||
"github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless"
|
||||
cmdutil "github.com/argoproj/argo-cd/v2/cmd/util"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/util/cli"
|
||||
"github.com/argoproj/argo-cd/v2/util/config"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/localconfig"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -38,19 +40,20 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
command.AddCommand(NewCompletionCommand())
|
||||
command.AddCommand(NewVersionCmd(&clientOpts))
|
||||
command.AddCommand(NewClusterCommand(&clientOpts, pathOpts))
|
||||
command.AddCommand(NewApplicationCommand(&clientOpts))
|
||||
command.AddCommand(headless.InitCommand(NewVersionCmd(&clientOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(headless.InitCommand(NewClusterCommand(&clientOpts, pathOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(headless.InitCommand(NewApplicationCommand(&clientOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(NewLoginCommand(&clientOpts))
|
||||
command.AddCommand(NewReloginCommand(&clientOpts))
|
||||
command.AddCommand(NewRepoCommand(&clientOpts))
|
||||
command.AddCommand(NewRepoCredsCommand(&clientOpts))
|
||||
command.AddCommand(headless.InitCommand(NewRepoCommand(&clientOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(headless.InitCommand(NewRepoCredsCommand(&clientOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(NewContextCommand(&clientOpts))
|
||||
command.AddCommand(NewProjectCommand(&clientOpts))
|
||||
command.AddCommand(NewAccountCommand(&clientOpts))
|
||||
command.AddCommand(headless.InitCommand(NewProjectCommand(&clientOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(headless.InitCommand(NewAccountCommand(&clientOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(NewLogoutCommand(&clientOpts))
|
||||
command.AddCommand(NewCertCommand(&clientOpts))
|
||||
command.AddCommand(NewGPGCommand(&clientOpts))
|
||||
command.AddCommand(headless.InitCommand(NewCertCommand(&clientOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(headless.InitCommand(NewGPGCommand(&clientOpts), &clientOpts, nil, nil))
|
||||
command.AddCommand(admin.NewAdminCommand())
|
||||
|
||||
defaultLocalConfigPath, err := localconfig.DefaultLocalConfigPath()
|
||||
errors.CheckError(err)
|
||||
@@ -69,5 +72,7 @@ func NewCommand() *cobra.Command {
|
||||
command.PersistentFlags().StringSliceVarP(&clientOpts.Headers, "header", "H", []string{}, "Sets additional header to all requests made by Argo CD CLI. (Can be repeated multiple times to add multiple headers, also supports comma separated headers)")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.PortForward, "port-forward", config.GetBoolFlag("port-forward"), "Connect to a random argocd-server port using port forwarding")
|
||||
command.PersistentFlags().StringVar(&clientOpts.PortForwardNamespace, "port-forward-namespace", config.GetFlag("port-forward-namespace", ""), "Namespace name which should be used for port forwarding")
|
||||
command.PersistentFlags().IntVar(&clientOpts.HttpRetryMax, "http-retry-max", 0, "Maximum number of retries to establish http connection to Argo CD server")
|
||||
command.PersistentFlags().BoolVar(&clientOpts.Core, "core", false, "If set to true then CLI talks directly to Kubernetes instead of talking to Argo CD API server")
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
argocdclient "github.com/argoproj/argo-cd/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/pkg/apiclient/version"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/util/io"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apiclient/version"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
argoio "github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
// NewVersionCmd returns a new `version` command to be used as a sub-command to root
|
||||
|
||||
35
cmd/main.go
35
cmd/main.go
@@ -7,12 +7,13 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
appcontroller "github.com/argoproj/argo-cd/cmd/argocd-application-controller/commands"
|
||||
dex "github.com/argoproj/argo-cd/cmd/argocd-dex/commands"
|
||||
reposerver "github.com/argoproj/argo-cd/cmd/argocd-repo-server/commands"
|
||||
apiserver "github.com/argoproj/argo-cd/cmd/argocd-server/commands"
|
||||
util "github.com/argoproj/argo-cd/cmd/argocd-util/commands"
|
||||
cli "github.com/argoproj/argo-cd/cmd/argocd/commands"
|
||||
appcontroller "github.com/argoproj/argo-cd/v2/cmd/argocd-application-controller/commands"
|
||||
cmpserver "github.com/argoproj/argo-cd/v2/cmd/argocd-cmp-server/commands"
|
||||
dex "github.com/argoproj/argo-cd/v2/cmd/argocd-dex/commands"
|
||||
notification "github.com/argoproj/argo-cd/v2/cmd/argocd-notification/commands"
|
||||
reposerver "github.com/argoproj/argo-cd/v2/cmd/argocd-repo-server/commands"
|
||||
apiserver "github.com/argoproj/argo-cd/v2/cmd/argocd-server/commands"
|
||||
cli "github.com/argoproj/argo-cd/v2/cmd/argocd/commands"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -29,32 +30,20 @@ func main() {
|
||||
switch binaryName {
|
||||
case "argocd", "argocd-linux-amd64", "argocd-darwin-amd64", "argocd-windows-amd64.exe":
|
||||
command = cli.NewCommand()
|
||||
case "argocd-util", "argocd-util-linux-amd64", "argocd-util-darwin-amd64", "argocd-util-windows-amd64.exe":
|
||||
command = util.NewCommand()
|
||||
case "argocd-server":
|
||||
command = apiserver.NewCommand()
|
||||
case "argocd-application-controller":
|
||||
command = appcontroller.NewCommand()
|
||||
case "argocd-repo-server":
|
||||
command = reposerver.NewCommand()
|
||||
case "argocd-cmp-server":
|
||||
command = cmpserver.NewCommand()
|
||||
case "argocd-dex":
|
||||
command = dex.NewCommand()
|
||||
case "argocd-notifications":
|
||||
command = notification.NewCommand()
|
||||
default:
|
||||
if len(os.Args[1:]) > 0 {
|
||||
// trying to guess between argocd and argocd-util by matching sub command
|
||||
for _, cmd := range []*cobra.Command{cli.NewCommand(), util.NewCommand()} {
|
||||
if _, _, err := cmd.Find(os.Args[1:]); err == nil {
|
||||
command = cmd
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if command == nil {
|
||||
fmt.Printf("Unknown binary name '%s'.Use '%s' environment variable to specify required binary name "+
|
||||
"(possible values 'argocd' or 'argocd-util').\n", binaryName, binaryNameEnv)
|
||||
os.Exit(1)
|
||||
}
|
||||
command = cli.NewCommand()
|
||||
}
|
||||
|
||||
if err := command.Execute(); err != nil {
|
||||
|
||||
350
cmd/util/app.go
350
cmd/util/app.go
@@ -7,61 +7,73 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"github.com/argoproj/argo-cd/util/text/label"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/config"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/util/text/label"
|
||||
)
|
||||
|
||||
type AppOptions struct {
|
||||
repoURL string
|
||||
appPath string
|
||||
chart string
|
||||
env string
|
||||
revision string
|
||||
revisionHistoryLimit int
|
||||
destName string
|
||||
destServer string
|
||||
destNamespace string
|
||||
Parameters []string
|
||||
valuesFiles []string
|
||||
values string
|
||||
releaseName string
|
||||
helmSets []string
|
||||
helmSetStrings []string
|
||||
helmSetFiles []string
|
||||
helmVersion string
|
||||
project string
|
||||
syncPolicy string
|
||||
syncOptions []string
|
||||
autoPrune bool
|
||||
selfHeal bool
|
||||
allowEmpty bool
|
||||
namePrefix string
|
||||
nameSuffix string
|
||||
directoryRecurse bool
|
||||
configManagementPlugin string
|
||||
jsonnetTlaStr []string
|
||||
jsonnetTlaCode []string
|
||||
jsonnetExtVarStr []string
|
||||
jsonnetExtVarCode []string
|
||||
jsonnetLibs []string
|
||||
kustomizeImages []string
|
||||
kustomizeVersion string
|
||||
kustomizeCommonLabels []string
|
||||
kustomizeCommonAnnotations []string
|
||||
pluginEnvs []string
|
||||
Validate bool
|
||||
directoryExclude string
|
||||
directoryInclude string
|
||||
repoURL string
|
||||
appPath string
|
||||
chart string
|
||||
env string
|
||||
revision string
|
||||
revisionHistoryLimit int
|
||||
destName string
|
||||
destServer string
|
||||
destNamespace string
|
||||
Parameters []string
|
||||
valuesFiles []string
|
||||
ignoreMissingValueFiles bool
|
||||
values string
|
||||
releaseName string
|
||||
helmSets []string
|
||||
helmSetStrings []string
|
||||
helmSetFiles []string
|
||||
helmVersion string
|
||||
helmPassCredentials bool
|
||||
helmSkipCrds bool
|
||||
project string
|
||||
syncPolicy string
|
||||
syncOptions []string
|
||||
autoPrune bool
|
||||
selfHeal bool
|
||||
allowEmpty bool
|
||||
namePrefix string
|
||||
nameSuffix string
|
||||
directoryRecurse bool
|
||||
configManagementPlugin string
|
||||
jsonnetTlaStr []string
|
||||
jsonnetTlaCode []string
|
||||
jsonnetExtVarStr []string
|
||||
jsonnetExtVarCode []string
|
||||
jsonnetLibs []string
|
||||
kustomizeImages []string
|
||||
kustomizeVersion string
|
||||
kustomizeCommonLabels []string
|
||||
kustomizeCommonAnnotations []string
|
||||
kustomizeForceCommonLabels bool
|
||||
kustomizeForceCommonAnnotations bool
|
||||
pluginEnvs []string
|
||||
Validate bool
|
||||
directoryExclude string
|
||||
directoryInclude string
|
||||
retryLimit int64
|
||||
retryBackoffDuration time.Duration
|
||||
retryBackoffMaxDuration time.Duration
|
||||
retryBackoffFactor int64
|
||||
}
|
||||
|
||||
func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
@@ -70,18 +82,21 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
command.Flags().StringVar(&opts.chart, "helm-chart", "", "Helm Chart name")
|
||||
command.Flags().StringVar(&opts.env, "env", "", "Application environment to monitor")
|
||||
command.Flags().StringVar(&opts.revision, "revision", "", "The tracking source branch, tag, commit or Helm chart version the application will sync to")
|
||||
command.Flags().IntVar(&opts.revisionHistoryLimit, "revision-history-limit", common.RevisionHistoryLimit, "How many items to keep in revision history")
|
||||
command.Flags().IntVar(&opts.revisionHistoryLimit, "revision-history-limit", argoappv1.RevisionHistoryLimit, "How many items to keep in revision history")
|
||||
command.Flags().StringVar(&opts.destServer, "dest-server", "", "K8s cluster URL (e.g. https://kubernetes.default.svc)")
|
||||
command.Flags().StringVar(&opts.destName, "dest-name", "", "K8s cluster Name (e.g. minikube)")
|
||||
command.Flags().StringVar(&opts.destNamespace, "dest-namespace", "", "K8s target namespace (overrides the namespace specified in the ksonnet app.yaml)")
|
||||
command.Flags().StringArrayVarP(&opts.Parameters, "parameter", "p", []string{}, "set a parameter override (e.g. -p guestbook=image=example/guestbook:latest)")
|
||||
command.Flags().StringArrayVar(&opts.valuesFiles, "values", []string{}, "Helm values file(s) to use")
|
||||
command.Flags().BoolVar(&opts.ignoreMissingValueFiles, "ignore-missing-value-files", false, "Ignore locally missing valueFiles when setting helm template --values")
|
||||
command.Flags().StringVar(&opts.values, "values-literal-file", "", "Filename or URL to import as a literal Helm values block")
|
||||
command.Flags().StringVar(&opts.releaseName, "release-name", "", "Helm release-name")
|
||||
command.Flags().StringVar(&opts.helmVersion, "helm-version", "", "Helm version")
|
||||
command.Flags().BoolVar(&opts.helmPassCredentials, "helm-pass-credentials", false, "Pass credentials to all domain")
|
||||
command.Flags().StringArrayVar(&opts.helmSets, "helm-set", []string{}, "Helm set values on the command line (can be repeated to set several values: --helm-set key1=val1 --helm-set key2=val2)")
|
||||
command.Flags().StringArrayVar(&opts.helmSetStrings, "helm-set-string", []string{}, "Helm set STRING values on the command line (can be repeated to set several values: --helm-set-string key1=val1 --helm-set-string key2=val2)")
|
||||
command.Flags().StringArrayVar(&opts.helmSetFiles, "helm-set-file", []string{}, "Helm set values from respective files specified via the command line (can be repeated to set several values: --helm-set-file key1=path1 --helm-set-file key2=path2)")
|
||||
command.Flags().BoolVar(&opts.helmSkipCrds, "helm-skip-crds", false, "Skip helm crd installation step")
|
||||
command.Flags().StringVar(&opts.project, "project", "", "Application project name")
|
||||
command.Flags().StringVar(&opts.syncPolicy, "sync-policy", "", "Set the sync policy (one of: none, automated (aliases of automated: auto, automatic))")
|
||||
command.Flags().StringArrayVar(&opts.syncOptions, "sync-option", []string{}, "Add or remove a sync option, e.g add `Prune=false`. Remove using `!` prefix, e.g. `!Prune=false`")
|
||||
@@ -103,12 +118,21 @@ func AddAppFlags(command *cobra.Command, opts *AppOptions) {
|
||||
command.Flags().BoolVar(&opts.Validate, "validate", true, "Validation of repo and cluster")
|
||||
command.Flags().StringArrayVar(&opts.kustomizeCommonLabels, "kustomize-common-label", []string{}, "Set common labels in Kustomize")
|
||||
command.Flags().StringArrayVar(&opts.kustomizeCommonAnnotations, "kustomize-common-annotation", []string{}, "Set common labels in Kustomize")
|
||||
command.Flags().BoolVar(&opts.kustomizeForceCommonLabels, "kustomize-force-common-label", false, "Force common labels in Kustomize")
|
||||
command.Flags().BoolVar(&opts.kustomizeForceCommonAnnotations, "kustomize-force-common-annotation", false, "Force common annotations in Kustomize")
|
||||
command.Flags().StringVar(&opts.directoryExclude, "directory-exclude", "", "Set glob expression used to exclude files from application source path")
|
||||
command.Flags().StringVar(&opts.directoryInclude, "directory-include", "", "Set glob expression used to include files from application source path")
|
||||
command.Flags().Int64Var(&opts.retryLimit, "sync-retry-limit", 0, "Max number of allowed sync retries")
|
||||
command.Flags().DurationVar(&opts.retryBackoffDuration, "sync-retry-backoff-duration", argoappv1.DefaultSyncRetryDuration, "Sync retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().DurationVar(&opts.retryBackoffMaxDuration, "sync-retry-backoff-max-duration", argoappv1.DefaultSyncRetryMaxDuration, "Max sync retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().Int64Var(&opts.retryBackoffFactor, "sync-retry-backoff-factor", argoappv1.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed sync retry")
|
||||
}
|
||||
|
||||
func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, appOpts *AppOptions) int {
|
||||
visited := 0
|
||||
if flags == nil {
|
||||
return visited
|
||||
}
|
||||
flags.Visit(func(f *pflag.Flag) {
|
||||
visited++
|
||||
switch f.Name {
|
||||
@@ -127,6 +151,8 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
spec.RevisionHistoryLimit = &i
|
||||
case "values":
|
||||
setHelmOpt(&spec.Source, helmOpts{valueFiles: appOpts.valuesFiles})
|
||||
case "ignore-missing-value-files":
|
||||
setHelmOpt(&spec.Source, helmOpts{ignoreMissingValueFiles: appOpts.ignoreMissingValueFiles})
|
||||
case "values-literal-file":
|
||||
var data []byte
|
||||
|
||||
@@ -143,12 +169,16 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
setHelmOpt(&spec.Source, helmOpts{releaseName: appOpts.releaseName})
|
||||
case "helm-version":
|
||||
setHelmOpt(&spec.Source, helmOpts{version: appOpts.helmVersion})
|
||||
case "helm-pass-credentials":
|
||||
setHelmOpt(&spec.Source, helmOpts{passCredentials: appOpts.helmPassCredentials})
|
||||
case "helm-set":
|
||||
setHelmOpt(&spec.Source, helmOpts{helmSets: appOpts.helmSets})
|
||||
case "helm-set-string":
|
||||
setHelmOpt(&spec.Source, helmOpts{helmSetStrings: appOpts.helmSetStrings})
|
||||
case "helm-set-file":
|
||||
setHelmOpt(&spec.Source, helmOpts{helmSetFiles: appOpts.helmSetFiles})
|
||||
case "helm-skip-crds":
|
||||
setHelmOpt(&spec.Source, helmOpts{skipCrds: appOpts.helmSkipCrds})
|
||||
case "directory-recurse":
|
||||
if spec.Source.Directory != nil {
|
||||
spec.Source.Directory.Recurse = appOpts.directoryRecurse
|
||||
@@ -193,6 +223,10 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
parsedAnnotations, err := label.Parse(appOpts.kustomizeCommonAnnotations)
|
||||
errors.CheckError(err)
|
||||
setKustomizeOpt(&spec.Source, kustomizeOpts{commonAnnotations: parsedAnnotations})
|
||||
case "kustomize-force-common-label":
|
||||
setKustomizeOpt(&spec.Source, kustomizeOpts{forceCommonLabels: appOpts.kustomizeForceCommonLabels})
|
||||
case "kustomize-force-common-annotation":
|
||||
setKustomizeOpt(&spec.Source, kustomizeOpts{forceCommonAnnotations: appOpts.kustomizeForceCommonAnnotations})
|
||||
case "jsonnet-tla-str":
|
||||
setJsonnetOpt(&spec.Source, appOpts.jsonnetTlaStr, false)
|
||||
case "jsonnet-tla-code":
|
||||
@@ -238,6 +272,28 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
if spec.SyncPolicy.IsZero() {
|
||||
spec.SyncPolicy = nil
|
||||
}
|
||||
case "sync-retry-limit":
|
||||
if appOpts.retryLimit > 0 {
|
||||
if spec.SyncPolicy == nil {
|
||||
spec.SyncPolicy = &argoappv1.SyncPolicy{}
|
||||
}
|
||||
spec.SyncPolicy.Retry = &argoappv1.RetryStrategy{
|
||||
Limit: appOpts.retryLimit,
|
||||
Backoff: &argoappv1.Backoff{
|
||||
Duration: appOpts.retryBackoffDuration.String(),
|
||||
MaxDuration: appOpts.retryBackoffMaxDuration.String(),
|
||||
Factor: pointer.Int64Ptr(appOpts.retryBackoffFactor),
|
||||
},
|
||||
}
|
||||
} else if appOpts.retryLimit == 0 {
|
||||
if spec.SyncPolicy.IsZero() {
|
||||
spec.SyncPolicy = nil
|
||||
} else {
|
||||
spec.SyncPolicy.Retry = nil
|
||||
}
|
||||
} else {
|
||||
log.Fatalf("Invalid sync-retry-limit [%d]", appOpts.retryLimit)
|
||||
}
|
||||
}
|
||||
})
|
||||
if flags.Changed("auto-prune") {
|
||||
@@ -275,12 +331,14 @@ func setKsonnetOpt(src *argoappv1.ApplicationSource, env *string) {
|
||||
}
|
||||
|
||||
type kustomizeOpts struct {
|
||||
namePrefix string
|
||||
nameSuffix string
|
||||
images []string
|
||||
version string
|
||||
commonLabels map[string]string
|
||||
commonAnnotations map[string]string
|
||||
namePrefix string
|
||||
nameSuffix string
|
||||
images []string
|
||||
version string
|
||||
commonLabels map[string]string
|
||||
commonAnnotations map[string]string
|
||||
forceCommonLabels bool
|
||||
forceCommonAnnotations bool
|
||||
}
|
||||
|
||||
func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
|
||||
@@ -302,6 +360,12 @@ func setKustomizeOpt(src *argoappv1.ApplicationSource, opts kustomizeOpts) {
|
||||
if opts.commonAnnotations != nil {
|
||||
src.Kustomize.CommonAnnotations = opts.commonAnnotations
|
||||
}
|
||||
if opts.forceCommonLabels {
|
||||
src.Kustomize.ForceCommonLabels = opts.forceCommonLabels
|
||||
}
|
||||
if opts.forceCommonAnnotations {
|
||||
src.Kustomize.ForceCommonAnnotations = opts.forceCommonAnnotations
|
||||
}
|
||||
for _, image := range opts.images {
|
||||
src.Kustomize.MergeImage(argoappv1.KustomizeImage(image))
|
||||
}
|
||||
@@ -325,13 +389,16 @@ func setPluginOptEnvs(src *argoappv1.ApplicationSource, envs []string) {
|
||||
}
|
||||
|
||||
type helmOpts struct {
|
||||
valueFiles []string
|
||||
values string
|
||||
releaseName string
|
||||
version string
|
||||
helmSets []string
|
||||
helmSetStrings []string
|
||||
helmSetFiles []string
|
||||
valueFiles []string
|
||||
ignoreMissingValueFiles bool
|
||||
values string
|
||||
releaseName string
|
||||
version string
|
||||
helmSets []string
|
||||
helmSetStrings []string
|
||||
helmSetFiles []string
|
||||
passCredentials bool
|
||||
skipCrds bool
|
||||
}
|
||||
|
||||
func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) {
|
||||
@@ -341,6 +408,9 @@ func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) {
|
||||
if len(opts.valueFiles) > 0 {
|
||||
src.Helm.ValueFiles = opts.valueFiles
|
||||
}
|
||||
if opts.ignoreMissingValueFiles {
|
||||
src.Helm.IgnoreMissingValueFiles = opts.ignoreMissingValueFiles
|
||||
}
|
||||
if len(opts.values) > 0 {
|
||||
src.Helm.Values = opts.values
|
||||
}
|
||||
@@ -350,6 +420,12 @@ func setHelmOpt(src *argoappv1.ApplicationSource, opts helmOpts) {
|
||||
if opts.version != "" {
|
||||
src.Helm.Version = opts.version
|
||||
}
|
||||
if opts.passCredentials {
|
||||
src.Helm.PassCredentials = opts.passCredentials
|
||||
}
|
||||
if opts.skipCrds {
|
||||
src.Helm.SkipCrds = opts.skipCrds
|
||||
}
|
||||
for _, text := range opts.helmSets {
|
||||
p, err := argoappv1.NewHelmParameter(text, false)
|
||||
if err != nil {
|
||||
@@ -471,39 +547,99 @@ func SetParameterOverrides(app *argoappv1.Application, parameters []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func readAppFromStdin(app *argoappv1.Application) error {
|
||||
func readApps(yml []byte, apps *[]*argoappv1.Application) error {
|
||||
yamls, _ := kube.SplitYAMLToString(yml)
|
||||
|
||||
var err error
|
||||
|
||||
for _, yml := range yamls {
|
||||
var app argoappv1.Application
|
||||
err = config.Unmarshal([]byte(yml), &app)
|
||||
*apps = append(*apps, &app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func readAppsFromStdin(apps *[]*argoappv1.Application) error {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
err := config.UnmarshalReader(reader, &app)
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = readApps(data, apps)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read manifest from stdin: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readAppFromURI(fileURL string, app *argoappv1.Application) error {
|
||||
parsedURL, err := url.ParseRequestURI(fileURL)
|
||||
if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
|
||||
err = config.UnmarshalLocalFile(fileURL, &app)
|
||||
} else {
|
||||
err = config.UnmarshalRemoteFile(fileURL, &app)
|
||||
func readAppsFromURI(fileURL string, apps *[]*argoappv1.Application) error {
|
||||
|
||||
readFilePayload := func() ([]byte, error) {
|
||||
parsedURL, err := url.ParseRequestURI(fileURL)
|
||||
if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") {
|
||||
return ioutil.ReadFile(fileURL)
|
||||
}
|
||||
return config.ReadRemoteFile(fileURL)
|
||||
}
|
||||
return err
|
||||
|
||||
yml, err := readFilePayload()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return readApps(yml, apps)
|
||||
}
|
||||
|
||||
func ConstructApp(fileURL, appName string, labels, args []string, appOpts AppOptions, flags *pflag.FlagSet) (*argoappv1.Application, error) {
|
||||
var app argoappv1.Application
|
||||
if fileURL == "-" {
|
||||
// read stdin
|
||||
err := readAppFromStdin(&app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if fileURL != "" {
|
||||
// read uri
|
||||
err := readAppFromURI(fileURL, &app)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func constructAppsFromStdin() ([]*argoappv1.Application, error) {
|
||||
apps := make([]*argoappv1.Application, 0)
|
||||
// read stdin
|
||||
err := readAppsFromStdin(&apps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
func constructAppsBaseOnName(appName string, labels, annotations, args []string, appOpts AppOptions, flags *pflag.FlagSet) ([]*argoappv1.Application, error) {
|
||||
var app *argoappv1.Application
|
||||
// read arguments
|
||||
if len(args) == 1 {
|
||||
if appName != "" && appName != args[0] {
|
||||
return nil, fmt.Errorf("--name argument '%s' does not match app name %s", appName, args[0])
|
||||
}
|
||||
appName = args[0]
|
||||
}
|
||||
app = &argoappv1.Application{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: application.Group + "/v1alpha1",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: appName,
|
||||
},
|
||||
}
|
||||
SetAppSpecOptions(flags, &app.Spec, &appOpts)
|
||||
SetParameterOverrides(app, appOpts.Parameters)
|
||||
mergeLabels(app, labels)
|
||||
setAnnotations(app, annotations)
|
||||
return []*argoappv1.Application{
|
||||
app,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func constructAppsFromFileUrl(fileURL, appName string, labels, annotations, args []string, appOpts AppOptions, flags *pflag.FlagSet) ([]*argoappv1.Application, error) {
|
||||
apps := make([]*argoappv1.Application, 0)
|
||||
// read uri
|
||||
err := readAppsFromURI(fileURL, &apps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, app := range apps {
|
||||
if len(args) == 1 && args[0] != app.Name {
|
||||
return nil, fmt.Errorf("app name '%s' does not match app spec metadata.name '%s'", args[0], app.Name)
|
||||
}
|
||||
@@ -514,30 +650,20 @@ func ConstructApp(fileURL, appName string, labels, args []string, appOpts AppOpt
|
||||
return nil, fmt.Errorf("app.Name is empty. --name argument can be used to provide app.Name")
|
||||
}
|
||||
SetAppSpecOptions(flags, &app.Spec, &appOpts)
|
||||
SetParameterOverrides(&app, appOpts.Parameters)
|
||||
mergeLabels(&app, labels)
|
||||
} else {
|
||||
// read arguments
|
||||
if len(args) == 1 {
|
||||
if appName != "" && appName != args[0] {
|
||||
return nil, fmt.Errorf("--name argument '%s' does not match app name %s", appName, args[0])
|
||||
}
|
||||
appName = args[0]
|
||||
}
|
||||
app = argoappv1.Application{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: application.ApplicationKind,
|
||||
APIVersion: application.Group + "/v1alpha1",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: appName,
|
||||
},
|
||||
}
|
||||
SetAppSpecOptions(flags, &app.Spec, &appOpts)
|
||||
SetParameterOverrides(&app, appOpts.Parameters)
|
||||
mergeLabels(&app, labels)
|
||||
SetParameterOverrides(app, appOpts.Parameters)
|
||||
mergeLabels(app, labels)
|
||||
setAnnotations(app, annotations)
|
||||
}
|
||||
return &app, nil
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
func ConstructApps(fileURL, appName string, labels, annotations, args []string, appOpts AppOptions, flags *pflag.FlagSet) ([]*argoappv1.Application, error) {
|
||||
if fileURL == "-" {
|
||||
return constructAppsFromStdin()
|
||||
} else if fileURL != "" {
|
||||
return constructAppsFromFileUrl(fileURL, appName, labels, annotations, args, appOpts, flags)
|
||||
}
|
||||
return constructAppsBaseOnName(appName, labels, annotations, args, appOpts, flags)
|
||||
}
|
||||
|
||||
func mergeLabels(app *argoappv1.Application, labels []string) {
|
||||
@@ -556,3 +682,17 @@ func mergeLabels(app *argoappv1.Application, labels []string) {
|
||||
|
||||
app.SetLabels(mergedLabels)
|
||||
}
|
||||
|
||||
func setAnnotations(app *argoappv1.Application, annotations []string) {
|
||||
if len(annotations) > 0 && app.Annotations == nil {
|
||||
app.Annotations = map[string]string{}
|
||||
}
|
||||
for _, a := range annotations {
|
||||
annotation := strings.SplitN(a, "=", 2)
|
||||
if len(annotation) == 2 {
|
||||
app.Annotations[annotation[0]] = annotation[1]
|
||||
} else {
|
||||
app.Annotations[annotation[0]] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func Test_setHelmOpt(t *testing.T) {
|
||||
@@ -20,6 +24,11 @@ func Test_setHelmOpt(t *testing.T) {
|
||||
setHelmOpt(&src, helmOpts{valueFiles: []string{"foo"}})
|
||||
assert.Equal(t, []string{"foo"}, src.Helm.ValueFiles)
|
||||
})
|
||||
t.Run("IgnoreMissingValueFiles", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
setHelmOpt(&src, helmOpts{ignoreMissingValueFiles: true})
|
||||
assert.Equal(t, true, src.Helm.IgnoreMissingValueFiles)
|
||||
})
|
||||
t.Run("ReleaseName", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
setHelmOpt(&src, helmOpts{releaseName: "foo"})
|
||||
@@ -45,6 +54,16 @@ func Test_setHelmOpt(t *testing.T) {
|
||||
setHelmOpt(&src, helmOpts{version: "v3"})
|
||||
assert.Equal(t, "v3", src.Helm.Version)
|
||||
})
|
||||
t.Run("HelmPassCredentials", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
setHelmOpt(&src, helmOpts{passCredentials: true})
|
||||
assert.Equal(t, true, src.Helm.PassCredentials)
|
||||
})
|
||||
t.Run("HelmSkipCrds", func(t *testing.T) {
|
||||
src := v1alpha1.ApplicationSource{}
|
||||
setHelmOpt(&src, helmOpts{skipCrds: true})
|
||||
assert.Equal(t, true, src.Helm.SkipCrds)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_setKustomizeOpt(t *testing.T) {
|
||||
@@ -164,4 +183,132 @@ func Test_setAppSpecOptions(t *testing.T) {
|
||||
assert.NoError(t, f.SetFlag("sync-option", "!a=1"))
|
||||
assert.Nil(t, f.spec.SyncPolicy)
|
||||
})
|
||||
t.Run("RetryLimit", func(t *testing.T) {
|
||||
assert.NoError(t, f.SetFlag("sync-retry-limit", "5"))
|
||||
assert.True(t, f.spec.SyncPolicy.Retry.Limit == 5)
|
||||
|
||||
assert.NoError(t, f.SetFlag("sync-retry-limit", "0"))
|
||||
assert.Nil(t, f.spec.SyncPolicy.Retry)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_setAnnotations(t *testing.T) {
|
||||
t.Run("Annotations", func(t *testing.T) {
|
||||
app := v1alpha1.Application{}
|
||||
setAnnotations(&app, []string{"hoge=foo", "huga=bar"})
|
||||
assert.Equal(t, map[string]string{"hoge": "foo", "huga": "bar"}, app.Annotations)
|
||||
})
|
||||
t.Run("Annotations value contains equal", func(t *testing.T) {
|
||||
app := v1alpha1.Application{}
|
||||
setAnnotations(&app, []string{"hoge=foo=bar"})
|
||||
assert.Equal(t, map[string]string{"hoge": "foo=bar"}, app.Annotations)
|
||||
})
|
||||
t.Run("Annotations empty value", func(t *testing.T) {
|
||||
app := v1alpha1.Application{}
|
||||
setAnnotations(&app, []string{"hoge"})
|
||||
assert.Equal(t, map[string]string{"hoge": ""}, app.Annotations)
|
||||
})
|
||||
}
|
||||
|
||||
const appsYaml = `---
|
||||
# Source: apps/templates/helm.yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: sth1
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
destination:
|
||||
namespace: sth
|
||||
server: 'https://kubernetes.default.svc'
|
||||
project: default
|
||||
source:
|
||||
repoURL: 'https://github.com/pasha-codefresh/argocd-example-apps'
|
||||
targetRevision: HEAD
|
||||
path: apps
|
||||
helm:
|
||||
valueFiles:
|
||||
- values.yaml
|
||||
---
|
||||
# Source: apps/templates/helm.yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: sth2
|
||||
namespace: argocd
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
destination:
|
||||
namespace: sth
|
||||
server: 'https://kubernetes.default.svc'
|
||||
project: default
|
||||
source:
|
||||
repoURL: 'https://github.com/pasha-codefresh/argocd-example-apps'
|
||||
targetRevision: HEAD
|
||||
path: apps
|
||||
helm:
|
||||
valueFiles:
|
||||
- values.yaml`
|
||||
|
||||
func TestReadAppsFromURI(t *testing.T) {
|
||||
file, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(file.Name())
|
||||
}()
|
||||
|
||||
_, _ = file.WriteString(appsYaml)
|
||||
_ = file.Sync()
|
||||
|
||||
apps := make([]*argoappv1.Application, 0)
|
||||
err = readAppsFromURI(file.Name(), &apps)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(apps))
|
||||
|
||||
assert.Equal(t, "sth1", apps[0].Name)
|
||||
assert.Equal(t, "sth2", apps[1].Name)
|
||||
|
||||
}
|
||||
|
||||
func TestConstructAppFromStdin(t *testing.T) {
|
||||
file, err := ioutil.TempFile(os.TempDir(), "")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(file.Name())
|
||||
}()
|
||||
|
||||
_, _ = file.WriteString(appsYaml)
|
||||
_ = file.Sync()
|
||||
|
||||
if _, err := file.Seek(0, 0); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
os.Stdin = file
|
||||
|
||||
apps, err := ConstructApps("-", "test", []string{}, []string{}, []string{}, AppOptions{}, nil)
|
||||
|
||||
if err := file.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(apps))
|
||||
assert.Equal(t, "sth1", apps[0].Name)
|
||||
assert.Equal(t, "sth2", apps[1].Name)
|
||||
|
||||
}
|
||||
|
||||
func TestConstructBasedOnName(t *testing.T) {
|
||||
apps, err := ConstructApps("", "test", []string{}, []string{}, []string{}, AppOptions{}, nil)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(apps))
|
||||
assert.Equal(t, "test", apps[0].Name)
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
argoappv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/errors"
|
||||
)
|
||||
|
||||
func PrintKubeContexts(ca clientcmd.ConfigAccess) {
|
||||
@@ -55,7 +55,7 @@ func PrintKubeContexts(ca clientcmd.ConfigAccess) {
|
||||
}
|
||||
}
|
||||
|
||||
func NewCluster(name string, namespaces []string, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig, execProviderConf *argoappv1.ExecProviderConfig) *argoappv1.Cluster {
|
||||
func NewCluster(name string, namespaces []string, clusterResources bool, conf *rest.Config, managerBearerToken string, awsAuthConf *argoappv1.AWSAuthConfig, execProviderConf *argoappv1.ExecProviderConfig, labels, annotations map[string]string) *argoappv1.Cluster {
|
||||
tlsClientConfig := argoappv1.TLSClientConfig{
|
||||
Insecure: conf.TLSClientConfig.Insecure,
|
||||
ServerName: conf.TLSClientConfig.ServerName,
|
||||
@@ -80,14 +80,17 @@ func NewCluster(name string, namespaces []string, conf *rest.Config, managerBear
|
||||
}
|
||||
|
||||
clst := argoappv1.Cluster{
|
||||
Server: conf.Host,
|
||||
Name: name,
|
||||
Namespaces: namespaces,
|
||||
Server: conf.Host,
|
||||
Name: name,
|
||||
Namespaces: namespaces,
|
||||
ClusterResources: clusterResources,
|
||||
Config: argoappv1.ClusterConfig{
|
||||
TLSClientConfig: tlsClientConfig,
|
||||
AWSAuthConfig: awsAuthConf,
|
||||
ExecProviderConfig: execProviderConf,
|
||||
},
|
||||
Labels: labels,
|
||||
Annotations: annotations,
|
||||
}
|
||||
|
||||
// Bearer token will preferentially be used for auth if present,
|
||||
@@ -108,7 +111,9 @@ type ClusterOptions struct {
|
||||
AwsClusterName string
|
||||
SystemNamespace string
|
||||
Namespaces []string
|
||||
ClusterResources bool
|
||||
Name string
|
||||
Project string
|
||||
Shard int64
|
||||
ExecProviderCommand string
|
||||
ExecProviderArgs []string
|
||||
@@ -122,7 +127,9 @@ func AddClusterFlags(command *cobra.Command, opts *ClusterOptions) {
|
||||
command.Flags().StringVar(&opts.AwsClusterName, "aws-cluster-name", "", "AWS Cluster name if set then aws cli eks token command will be used to access cluster")
|
||||
command.Flags().StringVar(&opts.AwsRoleArn, "aws-role-arn", "", "Optional AWS role arn. If set then AWS IAM Authenticator assumes a role to perform cluster operations instead of the default AWS credential provider chain.")
|
||||
command.Flags().StringArrayVar(&opts.Namespaces, "namespace", nil, "List of namespaces which are allowed to manage")
|
||||
command.Flags().BoolVar(&opts.ClusterResources, "cluster-resources", false, "Indicates if cluster level resources should be managed. The setting is used only if list of managed namespaces is not empty.")
|
||||
command.Flags().StringVar(&opts.Name, "name", "", "Overwrite the cluster name")
|
||||
command.Flags().StringVar(&opts.Project, "project", "", "project of the cluster")
|
||||
command.Flags().Int64Var(&opts.Shard, "shard", -1, "Cluster shard number; inferred from hostname if not set")
|
||||
command.Flags().StringVar(&opts.ExecProviderCommand, "exec-command", "", "Command to run to provide client credentials to the cluster. You may need to build a custom ArgoCD image to ensure the command is available at runtime.")
|
||||
command.Flags().StringArrayVar(&opts.ExecProviderArgs, "exec-command-args", nil, "Arguments to supply to the --exec-command executable")
|
||||
|
||||
@@ -7,11 +7,13 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
func Test_newCluster(t *testing.T) {
|
||||
clusterWithData := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
|
||||
labels := map[string]string{"key1": "val1"}
|
||||
annotations := map[string]string{"key2": "val2"}
|
||||
clusterWithData := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
Insecure: false,
|
||||
ServerName: "test-endpoint.example.com",
|
||||
@@ -23,13 +25,15 @@ func Test_newCluster(t *testing.T) {
|
||||
},
|
||||
"test-bearer-token",
|
||||
&v1alpha1.AWSAuthConfig{},
|
||||
&v1alpha1.ExecProviderConfig{})
|
||||
&v1alpha1.ExecProviderConfig{}, labels, annotations)
|
||||
|
||||
assert.Equal(t, "test-cert-data", string(clusterWithData.Config.CertData))
|
||||
assert.Equal(t, "test-key-data", string(clusterWithData.Config.KeyData))
|
||||
assert.Equal(t, "", clusterWithData.Config.BearerToken)
|
||||
assert.Equal(t, labels, clusterWithData.Labels)
|
||||
assert.Equal(t, annotations, clusterWithData.Annotations)
|
||||
|
||||
clusterWithFiles := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
|
||||
clusterWithFiles := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
Insecure: false,
|
||||
ServerName: "test-endpoint.example.com",
|
||||
@@ -41,13 +45,15 @@ func Test_newCluster(t *testing.T) {
|
||||
},
|
||||
"test-bearer-token",
|
||||
&v1alpha1.AWSAuthConfig{},
|
||||
&v1alpha1.ExecProviderConfig{})
|
||||
&v1alpha1.ExecProviderConfig{}, labels, nil)
|
||||
|
||||
assert.True(t, strings.Contains(string(clusterWithFiles.Config.CertData), "test-cert-data"))
|
||||
assert.True(t, strings.Contains(string(clusterWithFiles.Config.KeyData), "test-key-data"))
|
||||
assert.Equal(t, "", clusterWithFiles.Config.BearerToken)
|
||||
assert.Equal(t, labels, clusterWithFiles.Labels)
|
||||
assert.Nil(t, clusterWithFiles.Annotations)
|
||||
|
||||
clusterWithBearerToken := NewCluster("test-cluster", []string{"test-namespace"}, &rest.Config{
|
||||
clusterWithBearerToken := NewCluster("test-cluster", []string{"test-namespace"}, false, &rest.Config{
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
Insecure: false,
|
||||
ServerName: "test-endpoint.example.com",
|
||||
@@ -57,7 +63,9 @@ func Test_newCluster(t *testing.T) {
|
||||
},
|
||||
"test-bearer-token",
|
||||
&v1alpha1.AWSAuthConfig{},
|
||||
&v1alpha1.ExecProviderConfig{})
|
||||
&v1alpha1.ExecProviderConfig{}, nil, nil)
|
||||
|
||||
assert.Equal(t, "test-bearer-token", clusterWithBearerToken.Config.BearerToken)
|
||||
assert.Nil(t, clusterWithBearerToken.Labels)
|
||||
assert.Nil(t, clusterWithBearerToken.Annotations)
|
||||
}
|
||||
|
||||
@@ -1,80 +1,6 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
)
|
||||
|
||||
var (
|
||||
LogFormat string
|
||||
LogLevel string
|
||||
)
|
||||
|
||||
// PrintResource prints a single resource in YAML or JSON format to stdout according to the output format
|
||||
func PrintResources(resources []interface{}, output string) error {
|
||||
for i, resource := range resources {
|
||||
filteredResource, err := omitFields(resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resources[i] = filteredResource
|
||||
}
|
||||
|
||||
switch output {
|
||||
case "json":
|
||||
jsonBytes, err := json.MarshalIndent(resources, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(jsonBytes))
|
||||
case "yaml":
|
||||
yamlBytes, err := yaml.Marshal(resources)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(yamlBytes))
|
||||
default:
|
||||
return fmt.Errorf("unknown output format: %s", output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// omit fields such as status, creationTimestamp and metadata.namespace in k8s objects
|
||||
func omitFields(resource interface{}) (interface{}, error) {
|
||||
jsonBytes, err := json.Marshal(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
toMap := make(map[string]interface{})
|
||||
err = json.Unmarshal([]byte(string(jsonBytes)), &toMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(toMap, "status")
|
||||
if v, ok := toMap["metadata"]; ok {
|
||||
if metadata, ok := v.(map[string]interface{}); ok {
|
||||
delete(metadata, "creationTimestamp")
|
||||
delete(metadata, "namespace")
|
||||
}
|
||||
}
|
||||
return toMap, nil
|
||||
}
|
||||
|
||||
// ConvertSecretData converts kubernetes secret's data to stringData
|
||||
func ConvertSecretData(secret *v1.Secret) {
|
||||
secret.Kind = kube.SecretKind
|
||||
secret.APIVersion = "v1"
|
||||
secret.StringData = map[string]string{}
|
||||
for k, v := range secret.Data {
|
||||
secret.StringData[k] = string(v)
|
||||
}
|
||||
secret.Data = map[string][]byte{}
|
||||
}
|
||||
|
||||
@@ -9,22 +9,28 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application"
|
||||
"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/config"
|
||||
"github.com/argoproj/argo-cd/util/gpg"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application"
|
||||
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/util/config"
|
||||
"github.com/argoproj/argo-cd/v2/util/gpg"
|
||||
)
|
||||
|
||||
type ProjectOpts struct {
|
||||
Description string
|
||||
destinations []string
|
||||
Sources []string
|
||||
SignatureKeys []string
|
||||
orphanedResourcesEnabled bool
|
||||
orphanedResourcesWarn bool
|
||||
Description string
|
||||
destinations []string
|
||||
Sources []string
|
||||
SignatureKeys []string
|
||||
|
||||
orphanedResourcesEnabled bool
|
||||
orphanedResourcesWarn bool
|
||||
allowedClusterResources []string
|
||||
deniedClusterResources []string
|
||||
allowedNamespacedResources []string
|
||||
deniedNamespacedResources []string
|
||||
}
|
||||
|
||||
func AddProjFlags(command *cobra.Command, opts *ProjectOpts) {
|
||||
@@ -35,6 +41,39 @@ func AddProjFlags(command *cobra.Command, opts *ProjectOpts) {
|
||||
command.Flags().StringSliceVar(&opts.SignatureKeys, "signature-keys", []string{}, "GnuPG public key IDs for commit signature verification")
|
||||
command.Flags().BoolVar(&opts.orphanedResourcesEnabled, "orphaned-resources", false, "Enables orphaned resources monitoring")
|
||||
command.Flags().BoolVar(&opts.orphanedResourcesWarn, "orphaned-resources-warn", false, "Specifies if applications should have a warning condition when orphaned resources detected")
|
||||
command.Flags().StringArrayVar(&opts.allowedClusterResources, "allow-cluster-resource", []string{}, "List of allowed cluster level resources")
|
||||
command.Flags().StringArrayVar(&opts.deniedClusterResources, "deny-cluster-resource", []string{}, "List of denied cluster level resources")
|
||||
command.Flags().StringArrayVar(&opts.allowedNamespacedResources, "allow-namespaced-resource", []string{}, "List of allowed namespaced resources")
|
||||
command.Flags().StringArrayVar(&opts.deniedNamespacedResources, "deny-namespaced-resource", []string{}, "List of denied namespaced resources")
|
||||
|
||||
}
|
||||
|
||||
func getGroupKindList(values []string) []v1.GroupKind {
|
||||
var res []v1.GroupKind
|
||||
for _, val := range values {
|
||||
if parts := strings.Split(val, "/"); len(parts) == 2 {
|
||||
res = append(res, v1.GroupKind{Group: parts[0], Kind: parts[1]})
|
||||
} else if len(parts) == 1 {
|
||||
res = append(res, v1.GroupKind{Kind: parts[0]})
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (opts *ProjectOpts) GetAllowedClusterResources() []v1.GroupKind {
|
||||
return getGroupKindList(opts.allowedClusterResources)
|
||||
}
|
||||
|
||||
func (opts *ProjectOpts) GetDeniedClusterResources() []v1.GroupKind {
|
||||
return getGroupKindList(opts.deniedClusterResources)
|
||||
}
|
||||
|
||||
func (opts *ProjectOpts) GetAllowedNamespacedResources() []v1.GroupKind {
|
||||
return getGroupKindList(opts.allowedNamespacedResources)
|
||||
}
|
||||
|
||||
func (opts *ProjectOpts) GetDeniedNamespacedResources() []v1.GroupKind {
|
||||
return getGroupKindList(opts.deniedNamespacedResources)
|
||||
}
|
||||
|
||||
func (opts *ProjectOpts) GetDestinations() []v1alpha1.ApplicationDestination {
|
||||
@@ -65,8 +104,8 @@ func (opts *ProjectOpts) GetSignatureKeys() []v1alpha1.SignatureKey {
|
||||
return signatureKeys
|
||||
}
|
||||
|
||||
func GetOrphanedResourcesSettings(c *cobra.Command, opts ProjectOpts) *v1alpha1.OrphanedResourcesMonitorSettings {
|
||||
warnChanged := c.Flag("orphaned-resources-warn").Changed
|
||||
func GetOrphanedResourcesSettings(flagSet *pflag.FlagSet, opts ProjectOpts) *v1alpha1.OrphanedResourcesMonitorSettings {
|
||||
warnChanged := flagSet.Changed("orphaned-resources-warn")
|
||||
if opts.orphanedResourcesEnabled || warnChanged {
|
||||
settings := v1alpha1.OrphanedResourcesMonitorSettings{}
|
||||
if warnChanged {
|
||||
@@ -96,8 +135,43 @@ func readProjFromURI(fileURL string, proj *v1alpha1.AppProject) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func SetProjSpecOptions(flags *pflag.FlagSet, spec *v1alpha1.AppProjectSpec, projOpts *ProjectOpts) int {
|
||||
visited := 0
|
||||
flags.Visit(func(f *pflag.Flag) {
|
||||
visited++
|
||||
switch f.Name {
|
||||
case "description":
|
||||
spec.Description = projOpts.Description
|
||||
case "dest":
|
||||
spec.Destinations = projOpts.GetDestinations()
|
||||
case "src":
|
||||
spec.SourceRepos = projOpts.Sources
|
||||
case "signature-keys":
|
||||
spec.SignatureKeys = projOpts.GetSignatureKeys()
|
||||
case "allow-cluster-resource":
|
||||
spec.ClusterResourceWhitelist = projOpts.GetAllowedClusterResources()
|
||||
case "deny-cluster-resource":
|
||||
spec.ClusterResourceBlacklist = projOpts.GetDeniedClusterResources()
|
||||
case "allow-namespaced-resource":
|
||||
spec.NamespaceResourceWhitelist = projOpts.GetAllowedNamespacedResources()
|
||||
case "deny-namespaced-resource":
|
||||
spec.NamespaceResourceBlacklist = projOpts.GetDeniedNamespacedResources()
|
||||
}
|
||||
})
|
||||
if flags.Changed("orphaned-resources") || flags.Changed("orphaned-resources-warn") {
|
||||
spec.OrphanedResources = GetOrphanedResourcesSettings(flags, *projOpts)
|
||||
visited++
|
||||
}
|
||||
return visited
|
||||
}
|
||||
|
||||
func ConstructAppProj(fileURL string, args []string, opts ProjectOpts, c *cobra.Command) (*v1alpha1.AppProject, error) {
|
||||
var proj v1alpha1.AppProject
|
||||
var proj = v1alpha1.AppProject{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: application.AppProjectKind,
|
||||
APIVersion: application.Group + "/v1alpha1",
|
||||
},
|
||||
}
|
||||
if fileURL == "-" {
|
||||
// read stdin
|
||||
err := readProjFromStdin(&proj)
|
||||
@@ -120,22 +194,9 @@ func ConstructAppProj(fileURL string, args []string, opts ProjectOpts, c *cobra.
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
projName := args[0]
|
||||
proj = v1alpha1.AppProject{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: application.AppProjectKind,
|
||||
APIVersion: application.Group + "/v1alpha1",
|
||||
},
|
||||
ObjectMeta: v1.ObjectMeta{Name: projName},
|
||||
Spec: v1alpha1.AppProjectSpec{
|
||||
Description: opts.Description,
|
||||
Destinations: opts.GetDestinations(),
|
||||
SourceRepos: opts.Sources,
|
||||
SignatureKeys: opts.GetSignatureKeys(),
|
||||
OrphanedResources: GetOrphanedResourcesSettings(c, opts),
|
||||
},
|
||||
}
|
||||
proj.Name = args[0]
|
||||
}
|
||||
SetProjSpecOptions(c.Flags(), &proj.Spec, &opts)
|
||||
|
||||
return &proj, nil
|
||||
}
|
||||
|
||||
24
cmd/util/project_test.go
Normal file
24
cmd/util/project_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestProjectOpts_ResourceLists(t *testing.T) {
|
||||
opts := ProjectOpts{
|
||||
allowedNamespacedResources: []string{"ConfigMap"},
|
||||
deniedNamespacedResources: []string{"apps/DaemonSet"},
|
||||
allowedClusterResources: []string{"apiextensions.k8s.io/CustomResourceDefinition"},
|
||||
deniedClusterResources: []string{"rbac.authorization.k8s.io/ClusterRole"},
|
||||
}
|
||||
|
||||
assert.ElementsMatch(t,
|
||||
[]v1.GroupKind{{Kind: "ConfigMap"}}, opts.GetAllowedNamespacedResources(),
|
||||
[]v1.GroupKind{{Group: "apps", Kind: "DaemonSet"}}, opts.GetDeniedNamespacedResources(),
|
||||
[]v1.GroupKind{{Group: "apiextensions.k8s.io", Kind: "CustomResourceDefinition"}}, opts.GetAllowedClusterResources(),
|
||||
[]v1.GroupKind{{Group: "rbac.authorization.k8s.io", Kind: "ClusterRole"}}, opts.GetDeniedClusterResources(),
|
||||
)
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package util
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
appsv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
|
||||
)
|
||||
|
||||
type RepoOptions struct {
|
||||
@@ -21,11 +21,13 @@ type RepoOptions struct {
|
||||
GithubAppInstallationId int64
|
||||
GithubAppPrivateKeyPath string
|
||||
GitHubAppEnterpriseBaseURL string
|
||||
Proxy string
|
||||
}
|
||||
|
||||
func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
|
||||
command.Flags().StringVar(&opts.Repo.Type, "type", common.DefaultRepoType, "type of the repository, \"git\" or \"helm\"")
|
||||
command.Flags().StringVar(&opts.Repo.Name, "name", "", "name of the repository, mandatory for repositories of type helm")
|
||||
command.Flags().StringVar(&opts.Repo.Project, "project", "", "project of the repository")
|
||||
command.Flags().StringVar(&opts.Repo.Username, "username", "", "username to the repository")
|
||||
command.Flags().StringVar(&opts.Repo.Password, "password", "", "password to the repository")
|
||||
command.Flags().StringVar(&opts.SshPrivateKeyPath, "ssh-private-key-path", "", "path to the private ssh key (e.g. ~/.ssh/id_rsa)")
|
||||
@@ -39,4 +41,5 @@ func AddRepoFlags(command *cobra.Command, opts *RepoOptions) {
|
||||
command.Flags().Int64Var(&opts.GithubAppInstallationId, "github-app-installation-id", 0, "installation id of the GitHub Application")
|
||||
command.Flags().StringVar(&opts.GithubAppPrivateKeyPath, "github-app-private-key-path", "", "private key of the GitHub Application")
|
||||
command.Flags().StringVar(&opts.GitHubAppEnterpriseBaseURL, "github-app-enterprise-base-url", "", "base url to use when using GitHub Enterprise (e.g. https://ghe.example.com/api/v3")
|
||||
command.Flags().StringVar(&opts.Proxy, "proxy", "", "use proxy to access repository")
|
||||
}
|
||||
|
||||
62
cmpserver/apiclient/clientset.go
Normal file
62
cmpserver/apiclient/clientset.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package apiclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
|
||||
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
grpc_util "github.com/argoproj/argo-cd/v2/util/grpc"
|
||||
"github.com/argoproj/argo-cd/v2/util/io"
|
||||
)
|
||||
|
||||
const (
|
||||
// MaxGRPCMessageSize contains max grpc message size
|
||||
MaxGRPCMessageSize = 100 * 1024 * 1024
|
||||
)
|
||||
|
||||
// Clientset represents config management plugin server api clients
|
||||
type Clientset interface {
|
||||
NewConfigManagementPluginClient() (io.Closer, ConfigManagementPluginServiceClient, error)
|
||||
}
|
||||
|
||||
type clientSet struct {
|
||||
address string
|
||||
}
|
||||
|
||||
func (c *clientSet) NewConfigManagementPluginClient() (io.Closer, ConfigManagementPluginServiceClient, error) {
|
||||
conn, err := NewConnection(c.address)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return conn, NewConfigManagementPluginServiceClient(conn), nil
|
||||
}
|
||||
|
||||
func NewConnection(address string) (*grpc.ClientConn, error) {
|
||||
retryOpts := []grpc_retry.CallOption{
|
||||
grpc_retry.WithMax(3),
|
||||
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(1000 * time.Millisecond)),
|
||||
}
|
||||
unaryInterceptors := []grpc.UnaryClientInterceptor{grpc_retry.UnaryClientInterceptor(retryOpts...)}
|
||||
dialOpts := []grpc.DialOption{
|
||||
grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
|
||||
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(unaryInterceptors...)),
|
||||
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(MaxGRPCMessageSize), grpc.MaxCallSendMsgSize(MaxGRPCMessageSize)),
|
||||
}
|
||||
|
||||
dialOpts = append(dialOpts, grpc.WithInsecure())
|
||||
conn, err := grpc_util.BlockingDial(context.Background(), "unix", address, nil, dialOpts...)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to connect to config management plugin service with address %s", address)
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// NewConfigManagementPluginClientSet creates new instance of config management plugin server Clientset
|
||||
func NewConfigManagementPluginClientSet(address string) Clientset {
|
||||
return &clientSet{address: address}
|
||||
}
|
||||
1944
cmpserver/apiclient/plugin.pb.go
Normal file
1944
cmpserver/apiclient/plugin.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
91
cmpserver/plugin/config.go
Normal file
91
cmpserver/plugin/config.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/common"
|
||||
configUtil "github.com/argoproj/argo-cd/v2/util/config"
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigManagementPluginKind string = "ConfigManagementPlugin"
|
||||
)
|
||||
|
||||
type PluginConfig struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
Metadata metav1.ObjectMeta `json:"metadata"`
|
||||
Spec PluginConfigSpec `json:"spec"`
|
||||
}
|
||||
|
||||
type PluginConfigSpec struct {
|
||||
Version string `json:"version"`
|
||||
Init Command `json:"init,omitempty"`
|
||||
Generate Command `json:"generate"`
|
||||
Discover Discover `json:"discover"`
|
||||
AllowConcurrency bool `json:"allowConcurrency"`
|
||||
LockRepo bool `json:"lockRepo"`
|
||||
}
|
||||
|
||||
//Discover holds find and fileName
|
||||
type Discover struct {
|
||||
Find Find `json:"find"`
|
||||
FileName string `json:"fileName"`
|
||||
}
|
||||
|
||||
// Command holds binary path and arguments list
|
||||
type Command struct {
|
||||
Command []string `json:"command,omitempty"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
}
|
||||
|
||||
// Find holds find command or glob pattern
|
||||
type Find struct {
|
||||
Command
|
||||
Glob string `json:"glob"`
|
||||
}
|
||||
|
||||
func ReadPluginConfig(filePath string) (*PluginConfig, error) {
|
||||
path := fmt.Sprintf("%s/%s", strings.TrimRight(filePath, "/"), common.PluginConfigFileName)
|
||||
|
||||
var config PluginConfig
|
||||
err := configUtil.UnmarshalLocalFile(path, &config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = ValidatePluginConfig(config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func ValidatePluginConfig(config PluginConfig) error {
|
||||
if config.Metadata.Name == "" {
|
||||
return fmt.Errorf("invalid plugin configuration file. metadata.name should be non-empty.")
|
||||
}
|
||||
if config.TypeMeta.Kind != ConfigManagementPluginKind {
|
||||
return fmt.Errorf("invalid plugin configuration file. kind should be %s, found %s", ConfigManagementPluginKind, config.TypeMeta.Kind)
|
||||
}
|
||||
if len(config.Spec.Generate.Command) == 0 {
|
||||
return fmt.Errorf("invalid plugin configuration file. spec.generate command should be non-empty")
|
||||
}
|
||||
if config.Spec.Discover.Find.Glob == "" && len(config.Spec.Discover.Find.Command.Command) == 0 && config.Spec.Discover.FileName == "" {
|
||||
return fmt.Errorf("invalid plugin configuration file. atleast one of discover.find.command or discover.find.glob or discover.fineName should be non-empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *PluginConfig) Address() string {
|
||||
var address string
|
||||
pluginSockFilePath := common.GetPluginSockFilePath()
|
||||
if cfg.Spec.Version != "" {
|
||||
address = fmt.Sprintf("%s/%s-%s.sock", pluginSockFilePath, cfg.Metadata.Name, cfg.Spec.Version)
|
||||
} else {
|
||||
address = fmt.Sprintf("%s/%s.sock", pluginSockFilePath, cfg.Metadata.Name)
|
||||
}
|
||||
return address
|
||||
}
|
||||
223
cmpserver/plugin/plugin.go
Normal file
223
cmpserver/plugin/plugin.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/rand"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/buffered_context"
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/mattn/go-zglob"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmpserver/apiclient"
|
||||
)
|
||||
|
||||
// cmpTimeoutBuffer is the amount of time before the request deadline to timeout server-side work. It makes sure there's
|
||||
// enough time before the client times out to send a meaningful error message.
|
||||
const cmpTimeoutBuffer = 100 * time.Millisecond
|
||||
|
||||
// Service implements ConfigManagementPluginService interface
|
||||
type Service struct {
|
||||
initConstants CMPServerInitConstants
|
||||
}
|
||||
|
||||
type CMPServerInitConstants struct {
|
||||
PluginConfig PluginConfig
|
||||
}
|
||||
|
||||
// NewService returns a new instance of the ConfigManagementPluginService
|
||||
func NewService(initConstants CMPServerInitConstants) *Service {
|
||||
return &Service{
|
||||
initConstants: initConstants,
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(ctx context.Context, command Command, path string, env []string) (string, error) {
|
||||
if len(command.Command) == 0 {
|
||||
return "", fmt.Errorf("Command is empty")
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, command.Command[0], append(command.Command[1:], command.Args...)...)
|
||||
|
||||
cmd.Env = env
|
||||
cmd.Dir = path
|
||||
|
||||
execId, err := rand.RandString(5)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
logCtx := log.WithFields(log.Fields{"execID": execId})
|
||||
|
||||
// log in a way we can copy-and-paste into a terminal
|
||||
args := strings.Join(cmd.Args, " ")
|
||||
logCtx.WithFields(log.Fields{"dir": cmd.Dir}).Info(args)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
// Make sure the command is killed immediately on timeout. https://stackoverflow.com/a/38133948/684776
|
||||
cmd.SysProcAttr = newSysProcAttr(true)
|
||||
|
||||
start := time.Now()
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
// Kill by group ID to make sure child processes are killed. The - tells `kill` that it's a group ID.
|
||||
// Since we didn't set Pgid in SysProcAttr, the group ID is the same as the process ID. https://pkg.go.dev/syscall#SysProcAttr
|
||||
_ = sysCallKill(-cmd.Process.Pid)
|
||||
}()
|
||||
|
||||
err = cmd.Wait()
|
||||
|
||||
duration := time.Since(start)
|
||||
output := stdout.String()
|
||||
|
||||
logCtx.WithFields(log.Fields{"duration": duration}).Debug(output)
|
||||
|
||||
if err != nil {
|
||||
err := newCmdError(args, errors.New(err.Error()), strings.TrimSpace(stderr.String()))
|
||||
logCtx.Error(err.Error())
|
||||
return strings.TrimSuffix(output, "\n"), err
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(output, "\n"), nil
|
||||
}
|
||||
|
||||
type CmdError struct {
|
||||
Args string
|
||||
Stderr string
|
||||
Cause error
|
||||
}
|
||||
|
||||
func (ce *CmdError) Error() string {
|
||||
res := fmt.Sprintf("`%v` failed %v", ce.Args, ce.Cause)
|
||||
if ce.Stderr != "" {
|
||||
res = fmt.Sprintf("%s: %s", res, ce.Stderr)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func newCmdError(args string, cause error, stderr string) *CmdError {
|
||||
return &CmdError{Args: args, Stderr: stderr, Cause: cause}
|
||||
}
|
||||
|
||||
// Environ returns a list of environment variables in name=value format from a list of variables
|
||||
func environ(envVars []*apiclient.EnvEntry) []string {
|
||||
var environ []string
|
||||
for _, item := range envVars {
|
||||
if item != nil && item.Name != "" && item.Value != "" {
|
||||
environ = append(environ, fmt.Sprintf("%s=%s", item.Name, item.Value))
|
||||
}
|
||||
}
|
||||
return environ
|
||||
}
|
||||
|
||||
// GenerateManifest runs generate command from plugin config file and returns generated manifest files
|
||||
func (s *Service) GenerateManifest(ctx context.Context, q *apiclient.ManifestRequest) (*apiclient.ManifestResponse, error) {
|
||||
bufferedCtx, cancel := buffered_context.WithEarlierDeadline(ctx, cmpTimeoutBuffer)
|
||||
defer cancel()
|
||||
|
||||
if deadline, ok := bufferedCtx.Deadline(); ok {
|
||||
log.Infof("Generating manifests with deadline %v from now", time.Until(deadline))
|
||||
} else {
|
||||
log.Info("Generating manifests with no request-level timeout")
|
||||
}
|
||||
|
||||
config := s.initConstants.PluginConfig
|
||||
|
||||
env := append(os.Environ(), environ(q.Env)...)
|
||||
if len(config.Spec.Init.Command) > 0 {
|
||||
_, err := runCommand(bufferedCtx, config.Spec.Init, q.AppPath, env)
|
||||
if err != nil {
|
||||
return &apiclient.ManifestResponse{}, err
|
||||
}
|
||||
}
|
||||
|
||||
out, err := runCommand(bufferedCtx, config.Spec.Generate, q.AppPath, env)
|
||||
if err != nil {
|
||||
return &apiclient.ManifestResponse{}, err
|
||||
}
|
||||
|
||||
manifests, err := kube.SplitYAMLToString([]byte(out))
|
||||
if err != nil {
|
||||
return &apiclient.ManifestResponse{}, err
|
||||
}
|
||||
|
||||
return &apiclient.ManifestResponse{
|
||||
Manifests: manifests,
|
||||
}, err
|
||||
}
|
||||
|
||||
// MatchRepository checks whether the application repository type is supported by config management plugin server
|
||||
func (s *Service) MatchRepository(ctx context.Context, q *apiclient.RepositoryRequest) (*apiclient.RepositoryResponse, error) {
|
||||
bufferedCtx, cancel := buffered_context.WithEarlierDeadline(ctx, cmpTimeoutBuffer)
|
||||
defer cancel()
|
||||
|
||||
var repoResponse apiclient.RepositoryResponse
|
||||
config := s.initConstants.PluginConfig
|
||||
if config.Spec.Discover.FileName != "" {
|
||||
log.Debugf("config.Spec.Discover.FileName is provided")
|
||||
pattern := strings.TrimSuffix(q.Path, "/") + "/" + strings.TrimPrefix(config.Spec.Discover.FileName, "/")
|
||||
matches, err := filepath.Glob(pattern)
|
||||
if err != nil || len(matches) == 0 {
|
||||
log.Debugf("Could not find match for pattern %s. Error is %v.", pattern, err)
|
||||
return &repoResponse, err
|
||||
} else if len(matches) > 0 {
|
||||
repoResponse.IsSupported = true
|
||||
return &repoResponse, nil
|
||||
}
|
||||
}
|
||||
|
||||
if config.Spec.Discover.Find.Glob != "" {
|
||||
log.Debugf("config.Spec.Discover.Find.Glob is provided")
|
||||
pattern := strings.TrimSuffix(q.Path, "/") + "/" + strings.TrimPrefix(config.Spec.Discover.Find.Glob, "/")
|
||||
// filepath.Glob doesn't have '**' support hence selecting third-party lib
|
||||
// https://github.com/golang/go/issues/11862
|
||||
matches, err := zglob.Glob(pattern)
|
||||
if err != nil || len(matches) == 0 {
|
||||
log.Debugf("Could not find match for pattern %s. Error is %v.", pattern, err)
|
||||
return &repoResponse, err
|
||||
} else if len(matches) > 0 {
|
||||
repoResponse.IsSupported = true
|
||||
return &repoResponse, nil
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Going to try runCommand.")
|
||||
find, err := runCommand(bufferedCtx, config.Spec.Discover.Find.Command, q.Path, os.Environ())
|
||||
if err != nil {
|
||||
return &repoResponse, err
|
||||
}
|
||||
|
||||
var isSupported bool
|
||||
if find != "" {
|
||||
isSupported = true
|
||||
}
|
||||
return &apiclient.RepositoryResponse{
|
||||
IsSupported: isSupported,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetPluginConfig returns plugin config
|
||||
func (s *Service) GetPluginConfig(ctx context.Context, q *apiclient.ConfigRequest) (*apiclient.ConfigResponse, error) {
|
||||
config := s.initConstants.PluginConfig
|
||||
return &apiclient.ConfigResponse{
|
||||
AllowConcurrency: config.Spec.AllowConcurrency,
|
||||
LockRepo: config.Spec.LockRepo,
|
||||
}, nil
|
||||
}
|
||||
61
cmpserver/plugin/plugin.proto
Normal file
61
cmpserver/plugin/plugin.proto
Normal file
@@ -0,0 +1,61 @@
|
||||
syntax = "proto3";
|
||||
option go_package = "github.com/argoproj/argo-cd/v2/cmpserver/apiclient";
|
||||
|
||||
package plugin;
|
||||
|
||||
import "k8s.io/api/core/v1/generated.proto";
|
||||
|
||||
// ManifestRequest is a query for manifest generation.
|
||||
message ManifestRequest {
|
||||
// Name of the application for which the request is triggered
|
||||
string appName = 1;
|
||||
string appPath = 2;
|
||||
string repoPath = 3;
|
||||
bool noCache = 4;
|
||||
repeated EnvEntry env = 5;
|
||||
}
|
||||
|
||||
// EnvEntry represents an entry in the application's environment
|
||||
message EnvEntry {
|
||||
// Name is the name of the variable, usually expressed in uppercase
|
||||
string name = 1;
|
||||
// Value is the value of the variable
|
||||
string value = 2;
|
||||
}
|
||||
|
||||
message ManifestResponse {
|
||||
repeated string manifests = 1;
|
||||
string sourceType = 2;
|
||||
}
|
||||
|
||||
message RepositoryRequest {
|
||||
string path = 1;
|
||||
repeated EnvEntry env = 2;
|
||||
}
|
||||
|
||||
message RepositoryResponse {
|
||||
bool isSupported = 1;
|
||||
}
|
||||
|
||||
message ConfigRequest {
|
||||
}
|
||||
|
||||
message ConfigResponse {
|
||||
bool allowConcurrency = 1;
|
||||
bool lockRepo = 2;
|
||||
}
|
||||
|
||||
// ConfigManagementPlugin Service
|
||||
service ConfigManagementPluginService {
|
||||
// GenerateManifest generates manifest for application in specified repo name and revision
|
||||
rpc GenerateManifest(ManifestRequest) returns (ManifestResponse) {
|
||||
}
|
||||
|
||||
// MatchRepository returns whether or not the given path is supported by the plugin
|
||||
rpc MatchRepository(RepositoryRequest) returns (RepositoryResponse) {
|
||||
}
|
||||
|
||||
// Get configuration of the plugin
|
||||
rpc GetPluginConfig(ConfigRequest) returns (ConfigResponse) {
|
||||
}
|
||||
}
|
||||
83
cmpserver/plugin/plugin_test.go
Normal file
83
cmpserver/plugin/plugin_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/cmpserver/apiclient"
|
||||
)
|
||||
|
||||
func newService(configFilePath string) (*Service, error) {
|
||||
config, err := ReadPluginConfig(configFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
initConstants := CMPServerInitConstants{
|
||||
PluginConfig: *config,
|
||||
}
|
||||
|
||||
service := &Service{
|
||||
initConstants: initConstants,
|
||||
}
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func TestMatchRepository(t *testing.T) {
|
||||
configFilePath := "./testdata/ksonnet/config"
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
q := apiclient.RepositoryRequest{}
|
||||
path, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
q.Path = path
|
||||
|
||||
res1, err := service.MatchRepository(context.Background(), &q)
|
||||
require.NoError(t, err)
|
||||
require.True(t, res1.IsSupported)
|
||||
}
|
||||
|
||||
func Test_Negative_ConfigFile_DoesnotExist(t *testing.T) {
|
||||
configFilePath := "./testdata/kustomize-neg/config"
|
||||
service, err := newService(configFilePath)
|
||||
require.Error(t, err)
|
||||
require.Nil(t, service)
|
||||
}
|
||||
|
||||
func TestGenerateManifest(t *testing.T) {
|
||||
configFilePath := "./testdata/kustomize/config"
|
||||
service, err := newService(configFilePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
q := apiclient.ManifestRequest{}
|
||||
res1, err := service.GenerateManifest(context.Background(), &q)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res1)
|
||||
|
||||
expectedOutput := "{\"apiVersion\":\"v1\",\"data\":{\"foo\":\"bar\"},\"kind\":\"ConfigMap\",\"metadata\":{\"name\":\"my-map\"}}"
|
||||
if res1 != nil {
|
||||
require.Equal(t, expectedOutput, res1.Manifests[0])
|
||||
}
|
||||
}
|
||||
|
||||
// TestRunCommandContextTimeout makes sure the command dies at timeout rather than sleeping past the timeout.
|
||||
func TestRunCommandContextTimeout(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 990*time.Millisecond)
|
||||
defer cancel()
|
||||
// Use a subshell so there's a child command.
|
||||
command := Command{
|
||||
Command: []string{"sh", "-c"},
|
||||
Args: []string{"sleep 5"},
|
||||
}
|
||||
before := time.Now()
|
||||
_, err := runCommand(ctx, command, "", []string{})
|
||||
after := time.Now()
|
||||
assert.Error(t, err) // The command should time out, causing an error.
|
||||
assert.Less(t, after.Sub(before), 1*time.Second)
|
||||
}
|
||||
16
cmpserver/plugin/plugin_unix.go
Normal file
16
cmpserver/plugin/plugin_unix.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func newSysProcAttr(setpgid bool) *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{Setpgid: setpgid}
|
||||
}
|
||||
|
||||
func sysCallKill(pid int) error {
|
||||
return syscall.Kill(pid, syscall.SIGKILL)
|
||||
}
|
||||
16
cmpserver/plugin/plugin_windows.go
Normal file
16
cmpserver/plugin/plugin_windows.go
Normal file
@@ -0,0 +1,16 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func newSysProcAttr(setpgid bool) *syscall.SysProcAttr {
|
||||
return &syscall.SysProcAttr{}
|
||||
}
|
||||
|
||||
func sysCallKill(pid int) error {
|
||||
return nil
|
||||
}
|
||||
15
cmpserver/plugin/testdata/ksonnet/config/plugin.yaml
vendored
Normal file
15
cmpserver/plugin/testdata/ksonnet/config/plugin.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ConfigManagementPlugin
|
||||
metadata:
|
||||
name: ksonnet
|
||||
spec:
|
||||
version: v1.0
|
||||
init:
|
||||
command: [ks, version]
|
||||
generate:
|
||||
command: [sh, -c, "ks show $ARGOCD_APP_ENV"]
|
||||
discover:
|
||||
find:
|
||||
glob: "**/*/main.jsonnet"
|
||||
allowConcurrency: false
|
||||
lockRepo: false
|
||||
0
cmpserver/plugin/testdata/ksonnet/main.jsonnet
vendored
Normal file
0
cmpserver/plugin/testdata/ksonnet/main.jsonnet
vendored
Normal file
16
cmpserver/plugin/testdata/kustomize-neg/config/plugin-bad.yaml
vendored
Normal file
16
cmpserver/plugin/testdata/kustomize-neg/config/plugin-bad.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ConfigManagementPlugin
|
||||
metadata:
|
||||
name: kustomize
|
||||
spec:
|
||||
version: v1.0
|
||||
init:
|
||||
command: [kustomize, version]
|
||||
generate:
|
||||
command: [sh, -c, "cd testdata/kustomize && kustomize build"]
|
||||
discover:
|
||||
find:
|
||||
command: [sh, -c, find . -name kustomization.yaml]
|
||||
glob: "**/*/kustomization.yaml"
|
||||
allowConcurrency: true
|
||||
lockRepo: false
|
||||
6
cmpserver/plugin/testdata/kustomize/cm.yaml
vendored
Normal file
6
cmpserver/plugin/testdata/kustomize/cm.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-map
|
||||
data:
|
||||
foo: bar
|
||||
16
cmpserver/plugin/testdata/kustomize/config/plugin.yaml
vendored
Normal file
16
cmpserver/plugin/testdata/kustomize/config/plugin.yaml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: ConfigManagementPlugin
|
||||
metadata:
|
||||
name: kustomize
|
||||
spec:
|
||||
version: v1.0
|
||||
init:
|
||||
command: [kustomize, version]
|
||||
generate:
|
||||
command: [sh, -c, "cd testdata/kustomize && kustomize build"]
|
||||
discover:
|
||||
find:
|
||||
command: [sh, -c, find . -name kustomization.yaml]
|
||||
glob: "**/*/kustomization.yaml"
|
||||
allowConcurrency: true
|
||||
lockRepo: false
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user