mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 09:38:49 +01:00
Compare commits
938 Commits
release-2.
...
v2.3.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1287d24bfe | ||
|
|
68e825cf9b | ||
|
|
738384bdd0 | ||
|
|
7fb77d578d | ||
|
|
336d61cac6 | ||
|
|
c0b62972ca | ||
|
|
048c025cfe | ||
|
|
7eb34755e5 | ||
|
|
2f4afe8c08 | ||
|
|
92f53aeb72 | ||
|
|
43f8027f6d | ||
|
|
909f94a383 | ||
|
|
af5348c9b1 | ||
|
|
d4faef6324 | ||
|
|
af45491a7d | ||
|
|
b0e4473fcd | ||
|
|
cf1f44e379 | ||
|
|
ac8b7df946 | ||
|
|
5d515b8423 | ||
|
|
69dcee049e | ||
|
|
d36d95dc9f | ||
|
|
df79d7db1d | ||
|
|
7165431a84 | ||
|
|
13ec3f43d8 | ||
|
|
eea93c5103 | ||
|
|
2cc81959b4 | ||
|
|
07ac038a8f | ||
|
|
3828da7f8c | ||
|
|
3bd9121545 | ||
|
|
df6e7c169e | ||
|
|
25b01666f0 | ||
|
|
9d6e6d84de | ||
|
|
7f9ff6e8c3 | ||
|
|
ecc2af9dca | ||
|
|
c5b0279050 | ||
|
|
6df17e7c56 | ||
|
|
e55ecf9107 | ||
|
|
21f208f17e | ||
|
|
b65c1699fa | ||
|
|
4c1428a6be | ||
|
|
bca190bd0c | ||
|
|
88ca5aabf2 | ||
|
|
8297f827a8 | ||
|
|
b85ef39e0d | ||
|
|
fe42780229 | ||
|
|
057d95374d | ||
|
|
1546c1d314 | ||
|
|
31564c6067 | ||
|
|
6e54e59e82 | ||
|
|
196dab98dc | ||
|
|
9d4ed2847e | ||
|
|
ffc6080060 | ||
|
|
ca2e3041f1 | ||
|
|
31676e2aea | ||
|
|
81f9bc20ec | ||
|
|
0b868bd221 | ||
|
|
37c1585c2f | ||
|
|
e095ef6e4c | ||
|
|
8396e06121 | ||
|
|
d4a6498d80 | ||
|
|
4fc814bc16 | ||
|
|
bf67d55f3f | ||
|
|
87402fdc4c | ||
|
|
10b4c47aad | ||
|
|
e6a9400c38 | ||
|
|
ebb21736df | ||
|
|
ea7ad844cb | ||
|
|
76c0b32887 | ||
|
|
827096c195 | ||
|
|
bca450f7aa | ||
|
|
3c7d99f82c | ||
|
|
c5c25cd75b | ||
|
|
f54d0a037f | ||
|
|
6da138956a | ||
|
|
b3a181ea88 | ||
|
|
2f5da24654 | ||
|
|
cc572a5eb2 | ||
|
|
ae03c3b872 | ||
|
|
79ac472382 | ||
|
|
061ba811f1 | ||
|
|
b5909d59ec | ||
|
|
68f17dbe8e | ||
|
|
bc6b4950ba | ||
|
|
c2cf633bbc | ||
|
|
bdd05d5c7a | ||
|
|
4b04a39180 | ||
|
|
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 |
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**
|
||||
|
||||
|
||||
43
.github/workflows/ci-build.yaml
vendored
43
.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'
|
||||
|
||||
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.16.2'
|
||||
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.16.2'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: Restore go build cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
@@ -67,10 +61,10 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.38.0
|
||||
args: --timeout 5m --exclude SA5011
|
||||
version: v1.46.2
|
||||
args: --timeout 10m --exclude SA5011 --verbose
|
||||
|
||||
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.16.2'
|
||||
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.16.2'
|
||||
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.16.2'
|
||||
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.23.3, v1.22.6, v1.21.2]
|
||||
needs:
|
||||
- build-go
|
||||
env:
|
||||
@@ -354,7 +350,7 @@ jobs:
|
||||
- name: Setup Golang
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.16.2'
|
||||
go-version: ${{ env.GOLANG_VERSION }}
|
||||
- name: GH actions workaround - Kill XSP4 process
|
||||
run: |
|
||||
sudo pkill mono || true
|
||||
@@ -380,10 +376,13 @@ jobs:
|
||||
- name: Add /usr/local/bin to PATH
|
||||
run: |
|
||||
echo "/usr/local/bin" >> $GITHUB_PATH
|
||||
- name: Add ./dist to PATH
|
||||
run: |
|
||||
echo "$(pwd)/dist" >> $GITHUB_PATH
|
||||
- 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
|
||||
@@ -395,7 +394,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:6.2.1-alpine
|
||||
docker pull redis:6.2.6-alpine
|
||||
- name: Create target directory for binaries in the build-process
|
||||
run: |
|
||||
mkdir -p dist
|
||||
|
||||
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'
|
||||
|
||||
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.16.2'
|
||||
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'
|
||||
|
||||
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.16.2'
|
||||
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
|
||||
@@ -1,22 +0,0 @@
|
||||
run:
|
||||
timeout: 2m
|
||||
skip-files:
|
||||
- ".*\\.pb\\.go"
|
||||
skip-dirs:
|
||||
- pkg/client/
|
||||
- vendor/
|
||||
linters:
|
||||
enable:
|
||||
- vet
|
||||
- deadcode
|
||||
- goimports
|
||||
- varcheck
|
||||
- structcheck
|
||||
- ineffassign
|
||||
- unconvert
|
||||
- unparam
|
||||
linters-settings:
|
||||
goimports:
|
||||
local-prefixes: github.com/argoproj/argo-cd
|
||||
service:
|
||||
golangci-lint-version: 1.21.0
|
||||
352
CHANGELOG.md
352
CHANGELOG.md
@@ -1,6 +1,328 @@
|
||||
# Changelog
|
||||
|
||||
## v2.0.0 (Unreleased)
|
||||
## v2.3.0 (Unreleased)
|
||||
|
||||
### Argo CD ApplicationSet and Notifications are now part of Argo CD
|
||||
|
||||
Two popular [Argoproj Labs](https://github.com/argoproj-labs) projects [Argo CD ApplicationSet](https://github.com/argoproj/applicationset) and
|
||||
[Argo CD Notifications](https://github.com/argoproj-labs/argocd-notifications) are now part of Argo CD! The default Argo CD installation manifests now
|
||||
bundle both projects out of the box. Going forward you can expect more tightened integration of these projects into Argo CD.
|
||||
|
||||
### New sync and diff strategies
|
||||
|
||||
Users can now configure the Application resource to instruct Argo CD to consider the ignore difference setup during the sync process.
|
||||
In order to do so, add the new sync option RespectIgnoreDifferences=true in the Application resource. Once the sync option is added,
|
||||
Argo CD won't change ignored fields during the syncing process.
|
||||
|
||||
Configuring ignored fields is also easier now. Instead of listing fields one by one users can now leverage the
|
||||
managedFields metadata to instruct Argo CD about trusted managers and automatically ignore any fields owned by them. A new diff customization
|
||||
(managedFieldsManagers) is now available allowing users to specify managers the application should trust and to ignore all fields owned by those managers.
|
||||
Read more about these changes at [New sync and diff strategies in ArgoCD](https://blog.argoproj.io/new-sync-and-diff-strategies-in-argocd-44195d3f8b8c) blog post.
|
||||
|
||||
### ARM Images
|
||||
|
||||
An officially supported ARM 64 image is now available. Enjoy running Argo CD on your Raspberry Pi! Additionally, the image size was reduced by nearly ~50%
|
||||
and is only 200MB now. The ARM version of `argocd` CLI is also available and published as a Github release artifact.
|
||||
|
||||
### Compact Tree View And Click Application Navigation
|
||||
|
||||
The application details page now supports compact application resources tree visualization. Using the "Group Nodes" button, you can collapse the similar resources
|
||||
into a single group node to remove the clutter and make it easier to understand the state of application resources. You still can get detailed information about the collapsed resources by clicking on the group node. The list of collapsed resources will be available in a sliding panel. Compact resource tree is still too big?
|
||||
You can use the zoom in and zoom out feature to make it smaller - or even larger!
|
||||
|
||||
You no longer need to move back and forth between the application details page and the application list page. Instead you can navigate directly to the required application by clicking the search icon in the application details page title.
|
||||
|
||||
### Upgraded Config Management Tools
|
||||
|
||||
Both bundled Helm and Kustomize binaries have been upgraded to the latest versions. Kustomize has been upgraded from 4.2.0 to 4.4.1 and Helm has been upgraded from 3.7.1 to 3.8.0.
|
||||
|
||||
### Bug Fixes and Performance Enhancements
|
||||
|
||||
* Config management tools enhancements:
|
||||
* The skipCrds flag and ability to ignore missing values files for Helm (#8012, #8003)
|
||||
* Additional environment variables for Kustomize (#8096)
|
||||
* Argo CD CLI follows the XDG Base directory standard (#7638)
|
||||
* Redis is no longer used during SSO login (#8241)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
- feat: Add app list and details page views to navigation history (#7776) (#7937)
|
||||
- feat: Add skipCrds flag for helm charts (#8012)
|
||||
- feat: Add visual indicator for newly created pods (#8006)
|
||||
- feat: Added a new Helm option ignoreMissingValueFiles (#7767) (#8003)
|
||||
- feat: Allow configuring system wide ignore differences for all resources (#8224)
|
||||
- feat: Allow escaping dollar in Envsubst (#7961)
|
||||
- feat: Allow external links on Application (#3487) (#8231)
|
||||
- feat: Allow selecting application on detail page (#8176)
|
||||
- feat: Bundle applicationset-controller with argocd (#8148)
|
||||
- feat: Enable specifying root ca for oidc (#6712)
|
||||
- feat: Expose ARGOCD_APP_NAME to the `kustomize build` command (#8096)
|
||||
- feat: Ignore differences owned by trusted managers from managedFields (#7869)
|
||||
- feat: New sync option to use ignore diff configs during sync (#8078)
|
||||
- feat: Provide address flag for admin dashboard command (#8095)
|
||||
- feat: Store "Group Nodes" button state in application details preferences (#8036)
|
||||
- feat: Support specifying cluster by name in addition to API server URL in Cluster API (#8077)
|
||||
- feat: Support XDG Base directory standard (#7638) (#7791)
|
||||
- feat: Use encrypted cookie to store OAuth2 state nonce (instead of redis) (#8241)
|
||||
- feat: Build images on PR and conditionally build arm64 image on push (#8108)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- fix: Add "Restarting MinIO" status to MiniO Tenant health check (#8191)
|
||||
- fix: Add all resources in list view (#7295)
|
||||
- fix: Adding pagination to grouped nodes sliding panel#7837 (#7915)
|
||||
- fix: Allow all resources to add external links (#7923)
|
||||
- fix: Always call ValidateDestination (#7976)
|
||||
- fix: Application exist panic when execute api call (#8188)
|
||||
- fix: Application-icons-alignment (#8054)
|
||||
- fix: Controller panics if resource manifest has incorrect annotation (#8022)
|
||||
- fix: Correctly handle project field during partial cluster update (#7994)
|
||||
- fix: Default value for retry validation #8055 (#8064)
|
||||
- fix: Fix a possible crash when parsing RBAC (#8165)
|
||||
- fix: Grouped node list missing resources on Compact resources view #8014 (#8018)
|
||||
- fix: Issue with headless installation (#7958)
|
||||
- fix: Issue with project scoped resources (#8048)
|
||||
- fix: Kubernetes labels normalization for Prometheus (#7925)
|
||||
- fix: Nested Refresh dropdown does not work on Application Details page #1524 (#7950)
|
||||
- fix: Network line colors and menu icon alignment (#8059)
|
||||
- fix: Opening app details shows UI error on some apps (#8016) (#8019)
|
||||
- fix: Parse to correct uint32 type (#8177)
|
||||
- fix: Prevent possible nil-pointer deref in normalizer (#8185)
|
||||
- fix: Prevent possible out-of-bounds access when loading policies (#8186)
|
||||
- fix: Provide a semantic version parsed version for KUBE_VERSION (#8250)
|
||||
- fix: Refreshing label toast (#7979)
|
||||
- fix: Resource details page crashes when resource is not deployed and hide managed fields is selected (#7971)
|
||||
- fix: Retry disabled text (#8004)
|
||||
- fix: Route health check stuck in 'Progressing' (#8170)
|
||||
- fix: Sync window panel is crashed if resource name not contain letters (#8053)
|
||||
- fix: Targetervision compatible without prefix refs/heads or refs/tags (#7939)
|
||||
- fix: Trailing line in Filter Dropdown Menus #7821 (#8001)
|
||||
- fix: Webhook URL matching edge cases (#7981)
|
||||
- fix(ui): Use consistent case for diff modes (#7945)
|
||||
- fix: Use gRPC timeout for sidecar CMPs (#8131) (#8236)
|
||||
|
||||
### Other
|
||||
|
||||
- chore: Bump go-jsonnet to v0.18.0 (#8011)
|
||||
- chore: Escape proj in regex (#7985)
|
||||
- chore: Exclude argocd-server rbac for core-install (#8234)
|
||||
- chore: Log out the resource triggering reconciliation (#8192)
|
||||
- chore: Migrate to use golang-jwt/jwt v4.2.0 (#8136)
|
||||
- chore: Move resolveRevision from api-server to repo-server (#7966)
|
||||
- chore: Update notifications version (#8267)
|
||||
- chore: Update slack version (#8299)
|
||||
- chore: Update to Redis 6.2.4 (#8157)
|
||||
- chore: Upgrade awscli to 2.4.6 and remove python deps (#7947)
|
||||
- chore: Upgrade base image to ubuntu:21.10 (#8230)
|
||||
- chore: Upgrade dex to v2.30.2 (https://github.com/dexidp/dex/issues/2326) (#8237)
|
||||
- chore: Upgrade gitops engine (#8288)
|
||||
- chore: Upgrade golang to 1.17.6 (#8229)
|
||||
- chore: Upgrade helm to most recent version (v3.7.2) (#8226)
|
||||
- chore: Upgrade k8s client to v1.23 (#8213)
|
||||
- chore: Upgrade kustomize to most recent version (v4.4.1) (#8227)
|
||||
- refactor: Introduce 'byClusterName' secret index to speedup cluster server URL lookup (#8133)
|
||||
- refactor: Move project filtering to server side (#8102)
|
||||
|
||||
## v2.2.3 (2022-01-18)
|
||||
|
||||
- fix: Application exist panic when execute api call (#8188)
|
||||
- fix: Route health check stuck in 'Progressing' (#8170)
|
||||
- refactor: Introduce 'byClusterName' secret index to speedup cluster server URL lookup (#8133)
|
||||
- chore: Update to Redis 6.2.4 (#8157) (#8158)
|
||||
|
||||
## v2.2.2 (2021-12-31)
|
||||
|
||||
- fix: Issue with project scoped resources (#8048)
|
||||
- fix: Escape proj in regex (#7985)
|
||||
- fix: Default value for retry validation #8055 (#8064)
|
||||
- fix: Sync window panel is crashed if resource name not contain letters (#8053)
|
||||
- fix: Upgrade github.com/argoproj/gitops-engine to v0.5.2
|
||||
- fix: Retry disabled text (#8004)
|
||||
- fix: Opening app details shows UI error on some apps (#8016) (#8019)
|
||||
- fix: Correctly handle project field during partial cluster update (#7994)
|
||||
- fix: Cluster API does not support updating labels and annotations (#7901)
|
||||
|
||||
## v2.2.1 (2021-12-16)
|
||||
|
||||
- fix: Resource details page crashes when resource is not deployed and hide managed fields is selected (#7971)
|
||||
- fix: Issue with headless installation (#7958)
|
||||
- fix: Nil pointer (#7905)
|
||||
|
||||
## 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)
|
||||
|
||||
@@ -198,7 +520,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)
|
||||
@@ -332,7 +654,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)
|
||||
|
||||
@@ -395,7 +717,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
|
||||
@@ -427,7 +749,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)
|
||||
@@ -543,7 +865,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)
|
||||
@@ -650,10 +972,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)
|
||||
@@ -838,7 +1160,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)
|
||||
@@ -920,7 +1242,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)
|
||||
@@ -1005,7 +1327,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)
|
||||
@@ -1061,7 +1383,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
|
||||
@@ -1425,7 +1747,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)
|
||||
@@ -1917,8 +2239,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
|
||||
|
||||
43
Dockerfile
43
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.16.2 as builder
|
||||
FROM docker.io/library/golang:1.17 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,24 +48,23 @@ 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
|
||||
COPY hack/gpg-wrapper.sh /usr/local/bin/gpg-wrapper.sh
|
||||
COPY hack/git-verify-wrapper.sh /usr/local/bin/git-verify-wrapper.sh
|
||||
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 +86,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 200000
|
||||
|
||||
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.16.0 as argocd-build
|
||||
|
||||
COPY --from=builder /usr/local/bin/packr /usr/local/bin/packr
|
||||
FROM docker.io/library/golang:1.17 as argocd-build
|
||||
|
||||
WORKDIR /go/src/github.com/argoproj/argo-cd
|
||||
|
||||
@@ -115,26 +113,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
|
||||
|
||||
134
Makefile
134
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
|
||||
@@ -47,6 +47,8 @@ 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
|
||||
|
||||
@@ -75,6 +77,9 @@ define run-in-test-server
|
||||
-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} \
|
||||
@@ -83,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
|
||||
@@ -154,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
|
||||
@@ -173,7 +179,7 @@ gogen: ensure-gopath
|
||||
go generate ./util/argo/...
|
||||
|
||||
.PHONY: protogen
|
||||
protogen: ensure-gopath
|
||||
protogen: ensure-gopath mod-vendor-local
|
||||
export GO111MODULE=off
|
||||
./hack/generate-proto.sh
|
||||
|
||||
@@ -182,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
|
||||
@@ -191,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
|
||||
@@ -205,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 build-ui
|
||||
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:
|
||||
@@ -245,25 +252,25 @@ 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
|
||||
CGO_ENABLED=0 go build -v -ldflags '${LDFLAGS}' -o ${DIST_DIR}/argocd-application-controller ./cmd
|
||||
|
||||
.PHONY: packr
|
||||
packr:
|
||||
go build -o ${DIST_DIR}/packr github.com/gobuffalo/packr/packr/
|
||||
.PHONY: build-ui
|
||||
build-ui:
|
||||
docker build -t argocd-ui --target argocd-ui .
|
||||
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/'
|
||||
|
||||
.PHONY: image
|
||||
ifeq ($(DEV_IMAGE), true)
|
||||
@@ -271,19 +278,14 @@ 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: build-ui
|
||||
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
|
||||
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
|
||||
@@ -293,10 +295,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:
|
||||
@@ -309,7 +309,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
|
||||
@@ -399,7 +399,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
|
||||
@@ -426,19 +426,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
|
||||
@@ -464,10 +466,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
|
||||
@@ -501,10 +509,6 @@ serve-docs-local:
|
||||
serve-docs:
|
||||
docker run ${MKDOCS_RUN_ARGS} --rm -it -p 8000:8000 -v ${CURRENT_DIR}:/docs ${MKDOCS_DOCKER_IMAGE} serve -a 0.0.0.0:8000
|
||||
|
||||
.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 --
|
||||
|
||||
# Verify that kubectl can connect to your K8s cluster from Docker
|
||||
.PHONY: verify-kube-connect
|
||||
@@ -526,16 +530,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
|
||||
@@ -548,3 +551,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 '^$@$$'
|
||||
|
||||
11
OWNERS
11
OWNERS
@@ -9,8 +9,19 @@ approvers:
|
||||
- jessesuen
|
||||
- jgwest
|
||||
- mayzhang2000
|
||||
- rbreeze
|
||||
|
||||
reviewers:
|
||||
- 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:6.2.1-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}
|
||||
|
||||
10
README.md
10
README.md
@@ -45,6 +45,11 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
|
||||
### 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)
|
||||
@@ -52,10 +57,9 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
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)
|
||||
@@ -67,3 +71,5 @@ Participation in the Argo CD project is governed by the [CNCF Code of Conduct](h
|
||||
1. [Setting up Argo CD with Helm](https://www.arthurkoziel.com/setting-up-argocd-with-helm/)
|
||||
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.
|
||||
|
||||
73
USERS.md
73
USERS.md
@@ -8,26 +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)
|
||||
@@ -36,18 +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/)
|
||||
@@ -58,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)
|
||||
@@ -76,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)
|
||||
@@ -92,9 +129,13 @@ Currently, the following organizations are **officially** using Argo CD:
|
||||
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/)
|
||||
@@ -104,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/)
|
||||
@@ -117,8 +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. [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/)
|
||||
|
||||
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)
|
||||
|
||||
@@ -256,7 +256,7 @@
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "the selector to to restrict returned list to applications only with matched labels.",
|
||||
"description": "the selector to restrict returned list to applications only with matched labels.",
|
||||
"name": "selector",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -520,7 +520,7 @@
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "the selector to to restrict returned list to applications only with matched labels.",
|
||||
"description": "the selector to restrict returned list to applications only with matched labels.",
|
||||
"name": "selector",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -753,6 +753,11 @@
|
||||
"type": "string",
|
||||
"name": "resourceName",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "previous",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -932,6 +937,11 @@
|
||||
"type": "string",
|
||||
"name": "resourceName",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"name": "previous",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1595,6 +1605,18 @@
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "type is the type of the specified cluster identifier ( \"server\" - default, \"name\" ).",
|
||||
"name": "id.type",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "value holds the cluster server URL or cluster name.",
|
||||
"name": "id.value",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1649,7 +1671,53 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/clusters/{cluster.server}": {
|
||||
"/api/v1/clusters/{id.value}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"ClusterService"
|
||||
],
|
||||
"summary": "Get returns a cluster by server address",
|
||||
"operationId": "ClusterService_Get",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "value holds the cluster server URL or cluster name",
|
||||
"name": "id.value",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "server",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "type is the type of the specified cluster identifier ( \"server\" - default, \"name\" ).",
|
||||
"name": "id.type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1alpha1Cluster"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/runtimeError"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"ClusterService"
|
||||
@@ -1659,8 +1727,8 @@
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Server is the API server URL of the Kubernetes cluster",
|
||||
"name": "cluster.server",
|
||||
"description": "value holds the cluster server URL or cluster name",
|
||||
"name": "id.value",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
@@ -1680,41 +1748,11 @@
|
||||
"collectionFormat": "multi",
|
||||
"name": "updatedFields",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/v1alpha1Cluster"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/runtimeError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/clusters/{server}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"ClusterService"
|
||||
],
|
||||
"summary": "Get returns a cluster by server address",
|
||||
"operationId": "ClusterService_Get",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "server",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"description": "type is the type of the specified cluster identifier ( \"server\" - default, \"name\" ).",
|
||||
"name": "id.type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
@@ -1742,14 +1780,26 @@
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "server",
|
||||
"description": "value holds the cluster server URL or cluster name",
|
||||
"name": "id.value",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "server",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "type is the type of the specified cluster identifier ( \"server\" - default, \"name\" ).",
|
||||
"name": "id.type",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -1768,7 +1818,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/clusters/{server}/invalidate-cache": {
|
||||
"/api/v1/clusters/{id.value}/invalidate-cache": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"ClusterService"
|
||||
@@ -1778,7 +1828,8 @@
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "server",
|
||||
"description": "value holds the cluster server URL or cluster name",
|
||||
"name": "id.value",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
@@ -1799,7 +1850,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/clusters/{server}/rotate-auth": {
|
||||
"/api/v1/clusters/{id.value}/rotate-auth": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"ClusterService"
|
||||
@@ -1809,7 +1860,8 @@
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "server",
|
||||
"description": "value holds the cluster server URL or cluster name",
|
||||
"name": "id.value",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
@@ -2081,6 +2133,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/projects/{name}/detailed": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"ProjectService"
|
||||
],
|
||||
"summary": "GetDetailedProject returns a project that include project, global project and scoped resources by name",
|
||||
"operationId": "ProjectService_GetDetailedProject",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "A successful response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/projectDetailedProjectsResponse"
|
||||
}
|
||||
},
|
||||
"default": {
|
||||
"description": "An unexpected error response.",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/runtimeError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/projects/{name}/events": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -2648,7 +2731,7 @@
|
||||
"tags": [
|
||||
"RepositoryService"
|
||||
],
|
||||
"summary": "ListApps returns list of apps in the repe",
|
||||
"summary": "ListApps returns list of apps in the repo",
|
||||
"operationId": "RepositoryService_ListApps",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -2661,6 +2744,16 @@
|
||||
"type": "string",
|
||||
"name": "revision",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "appName",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "appProject",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -2857,6 +2950,18 @@
|
||||
"description": "Github App Enterprise base url if empty will default to https://api.github.com.",
|
||||
"name": "githubAppEnterpriseBaseUrl",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "HTTP/HTTPS proxy to access the repository.",
|
||||
"name": "proxy",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity.",
|
||||
"name": "project",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
@@ -3053,7 +3158,7 @@
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "the selector to to restrict returned list to applications only with matched labels.",
|
||||
"description": "the selector to restrict returned list to applications only with matched labels.",
|
||||
"name": "selector",
|
||||
"in": "query"
|
||||
},
|
||||
@@ -3477,6 +3582,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clusterClusterID": {
|
||||
"type": "object",
|
||||
"title": "ClusterID holds a cluster server URL or cluster name",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "type is the type of the specified cluster identifier ( \"server\" - default, \"name\" )"
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"title": "value holds the cluster server URL or cluster name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"clusterClusterResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
@@ -3517,6 +3636,13 @@
|
||||
"type": "object",
|
||||
"title": "Help settings",
|
||||
"properties": {
|
||||
"binaryUrls": {
|
||||
"type": "object",
|
||||
"title": "the URLs for downloading argocd binaries",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"chatText": {
|
||||
"type": "string",
|
||||
"title": "the text for getting chat help, defaults to \"Chat now!\""
|
||||
@@ -3599,6 +3725,9 @@
|
||||
"oidcConfig": {
|
||||
"$ref": "#/definitions/clusterOIDCConfig"
|
||||
},
|
||||
"passwordPattern": {
|
||||
"type": "string"
|
||||
},
|
||||
"plugins": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -3614,9 +3743,18 @@
|
||||
"statusBadgeEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"trackingMethod": {
|
||||
"type": "string"
|
||||
},
|
||||
"uiBannerContent": {
|
||||
"type": "string"
|
||||
},
|
||||
"uiBannerPermanent": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"uiBannerPosition": {
|
||||
"type": "string"
|
||||
},
|
||||
"uiBannerURL": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -3668,6 +3806,32 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"projectDetailedProjectsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clusters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1Cluster"
|
||||
}
|
||||
},
|
||||
"globalProjects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1AppProject"
|
||||
}
|
||||
},
|
||||
"project": {
|
||||
"$ref": "#/definitions/v1alpha1AppProject"
|
||||
},
|
||||
"repositories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1Repository"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"projectEmptyResponse": {
|
||||
"type": "object"
|
||||
},
|
||||
@@ -3948,6 +4112,12 @@
|
||||
"type": "object",
|
||||
"title": "RepoAppDetailsQuery contains query information for app details request",
|
||||
"properties": {
|
||||
"appName": {
|
||||
"type": "string"
|
||||
},
|
||||
"appProject": {
|
||||
"type": "string"
|
||||
},
|
||||
"source": {
|
||||
"$ref": "#/definitions/v1alpha1ApplicationSource"
|
||||
}
|
||||
@@ -4274,6 +4444,10 @@
|
||||
"description": "Operation is the type of operation which lead to this ManagedFieldsEntry being created.\nThe only valid values for this field are 'Apply' and 'Update'.",
|
||||
"type": "string"
|
||||
},
|
||||
"subresource": {
|
||||
"description": "Subresource is the name of the subresource used to update that object, or\nempty string if the object was updated through the main resource. The\nvalue of this field is used to distinguish between managers, even if they\nshare the same name. For example, a status update will be distinct from a\nregular update using the same manager name.\nNote that the APIVersion field is not related to the Subresource field and\nit always corresponds to the version of the main resource.",
|
||||
"type": "string"
|
||||
},
|
||||
"time": {
|
||||
"$ref": "#/definitions/v1Time"
|
||||
}
|
||||
@@ -4428,7 +4602,7 @@
|
||||
},
|
||||
"v1ObjectReference": {
|
||||
"type": "object",
|
||||
"title": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object",
|
||||
"title": "ObjectReference contains enough information to let you inspect or modify the referred object.\n---\nNew uses of this type are discouraged because of difficulty describing its usage when embedded in APIs.\n 1. Ignored fields. It includes many fields which are not generally honored. For instance, ResourceVersion and FieldPath are both very rarely valid in actual usage.\n 2. Invalid usage help. It is impossible to add specific help for individual usage. In most embedded usages, there are particular\n restrictions like, \"must refer only to types A and B\" or \"UID not honored\" or \"name must be restricted\".\n Those cannot be well described when embedded.\n 3. Inconsistent validation. Because the usages are different, the validation rules are different by usage, which makes it hard for users to predict what will happen.\n 4. The fields are both imprecise and overly precise. Kind is not a precise mapping to a URL. This can produce ambiguity\n during interpretation and require a REST mapping. In most cases, the dependency is on the group,resource tuple\n and the version of the actual struct is irrelevant.\n 5. We cannot easily change it. Because this type is embedded in many locations, updates to this type\n will affect numerous schemas. Don't make new APIs embed an underspecified API type they do not control.\nInstead of using this type, create a locally provided and used type that is well-focused on your reference.\nFor example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 .\n+k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object\n+structType=atomic",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"type": "string",
|
||||
@@ -4461,8 +4635,8 @@
|
||||
}
|
||||
},
|
||||
"v1OwnerReference": {
|
||||
"description": "OwnerReference contains enough information to let you identify an owning\nobject. An owning object must be in the same namespace as the dependent, or\nbe cluster-scoped, so there is no namespace field.",
|
||||
"type": "object",
|
||||
"title": "OwnerReference contains enough information to let you identify an owning\nobject. An owning object must be in the same namespace as the dependent, or\nbe cluster-scoped, so there is no namespace field.\n+structType=atomic",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "API version of the referent.",
|
||||
@@ -4794,6 +4968,10 @@
|
||||
"$ref": "#/definitions/v1alpha1HelmFileParameter"
|
||||
}
|
||||
},
|
||||
"ignoreMissingValueFiles": {
|
||||
"type": "boolean",
|
||||
"title": "IgnoreMissingValueFiles prevents helm template from failing when valueFiles do not exist locally by not appending them to helm template --values"
|
||||
},
|
||||
"parameters": {
|
||||
"type": "array",
|
||||
"title": "Parameters is a list of Helm parameters which are passed to the helm template command upon manifest generation",
|
||||
@@ -4801,10 +4979,18 @@
|
||||
"$ref": "#/definitions/v1alpha1HelmParameter"
|
||||
}
|
||||
},
|
||||
"passCredentials": {
|
||||
"type": "boolean",
|
||||
"title": "PassCredentials pass credentials to all domains (Helm's --pass-credentials)"
|
||||
},
|
||||
"releaseName": {
|
||||
"type": "string",
|
||||
"title": "ReleaseName is the Helm release name to use. If omitted it will use the application name"
|
||||
},
|
||||
"skipCrds": {
|
||||
"type": "boolean",
|
||||
"title": "SkipCrds skips custom resource definition installation step (Helm's --skip-crds)"
|
||||
},
|
||||
"valueFiles": {
|
||||
"type": "array",
|
||||
"title": "ValuesFiles is a list of Helm value files to use when generating a template",
|
||||
@@ -4884,6 +5070,14 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"forceCommonAnnotations": {
|
||||
"type": "boolean",
|
||||
"title": "ForceCommonAnnotations specifies whether to force applying common annotations to resources for Kustomize apps"
|
||||
},
|
||||
"forceCommonLabels": {
|
||||
"type": "boolean",
|
||||
"title": "ForceCommonLabels specifies whether to force applying common labels to resources for Kustomize apps"
|
||||
},
|
||||
"images": {
|
||||
"type": "array",
|
||||
"title": "Images is a list of Kustomize image override specifications",
|
||||
@@ -5089,6 +5283,17 @@
|
||||
"type": "object",
|
||||
"title": "Cluster is the definition of a cluster resource",
|
||||
"properties": {
|
||||
"annotations": {
|
||||
"type": "object",
|
||||
"title": "Annotations for cluster secret metadata",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"clusterResources": {
|
||||
"description": "Indicates if cluster level resources should be managed. This setting is used only if cluster is connected in a namespaced mode.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"config": {
|
||||
"$ref": "#/definitions/v1alpha1ClusterConfig"
|
||||
},
|
||||
@@ -5098,6 +5303,13 @@
|
||||
"info": {
|
||||
"$ref": "#/definitions/v1alpha1ClusterInfo"
|
||||
},
|
||||
"labels": {
|
||||
"type": "object",
|
||||
"title": "Labels for cluster secret metadata",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name of the cluster. If omitted, will use the server address"
|
||||
@@ -5109,6 +5321,10 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"title": "Reference between project and cluster that allow you automatically to be added as item inside Destinations project entity"
|
||||
},
|
||||
"refreshRequestedAt": {
|
||||
"$ref": "#/definitions/v1Time"
|
||||
},
|
||||
@@ -5176,6 +5392,13 @@
|
||||
"type": "object",
|
||||
"title": "ClusterInfo contains information about the cluster",
|
||||
"properties": {
|
||||
"apiVersions": {
|
||||
"type": "array",
|
||||
"title": "APIVersions contains list of API versions supported by the cluster",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"applicationsCount": {
|
||||
"type": "string",
|
||||
"format": "int64",
|
||||
@@ -5248,6 +5471,9 @@
|
||||
"init": {
|
||||
"$ref": "#/definitions/v1alpha1Command"
|
||||
},
|
||||
"lockRepo": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5643,10 +5869,25 @@
|
||||
},
|
||||
"v1alpha1OverrideIgnoreDiff": {
|
||||
"type": "object",
|
||||
"title": "TODO: describe this type",
|
||||
"title": "OverrideIgnoreDiff contains configurations about how fields should be ignored during diffs between\nthe desired state and live state",
|
||||
"properties": {
|
||||
"jSONPointers": {
|
||||
"type": "array",
|
||||
"title": "JSONPointers is a JSON path list following the format defined in RFC4627 (https://datatracker.ietf.org/doc/html/rfc6902#section-3)",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"jqPathExpressions": {
|
||||
"type": "array",
|
||||
"title": "JQPathExpressions is a JQ path list that will be evaludated during the diff process",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"managedFieldsManagers": {
|
||||
"type": "array",
|
||||
"title": "ManagedFieldsManagers is a list of trusted managers. Fields mutated by those managers will take precedence over the\ndesired state defined in the SCM and won't be displayed in diffs",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5681,7 +5922,7 @@
|
||||
},
|
||||
"policies": {
|
||||
"type": "array",
|
||||
"title": "Policies Stores a list of casbin formated strings that define access policies for the role in the project",
|
||||
"title": "Policies Stores a list of casbin formatted strings that define access policies for the role in the project",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
@@ -5692,6 +5933,10 @@
|
||||
"type": "object",
|
||||
"title": "RepoCreds holds the definition for repository credentials",
|
||||
"properties": {
|
||||
"enableOCI": {
|
||||
"type": "boolean",
|
||||
"title": "EnableOCI specifies whether helm-oci support should be enabled for this repo"
|
||||
},
|
||||
"githubAppEnterpriseBaseUrl": {
|
||||
"type": "string",
|
||||
"title": "GithubAppEnterpriseBaseURL specifies the GitHub API URL for GitHub app authentication. If empty will default to https://api.github.com"
|
||||
@@ -5726,6 +5971,10 @@
|
||||
"type": "string",
|
||||
"title": "TLSClientCertKey specifies the TLS client cert key for authenticating at the repo server"
|
||||
},
|
||||
"type": {
|
||||
"description": "Type specifies the type of the repoCreds. Can be either \"git\" or \"helm. \"git\" is assumed if empty or absent.",
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"title": "URL is the URL that this credentials matches to"
|
||||
@@ -5804,6 +6053,14 @@
|
||||
"type": "string",
|
||||
"title": "Password contains the password or PAT used for authenticating at the remote repository"
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"title": "Reference between project and repository that allow you automatically to be added as item inside SourceRepos project entity"
|
||||
},
|
||||
"proxy": {
|
||||
"type": "string",
|
||||
"title": "Proxy specifies the HTTP/HTTPS proxy used to access the repo"
|
||||
},
|
||||
"repo": {
|
||||
"type": "string",
|
||||
"title": "Repo contains the URL to the remote repository"
|
||||
@@ -5978,6 +6235,12 @@
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"jqPathExpressions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"jsonPointers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -5987,6 +6250,13 @@
|
||||
"kind": {
|
||||
"type": "string"
|
||||
},
|
||||
"managedFieldsManagers": {
|
||||
"type": "array",
|
||||
"title": "ManagedFieldsManagers is a list of trusted managers. Fields mutated by those managers will take precedence over the\ndesired state defined in the SCM and won't be displayed in diffs",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -6089,6 +6359,9 @@
|
||||
"items": {
|
||||
"$ref": "#/definitions/v1alpha1KnownTypeField"
|
||||
}
|
||||
},
|
||||
"useOpenLibs": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6479,6 +6752,10 @@
|
||||
"schedule": {
|
||||
"type": "string",
|
||||
"title": "Schedule is the time the window will begin, specified in cron format"
|
||||
},
|
||||
"timeZone": {
|
||||
"type": "string",
|
||||
"title": "TimeZone of the sync that will be applied to the schedule"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -13,21 +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"
|
||||
"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/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 (
|
||||
@@ -49,6 +49,7 @@ func NewCommand() *cobra.Command {
|
||||
glogLevel int
|
||||
metricsPort int
|
||||
metricsCacheExpiration time.Duration
|
||||
metricsAplicationLabels []string
|
||||
kubectlParallelismLimit int64
|
||||
cacheSrc func() (*appstatecache.Cache, error)
|
||||
redisClient *redis.Client
|
||||
@@ -75,7 +76,14 @@ func NewCommand() *cobra.Command {
|
||||
namespace, _, err := clientConfig.Namespace()
|
||||
errors.CheckError(err)
|
||||
|
||||
resyncDuration := time.Duration(appResyncPeriod) * time.Second
|
||||
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,
|
||||
@@ -103,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,
|
||||
@@ -118,6 +130,7 @@ func NewCommand() *cobra.Command {
|
||||
time.Duration(selfHealTimeoutSeconds)*time.Second,
|
||||
metricsPort,
|
||||
metricsCacheExpiration,
|
||||
metricsAplicationLabels,
|
||||
kubectlParallelismLimit,
|
||||
clusterFilter)
|
||||
errors.CheckError(err)
|
||||
@@ -137,20 +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", false, "Disable TLS on connections to repo server")
|
||||
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", false, "Whether to use strict validation of the TLS cert presented by the repo server")
|
||||
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 (
|
||||
|
||||
57
cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go
Normal file
57
cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/git"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/askpass"
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
// cliName is the name of the CLI
|
||||
cliName = "argocd-git-ask-pass"
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
Short: "Argo CD git credential helper",
|
||||
DisableAutoGenTag: true,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(os.Args) != 2 {
|
||||
errors.CheckError(fmt.Errorf("expected 1 argument, got %d", len(os.Args)-1))
|
||||
}
|
||||
nonce := os.Getenv(git.ASKPASS_NONCE_ENV)
|
||||
if nonce == "" {
|
||||
errors.CheckError(fmt.Errorf("%s is not set", git.ASKPASS_NONCE_ENV))
|
||||
}
|
||||
conn, err := grpc_util.BlockingDial(context.Background(), "unix", askpass.SocketPath, nil, grpc.WithInsecure())
|
||||
errors.CheckError(err)
|
||||
defer io.Close(conn)
|
||||
client := askpass.NewAskPassServiceClient(conn)
|
||||
|
||||
creds, err := client.GetCredentials(context.Background(), &askpass.CredentialsRequest{Nonce: nonce})
|
||||
errors.CheckError(err)
|
||||
switch {
|
||||
case strings.HasPrefix(os.Args[1], "Username"):
|
||||
fmt.Println(creds.Username)
|
||||
case strings.HasPrefix(os.Args[1], "Password"):
|
||||
fmt.Println(creds.Password)
|
||||
default:
|
||||
errors.CheckError(fmt.Errorf("unknown credential type '%s'", os.Args[1]))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return &command
|
||||
}
|
||||
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
|
||||
}
|
||||
@@ -13,22 +13,24 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
||||
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"
|
||||
"github.com/argoproj/argo-cd/v2/reposerver/askpass"
|
||||
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 (
|
||||
@@ -61,16 +63,21 @@ func getPauseGenerationOnFailureForRequests() int {
|
||||
return env.ParseNumFromEnv(common.EnvPauseGenerationRequests, defaultPauseGenerationOnFailureForRequests, 0, math.MaxInt32)
|
||||
}
|
||||
|
||||
func getSubmoduleEnabled() bool {
|
||||
return env.ParseBoolFromEnv(common.EnvGitSubmoduleEnabled, true)
|
||||
}
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
var (
|
||||
parallelismLimit int64
|
||||
listenPort int
|
||||
metricsPort int
|
||||
cacheSrc func() (*reposervercache.Cache, error)
|
||||
tlsConfigCustomizer tls.ConfigCustomizer
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
redisClient *redis.Client
|
||||
disableTLS bool
|
||||
parallelismLimit int64
|
||||
listenPort int
|
||||
metricsPort int
|
||||
cacheSrc func() (*reposervercache.Cache, error)
|
||||
tlsConfigCustomizer tls.ConfigCustomizer
|
||||
tlsConfigCustomizerSrc func() (tls.ConfigCustomizer, error)
|
||||
redisClient *redis.Client
|
||||
disableTLS bool
|
||||
maxCombinedDirectoryManifestsSize string
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -90,14 +97,20 @@ func NewCommand() *cobra.Command {
|
||||
cache, err := cacheSrc()
|
||||
errors.CheckError(err)
|
||||
|
||||
maxCombinedDirectoryManifestsQuantity, err := resource.ParseQuantity(maxCombinedDirectoryManifestsSize)
|
||||
errors.CheckError(err)
|
||||
|
||||
askPassServer := askpass.NewServer()
|
||||
metricsServer := metrics.NewMetricsServer()
|
||||
cacheutil.CollectMetrics(redisClient, metricsServer)
|
||||
server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, repository.RepoServerInitConstants{
|
||||
ParallelismLimit: parallelismLimit,
|
||||
ParallelismLimit: parallelismLimit,
|
||||
PauseGenerationAfterFailedGenerationAttempts: getPauseGenerationAfterFailedGenerationAttempts(),
|
||||
PauseGenerationOnFailureForMinutes: getPauseGenerationOnFailureForMinutes(),
|
||||
PauseGenerationOnFailureForRequests: getPauseGenerationOnFailureForRequests(),
|
||||
})
|
||||
SubmoduleEnabled: getSubmoduleEnabled(),
|
||||
MaxCombinedDirectoryManifestsSize: maxCombinedDirectoryManifestsQuantity,
|
||||
}, askPassServer)
|
||||
errors.CheckError(err)
|
||||
|
||||
grpc := server.CreateGRPC()
|
||||
@@ -128,6 +141,7 @@ func NewCommand() *cobra.Command {
|
||||
})
|
||||
http.Handle("/metrics", metricsServer.GetHandler())
|
||||
go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf(":%d", metricsPort), nil)) }()
|
||||
go func() { errors.CheckError(askPassServer.Run(askpass.SocketPath)) }()
|
||||
|
||||
if gpg.IsGPGEnabled() {
|
||||
log.Infof("Initializing GnuPG keyring at %s", common.GetGnuPGHomePath())
|
||||
@@ -151,13 +165,16 @@ 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", false, "Disable TLS on the gRPC endpoint")
|
||||
command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_REPO_SERVER_DISABLE_TLS", false), "Disable TLS on the gRPC endpoint")
|
||||
command.Flags().StringVar(&maxCombinedDirectoryManifestsSize, "max-combined-directory-manifests-size", env.StringFromEnv("ARGOCD_REPO_SERVER_MAX_COMBINED_DIRECTORY_MANIFESTS_SIZE", "10M"), "Max combined size of manifest files in a directory-type Application")
|
||||
|
||||
tlsConfigCustomizerSrc = tls.AddTLSFlagsToCmd(&command)
|
||||
cacheSrc = reposervercache.AddCacheFlagsToCmd(&command, func(client *redis.Client) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package commands
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/pkg/stats"
|
||||
@@ -12,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 (
|
||||
@@ -51,7 +52,6 @@ func NewCommand() *cobra.Command {
|
||||
glogLevel int
|
||||
clientConfig clientcmd.ClientConfig
|
||||
repoServerTimeoutSeconds int
|
||||
staticAssetsDir string
|
||||
baseHRef string
|
||||
rootPath string
|
||||
repoServerAddress string
|
||||
@@ -63,6 +63,7 @@ func NewCommand() *cobra.Command {
|
||||
frameOptions string
|
||||
repoServerPlaintext bool
|
||||
repoServerStrictTLS bool
|
||||
staticAssetsDir string
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: cliName,
|
||||
@@ -127,7 +128,6 @@ func NewCommand() *cobra.Command {
|
||||
ListenPort: listenPort,
|
||||
MetricsPort: metricsPort,
|
||||
Namespace: namespace,
|
||||
StaticAssetsDir: staticAssetsDir,
|
||||
BaseHRef: baseHRef,
|
||||
RootPath: rootPath,
|
||||
KubeClientset: kubeclientset,
|
||||
@@ -140,6 +140,7 @@ func NewCommand() *cobra.Command {
|
||||
Cache: cache,
|
||||
XFrameOptions: frameOptions,
|
||||
RedisClient: redisClient,
|
||||
StaticAssetsDir: staticAssetsDir,
|
||||
}
|
||||
|
||||
stats.RegisterStackDumper()
|
||||
@@ -157,24 +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().BoolVar(&repoServerPlaintext, "repo-server-plaintext", false, "Use a plaintext client (non-TLS) to connect to repository server")
|
||||
command.Flags().BoolVar(&repoServerStrictTLS, "repo-server-strict-tls", false, "Perform strict validation of TLS certificates when connecting to repo server")
|
||||
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,237 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"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/tools/clientcmd"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/controller/sharding"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
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/db"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
kubeutil "github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/settings"
|
||||
)
|
||||
|
||||
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())
|
||||
|
||||
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)
|
||||
if replicas == 0 {
|
||||
controllerPods, err := kubeClient.CoreV1().Pods(namespace).List(context.Background(), v1.ListOptions{
|
||||
LabelSelector: "app.kubernetes.io/name=argocd-application-controller"})
|
||||
errors.CheckError(err)
|
||||
replicas = len(controllerPods.Items)
|
||||
}
|
||||
|
||||
settingsMgr := settings.NewSettingsManager(context.Background(), kubeClient, namespace)
|
||||
|
||||
argoDB := db.NewDB(namespace, settingsMgr, kubeClient)
|
||||
clusters, err := argoDB.ListClusters(context.Background())
|
||||
errors.CheckError(err)
|
||||
var cache *appstatecache.Cache
|
||||
if portForwardRedis {
|
||||
port, err := kubeutil.PortForward("app.kubernetes.io/name=argocd-redis-ha-haproxy", 6379, namespace)
|
||||
errors.CheckError(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()
|
||||
errors.CheckError(err)
|
||||
}
|
||||
|
||||
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
||||
_, _ = fmt.Fprintf(w, "SERVER\tSHARD\tCONNECTION\tAPPS COUNT\tRESOURCES COUNT\n")
|
||||
|
||||
for _, cluster := range clusters.Items {
|
||||
clusterShard := 0
|
||||
if replicas > 0 {
|
||||
clusterShard = sharding.GetShardByID(cluster.ID, replicas)
|
||||
}
|
||||
|
||||
if shard != -1 && clusterShard != shard {
|
||||
continue
|
||||
}
|
||||
|
||||
var info argoappv1.ClusterInfo
|
||||
_ = cache.GetClusterInfo(cluster.Server, &info)
|
||||
_, _ = fmt.Fprintf(w, "%s\t%d\t%s\t%d\t%d\n", cluster.Server, clusterShard, info.ConnectionState.Status, info.ApplicationsCount, 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-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 NewGenClusterConfigCommand(pathOpts *clientcmd.PathOptions) *cobra.Command {
|
||||
var (
|
||||
clusterOpts cmdutil.ClusterOptions
|
||||
bearerToken string
|
||||
outputFormat 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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/undefinedlabs/go-mpatch"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/discovery"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
func TestProjectAllowListGen(t *testing.T) {
|
||||
useMock := true
|
||||
rules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
overrides := &clientcmd.ConfigOverrides{}
|
||||
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, overrides)
|
||||
|
||||
if useMock {
|
||||
var patchClientConfig *mpatch.Patch
|
||||
patchClientConfig, err := mpatch.PatchInstanceMethodByName(reflect.TypeOf(clientConfig), "ClientConfig", func(*clientcmd.DeferredLoadingClientConfig) (*restclient.Config, error) {
|
||||
return nil, nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
patch, err := mpatch.PatchMethod(discovery.NewDiscoveryClientForConfig, func(c *restclient.Config) (*discovery.DiscoveryClient, error) {
|
||||
return &discovery.DiscoveryClient{LegacyPrefix: "/api"}, nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
var patchSeverPreferedResources *mpatch.Patch
|
||||
discoClient := &discovery.DiscoveryClient{}
|
||||
patchSeverPreferedResources, err = mpatch.PatchInstanceMethodByName(reflect.TypeOf(discoClient), "ServerPreferredResources", func(*discovery.DiscoveryClient) ([]*metav1.APIResourceList, error) {
|
||||
res := metav1.APIResource{
|
||||
Name: "services",
|
||||
Kind: "Service",
|
||||
}
|
||||
resourceList := []*metav1.APIResourceList{{APIResources: []metav1.APIResource{res}}}
|
||||
return resourceList, nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
err = patchClientConfig.Unpatch()
|
||||
assert.NoError(t, err)
|
||||
err = patch.Unpatch()
|
||||
assert.NoError(t, err)
|
||||
err = patchSeverPreferedResources.Unpatch()
|
||||
err = patch.Unpatch()
|
||||
}()
|
||||
}
|
||||
|
||||
globalProj := generateProjectAllowList(clientConfig, "testdata/test_clusterrole.yaml", "testproj")
|
||||
assert.True(t, len(globalProj.Spec.NamespaceResourceWhitelist) > 0)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
@@ -13,44 +13,40 @@ import (
|
||||
"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/errors"
|
||||
"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/util/errors"
|
||||
"github.com/argoproj/argo-cd/v2/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"}
|
||||
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"}
|
||||
)
|
||||
|
||||
// NewCommand returns a new instance of an argocd command
|
||||
func NewCommand() *cobra.Command {
|
||||
// NewAdminCommand returns a new instance of an argocd command
|
||||
func NewAdminCommand() *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",
|
||||
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(cli.NewVersionCmd(cliName))
|
||||
command.AddCommand(NewClusterCommand(pathOpts))
|
||||
command.AddCommand(NewProjectsCommand())
|
||||
command.AddCommand(NewSettingsCommand())
|
||||
@@ -58,6 +54,8 @@ func NewCommand() *cobra.Command {
|
||||
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")
|
||||
@@ -65,20 +63,22 @@ func NewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
type argoCDClientsets struct {
|
||||
configMaps dynamic.ResourceInterface
|
||||
secrets dynamic.ResourceInterface
|
||||
applications dynamic.ResourceInterface
|
||||
projects dynamic.ResourceInterface
|
||||
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),
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/argoproj/argo-cd/v2/util/argo"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/spf13/cobra"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
@@ -20,23 +22,23 @@ import (
|
||||
kubecache "k8s.io/client-go/tools/cache"
|
||||
"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/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"
|
||||
appstatecache "github.com/argoproj/argo-cd/util/cache/appstate"
|
||||
"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 NewAppCommand() *cobra.Command {
|
||||
@@ -62,47 +64,56 @@ func NewGenAppSpecCommand() *cobra.Command {
|
||||
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-util 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
|
||||
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-util 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
|
||||
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-util 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
|
||||
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-util app generate-spec 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 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-util 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
|
||||
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-util 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
|
||||
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) {
|
||||
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 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)
|
||||
}
|
||||
|
||||
var printResources []interface{}
|
||||
printResources = append(printResources, app)
|
||||
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
|
||||
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"})
|
||||
@@ -237,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()
|
||||
@@ -247,7 +258,8 @@ 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)
|
||||
}
|
||||
@@ -322,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()
|
||||
@@ -343,7 +355,7 @@ func reconcileApplications(
|
||||
return true
|
||||
}, func(r *http.Request) error {
|
||||
return nil
|
||||
})
|
||||
}, []string{})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -359,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 {
|
||||
@@ -389,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,
|
||||
@@ -401,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",
|
||||
},
|
||||
},
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/argoproj/gitops-engine/pkg/utils/kube"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/sirupsen/logrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -18,9 +18,9 @@ import (
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
"github.com/argoproj/argo-cd/util/cli"
|
||||
"github.com/argoproj/argo-cd/util/errors"
|
||||
"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.
|
||||
@@ -86,6 +86,19 @@ func NewExportCommand() *cobra.Command {
|
||||
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)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -98,10 +111,11 @@ func NewExportCommand() *cobra.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
|
||||
clientConfig clientcmd.ClientConfig
|
||||
prune bool
|
||||
dryRun bool
|
||||
verbose bool
|
||||
stopOperation bool
|
||||
)
|
||||
var command = cobra.Command{
|
||||
Use: "import SOURCE",
|
||||
@@ -167,6 +181,17 @@ func NewImportCommand() *cobra.Command {
|
||||
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)
|
||||
@@ -186,24 +211,43 @@ func NewImportCommand() *cobra.Command {
|
||||
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{})
|
||||
errors.CheckError(err)
|
||||
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)
|
||||
}
|
||||
}
|
||||
fmt.Printf("%s/%s %s created%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
} else if specsEqual(*bakObj, liveObj) {
|
||||
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)
|
||||
newLive := updateLive(bakObj, &liveObj, stopOperation)
|
||||
_, err = dynClient.Update(context.Background(), newLive, v1.UpdateOptions{})
|
||||
errors.CheckError(err)
|
||||
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)
|
||||
}
|
||||
fmt.Printf("%s/%s %s updated%s\n", gvk.Group, gvk.Kind, bakObj.GetName(), dryRunMsg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,16 +272,24 @@ func NewImportCommand() *cobra.Command {
|
||||
}
|
||||
}
|
||||
}
|
||||
case "ApplicationSet":
|
||||
dynClient = acdClients.applicationSets
|
||||
default:
|
||||
logrus.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
|
||||
log.Fatalf("Unexpected kind '%s' in prune list", key.Kind)
|
||||
}
|
||||
isForbidden := false
|
||||
if !dryRun {
|
||||
err = dynClient.Delete(context.Background(), key.Name, v1.DeleteOptions{})
|
||||
if err != nil && !apierr.IsNotFound(err) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
fmt.Printf("%s/%s %s pruned%s\n", key.Group, key.Kind, key.Name, dryRunMsg)
|
||||
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)
|
||||
}
|
||||
@@ -249,10 +301,23 @@ func NewImportCommand() *cobra.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()
|
||||
@@ -278,7 +343,7 @@ func export(w io.Writer, un unstructured.Unstructured) {
|
||||
|
||||
// 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 {
|
||||
func updateLive(bak, live *unstructured.Unstructured, stopOperation bool) *unstructured.Unstructured {
|
||||
newLive := live.DeepCopy()
|
||||
newLive.SetAnnotations(bak.GetAnnotations())
|
||||
newLive.SetLabels(bak.GetLabels())
|
||||
@@ -293,6 +358,12 @@ func updateLive(bak, live *unstructured.Unstructured) *unstructured.Unstructured
|
||||
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,12 +7,13 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
cmdutil "github.com/argoproj/argo-cd/cmd/util"
|
||||
"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"
|
||||
@@ -35,12 +36,13 @@ func NewProjectsCommand() *cobra.Command {
|
||||
return command
|
||||
}
|
||||
|
||||
// NewGenProjectConfigCommand generates declarative configuration file for given project
|
||||
// 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",
|
||||
@@ -49,13 +51,16 @@ func NewGenProjectSpecCommand() *cobra.Command {
|
||||
proj, err := cmdutil.ConstructAppProj(fileURL, args, opts, c)
|
||||
errors.CheckError(err)
|
||||
|
||||
var printResources []interface{}
|
||||
printResources = append(printResources, proj)
|
||||
errors.CheckError(cmdutil.PrintResources(printResources, outputFormat))
|
||||
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"})
|
||||
@@ -138,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,7 +1,8 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -17,10 +18,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"
|
||||
@@ -63,7 +64,10 @@ func NewProjectAllowListGenCommand() *cobra.Command {
|
||||
}()
|
||||
}
|
||||
|
||||
globalProj := generateProjectAllowList(clientConfig, clusterRoleFileName, projName)
|
||||
resourceList, err := getResourceList(clientConfig)
|
||||
errors.CheckError(err)
|
||||
globalProj, err := generateProjectAllowList(resourceList, clusterRoleFileName, projName)
|
||||
errors.CheckError(err)
|
||||
|
||||
yamlBytes, err := yaml.Marshal(globalProj)
|
||||
errors.CheckError(err)
|
||||
@@ -78,23 +82,38 @@ func NewProjectAllowListGenCommand() *cobra.Command {
|
||||
return command
|
||||
}
|
||||
|
||||
func generateProjectAllowList(clientConfig clientcmd.ClientConfig, clusterRoleFileName string, projName string) v1alpha1.AppProject {
|
||||
func getResourceList(clientConfig clientcmd.ClientConfig) ([]*metav1.APIResourceList, error) {
|
||||
config, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while creating client config: %s", err)
|
||||
}
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while creating discovery client: %s", err)
|
||||
}
|
||||
serverResources, err := disco.ServerPreferredResources()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while getting server resources: %s", err)
|
||||
}
|
||||
return serverResources, nil
|
||||
}
|
||||
|
||||
func generateProjectAllowList(serverResources []*metav1.APIResourceList, clusterRoleFileName string, projName string) (*v1alpha1.AppProject, error) {
|
||||
yamlBytes, err := ioutil.ReadFile(clusterRoleFileName)
|
||||
errors.CheckError(err)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading cluster role file: %s", err)
|
||||
}
|
||||
var obj unstructured.Unstructured
|
||||
err = yaml.Unmarshal(yamlBytes, &obj)
|
||||
errors.CheckError(err)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error unmarshalling cluster role file yaml: %s", err)
|
||||
}
|
||||
|
||||
clusterRole := &rbacv1.ClusterRole{}
|
||||
err = scheme.Scheme.Convert(&obj, clusterRole, nil)
|
||||
errors.CheckError(err)
|
||||
|
||||
config, err := clientConfig.ClientConfig()
|
||||
errors.CheckError(err)
|
||||
disco, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
errors.CheckError(err)
|
||||
serverResources, err := disco.ServerPreferredResources()
|
||||
errors.CheckError(err)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting cluster role yaml into ClusterRole struct: %s", err)
|
||||
}
|
||||
|
||||
resourceList := make([]metav1.GroupKind, 0)
|
||||
for _, rule := range clusterRole.Rules {
|
||||
@@ -140,5 +159,5 @@ func generateProjectAllowList(clientConfig clientcmd.ClientConfig, clusterRoleFi
|
||||
Spec: v1alpha1.AppProjectSpec{},
|
||||
}
|
||||
globalProj.Spec.NamespaceResourceWhitelist = resourceList
|
||||
return globalProj
|
||||
return &globalProj, nil
|
||||
}
|
||||
20
cmd/argocd/commands/admin/project_allowlist_test.go
Normal file
20
cmd/argocd/commands/admin/project_allowlist_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestProjectAllowListGen(t *testing.T) {
|
||||
res := metav1.APIResource{
|
||||
Name: "services",
|
||||
Kind: "Service",
|
||||
}
|
||||
resourceList := []*metav1.APIResourceList{{APIResources: []metav1.APIResource{res}}}
|
||||
|
||||
globalProj, err := generateProjectAllowList(resourceList, "testdata/test_clusterrole.yaml", "testproj")
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, len(globalProj.Spec.NamespaceResourceWhitelist) > 0)
|
||||
}
|
||||
@@ -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 (
|
||||
@@ -1,4 +1,4 @@
|
||||
package commands
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -9,17 +9,16 @@ import (
|
||||
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"
|
||||
|
||||
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/git"
|
||||
"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/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 (
|
||||
@@ -49,25 +48,25 @@ func NewGenRepoSpecCommand() *cobra.Command {
|
||||
// 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 repo generate-spec git@git.example.com:repos/repo --insecure-ignore-host-key --ssh-private-key-path ~/id_rsa
|
||||
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-util repo generate-spec ssh://git@git.example.com:2222/repos/repo --ssh-private-key-path ~/id_rsa
|
||||
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-util 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
|
||||
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-util repo generate-spec https://git.example.com/repos/repo --username git --password secret --insecure-skip-server-verification
|
||||
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-util repo generate-spec https://kubernetes-charts.storage.googleapis.com --type helm --name stable
|
||||
argocd admin repo generate-spec https://charts.helm.sh/stable --type helm --name stable
|
||||
|
||||
# Add a private Helm repository named 'stable' via HTTPS
|
||||
argocd-util repo generate-spec https://kubernetes-charts.storage.googleapis.com --type helm --name stable --username test --password test
|
||||
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-util repo generate-spec helm-oci-registry.cn-zhangjiakou.cr.aliyuncs.com --type helm --name stable --enable-oci --username test --password test
|
||||
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{
|
||||
@@ -155,25 +154,13 @@ func NewGenRepoSpecCommand() *cobra.Command {
|
||||
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))
|
||||
errors.CheckError(PrintResources(outputFormat, os.Stdout, secret))
|
||||
},
|
||||
}
|
||||
command.Flags().StringVarP(&outputFormat, "output", "o", "yaml", "Output format. One of: json|yaml")
|
||||
@@ -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 {
|
||||
@@ -303,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)
|
||||
@@ -392,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)
|
||||
@@ -401,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)
|
||||
|
||||
@@ -436,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)
|
||||
@@ -467,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)
|
||||
@@ -510,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"
|
||||
@@ -25,33 +26,33 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"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 +94,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 +104,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 +125,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 +136,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 +283,8 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
sinceSeconds int64
|
||||
untilTime string
|
||||
filter string
|
||||
container string
|
||||
previous bool
|
||||
)
|
||||
var command = &cobra.Command{
|
||||
Use: "logs APPNAME",
|
||||
@@ -304,6 +313,8 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
SinceSeconds: sinceSeconds,
|
||||
UntilTime: &untilTime,
|
||||
Filter: &filter,
|
||||
Container: container,
|
||||
Previous: previous,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("failed to get pod logs: %v", err)
|
||||
@@ -344,6 +355,8 @@ 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")
|
||||
command.Flags().BoolVarP(&previous, "previous", "p", false, "Specify if the previously terminated container logs should be returned")
|
||||
|
||||
return command
|
||||
}
|
||||
@@ -549,15 +562,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
|
||||
pluginEnvs []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",
|
||||
@@ -634,7 +648,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)
|
||||
}
|
||||
@@ -662,6 +676,14 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
}
|
||||
}
|
||||
}
|
||||
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 {
|
||||
@@ -671,9 +693,10 @@ func NewApplicationUnsetCommand(clientOpts *argocdclient.ClientOptions) *cobra.C
|
||||
}
|
||||
for _, env := range pluginEnvs {
|
||||
err = app.Spec.Source.Plugin.RemoveEnvEntry(env)
|
||||
errors.CheckError(err)
|
||||
if err == nil {
|
||||
updated = true
|
||||
}
|
||||
}
|
||||
updated = true
|
||||
}
|
||||
|
||||
if !updated {
|
||||
@@ -692,6 +715,7 @@ 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")
|
||||
@@ -726,9 +750,9 @@ func liveObjects(resources []*argoappv1.ResourceDiff) ([]*unstructured.Unstructu
|
||||
return objs, nil
|
||||
}
|
||||
|
||||
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)
|
||||
func getLocalObjects(app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
configManagementPlugins []*argoappv1.ConfigManagementPlugin, trackingMethod string) []*unstructured.Unstructured {
|
||||
manifestStrings := getLocalObjectsString(app, local, localRepoRoot, appLabelKey, kubeVersion, apiVersions, kustomizeOptions, configManagementPlugins, trackingMethod)
|
||||
objs := make([]*unstructured.Unstructured, len(manifestStrings))
|
||||
for i := range manifestStrings {
|
||||
obj := unstructured.Unstructured{}
|
||||
@@ -739,10 +763,10 @@ func getLocalObjects(app *argoappv1.Application, local, localRepoRoot, appLabelK
|
||||
return objs
|
||||
}
|
||||
|
||||
func getLocalObjectsString(app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
configManagementPlugins []*argoappv1.ConfigManagementPlugin) []string {
|
||||
func getLocalObjectsString(app *argoappv1.Application, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions,
|
||||
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,
|
||||
@@ -750,8 +774,10 @@ func getLocalObjectsString(app *argoappv1.Application, local, localRepoRoot, app
|
||||
ApplicationSource: &app.Spec.Source,
|
||||
KustomizeOptions: kustomizeOptions,
|
||||
KubeVersion: kubeVersion,
|
||||
ApiVersions: apiVersions,
|
||||
Plugins: configManagementPlugins,
|
||||
}, true)
|
||||
TrackingMethod: trackingMethod,
|
||||
}, true, &git.NoopCredsStore{}, resource.MustParse("0"))
|
||||
errors.CheckError(err)
|
||||
|
||||
return res.Manifests
|
||||
@@ -798,6 +824,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
var (
|
||||
refresh bool
|
||||
hardRefresh bool
|
||||
exitCode bool
|
||||
local string
|
||||
revision string
|
||||
localRepoRoot string
|
||||
@@ -835,7 +862,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.Info.ServerVersion, cluster.Info.APIVersions, 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
|
||||
@@ -877,10 +904,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 {
|
||||
@@ -901,7 +936,7 @@ func NewApplicationDiffCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
_ = cli.PrintDiff(item.key.Name, live, target)
|
||||
}
|
||||
}
|
||||
if foundDiffs {
|
||||
if foundDiffs && exitCode {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -909,6 +944,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")
|
||||
@@ -916,6 +952,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)
|
||||
@@ -929,7 +966,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)
|
||||
}
|
||||
|
||||
@@ -1262,6 +1299,7 @@ func NewApplicationSyncCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
|
||||
timeout uint
|
||||
strategy string
|
||||
force bool
|
||||
replace bool
|
||||
async bool
|
||||
retryLimit int64
|
||||
retryBackoffDuration time.Duration
|
||||
@@ -1369,18 +1407,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.Info.ServerVersion, cluster.Info.APIVersions, 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{}}
|
||||
@@ -1432,11 +1487,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")
|
||||
@@ -1807,6 +1863,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 (
|
||||
@@ -1814,36 +1887,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)
|
||||
@@ -2181,3 +2251,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,
|
||||
@@ -188,7 +200,10 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
|
||||
// completionChan is to signal flow completed. Non-empty string indicates error
|
||||
completionChan := make(chan string)
|
||||
// stateNonce is an OAuth2 state nonce
|
||||
stateNonce := rand.RandString(10)
|
||||
// According to the spec (https://www.rfc-editor.org/rfc/rfc6749#section-10.10), this must be guessable with
|
||||
// probability <= 2^(-128). The following call generates one of 52^24 random strings, ~= 2^136 possibilities.
|
||||
stateNonce, err := rand.String(24)
|
||||
errors.CheckError(err)
|
||||
var tokenString string
|
||||
var refreshToken string
|
||||
|
||||
@@ -198,7 +213,8 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
|
||||
}
|
||||
|
||||
// PKCE implementation of https://tools.ietf.org/html/rfc7636
|
||||
codeVerifier := rand.RandStringCharset(43, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
|
||||
codeVerifier, err := rand.StringFromCharset(43, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
|
||||
errors.CheckError(err)
|
||||
codeChallengeHash := sha256.Sum256([]byte(codeVerifier))
|
||||
codeChallenge := base64.RawURLEncoding.EncodeToString(codeChallengeHash[:])
|
||||
|
||||
@@ -282,7 +298,8 @@ func oauth2Login(ctx context.Context, port int, oidcSettings *settingspkg.OIDCCo
|
||||
opts = append(opts, oauth2.SetAuthURLParam("code_challenge_method", "S256"))
|
||||
url = oauth2conf.AuthCodeURL(stateNonce, opts...)
|
||||
case oidcutil.GrantTypeImplicit:
|
||||
url = oidcutil.ImplicitFlowURL(oauth2conf, stateNonce, opts...)
|
||||
url, err = oidcutil.ImplicitFlowURL(oauth2conf, stateNonce, opts...)
|
||||
errors.CheckError(err)
|
||||
default:
|
||||
log.Fatalf("Unsupported grant type: %v", grantType)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -17,15 +17,15 @@ import (
|
||||
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 {
|
||||
@@ -219,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 {
|
||||
@@ -228,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)
|
||||
|
||||
@@ -237,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
|
||||
|
||||
38
cmd/main.go
38
cmd/main.go
@@ -7,12 +7,14 @@ 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"
|
||||
gitaskpass "github.com/argoproj/argo-cd/v2/cmd/argocd-git-ask-pass/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 +31,22 @@ 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()
|
||||
case "argocd-git-ask-pass":
|
||||
command = gitaskpass.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 {
|
||||
|
||||
338
cmd/util/app.go
338
cmd/util/app.go
@@ -9,65 +9,71 @@ import (
|
||||
"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
|
||||
retryLimit int64
|
||||
retryBackoffDuration time.Duration
|
||||
retryBackoffMaxDuration time.Duration
|
||||
retryBackoffFactor int64
|
||||
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) {
|
||||
@@ -76,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`")
|
||||
@@ -109,16 +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, "retry-limit", 0, "Max number of allowed sync retries")
|
||||
command.Flags().DurationVar(&opts.retryBackoffDuration, "retry-backoff-duration", common.DefaultSyncRetryDuration, "Retry backoff base duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().DurationVar(&opts.retryBackoffMaxDuration, "retry-backoff-max-duration", common.DefaultSyncRetryMaxDuration, "Max retry backoff duration. Input needs to be a duration (e.g. 2m, 1h)")
|
||||
command.Flags().Int64Var(&opts.retryBackoffFactor, "retry-backoff-factor", common.DefaultSyncRetryFactor, "Factor multiplies the base duration after each failed retry")
|
||||
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 {
|
||||
@@ -137,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
|
||||
|
||||
@@ -153,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
|
||||
@@ -203,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":
|
||||
@@ -248,7 +272,7 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
if spec.SyncPolicy.IsZero() {
|
||||
spec.SyncPolicy = nil
|
||||
}
|
||||
case "retry-limit":
|
||||
case "sync-retry-limit":
|
||||
if appOpts.retryLimit > 0 {
|
||||
if spec.SyncPolicy == nil {
|
||||
spec.SyncPolicy = &argoappv1.SyncPolicy{}
|
||||
@@ -268,7 +292,7 @@ func SetAppSpecOptions(flags *pflag.FlagSet, spec *argoappv1.ApplicationSpec, ap
|
||||
spec.SyncPolicy.Retry = nil
|
||||
}
|
||||
} else {
|
||||
log.Fatalf("Invalid retry-limit [%d]", appOpts.retryLimit)
|
||||
log.Fatalf("Invalid sync-retry-limit [%d]", appOpts.retryLimit)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -307,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) {
|
||||
@@ -334,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))
|
||||
}
|
||||
@@ -357,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) {
|
||||
@@ -373,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
|
||||
}
|
||||
@@ -382,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 {
|
||||
@@ -503,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)
|
||||
}
|
||||
@@ -546,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) {
|
||||
@@ -588,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) {
|
||||
@@ -165,10 +184,131 @@ func Test_setAppSpecOptions(t *testing.T) {
|
||||
assert.Nil(t, f.spec.SyncPolicy)
|
||||
})
|
||||
t.Run("RetryLimit", func(t *testing.T) {
|
||||
assert.NoError(t, f.SetFlag("retry-limit", "5"))
|
||||
assert.NoError(t, f.SetFlag("sync-retry-limit", "5"))
|
||||
assert.True(t, f.spec.SyncPolicy.Retry.Limit == 5)
|
||||
|
||||
assert.NoError(t, f.SetFlag("retry-limit", "0"))
|
||||
assert.Nil(t, f.spec.SyncPolicy)
|
||||
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,85 +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
|
||||
}
|
||||
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.Println(string(jsonBytes))
|
||||
case "yaml":
|
||||
yamlBytes, err := yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// marshaled YAML already ends with the new line character
|
||||
fmt.Print(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{}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
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 {
|
||||
|
||||
@@ -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