Compare commits
574 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53ad742e2d | ||
|
|
c22d8f28f9 | ||
|
|
4c905310c0 | ||
|
|
4cb756bfb0 | ||
|
|
b0629d74b1 | ||
|
|
cdcf332491 | ||
|
|
7b56fffcc9 | ||
|
|
04cc1278fb | ||
|
|
b0c697d2ac | ||
|
|
2b653f9cfa | ||
|
|
b4f5d121c8 | ||
|
|
afcf12194d | ||
|
|
437a943677 | ||
|
|
bed6efa671 | ||
|
|
7cf04515d5 | ||
|
|
eeaffd5c0e | ||
|
|
43c9cfc52f | ||
|
|
03b76aa0d1 | ||
|
|
8ce8b1b690 | ||
|
|
b742799307 | ||
|
|
3abc829250 | ||
|
|
92486b70be | ||
|
|
1e37cbeae7 | ||
|
|
0e7dedc728 | ||
|
|
2649bfb3a3 | ||
|
|
e901e8b6f8 | ||
|
|
b28b5dd9b9 | ||
|
|
6135c326d8 | ||
|
|
05c13fe35a | ||
|
|
8e3a809e91 | ||
|
|
2289c6cc60 | ||
|
|
b6447800a7 | ||
|
|
e4747b55c7 | ||
|
|
3c06ebcda2 | ||
|
|
5685afb9d5 | ||
|
|
3da7e1ce2d | ||
|
|
fd163ddcf2 | ||
|
|
62ac960273 | ||
|
|
daa25a9a30 | ||
|
|
f20fe76168 | ||
|
|
b596aefb91 | ||
|
|
c5b5263b1b | ||
|
|
765ebbacfe | ||
|
|
2fb132bdc5 | ||
|
|
80efd29ec0 | ||
|
|
4cb89adec4 | ||
|
|
aca461912c | ||
|
|
e645402ced | ||
|
|
e184ef2618 | ||
|
|
0fcda45659 | ||
|
|
c5dd6a18fa | ||
|
|
3c8072d0e6 | ||
|
|
cbccb8c46a | ||
|
|
e73d1acb20 | ||
|
|
1b239fbc82 | ||
|
|
0ad4db9f82 | ||
|
|
659ce798c9 | ||
|
|
6dd0cbfadd | ||
|
|
e7c015d24c | ||
|
|
dcbca6b1bf | ||
|
|
28971edc07 | ||
|
|
9cd4a7215f | ||
|
|
2f15ad972b | ||
|
|
10cddb457f | ||
|
|
7771107e4d | ||
|
|
5879d8575a | ||
|
|
80810bdc0f | ||
|
|
e3605acc13 | ||
|
|
cbce9b5e26 | ||
|
|
1ff76bd4a5 | ||
|
|
920fea7d1b | ||
|
|
adc04651cf | ||
|
|
0eba9662e6 | ||
|
|
7949135a74 | ||
|
|
025fe9ced1 | ||
|
|
5f1fe306bb | ||
|
|
f137263fe9 | ||
|
|
ba1180ad4d | ||
|
|
e3f6f0f8b3 | ||
|
|
f5d3283cff | ||
|
|
3fdb292381 | ||
|
|
22134b1233 | ||
|
|
ce3e264c7d | ||
|
|
5b22207c98 | ||
|
|
3278a0782f | ||
|
|
43c2359705 | ||
|
|
a2a3b4f00f | ||
|
|
3c495861a1 | ||
|
|
b67a46371b | ||
|
|
16d352b15b | ||
|
|
ac95ac0653 | ||
|
|
840e717b72 | ||
|
|
ae0c3c8020 | ||
|
|
b46e298b4d | ||
|
|
e9d4b42b12 | ||
|
|
771b7ab98a | ||
|
|
a51ede681f | ||
|
|
be03ca3b5d | ||
|
|
08cb3858ed | ||
|
|
39d4270f32 | ||
|
|
42372d9ef6 | ||
|
|
e9aa53d2ed | ||
|
|
8f7ed74913 | ||
|
|
2c090dbdf6 | ||
|
|
084fb6318d | ||
|
|
1daa7a8aa4 | ||
|
|
cf78e029aa | ||
|
|
a337a7ec73 | ||
|
|
630ca340dd | ||
|
|
e12839567e | ||
|
|
524a729f5d | ||
|
|
d3eacd4c20 | ||
|
|
f9e7190a04 | ||
|
|
15a63e3f2e | ||
|
|
1cba3e4476 | ||
|
|
5c567a1ea8 | ||
|
|
0c47d738a9 | ||
|
|
ebace983b9 | ||
|
|
1654791feb | ||
|
|
c20a57f1df | ||
|
|
1fc463c065 | ||
|
|
904226fcf3 | ||
|
|
c721474e0b | ||
|
|
1902f4af0d | ||
|
|
bc90e7cf01 | ||
|
|
0d18dc128a | ||
|
|
8cd7d8c5d2 | ||
|
|
9b1e160798 | ||
|
|
ab18cfe1a2 | ||
|
|
037562bf7b | ||
|
|
2d1937a5c8 | ||
|
|
c5723821da | ||
|
|
dbb9ea303d | ||
|
|
47c8e5f44d | ||
|
|
aff3365750 | ||
|
|
d61efd1e2c | ||
|
|
5d1cadcd5e | ||
|
|
ffe5657367 | ||
|
|
a9a67a27e7 | ||
|
|
c2e526ca57 | ||
|
|
7141271bae | ||
|
|
3b637a5125 | ||
|
|
cdc1c98a11 | ||
|
|
ab94c8064c | ||
|
|
e63ddb99de | ||
|
|
7c1b897919 | ||
|
|
f6b0c6e7ef | ||
|
|
1730f3c46b | ||
|
|
a814487d4b | ||
|
|
cd90bc2c92 | ||
|
|
709b17ce59 | ||
|
|
39fa7d5502 | ||
|
|
29f77d2cb3 | ||
|
|
22cf986a79 | ||
|
|
d80b697fbd | ||
|
|
8b9aaad20a | ||
|
|
4080d5807a | ||
|
|
7c2072eed1 | ||
|
|
d21e779b9f | ||
|
|
f7e5223533 | ||
|
|
525bd21cd1 | ||
|
|
8df27d8c3a | ||
|
|
6464d6c7b4 | ||
|
|
2fc9d73c5f | ||
|
|
b32051df53 | ||
|
|
bf36a29ca2 | ||
|
|
a2ac94b625 | ||
|
|
c81afa6993 | ||
|
|
4fa93ec4d6 | ||
|
|
00c6ac81b9 | ||
|
|
8801031f06 | ||
|
|
2a033215a4 | ||
|
|
8214fefde6 | ||
|
|
4cdb75b74d | ||
|
|
eac82585ad | ||
|
|
2c76c70205 | ||
|
|
a9e09b856f | ||
|
|
c03a0b7f87 | ||
|
|
0ab8740832 | ||
|
|
aa1eae22c7 | ||
|
|
4f3b34f86b | ||
|
|
8435cb8df9 | ||
|
|
0d457d6d11 | ||
|
|
484015ceea | ||
|
|
1b5c4245df | ||
|
|
ce68a06436 | ||
|
|
7273059cb9 | ||
|
|
a03ea8ace3 | ||
|
|
d6cbfa39f8 | ||
|
|
a2e4cbbfe2 | ||
|
|
23518953d0 | ||
|
|
2716ca449e | ||
|
|
ecb1ad6ca5 | ||
|
|
3fa7af12e0 | ||
|
|
12efb1967e | ||
|
|
56b1e80758 | ||
|
|
baf874ea67 | ||
|
|
28262f59d3 | ||
|
|
44d69d6fc0 | ||
|
|
4d76ff4e6a | ||
|
|
1eaf259e52 | ||
|
|
bca1fa0968 | ||
|
|
bd79884d84 | ||
|
|
3b42b200d3 | ||
|
|
dad4a20fa7 | ||
|
|
90d95988aa | ||
|
|
e88577fe52 | ||
|
|
6fa495b843 | ||
|
|
3311bfd3ca | ||
|
|
cfd4d285da | ||
|
|
c751bf6bdb | ||
|
|
6f94844a35 | ||
|
|
f74d097837 | ||
|
|
0a58b0cdad | ||
|
|
6f94ec728f | ||
|
|
e3747209eb | ||
|
|
36b39a50a4 | ||
|
|
d9c7ff8685 | ||
|
|
625d865625 | ||
|
|
00c6bd0240 | ||
|
|
506da2466b | ||
|
|
9ea0a535ea | ||
|
|
25d2a3cdf1 | ||
|
|
9fa59df798 | ||
|
|
93c0467f91 | ||
|
|
c377fe7651 | ||
|
|
9ed24522bb | ||
|
|
ddcabbf95d | ||
|
|
5c58b45340 | ||
|
|
5690b639cd | ||
|
|
22d0ff8173 | ||
|
|
8e61fe805c | ||
|
|
5356436c94 | ||
|
|
8ca8b92f92 | ||
|
|
7c98f20e11 | ||
|
|
1a8798a5d4 | ||
|
|
926842a216 | ||
|
|
9e11b860ec | ||
|
|
5abf1ee817 | ||
|
|
4e78e80619 | ||
|
|
33be9840f0 | ||
|
|
33fdaee399 | ||
|
|
91660a98d5 | ||
|
|
8d5c4492d8 | ||
|
|
29ad52bb46 | ||
|
|
590b7b7682 | ||
|
|
ea06d9614f | ||
|
|
41ba55634a | ||
|
|
f09616e780 | ||
|
|
a4d7e35cdd | ||
|
|
346e0ea734 | ||
|
|
ce854236cf | ||
|
|
1588663358 | ||
|
|
68fdc0a2b6 | ||
|
|
d335f8f981 | ||
|
|
b4efd15afd | ||
|
|
0e6f9da761 | ||
|
|
02b34f05c8 | ||
|
|
f26800eb85 | ||
|
|
d2cc01169b | ||
|
|
d8924cd8a9 | ||
|
|
25af5d2968 | ||
|
|
3580d4ff85 | ||
|
|
42607aadc3 | ||
|
|
2fe86a4cde | ||
|
|
db0256e0f7 | ||
|
|
224e808c21 | ||
|
|
6ed6b937f8 | ||
|
|
99b940f56f | ||
|
|
66a417a3ee | ||
|
|
761762bdc0 | ||
|
|
bd8ada9e4a | ||
|
|
ba5c7e4fc3 | ||
|
|
903284fe59 | ||
|
|
cf7ee0081c | ||
|
|
e6a1d8156b | ||
|
|
b850f51ef5 | ||
|
|
4e57de4776 | ||
|
|
0a2945e7f1 | ||
|
|
4f9beae49d | ||
|
|
15a49334d8 | ||
|
|
cf5e7c39e0 | ||
|
|
173ee5fcdc | ||
|
|
67968dff7e | ||
|
|
c596c70d42 | ||
|
|
d30c3aef32 | ||
|
|
f7e5101753 | ||
|
|
597b13d1b3 | ||
|
|
2b8385a874 | ||
|
|
ddcc301ab6 | ||
|
|
ab983c678f | ||
|
|
79dfbec910 | ||
|
|
6bf439d4ce | ||
|
|
063c5ee328 | ||
|
|
19137b8e8c | ||
|
|
95aa30ad41 | ||
|
|
55aa96a33a | ||
|
|
5460348450 | ||
|
|
0d1a68ecb4 | ||
|
|
98466220c7 | ||
|
|
6d2dd076dc | ||
|
|
5e07bcb79a | ||
|
|
9c81a74743 | ||
|
|
a3f2b1d829 | ||
|
|
cbdd71e44e | ||
|
|
f01cf5e04c | ||
|
|
bbb3063cb2 | ||
|
|
c0fa6e6939 | ||
|
|
0337362eec | ||
|
|
fb3013cb95 | ||
|
|
833fec3d21 | ||
|
|
007b79c729 | ||
|
|
09f55955e8 | ||
|
|
29c2c72d8f | ||
|
|
ae9b541ce2 | ||
|
|
8d86637d3f | ||
|
|
390ff1bcf7 | ||
|
|
aa65589391 | ||
|
|
9ee23a9086 | ||
|
|
41cdd3dcf7 | ||
|
|
aba5741450 | ||
|
|
cb5eb417c5 | ||
|
|
9f8b8b624b | ||
|
|
8728b07e85 | ||
|
|
e1ffd83b64 | ||
|
|
020a494f3f | ||
|
|
96ed1e1372 | ||
|
|
860682e476 | ||
|
|
2903b4910c | ||
|
|
c07e33eff0 | ||
|
|
a5b1b04915 | ||
|
|
799848c2a0 | ||
|
|
7e6cd34071 | ||
|
|
b8c24f906d | ||
|
|
e678738ded | ||
|
|
9000be6daa | ||
|
|
13ff4afaa4 | ||
|
|
40b9c07641 | ||
|
|
0ce5117715 | ||
|
|
526d154172 | ||
|
|
9e422576b8 | ||
|
|
069bca29d1 | ||
|
|
34ac124b6b | ||
|
|
e475a22435 | ||
|
|
d1ec628d6c | ||
|
|
d13227b277 | ||
|
|
fcb992240e | ||
|
|
cf5eb6a0bc | ||
|
|
2aa38baa35 | ||
|
|
84d046ab2a | ||
|
|
29b08fc306 | ||
|
|
a7153a0ff5 | ||
|
|
bc67308f85 | ||
|
|
6e93a87265 | ||
|
|
6fd1f794f9 | ||
|
|
5942e65b1c | ||
|
|
77d670c224 | ||
|
|
70546e5dfe | ||
|
|
fec7dfefa4 | ||
|
|
9a8944df94 | ||
|
|
ace242fca2 | ||
|
|
96d4034a74 | ||
|
|
2e3624d636 | ||
|
|
cb54bbcd97 | ||
|
|
847e78541a | ||
|
|
ad3eebcf63 | ||
|
|
86df468c3f | ||
|
|
2cf2486a2b | ||
|
|
4d443d614b | ||
|
|
50968ec1a6 | ||
|
|
38ada0b998 | ||
|
|
0b0f9827b9 | ||
|
|
88b5e3f71b | ||
|
|
bc608dc130 | ||
|
|
961c237d33 | ||
|
|
73e2f56ff5 | ||
|
|
a122ceb09b | ||
|
|
b30d7cb3dc | ||
|
|
cd81362781 | ||
|
|
63abf0b840 | ||
|
|
56d265ae11 | ||
|
|
c0c76da4c8 | ||
|
|
3edfff49a2 | ||
|
|
f9be835288 | ||
|
|
86e542e524 | ||
|
|
f28e189cf5 | ||
|
|
2538b6921f | ||
|
|
83525807a2 | ||
|
|
c6a61c1b92 | ||
|
|
0257171cf5 | ||
|
|
f431f9858c | ||
|
|
71e9230789 | ||
|
|
08859f1588 | ||
|
|
d0a255c84a | ||
|
|
ec6f341497 | ||
|
|
811b26275f | ||
|
|
107894eccf | ||
|
|
cb8387cba6 | ||
|
|
6700aac4a4 | ||
|
|
3d1173a2cd | ||
|
|
52c7cca3fc | ||
|
|
9a59a90187 | ||
|
|
08370cb3a6 | ||
|
|
2dce1f23b0 | ||
|
|
a52f5aaa75 | ||
|
|
ef5f7bca64 | ||
|
|
1435c71d9c | ||
|
|
0d2517b96d | ||
|
|
48a1c0f5d4 | ||
|
|
64fb04b77b | ||
|
|
2696a93ee0 | ||
|
|
c350e300f9 | ||
|
|
b74638c25c | ||
|
|
f00fee5328 | ||
|
|
c3aa940c2b | ||
|
|
3fc15c42c4 | ||
|
|
f0c498ff54 | ||
|
|
cecffc0588 | ||
|
|
9293781702 | ||
|
|
97b064be13 | ||
|
|
52acac1a37 | ||
|
|
a296afdd69 | ||
|
|
2e71c718ce | ||
|
|
e44ab85880 | ||
|
|
24452ecd37 | ||
|
|
ccb7d62d95 | ||
|
|
83d2739dc5 | ||
|
|
c0aa1b99b5 | ||
|
|
1260db1bcf | ||
|
|
0625bf8185 | ||
|
|
403ea39825 | ||
|
|
28e1f6e3c7 | ||
|
|
7ee92db427 | ||
|
|
d4cc45f55e | ||
|
|
8240d19fbd | ||
|
|
a158360d7a | ||
|
|
a30f77f8a4 | ||
|
|
8ec9e9ef4d | ||
|
|
dbfb4269cb | ||
|
|
fad7112dd0 | ||
|
|
6afef04a88 | ||
|
|
10f8849e12 | ||
|
|
b55f49b4fe | ||
|
|
883815cd53 | ||
|
|
c9e73edd86 | ||
|
|
2ac2a75c8c | ||
|
|
2e1721ca85 | ||
|
|
91d1e1df48 | ||
|
|
2cfdeeb5a2 | ||
|
|
c9fea671a7 | ||
|
|
dbda8fbdb8 | ||
|
|
e2012a8272 | ||
|
|
c930d58672 | ||
|
|
cdd116dedf | ||
|
|
ad3141dac7 | ||
|
|
ce405b6060 | ||
|
|
53d98c882e | ||
|
|
519cf58d27 | ||
|
|
fdeae99ba5 | ||
|
|
1071f04e93 | ||
|
|
3f6ca549b9 | ||
|
|
a452f15905 | ||
|
|
5fdc8c3e08 | ||
|
|
5a45d2b127 | ||
|
|
0fd8fbe301 | ||
|
|
ec978fc79f | ||
|
|
8c3abb0d8b | ||
|
|
e1fca7f062 | ||
|
|
50829c3217 | ||
|
|
da06854e05 | ||
|
|
865f172e33 | ||
|
|
5607754702 | ||
|
|
17d134766d | ||
|
|
06ed881e37 | ||
|
|
eabd250ed2 | ||
|
|
af153ea0cf | ||
|
|
3f3d68a33a | ||
|
|
c0916edc44 | ||
|
|
34220fd514 | ||
|
|
5b8f673baa | ||
|
|
5b18289808 | ||
|
|
2589c90c25 | ||
|
|
16e0b93bec | ||
|
|
9274e80d71 | ||
|
|
66deeec9f1 | ||
|
|
59ca6548ea | ||
|
|
267d2314e0 | ||
|
|
0b5502963e | ||
|
|
4431aa2dd0 | ||
|
|
26aafa1b6f | ||
|
|
d29f1c062f | ||
|
|
879558fe20 | ||
|
|
940b5c4fb9 | ||
|
|
5ab9c01378 | ||
|
|
db67d2c4df | ||
|
|
47867cd80b | ||
|
|
fe0baa4de9 | ||
|
|
262e6bb543 | ||
|
|
18760acaa8 | ||
|
|
a62404c16f | ||
|
|
44db4e100a | ||
|
|
62d8448b9c | ||
|
|
1cc7682d44 | ||
|
|
6eb3fdf863 | ||
|
|
2e4de673b2 | ||
|
|
a64913f2f0 | ||
|
|
2c7d650d44 | ||
|
|
c05b540518 | ||
|
|
c8bd8d0f26 | ||
|
|
a4cd8095a1 | ||
|
|
79485c99ab | ||
|
|
e29f171844 | ||
|
|
c16cca891f | ||
|
|
68ef3a464e | ||
|
|
a5958b494d | ||
|
|
c88a2f4137 | ||
|
|
349bad9240 | ||
|
|
8c7b534544 | ||
|
|
3028897392 | ||
|
|
b28d80e752 | ||
|
|
76552855bb | ||
|
|
e4d19c84eb | ||
|
|
737d615fbc | ||
|
|
2cac6ce281 | ||
|
|
7490d4d4b8 | ||
|
|
3cb9e7cbee | ||
|
|
7b731f7777 | ||
|
|
7e5cbdaea5 | ||
|
|
6e81e82c8c | ||
|
|
a4e1b80ba7 | ||
|
|
e0131f22dd | ||
|
|
caf55ccb7f | ||
|
|
04afc09d58 | ||
|
|
b67c432bc1 | ||
|
|
a59aaa4dfb | ||
|
|
1302fb17c2 | ||
|
|
2e1e4106fd | ||
|
|
e4df6c9d14 | ||
|
|
bca26ebf0a | ||
|
|
2827171dd8 | ||
|
|
b44a3d36ba | ||
|
|
ea04bc8e47 | ||
|
|
0f7b903ace | ||
|
|
71d59e36cc | ||
|
|
7f99655a2b | ||
|
|
d56844010f | ||
|
|
218a661449 | ||
|
|
873950dc55 | ||
|
|
b445013d25 | ||
|
|
65055c273f | ||
|
|
3c14f8beb0 | ||
|
|
9dd98fb2b2 | ||
|
|
4a871cdf10 | ||
|
|
bb6a7b8f07 | ||
|
|
ae9728685c | ||
|
|
662f0d8cae | ||
|
|
1f5961d2ad | ||
|
|
b263e14fa8 | ||
|
|
1e67d75848 | ||
|
|
f4acc0cfc9 | ||
|
|
e5c9132d1d | ||
|
|
3c330b71aa | ||
|
|
f3f6c373fc | ||
|
|
577d4e71af | ||
|
|
a80325995d | ||
|
|
c093714597 | ||
|
|
fa9d42d7bf | ||
|
|
e93b3d8d75 | ||
|
|
6ea3e5b4da | ||
|
|
ae5294f010 | ||
|
|
8571f9e1f8 | ||
|
|
0cd0bf3b58 | ||
|
|
d62cc90f51 | ||
|
|
3fc5f34344 |
9
.github/aur/flux-bin/.SRCINFO.template
vendored
9
.github/aur/flux-bin/.SRCINFO.template
vendored
@@ -4,19 +4,16 @@ pkgbase = flux-bin
|
|||||||
pkgrel = ${PKGREL}
|
pkgrel = ${PKGREL}
|
||||||
url = https://fluxcd.io/
|
url = https://fluxcd.io/
|
||||||
arch = x86_64
|
arch = x86_64
|
||||||
arch = armv6h
|
|
||||||
arch = armv7h
|
arch = armv7h
|
||||||
arch = aarch64
|
arch = aarch64
|
||||||
license = APACHE
|
license = APACHE
|
||||||
optdepends = bash-completion: auto-completion for flux in Bash
|
optdepends = bash-completion: auto-completion for flux in Bash
|
||||||
optdepends = zsh-completions: auto-completion for flux in ZSH
|
optdepends = zsh-completions: auto-completion for flux in ZSH
|
||||||
source_x86_64 = ${PKGNAME}-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${PKGVER}/flux_${PKGVER}_linux_amd64.tar.gz
|
source_x86_64 = flux-bin-${PKGVER}_linux_amd64.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_amd64.tar.gz
|
||||||
sha256sums_x86_64 = ${SHA256SUM_AMD64}
|
sha256sums_x86_64 = ${SHA256SUM_AMD64}
|
||||||
source_armv6h = ${PKGNAME}-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${PKGVER}/flux_${PKGVER}_linux_arm.tar.gz
|
source_armv7h = flux-bin-${PKGVER}_linux_arm.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_arm.tar.gz
|
||||||
sha256sums_armv6h = ${SHA256SUM_ARM}
|
|
||||||
source_armv7h = ${PKGNAME}-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${PKGVER}/flux_${PKGVER}_linux_arm.tar.gz
|
|
||||||
sha256sums_armv7h = ${SHA256SUM_ARM}
|
sha256sums_armv7h = ${SHA256SUM_ARM}
|
||||||
source_aarch64 = ${PKGNAME}-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${PKGVER}/flux_${PKGVER}_linux_arm64.tar.gz
|
source_aarch64 = flux-bin-${PKGVER}_linux_arm64.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_arm64.tar.gz
|
||||||
sha256sums_aarch64 = ${SHA256SUM_ARM64}
|
sha256sums_aarch64 = ${SHA256SUM_ARM64}
|
||||||
|
|
||||||
pkgname = flux-bin
|
pkgname = flux-bin
|
||||||
|
|||||||
17
.github/aur/flux-bin/PKGBUILD.template
vendored
17
.github/aur/flux-bin/PKGBUILD.template
vendored
@@ -4,37 +4,32 @@
|
|||||||
pkgname=flux-bin
|
pkgname=flux-bin
|
||||||
pkgver=${PKGVER}
|
pkgver=${PKGVER}
|
||||||
pkgrel=${PKGREL}
|
pkgrel=${PKGREL}
|
||||||
|
_srcname=flux
|
||||||
|
_srcver=${VERSION}
|
||||||
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
||||||
url="https://fluxcd.io/"
|
url="https://fluxcd.io/"
|
||||||
arch=("x86_64" "armv6h" "armv7h" "aarch64")
|
arch=("x86_64" "armv7h" "aarch64")
|
||||||
license=("APACHE")
|
license=("APACHE")
|
||||||
optdepends=('bash-completion: auto-completion for flux in Bash'
|
optdepends=('bash-completion: auto-completion for flux in Bash'
|
||||||
'zsh-completions: auto-completion for flux in ZSH')
|
'zsh-completions: auto-completion for flux in ZSH')
|
||||||
source_x86_64=(
|
source_x86_64=(
|
||||||
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_amd64.tar.gz"
|
"${pkgname}-${pkgver}_linux_amd64.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${_srcver}/flux_${_srcver}_linux_amd64.tar.gz"
|
||||||
)
|
|
||||||
source_armv6h=(
|
|
||||||
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm.tar.gz"
|
|
||||||
)
|
)
|
||||||
source_armv7h=(
|
source_armv7h=(
|
||||||
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm.tar.gz"
|
"${pkgname}-${pkgver}_linux_arm.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${_srcver}/flux_${_srcver}_linux_arm.tar.gz"
|
||||||
)
|
)
|
||||||
source_aarch64=(
|
source_aarch64=(
|
||||||
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm64.tar.gz"
|
"${pkgname}-${pkgver}_linux_arm64.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${_srcver}/flux_${_srcver}_linux_arm64.tar.gz"
|
||||||
)
|
)
|
||||||
sha256sums_x86_64=(
|
sha256sums_x86_64=(
|
||||||
${SHA256SUM_AMD64}
|
${SHA256SUM_AMD64}
|
||||||
)
|
)
|
||||||
sha256sums_armv6h=(
|
|
||||||
${SHA256SUM_ARM}
|
|
||||||
)
|
|
||||||
sha256sums_armv7h=(
|
sha256sums_armv7h=(
|
||||||
${SHA256SUM_ARM}
|
${SHA256SUM_ARM}
|
||||||
)
|
)
|
||||||
sha256sums_aarch64=(
|
sha256sums_aarch64=(
|
||||||
${SHA256SUM_ARM64}
|
${SHA256SUM_ARM64}
|
||||||
)
|
)
|
||||||
_srcname=flux
|
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
|
install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
|
||||||
|
|||||||
11
.github/aur/flux-bin/publish.sh
vendored
11
.github/aur/flux-bin/publish.sh
vendored
@@ -28,6 +28,7 @@ git clone aur@aur.archlinux.org:$PKGNAME $GITDIR 2>&1
|
|||||||
CURRENT_PKGVER=$(cat $GITDIR/.SRCINFO | grep pkgver | awk '{ print $3 }')
|
CURRENT_PKGVER=$(cat $GITDIR/.SRCINFO | grep pkgver | awk '{ print $3 }')
|
||||||
CURRENT_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
CURRENT_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
||||||
|
|
||||||
|
# Transform pre-release to AUR compatible version format
|
||||||
export PKGVER=${VERSION/-/}
|
export PKGVER=${VERSION/-/}
|
||||||
|
|
||||||
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
||||||
@@ -36,12 +37,12 @@ else
|
|||||||
export PKGREL=1
|
export PKGREL=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export SHA256SUM_ARM=$(sha256sum ${ROOT}/dist/flux_${PKGVER}_linux_arm.tar.gz | awk '{ print $1 }')
|
export SHA256SUM_ARM=$(sha256sum ${ROOT}/dist/flux_${VERSION}_linux_arm.tar.gz | awk '{ print $1 }')
|
||||||
export SHA256SUM_ARM64=$(sha256sum ${ROOT}/dist/flux_${PKGVER}_linux_arm64.tar.gz | awk '{ print $1 }')
|
export SHA256SUM_ARM64=$(sha256sum ${ROOT}/dist/flux_${VERSION}_linux_arm64.tar.gz | awk '{ print $1 }')
|
||||||
export SHA256SUM_AMD64=$(sha256sum ${ROOT}/dist/flux_${PKGVER}_linux_amd64.tar.gz | awk '{ print $1 }')
|
export SHA256SUM_AMD64=$(sha256sum ${ROOT}/dist/flux_${VERSION}_linux_amd64.tar.gz | awk '{ print $1 }')
|
||||||
|
|
||||||
envsubst '$PKGVER $PKGREL $SHA256SUM_AMD64 $SHA256SUM_ARM $SHA256SUM_ARM64' < .SRCINFO.template > $GITDIR/.SRCINFO
|
envsubst '$VERSION $PKGVER $PKGREL $SHA256SUM_AMD64 $SHA256SUM_ARM $SHA256SUM_ARM64' < .SRCINFO.template > $GITDIR/.SRCINFO
|
||||||
envsubst '$PKGVER $PKGREL $SHA256SUM_AMD64 $SHA256SUM_ARM $SHA256SUM_ARM64' < PKGBUILD.template > $GITDIR/PKGBUILD
|
envsubst '$VERSION $PKGVER $PKGREL $SHA256SUM_AMD64 $SHA256SUM_ARM $SHA256SUM_ARM64' < PKGBUILD.template > $GITDIR/PKGBUILD
|
||||||
|
|
||||||
cd $GITDIR
|
cd $GITDIR
|
||||||
git config user.name "fluxcdbot"
|
git config user.name "fluxcdbot"
|
||||||
|
|||||||
3
.github/aur/flux-go/.SRCINFO.template
vendored
3
.github/aur/flux-go/.SRCINFO.template
vendored
@@ -4,7 +4,6 @@ pkgbase = flux-go
|
|||||||
pkgrel = ${PKGREL}
|
pkgrel = ${PKGREL}
|
||||||
url = https://fluxcd.io/
|
url = https://fluxcd.io/
|
||||||
arch = x86_64
|
arch = x86_64
|
||||||
arch = armv6h
|
|
||||||
arch = armv7h
|
arch = armv7h
|
||||||
arch = aarch64
|
arch = aarch64
|
||||||
license = APACHE
|
license = APACHE
|
||||||
@@ -13,6 +12,6 @@ pkgbase = flux-go
|
|||||||
provides = flux-bin
|
provides = flux-bin
|
||||||
conflicts = flux-bin
|
conflicts = flux-bin
|
||||||
replaces = flux-cli
|
replaces = flux-cli
|
||||||
source = flux-go-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/archive/v${PKGVER}.tar.gz
|
source = flux-go-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/archive/v${VERSION}.tar.gz
|
||||||
|
|
||||||
pkgname = flux-go
|
pkgname = flux-go
|
||||||
|
|||||||
19
.github/aur/flux-go/PKGBUILD.template
vendored
19
.github/aur/flux-go/PKGBUILD.template
vendored
@@ -4,43 +4,44 @@
|
|||||||
pkgname=flux-go
|
pkgname=flux-go
|
||||||
pkgver=${PKGVER}
|
pkgver=${PKGVER}
|
||||||
pkgrel=${PKGREL}
|
pkgrel=${PKGREL}
|
||||||
|
_srcname=flux
|
||||||
|
_srcver=${VERSION}
|
||||||
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
||||||
url="https://fluxcd.io/"
|
url="https://fluxcd.io/"
|
||||||
arch=("x86_64" "armv6h" "armv7h" "aarch64")
|
arch=("x86_64" "armv7h" "aarch64")
|
||||||
license=("APACHE")
|
license=("APACHE")
|
||||||
provides=("flux-bin")
|
provides=("flux-bin")
|
||||||
conflicts=("flux-bin")
|
conflicts=("flux-bin")
|
||||||
replaces=("flux-cli")
|
replaces=("flux-cli")
|
||||||
depends=("glibc")
|
depends=("glibc")
|
||||||
makedepends=('go>=1.17', 'kustomize>=3.0')
|
makedepends=('go>=1.20', 'kustomize>=5.0')
|
||||||
optdepends=('bash-completion: auto-completion for flux in Bash',
|
optdepends=('bash-completion: auto-completion for flux in Bash',
|
||||||
'zsh-completions: auto-completion for flux in ZSH')
|
'zsh-completions: auto-completion for flux in ZSH')
|
||||||
source=(
|
source=(
|
||||||
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/archive/v${pkgver}.tar.gz"
|
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/archive/v${_srcver}.tar.gz"
|
||||||
)
|
)
|
||||||
sha256sums=(
|
sha256sums=(
|
||||||
${SHA256SUM}
|
${SHA256SUM}
|
||||||
)
|
)
|
||||||
_srcname=flux
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "flux2-${pkgver}"
|
cd "flux2-${_srcver}"
|
||||||
export CGO_LDFLAGS="$LDFLAGS"
|
export CGO_LDFLAGS="$LDFLAGS"
|
||||||
export CGO_CFLAGS="$CFLAGS"
|
export CGO_CFLAGS="$CFLAGS"
|
||||||
export CGO_CXXFLAGS="$CXXFLAGS"
|
export CGO_CXXFLAGS="$CXXFLAGS"
|
||||||
export CGO_CPPFLAGS="$CPPFLAGS"
|
export CGO_CPPFLAGS="$CPPFLAGS"
|
||||||
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
|
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
|
||||||
make cmd/flux/.manifests.done
|
make cmd/flux/.manifests.done
|
||||||
go build -ldflags "-linkmode=external -X main.VERSION=${pkgver}" -o ${_srcname} ./cmd/flux
|
go build -ldflags "-linkmode=external -X main.VERSION=${_srcver}" -o ${_srcname} ./cmd/flux
|
||||||
}
|
}
|
||||||
|
|
||||||
check() {
|
check() {
|
||||||
cd "flux2-${pkgver}"
|
cd "flux2-${_srcver}"
|
||||||
case $CARCH in
|
case $CARCH in
|
||||||
aarch64)
|
aarch64)
|
||||||
export ENVTEST_ARCH=arm64
|
export ENVTEST_ARCH=arm64
|
||||||
;;
|
;;
|
||||||
armv6h|armv7h)
|
armv7h)
|
||||||
export ENVTEST_ARCH=arm
|
export ENVTEST_ARCH=arm
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -48,7 +49,7 @@ check() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
cd "flux2-${pkgver}"
|
cd "flux2-${_srcver}"
|
||||||
install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
|
install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
|
||||||
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
|
||||||
|
|
||||||
|
|||||||
7
.github/aur/flux-go/publish.sh
vendored
7
.github/aur/flux-go/publish.sh
vendored
@@ -28,6 +28,7 @@ git clone aur@aur.archlinux.org:$PKGNAME $GITDIR 2>&1
|
|||||||
CURRENT_PKGVER=$(cat $GITDIR/.SRCINFO | grep pkgver | awk '{ print $3 }')
|
CURRENT_PKGVER=$(cat $GITDIR/.SRCINFO | grep pkgver | awk '{ print $3 }')
|
||||||
CURRENT_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
CURRENT_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
||||||
|
|
||||||
|
# Transform pre-release to AUR compatible version format
|
||||||
export PKGVER=${VERSION/-/}
|
export PKGVER=${VERSION/-/}
|
||||||
|
|
||||||
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
||||||
@@ -36,10 +37,10 @@ else
|
|||||||
export PKGREL=1
|
export PKGREL=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export SHA256SUM=$(curl -sL https://github.com/fluxcd/flux2/archive/v$PKGVER.tar.gz | sha256sum | awk '{ print $1 }')
|
export SHA256SUM=$(curl -sL https://github.com/fluxcd/flux2/archive/v${VERSION}.tar.gz | sha256sum | awk '{ print $1 }')
|
||||||
|
|
||||||
envsubst '$PKGVER $PKGREL $SHA256SUM' < .SRCINFO.template > $GITDIR/.SRCINFO
|
envsubst '$VERSION $PKGVER $PKGREL $SHA256SUM' < .SRCINFO.template > $GITDIR/.SRCINFO
|
||||||
envsubst '$PKGVER $PKGREL $SHA256SUM' < PKGBUILD.template > $GITDIR/PKGBUILD
|
envsubst '$VERSION $PKGVER $PKGREL $SHA256SUM' < PKGBUILD.template > $GITDIR/PKGBUILD
|
||||||
|
|
||||||
cd $GITDIR
|
cd $GITDIR
|
||||||
git config user.name "fluxcdbot"
|
git config user.name "fluxcdbot"
|
||||||
|
|||||||
1
.github/aur/flux-scm/.SRCINFO.template
vendored
1
.github/aur/flux-scm/.SRCINFO.template
vendored
@@ -4,7 +4,6 @@ pkgbase = flux-scm
|
|||||||
pkgrel = ${PKGREL}
|
pkgrel = ${PKGREL}
|
||||||
url = https://fluxcd.io/
|
url = https://fluxcd.io/
|
||||||
arch = x86_64
|
arch = x86_64
|
||||||
arch = armv6h
|
|
||||||
arch = armv7h
|
arch = armv7h
|
||||||
arch = aarch64
|
arch = aarch64
|
||||||
license = APACHE
|
license = APACHE
|
||||||
|
|||||||
8
.github/aur/flux-scm/PKGBUILD.template
vendored
8
.github/aur/flux-scm/PKGBUILD.template
vendored
@@ -4,21 +4,21 @@
|
|||||||
pkgname=flux-scm
|
pkgname=flux-scm
|
||||||
pkgver=${PKGVER}
|
pkgver=${PKGVER}
|
||||||
pkgrel=${PKGREL}
|
pkgrel=${PKGREL}
|
||||||
|
_srcname=flux
|
||||||
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
||||||
url="https://fluxcd.io/"
|
url="https://fluxcd.io/"
|
||||||
arch=("x86_64" "armv6h" "armv7h" "aarch64")
|
arch=("x86_64" "armv7h" "aarch64")
|
||||||
license=("APACHE")
|
license=("APACHE")
|
||||||
provides=("flux-bin")
|
provides=("flux-bin")
|
||||||
conflicts=("flux-bin")
|
conflicts=("flux-bin")
|
||||||
depends=("glibc")
|
depends=("glibc")
|
||||||
makedepends=('go>=1.17', 'kustomize>=3.0', 'git')
|
makedepends=('go>=1.20', 'kustomize>=5.0', 'git')
|
||||||
optdepends=('bash-completion: auto-completion for flux in Bash',
|
optdepends=('bash-completion: auto-completion for flux in Bash',
|
||||||
'zsh-completions: auto-completion for flux in ZSH')
|
'zsh-completions: auto-completion for flux in ZSH')
|
||||||
source=(
|
source=(
|
||||||
"git+https://github.com/fluxcd/flux2.git"
|
"git+https://github.com/fluxcd/flux2.git"
|
||||||
)
|
)
|
||||||
md5sums=('SKIP')
|
md5sums=('SKIP')
|
||||||
_srcname=flux
|
|
||||||
|
|
||||||
pkgver() {
|
pkgver() {
|
||||||
cd "flux2"
|
cd "flux2"
|
||||||
@@ -42,7 +42,7 @@ check() {
|
|||||||
aarch64)
|
aarch64)
|
||||||
export ENVTEST_ARCH=arm64
|
export ENVTEST_ARCH=arm64
|
||||||
;;
|
;;
|
||||||
armv6h|armv7h)
|
armv7h)
|
||||||
export ENVTEST_ARCH=arm
|
export ENVTEST_ARCH=arm
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
1
.github/aur/flux-scm/publish.sh
vendored
1
.github/aur/flux-scm/publish.sh
vendored
@@ -28,6 +28,7 @@ git clone aur@aur.archlinux.org:$PKGNAME $GITDIR 2>&1
|
|||||||
CURRENT_PKGVER=$(cat $GITDIR/.SRCINFO | grep pkgver | awk '{ print $3 }')
|
CURRENT_PKGVER=$(cat $GITDIR/.SRCINFO | grep pkgver | awk '{ print $3 }')
|
||||||
CURRENT_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
CURRENT_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
||||||
|
|
||||||
|
# Transform pre-release to AUR compatible version format
|
||||||
export PKGVER=${VERSION/-/}
|
export PKGVER=${VERSION/-/}
|
||||||
|
|
||||||
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
||||||
|
|||||||
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@@ -3,7 +3,14 @@ version: 2
|
|||||||
updates:
|
updates:
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
labels: ["area/build"]
|
labels: ["area/ci", "dependencies"]
|
||||||
|
groups:
|
||||||
|
# Group all updates together, so that they are all applied in a single PR.
|
||||||
|
# Grouped updates are currently in beta and is subject to change.
|
||||||
|
# xref: https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups
|
||||||
|
ci:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
schedule:
|
schedule:
|
||||||
# by default this will be on a monday.
|
# By default, this will be on a monday.
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
|||||||
52
.github/labels.yaml
vendored
Normal file
52
.github/labels.yaml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Configuration file to declaratively configure labels
|
||||||
|
# Ref: https://github.com/EndBug/label-sync#Config-files
|
||||||
|
|
||||||
|
- name: area/bootstrap
|
||||||
|
description: Bootstrap related issues and pull requests
|
||||||
|
color: '#86efc9'
|
||||||
|
- name: area/install
|
||||||
|
description: Install and uninstall related issues and pull requests
|
||||||
|
color: '#86efc9'
|
||||||
|
- name: area/diff
|
||||||
|
description: Diff related issues and pull requests
|
||||||
|
color: '#BA4192'
|
||||||
|
- name: area/bucket
|
||||||
|
description: Bucket related issues and pull requests
|
||||||
|
color: '#00b140'
|
||||||
|
- name: area/git
|
||||||
|
description: Git related issues and pull requests
|
||||||
|
color: '#863faf'
|
||||||
|
- name: area/oci
|
||||||
|
description: OCI related issues and pull requests
|
||||||
|
color: '#c739ff'
|
||||||
|
- name: area/kustomization
|
||||||
|
description: Kustomization related issues and pull requests
|
||||||
|
color: '#00e54d'
|
||||||
|
- name: area/helm
|
||||||
|
description: Helm related issues and pull requests
|
||||||
|
color: '#1673b6'
|
||||||
|
- name: area/image-automation
|
||||||
|
description: Automated image updates related issues and pull requests
|
||||||
|
color: '#c5def5'
|
||||||
|
- name: area/monitoring
|
||||||
|
description: Monitoring related issues and pull requests
|
||||||
|
color: '#dd75ae'
|
||||||
|
- name: area/multi-tenancy
|
||||||
|
description: Multi-tenancy related issues and pull requests
|
||||||
|
color: '#72CDBD'
|
||||||
|
- name: area/notification
|
||||||
|
description: Notification API related issues and pull requests
|
||||||
|
color: '#434ec1'
|
||||||
|
- name: area/source
|
||||||
|
description: Source API related issues and pull requests
|
||||||
|
color: '#863faf'
|
||||||
|
- name: area/rfc
|
||||||
|
description: Feature request proposals in the RFC format
|
||||||
|
color: '#D621C3'
|
||||||
|
aliases: ['area/RFC']
|
||||||
|
- name: backport:release/v2.0.x
|
||||||
|
description: To be backported to release/v2.0.x
|
||||||
|
color: '#ffd700'
|
||||||
|
- name: backport:release/v2.1.x
|
||||||
|
description: To be backported to release/v2.1.x
|
||||||
|
color: '#ffd700'
|
||||||
29
.github/workflows/action.yaml
vendored
Normal file
29
.github/workflows/action.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
name: test-gh-action
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'action/**'
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'action/**'
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
- 'release/**'
|
||||||
|
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
actions:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
version: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.version }}
|
||||||
|
name: action on ${{ matrix.version }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
- name: Setup flux
|
||||||
|
uses: ./action
|
||||||
31
.github/workflows/backport.yaml
vendored
Normal file
31
.github/workflows/backport.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: backport
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [closed, labeled]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
pull-request:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
if: github.event.pull_request.state == 'closed' && github.event.pull_request.merged && (github.event_name != 'labeled' || startsWith('backport:', github.event.label.name))
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
- name: Create backport PRs
|
||||||
|
uses: korthout/backport-action@b982d297e31f500652b2246cf26714796312bd23 # v2.2.0
|
||||||
|
# xref: https://github.com/korthout/backport-action#inputs
|
||||||
|
with:
|
||||||
|
# Use token to allow workflows to be triggered for the created PR
|
||||||
|
github_token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||||
|
# Match labels with a pattern `backport:<target-branch>`
|
||||||
|
label_pattern: '^backport:([^ ]+)$'
|
||||||
|
# A bit shorter pull-request title than the default
|
||||||
|
pull_title: '[${target_branch}] ${pull_title}'
|
||||||
|
# Simpler PR description than default
|
||||||
|
pull_description: |-
|
||||||
|
Automated backport to `${target_branch}`, triggered by a label in #${pull_number}.
|
||||||
15
.github/workflows/e2e-arm64.yaml
vendored
15
.github/workflows/e2e-arm64.yaml
vendored
@@ -3,7 +3,7 @@ name: e2e-arm64
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ main, update-components, e2e-arm64* ]
|
branches: [ 'main', 'update-components', 'e2e-*', 'release/**' ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -16,14 +16,19 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# Keep this list up-to-date with https://endoflife.date/kubernetes
|
# Keep this list up-to-date with https://endoflife.date/kubernetes
|
||||||
KUBERNETES_VERSION: [ 1.23.13, 1.24.7, 1.25.3 ]
|
# Check which versions are available on DockerHub with 'crane ls kindest/node'
|
||||||
|
KUBERNETES_VERSION: [ 1.26.6, 1.27.3, 1.28.0 ]
|
||||||
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.19.x
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
**/go.mod
|
||||||
- name: Prepare
|
- name: Prepare
|
||||||
id: prep
|
id: prep
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
102
.github/workflows/e2e-azure.yaml
vendored
102
.github/workflows/e2e-azure.yaml
vendored
@@ -3,9 +3,19 @@ name: e2e-azure
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 6 * * *'
|
- cron: '0 6 * * *'
|
||||||
push:
|
push:
|
||||||
branches: [ azure* ]
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'tests/**'
|
||||||
|
- '.github/workflows/e2e-azure.yaml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'tests/**'
|
||||||
|
- '.github/workflows/e2e-azure.yaml'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -13,33 +23,32 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
e2e-amd64-aks:
|
e2e-amd64-aks:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./tests/azure
|
||||||
|
# This job is currently disabled. Remove the false check when Azure subscription is enabled.
|
||||||
|
if: false && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Restore Go cache
|
|
||||||
uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12
|
|
||||||
with:
|
|
||||||
path: ~/go/pkg/mod
|
|
||||||
key: ${{ runner.os }}-go1.18-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-go1.18-
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.19.x
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: tests/azure/go.sum
|
||||||
- name: Setup Flux CLI
|
- name: Setup Flux CLI
|
||||||
run: |
|
run: |
|
||||||
make build
|
make build
|
||||||
mkdir -p $HOME/.local/bin
|
mkdir -p $HOME/.local/bin
|
||||||
mv ./bin/flux $HOME/.local/bin
|
mv ./bin/flux $HOME/.local/bin
|
||||||
|
working-directory: ./
|
||||||
- name: Setup SOPS
|
- name: Setup SOPS
|
||||||
run: |
|
run: |
|
||||||
wget https://github.com/mozilla/sops/releases/download/v3.7.1/sops-v3.7.1.linux
|
|
||||||
chmod +x sops-v3.7.1.linux
|
|
||||||
mkdir -p $HOME/.local/bin
|
mkdir -p $HOME/.local/bin
|
||||||
mv sops-v3.7.1.linux $HOME/.local/bin/sops
|
wget https://github.com/mozilla/sops/releases/download/v3.7.1/sops-v3.7.1.linux -O $HOME/.local/bin/sops
|
||||||
|
chmod +x $HOME/.local/bin/sops
|
||||||
- name: Setup Terraform
|
- name: Setup Terraform
|
||||||
uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 # v2
|
uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v2
|
||||||
with:
|
with:
|
||||||
terraform_version: 1.2.8
|
terraform_version: 1.2.8
|
||||||
terraform_wrapper: false
|
terraform_wrapper: false
|
||||||
@@ -53,9 +62,64 @@ jobs:
|
|||||||
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
|
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
|
||||||
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
|
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
|
||||||
run: |
|
run: |
|
||||||
echo $HOME
|
|
||||||
echo $PATH
|
|
||||||
ls $HOME/.local/bin
|
ls $HOME/.local/bin
|
||||||
az login --service-principal -u ${ARM_CLIENT_ID} -p ${ARM_CLIENT_SECRET} -t ${ARM_TENANT_ID}
|
az login --service-principal -u ${ARM_CLIENT_ID} -p ${ARM_CLIENT_SECRET} -t ${ARM_TENANT_ID}
|
||||||
cd ./tests/azure
|
|
||||||
go test -v -coverprofile cover.out -timeout 60m .
|
go test -v -coverprofile cover.out -timeout 60m .
|
||||||
|
|
||||||
|
refactored-e2e-amd64-aks:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./tests/integration
|
||||||
|
# This job is currently disabled. Remove the false check when Azure subscription is enabled.
|
||||||
|
if: false && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
|
||||||
|
steps:
|
||||||
|
- name: CheckoutD
|
||||||
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
|
with:
|
||||||
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: tests/integration/go.sum
|
||||||
|
- name: Setup Flux CLI
|
||||||
|
run: make build
|
||||||
|
working-directory: ./
|
||||||
|
- name: Setup SOPS
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.local/bin
|
||||||
|
wget -O $HOME/.local/bin/sops https://github.com/mozilla/sops/releases/download/v$SOPS_VER/sops-v$SOPS_VER.linux
|
||||||
|
chmod +x $HOME/.local/bin/sops
|
||||||
|
env:
|
||||||
|
SOPS_VER: 3.7.1
|
||||||
|
- name: Authenticate to Azure
|
||||||
|
uses: Azure/login@de95379fe4dadc2defb305917eaa7e5dde727294 # v1.4.6
|
||||||
|
with:
|
||||||
|
creds: '{"clientId":"${{ secrets.AZ_ARM_CLIENT_ID }}","clientSecret":"${{ secrets.AZ_ARM_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZ_ARM_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZ_ARM_TENANT_ID }}"}'
|
||||||
|
- name: Set dynamic variables in .env
|
||||||
|
run: |
|
||||||
|
cat > .env <<EOF
|
||||||
|
export TF_VAR_tags='{ "environment"="github", "ci"="true", "repo"="flux2", "createdat"="$(date -u +x%Y-%m-%d_%Hh%Mm%Ss)" }'
|
||||||
|
EOF
|
||||||
|
- name: Print .env for dynamic tag value reference
|
||||||
|
run: cat .env
|
||||||
|
- name: Run Azure e2e tests
|
||||||
|
env:
|
||||||
|
ARM_CLIENT_ID: ${{ secrets.AZ_ARM_CLIENT_ID }}
|
||||||
|
ARM_CLIENT_SECRET: ${{ secrets.AZ_ARM_CLIENT_SECRET }}
|
||||||
|
ARM_SUBSCRIPTION_ID: ${{ secrets.AZ_ARM_SUBSCRIPTION_ID }}
|
||||||
|
ARM_TENANT_ID: ${{ secrets.AZ_ARM_TENANT_ID }}
|
||||||
|
TF_VAR_azuredevops_org: ${{ secrets.TF_VAR_azuredevops_org }}
|
||||||
|
TF_VAR_azuredevops_pat: ${{ secrets.TF_VAR_azuredevops_pat }}
|
||||||
|
TF_VAR_location: ${{ vars.TF_VAR_azure_location }}
|
||||||
|
GITREPO_SSH_CONTENTS: ${{ secrets.AZURE_GITREPO_SSH_CONTENTS }}
|
||||||
|
GITREPO_SSH_PUB_CONTENTS: ${{ secrets.AZURE_GITREPO_SSH_PUB_CONTENTS }}
|
||||||
|
run: |
|
||||||
|
source .env
|
||||||
|
mkdir -p ./build/ssh
|
||||||
|
touch ./build/ssh/key
|
||||||
|
echo $GITREPO_SSH_CONTENTS | base64 -d > build/ssh/key
|
||||||
|
export GITREPO_SSH_PATH=build/ssh/key
|
||||||
|
touch ./build/ssh/key.pub
|
||||||
|
echo $GITREPO_SSH_PUB_CONTENTS | base64 -d > ./build/ssh/key.pub
|
||||||
|
export GITREPO_SSH_PUB_PATH=build/ssh/key.pub
|
||||||
|
make test-azure
|
||||||
|
|||||||
42
.github/workflows/e2e-bootstrap.yaml
vendored
42
.github/workflows/e2e-bootstrap.yaml
vendored
@@ -3,9 +3,10 @@ name: e2e-bootstrap
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ 'main', 'release/**' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ 'main', 'release/**' ]
|
||||||
|
paths-ignore: [ 'docs/**', 'rfcs/**' ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -16,26 +17,25 @@ jobs:
|
|||||||
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
|
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Restore Go cache
|
|
||||||
uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12
|
|
||||||
with:
|
|
||||||
path: ~/go/pkg/mod
|
|
||||||
key: ${{ runner.os }}-go1.18-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-go1.18-
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.19.x
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
**/go.mod
|
||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: helm/kind-action@d8ccf8fb623ce1bb360ae2f45f323d9d5c5e9f00 # v1.5.0
|
uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0
|
||||||
with:
|
with:
|
||||||
version: v0.17.0
|
version: v0.20.0
|
||||||
cluster_name: kind
|
cluster_name: kind
|
||||||
node_image: kindest/node:v1.25.2
|
# The versions below should target the newest Kubernetes version
|
||||||
|
# Keep this up-to-date with https://endoflife.date/kubernetes
|
||||||
|
node_image: kindest/node:v1.28.0@sha256:9f3ff58f19dcf1a0611d11e8ac989fdb30a28f40f236f59f0bea31fb956ccf5c
|
||||||
|
kubectl_version: v1.28.0
|
||||||
- name: Setup Kustomize
|
- name: Setup Kustomize
|
||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg/actions/kustomize@main
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
make cmd/flux/.manifests.done
|
make cmd/flux/.manifests.done
|
||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
COMMIT_SHA=$(git rev-parse HEAD)
|
COMMIT_SHA=$(git rev-parse HEAD)
|
||||||
PSEUDO_RAND_SUFFIX=$(echo "${BRANCH_NAME}-${COMMIT_SHA}" | shasum | awk '{print $1}')
|
PSEUDO_RAND_SUFFIX=$(echo "${BRANCH_NAME}-${COMMIT_SHA}" | shasum | awk '{print $1}')
|
||||||
TEST_REPO_NAME="${REPOSITORY_NAME}-${PSEUDO_RAND_SUFFIX}"
|
TEST_REPO_NAME="${REPOSITORY_NAME}-${PSEUDO_RAND_SUFFIX}"
|
||||||
echo "::set-output name=test_repo_name::$TEST_REPO_NAME"
|
echo "test_repo_name=$TEST_REPO_NAME" >> $GITHUB_OUTPUT
|
||||||
- name: bootstrap init
|
- name: bootstrap init
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
/tmp/flux bootstrap github --manifests ./manifests/install/ \
|
||||||
@@ -85,13 +85,6 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITPROVIDER_BOT_TOKEN }}
|
||||||
GITHUB_REPO_NAME: ${{ steps.vars.outputs.test_repo_name }}
|
GITHUB_REPO_NAME: ${{ steps.vars.outputs.test_repo_name }}
|
||||||
GITHUB_ORG_NAME: fluxcd-testing
|
GITHUB_ORG_NAME: fluxcd-testing
|
||||||
- name: libgit2
|
|
||||||
run: |
|
|
||||||
/tmp/flux create source git test-libgit2 \
|
|
||||||
--url=ssh://git@github.com/fluxcd-testing/${{ steps.vars.outputs.test_repo_name }} \
|
|
||||||
--git-implementation=libgit2 \
|
|
||||||
--secret-ref=flux-system \
|
|
||||||
--branch=main
|
|
||||||
- name: uninstall
|
- name: uninstall
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux uninstall -s --keep-namespace
|
/tmp/flux uninstall -s --keep-namespace
|
||||||
@@ -106,7 +99,6 @@ jobs:
|
|||||||
--path=test-cluster \
|
--path=test-cluster \
|
||||||
--read-write-key
|
--read-write-key
|
||||||
/tmp/flux reconcile image repository podinfo
|
/tmp/flux reconcile image repository podinfo
|
||||||
/tmp/flux reconcile image update flux-system
|
|
||||||
/tmp/flux get images all
|
/tmp/flux get images all
|
||||||
|
|
||||||
retries=10
|
retries=10
|
||||||
|
|||||||
92
.github/workflows/e2e-gcp.yaml
vendored
Normal file
92
.github/workflows/e2e-gcp.yaml
vendored
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
name: e2e-gcp
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 6 * * *'
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'tests/**'
|
||||||
|
- '.github/workflows/e2e-gcp.yaml'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'tests/**'
|
||||||
|
- '.github/workflows/e2e-gcp.yaml'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
e2e-gcp:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./tests/integration
|
||||||
|
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
|
with:
|
||||||
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: tests/integration/go.sum
|
||||||
|
- name: Setup Flux CLI
|
||||||
|
run: make build
|
||||||
|
working-directory: ./
|
||||||
|
- name: Setup SOPS
|
||||||
|
run: |
|
||||||
|
mkdir -p $HOME/.local/bin
|
||||||
|
wget -O $HOME/.local/bin/sops https://github.com/mozilla/sops/releases/download/v$SOPS_VER/sops-v$SOPS_VER.linux
|
||||||
|
chmod +x $HOME/.local/bin/sops
|
||||||
|
env:
|
||||||
|
SOPS_VER: 3.7.1
|
||||||
|
- name: Authenticate to Google Cloud
|
||||||
|
uses: google-github-actions/auth@67e9c72af6e0492df856527b474995862b7b6591 # v2.0.0
|
||||||
|
id: 'auth'
|
||||||
|
with:
|
||||||
|
credentials_json: '${{ secrets.FLUX2_E2E_GOOGLE_CREDENTIALS }}'
|
||||||
|
token_format: 'access_token'
|
||||||
|
- name: Setup gcloud
|
||||||
|
uses: google-github-actions/setup-gcloud@825196879a077b7efa50db2e88409f44de4635c2 # v2.0.0
|
||||||
|
- name: Setup QEMU
|
||||||
|
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
|
||||||
|
- name: Setup Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||||
|
- name: Log into us-central1-docker.pkg.dev
|
||||||
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||||
|
with:
|
||||||
|
registry: us-central1-docker.pkg.dev
|
||||||
|
username: oauth2accesstoken
|
||||||
|
password: ${{ steps.auth.outputs.access_token }}
|
||||||
|
- name: Set dynamic variables in .env
|
||||||
|
run: |
|
||||||
|
cat > .env <<EOF
|
||||||
|
export TF_VAR_tags='{ "environment"="github", "ci"="true", "repo"="flux2", "createdat"="$(date -u +x%Y-%m-%d_%Hh%Mm%Ss)" }'
|
||||||
|
EOF
|
||||||
|
- name: Print .env for dynamic tag value reference
|
||||||
|
run: cat .env
|
||||||
|
- name: Run GCP e2e tests
|
||||||
|
env:
|
||||||
|
TF_VAR_gcp_project_id: ${{ vars.TF_VAR_gcp_project_id }}
|
||||||
|
TF_VAR_gcp_region: ${{ vars.TF_VAR_gcp_region }}
|
||||||
|
TF_VAR_gcp_zone: ${{ vars.TF_VAR_gcp_zone }}
|
||||||
|
TF_VAR_gcp_email: ${{ secrets.TF_VAR_gcp_email }}
|
||||||
|
TF_VAR_gcp_keyring: ${{ secrets.TF_VAR_gcp_keyring }}
|
||||||
|
TF_VAR_gcp_crypto_key: ${{ secrets.TF_VAR_gcp_crypto_key }}
|
||||||
|
GITREPO_SSH_CONTENTS: ${{ secrets.GCP_GITREPO_SSH_CONTENTS }}
|
||||||
|
GITREPO_SSH_PUB_CONTENTS: ${{ secrets.GCP_GITREPO_SSH_PUB_CONTENTS }}
|
||||||
|
run: |
|
||||||
|
source .env
|
||||||
|
mkdir -p ./build/ssh
|
||||||
|
touch ./build/ssh/key
|
||||||
|
echo $GITREPO_SSH_CONTENTS | base64 -d > build/ssh/key
|
||||||
|
export GITREPO_SSH_PATH=build/ssh/key
|
||||||
|
touch ./build/ssh/key.pub
|
||||||
|
echo $GITREPO_SSH_PUB_CONTENTS | base64 -d > ./build/ssh/key.pub
|
||||||
|
export GITREPO_SSH_PUB_PATH=build/ssh/key.pub
|
||||||
|
make test-gcp
|
||||||
55
.github/workflows/e2e.yaml
vendored
55
.github/workflows/e2e.yaml
vendored
@@ -3,9 +3,10 @@ name: e2e
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ 'main', 'release/**' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main, oci ]
|
branches: [ 'main', 'release/**' ]
|
||||||
|
paths-ignore: [ 'docs/**', 'rfcs/**' ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -20,31 +21,30 @@ jobs:
|
|||||||
- 5000:5000
|
- 5000:5000
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Restore Go cache
|
|
||||||
uses: actions/cache@58c146cc91c5b9e778e71775dfe9bf1442ad9a12
|
|
||||||
with:
|
|
||||||
path: ~/go/pkg/mod
|
|
||||||
key: ${{ runner.os }}-go1.18-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-go1.18-
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.19.x
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
**/go.mod
|
||||||
- name: Setup Kubernetes
|
- name: Setup Kubernetes
|
||||||
uses: helm/kind-action@d8ccf8fb623ce1bb360ae2f45f323d9d5c5e9f00 # v1.5.0
|
uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0
|
||||||
with:
|
with:
|
||||||
version: v0.17.0
|
version: v0.20.0
|
||||||
cluster_name: kind
|
cluster_name: kind
|
||||||
config: .github/kind/config.yaml # disable KIND-net
|
config: .github/kind/config.yaml # disable KIND-net
|
||||||
node_image: kindest/node:v1.23.13
|
# The versions below should target the newest Kubernetes version
|
||||||
|
# Keep this up-to-date with https://endoflife.date/kubernetes
|
||||||
|
node_image: kindest/node:v1.28.0@sha256:9f3ff58f19dcf1a0611d11e8ac989fdb30a28f40f236f59f0bea31fb956ccf5c
|
||||||
|
kubectl_version: v1.28.0
|
||||||
- name: Setup Calico for network policy
|
- name: Setup Calico for network policy
|
||||||
run: |
|
run: |
|
||||||
kubectl apply -f https://docs.projectcalico.org/v3.20/manifests/calico.yaml
|
kubectl apply -f https://docs.projectcalico.org/v3.25/manifests/calico.yaml
|
||||||
kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
|
kubectl -n kube-system set env daemonset/calico-node FELIX_IGNORELOOSERPF=true
|
||||||
- name: Setup Kustomize
|
- name: Setup Kustomize
|
||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg/actions/kustomize@main
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: make test
|
run: make test
|
||||||
- name: Run e2e tests
|
- name: Run e2e tests
|
||||||
@@ -78,21 +78,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
/tmp/flux create source git podinfo \
|
/tmp/flux create source git podinfo \
|
||||||
--url https://github.com/stefanprodan/podinfo \
|
--url https://github.com/stefanprodan/podinfo \
|
||||||
--tag-semver=">=3.2.3"
|
--tag-semver=">=6.3.5"
|
||||||
- name: flux create source git export apply
|
- name: flux create source git export apply
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux create source git podinfo-export \
|
/tmp/flux create source git podinfo-export \
|
||||||
--url https://github.com/stefanprodan/podinfo \
|
--url https://github.com/stefanprodan/podinfo \
|
||||||
--tag-semver=">=3.2.3" \
|
--tag-semver=">=6.3.5" \
|
||||||
--export | kubectl apply -f -
|
--export | kubectl apply -f -
|
||||||
/tmp/flux delete source git podinfo-export --silent
|
/tmp/flux delete source git podinfo-export --silent
|
||||||
- name: flux create source git libgit2 semver
|
|
||||||
run: |
|
|
||||||
/tmp/flux create source git podinfo-libgit2 \
|
|
||||||
--url https://github.com/stefanprodan/podinfo \
|
|
||||||
--tag-semver=">=3.2.3" \
|
|
||||||
--git-implementation=libgit2
|
|
||||||
/tmp/flux delete source git podinfo-libgit2 --silent
|
|
||||||
- name: flux get sources git
|
- name: flux get sources git
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux get sources git
|
/tmp/flux get sources git
|
||||||
@@ -147,7 +140,7 @@ jobs:
|
|||||||
--target-namespace=default \
|
--target-namespace=default \
|
||||||
--source=HelmRepository/podinfo.flux-system \
|
--source=HelmRepository/podinfo.flux-system \
|
||||||
--chart=podinfo \
|
--chart=podinfo \
|
||||||
--chart-version=">4.0.0 <5.0.0"
|
--chart-version=">6.0.0 <7.0.0"
|
||||||
- name: flux create helmrelease --source=GitRepository/podinfo
|
- name: flux create helmrelease --source=GitRepository/podinfo
|
||||||
run: |
|
run: |
|
||||||
/tmp/flux create hr podinfo-git \
|
/tmp/flux create hr podinfo-git \
|
||||||
@@ -183,7 +176,7 @@ jobs:
|
|||||||
/tmp/flux push artifact oci://localhost:5000/fluxcd/flux:${{ github.sha }} \
|
/tmp/flux push artifact oci://localhost:5000/fluxcd/flux:${{ github.sha }} \
|
||||||
--path="./manifests" \
|
--path="./manifests" \
|
||||||
--source="${{ github.repositoryUrl }}" \
|
--source="${{ github.repositoryUrl }}" \
|
||||||
--revision="${{ github.ref }}/${{ github.sha }}"
|
--revision="${{ github.ref }}@sha1:${{ github.sha }}"
|
||||||
/tmp/flux tag artifact oci://localhost:5000/fluxcd/flux:${{ github.sha }} \
|
/tmp/flux tag artifact oci://localhost:5000/fluxcd/flux:${{ github.sha }} \
|
||||||
--tag latest
|
--tag latest
|
||||||
/tmp/flux list artifacts oci://localhost:5000/fluxcd/flux
|
/tmp/flux list artifacts oci://localhost:5000/fluxcd/flux
|
||||||
@@ -191,11 +184,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
/tmp/flux create source oci podinfo-oci \
|
/tmp/flux create source oci podinfo-oci \
|
||||||
--url oci://ghcr.io/stefanprodan/manifests/podinfo \
|
--url oci://ghcr.io/stefanprodan/manifests/podinfo \
|
||||||
--tag-semver 6.1.x \
|
--tag-semver 6.3.x \
|
||||||
--interval 10m
|
--interval 10m
|
||||||
/tmp/flux create kustomization podinfo-oci \
|
/tmp/flux create kustomization podinfo-oci \
|
||||||
--source=OCIRepository/podinfo-oci \
|
--source=OCIRepository/podinfo-oci \
|
||||||
--path="./kustomize" \
|
--path="./" \
|
||||||
--prune=true \
|
--prune=true \
|
||||||
--interval=5m \
|
--interval=5m \
|
||||||
--target-namespace=default \
|
--target-namespace=default \
|
||||||
@@ -216,7 +209,7 @@ jobs:
|
|||||||
/tmp/flux -n apps create hr podinfo-helm \
|
/tmp/flux -n apps create hr podinfo-helm \
|
||||||
--source=HelmRepository/podinfo \
|
--source=HelmRepository/podinfo \
|
||||||
--chart=podinfo \
|
--chart=podinfo \
|
||||||
--chart-version="5.0.x" \
|
--chart-version="6.3.x" \
|
||||||
--service-account=dev-team
|
--service-account=dev-team
|
||||||
- name: flux2-kustomize-helm-example
|
- name: flux2-kustomize-helm-example
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
39
.github/workflows/ossf.yaml
vendored
Normal file
39
.github/workflows/ossf.yaml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: ossf
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
schedule:
|
||||||
|
# Weekly on Saturdays.
|
||||||
|
- cron: '30 1 * * 6'
|
||||||
|
|
||||||
|
permissions: read-all
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
scorecard:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
id-token: write
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
- name: Run analysis
|
||||||
|
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
|
||||||
|
with:
|
||||||
|
results_file: results.sarif
|
||||||
|
results_format: sarif
|
||||||
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_results: true
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
|
with:
|
||||||
|
name: SARIF file
|
||||||
|
path: results.sarif
|
||||||
|
retention-days: 5
|
||||||
|
- name: Upload SARIF results
|
||||||
|
uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||||
|
with:
|
||||||
|
sarif_file: results.sarif
|
||||||
112
.github/workflows/release.yaml
vendored
112
.github/workflows/release.yaml
vendored
@@ -9,6 +9,10 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release-flux-cli:
|
release-flux-cli:
|
||||||
|
outputs:
|
||||||
|
hashes: ${{ steps.slsa.outputs.hashes }}
|
||||||
|
image_url: ${{ steps.slsa.outputs.image_url }}
|
||||||
|
image_digest: ${{ steps.slsa.outputs.image_digest }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # needed to write releases
|
contents: write # needed to write releases
|
||||||
@@ -16,32 +20,33 @@ jobs:
|
|||||||
packages: write # needed for ghcr access
|
packages: write # needed for ghcr access
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Unshallow
|
- name: Unshallow
|
||||||
run: git fetch --prune --unshallow
|
run: git fetch --prune --unshallow
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.19.x
|
go-version: 1.20.x
|
||||||
|
cache: false
|
||||||
- name: Setup QEMU
|
- name: Setup QEMU
|
||||||
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2
|
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
|
||||||
- name: Setup Docker Buildx
|
- name: Setup Docker Buildx
|
||||||
id: buildx
|
id: buildx
|
||||||
uses: docker/setup-buildx-action@15c905b16b06416d2086efa066dd8e3a35cc7f98 # v2
|
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||||
- name: Setup Syft
|
- name: Setup Syft
|
||||||
uses: anchore/sbom-action/download-syft@07978da4bdb4faa726e52dfc6b1bed63d4b56479 # v0.13.3
|
uses: anchore/sbom-action/download-syft@5ecf649a417b8ae17dc8383dc32d46c03f2312df # v0.15.1
|
||||||
- name: Setup Cosign
|
- name: Setup Cosign
|
||||||
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
|
uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0
|
||||||
- name: Setup Kustomize
|
- name: Setup Kustomize
|
||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg/actions/kustomize@main
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: fluxcdbot
|
username: fluxcdbot
|
||||||
password: ${{ secrets.GHCR_TOKEN }}
|
password: ${{ secrets.GHCR_TOKEN }}
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||||
with:
|
with:
|
||||||
username: fluxcdbot
|
username: fluxcdbot
|
||||||
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||||
@@ -54,7 +59,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
kustomize build manifests/crds > all-crds.yaml
|
kustomize build manifests/crds > all-crds.yaml
|
||||||
- name: Generate OpenAPI JSON schemas from CRDs
|
- name: Generate OpenAPI JSON schemas from CRDs
|
||||||
uses: fluxcd/pkg//actions/crdjsonschema@main
|
uses: fluxcd/pkg/actions/crdjsonschema@main
|
||||||
with:
|
with:
|
||||||
crd: all-crds.yaml
|
crd: all-crds.yaml
|
||||||
output: schemas
|
output: schemas
|
||||||
@@ -73,7 +78,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@9754a253a8673b0ea869c2e863b4e975497efd0c # v3
|
id: run-goreleaser
|
||||||
|
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
version: latest
|
version: latest
|
||||||
args: release --release-notes=output/notes.md --skip-validate
|
args: release --release-notes=output/notes.md --skip-validate
|
||||||
@@ -81,6 +87,22 @@ jobs:
|
|||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||||
AUR_BOT_SSH_PRIVATE_KEY: ${{ secrets.AUR_BOT_SSH_PRIVATE_KEY }}
|
AUR_BOT_SSH_PRIVATE_KEY: ${{ secrets.AUR_BOT_SSH_PRIVATE_KEY }}
|
||||||
|
- name: Generate SLSA metadata
|
||||||
|
id: slsa
|
||||||
|
env:
|
||||||
|
ARTIFACTS: "${{ steps.run-goreleaser.outputs.artifacts }}"
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
hashes=$(echo -E $ARTIFACTS | jq --raw-output '.[] | {name, "digest": (.extra.Digest // .extra.Checksum)} | select(.digest) | {digest} + {name} | join(" ") | sub("^sha256:";"")' | base64 -w0)
|
||||||
|
echo "hashes=$hashes" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
image_url=fluxcd/flux-cli:$GITHUB_REF_NAME
|
||||||
|
echo "image_url=$image_url" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
image_digest=$(docker buildx imagetools inspect ${image_url} --format '{{json .}}' | jq -r .manifest.digest)
|
||||||
|
echo "image_digest=$image_digest" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
release-flux-manifests:
|
release-flux-manifests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: release-flux-cli
|
needs: release-flux-cli
|
||||||
@@ -88,7 +110,7 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Setup Kustomize
|
- name: Setup Kustomize
|
||||||
uses: fluxcd/pkg/actions/kustomize@main
|
uses: fluxcd/pkg/actions/kustomize@main
|
||||||
- name: Setup Flux CLI
|
- name: Setup Flux CLI
|
||||||
@@ -97,15 +119,15 @@ jobs:
|
|||||||
id: prep
|
id: prep
|
||||||
run: |
|
run: |
|
||||||
VERSION=$(flux version --client | awk '{ print $NF }')
|
VERSION=$(flux version --client | awk '{ print $NF }')
|
||||||
echo ::set-output name=VERSION::${VERSION}
|
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||||
- name: Login to GHCR
|
- name: Login to GHCR
|
||||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: fluxcdbot
|
username: fluxcdbot
|
||||||
password: ${{ secrets.GHCR_TOKEN }}
|
password: ${{ secrets.GHCR_TOKEN }}
|
||||||
- name: Login to DockerHub
|
- name: Login to DockerHub
|
||||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||||
with:
|
with:
|
||||||
username: fluxcdbot
|
username: fluxcdbot
|
||||||
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||||
@@ -117,10 +139,10 @@ jobs:
|
|||||||
--export > ./ghcr.io/flux-system/gotk-components.yaml
|
--export > ./ghcr.io/flux-system/gotk-components.yaml
|
||||||
|
|
||||||
cd ./ghcr.io && flux push artifact \
|
cd ./ghcr.io && flux push artifact \
|
||||||
oci://ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.VERSION }} \
|
oci://ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }} \
|
||||||
--path="./flux-system" \
|
--path="./flux-system" \
|
||||||
--source=${{ github.repositoryUrl }} \
|
--source=${{ github.repositoryUrl }} \
|
||||||
--revision="${{ github.ref_name }}/${{ github.sha }}"
|
--revision="${{ github.ref_name }}@sha1:${{ github.sha }}"
|
||||||
- name: Push manifests to DockerHub
|
- name: Push manifests to DockerHub
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./docker.io/flux-system
|
mkdir -p ./docker.io/flux-system
|
||||||
@@ -129,21 +151,61 @@ jobs:
|
|||||||
--export > ./docker.io/flux-system/gotk-components.yaml
|
--export > ./docker.io/flux-system/gotk-components.yaml
|
||||||
|
|
||||||
cd ./docker.io && flux push artifact \
|
cd ./docker.io && flux push artifact \
|
||||||
oci://docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.VERSION }} \
|
oci://docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }} \
|
||||||
--path="./flux-system" \
|
--path="./flux-system" \
|
||||||
--source=${{ github.repositoryUrl }} \
|
--source=${{ github.repositoryUrl }} \
|
||||||
--revision="${{ github.ref_name }}/${{ github.sha }}"
|
--revision="${{ github.ref_name }}@sha1:${{ github.sha }}"
|
||||||
- uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
|
- uses: sigstore/cosign-installer@1fc5bd396d372bee37d608f955b336615edf79c8 # v3.2.0
|
||||||
- name: Sign manifests
|
- name: Sign manifests
|
||||||
env:
|
env:
|
||||||
COSIGN_EXPERIMENTAL: 1
|
COSIGN_EXPERIMENTAL: 1
|
||||||
run: |
|
run: |
|
||||||
cosign sign ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.VERSION }}
|
cosign sign --yes ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }}
|
||||||
cosign sign docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.VERSION }}
|
cosign sign --yes docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }}
|
||||||
- name: Tag manifests
|
- name: Tag manifests
|
||||||
run: |
|
run: |
|
||||||
flux tag artifact oci://ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.VERSION }} \
|
flux tag artifact oci://ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }} \
|
||||||
--tag latest
|
--tag latest
|
||||||
|
|
||||||
flux tag artifact oci://docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.VERSION }} \
|
flux tag artifact oci://docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }} \
|
||||||
--tag latest
|
--tag latest
|
||||||
|
|
||||||
|
release-provenance:
|
||||||
|
needs: [release-flux-cli]
|
||||||
|
permissions:
|
||||||
|
actions: read # for detecting the Github Actions environment.
|
||||||
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
|
contents: write # for uploading attestations to GitHub releases.
|
||||||
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.9.0
|
||||||
|
with:
|
||||||
|
provenance-name: "provenance.intoto.jsonl"
|
||||||
|
base64-subjects: "${{ needs.release-flux-cli.outputs.hashes }}"
|
||||||
|
upload-assets: true
|
||||||
|
|
||||||
|
dockerhub-provenance:
|
||||||
|
needs: [release-flux-cli]
|
||||||
|
permissions:
|
||||||
|
actions: read # for detecting the Github Actions environment.
|
||||||
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
|
packages: write # for uploading attestations.
|
||||||
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
|
||||||
|
with:
|
||||||
|
image: ${{ needs.release-flux-cli.outputs.image_url }}
|
||||||
|
digest: ${{ needs.release-flux-cli.outputs.image_digest }}
|
||||||
|
registry-username: fluxcdbot
|
||||||
|
secrets:
|
||||||
|
registry-password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||||
|
|
||||||
|
ghcr-provenance:
|
||||||
|
needs: [release-flux-cli]
|
||||||
|
permissions:
|
||||||
|
actions: read # for detecting the Github Actions environment.
|
||||||
|
id-token: write # for creating OIDC tokens for signing.
|
||||||
|
packages: write # for uploading attestations.
|
||||||
|
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
|
||||||
|
with:
|
||||||
|
image: ghcr.io/${{ needs.release-flux-cli.outputs.image_url }}
|
||||||
|
digest: ${{ needs.release-flux-cli.outputs.image_digest }}
|
||||||
|
registry-username: fluxcdbot
|
||||||
|
secrets:
|
||||||
|
registry-password: ${{ secrets.GHCR_TOKEN }}
|
||||||
|
|||||||
52
.github/workflows/scan.yaml
vendored
52
.github/workflows/scan.yaml
vendored
@@ -3,9 +3,9 @@ name: scan
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ 'main', 'release/**' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ 'main', 'release/**' ]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '18 10 * * 3'
|
- cron: '18 10 * * 3'
|
||||||
|
|
||||||
@@ -17,9 +17,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.actor != 'dependabot[bot]'
|
if: github.actor != 'dependabot[bot]'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Run FOSSA scan and upload build data
|
- name: Run FOSSA scan and upload build data
|
||||||
uses: fossa-contrib/fossa-action@6cffaa064112e1cf9b5798c6224f9487dc1ec316 # v1
|
uses: fossa-contrib/fossa-action@cdc5065bcdee31a32e47d4585df72d66e8e941c2 # v3.0.0
|
||||||
with:
|
with:
|
||||||
# FOSSA Push-Only API Token
|
# FOSSA Push-Only API Token
|
||||||
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
|
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
|
||||||
@@ -31,21 +31,29 @@ jobs:
|
|||||||
security-events: write
|
security-events: write
|
||||||
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
|
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Setup Kustomize
|
- name: Setup Kustomize
|
||||||
uses: fluxcd/pkg//actions/kustomize@main
|
uses: fluxcd/pkg/actions/kustomize@main
|
||||||
- name: Build manifests
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
|
with:
|
||||||
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
**/go.mod
|
||||||
|
- name: Download modules and build manifests
|
||||||
run: |
|
run: |
|
||||||
|
make tidy
|
||||||
make cmd/flux/.manifests.done
|
make cmd/flux/.manifests.done
|
||||||
- name: Run Snyk to check for vulnerabilities
|
- uses: snyk/actions/setup@b98d498629f1c368650224d6d212bf7dfa89e4bf
|
||||||
uses: snyk/actions/golang@e25b2e6f5658d1bb7a6671b113260f13134cc3af # v0.3.0
|
- name: Run Snyk to check for vulnerabilities
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
snyk test --sarif-file-output=snyk.sarif
|
||||||
env:
|
env:
|
||||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||||
with:
|
|
||||||
args: --sarif-file-output=snyk.sarif
|
|
||||||
- name: Upload result to GitHub Code Scanning
|
- name: Upload result to GitHub Code Scanning
|
||||||
uses: github/codeql-action/upload-sarif@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
|
uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||||
with:
|
with:
|
||||||
sarif_file: snyk.sarif
|
sarif_file: snyk.sarif
|
||||||
|
|
||||||
@@ -56,16 +64,22 @@ jobs:
|
|||||||
if: github.actor != 'dependabot[bot]'
|
if: github.actor != 'dependabot[bot]'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Set up Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.19.x
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
**/go.mod
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
|
uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||||
with:
|
with:
|
||||||
languages: go
|
languages: go
|
||||||
|
# xref: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||||
|
# xref: https://codeql.github.com/codeql-query-help/go/
|
||||||
|
queries: security-and-quality
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
|
uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@3ebbd71c74ef574dbc558c82f70e52732c8b44fe # v2
|
uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||||
|
|||||||
28
.github/workflows/sync-labels.yaml
vendored
Normal file
28
.github/workflows/sync-labels.yaml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: sync-labels
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- .github/labels.yaml
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
labels:
|
||||||
|
name: Run sync
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
|
- uses: EndBug/label-sync@da00f2c11fdb78e4fae44adac2fdd713778ea3e8 # v2.3.2
|
||||||
|
with:
|
||||||
|
# Configuration file
|
||||||
|
config-file: |
|
||||||
|
https://raw.githubusercontent.com/fluxcd/community/main/.github/standard-labels.yaml
|
||||||
|
.github/labels.yaml
|
||||||
|
# Strictly declarative
|
||||||
|
delete-other-labels: true
|
||||||
25
.github/workflows/update.yaml
vendored
25
.github/workflows/update.yaml
vendored
@@ -18,15 +18,18 @@ jobs:
|
|||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.19.x
|
go-version: 1.20.x
|
||||||
|
cache-dependency-path: |
|
||||||
|
**/go.sum
|
||||||
|
**/go.mod
|
||||||
- name: Update component versions
|
- name: Update component versions
|
||||||
id: update
|
id: update
|
||||||
run: |
|
run: |
|
||||||
PR_BODY=""
|
PR_BODY=$(mktemp)
|
||||||
|
|
||||||
bump_version() {
|
bump_version() {
|
||||||
local LATEST_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
|
local LATEST_VERSION=$(curl -s https://api.github.com/repos/fluxcd/$1/releases | jq -r 'sort_by(.published_at) | .[-1] | .tag_name')
|
||||||
@@ -53,7 +56,8 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$changed" == true ]]; then
|
if [[ "$changed" == true ]]; then
|
||||||
PR_BODY="$PR_BODY- $1 to ${LATEST_VERSION}%0A https://github.com/fluxcd/$1/blob/${LATEST_VERSION}/CHANGELOG.md%0A"
|
echo "- $1 to ${LATEST_VERSION}" >> $PR_BODY
|
||||||
|
echo " https://github.com/fluxcd/$1/blob/${LATEST_VERSION}/CHANGELOG.md" >> $PR_BODY
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,12 +74,17 @@ jobs:
|
|||||||
git diff
|
git diff
|
||||||
|
|
||||||
# export PR_BODY for PR and commit
|
# export PR_BODY for PR and commit
|
||||||
echo "::set-output name=pr_body::$PR_BODY"
|
# NB: this may look strange but it is the way it should be done to
|
||||||
|
# maintain our precious newlines
|
||||||
|
# Ref: https://github.com/github/docs/issues/21529
|
||||||
|
echo 'pr_body<<EOF' >> $GITHUB_OUTPUT
|
||||||
|
cat $PR_BODY >> $GITHUB_OUTPUT
|
||||||
|
echo 'EOF' >> $GITHUB_OUTPUT
|
||||||
}
|
}
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 # v4
|
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||||
commit-message: |
|
commit-message: |
|
||||||
@@ -90,7 +99,7 @@ jobs:
|
|||||||
body: |
|
body: |
|
||||||
${{ steps.update.outputs.pr_body }}
|
${{ steps.update.outputs.pr_body }}
|
||||||
labels: |
|
labels: |
|
||||||
area/build
|
dependencies
|
||||||
reviewers: ${{ secrets.ASSIGNEES }}
|
reviewers: ${{ secrets.ASSIGNEES }}
|
||||||
|
|
||||||
- name: Check output
|
- name: Check output
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ signs:
|
|||||||
certificate: '${artifact}.pem'
|
certificate: '${artifact}.pem'
|
||||||
args:
|
args:
|
||||||
- sign-blob
|
- sign-blob
|
||||||
|
- "--yes"
|
||||||
- '--output-certificate=${certificate}'
|
- '--output-certificate=${certificate}'
|
||||||
- '--output-signature=${signature}'
|
- '--output-signature=${signature}'
|
||||||
- '${artifact}'
|
- '${artifact}'
|
||||||
@@ -82,14 +83,7 @@ brews:
|
|||||||
install: |
|
install: |
|
||||||
bin.install "flux"
|
bin.install "flux"
|
||||||
|
|
||||||
bash_output = Utils.safe_popen_read(bin/"flux", "completion", "bash")
|
generate_completions_from_executable(bin/"flux", "completion")
|
||||||
(bash_completion/"flux").write bash_output
|
|
||||||
|
|
||||||
zsh_output = Utils.safe_popen_read(bin/"flux", "completion", "zsh")
|
|
||||||
(zsh_completion/"_flux").write zsh_output
|
|
||||||
|
|
||||||
fish_output = Utils.safe_popen_read(bin/"flux", "completion", "fish")
|
|
||||||
(fish_completion/"flux.fish").write fish_output
|
|
||||||
test: |
|
test: |
|
||||||
system "#{bin}/flux --version"
|
system "#{bin}/flux --version"
|
||||||
publishers:
|
publishers:
|
||||||
@@ -175,6 +169,7 @@ docker_signs:
|
|||||||
- COSIGN_EXPERIMENTAL=1
|
- COSIGN_EXPERIMENTAL=1
|
||||||
args:
|
args:
|
||||||
- sign
|
- sign
|
||||||
|
- "--yes"
|
||||||
- '${artifact}'
|
- '${artifact}'
|
||||||
artifacts: all
|
artifacts: all
|
||||||
output: true
|
output: true
|
||||||
|
|||||||
@@ -67,9 +67,9 @@ for source changes.
|
|||||||
|
|
||||||
Prerequisites:
|
Prerequisites:
|
||||||
|
|
||||||
* go >= 1.19
|
* go >= 1.20
|
||||||
* kubectl >= 1.20
|
* kubectl >= 1.24
|
||||||
* kustomize >= 4.4
|
* kustomize >= 5.0
|
||||||
* coreutils (on Mac OS)
|
* coreutils (on Mac OS)
|
||||||
|
|
||||||
Install the [controller-runtime/envtest](https://github.com/kubernetes-sigs/controller-runtime/tree/master/tools/setup-envtest) binaries with:
|
Install the [controller-runtime/envtest](https://github.com/kubernetes-sigs/controller-runtime/tree/master/tools/setup-envtest) binaries with:
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
FROM alpine:3.17 as builder
|
FROM alpine:3.19 as builder
|
||||||
|
|
||||||
RUN apk add --no-cache ca-certificates curl
|
RUN apk add --no-cache ca-certificates curl
|
||||||
|
|
||||||
ARG ARCH=linux/amd64
|
ARG ARCH=linux/amd64
|
||||||
ARG KUBECTL_VER=1.26.1
|
ARG KUBECTL_VER=1.28.4
|
||||||
|
|
||||||
RUN curl -sL https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \
|
RUN curl -sL https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/${ARCH}/kubectl \
|
||||||
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl && \
|
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl && \
|
||||||
kubectl version --client=true
|
kubectl version --client=true
|
||||||
|
|
||||||
FROM alpine:3.17 as flux-cli
|
FROM alpine:3.19 as flux-cli
|
||||||
|
|
||||||
RUN apk add --no-cache ca-certificates
|
RUN apk add --no-cache ca-certificates
|
||||||
|
|
||||||
|
|||||||
5
Makefile
5
Makefile
@@ -17,8 +17,9 @@ rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2
|
|||||||
all: test build
|
all: test build
|
||||||
|
|
||||||
tidy:
|
tidy:
|
||||||
go mod tidy -compat=1.19
|
go mod tidy -compat=1.20
|
||||||
cd tests/azure && go mod tidy -compat=1.19
|
cd tests/azure && go mod tidy -compat=1.20
|
||||||
|
cd tests/integration && go mod tidy -compat=1.20
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
go fmt ./...
|
go fmt ./...
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
[](https://github.com/fluxcd/flux2/releases)
|
[](https://github.com/fluxcd/flux2/releases)
|
||||||
[](https://bestpractices.coreinfrastructure.org/projects/4782)
|
[](https://bestpractices.coreinfrastructure.org/projects/4782)
|
||||||
|
[](https://api.securityscorecards.dev/projects/github.com/fluxcd/flux2)
|
||||||
[](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Ffluxcd%2Fflux2?ref=badge_shield)
|
[](https://app.fossa.com/projects/custom%2B162%2Fgithub.com%2Ffluxcd%2Fflux2?ref=badge_shield)
|
||||||
[](https://artifacthub.io/packages/helm/fluxcd-community/flux2)
|
[](https://artifacthub.io/packages/helm/fluxcd-community/flux2)
|
||||||
|
[](https://fluxcd.io/flux/security/slsa-assessment)
|
||||||
|
|
||||||
Flux is a tool for keeping Kubernetes clusters in sync with sources of
|
Flux is a tool for keeping Kubernetes clusters in sync with sources of
|
||||||
configuration (like Git repositories and OCI artifacts),
|
configuration (like Git repositories and OCI artifacts),
|
||||||
@@ -31,7 +33,7 @@ For more comprehensive documentation, see the following guides:
|
|||||||
- [Ways of structuring your repositories](https://fluxcd.io/flux/guides/repository-structure/)
|
- [Ways of structuring your repositories](https://fluxcd.io/flux/guides/repository-structure/)
|
||||||
- [Manage Helm Releases](https://fluxcd.io/flux/guides/helmreleases/)
|
- [Manage Helm Releases](https://fluxcd.io/flux/guides/helmreleases/)
|
||||||
- [Automate image updates to Git](https://fluxcd.io/flux/guides/image-update/)
|
- [Automate image updates to Git](https://fluxcd.io/flux/guides/image-update/)
|
||||||
- [Manage Kubernetes secrets with Mozilla SOPS](https://fluxcd.io/flux/guides/mozilla-sops/)
|
- [Manage Kubernetes secrets with Flux and SOPS](https://fluxcd.io/flux/guides/mozilla-sops/)
|
||||||
|
|
||||||
If you need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
If you need help, please refer to our **[Support page](https://fluxcd.io/support/)**.
|
||||||
|
|
||||||
@@ -42,7 +44,7 @@ runtime for Flux v2. The APIs comprise Kubernetes custom resources,
|
|||||||
which can be created and updated by a cluster user, or by other
|
which can be created and updated by a cluster user, or by other
|
||||||
automation tooling.
|
automation tooling.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
You can use the toolkit to extend Flux, or to build your own systems
|
You can use the toolkit to extend Flux, or to build your own systems
|
||||||
for continuous delivery -- see [the developer
|
for continuous delivery -- see [the developer
|
||||||
@@ -57,18 +59,18 @@ guides](https://fluxcd.io/flux/gitops-toolkit/source-watcher/).
|
|||||||
- [HelmChart CRD](https://fluxcd.io/flux/components/source/helmcharts/)
|
- [HelmChart CRD](https://fluxcd.io/flux/components/source/helmcharts/)
|
||||||
- [Bucket CRD](https://fluxcd.io/flux/components/source/buckets/)
|
- [Bucket CRD](https://fluxcd.io/flux/components/source/buckets/)
|
||||||
- [Kustomize Controller](https://fluxcd.io/flux/components/kustomize/)
|
- [Kustomize Controller](https://fluxcd.io/flux/components/kustomize/)
|
||||||
- [Kustomization CRD](https://fluxcd.io/flux/components/kustomize/kustomization/)
|
- [Kustomization CRD](https://fluxcd.io/flux/components/kustomize/kustomizations/)
|
||||||
- [Helm Controller](https://fluxcd.io/flux/components/helm/)
|
- [Helm Controller](https://fluxcd.io/flux/components/helm/)
|
||||||
- [HelmRelease CRD](https://fluxcd.io/flux/components/helm/helmreleases/)
|
- [HelmRelease CRD](https://fluxcd.io/flux/components/helm/helmreleases/)
|
||||||
- [Notification Controller](https://fluxcd.io/flux/components/notification/)
|
- [Notification Controller](https://fluxcd.io/flux/components/notification/)
|
||||||
- [Provider CRD](https://fluxcd.io/flux/components/notification/provider/)
|
- [Provider CRD](https://fluxcd.io/flux/components/notification/providers/)
|
||||||
- [Alert CRD](https://fluxcd.io/flux/components/notification/alert/)
|
- [Alert CRD](https://fluxcd.io/flux/components/notification/alerts/)
|
||||||
- [Receiver CRD](https://fluxcd.io/flux/components/notification/receiver/)
|
- [Receiver CRD](https://fluxcd.io/flux/components/notification/receivers/)
|
||||||
- [Image Automation Controllers](https://fluxcd.io/flux/components/image/)
|
- [Image Automation Controllers](https://fluxcd.io/flux/components/image/)
|
||||||
- [ImageRepository CRD](https://fluxcd.io/flux/components/image/imagerepositories/)
|
- [ImageRepository CRD](https://fluxcd.io/flux/components/image/imagerepositories/)
|
||||||
- [ImagePolicy CRD](https://fluxcd.io/flux/components/image/imagepolicies/)
|
- [ImagePolicy CRD](https://fluxcd.io/flux/components/image/imagepolicies/)
|
||||||
- [ImageUpdateAutomation CRD](https://fluxcd.io/flux/components/image/imageupdateautomations/)
|
- [ImageUpdateAutomation CRD](https://fluxcd.io/flux/components/image/imageupdateautomations/)
|
||||||
|
|
||||||
## Community
|
## Community
|
||||||
|
|
||||||
Need help or want to contribute? Please see the links below. The Flux project is always looking for
|
Need help or want to contribute? Please see the links below. The Flux project is always looking for
|
||||||
|
|||||||
216
action/README.md
216
action/README.md
@@ -1,212 +1,22 @@
|
|||||||
# Flux GitHub Action
|
# Flux GitHub Action
|
||||||
|
|
||||||
Usage:
|
To install the latest Flux CLI on Linux, macOS or Windows GitHub runners:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Flux CLI
|
- name: Setup Flux CLI
|
||||||
uses: fluxcd/flux2/action@main
|
uses: fluxcd/flux2/action@main
|
||||||
- name: Run Flux commands
|
with:
|
||||||
run: flux -v
|
version: 'latest'
|
||||||
|
- name: Run Flux CLI
|
||||||
|
run: flux version --client
|
||||||
```
|
```
|
||||||
|
|
||||||
The latest stable version of the `flux` binary is downloaded from
|
The Flux GitHub Action can be used to automate various tasks in CI, such as:
|
||||||
GitHub [releases](https://github.com/fluxcd/flux2/releases)
|
|
||||||
and placed at `/usr/local/bin/flux`.
|
|
||||||
|
|
||||||
Note that this action can only be used on GitHub **Linux** runners.
|
- [Automate Flux upgrades on clusters via Pull Requests](https://fluxcd.io/flux/flux-gh-action/#automate-flux-updates)
|
||||||
You can change the arch (defaults to `amd64`) with:
|
- [Push Kubernetes manifests to container registries](https://fluxcd.io/flux/flux-gh-action/#push-kubernetes-manifests-to-container-registries)
|
||||||
|
- [Run end-to-end testing with Flux and Kubernetes Kind](https://fluxcd.io/flux/flux-gh-action/#end-to-end-testing)
|
||||||
|
|
||||||
```yaml
|
For more information, please see the [Flux GitHub Action documentation](https://fluxcd.io/flux/flux-gh-action/).
|
||||||
steps:
|
|
||||||
- name: Setup Flux CLI
|
|
||||||
uses: fluxcd/flux2/action@main
|
|
||||||
with:
|
|
||||||
arch: arm64 # can be amd64, arm64 or arm
|
|
||||||
```
|
|
||||||
|
|
||||||
You can download a specific version with:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
steps:
|
|
||||||
- name: Setup Flux CLI
|
|
||||||
uses: fluxcd/flux2/action@main
|
|
||||||
with:
|
|
||||||
version: 0.32.0
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also authenticate against the GitHub API using GitHub Actions' `GITHUB_TOKEN` secret.
|
|
||||||
|
|
||||||
For more information, please [read about the GitHub token secret](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret).
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
steps:
|
|
||||||
- name: Setup Flux CLI
|
|
||||||
uses: fluxcd/flux2/action@main
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is useful if you are seeing failures on shared runners, those failures are usually API limits being hit.
|
|
||||||
|
|
||||||
### Automate Flux updates
|
|
||||||
|
|
||||||
Example workflow for updating Flux's components generated with `flux bootstrap --path=clusters/production`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: update-flux
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 * * * *"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
components:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check out code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Flux CLI
|
|
||||||
uses: fluxcd/flux2/action@main
|
|
||||||
- name: Check for updates
|
|
||||||
id: update
|
|
||||||
run: |
|
|
||||||
flux install \
|
|
||||||
--export > ./clusters/production/flux-system/gotk-components.yaml
|
|
||||||
|
|
||||||
VERSION="$(flux -v)"
|
|
||||||
echo "flux_version=$VERSION" >> $GITHUB_OUTPUT
|
|
||||||
- name: Create Pull Request
|
|
||||||
uses: peter-evans/create-pull-request@v4
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
branch: update-flux
|
|
||||||
commit-message: Update to ${{ steps.update.outputs.flux_version }}
|
|
||||||
title: Update to ${{ steps.update.outputs.flux_version }}
|
|
||||||
body: |
|
|
||||||
${{ steps.update.outputs.flux_version }}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Push Kubernetes manifests to container registries
|
|
||||||
|
|
||||||
Example workflow for publishing Kubernetes manifests bundled as OCI artifacts to GitHub Container Registry:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: push-artifact-staging
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
packages: write # needed for ghcr.io access
|
|
||||||
|
|
||||||
env:
|
|
||||||
OCI_REPO: "oci://ghcr.io/my-org/manifests/${{ github.event.repository.name }}"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
kubernetes:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Flux CLI
|
|
||||||
uses: fluxcd/flux2/action@main
|
|
||||||
- name: Login to GHCR
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Generate manifests
|
|
||||||
run: |
|
|
||||||
kustomize build ./manifests/staging > ./deploy/app.yaml
|
|
||||||
- name: Push manifests
|
|
||||||
run: |
|
|
||||||
flux push artifact $OCI_REPO:$(git rev-parse --short HEAD) \
|
|
||||||
--path="./deploy" \
|
|
||||||
--source="$(git config --get remote.origin.url)" \
|
|
||||||
--revision="$(git branch --show-current)/$(git rev-parse HEAD)"
|
|
||||||
- name: Deploy manifests to staging
|
|
||||||
run: |
|
|
||||||
flux tag artifact $OCI_REPO:$(git rev-parse --short HEAD) --tag staging
|
|
||||||
```
|
|
||||||
|
|
||||||
### Push and sign Kubernetes manifests to container registries
|
|
||||||
|
|
||||||
Example workflow for publishing Kubernetes manifests bundled as OCI artifacts
|
|
||||||
which are signed with Cosign and GitHub OIDC:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: push-sign-artifact
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'main'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
packages: write # needed for ghcr.io access
|
|
||||||
id-token: write # needed for keyless signing
|
|
||||||
|
|
||||||
env:
|
|
||||||
OCI_REPO: "oci://ghcr.io/my-org/manifests/${{ github.event.repository.name }}"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
kubernetes:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Flux CLI
|
|
||||||
uses: fluxcd/flux2/action@main
|
|
||||||
- name: Setup Cosign
|
|
||||||
uses: sigstore/cosign-installer@main
|
|
||||||
- name: Login to GHCR
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
- name: Push and sign manifests
|
|
||||||
run: |
|
|
||||||
digest_url=$(flux push artifact \
|
|
||||||
$OCI_REPO:$(git rev-parse --short HEAD) \
|
|
||||||
--path="./manifests" \
|
|
||||||
--source="$(git config --get remote.origin.url)" \
|
|
||||||
--revision="$(git branch --show-current)/$(git rev-parse HEAD)" |\
|
|
||||||
jq -r '. | .repository + "@" + .digest')
|
|
||||||
|
|
||||||
cosign sign $digest_url
|
|
||||||
```
|
|
||||||
|
|
||||||
### End-to-end testing
|
|
||||||
|
|
||||||
Example workflow for running Flux in Kubernetes Kind:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: e2e
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
kubernetes:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Flux CLI
|
|
||||||
uses: fluxcd/flux2/action@main
|
|
||||||
- name: Setup Kubernetes Kind
|
|
||||||
uses: engineerd/setup-kind@v0.5.0
|
|
||||||
- name: Install Flux in Kubernetes Kind
|
|
||||||
run: flux install
|
|
||||||
```
|
|
||||||
|
|
||||||
A complete e2e testing workflow is available here
|
|
||||||
[flux2-kustomize-helm-example](https://github.com/fluxcd/flux2-kustomize-helm-example/blob/main/.github/workflows/e2e.yaml)
|
|
||||||
|
|||||||
@@ -1,64 +1,120 @@
|
|||||||
name: Setup Flux CLI
|
name: Setup Flux CLI
|
||||||
description: A GitHub Action for running Flux commands
|
description: A GitHub Action for installing the Flux CLI
|
||||||
author: Stefan Prodan
|
author: Flux project
|
||||||
branding:
|
branding:
|
||||||
color: blue
|
color: blue
|
||||||
icon: command
|
icon: command
|
||||||
inputs:
|
inputs:
|
||||||
version:
|
version:
|
||||||
description: "Flux version e.g. 0.8.0 (defaults to latest stable release)"
|
description: "Flux version e.g. 2.0.0 (defaults to latest stable release)"
|
||||||
required: false
|
required: false
|
||||||
arch:
|
arch:
|
||||||
description: "arch can be amd64, arm64 or arm"
|
description: "arch can be amd64, arm64 or arm"
|
||||||
required: true
|
required: false
|
||||||
default: "amd64"
|
deprecationMessage: "No longer required, action will now detect runner arch."
|
||||||
bindir:
|
bindir:
|
||||||
description: "Optional location of the Flux binary. Will not use sudo if set. Updates System Path."
|
description: "Alternative location for the Flux binary, defaults to path relative to $RUNNER_TOOL_CACHE."
|
||||||
required: false
|
required: false
|
||||||
token:
|
token:
|
||||||
description: "GitHub Token used to authentication against the API (generally only needed to prevent quota limit errors)"
|
description: "Token used to authentication against the GitHub.com API. Defaults to the token from the GitHub context of the workflow."
|
||||||
required: false
|
required: false
|
||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- name: "Download flux binary to tmp"
|
- name: "Download the binary to the runner's cache dir"
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
ARCH=${{ inputs.arch }}
|
|
||||||
VERSION=${{ inputs.version }}
|
VERSION=${{ inputs.version }}
|
||||||
TOKEN=${{ inputs.token }}
|
|
||||||
|
|
||||||
if [ -z "${VERSION}" ]; then
|
TOKEN=${{ inputs.token }}
|
||||||
if [ -n "${TOKEN}" ]; then
|
if [[ -z "$TOKEN" ]]; then
|
||||||
VERSION_SLUG=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest --silent --location --header "Authorization: token ${TOKEN}" | grep tag_name)
|
TOKEN=${{ github.token }}
|
||||||
else
|
fi
|
||||||
# With no GITHUB_TOKEN you will experience occasional failures due to rate limiting
|
|
||||||
# Ref: https://github.com/fluxcd/flux2/issues/3509#issuecomment-1400820992
|
if [[ -z "$VERSION" ]] || [[ "$VERSION" = "latest" ]]; then
|
||||||
VERSION_SLUG=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest --silent --location | grep tag_name)
|
VERSION=$(curl -fsSL -H "Authorization: token ${TOKEN}" https://api.github.com/repos/fluxcd/flux2/releases/latest | grep tag_name | cut -d '"' -f 4)
|
||||||
|
fi
|
||||||
|
if [[ -z "$VERSION" ]]; then
|
||||||
|
echo "Unable to determine Flux CLI version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ $VERSION = v* ]]; then
|
||||||
|
VERSION="${VERSION:1}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
OS=$(echo "${RUNNER_OS}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
if [[ "$OS" == "macos" ]]; then
|
||||||
|
OS="darwin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARCH=$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
if [[ "$ARCH" == "x64" ]]; then
|
||||||
|
ARCH="amd64"
|
||||||
|
elif [[ "$ARCH" == "x86" ]]; then
|
||||||
|
ARCH="386"
|
||||||
|
fi
|
||||||
|
|
||||||
|
FLUX_EXEC_FILE="flux"
|
||||||
|
if [[ "$OS" == "windows" ]]; then
|
||||||
|
FLUX_EXEC_FILE="${FLUX_EXEC_FILE}.exe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
FLUX_TOOL_DIR=${{ inputs.bindir }}
|
||||||
|
if [[ -z "$FLUX_TOOL_DIR" ]]; then
|
||||||
|
FLUX_TOOL_DIR="${RUNNER_TOOL_CACHE}/flux2/${VERSION}/${OS}/${ARCH}"
|
||||||
|
fi
|
||||||
|
if [[ ! -x "$FLUX_TOOL_DIR/FLUX_EXEC_FILE" ]]; then
|
||||||
|
DL_DIR="$(mktemp -dt flux2-XXXXXX)"
|
||||||
|
trap 'rm -rf $DL_DIR' EXIT
|
||||||
|
|
||||||
|
echo "Downloading flux ${VERSION} for ${OS}/${ARCH}"
|
||||||
|
FLUX_TARGET_FILE="flux_${VERSION}_${OS}_${ARCH}.tar.gz"
|
||||||
|
if [[ "$OS" == "windows" ]]; then
|
||||||
|
FLUX_TARGET_FILE="flux_${VERSION}_${OS}_${ARCH}.zip"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERSION=$(echo "${VERSION_SLUG}" | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
|
FLUX_CHECKSUMS_FILE="flux_${VERSION}_checksums.txt"
|
||||||
|
|
||||||
|
FLUX_DOWNLOAD_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/"
|
||||||
|
|
||||||
|
curl -fsSL -o "$DL_DIR/$FLUX_TARGET_FILE" "$FLUX_DOWNLOAD_URL/$FLUX_TARGET_FILE"
|
||||||
|
curl -fsSL -o "$DL_DIR/$FLUX_CHECKSUMS_FILE" "$FLUX_DOWNLOAD_URL/$FLUX_CHECKSUMS_FILE"
|
||||||
|
|
||||||
|
echo "Verifying checksum"
|
||||||
|
sum=""
|
||||||
|
if command -v openssl > /dev/null; then
|
||||||
|
sum=$(openssl sha256 "$DL_DIR/$FLUX_TARGET_FILE" | awk '{print $2}')
|
||||||
|
elif command -v sha256sum > /dev/null; then
|
||||||
|
sum=$(sha256sum "$DL_DIR/$FLUX_TARGET_FILE" | awk '{print $1}')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$sum" ]]; then
|
||||||
|
echo "Neither openssl nor sha256sum found. Cannot calculate checksum."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
expected_sum=$(grep " $FLUX_TARGET_FILE\$" "$DL_DIR/$FLUX_CHECKSUMS_FILE" | awk '{print $1}')
|
||||||
|
if [ "$sum" != "$expected_sum" ]; then
|
||||||
|
echo "SHA sum of ${FLUX_TARGET_FILE} does not match. Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Installing flux to ${FLUX_TOOL_DIR}"
|
||||||
|
mkdir -p "$FLUX_TOOL_DIR"
|
||||||
|
|
||||||
|
if [[ "$OS" == "windows" ]]; then
|
||||||
|
unzip "$DL_DIR/$FLUX_TARGET_FILE" "$FLUX_EXEC_FILE" -d "$FLUX_TOOL_DIR"
|
||||||
|
else
|
||||||
|
tar xzf "$DL_DIR/$FLUX_TARGET_FILE" -C "$FLUX_TOOL_DIR" $FLUX_EXEC_FILE
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "$FLUX_TOOL_DIR/$FLUX_EXEC_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_${ARCH}.tar.gz"
|
echo "Adding flux to path"
|
||||||
curl --silent --fail --location "${BIN_URL}" --output /tmp/flux.tar.gz
|
echo "$FLUX_TOOL_DIR" >> "$GITHUB_PATH"
|
||||||
mkdir -p /tmp/flux
|
|
||||||
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz
|
- name: "Print installed flux version"
|
||||||
- name: "Copy Flux binary to execute location"
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
BINDIR=${{ inputs.bindir }}
|
|
||||||
if [ -z "${BINDIR}" ]; then
|
|
||||||
sudo cp /tmp/flux/flux /usr/local/bin
|
|
||||||
else
|
|
||||||
cp /tmp/flux/flux "${BINDIR}"
|
|
||||||
echo "${BINDIR}" >> $GITHUB_PATH
|
|
||||||
fi
|
|
||||||
- name: "Cleanup tmp"
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
rm -rf /tmp/flux/ /tmp/flux.tar.gz
|
|
||||||
- name: "Verify correct installation of binary"
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
flux -v
|
flux -v
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// notificationv1.Alert
|
// notificationv1.Alert
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// notificationv1.Provider
|
// notificationv1.Provider
|
||||||
|
|||||||
@@ -17,27 +17,31 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/manifoldco/promptui"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapCmd = &cobra.Command{
|
var bootstrapCmd = &cobra.Command{
|
||||||
Use: "bootstrap",
|
Use: "bootstrap",
|
||||||
Short: "Bootstrap toolkit components",
|
Short: "Deploy Flux on a cluster the GitOps way.",
|
||||||
Long: "The bootstrap sub-commands bootstrap the toolkit components on the targeted Git provider.",
|
Long: `The bootstrap sub-commands push the Flux manifests to a Git repository
|
||||||
|
and deploy Flux on the cluster.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
type bootstrapFlags struct {
|
type bootstrapFlags struct {
|
||||||
version string
|
version string
|
||||||
arch flags.Arch
|
|
||||||
logLevel flags.LogLevel
|
logLevel flags.LogLevel
|
||||||
|
|
||||||
branch string
|
branch string
|
||||||
@@ -72,6 +76,8 @@ type bootstrapFlags struct {
|
|||||||
gpgPassphrase string
|
gpgPassphrase string
|
||||||
gpgKeyID string
|
gpgKeyID string
|
||||||
|
|
||||||
|
force bool
|
||||||
|
|
||||||
commitMessageAppendix string
|
commitMessageAppendix string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,9 +97,9 @@ func init() {
|
|||||||
"list of components in addition to those supplied or defaulted, accepts values such as 'image-reflector-controller,image-automation-controller'")
|
"list of components in addition to those supplied or defaulted, accepts values such as 'image-reflector-controller,image-automation-controller'")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.registry, "registry", "ghcr.io/fluxcd",
|
||||||
"container registry where the toolkit images are published")
|
"container registry where the Flux controller images are published")
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.imagePullSecret, "image-pull-secret", "",
|
||||||
"Kubernetes secret name used for pulling the toolkit images from a private registry")
|
"Kubernetes secret name used for pulling the controller images from a private registry")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch, "Git branch")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.branch, "branch", bootstrapDefaultBranch, "Git branch")
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.recurseSubmodules, "recurse-submodules", false,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.recurseSubmodules, "recurse-submodules", false,
|
||||||
@@ -102,15 +108,15 @@ func init() {
|
|||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.manifestsPath, "manifests", "", "path to the manifest directory")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.watchAllNamespaces, "watch-all-namespaces", true,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.watchAllNamespaces, "watch-all-namespaces", true,
|
||||||
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the toolkit is installed")
|
"watch for custom resources in all namespaces, if set to false it will only watch the namespace where the Flux controllers are installed")
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.networkPolicy, "network-policy", true,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.networkPolicy, "network-policy", true,
|
||||||
"deny ingress access to the toolkit controllers from other namespaces using network policies")
|
"setup Kubernetes network policies to deny ingress access to the Flux controllers from other namespaces")
|
||||||
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false,
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.tokenAuth, "token-auth", false,
|
||||||
"when enabled, the personal access token will be used instead of SSH deploy key")
|
"when enabled, the personal access token will be used instead of the SSH deploy key")
|
||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description())
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.logLevel, "log-level", bootstrapArgs.logLevel.Description())
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.clusterDomain, "cluster-domain", rootArgs.defaults.ClusterDomain, "internal cluster domain")
|
||||||
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
|
bootstrapCmd.PersistentFlags().StringSliceVar(&bootstrapArgs.tolerationKeys, "toleration-keys", nil,
|
||||||
"list of toleration keys used to schedule the components pods onto nodes with matching taints")
|
"list of toleration keys used to schedule the controller pods onto nodes with matching taints")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.secretName, "secret-name", rootArgs.defaults.Namespace, "name of the secret the sync credentials can be found in or stored to")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.secretName, "secret-name", rootArgs.defaults.Namespace, "name of the secret the sync credentials can be found in or stored to")
|
||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.keyAlgorithm, "ssh-key-algorithm", bootstrapArgs.keyAlgorithm.Description())
|
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.keyAlgorithm, "ssh-key-algorithm", bootstrapArgs.keyAlgorithm.Description())
|
||||||
@@ -129,8 +135,7 @@ func init() {
|
|||||||
|
|
||||||
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.commitMessageAppendix, "commit-message-appendix", "", "string to add to the commit messages, e.g. '[ci skip]'")
|
bootstrapCmd.PersistentFlags().StringVar(&bootstrapArgs.commitMessageAppendix, "commit-message-appendix", "", "string to add to the commit messages, e.g. '[ci skip]'")
|
||||||
|
|
||||||
bootstrapCmd.PersistentFlags().Var(&bootstrapArgs.arch, "arch", bootstrapArgs.arch.Description())
|
bootstrapCmd.PersistentFlags().BoolVar(&bootstrapArgs.force, "force", false, "override existing Flux installation if it's managed by a diffrent tool such as Helm")
|
||||||
bootstrapCmd.PersistentFlags().MarkDeprecated("arch", "multi-arch container image is now available for AMD64, ARMv7 and ARM64")
|
|
||||||
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
||||||
|
|
||||||
rootCmd.AddCommand(bootstrapCmd)
|
rootCmd.AddCommand(bootstrapCmd)
|
||||||
@@ -190,3 +195,27 @@ func mapTeamSlice(s []string, defaultPermission string) map[string]string {
|
|||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// confirmBootstrap gets a confirmation for running bootstrap over an existing Flux installation.
|
||||||
|
// It returns a nil error if Flux is not installed or the user confirms overriding an existing installation
|
||||||
|
func confirmBootstrap(ctx context.Context, kubeClient client.Client) error {
|
||||||
|
installed := true
|
||||||
|
info, err := getFluxClusterInfo(ctx, kubeClient)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.IsNotFound(err) {
|
||||||
|
return fmt.Errorf("cluster info unavailable: %w", err)
|
||||||
|
}
|
||||||
|
installed = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if installed {
|
||||||
|
err = confirmFluxInstallOverride(info)
|
||||||
|
if err != nil {
|
||||||
|
if err == promptui.ErrAbort {
|
||||||
|
return fmt.Errorf("bootstrap cancelled")
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,41 +26,38 @@ import (
|
|||||||
"github.com/fluxcd/pkg/git/gogit"
|
"github.com/fluxcd/pkg/git/gogit"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/bootstrap"
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap"
|
||||||
"github.com/fluxcd/flux2/pkg/bootstrap/provider"
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap/provider"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapBServerCmd = &cobra.Command{
|
var bootstrapBServerCmd = &cobra.Command{
|
||||||
Use: "bitbucket-server",
|
Use: "bitbucket-server",
|
||||||
Short: "Bootstrap toolkit components in a Bitbucket Server repository",
|
Short: "Deploy Flux on a cluster connected to a Bitbucket Server repository",
|
||||||
Long: `The bootstrap bitbucket-server command creates the Bitbucket Server repository if it doesn't exists and
|
Long: `The bootstrap bitbucket-server command creates the Bitbucket Server repository if it doesn't exists and
|
||||||
commits the toolkit components manifests to the master branch.
|
commits the Flux manifests to the master branch.
|
||||||
Then it configures the target cluster to synchronize with the repository.
|
Then it configures the target cluster to synchronize with the repository.
|
||||||
If the toolkit components are present on the cluster,
|
If the Flux components are present on the cluster,
|
||||||
the bootstrap command will perform an upgrade if needed.`,
|
the bootstrap command will perform an upgrade if needed.`,
|
||||||
Example: ` # Create a Bitbucket Server API token and export it as an env var
|
Example: ` # Create a Bitbucket Server API token and export it as an env var
|
||||||
export BITBUCKET_TOKEN=<my-token>
|
export BITBUCKET_TOKEN=<my-token>
|
||||||
|
|
||||||
# Run bootstrap for a private repository using HTTPS token authentication
|
# Run bootstrap for a private repository using HTTPS token authentication
|
||||||
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --hostname=<domain> --token-auth
|
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --hostname=<domain> --token-auth --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a private repository using SSH authentication
|
# Run bootstrap for a private repository using SSH authentication
|
||||||
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --hostname=<domain>
|
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --hostname=<domain> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a repository path
|
|
||||||
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --path=dev-cluster --hostname=<domain>
|
|
||||||
|
|
||||||
# Run bootstrap for a public repository on a personal account
|
# Run bootstrap for a public repository on a personal account
|
||||||
flux bootstrap bitbucket-server --owner=<user> --repository=<repository name> --private=false --personal --hostname=<domain> --token-auth
|
flux bootstrap bitbucket-server --owner=<user> --repository=<repository name> --private=false --personal --hostname=<domain> --token-auth --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a an existing repository with a branch named main
|
# Run bootstrap for an existing repository with a branch named main
|
||||||
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --branch=main --hostname=<domain> --token-auth`,
|
flux bootstrap bitbucket-server --owner=<project> --username=<user> --repository=<repository name> --branch=main --hostname=<domain> --token-auth --path=clusters/my-cluster`,
|
||||||
RunE: bootstrapBServerCmdRun,
|
RunE: bootstrapBServerCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +124,13 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !bootstrapArgs.force {
|
||||||
|
err = confirmBootstrap(ctx, kubeClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Manifest base
|
// Manifest base
|
||||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -247,7 +251,6 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Secret: bootstrapArgs.secretName,
|
Secret: bootstrapArgs.secretName,
|
||||||
TargetPath: bServerArgs.path.ToSlash(),
|
TargetPath: bServerArgs.path.ToSlash(),
|
||||||
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
GitImplementation: sourceGitArgs.gitImplementation.String(),
|
|
||||||
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,44 +28,44 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/bootstrap"
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sync"
|
||||||
"github.com/fluxcd/pkg/git"
|
"github.com/fluxcd/pkg/git"
|
||||||
"github.com/fluxcd/pkg/git/gogit"
|
"github.com/fluxcd/pkg/git/gogit"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitCmd = &cobra.Command{
|
var bootstrapGitCmd = &cobra.Command{
|
||||||
Use: "git",
|
Use: "git",
|
||||||
Short: "Bootstrap toolkit components in a Git repository",
|
Short: "Deploy Flux on a cluster connected to a Git repository",
|
||||||
Long: `The bootstrap git command commits the toolkit components manifests to the
|
Long: `The bootstrap git command commits the Flux manifests to the
|
||||||
branch of a Git repository. It then configures the target cluster to synchronize with
|
branch of a Git repository. And then it configures the target cluster to synchronize with
|
||||||
the repository. If the toolkit components are present on the cluster, the bootstrap
|
that repository. If the Flux components are present on the cluster, the bootstrap
|
||||||
command will perform an upgrade if needed.`,
|
command will perform an upgrade if needed.`,
|
||||||
Example: ` # Run bootstrap for a Git repository and authenticate with your SSH agent
|
Example: ` # Run bootstrap for a Git repository and authenticate with your SSH agent
|
||||||
flux bootstrap git --url=ssh://git@example.com/repository.git
|
flux bootstrap git --url=ssh://git@example.com/repository.git --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a Git repository and authenticate using a password
|
# Run bootstrap for a Git repository and authenticate using a password
|
||||||
flux bootstrap git --url=https://example.com/repository.git --password=<password>
|
flux bootstrap git --url=https://example.com/repository.git --password=<password> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a Git repository and authenticate using a password from environment variable
|
# Run bootstrap for a Git repository and authenticate using a password from environment variable
|
||||||
GIT_PASSWORD=<password> && flux bootstrap git --url=https://example.com/repository.git
|
GIT_PASSWORD=<password> && flux bootstrap git --url=https://example.com/repository.git --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a Git repository with a passwordless private key
|
# Run bootstrap for a Git repository with a passwordless private key
|
||||||
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key>
|
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a Git repository with a private key and password
|
# Run bootstrap for a Git repository with a private key and password
|
||||||
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key> --password=<password>
|
flux bootstrap git --url=ssh://git@example.com/repository.git --private-key-file=<path/to/private.key> --password=<password> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a Git repository on AWS CodeCommit
|
# Run bootstrap for a Git repository on AWS CodeCommit
|
||||||
flux bootstrap git --url=ssh://<SSH-Key-ID>@git-codecommit.<region>.amazonaws.com/v1/repos/<repository> --private-key-file=<path/to/private.key> --password=<SSH-passphrase>
|
flux bootstrap git --url=ssh://<SSH-Key-ID>@git-codecommit.<region>.amazonaws.com/v1/repos/<repository> --private-key-file=<path/to/private.key> --password=<SSH-passphrase> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a Git repository on Azure Devops
|
# Run bootstrap for a Git repository on Azure Devops
|
||||||
flux bootstrap git --url=ssh://git@ssh.dev.azure.com/v3/<org>/<project>/<repository> --ssh-key-algorithm=rsa --ssh-rsa-bits=4096
|
flux bootstrap git --url=ssh://git@ssh.dev.azure.com/v3/<org>/<project>/<repository> --ssh-key-algorithm=rsa --ssh-rsa-bits=4096 --path=clusters/my-cluster
|
||||||
`,
|
`,
|
||||||
RunE: bootstrapGitCmdRun,
|
RunE: bootstrapGitCmdRun,
|
||||||
}
|
}
|
||||||
@@ -93,7 +93,7 @@ func init() {
|
|||||||
bootstrapGitCmd.Flags().StringVarP(&gitArgs.username, "username", "u", "git", "basic authentication username")
|
bootstrapGitCmd.Flags().StringVarP(&gitArgs.username, "username", "u", "git", "basic authentication username")
|
||||||
bootstrapGitCmd.Flags().StringVarP(&gitArgs.password, "password", "p", "", "basic authentication password")
|
bootstrapGitCmd.Flags().StringVarP(&gitArgs.password, "password", "p", "", "basic authentication password")
|
||||||
bootstrapGitCmd.Flags().BoolVarP(&gitArgs.silent, "silent", "s", false, "assumes the deploy key is already setup, skips confirmation")
|
bootstrapGitCmd.Flags().BoolVarP(&gitArgs.silent, "silent", "s", false, "assumes the deploy key is already setup, skips confirmation")
|
||||||
bootstrapGitCmd.Flags().BoolVar(&gitArgs.insecureHttpAllowed, "allow-insecure-http", false, "allows http git url connections")
|
bootstrapGitCmd.Flags().BoolVar(&gitArgs.insecureHttpAllowed, "allow-insecure-http", false, "allows insecure HTTP connections")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitCmd)
|
bootstrapCmd.AddCommand(bootstrapGitCmd)
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if repositoryURL.Scheme == string(git.HTTPS) && !bootstrapArgs.tokenAuth {
|
if repositoryURL.Scheme == string(git.HTTPS) && !bootstrapArgs.tokenAuth {
|
||||||
return fmt.Errorf("--token-auth=true must be specified for using a HTTPS AWS CodeCommit url")
|
return fmt.Errorf("--token-auth=true must be specified for using an HTTPS AWS CodeCommit url")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +146,13 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !bootstrapArgs.force {
|
||||||
|
err = confirmBootstrap(ctx, kubeClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Manifest base
|
// Manifest base
|
||||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -271,7 +278,6 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Secret: bootstrapArgs.secretName,
|
Secret: bootstrapArgs.secretName,
|
||||||
TargetPath: gitArgs.path.ToSlash(),
|
TargetPath: gitArgs.path.ToSlash(),
|
||||||
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
GitImplementation: sourceGitArgs.gitImplementation.String(),
|
|
||||||
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
275
cmd/flux/bootstrap_gitea.go
Normal file
275
cmd/flux/bootstrap_gitea.go
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/git"
|
||||||
|
"github.com/fluxcd/pkg/git/gogit"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap"
|
||||||
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap/provider"
|
||||||
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||||
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||||
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bootstrapGiteaCmd = &cobra.Command{
|
||||||
|
Use: "gitea",
|
||||||
|
Short: "Deploy Flux on a cluster connected to a Gitea repository",
|
||||||
|
Long: `The bootstrap gitea command creates the Gitea repository if it doesn't exists and
|
||||||
|
commits the Flux manifests to the specified branch.
|
||||||
|
Then it configures the target cluster to synchronize with that repository.
|
||||||
|
If the Flux components are present on the cluster,
|
||||||
|
the bootstrap command will perform an upgrade if needed.`,
|
||||||
|
Example: ` # Create a Gitea personal access token and export it as an env var
|
||||||
|
export GITEA_TOKEN=<my-token>
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository owned by a Gitea organization
|
||||||
|
flux bootstrap gitea --owner=<organization> --repository=<repository name> --path=clusters/my-cluster
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository and assign organization teams to it
|
||||||
|
flux bootstrap gitea --owner=<organization> --repository=<repository name> --team=<team1 slug> --team=<team2 slug> --path=clusters/my-cluster
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository and assign organization teams with their access level(e.g maintain, admin) to it
|
||||||
|
flux bootstrap gitea --owner=<organization> --repository=<repository name> --team=<team1 slug>:<access-level> --path=clusters/my-cluster
|
||||||
|
|
||||||
|
# Run bootstrap for a public repository on a personal account
|
||||||
|
flux bootstrap gitea --owner=<user> --repository=<repository name> --private=false --personal=true --path=clusters/my-cluster
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository hosted on Gitea Enterprise using SSH auth
|
||||||
|
flux bootstrap gitea --owner=<organization> --repository=<repository name> --hostname=<domain> --ssh-hostname=<domain> --path=clusters/my-cluster
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository hosted on Gitea Enterprise using HTTPS auth
|
||||||
|
flux bootstrap gitea --owner=<organization> --repository=<repository name> --hostname=<domain> --token-auth --path=clusters/my-cluster
|
||||||
|
|
||||||
|
# Run bootstrap for an existing repository with a branch named main
|
||||||
|
flux bootstrap gitea --owner=<organization> --repository=<repository name> --branch=main --path=clusters/my-cluster`,
|
||||||
|
RunE: bootstrapGiteaCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type giteaFlags struct {
|
||||||
|
owner string
|
||||||
|
repository string
|
||||||
|
interval time.Duration
|
||||||
|
personal bool
|
||||||
|
private bool
|
||||||
|
hostname string
|
||||||
|
path flags.SafeRelativePath
|
||||||
|
teams []string
|
||||||
|
readWriteKey bool
|
||||||
|
reconcile bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
gtDefaultPermission = "maintain"
|
||||||
|
gtDefaultDomain = "gitea.com"
|
||||||
|
gtTokenEnvVar = "GITEA_TOKEN"
|
||||||
|
)
|
||||||
|
|
||||||
|
var giteaArgs giteaFlags
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
bootstrapGiteaCmd.Flags().StringVar(&giteaArgs.owner, "owner", "", "Gitea user or organization name")
|
||||||
|
bootstrapGiteaCmd.Flags().StringVar(&giteaArgs.repository, "repository", "", "Gitea repository name")
|
||||||
|
bootstrapGiteaCmd.Flags().StringSliceVar(&giteaArgs.teams, "team", []string{}, "Gitea team and the access to be given to it(team:maintain). Defaults to maintainer access if no access level is specified (also accepts comma-separated values)")
|
||||||
|
bootstrapGiteaCmd.Flags().BoolVar(&giteaArgs.personal, "personal", false, "if true, the owner is assumed to be a Gitea user; otherwise an org")
|
||||||
|
bootstrapGiteaCmd.Flags().BoolVar(&giteaArgs.private, "private", true, "if true, the repository is setup or configured as private")
|
||||||
|
bootstrapGiteaCmd.Flags().DurationVar(&giteaArgs.interval, "interval", time.Minute, "sync interval")
|
||||||
|
bootstrapGiteaCmd.Flags().StringVar(&giteaArgs.hostname, "hostname", gtDefaultDomain, "Gitea hostname")
|
||||||
|
bootstrapGiteaCmd.Flags().Var(&giteaArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
|
bootstrapGiteaCmd.Flags().BoolVar(&giteaArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions")
|
||||||
|
bootstrapGiteaCmd.Flags().BoolVar(&giteaArgs.reconcile, "reconcile", false, "if true, the configured options are also reconciled if the repository already exists")
|
||||||
|
|
||||||
|
bootstrapCmd.AddCommand(bootstrapGiteaCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bootstrapGiteaCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
gtToken := os.Getenv(gtTokenEnvVar)
|
||||||
|
if gtToken == "" {
|
||||||
|
var err error
|
||||||
|
gtToken, err = readPasswordFromStdin("Please enter your Gitea personal access token (PAT): ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not read token: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bootstrapValidate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manifest base
|
||||||
|
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
bootstrapArgs.version = ver
|
||||||
|
}
|
||||||
|
manifestsBase, err := buildEmbeddedManifestBase()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(manifestsBase)
|
||||||
|
|
||||||
|
var caBundle []byte
|
||||||
|
if bootstrapArgs.caFile != "" {
|
||||||
|
var err error
|
||||||
|
caBundle, err = os.ReadFile(bootstrapArgs.caFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Build Gitea provider
|
||||||
|
providerCfg := provider.Config{
|
||||||
|
Provider: provider.GitProviderGitea,
|
||||||
|
Hostname: giteaArgs.hostname,
|
||||||
|
Token: gtToken,
|
||||||
|
CaBundle: caBundle,
|
||||||
|
}
|
||||||
|
providerClient, err := provider.BuildGitProvider(providerCfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir, err := manifestgen.MkdirTempAbs("", "flux-bootstrap-")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create temporary working dir: %w", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
clientOpts := []gogit.ClientOption{gogit.WithDiskStorage(), gogit.WithFallbackToDefaultKnownHosts()}
|
||||||
|
gitClient, err := gogit.NewClient(tmpDir, &git.AuthOptions{
|
||||||
|
Transport: git.HTTPS,
|
||||||
|
Username: giteaArgs.owner,
|
||||||
|
Password: gtToken,
|
||||||
|
CAFile: caBundle,
|
||||||
|
}, clientOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create a Git client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install manifest config
|
||||||
|
installOptions := install.Options{
|
||||||
|
BaseURL: rootArgs.defaults.BaseURL,
|
||||||
|
Version: bootstrapArgs.version,
|
||||||
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
|
Components: bootstrapComponents(),
|
||||||
|
Registry: bootstrapArgs.registry,
|
||||||
|
ImagePullSecret: bootstrapArgs.imagePullSecret,
|
||||||
|
WatchAllNamespaces: bootstrapArgs.watchAllNamespaces,
|
||||||
|
NetworkPolicy: bootstrapArgs.networkPolicy,
|
||||||
|
LogLevel: bootstrapArgs.logLevel.String(),
|
||||||
|
NotificationController: rootArgs.defaults.NotificationController,
|
||||||
|
ManifestFile: rootArgs.defaults.ManifestFile,
|
||||||
|
Timeout: rootArgs.timeout,
|
||||||
|
TargetPath: giteaArgs.path.ToSlash(),
|
||||||
|
ClusterDomain: bootstrapArgs.clusterDomain,
|
||||||
|
TolerationKeys: bootstrapArgs.tolerationKeys,
|
||||||
|
}
|
||||||
|
if customBaseURL := bootstrapArgs.manifestsPath; customBaseURL != "" {
|
||||||
|
installOptions.BaseURL = customBaseURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source generation and secret config
|
||||||
|
secretOpts := sourcesecret.Options{
|
||||||
|
Name: bootstrapArgs.secretName,
|
||||||
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
|
TargetPath: giteaArgs.path.ToSlash(),
|
||||||
|
ManifestFile: sourcesecret.MakeDefaultOptions().ManifestFile,
|
||||||
|
}
|
||||||
|
if bootstrapArgs.tokenAuth {
|
||||||
|
secretOpts.Username = "git"
|
||||||
|
secretOpts.Password = gtToken
|
||||||
|
secretOpts.CAFile = caBundle
|
||||||
|
} else {
|
||||||
|
secretOpts.PrivateKeyAlgorithm = sourcesecret.PrivateKeyAlgorithm(bootstrapArgs.keyAlgorithm)
|
||||||
|
secretOpts.RSAKeyBits = int(bootstrapArgs.keyRSABits)
|
||||||
|
secretOpts.ECDSACurve = bootstrapArgs.keyECDSACurve.Curve
|
||||||
|
|
||||||
|
secretOpts.SSHHostname = giteaArgs.hostname
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
|
secretOpts.SSHHostname = bootstrapArgs.sshHostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync manifest config
|
||||||
|
syncOpts := sync.Options{
|
||||||
|
Interval: giteaArgs.interval,
|
||||||
|
Name: *kubeconfigArgs.Namespace,
|
||||||
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
|
Branch: bootstrapArgs.branch,
|
||||||
|
Secret: bootstrapArgs.secretName,
|
||||||
|
TargetPath: giteaArgs.path.ToSlash(),
|
||||||
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
|
}
|
||||||
|
|
||||||
|
entityList, err := bootstrap.LoadEntityListFromPath(bootstrapArgs.gpgKeyRingPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrap config
|
||||||
|
bootstrapOpts := []bootstrap.GitProviderOption{
|
||||||
|
bootstrap.WithProviderRepository(giteaArgs.owner, giteaArgs.repository, giteaArgs.personal),
|
||||||
|
bootstrap.WithBranch(bootstrapArgs.branch),
|
||||||
|
bootstrap.WithBootstrapTransportType("https"),
|
||||||
|
bootstrap.WithSignature(bootstrapArgs.authorName, bootstrapArgs.authorEmail),
|
||||||
|
bootstrap.WithCommitMessageAppendix(bootstrapArgs.commitMessageAppendix),
|
||||||
|
bootstrap.WithProviderTeamPermissions(mapTeamSlice(giteaArgs.teams, gtDefaultPermission)),
|
||||||
|
bootstrap.WithReadWriteKeyPermissions(giteaArgs.readWriteKey),
|
||||||
|
bootstrap.WithKubeconfig(kubeconfigArgs, kubeclientOptions),
|
||||||
|
bootstrap.WithLogger(logger),
|
||||||
|
bootstrap.WithGitCommitSigning(entityList, bootstrapArgs.gpgPassphrase, bootstrapArgs.gpgKeyID),
|
||||||
|
}
|
||||||
|
if bootstrapArgs.sshHostname != "" {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||||
|
}
|
||||||
|
if bootstrapArgs.tokenAuth {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||||
|
}
|
||||||
|
if !giteaArgs.private {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||||
|
}
|
||||||
|
if giteaArgs.reconcile {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithReconcile())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup bootstrapper with constructed configs
|
||||||
|
b, err := bootstrap.NewGitProviderBootstrapper(gitClient, providerClient, kubeClient, bootstrapOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run
|
||||||
|
return bootstrap.Run(ctx, b, manifestsBase, installOptions, secretOpts, syncOpts, rootArgs.pollInterval, rootArgs.timeout)
|
||||||
|
}
|
||||||
@@ -26,50 +26,47 @@ import (
|
|||||||
"github.com/fluxcd/pkg/git/gogit"
|
"github.com/fluxcd/pkg/git/gogit"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/bootstrap"
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap"
|
||||||
"github.com/fluxcd/flux2/pkg/bootstrap/provider"
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap/provider"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitHubCmd = &cobra.Command{
|
var bootstrapGitHubCmd = &cobra.Command{
|
||||||
Use: "github",
|
Use: "github",
|
||||||
Short: "Bootstrap toolkit components in a GitHub repository",
|
Short: "Deploy Flux on a cluster connected to a GitHub repository",
|
||||||
Long: `The bootstrap github command creates the GitHub repository if it doesn't exists and
|
Long: `The bootstrap github command creates the GitHub repository if it doesn't exists and
|
||||||
commits the toolkit components manifests to the main branch.
|
commits the Flux manifests to the specified branch.
|
||||||
Then it configures the target cluster to synchronize with the repository.
|
Then it configures the target cluster to synchronize with that repository.
|
||||||
If the toolkit components are present on the cluster,
|
If the Flux components are present on the cluster,
|
||||||
the bootstrap command will perform an upgrade if needed.`,
|
the bootstrap command will perform an upgrade if needed.`,
|
||||||
Example: ` # Create a GitHub personal access token and export it as an env var
|
Example: ` # Create a GitHub personal access token and export it as an env var
|
||||||
export GITHUB_TOKEN=<my-token>
|
export GITHUB_TOKEN=<my-token>
|
||||||
|
|
||||||
# Run bootstrap for a private repository owned by a GitHub organization
|
# Run bootstrap for a private repository owned by a GitHub organization
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name>
|
flux bootstrap github --owner=<organization> --repository=<repository name> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a private repository and assign organization teams to it
|
# Run bootstrap for a private repository and assign organization teams to it
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug> --team=<team2 slug>
|
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug> --team=<team2 slug> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a private repository and assign organization teams with their access level(e.g maintain, admin) to it
|
# Run bootstrap for a private repository and assign organization teams with their access level(e.g maintain, admin) to it
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug>:<access-level>
|
flux bootstrap github --owner=<organization> --repository=<repository name> --team=<team1 slug>:<access-level> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a repository path
|
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name> --path=dev-cluster
|
|
||||||
|
|
||||||
# Run bootstrap for a public repository on a personal account
|
# Run bootstrap for a public repository on a personal account
|
||||||
flux bootstrap github --owner=<user> --repository=<repository name> --private=false --personal=true
|
flux bootstrap github --owner=<user> --repository=<repository name> --private=false --personal=true --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a private repository hosted on GitHub Enterprise using SSH auth
|
# Run bootstrap for a private repository hosted on GitHub Enterprise using SSH auth
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name> --hostname=<domain> --ssh-hostname=<domain>
|
flux bootstrap github --owner=<organization> --repository=<repository name> --hostname=<domain> --ssh-hostname=<domain> --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for a private repository hosted on GitHub Enterprise using HTTPS auth
|
# Run bootstrap for a private repository hosted on GitHub Enterprise using HTTPS auth
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name> --hostname=<domain> --token-auth
|
flux bootstrap github --owner=<organization> --repository=<repository name> --hostname=<domain> --token-auth --path=clusters/my-cluster
|
||||||
|
|
||||||
# Run bootstrap for an existing repository with a branch named main
|
# Run bootstrap for an existing repository with a branch named main
|
||||||
flux bootstrap github --owner=<organization> --repository=<repository name> --branch=main`,
|
flux bootstrap github --owner=<organization> --repository=<repository name> --branch=main --path=clusters/my-cluster`,
|
||||||
RunE: bootstrapGitHubCmdRun,
|
RunE: bootstrapGitHubCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,6 +128,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !bootstrapArgs.force {
|
||||||
|
err = confirmBootstrap(ctx, kubeClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Manifest base
|
// Manifest base
|
||||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -233,7 +237,6 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Secret: bootstrapArgs.secretName,
|
Secret: bootstrapArgs.secretName,
|
||||||
TargetPath: githubArgs.path.ToSlash(),
|
TargetPath: githubArgs.path.ToSlash(),
|
||||||
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
GitImplementation: sourceGitArgs.gitImplementation.String(),
|
|
||||||
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,23 +28,23 @@ import (
|
|||||||
"github.com/fluxcd/pkg/git/gogit"
|
"github.com/fluxcd/pkg/git/gogit"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/bootstrap"
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap"
|
||||||
"github.com/fluxcd/flux2/pkg/bootstrap/provider"
|
"github.com/fluxcd/flux2/v2/pkg/bootstrap/provider"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
var bootstrapGitLabCmd = &cobra.Command{
|
var bootstrapGitLabCmd = &cobra.Command{
|
||||||
Use: "gitlab",
|
Use: "gitlab",
|
||||||
Short: "Bootstrap toolkit components in a GitLab repository",
|
Short: "Deploy Flux on a cluster connected to a GitLab repository",
|
||||||
Long: `The bootstrap gitlab command creates the GitLab repository if it doesn't exists and
|
Long: `The bootstrap gitlab command creates the GitLab repository if it doesn't exists and
|
||||||
commits the toolkit components manifests to the master branch.
|
commits the Flux manifests to the specified branch.
|
||||||
Then it configures the target cluster to synchronize with the repository.
|
Then it configures the target cluster to synchronize with that repository.
|
||||||
If the toolkit components are present on the cluster,
|
If the Flux components are present on the cluster,
|
||||||
the bootstrap command will perform an upgrade if needed.`,
|
the bootstrap command will perform an upgrade if needed.`,
|
||||||
Example: ` # Create a GitLab API token and export it as an env var
|
Example: ` # Create a GitLab API token and export it as an env var
|
||||||
export GITLAB_TOKEN=<my-token>
|
export GITLAB_TOKEN=<my-token>
|
||||||
@@ -64,8 +64,12 @@ the bootstrap command will perform an upgrade if needed.`,
|
|||||||
# Run bootstrap for a private repository hosted on a GitLab server
|
# Run bootstrap for a private repository hosted on a GitLab server
|
||||||
flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
|
flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
|
||||||
|
|
||||||
# Run bootstrap for a an existing repository with a branch named main
|
# Run bootstrap for an existing repository with a branch named main
|
||||||
flux bootstrap gitlab --owner=<organization> --repository=<repository name> --branch=main --token-auth`,
|
flux bootstrap gitlab --owner=<organization> --repository=<repository name> --branch=main --token-auth
|
||||||
|
|
||||||
|
# Run bootstrap for a private repository using Deploy Token authentication
|
||||||
|
flux bootstrap gitlab --owner=<group> --repository=<repository name> --deploy-token-auth
|
||||||
|
`,
|
||||||
RunE: bootstrapGitLabCmdRun,
|
RunE: bootstrapGitLabCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,16 +81,17 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type gitlabFlags struct {
|
type gitlabFlags struct {
|
||||||
owner string
|
owner string
|
||||||
repository string
|
repository string
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
personal bool
|
personal bool
|
||||||
private bool
|
private bool
|
||||||
hostname string
|
hostname string
|
||||||
path flags.SafeRelativePath
|
path flags.SafeRelativePath
|
||||||
teams []string
|
teams []string
|
||||||
readWriteKey bool
|
readWriteKey bool
|
||||||
reconcile bool
|
reconcile bool
|
||||||
|
deployTokenAuth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitlabArgs gitlabFlags
|
var gitlabArgs gitlabFlags
|
||||||
@@ -102,6 +107,7 @@ func init() {
|
|||||||
bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
bootstrapGitLabCmd.Flags().Var(&gitlabArgs.path, "path", "path relative to the repository root, when specified the cluster sync will be scoped to this path")
|
||||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions")
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.readWriteKey, "read-write-key", false, "if true, the deploy key is configured with read/write permissions")
|
||||||
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.reconcile, "reconcile", false, "if true, the configured options are also reconciled if the repository already exists")
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.reconcile, "reconcile", false, "if true, the configured options are also reconciled if the repository already exists")
|
||||||
|
bootstrapGitLabCmd.Flags().BoolVar(&gitlabArgs.deployTokenAuth, "deploy-token-auth", false, "when enabled, a Project Deploy Token is generated and will be used instead of the SSH deploy token")
|
||||||
|
|
||||||
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
bootstrapCmd.AddCommand(bootstrapGitLabCmd)
|
||||||
}
|
}
|
||||||
@@ -123,6 +129,10 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bootstrapArgs.tokenAuth && gitlabArgs.deployTokenAuth {
|
||||||
|
return fmt.Errorf("--token-auth and --deploy-token-auth cannot be set both.")
|
||||||
|
}
|
||||||
|
|
||||||
if err := bootstrapValidate(); err != nil {
|
if err := bootstrapValidate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -135,6 +145,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !bootstrapArgs.force {
|
||||||
|
err = confirmBootstrap(ctx, kubeClient)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Manifest base
|
// Manifest base
|
||||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -225,6 +242,9 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
secretOpts.Username = "git"
|
secretOpts.Username = "git"
|
||||||
secretOpts.Password = glToken
|
secretOpts.Password = glToken
|
||||||
secretOpts.CAFile = caBundle
|
secretOpts.CAFile = caBundle
|
||||||
|
} else if gitlabArgs.deployTokenAuth {
|
||||||
|
// the actual deploy token will be reconciled later
|
||||||
|
secretOpts.CAFile = caBundle
|
||||||
} else {
|
} else {
|
||||||
keypair, err := sourcesecret.LoadKeyPairFromPath(bootstrapArgs.privateKeyFile, gitArgs.password)
|
keypair, err := sourcesecret.LoadKeyPairFromPath(bootstrapArgs.privateKeyFile, gitArgs.password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -250,7 +270,6 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
Secret: bootstrapArgs.secretName,
|
Secret: bootstrapArgs.secretName,
|
||||||
TargetPath: gitlabArgs.path.ToSlash(),
|
TargetPath: gitlabArgs.path.ToSlash(),
|
||||||
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
ManifestFile: sync.MakeDefaultOptions().ManifestFile,
|
||||||
GitImplementation: sourceGitArgs.gitImplementation.String(),
|
|
||||||
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
RecurseSubmodules: bootstrapArgs.recurseSubmodules,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,9 +294,12 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
if bootstrapArgs.sshHostname != "" {
|
if bootstrapArgs.sshHostname != "" {
|
||||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||||
}
|
}
|
||||||
if bootstrapArgs.tokenAuth {
|
if bootstrapArgs.tokenAuth || gitlabArgs.deployTokenAuth {
|
||||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||||
}
|
}
|
||||||
|
if gitlabArgs.deployTokenAuth {
|
||||||
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithDeployTokenAuth())
|
||||||
|
}
|
||||||
if !gitlabArgs.private {
|
if !gitlabArgs.private {
|
||||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
var buildCmd = &cobra.Command{
|
var buildCmd = &cobra.Command{
|
||||||
Use: "build",
|
Use: "build",
|
||||||
Short: "Build a flux resource",
|
Short: "Build a flux resource",
|
||||||
Long: "The build command is used to build flux resources.",
|
Long: `The build command is used to build flux resources.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ import (
|
|||||||
var buildArtifactCmd = &cobra.Command{
|
var buildArtifactCmd = &cobra.Command{
|
||||||
Use: "artifact",
|
Use: "artifact",
|
||||||
Short: "Build artifact",
|
Short: "Build artifact",
|
||||||
Long: `The build artifact command creates a tgz file with the manifests from the given directory or a single manifest file.`,
|
Long: withPreviewNote(`The build artifact command creates a tgz file with the manifests
|
||||||
|
from the given directory or a single manifest file.`),
|
||||||
Example: ` # Build the given manifests directory into an artifact
|
Example: ` # Build the given manifests directory into an artifact
|
||||||
flux build artifact --path ./path/to/local/manifests --output ./path/to/artifact.tgz
|
flux build artifact --path ./path/to/local/manifests --output ./path/to/artifact.tgz
|
||||||
|
|
||||||
@@ -86,9 +87,9 @@ func buildArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
logger.Actionf("building artifact from %s", path)
|
logger.Actionf("building artifact from %s", path)
|
||||||
|
|
||||||
ociClient := oci.NewLocalClient()
|
ociClient := oci.NewClient(oci.DefaultOptions())
|
||||||
if err := ociClient.Build(buildArtifactArgs.output, path, buildArtifactArgs.ignorePaths); err != nil {
|
if err := ociClient.Build(buildArtifactArgs.output, path, buildArtifactArgs.ignorePaths); err != nil {
|
||||||
return fmt.Errorf("bulding artifact failed, error: %w", err)
|
return fmt.Errorf("building artifact failed, error: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Successf("artifact created at %s", buildArtifactArgs.output)
|
logger.Successf("artifact created at %s", buildArtifactArgs.output)
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ data:
|
|||||||
tmpFile, err := saveReaderToFile(strings.NewReader(tt.string))
|
tmpFile, err := saveReaderToFile(strings.NewReader(tt.string))
|
||||||
g.Expect(err).To(BeNil())
|
g.Expect(err).To(BeNil())
|
||||||
|
|
||||||
defer os.Remove(tmpFile)
|
t.Cleanup(func() { _ = os.Remove(tmpFile) })
|
||||||
|
|
||||||
b, err := os.ReadFile(tmpFile)
|
b, err := os.ReadFile(tmpFile)
|
||||||
if tt.expectErr {
|
if tt.expectErr {
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/ssa"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/build"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
|
||||||
|
"github.com/fluxcd/flux2/v2/internal/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
var buildKsCmd = &cobra.Command{
|
var buildKsCmd = &cobra.Command{
|
||||||
@@ -44,7 +46,14 @@ flux build kustomization my-app --path ./path/to/local/manifests --kustomization
|
|||||||
|
|
||||||
# Build in dry-run mode without connecting to the cluster.
|
# Build in dry-run mode without connecting to the cluster.
|
||||||
# Note that variable substitutions from Secrets and ConfigMaps are skipped in dry-run mode.
|
# Note that variable substitutions from Secrets and ConfigMaps are skipped in dry-run mode.
|
||||||
flux build kustomization my-app --path ./path/to/local/manifests --kustomization-file ./path/to/local/my-app.yaml --dry-run`,
|
flux build kustomization my-app --path ./path/to/local/manifests \
|
||||||
|
--kustomization-file ./path/to/local/my-app.yaml \
|
||||||
|
--dry-run
|
||||||
|
|
||||||
|
# Exclude files by providing a comma separated list of entries that follow the .gitignore pattern fromat.
|
||||||
|
flux build kustomization my-app --path ./path/to/local/manifests \
|
||||||
|
--kustomization-file ./path/to/local/my-app.yaml \
|
||||||
|
--ignore-paths "/to_ignore/**/*.yaml,ignore.yaml"`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||||
RunE: buildKsCmdRun,
|
RunE: buildKsCmdRun,
|
||||||
}
|
}
|
||||||
@@ -52,6 +61,7 @@ flux build kustomization my-app --path ./path/to/local/manifests --kustomization
|
|||||||
type buildKsFlags struct {
|
type buildKsFlags struct {
|
||||||
kustomizationFile string
|
kustomizationFile string
|
||||||
path string
|
path string
|
||||||
|
ignorePaths []string
|
||||||
dryRun bool
|
dryRun bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +70,7 @@ var buildKsArgs buildKsFlags
|
|||||||
func init() {
|
func init() {
|
||||||
buildKsCmd.Flags().StringVar(&buildKsArgs.path, "path", "", "Path to the manifests location.")
|
buildKsCmd.Flags().StringVar(&buildKsArgs.path, "path", "", "Path to the manifests location.")
|
||||||
buildKsCmd.Flags().StringVar(&buildKsArgs.kustomizationFile, "kustomization-file", "", "Path to the Flux Kustomization YAML file.")
|
buildKsCmd.Flags().StringVar(&buildKsArgs.kustomizationFile, "kustomization-file", "", "Path to the Flux Kustomization YAML file.")
|
||||||
|
buildKsCmd.Flags().StringSliceVar(&buildKsArgs.ignorePaths, "ignore-paths", nil, "set paths to ignore in .gitignore format")
|
||||||
buildKsCmd.Flags().BoolVar(&buildKsArgs.dryRun, "dry-run", false, "Dry run mode.")
|
buildKsCmd.Flags().BoolVar(&buildKsArgs.dryRun, "dry-run", false, "Dry run mode.")
|
||||||
buildCmd.AddCommand(buildKsCmd)
|
buildCmd.AddCommand(buildKsCmd)
|
||||||
}
|
}
|
||||||
@@ -95,12 +106,14 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) (err error) {
|
|||||||
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
|
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
|
||||||
build.WithDryRun(buildKsArgs.dryRun),
|
build.WithDryRun(buildKsArgs.dryRun),
|
||||||
build.WithNamespace(*kubeconfigArgs.Namespace),
|
build.WithNamespace(*kubeconfigArgs.Namespace),
|
||||||
|
build.WithIgnore(buildKsArgs.ignorePaths),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
builder, err = build.NewBuilder(name, buildKsArgs.path,
|
builder, err = build.NewBuilder(name, buildKsArgs.path,
|
||||||
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
||||||
build.WithTimeout(rootArgs.timeout),
|
build.WithTimeout(rootArgs.timeout),
|
||||||
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
|
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
|
||||||
|
build.WithIgnore(buildKsArgs.ignorePaths),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,12 +127,17 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) (err error) {
|
|||||||
|
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
manifests, err := builder.Build()
|
objects, err := builder.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errChan <- err
|
errChan <- err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Print(string(manifests))
|
manifests, err := ssa.ObjectsToYAML(objects)
|
||||||
|
if err != nil {
|
||||||
|
errChan <- err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Print(manifests)
|
||||||
errChan <- nil
|
errChan <- nil
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ func TestBuildKustomization(t *testing.T) {
|
|||||||
resultFile: "./testdata/build-kustomization/podinfo-with-var-substitution-result.yaml",
|
resultFile: "./testdata/build-kustomization/podinfo-with-var-substitution-result.yaml",
|
||||||
assertFunc: "assertGoldenTemplateFile",
|
assertFunc: "assertGoldenTemplateFile",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "build ignore",
|
||||||
|
args: "build kustomization podinfo --path ./testdata/build-kustomization/ignore --ignore-paths \"!configmap.yaml,!secret.yaml\"",
|
||||||
|
resultFile: "./testdata/build-kustomization/podinfo-with-ignore-result.yaml",
|
||||||
|
assertFunc: "assertGoldenTemplateFile",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := map[string]string{
|
tmpl := map[string]string{
|
||||||
@@ -92,7 +98,7 @@ func TestBuildKustomization(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildLocalKustomization(t *testing.T) {
|
func TestBuildLocalKustomization(t *testing.T) {
|
||||||
podinfo := `apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
|
podinfo := `apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
kind: Kustomization
|
kind: Kustomization
|
||||||
metadata:
|
metadata:
|
||||||
name: podinfo
|
name: podinfo
|
||||||
@@ -153,6 +159,7 @@ spec:
|
|||||||
tmpl := map[string]string{
|
tmpl := map[string]string{
|
||||||
"fluxns": allocateNamespace("flux-system"),
|
"fluxns": allocateNamespace("flux-system"),
|
||||||
}
|
}
|
||||||
|
setup(t, tmpl)
|
||||||
|
|
||||||
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-source.yaml", tmpl, t)
|
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-source.yaml", tmpl, t)
|
||||||
|
|
||||||
@@ -171,8 +178,7 @@ spec:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
t.Cleanup(func() { _ = os.Remove("./testdata/build-kustomization/podinfo.yaml") })
|
||||||
defer os.Remove("./testdata/build-kustomization/podinfo.yaml")
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -26,21 +27,22 @@ import (
|
|||||||
v1 "k8s.io/api/apps/v1"
|
v1 "k8s.io/api/apps/v1"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/version"
|
"github.com/fluxcd/pkg/version"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||||
"github.com/fluxcd/flux2/pkg/status"
|
"github.com/fluxcd/flux2/v2/pkg/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
var checkCmd = &cobra.Command{
|
var checkCmd = &cobra.Command{
|
||||||
Use: "check",
|
Use: "check",
|
||||||
Short: "Check requirements and installation",
|
Short: "Check requirements and installation",
|
||||||
Long: `The check command will perform a series of checks to validate that
|
Long: withPreviewNote(`The check command will perform a series of checks to validate that
|
||||||
the local environment is configured correctly and if the installed components are healthy.`,
|
the local environment is configured correctly and if the installed components are healthy.`),
|
||||||
Example: ` # Run pre-installation checks
|
Example: ` # Run pre-installation checks
|
||||||
flux check --pre
|
flux check --pre
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ type checkFlags struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var kubernetesConstraints = []string{
|
var kubernetesConstraints = []string{
|
||||||
">=1.20.6-0",
|
">=1.26.0-0",
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkArgs checkFlags
|
var checkArgs checkFlags
|
||||||
@@ -80,7 +82,20 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
fluxCheck()
|
fluxCheck()
|
||||||
|
|
||||||
if !kubernetesCheck(kubernetesConstraints) {
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cfg, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Kubernetes client initialization failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeClient, err := client.New(cfg, client.Options{Scheme: utils.NewScheme()})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !kubernetesCheck(cfg, kubernetesConstraints) {
|
||||||
checkFailed = true
|
checkFailed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,13 +107,18 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Actionf("checking version in cluster")
|
||||||
|
if !fluxClusterVersionCheck(ctx, kubeClient) {
|
||||||
|
checkFailed = true
|
||||||
|
}
|
||||||
|
|
||||||
logger.Actionf("checking controllers")
|
logger.Actionf("checking controllers")
|
||||||
if !componentsCheck() {
|
if !componentsCheck(ctx, kubeClient) {
|
||||||
checkFailed = true
|
checkFailed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Actionf("checking crds")
|
logger.Actionf("checking crds")
|
||||||
if !crdsCheck() {
|
if !crdsCheck(ctx, kubeClient) {
|
||||||
checkFailed = true
|
checkFailed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,17 +149,11 @@ func fluxCheck() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if latestSv.GreaterThan(curSv) {
|
if latestSv.GreaterThan(curSv) {
|
||||||
logger.Failuref("flux %s <%s (new version is available, please upgrade)", curSv, latestSv)
|
logger.Failuref("flux %s <%s (new CLI version is available, please upgrade)", curSv, latestSv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func kubernetesCheck(constraints []string) bool {
|
func kubernetesCheck(cfg *rest.Config, constraints []string) bool {
|
||||||
cfg, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
|
|
||||||
if err != nil {
|
|
||||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
clientSet, err := kubernetes.NewForConfig(cfg)
|
clientSet, err := kubernetes.NewForConfig(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||||
@@ -178,21 +192,8 @@ func kubernetesCheck(constraints []string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func componentsCheck() bool {
|
func componentsCheck(ctx context.Context, kubeClient client.Client) bool {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
statusChecker, err := status.NewStatusCheckerWithClient(kubeClient, checkArgs.pollInterval, rootArgs.timeout, logger)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeConfig, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
statusChecker, err := status.NewStatusChecker(kubeConfig, checkArgs.pollInterval, rootArgs.timeout, logger)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -222,15 +223,7 @@ func componentsCheck() bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func crdsCheck() bool {
|
func crdsCheck(ctx context.Context, kubeClient client.Client) bool {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := true
|
ok := true
|
||||||
selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
|
selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
|
||||||
var list apiextensionsv1.CustomResourceDefinitionList
|
var list apiextensionsv1.CustomResourceDefinitionList
|
||||||
@@ -253,3 +246,17 @@ func crdsCheck() bool {
|
|||||||
}
|
}
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fluxClusterVersionCheck(ctx context.Context, kubeClient client.Client) bool {
|
||||||
|
clusterInfo, err := getFluxClusterInfo(ctx, kubeClient)
|
||||||
|
if err != nil {
|
||||||
|
logger.Failuref("checking failed: %s", err.Error())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if clusterInfo.distribution() != "" {
|
||||||
|
logger.Successf("distribution: %s", clusterInfo.distribution())
|
||||||
|
}
|
||||||
|
logger.Successf("bootstrapped: %t", clusterInfo.bootstrapped)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCheckPre(t *testing.T) {
|
func TestCheckPre(t *testing.T) {
|
||||||
|
|||||||
126
cmd/flux/cluster_info.go
Normal file
126
cmd/flux/cluster_info.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/manifoldco/promptui"
|
||||||
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||||
|
)
|
||||||
|
|
||||||
|
// bootstrapLabels are labels put on a resource by kustomize-controller. These labels on the CRD indicates
|
||||||
|
// that flux has been bootstrapped.
|
||||||
|
var bootstrapLabels = []string{
|
||||||
|
fmt.Sprintf("%s/name", kustomizev1.GroupVersion.Group),
|
||||||
|
fmt.Sprintf("%s/namespace", kustomizev1.GroupVersion.Group),
|
||||||
|
}
|
||||||
|
|
||||||
|
// fluxClusterInfo contains information about an existing flux installation on a cluster.
|
||||||
|
type fluxClusterInfo struct {
|
||||||
|
// bootstrapped indicates that Flux was installed using the `flux bootstrap` command.
|
||||||
|
bootstrapped bool
|
||||||
|
// managedBy is the name of the tool being used to manage the installation of Flux.
|
||||||
|
managedBy string
|
||||||
|
// partOf indicates which distribution the instance is a part of.
|
||||||
|
partOf string
|
||||||
|
// version is the Flux version number in semver format.
|
||||||
|
version string
|
||||||
|
}
|
||||||
|
|
||||||
|
// getFluxClusterInfo returns information on the Flux installation running on the cluster.
|
||||||
|
// If an error occurred, the returned error will be non-nil.
|
||||||
|
//
|
||||||
|
// This function retrieves the GitRepository CRD from the cluster and checks it
|
||||||
|
// for a set of labels used to determine the Flux version and how Flux was installed.
|
||||||
|
// It returns the NotFound error from the underlying library if it was unable to find
|
||||||
|
// the GitRepository CRD and this can be used to check if Flux is installed.
|
||||||
|
func getFluxClusterInfo(ctx context.Context, c client.Client) (fluxClusterInfo, error) {
|
||||||
|
var info fluxClusterInfo
|
||||||
|
crdMetadata := &metav1.PartialObjectMetadata{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: apiextensionsv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: "CustomResourceDefinition",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: fmt.Sprintf("gitrepositories.%s", sourcev1.GroupVersion.Group),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := c.Get(ctx, client.ObjectKeyFromObject(crdMetadata), crdMetadata); err != nil {
|
||||||
|
return info, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info.version = crdMetadata.Labels[manifestgen.VersionLabelKey]
|
||||||
|
|
||||||
|
var present bool
|
||||||
|
for _, l := range bootstrapLabels {
|
||||||
|
_, present = crdMetadata.Labels[l]
|
||||||
|
}
|
||||||
|
if present {
|
||||||
|
info.bootstrapped = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// the `app.kubernetes.io/managed-by` label is not set by flux but might be set by other
|
||||||
|
// tools used to install Flux e.g Helm.
|
||||||
|
if manager, ok := crdMetadata.Labels["app.kubernetes.io/managed-by"]; ok {
|
||||||
|
info.managedBy = manager
|
||||||
|
}
|
||||||
|
|
||||||
|
if partOf, ok := crdMetadata.Labels[manifestgen.PartOfLabelKey]; ok {
|
||||||
|
info.partOf = partOf
|
||||||
|
}
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// confirmFluxInstallOverride displays a prompt to the user so that they can confirm before overriding
|
||||||
|
// a Flux installation. It returns nil if the installation should continue,
|
||||||
|
// promptui.ErrAbort if the user doesn't confirm, or an error encountered.
|
||||||
|
func confirmFluxInstallOverride(info fluxClusterInfo) error {
|
||||||
|
// no need to display prompt if installation is managed by Flux
|
||||||
|
if installManagedByFlux(info.managedBy) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
display := fmt.Sprintf("Flux %s has been installed on this cluster with %s!", info.version, info.managedBy)
|
||||||
|
fmt.Fprintln(rootCmd.ErrOrStderr(), display)
|
||||||
|
prompt := promptui.Prompt{
|
||||||
|
Label: fmt.Sprintf("Are you sure you want to override the %s installation? Y/N", info.managedBy),
|
||||||
|
IsConfirm: true,
|
||||||
|
}
|
||||||
|
_, err := prompt.Run()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info fluxClusterInfo) distribution() string {
|
||||||
|
distribution := info.version
|
||||||
|
if info.partOf != "" {
|
||||||
|
distribution = fmt.Sprintf("%s-%s", info.partOf, info.version)
|
||||||
|
}
|
||||||
|
return distribution
|
||||||
|
}
|
||||||
|
|
||||||
|
func installManagedByFlux(manager string) bool {
|
||||||
|
return manager == "" || manager == "flux"
|
||||||
|
}
|
||||||
141
cmd/flux/cluster_info_test.go
Normal file
141
cmd/flux/cluster_info_test.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||||
|
"github.com/fluxcd/pkg/ssa"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_getFluxClusterInfo(t *testing.T) {
|
||||||
|
g := NewWithT(t)
|
||||||
|
f, err := os.Open("./testdata/cluster_info/gitrepositories.yaml")
|
||||||
|
g.Expect(err).To(BeNil())
|
||||||
|
|
||||||
|
objs, err := ssa.ReadObjects(f)
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
gitrepo := objs[0]
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
labels map[string]string
|
||||||
|
wantErr bool
|
||||||
|
wantInfo fluxClusterInfo
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no git repository CRD present",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CRD with kustomize-controller labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
fmt.Sprintf("%s/name", kustomizev1.GroupVersion.Group): "flux-system",
|
||||||
|
fmt.Sprintf("%s/namespace", kustomizev1.GroupVersion.Group): "flux-system",
|
||||||
|
"app.kubernetes.io/version": "v2.1.0",
|
||||||
|
},
|
||||||
|
wantInfo: fluxClusterInfo{
|
||||||
|
version: "v2.1.0",
|
||||||
|
bootstrapped: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CRD with kustomize-controller labels and managed-by label",
|
||||||
|
labels: map[string]string{
|
||||||
|
fmt.Sprintf("%s/name", kustomizev1.GroupVersion.Group): "flux-system",
|
||||||
|
fmt.Sprintf("%s/namespace", kustomizev1.GroupVersion.Group): "flux-system",
|
||||||
|
"app.kubernetes.io/version": "v2.1.0",
|
||||||
|
"app.kubernetes.io/managed-by": "flux",
|
||||||
|
},
|
||||||
|
wantInfo: fluxClusterInfo{
|
||||||
|
version: "v2.1.0",
|
||||||
|
bootstrapped: true,
|
||||||
|
managedBy: "flux",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CRD with only managed-by label",
|
||||||
|
labels: map[string]string{
|
||||||
|
"app.kubernetes.io/version": "v2.1.0",
|
||||||
|
"app.kubernetes.io/managed-by": "helm",
|
||||||
|
},
|
||||||
|
wantInfo: fluxClusterInfo{
|
||||||
|
version: "v2.1.0",
|
||||||
|
managedBy: "helm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CRD with no labels",
|
||||||
|
labels: map[string]string{},
|
||||||
|
wantInfo: fluxClusterInfo{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CRD with only version label",
|
||||||
|
labels: map[string]string{
|
||||||
|
"app.kubernetes.io/version": "v2.1.0",
|
||||||
|
},
|
||||||
|
wantInfo: fluxClusterInfo{
|
||||||
|
version: "v2.1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CRD with version and part-of labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
"app.kubernetes.io/version": "v2.1.0",
|
||||||
|
"app.kubernetes.io/part-of": "flux",
|
||||||
|
},
|
||||||
|
wantInfo: fluxClusterInfo{
|
||||||
|
version: "v2.1.0",
|
||||||
|
partOf: "flux",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
g := NewWithT(t)
|
||||||
|
newscheme := runtime.NewScheme()
|
||||||
|
apiextensionsv1.AddToScheme(newscheme)
|
||||||
|
builder := fake.NewClientBuilder().WithScheme(newscheme)
|
||||||
|
if tt.labels != nil {
|
||||||
|
gitrepo.SetLabels(tt.labels)
|
||||||
|
builder = builder.WithRuntimeObjects(gitrepo)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := builder.Build()
|
||||||
|
info, err := getFluxClusterInfo(context.Background(), client)
|
||||||
|
if tt.wantErr {
|
||||||
|
g.Expect(err).To(HaveOccurred())
|
||||||
|
g.Expect(errors.IsNotFound(err)).To(BeTrue())
|
||||||
|
} else {
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Expect(info).To(BeEquivalentTo(tt.wantInfo))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -31,7 +31,7 @@ import (
|
|||||||
var completionCmd = &cobra.Command{
|
var completionCmd = &cobra.Command{
|
||||||
Use: "completion",
|
Use: "completion",
|
||||||
Short: "Generates completion scripts for various shells",
|
Short: "Generates completion scripts for various shells",
|
||||||
Long: "The completion sub-command generates completion scripts for various shells",
|
Long: `The completion sub-command generates completion scripts for various shells.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
var completionBashCmd = &cobra.Command{
|
var completionBashCmd = &cobra.Command{
|
||||||
Use: "bash",
|
Use: "bash",
|
||||||
Short: "Generates bash completion scripts",
|
Short: "Generates bash completion scripts",
|
||||||
|
Long: `The completion sub-command generates completion scripts for bash.`,
|
||||||
Example: `To load completion run
|
Example: `To load completion run
|
||||||
|
|
||||||
. <(flux completion bash)
|
. <(flux completion bash)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
var completionFishCmd = &cobra.Command{
|
var completionFishCmd = &cobra.Command{
|
||||||
Use: "fish",
|
Use: "fish",
|
||||||
Short: "Generates fish completion scripts",
|
Short: "Generates fish completion scripts",
|
||||||
|
Long: `The completion sub-command generates completion scripts for fish.`,
|
||||||
Example: `To configure your fish shell to load completions for each session write this script to your completions dir:
|
Example: `To configure your fish shell to load completions for each session write this script to your completions dir:
|
||||||
|
|
||||||
flux completion fish > ~/.config/fish/completions/flux.fish
|
flux completion fish > ~/.config/fish/completions/flux.fish
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
var completionPowerShellCmd = &cobra.Command{
|
var completionPowerShellCmd = &cobra.Command{
|
||||||
Use: "powershell",
|
Use: "powershell",
|
||||||
Short: "Generates powershell completion scripts",
|
Short: "Generates powershell completion scripts",
|
||||||
|
Long: `The completion sub-command generates completion scripts for powershell.`,
|
||||||
Example: `To load completion run
|
Example: `To load completion run
|
||||||
|
|
||||||
. <(flux completion powershell)
|
. <(flux completion powershell)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import (
|
|||||||
var completionZshCmd = &cobra.Command{
|
var completionZshCmd = &cobra.Command{
|
||||||
Use: "zsh",
|
Use: "zsh",
|
||||||
Short: "Generates zsh completion scripts",
|
Short: "Generates zsh completion scripts",
|
||||||
|
Long: `The completion sub-command generates completion scripts for zsh.`,
|
||||||
Example: `To load completion run
|
Example: `To load completion run
|
||||||
|
|
||||||
. <(flux completion zsh)
|
. <(flux completion zsh)
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createCmd = &cobra.Command{
|
var createCmd = &cobra.Command{
|
||||||
Use: "create",
|
Use: "create",
|
||||||
Short: "Create or update sources and resources",
|
Short: "Create or update sources and resources",
|
||||||
Long: "The create sub-commands generate sources and resources.",
|
Long: `The create sub-commands generate sources and resources.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
type createFlags struct {
|
type createFlags struct {
|
||||||
@@ -131,8 +131,8 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for %s reconciliation", names.kind)
|
logger.Waitingf("waiting for %s reconciliation", names.kind)
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isReady(ctx, kubeClient, namespacedName, object)); err != nil {
|
isObjectReadyConditionFunc(kubeClient, namespacedName, object.asClientObject())); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("%s reconciliation completed", names.kind)
|
logger.Successf("%s reconciliation completed", names.kind)
|
||||||
@@ -165,6 +165,6 @@ func parseLabels() (map[string]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateObjectName(name string) bool {
|
func validateObjectName(name string) bool {
|
||||||
r := regexp.MustCompile("^[a-z0-9]([a-z0-9\\-]){0,61}[a-z0-9]$")
|
r := regexp.MustCompile(`^[a-z0-9]([a-z0-9\-]){0,61}[a-z0-9]$`)
|
||||||
return r.MatchString(name)
|
return r.MatchString(name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,22 +22,22 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||||
|
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createAlertCmd = &cobra.Command{
|
var createAlertCmd = &cobra.Command{
|
||||||
Use: "alert [name]",
|
Use: "alert [name]",
|
||||||
Short: "Create or update a Alert resource",
|
Short: "Create or update a Alert resource",
|
||||||
Long: "The create alert command generates a Alert resource.",
|
Long: withPreviewNote(`The create alert command generates a Alert resource.`),
|
||||||
Example: ` # Create an Alert for kustomization events
|
Example: ` # Create an Alert for kustomization events
|
||||||
flux create alert \
|
flux create alert \
|
||||||
--event-severity info \
|
--event-severity info \
|
||||||
@@ -96,13 +96,13 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
logger.Generatef("generating Alert")
|
logger.Generatef("generating Alert")
|
||||||
}
|
}
|
||||||
|
|
||||||
alert := notificationv1.Alert{
|
alert := notificationv1b3.Alert{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: name,
|
Name: name,
|
||||||
Namespace: *kubeconfigArgs.Namespace,
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
Labels: sourceLabels,
|
Labels: sourceLabels,
|
||||||
},
|
},
|
||||||
Spec: notificationv1.AlertSpec{
|
Spec: notificationv1b3.AlertSpec{
|
||||||
ProviderRef: meta.LocalObjectReference{
|
ProviderRef: meta.LocalObjectReference{
|
||||||
Name: alertArgs.providerRef,
|
Name: alertArgs.providerRef,
|
||||||
},
|
},
|
||||||
@@ -131,8 +131,8 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Alert reconciliation")
|
logger.Waitingf("waiting for Alert reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
isStaticObjectReadyConditionFunc(kubeClient, namespacedName, &alert)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("Alert %s is ready", name)
|
logger.Successf("Alert %s is ready", name)
|
||||||
@@ -140,13 +140,13 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func upsertAlert(ctx context.Context, kubeClient client.Client,
|
func upsertAlert(ctx context.Context, kubeClient client.Client,
|
||||||
alert *notificationv1.Alert) (types.NamespacedName, error) {
|
alert *notificationv1b3.Alert) (types.NamespacedName, error) {
|
||||||
namespacedName := types.NamespacedName{
|
namespacedName := types.NamespacedName{
|
||||||
Namespace: alert.GetNamespace(),
|
Namespace: alert.GetNamespace(),
|
||||||
Name: alert.GetName(),
|
Name: alert.GetName(),
|
||||||
}
|
}
|
||||||
|
|
||||||
var existing notificationv1.Alert
|
var existing notificationv1b3.Alert
|
||||||
err := kubeClient.Get(ctx, namespacedName, &existing)
|
err := kubeClient.Get(ctx, namespacedName, &existing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsNotFound(err) {
|
if errors.IsNotFound(err) {
|
||||||
@@ -169,23 +169,3 @@ func upsertAlert(ctx context.Context, kubeClient client.Client,
|
|||||||
logger.Successf("Alert updated")
|
logger.Successf("Alert updated")
|
||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAlertReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, alert *notificationv1.Alert) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, alert)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(alert.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,22 +22,21 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createAlertProviderCmd = &cobra.Command{
|
var createAlertProviderCmd = &cobra.Command{
|
||||||
Use: "alert-provider [name]",
|
Use: "alert-provider [name]",
|
||||||
Short: "Create or update a Provider resource",
|
Short: "Create or update a Provider resource",
|
||||||
Long: "The create alert-provider command generates a Provider resource.",
|
Long: withPreviewNote(`The create alert-provider command generates a Provider resource.`),
|
||||||
Example: ` # Create a Provider for a Slack channel
|
Example: ` # Create a Provider for a Slack channel
|
||||||
flux create alert-provider slack \
|
flux create alert-provider slack \
|
||||||
--type slack \
|
--type slack \
|
||||||
@@ -127,8 +126,8 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Provider reconciliation")
|
logger.Waitingf("waiting for Provider reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
|
isStaticObjectReadyConditionFunc(kubeClient, namespacedName, &provider)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,23 +166,3 @@ func upsertAlertProvider(ctx context.Context, kubeClient client.Client,
|
|||||||
logger.Successf("Provider updated")
|
logger.Successf("Provider updated")
|
||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAlertProviderReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, provider *notificationv1.Provider) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, provider)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(provider.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,29 +24,29 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/runtime/transform"
|
"github.com/fluxcd/pkg/runtime/transform"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createHelmReleaseCmd = &cobra.Command{
|
var createHelmReleaseCmd = &cobra.Command{
|
||||||
Use: "helmrelease [name]",
|
Use: "helmrelease [name]",
|
||||||
Aliases: []string{"hr"},
|
Aliases: []string{"hr"},
|
||||||
Short: "Create or update a HelmRelease resource",
|
Short: "Create or update a HelmRelease resource",
|
||||||
Long: "The helmrelease create command generates a HelmRelease resource for a given HelmRepository source.",
|
Long: withPreviewNote(`The helmrelease create command generates a HelmRelease resource for a given HelmRepository source.`),
|
||||||
Example: ` # Create a HelmRelease with a chart from a HelmRepository source
|
Example: ` # Create a HelmRelease with a chart from a HelmRepository source
|
||||||
flux create hr podinfo \
|
flux create hr podinfo \
|
||||||
--interval=10m \
|
--interval=10m \
|
||||||
@@ -83,9 +83,9 @@ var createHelmReleaseCmd = &cobra.Command{
|
|||||||
|
|
||||||
# Create a HelmRelease with a custom release name
|
# Create a HelmRelease with a custom release name
|
||||||
flux create hr podinfo \
|
flux create hr podinfo \
|
||||||
--release-name=podinfo-dev
|
--release-name=podinfo-dev \
|
||||||
--source=HelmRepository/podinfo \
|
--source=HelmRepository/podinfo \
|
||||||
--chart=podinfo \
|
--chart=podinfo
|
||||||
|
|
||||||
# Create a HelmRelease targeting another namespace than the resource
|
# Create a HelmRelease targeting another namespace than the resource
|
||||||
flux create hr podinfo \
|
flux create hr podinfo \
|
||||||
@@ -200,7 +200,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if helmReleaseArgs.kubeConfigSecretRef != "" {
|
if helmReleaseArgs.kubeConfigSecretRef != "" {
|
||||||
helmRelease.Spec.KubeConfig = &helmv2.KubeConfig{
|
helmRelease.Spec.KubeConfig = &meta.KubeConfigReference{
|
||||||
SecretRef: meta.SecretKeyReference{
|
SecretRef: meta.SecretKeyReference{
|
||||||
Name: helmReleaseArgs.kubeConfigSecretRef,
|
Name: helmReleaseArgs.kubeConfigSecretRef,
|
||||||
},
|
},
|
||||||
@@ -303,8 +303,8 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmRelease reconciliation")
|
logger.Waitingf("waiting for HelmRelease reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
|
isObjectReadyConditionFunc(kubeClient, namespacedName, &helmRelease)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("HelmRelease %s is ready", name)
|
logger.Successf("HelmRelease %s is ready", name)
|
||||||
@@ -344,23 +344,6 @@ func upsertHelmRelease(ctx context.Context, kubeClient client.Client,
|
|||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHelmReleaseReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, helmRelease *helmv2.HelmRelease) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, helmRelease)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state we are observing is for the current generation
|
|
||||||
if helmRelease.Generation != helmRelease.Status.ObservedGeneration {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return apimeta.IsStatusConditionTrue(helmRelease.Status.Conditions, meta.ReadyCondition), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateStrategy(input string) bool {
|
func validateStrategy(input string) bool {
|
||||||
allowedStrategy := []string{"Revision", "ChartVersion"}
|
allowedStrategy := []string{"Revision", "ChartVersion"}
|
||||||
|
|
||||||
|
|||||||
@@ -20,14 +20,12 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const createImageLong = `The create image sub-commands work with image automation objects; that is,
|
|
||||||
object controlling updates to git based on e.g., new container images
|
|
||||||
being available.`
|
|
||||||
|
|
||||||
var createImageCmd = &cobra.Command{
|
var createImageCmd = &cobra.Command{
|
||||||
Use: "image",
|
Use: "image",
|
||||||
Short: "Create or update resources dealing with image automation",
|
Short: "Create or update resources dealing with image automation",
|
||||||
Long: createImageLong,
|
Long: `The create image sub-commands work with image automation objects;
|
||||||
|
that is, object controlling updates to git based on e.g., new container images
|
||||||
|
being available.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -28,18 +28,18 @@ import (
|
|||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImagePolicyCmd = &cobra.Command{
|
var createImagePolicyCmd = &cobra.Command{
|
||||||
Use: "policy [name]",
|
Use: "policy [name]",
|
||||||
Short: "Create or update an ImagePolicy object",
|
Short: "Create or update an ImagePolicy object",
|
||||||
Long: `The create image policy command generates an ImagePolicy resource.
|
Long: withPreviewNote(`The create image policy command generates an ImagePolicy resource.
|
||||||
An ImagePolicy object calculates a "latest image" given an image
|
An ImagePolicy object calculates a "latest image" given an image
|
||||||
repository and a policy, e.g., semver.
|
repository and a policy, e.g., semver.
|
||||||
|
|
||||||
The image that sorts highest according to the policy is recorded in
|
The image that sorts highest according to the policy is recorded in
|
||||||
the status of the object.`,
|
the status of the object.`),
|
||||||
Example: ` # Create an ImagePolicy to select the latest stable release
|
Example: ` # Create an ImagePolicy to select the latest stable release
|
||||||
flux create image policy podinfo \
|
flux create image policy podinfo \
|
||||||
--image-ref=podinfo \
|
--image-ref=podinfo \
|
||||||
@@ -54,13 +54,12 @@ the status of the object.`,
|
|||||||
RunE: createImagePolicyRun}
|
RunE: createImagePolicyRun}
|
||||||
|
|
||||||
type imagePolicyFlags struct {
|
type imagePolicyFlags struct {
|
||||||
imageRef string
|
imageRef string
|
||||||
semver string
|
semver string
|
||||||
alpha string
|
alpha string
|
||||||
numeric string
|
numeric string
|
||||||
filterRegex string
|
filterRegex string
|
||||||
filterExtract string
|
filterExtract string
|
||||||
filterNumerical string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var imagePolicyArgs = imagePolicyFlags{}
|
var imagePolicyArgs = imagePolicyFlags{}
|
||||||
@@ -183,7 +182,6 @@ func validateExtractStr(template string, capNames []string) error {
|
|||||||
name, num, rest, ok := extract(template)
|
name, num, rest, ok := extract(template)
|
||||||
if !ok {
|
if !ok {
|
||||||
// Malformed extract string, assume user didn't want this
|
// Malformed extract string, assume user didn't want this
|
||||||
template = template[1:]
|
|
||||||
return fmt.Errorf("--filter-extract is malformed")
|
return fmt.Errorf("--filter-extract is malformed")
|
||||||
}
|
}
|
||||||
template = rest
|
template = rest
|
||||||
|
|||||||
@@ -26,14 +26,14 @@ import (
|
|||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImageRepositoryCmd = &cobra.Command{
|
var createImageRepositoryCmd = &cobra.Command{
|
||||||
Use: "repository [name]",
|
Use: "repository [name]",
|
||||||
Short: "Create or update an ImageRepository object",
|
Short: "Create or update an ImageRepository object",
|
||||||
Long: `The create image repository command generates an ImageRepository resource.
|
Long: withPreviewNote(`The create image repository command generates an ImageRepository resource.
|
||||||
An ImageRepository object specifies an image repository to scan.`,
|
An ImageRepository object specifies an image repository to scan.`),
|
||||||
Example: ` # Create an ImageRepository object to scan the alpine image repository:
|
Example: ` # Create an ImageRepository object to scan the alpine image repository:
|
||||||
flux create image repository alpine-repo --image alpine --interval 20m
|
flux create image repository alpine-repo --image alpine --interval 20m
|
||||||
|
|
||||||
|
|||||||
@@ -23,15 +23,15 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createImageUpdateCmd = &cobra.Command{
|
var createImageUpdateCmd = &cobra.Command{
|
||||||
Use: "update [name]",
|
Use: "update [name]",
|
||||||
Short: "Create or update an ImageUpdateAutomation object",
|
Short: "Create or update an ImageUpdateAutomation object",
|
||||||
Long: `The create image update command generates an ImageUpdateAutomation resource.
|
Long: withPreviewNote(`The create image update command generates an ImageUpdateAutomation resource.
|
||||||
An ImageUpdateAutomation object specifies an automated update to images
|
An ImageUpdateAutomation object specifies an automated update to images
|
||||||
mentioned in YAMLs in a git repository.`,
|
mentioned in YAMLs in a git repository.`),
|
||||||
Example: ` # Configure image updates for the main repository created by flux bootstrap
|
Example: ` # Configure image updates for the main repository created by flux bootstrap
|
||||||
flux create image update flux-system \
|
flux create image update flux-system \
|
||||||
--git-repo-ref=flux-system \
|
--git-repo-ref=flux-system \
|
||||||
|
|||||||
@@ -24,25 +24,24 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta2"
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createKsCmd = &cobra.Command{
|
var createKsCmd = &cobra.Command{
|
||||||
Use: "kustomization [name]",
|
Use: "kustomization [name]",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Create or update a Kustomization resource",
|
Short: "Create or update a Kustomization resource",
|
||||||
Long: "The create command generates a Kustomization resource for a given source.",
|
Long: `The create command generates a Kustomization resource for a given source.`,
|
||||||
Example: ` # Create a Kustomization resource from a source at a given path
|
Example: ` # Create a Kustomization resource from a source at a given path
|
||||||
flux create kustomization kyverno \
|
flux create kustomization kyverno \
|
||||||
--source=GitRepository/kyverno \
|
--source=GitRepository/kyverno \
|
||||||
@@ -97,6 +96,7 @@ type kustomizationFlags struct {
|
|||||||
targetNamespace string
|
targetNamespace string
|
||||||
wait bool
|
wait bool
|
||||||
kubeConfigSecretRef string
|
kubeConfigSecretRef string
|
||||||
|
retryInterval time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
var kustomizationArgs = NewKustomizationFlags()
|
var kustomizationArgs = NewKustomizationFlags()
|
||||||
@@ -116,6 +116,7 @@ func init() {
|
|||||||
createKsCmd.Flags().StringVar(&kustomizationArgs.targetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.targetNamespace, "target-namespace", "", "overrides the namespace of all Kustomization objects reconciled by this Kustomization")
|
||||||
createKsCmd.Flags().StringVar(&kustomizationArgs.kubeConfigSecretRef, "kubeconfig-secret-ref", "", "the name of the Kubernetes Secret that contains a key with the kubeconfig file for connecting to a remote cluster")
|
createKsCmd.Flags().StringVar(&kustomizationArgs.kubeConfigSecretRef, "kubeconfig-secret-ref", "", "the name of the Kubernetes Secret that contains a key with the kubeconfig file for connecting to a remote cluster")
|
||||||
createKsCmd.Flags().MarkDeprecated("validation", "this arg is no longer used, all resources are validated using server-side apply dry-run")
|
createKsCmd.Flags().MarkDeprecated("validation", "this arg is no longer used, all resources are validated using server-side apply dry-run")
|
||||||
|
createKsCmd.Flags().DurationVar(&kustomizationArgs.retryInterval, "retry-interval", 0, "the interval at which to retry a previously failed reconciliation")
|
||||||
|
|
||||||
createCmd.AddCommand(createKsCmd)
|
createCmd.AddCommand(createKsCmd)
|
||||||
}
|
}
|
||||||
@@ -238,6 +239,10 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if kustomizationArgs.retryInterval > 0 {
|
||||||
|
kustomization.Spec.RetryInterval = &metav1.Duration{Duration: kustomizationArgs.retryInterval}
|
||||||
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return printExport(exportKs(&kustomization))
|
return printExport(exportKs(&kustomization))
|
||||||
}
|
}
|
||||||
@@ -257,8 +262,8 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Kustomization reconciliation")
|
logger.Waitingf("waiting for Kustomization reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
|
isObjectReadyConditionFunc(kubeClient, namespacedName, &kustomization)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("Kustomization %s is ready", name)
|
logger.Successf("Kustomization %s is ready", name)
|
||||||
@@ -297,28 +302,3 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client,
|
|||||||
logger.Successf("Kustomization updated")
|
logger.Successf("Kustomization updated")
|
||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isKustomizationReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, kustomization *kustomizev1.Kustomization) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, kustomization)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Confirm the state we are observing is for the current generation
|
|
||||||
if kustomization.Generation != kustomization.Status.ObservedGeneration {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(kustomization.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,22 +22,21 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createReceiverCmd = &cobra.Command{
|
var createReceiverCmd = &cobra.Command{
|
||||||
Use: "receiver [name]",
|
Use: "receiver [name]",
|
||||||
Short: "Create or update a Receiver resource",
|
Short: "Create or update a Receiver resource",
|
||||||
Long: "The create receiver command generates a Receiver resource.",
|
Long: `The create receiver command generates a Receiver resource.`,
|
||||||
Example: ` # Create a Receiver
|
Example: ` # Create a Receiver
|
||||||
flux create receiver github-receiver \
|
flux create receiver github-receiver \
|
||||||
--type github \
|
--type github \
|
||||||
@@ -139,13 +138,13 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Receiver reconciliation")
|
logger.Waitingf("waiting for Receiver reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
|
isObjectReadyConditionFunc(kubeClient, namespacedName, &receiver)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("Receiver %s is ready", name)
|
logger.Successf("Receiver %s is ready", name)
|
||||||
|
|
||||||
logger.Successf("generated webhook URL %s", receiver.Status.URL)
|
logger.Successf("generated webhook URL %s", receiver.Status.WebhookPath)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,23 +178,3 @@ func upsertReceiver(ctx context.Context, kubeClient client.Client,
|
|||||||
logger.Successf("Receiver updated")
|
logger.Successf("Receiver updated")
|
||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isReceiverReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, receiver *notificationv1.Receiver) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, receiver)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := apimeta.FindStatusCondition(receiver.Status.Conditions, meta.ReadyCondition); c != nil {
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import (
|
|||||||
var createSecretCmd = &cobra.Command{
|
var createSecretCmd = &cobra.Command{
|
||||||
Use: "secret",
|
Use: "secret",
|
||||||
Short: "Create or update Kubernetes secrets",
|
Short: "Create or update Kubernetes secrets",
|
||||||
Long: "The create source sub-commands generate Kubernetes secrets specific to Flux.",
|
Long: `The create source sub-commands generate Kubernetes secrets specific to Flux.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -27,17 +27,19 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSecretGitCmd = &cobra.Command{
|
var createSecretGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Create or update a Kubernetes secret for Git authentication",
|
Short: "Create or update a Kubernetes secret for Git authentication",
|
||||||
Long: `The create secret git command generates a Kubernetes secret with Git credentials.
|
Long: `The create secret git command generates a Kubernetes secret with Git credentials.
|
||||||
For Git over SSH, the host and SSH keys are automatically generated and stored in the secret.
|
For Git over SSH, the host and SSH keys are automatically generated and stored
|
||||||
For Git over HTTP/S, the provided basic authentication credentials are stored in the secret.`,
|
in the secret.
|
||||||
|
For Git over HTTP/S, the provided basic authentication credentials or bearer
|
||||||
|
authentication token are stored in the secret.`,
|
||||||
Example: ` # Create a Git SSH authentication secret using an ECDSA P-521 curve public key
|
Example: ` # Create a Git SSH authentication secret using an ECDSA P-521 curve public key
|
||||||
|
|
||||||
flux create secret git podinfo-auth \
|
flux create secret git podinfo-auth \
|
||||||
@@ -86,7 +88,9 @@ type secretGitFlags struct {
|
|||||||
rsaBits flags.RSAKeyBits
|
rsaBits flags.RSAKeyBits
|
||||||
ecdsaCurve flags.ECDSACurve
|
ecdsaCurve flags.ECDSACurve
|
||||||
caFile string
|
caFile string
|
||||||
|
caCrtFile string
|
||||||
privateKeyFile string
|
privateKeyFile string
|
||||||
|
bearerToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
var secretGitArgs = NewSecretGitFlags()
|
var secretGitArgs = NewSecretGitFlags()
|
||||||
@@ -99,7 +103,9 @@ func init() {
|
|||||||
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
|
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.Description())
|
||||||
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
|
createSecretGitCmd.Flags().Var(&secretGitArgs.ecdsaCurve, "ssh-ecdsa-curve", secretGitArgs.ecdsaCurve.Description())
|
||||||
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
||||||
|
createSecretGitCmd.Flags().StringVar(&secretGitArgs.caCrtFile, "ca-crt-file", "", "path to TLS CA certificate file used for validating self-signed certificates; takes precedence over --ca-file")
|
||||||
createSecretGitCmd.Flags().StringVar(&secretGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
|
createSecretGitCmd.Flags().StringVar(&secretGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
|
||||||
|
createSecretGitCmd.Flags().StringVar(&secretGitArgs.bearerToken, "bearer-token", "", "bearer authentication token")
|
||||||
|
|
||||||
createSecretCmd.AddCommand(createSecretGitCmd)
|
createSecretCmd.AddCommand(createSecretGitCmd)
|
||||||
}
|
}
|
||||||
@@ -147,17 +153,27 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
|
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
|
||||||
opts.Password = secretGitArgs.password
|
opts.Password = secretGitArgs.password
|
||||||
case "http", "https":
|
case "http", "https":
|
||||||
if secretGitArgs.username == "" || secretGitArgs.password == "" {
|
if (secretGitArgs.username == "" || secretGitArgs.password == "") && secretGitArgs.bearerToken == "" {
|
||||||
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
return fmt.Errorf("for Git over HTTP/S the username and password, or a bearer token is required")
|
||||||
}
|
}
|
||||||
opts.Username = secretGitArgs.username
|
opts.Username = secretGitArgs.username
|
||||||
opts.Password = secretGitArgs.password
|
opts.Password = secretGitArgs.password
|
||||||
if secretGitArgs.caFile != "" {
|
opts.BearerToken = secretGitArgs.bearerToken
|
||||||
caBundle, err := os.ReadFile(secretGitArgs.caFile)
|
if secretGitArgs.username != "" && secretGitArgs.password != "" && secretGitArgs.bearerToken != "" {
|
||||||
|
return fmt.Errorf("user credentials and bearer token cannot be used together")
|
||||||
|
}
|
||||||
|
|
||||||
|
// --ca-crt-file takes precedence over --ca-file.
|
||||||
|
if secretGitArgs.caCrtFile != "" {
|
||||||
|
opts.CACrt, err = os.ReadFile(secretGitArgs.caCrtFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
|
}
|
||||||
|
} else if secretGitArgs.caFile != "" {
|
||||||
|
opts.CAFile, err = os.ReadFile(secretGitArgs.caFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
}
|
}
|
||||||
opts.CAFile = caBundle
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateGitSecret(t *testing.T) {
|
func TestCreateGitSecret(t *testing.T) {
|
||||||
|
file, err := os.CreateTemp(t.TempDir(), "ca-crt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not create CA certificate file")
|
||||||
|
}
|
||||||
|
_, err = file.Write([]byte("ca-data"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("could not write to CA certificate file")
|
||||||
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args string
|
args string
|
||||||
@@ -30,6 +41,21 @@ func TestCreateGitSecret(t *testing.T) {
|
|||||||
args: "create secret git podinfo-auth --url=ssh://git@github.com/stefanprodan/podinfo --private-key-file=./testdata/create_secret/git/ecdsa-password.private --password=password --namespace=my-namespace --export",
|
args: "create secret git podinfo-auth --url=ssh://git@github.com/stefanprodan/podinfo --private-key-file=./testdata/create_secret/git/ecdsa-password.private --password=password --namespace=my-namespace --export",
|
||||||
assert: assertGoldenFile("testdata/create_secret/git/git-ssh-secret-password.yaml"),
|
assert: assertGoldenFile("testdata/create_secret/git/git-ssh-secret-password.yaml"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "git authentication with bearer token",
|
||||||
|
args: "create secret git bearer-token-auth --url=https://github.com/stefanprodan/podinfo --bearer-token=ghp_baR2qnFF0O41WlucePL3udt2N9vVZS4R0hAS --namespace=my-namespace --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_secret/git/git-bearer-token.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "git authentication with CA certificate",
|
||||||
|
args: fmt.Sprintf("create secret git ca-crt --url=https://github.com/stefanprodan/podinfo --password=my-password --username=my-username --ca-crt-file=%s --namespace=my-namespace --export", file.Name()),
|
||||||
|
assert: assertGoldenFile("testdata/create_secret/git/secret-ca-crt.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "git authentication with basic auth and bearer token",
|
||||||
|
args: "create secret git podinfo-auth --url=https://github.com/stefanprodan/podinfo --username=aaa --password=zzzz --bearer-token=aaaa --namespace=my-namespace --export",
|
||||||
|
assert: assertError("user credentials and bearer token cannot be used together"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSecretHelmCmd = &cobra.Command{
|
var createSecretHelmCmd = &cobra.Command{
|
||||||
Use: "helm [name]",
|
Use: "helm [name]",
|
||||||
Short: "Create or update a Kubernetes secret for Helm repository authentication",
|
Short: "Create or update a Kubernetes secret for Helm repository authentication",
|
||||||
Long: `The create secret helm command generates a Kubernetes secret with basic authentication credentials.`,
|
Long: withPreviewNote(`The create secret helm command generates a Kubernetes secret with basic authentication credentials.`),
|
||||||
Example: ` # Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
|
Example: ` # Create a Helm authentication secret on disk and encrypt it with Mozilla SOPS
|
||||||
flux create secret helm repo-auth \
|
flux create secret helm repo-auth \
|
||||||
--namespace=my-namespace \
|
--namespace=my-namespace \
|
||||||
@@ -41,15 +41,8 @@ var createSecretHelmCmd = &cobra.Command{
|
|||||||
--export > repo-auth.yaml
|
--export > repo-auth.yaml
|
||||||
|
|
||||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
--in-place repo-auth.yaml
|
--in-place repo-auth.yaml`,
|
||||||
|
|
||||||
# Create a Helm authentication secret using a custom TLS cert
|
|
||||||
flux create secret helm repo-auth \
|
|
||||||
--username=username \
|
|
||||||
--password=password \
|
|
||||||
--cert-file=./cert.crt \
|
|
||||||
--key-file=./key.crt \
|
|
||||||
--ca-file=./ca.crt`,
|
|
||||||
RunE: createSecretHelmCmdRun,
|
RunE: createSecretHelmCmdRun,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,9 +55,16 @@ type secretHelmFlags struct {
|
|||||||
var secretHelmArgs secretHelmFlags
|
var secretHelmArgs secretHelmFlags
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.username, "username", "u", "", "basic authentication username")
|
flags := createSecretHelmCmd.Flags()
|
||||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.password, "password", "p", "", "basic authentication password")
|
flags.StringVarP(&secretHelmArgs.username, "username", "u", "", "basic authentication username")
|
||||||
initSecretTLSFlags(createSecretHelmCmd.Flags(), &secretHelmArgs.secretTLSFlags)
|
flags.StringVarP(&secretHelmArgs.password, "password", "p", "", "basic authentication password")
|
||||||
|
|
||||||
|
initSecretDeprecatedTLSFlags(flags, &secretHelmArgs.secretTLSFlags)
|
||||||
|
deprecationMsg := "please use the command `flux create secret tls` to generate TLS secrets"
|
||||||
|
flags.MarkDeprecated("cert-file", deprecationMsg)
|
||||||
|
flags.MarkDeprecated("key-file", deprecationMsg)
|
||||||
|
flags.MarkDeprecated("ca-file", deprecationMsg)
|
||||||
|
|
||||||
createSecretCmd.AddCommand(createSecretHelmCmd)
|
createSecretCmd.AddCommand(createSecretHelmCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
"github.com/google/go-containerregistry/pkg/name"
|
"github.com/google/go-containerregistry/pkg/name"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -31,7 +31,7 @@ import (
|
|||||||
var createSecretOCICmd = &cobra.Command{
|
var createSecretOCICmd = &cobra.Command{
|
||||||
Use: "oci [name]",
|
Use: "oci [name]",
|
||||||
Short: "Create or update a Kubernetes image pull secret",
|
Short: "Create or update a Kubernetes image pull secret",
|
||||||
Long: `The create secret oci command generates a Kubernetes secret that can be used for OCIRepository authentication`,
|
Long: withPreviewNote(`The create secret oci command generates a Kubernetes secret that can be used for OCIRepository authentication`),
|
||||||
Example: ` # Create an OCI authentication secret on disk and encrypt it with Mozilla SOPS
|
Example: ` # Create an OCI authentication secret on disk and encrypt it with Mozilla SOPS
|
||||||
flux create secret oci podinfo-auth \
|
flux create secret oci podinfo-auth \
|
||||||
--url=ghcr.io \
|
--url=ghcr.io \
|
||||||
|
|||||||
@@ -26,20 +26,21 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSecretTLSCmd = &cobra.Command{
|
var createSecretTLSCmd = &cobra.Command{
|
||||||
Use: "tls [name]",
|
Use: "tls [name]",
|
||||||
Short: "Create or update a Kubernetes secret with TLS certificates",
|
Short: "Create or update a Kubernetes secret with TLS certificates",
|
||||||
Long: `The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`,
|
Long: withPreviewNote(`The create secret tls command generates a Kubernetes secret with certificates for use with TLS.`),
|
||||||
Example: ` # Create a TLS secret on disk and encrypt it with Mozilla SOPS.
|
Example: ` # Create a TLS secret on disk and encrypt it with Mozilla SOPS.
|
||||||
# Files are expected to be PEM-encoded.
|
# Files are expected to be PEM-encoded.
|
||||||
flux create secret tls certs \
|
flux create secret tls certs \
|
||||||
--namespace=my-namespace \
|
--namespace=my-namespace \
|
||||||
--cert-file=./client.crt \
|
--tls-crt-file=./client.crt \
|
||||||
--key-file=./client.key \
|
--tls-key-file=./client.key \
|
||||||
|
--ca-crt-file=./ca.crt \
|
||||||
--export > certs.yaml
|
--export > certs.yaml
|
||||||
|
|
||||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||||
@@ -48,22 +49,37 @@ var createSecretTLSCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type secretTLSFlags struct {
|
type secretTLSFlags struct {
|
||||||
certFile string
|
certFile string
|
||||||
keyFile string
|
keyFile string
|
||||||
caFile string
|
caFile string
|
||||||
|
caCrtFile string
|
||||||
|
tlsKeyFile string
|
||||||
|
tlsCrtFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
var secretTLSArgs secretTLSFlags
|
var secretTLSArgs secretTLSFlags
|
||||||
|
|
||||||
func initSecretTLSFlags(flags *pflag.FlagSet, args *secretTLSFlags) {
|
func initSecretDeprecatedTLSFlags(flags *pflag.FlagSet, args *secretTLSFlags) {
|
||||||
flags.StringVar(&args.certFile, "cert-file", "", "TLS authentication cert file path")
|
flags.StringVar(&args.certFile, "cert-file", "", "TLS authentication cert file path")
|
||||||
flags.StringVar(&args.keyFile, "key-file", "", "TLS authentication key file path")
|
flags.StringVar(&args.keyFile, "key-file", "", "TLS authentication key file path")
|
||||||
flags.StringVar(&args.caFile, "ca-file", "", "TLS authentication CA file path")
|
flags.StringVar(&args.caFile, "ca-file", "", "TLS authentication CA file path")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initSecretTLSFlags(flags *pflag.FlagSet, args *secretTLSFlags) {
|
||||||
|
flags.StringVar(&args.tlsCrtFile, "tls-crt-file", "", "TLS authentication cert file path")
|
||||||
|
flags.StringVar(&args.tlsKeyFile, "tls-key-file", "", "TLS authentication key file path")
|
||||||
|
flags.StringVar(&args.caCrtFile, "ca-crt-file", "", "TLS authentication CA file path")
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flags := createSecretTLSCmd.Flags()
|
flags := createSecretTLSCmd.Flags()
|
||||||
|
initSecretDeprecatedTLSFlags(flags, &secretTLSArgs)
|
||||||
initSecretTLSFlags(flags, &secretTLSArgs)
|
initSecretTLSFlags(flags, &secretTLSArgs)
|
||||||
|
|
||||||
|
flags.MarkDeprecated("cert-file", "please use --tls-crt-file instead")
|
||||||
|
flags.MarkDeprecated("key-file", "please use --tls-key-file instead")
|
||||||
|
flags.MarkDeprecated("ca-file", "please use --ca-crt-file instead")
|
||||||
|
|
||||||
createSecretCmd.AddCommand(createSecretTLSCmd)
|
createSecretCmd.AddCommand(createSecretTLSCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,33 +91,40 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
caBundle := []byte{}
|
opts := sourcesecret.Options{
|
||||||
if secretTLSArgs.caFile != "" {
|
Name: name,
|
||||||
var err error
|
Namespace: *kubeconfigArgs.Namespace,
|
||||||
caBundle, err = os.ReadFile(secretTLSArgs.caFile)
|
Labels: labels,
|
||||||
|
}
|
||||||
|
|
||||||
|
if secretTLSArgs.caCrtFile != "" {
|
||||||
|
opts.CACrt, err = os.ReadFile(secretTLSArgs.caCrtFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
|
}
|
||||||
|
} else if secretTLSArgs.caFile != "" {
|
||||||
|
opts.CAFile, err = os.ReadFile(secretTLSArgs.caFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var certFile, keyFile []byte
|
if secretTLSArgs.tlsCrtFile != "" && secretTLSArgs.tlsKeyFile != "" {
|
||||||
if secretTLSArgs.certFile != "" && secretTLSArgs.keyFile != "" {
|
if opts.TLSCrt, err = os.ReadFile(secretTLSArgs.tlsCrtFile); err != nil {
|
||||||
if certFile, err = os.ReadFile(secretTLSArgs.certFile); err != nil {
|
|
||||||
return fmt.Errorf("failed to read cert file: %w", err)
|
return fmt.Errorf("failed to read cert file: %w", err)
|
||||||
}
|
}
|
||||||
if keyFile, err = os.ReadFile(secretTLSArgs.keyFile); err != nil {
|
if opts.TLSKey, err = os.ReadFile(secretTLSArgs.tlsKeyFile); err != nil {
|
||||||
|
return fmt.Errorf("failed to read key file: %w", err)
|
||||||
|
}
|
||||||
|
} else if secretTLSArgs.certFile != "" && secretTLSArgs.keyFile != "" {
|
||||||
|
if opts.CertFile, err = os.ReadFile(secretTLSArgs.certFile); err != nil {
|
||||||
|
return fmt.Errorf("failed to read cert file: %w", err)
|
||||||
|
}
|
||||||
|
if opts.KeyFile, err = os.ReadFile(secretTLSArgs.keyFile); err != nil {
|
||||||
return fmt.Errorf("failed to read key file: %w", err)
|
return fmt.Errorf("failed to read key file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := sourcesecret.Options{
|
|
||||||
Name: name,
|
|
||||||
Namespace: *kubeconfigArgs.Namespace,
|
|
||||||
Labels: labels,
|
|
||||||
CAFile: caBundle,
|
|
||||||
CertFile: certFile,
|
|
||||||
KeyFile: keyFile,
|
|
||||||
}
|
|
||||||
secret, err := sourcesecret.Generate(opts)
|
secret, err := sourcesecret.Generate(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateTlsSecretNoArgs(t *testing.T) {
|
func TestCreateTlsSecret(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args string
|
args string
|
||||||
@@ -15,9 +15,13 @@ func TestCreateTlsSecretNoArgs(t *testing.T) {
|
|||||||
assert: assertError("name is required"),
|
assert: assertError("name is required"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
args: "create secret tls certs --namespace=my-namespace --cert-file=./testdata/create_secret/tls/test-cert.pem --key-file=./testdata/create_secret/tls/test-key.pem --export",
|
args: "create secret tls certs --namespace=my-namespace --tls-crt-file=./testdata/create_secret/tls/test-cert.pem --tls-key-file=./testdata/create_secret/tls/test-key.pem --ca-crt-file=./testdata/create_secret/tls/test-ca.pem --export",
|
||||||
assert: assertGoldenFile("testdata/create_secret/tls/secret-tls.yaml"),
|
assert: assertGoldenFile("testdata/create_secret/tls/secret-tls.yaml"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
args: "create secret tls certs --namespace=my-namespace --cert-file=./testdata/create_secret/tls/test-cert.pem --key-file=./testdata/create_secret/tls/test-key.pem --ca-file=./testdata/create_secret/tls/test-ca.pem --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_secret/tls/deprecated-secret-tls.yaml"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
var createSourceCmd = &cobra.Command{
|
var createSourceCmd = &cobra.Command{
|
||||||
Use: "source",
|
Use: "source",
|
||||||
Short: "Create or update sources",
|
Short: "Create or update sources",
|
||||||
Long: "The create source sub-commands generate sources.",
|
Long: `The create source sub-commands generate sources.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
type createSourceFlags struct {
|
type createSourceFlags struct {
|
||||||
|
|||||||
@@ -31,19 +31,18 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/runtime/conditions"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSourceBucketCmd = &cobra.Command{
|
var createSourceBucketCmd = &cobra.Command{
|
||||||
Use: "bucket [name]",
|
Use: "bucket [name]",
|
||||||
Short: "Create or update a Bucket source",
|
Short: "Create or update a Bucket source",
|
||||||
Long: `The create source bucket command generates a Bucket resource and waits for it to be downloaded.
|
Long: withPreviewNote(`The create source bucket command generates a Bucket resource and waits for it to be downloaded.
|
||||||
For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`,
|
For Buckets with static authentication, the credentials are stored in a Kubernetes secret.`),
|
||||||
Example: ` # Create a source for a Bucket using static authentication
|
Example: ` # Create a source for a Bucket using static authentication
|
||||||
flux create source bucket podinfo \
|
flux create source bucket podinfo \
|
||||||
--bucket-name=podinfo \
|
--bucket-name=podinfo \
|
||||||
@@ -204,8 +203,8 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for Bucket source reconciliation")
|
logger.Waitingf("waiting for Bucket source reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
|
isObjectReadyConditionFunc(kubeClient, namespacedName, bucket)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("Bucket source reconciliation completed")
|
logger.Successf("Bucket source reconciliation completed")
|
||||||
@@ -247,30 +246,3 @@ func upsertBucket(ctx context.Context, kubeClient client.Client,
|
|||||||
logger.Successf("Bucket source updated")
|
logger.Successf("Bucket source updated")
|
||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBucketReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, bucket *sourcev1.Bucket) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, bucket)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := conditions.Get(bucket, meta.ReadyCondition); c != nil {
|
|
||||||
// Confirm the Ready condition we are observing is for the
|
|
||||||
// current generation
|
|
||||||
if c.ObservedGeneration != bucket.GetGeneration() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Further check the Status
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -35,13 +35,12 @@ import (
|
|||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/runtime/conditions"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
)
|
)
|
||||||
|
|
||||||
type sourceGitFlags struct {
|
type sourceGitFlags struct {
|
||||||
@@ -49,13 +48,14 @@ type sourceGitFlags struct {
|
|||||||
branch string
|
branch string
|
||||||
tag string
|
tag string
|
||||||
semver string
|
semver string
|
||||||
|
refName string
|
||||||
|
commit string
|
||||||
username string
|
username string
|
||||||
password string
|
password string
|
||||||
keyAlgorithm flags.PublicKeyAlgorithm
|
keyAlgorithm flags.PublicKeyAlgorithm
|
||||||
keyRSABits flags.RSAKeyBits
|
keyRSABits flags.RSAKeyBits
|
||||||
keyECDSACurve flags.ECDSACurve
|
keyECDSACurve flags.ECDSACurve
|
||||||
secretRef string
|
secretRef string
|
||||||
gitImplementation flags.GitImplementation
|
|
||||||
caFile string
|
caFile string
|
||||||
privateKeyFile string
|
privateKeyFile string
|
||||||
recurseSubmodules bool
|
recurseSubmodules bool
|
||||||
@@ -130,13 +130,14 @@ func init() {
|
|||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.branch, "branch", "", "git branch")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.branch, "branch", "", "git branch")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.tag, "tag", "", "git tag")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.tag, "tag", "", "git tag")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.semver, "tag-semver", "", "git tag semver range")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.semver, "tag-semver", "", "git tag semver range")
|
||||||
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.refName, "ref-name", "", " git reference name")
|
||||||
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.commit, "commit", "", "git commit")
|
||||||
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.username, "username", "u", "", "basic authentication username")
|
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.username, "username", "u", "", "basic authentication username")
|
||||||
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.password, "password", "p", "", "basic authentication password")
|
createSourceGitCmd.Flags().StringVarP(&sourceGitArgs.password, "password", "p", "", "basic authentication password")
|
||||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyAlgorithm, "ssh-key-algorithm", sourceGitArgs.keyAlgorithm.Description())
|
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyAlgorithm, "ssh-key-algorithm", sourceGitArgs.keyAlgorithm.Description())
|
||||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyRSABits, "ssh-rsa-bits", sourceGitArgs.keyRSABits.Description())
|
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyRSABits, "ssh-rsa-bits", sourceGitArgs.keyRSABits.Description())
|
||||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyECDSACurve, "ssh-ecdsa-curve", sourceGitArgs.keyECDSACurve.Description())
|
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyECDSACurve, "ssh-ecdsa-curve", sourceGitArgs.keyECDSACurve.Description())
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.secretRef, "secret-ref", "", "the name of an existing secret containing SSH or basic credentials")
|
||||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.gitImplementation, "git-implementation", sourceGitArgs.gitImplementation.Description())
|
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.caFile, "ca-file", "", "path to TLS CA file used for validating self-signed certificates")
|
||||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
|
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.privateKeyFile, "private-key-file", "", "path to a passwordless private key file used for authenticating to the Git SSH server")
|
||||||
createSourceGitCmd.Flags().BoolVar(&sourceGitArgs.recurseSubmodules, "recurse-submodules", false,
|
createSourceGitCmd.Flags().BoolVar(&sourceGitArgs.recurseSubmodules, "recurse-submodules", false,
|
||||||
@@ -170,18 +171,14 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceGitArgs.branch == "" && sourceGitArgs.tag == "" && sourceGitArgs.semver == "" {
|
if sourceGitArgs.branch == "" && sourceGitArgs.tag == "" && sourceGitArgs.semver == "" && sourceGitArgs.commit == "" && sourceGitArgs.refName == "" {
|
||||||
return fmt.Errorf("a Git ref is required, use one of the following: --branch, --tag or --tag-semver")
|
return fmt.Errorf("a Git ref is required, use one of the following: --branch, --tag, --commit, --ref-name or --tag-semver")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceGitArgs.caFile != "" && u.Scheme == "ssh" {
|
if sourceGitArgs.caFile != "" && u.Scheme == "ssh" {
|
||||||
return fmt.Errorf("specifying a CA file is not supported for Git over SSH")
|
return fmt.Errorf("specifying a CA file is not supported for Git over SSH")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceGitArgs.recurseSubmodules && sourceGitArgs.gitImplementation == sourcev1.LibGit2Implementation {
|
|
||||||
return fmt.Errorf("recurse submodules requires --git-implementation=%s", sourcev1.GoGitImplementation)
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpDir, err := os.MkdirTemp("", name)
|
tmpDir, err := os.MkdirTemp("", name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -220,11 +217,12 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
gitRepository.Spec.Timeout = &metav1.Duration{Duration: createSourceArgs.fetchTimeout}
|
gitRepository.Spec.Timeout = &metav1.Duration{Duration: createSourceArgs.fetchTimeout}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceGitArgs.gitImplementation != "" {
|
if sourceGitArgs.commit != "" {
|
||||||
gitRepository.Spec.GitImplementation = sourceGitArgs.gitImplementation.String()
|
gitRepository.Spec.Reference.Commit = sourceGitArgs.commit
|
||||||
}
|
gitRepository.Spec.Reference.Branch = sourceGitArgs.branch
|
||||||
|
} else if sourceGitArgs.refName != "" {
|
||||||
if sourceGitArgs.semver != "" {
|
gitRepository.Spec.Reference.Name = sourceGitArgs.refName
|
||||||
|
} else if sourceGitArgs.semver != "" {
|
||||||
gitRepository.Spec.Reference.SemVer = sourceGitArgs.semver
|
gitRepository.Spec.Reference.SemVer = sourceGitArgs.semver
|
||||||
} else if sourceGitArgs.tag != "" {
|
} else if sourceGitArgs.tag != "" {
|
||||||
gitRepository.Spec.Reference.Tag = sourceGitArgs.tag
|
gitRepository.Spec.Reference.Tag = sourceGitArgs.tag
|
||||||
@@ -326,8 +324,8 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for GitRepository source reconciliation")
|
logger.Waitingf("waiting for GitRepository source reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
|
isObjectReadyConditionFunc(kubeClient, namespacedName, &gitRepository)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("GitRepository source reconciliation completed")
|
logger.Successf("GitRepository source reconciliation completed")
|
||||||
@@ -369,30 +367,3 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client,
|
|||||||
logger.Successf("GitRepository source updated")
|
logger.Successf("GitRepository source updated")
|
||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGitRepositoryReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, gitRepository *sourcev1.GitRepository) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, gitRepository)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := conditions.Get(gitRepository, meta.ReadyCondition); c != nil {
|
|
||||||
// Confirm the Ready condition we are observing is for the
|
|
||||||
// current generation
|
|
||||||
if c.ObservedGeneration != gitRepository.GetGeneration() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Further check the Status
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,14 +24,15 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pollInterval = 50 * time.Millisecond
|
var pollInterval = 50 * time.Millisecond
|
||||||
@@ -98,6 +99,41 @@ func TestCreateSourceGitExport(t *testing.T) {
|
|||||||
command,
|
command,
|
||||||
assertGoldenFile("testdata/create_source_git/export.golden"),
|
assertGoldenFile("testdata/create_source_git/export.golden"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "no args",
|
||||||
|
args: "create secret git",
|
||||||
|
assert: assertError("name is required"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source with commit",
|
||||||
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --commit=c88a2f41 --interval=1m0s --export",
|
||||||
|
assert: assertGoldenFile("./testdata/create_source_git/source-git-commit.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source with ref name",
|
||||||
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --ref-name=refs/heads/main --interval=1m0s --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_source_git/source-git-refname.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source with branch name and commit",
|
||||||
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --branch=main --commit=c88a2f41 --interval=1m0s --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_source_git/source-git-branch-commit.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source with semver",
|
||||||
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --tag-semver=v1.01 --interval=1m0s --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_source_git/source-git-semver.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source with git tag",
|
||||||
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --tag=test --interval=1m0s --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_source_git/source-git-tag.yaml"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source with git branch",
|
||||||
|
args: "create source git podinfo --namespace=flux-system --url=https://github.com/stefanprodan/podinfo --branch=test --interval=1m0s --export",
|
||||||
|
assert: assertGoldenFile("testdata/create_source_git/source-git-branch.yaml"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
@@ -141,13 +177,25 @@ func TestCreateSourceGit(t *testing.T) {
|
|||||||
repo.Status.Artifact = &sourcev1.Artifact{
|
repo.Status.Artifact = &sourcev1.Artifact{
|
||||||
Path: "some-path",
|
Path: "some-path",
|
||||||
Revision: "v1",
|
Revision: "v1",
|
||||||
|
LastUpdateTime: metav1.Time{
|
||||||
|
Time: time.Now(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
repo.Status.ObservedGeneration = repo.GetGeneration()
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
"Failed",
|
"Failed",
|
||||||
command,
|
command,
|
||||||
assertError("failed message"),
|
assertError("failed message"),
|
||||||
func(repo *sourcev1.GitRepository) {
|
func(repo *sourcev1.GitRepository) {
|
||||||
|
stalledCondition := metav1.Condition{
|
||||||
|
Type: meta.StalledCondition,
|
||||||
|
Status: metav1.ConditionTrue,
|
||||||
|
Reason: sourcev1.URLInvalidReason,
|
||||||
|
Message: "failed message",
|
||||||
|
ObservedGeneration: repo.GetGeneration(),
|
||||||
|
}
|
||||||
|
apimeta.SetStatusCondition(&repo.Status.Conditions, stalledCondition)
|
||||||
newCondition := metav1.Condition{
|
newCondition := metav1.Condition{
|
||||||
Type: meta.ReadyCondition,
|
Type: meta.ReadyCondition,
|
||||||
Status: metav1.ConditionFalse,
|
Status: metav1.ConditionFalse,
|
||||||
@@ -156,6 +204,7 @@ func TestCreateSourceGit(t *testing.T) {
|
|||||||
ObservedGeneration: repo.GetGeneration(),
|
ObservedGeneration: repo.GetGeneration(),
|
||||||
}
|
}
|
||||||
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
||||||
|
repo.Status.ObservedGeneration = repo.GetGeneration()
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
"NoArtifact",
|
"NoArtifact",
|
||||||
@@ -171,6 +220,7 @@ func TestCreateSourceGit(t *testing.T) {
|
|||||||
ObservedGeneration: repo.GetGeneration(),
|
ObservedGeneration: repo.GetGeneration(),
|
||||||
}
|
}
|
||||||
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
||||||
|
repo.Status.ObservedGeneration = repo.GetGeneration()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/runtime/conditions"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
@@ -35,15 +34,15 @@ import (
|
|||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSourceHelmCmd = &cobra.Command{
|
var createSourceHelmCmd = &cobra.Command{
|
||||||
Use: "helm [name]",
|
Use: "helm [name]",
|
||||||
Short: "Create or update a HelmRepository source",
|
Short: "Create or update a HelmRepository source",
|
||||||
Long: `The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
|
Long: withPreviewNote(`The create source helm command generates a HelmRepository resource and waits for it to fetch the index.
|
||||||
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
|
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`),
|
||||||
Example: ` # Create a source for an HTTPS public Helm repository
|
Example: ` # Create a source for an HTTPS public Helm repository
|
||||||
flux create source helm podinfo \
|
flux create source helm podinfo \
|
||||||
--url=https://stefanprodan.github.io/podinfo \
|
--url=https://stefanprodan.github.io/podinfo \
|
||||||
@@ -64,13 +63,13 @@ For private Helm repositories, the basic authentication credentials are stored i
|
|||||||
|
|
||||||
# Create a source for an OCI Helm repository
|
# Create a source for an OCI Helm repository
|
||||||
flux create source helm podinfo \
|
flux create source helm podinfo \
|
||||||
--url=oci://ghcr.io/stefanprodan/charts/podinfo
|
--url=oci://ghcr.io/stefanprodan/charts/podinfo \
|
||||||
--username=username \
|
--username=username \
|
||||||
--password=password
|
--password=password
|
||||||
|
|
||||||
# Create a source for an OCI Helm repository using an existing secret with basic auth or dockerconfig credentials
|
# Create a source for an OCI Helm repository using an existing secret with basic auth or dockerconfig credentials
|
||||||
flux create source helm podinfo \
|
flux create source helm podinfo \
|
||||||
--url=oci://ghcr.io/stefanprodan/charts/podinfo
|
--url=oci://ghcr.io/stefanprodan/charts/podinfo \
|
||||||
--secret-ref=docker-config`,
|
--secret-ref=docker-config`,
|
||||||
RunE: createSourceHelmCmdRun,
|
RunE: createSourceHelmCmdRun,
|
||||||
}
|
}
|
||||||
@@ -83,6 +82,7 @@ type sourceHelmFlags struct {
|
|||||||
keyFile string
|
keyFile string
|
||||||
caFile string
|
caFile string
|
||||||
secretRef string
|
secretRef string
|
||||||
|
ociProvider string
|
||||||
passCredentials bool
|
passCredentials bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +96,7 @@ func init() {
|
|||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.keyFile, "key-file", "", "TLS authentication key file path")
|
||||||
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.caFile, "ca-file", "", "TLS authentication CA file path")
|
||||||
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS, basic auth or docker-config credentials")
|
createSourceHelmCmd.Flags().StringVarP(&sourceHelmArgs.secretRef, "secret-ref", "", "", "the name of an existing secret containing TLS, basic auth or docker-config credentials")
|
||||||
|
createSourceHelmCmd.Flags().StringVar(&sourceHelmArgs.ociProvider, "oci-provider", "", "OCI provider for authentication")
|
||||||
createSourceHelmCmd.Flags().BoolVarP(&sourceHelmArgs.passCredentials, "pass-credentials", "", false, "pass credentials to all domains")
|
createSourceHelmCmd.Flags().BoolVarP(&sourceHelmArgs.passCredentials, "pass-credentials", "", false, "pass credentials to all domains")
|
||||||
|
|
||||||
createSourceCmd.AddCommand(createSourceHelmCmd)
|
createSourceCmd.AddCommand(createSourceHelmCmd)
|
||||||
@@ -143,6 +144,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
if url.Scheme == sourcev1.HelmRepositoryTypeOCI {
|
if url.Scheme == sourcev1.HelmRepositoryTypeOCI {
|
||||||
helmRepository.Spec.Type = sourcev1.HelmRepositoryTypeOCI
|
helmRepository.Spec.Type = sourcev1.HelmRepositoryTypeOCI
|
||||||
|
helmRepository.Spec.Provider = sourceHelmArgs.ociProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
if createSourceArgs.fetchTimeout > 0 {
|
if createSourceArgs.fetchTimeout > 0 {
|
||||||
@@ -228,8 +230,12 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for HelmRepository source reconciliation")
|
logger.Waitingf("waiting for HelmRepository source reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
readyConditionFunc := isObjectReadyConditionFunc(kubeClient, namespacedName, helmRepository)
|
||||||
isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
|
if helmRepository.Spec.Type == sourcev1.HelmRepositoryTypeOCI {
|
||||||
|
// HelmRepository type OCI is a static object.
|
||||||
|
readyConditionFunc = isStaticObjectReadyConditionFunc(kubeClient, namespacedName, helmRepository)
|
||||||
|
}
|
||||||
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true, readyConditionFunc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("HelmRepository source reconciliation completed")
|
logger.Successf("HelmRepository source reconciliation completed")
|
||||||
@@ -276,30 +282,3 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client,
|
|||||||
logger.Successf("source updated")
|
logger.Successf("source updated")
|
||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isHelmRepositoryReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, helmRepository *sourcev1.HelmRepository) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, helmRepository)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := conditions.Get(helmRepository, meta.ReadyCondition); c != nil {
|
|
||||||
// Confirm the Ready condition we are observing is for the
|
|
||||||
// current generation
|
|
||||||
if c.ObservedGeneration != helmRepository.GetGeneration() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Further check the Status
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,18 +29,17 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
"github.com/fluxcd/pkg/apis/meta"
|
"github.com/fluxcd/pkg/apis/meta"
|
||||||
"github.com/fluxcd/pkg/runtime/conditions"
|
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var createSourceOCIRepositoryCmd = &cobra.Command{
|
var createSourceOCIRepositoryCmd = &cobra.Command{
|
||||||
Use: "oci [name]",
|
Use: "oci [name]",
|
||||||
Short: "Create or update an OCIRepository",
|
Short: "Create or update an OCIRepository",
|
||||||
Long: `The create source oci command generates an OCIRepository resource and waits for it to be ready.`,
|
Long: withPreviewNote(`The create source oci command generates an OCIRepository resource and waits for it to be ready.`),
|
||||||
Example: ` # Create an OCIRepository for a public container image
|
Example: ` # Create an OCIRepository for a public container image
|
||||||
flux create source oci podinfo \
|
flux create source oci podinfo \
|
||||||
--url=oci://ghcr.io/stefanprodan/manifests/podinfo \
|
--url=oci://ghcr.io/stefanprodan/manifests/podinfo \
|
||||||
@@ -51,16 +50,18 @@ var createSourceOCIRepositoryCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type sourceOCIRepositoryFlags struct {
|
type sourceOCIRepositoryFlags struct {
|
||||||
url string
|
url string
|
||||||
tag string
|
tag string
|
||||||
semver string
|
semver string
|
||||||
digest string
|
digest string
|
||||||
secretRef string
|
secretRef string
|
||||||
serviceAccount string
|
serviceAccount string
|
||||||
certSecretRef string
|
certSecretRef string
|
||||||
ignorePaths []string
|
verifyProvider flags.SourceOCIVerifyProvider
|
||||||
provider flags.SourceOCIProvider
|
verifySecretRef string
|
||||||
insecure bool
|
ignorePaths []string
|
||||||
|
provider flags.SourceOCIProvider
|
||||||
|
insecure bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var sourceOCIRepositoryArgs = newSourceOCIFlags()
|
var sourceOCIRepositoryArgs = newSourceOCIFlags()
|
||||||
@@ -80,6 +81,8 @@ func init() {
|
|||||||
createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.secretRef, "secret-ref", "", "the name of the Kubernetes image pull secret (type 'kubernetes.io/dockerconfigjson')")
|
createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.secretRef, "secret-ref", "", "the name of the Kubernetes image pull secret (type 'kubernetes.io/dockerconfigjson')")
|
||||||
createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.serviceAccount, "service-account", "", "the name of the Kubernetes service account that refers to an image pull secret")
|
createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.serviceAccount, "service-account", "", "the name of the Kubernetes service account that refers to an image pull secret")
|
||||||
createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.certSecretRef, "cert-ref", "", "the name of a secret to use for TLS certificates")
|
createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.certSecretRef, "cert-ref", "", "the name of a secret to use for TLS certificates")
|
||||||
|
createSourceOCIRepositoryCmd.Flags().Var(&sourceOCIRepositoryArgs.verifyProvider, "verify-provider", sourceOCIRepositoryArgs.verifyProvider.Description())
|
||||||
|
createSourceOCIRepositoryCmd.Flags().StringVar(&sourceOCIRepositoryArgs.verifySecretRef, "verify-secret-ref", "", "the name of a secret to use for signature verification")
|
||||||
createSourceOCIRepositoryCmd.Flags().StringSliceVar(&sourceOCIRepositoryArgs.ignorePaths, "ignore-paths", nil, "set paths to ignore resources (can specify multiple paths with commas: path1,path2)")
|
createSourceOCIRepositoryCmd.Flags().StringSliceVar(&sourceOCIRepositoryArgs.ignorePaths, "ignore-paths", nil, "set paths to ignore resources (can specify multiple paths with commas: path1,path2)")
|
||||||
createSourceOCIRepositoryCmd.Flags().BoolVar(&sourceOCIRepositoryArgs.insecure, "insecure", false, "for when connecting to a non-TLS registries over plain HTTP")
|
createSourceOCIRepositoryCmd.Flags().BoolVar(&sourceOCIRepositoryArgs.insecure, "insecure", false, "for when connecting to a non-TLS registries over plain HTTP")
|
||||||
|
|
||||||
@@ -156,6 +159,19 @@ func createSourceOCIRepositoryCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if provider := sourceOCIRepositoryArgs.verifyProvider.String(); provider != "" {
|
||||||
|
repository.Spec.Verify = &sourcev1.OCIRepositoryVerification{
|
||||||
|
Provider: provider,
|
||||||
|
}
|
||||||
|
if secretName := sourceOCIRepositoryArgs.verifySecretRef; secretName != "" {
|
||||||
|
repository.Spec.Verify.SecretRef = &meta.LocalObjectReference{
|
||||||
|
Name: secretName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if sourceOCIRepositoryArgs.verifySecretRef != "" {
|
||||||
|
return fmt.Errorf("a verification provider must be specified when a secret is specified")
|
||||||
|
}
|
||||||
|
|
||||||
if createArgs.export {
|
if createArgs.export {
|
||||||
return printExport(exportOCIRepository(repository))
|
return printExport(exportOCIRepository(repository))
|
||||||
}
|
}
|
||||||
@@ -175,8 +191,8 @@ func createSourceOCIRepositoryCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Waitingf("waiting for OCIRepository reconciliation")
|
logger.Waitingf("waiting for OCIRepository reconciliation")
|
||||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||||
isOCIRepositoryReady(ctx, kubeClient, namespacedName, repository)); err != nil {
|
isObjectReadyConditionFunc(kubeClient, namespacedName, repository)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger.Successf("OCIRepository reconciliation completed")
|
logger.Successf("OCIRepository reconciliation completed")
|
||||||
@@ -218,30 +234,3 @@ func upsertOCIRepository(ctx context.Context, kubeClient client.Client,
|
|||||||
logger.Successf("OCIRepository updated")
|
logger.Successf("OCIRepository updated")
|
||||||
return namespacedName, nil
|
return namespacedName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOCIRepositoryReady(ctx context.Context, kubeClient client.Client,
|
|
||||||
namespacedName types.NamespacedName, ociRepository *sourcev1.OCIRepository) wait.ConditionFunc {
|
|
||||||
return func() (bool, error) {
|
|
||||||
err := kubeClient.Get(ctx, namespacedName, ociRepository)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := conditions.Get(ociRepository, meta.ReadyCondition); c != nil {
|
|
||||||
// Confirm the Ready condition we are observing is for the
|
|
||||||
// current generation
|
|
||||||
if c.ObservedGeneration != ociRepository.GetGeneration() {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Further check the Status
|
|
||||||
switch c.Status {
|
|
||||||
case metav1.ConditionTrue:
|
|
||||||
return true, nil
|
|
||||||
case metav1.ConditionFalse:
|
|
||||||
return false, fmt.Errorf(c.Message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -36,16 +36,26 @@ func TestCreateSourceOCI(t *testing.T) {
|
|||||||
args: "create source oci podinfo",
|
args: "create source oci podinfo",
|
||||||
assertFunc: assertError("url is required"),
|
assertFunc: assertError("url is required"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "verify provider not specified",
|
||||||
|
args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --verify-secret-ref=cosign-pub",
|
||||||
|
assertFunc: assertError("a verification provider must be specified when a secret is specified"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "export manifest",
|
name: "export manifest",
|
||||||
args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.1.6 --interval 10m --export",
|
args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --interval 10m --export",
|
||||||
assertFunc: assertGoldenFile("./testdata/oci/export.golden"),
|
assertFunc: assertGoldenFile("./testdata/oci/export.golden"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "export manifest with secret",
|
name: "export manifest with secret",
|
||||||
args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.1.6 --interval 10m --secret-ref=creds --export",
|
args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --interval 10m --secret-ref=creds --export",
|
||||||
assertFunc: assertGoldenFile("./testdata/oci/export_with_secret.golden"),
|
assertFunc: assertGoldenFile("./testdata/oci/export_with_secret.golden"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "export manifest with verify secret",
|
||||||
|
args: "create source oci podinfo --url=oci://ghcr.io/stefanprodan/manifests/podinfo --tag=6.3.5 --interval 10m --verify-provider=cosign --verify-secret-ref=cosign-pub --export",
|
||||||
|
assertFunc: assertGoldenFile("./testdata/oci/export_with_verify_secret.golden"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
@@ -37,8 +37,8 @@ import (
|
|||||||
var createTenantCmd = &cobra.Command{
|
var createTenantCmd = &cobra.Command{
|
||||||
Use: "tenant",
|
Use: "tenant",
|
||||||
Short: "Create or update a tenant",
|
Short: "Create or update a tenant",
|
||||||
Long: `The create tenant command generates namespaces, service accounts and role bindings to limit the
|
Long: withPreviewNote(`The create tenant command generates namespaces, service accounts and role bindings to limit the
|
||||||
reconcilers scope to the tenant namespaces.`,
|
reconcilers scope to the tenant namespaces.`),
|
||||||
Example: ` # Create a tenant with access to a namespace
|
Example: ` # Create a tenant with access to a namespace
|
||||||
flux create tenant dev-team \
|
flux create tenant dev-team \
|
||||||
--with-namespace=frontend \
|
--with-namespace=frontend \
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteCmd = &cobra.Command{
|
var deleteCmd = &cobra.Command{
|
||||||
Use: "delete",
|
Use: "delete",
|
||||||
Short: "Delete sources and resources",
|
Short: "Delete sources and resources",
|
||||||
Long: "The delete sub-commands delete sources and resources.",
|
Long: `The delete sub-commands delete sources and resources.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteFlags struct {
|
type deleteFlags struct {
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteAlertCmd = &cobra.Command{
|
var deleteAlertCmd = &cobra.Command{
|
||||||
Use: "alert [name]",
|
Use: "alert [name]",
|
||||||
Short: "Delete a Alert resource",
|
Short: "Delete a Alert resource",
|
||||||
Long: "The delete alert command removes the given Alert from the cluster.",
|
Long: withPreviewNote("The delete alert command removes the given Alert from the cluster."),
|
||||||
Example: ` # Delete an Alert and the Kubernetes resources created by it
|
Example: ` # Delete an Alert and the Kubernetes resources created by it
|
||||||
flux delete alert main`,
|
flux delete alert main`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteAlertProviderCmd = &cobra.Command{
|
var deleteAlertProviderCmd = &cobra.Command{
|
||||||
Use: "alert-provider [name]",
|
Use: "alert-provider [name]",
|
||||||
Short: "Delete a Provider resource",
|
Short: "Delete a Provider resource",
|
||||||
Long: "The delete alert-provider command removes the given Provider from the cluster.",
|
Long: withPreviewNote("The delete alert-provider command removes the given Provider from the cluster."),
|
||||||
Example: ` # Delete a Provider and the Kubernetes resources created by it
|
Example: ` # Delete a Provider and the Kubernetes resources created by it
|
||||||
flux delete alert-provider slack`,
|
flux delete alert-provider slack`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteHelmReleaseCmd = &cobra.Command{
|
var deleteHelmReleaseCmd = &cobra.Command{
|
||||||
Use: "helmrelease [name]",
|
Use: "helmrelease [name]",
|
||||||
Aliases: []string{"hr"},
|
Aliases: []string{"hr"},
|
||||||
Short: "Delete a HelmRelease resource",
|
Short: "Delete a HelmRelease resource",
|
||||||
Long: "The delete helmrelease command removes the given HelmRelease from the cluster.",
|
Long: withPreviewNote("The delete helmrelease command removes the given HelmRelease from the cluster."),
|
||||||
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
Example: ` # Delete a Helm release and the Kubernetes resources created by it
|
||||||
flux delete hr podinfo`,
|
flux delete hr podinfo`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
var deleteImageCmd = &cobra.Command{
|
var deleteImageCmd = &cobra.Command{
|
||||||
Use: "image",
|
Use: "image",
|
||||||
Short: "Delete image automation objects",
|
Short: "Delete image automation objects",
|
||||||
Long: "The delete image sub-commands delete image automation objects.",
|
Long: `The delete image sub-commands delete image automation objects.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImagePolicyCmd = &cobra.Command{
|
var deleteImagePolicyCmd = &cobra.Command{
|
||||||
Use: "policy [name]",
|
Use: "policy [name]",
|
||||||
Short: "Delete an ImagePolicy object",
|
Short: "Delete an ImagePolicy object",
|
||||||
Long: "The delete image policy command deletes the given ImagePolicy from the cluster.",
|
Long: withPreviewNote(`The delete image policy command deletes the given ImagePolicy from the cluster.`),
|
||||||
Example: ` # Delete an image policy
|
Example: ` # Delete an image policy
|
||||||
flux delete image policy alpine3.x`,
|
flux delete image policy alpine3.x`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteImageRepositoryCmd = &cobra.Command{
|
var deleteImageRepositoryCmd = &cobra.Command{
|
||||||
Use: "repository [name]",
|
Use: "repository [name]",
|
||||||
Short: "Delete an ImageRepository object",
|
Short: "Delete an ImageRepository object",
|
||||||
Long: "The delete image repository command deletes the given ImageRepository from the cluster.",
|
Long: withPreviewNote("The delete image repository command deletes the given ImageRepository from the cluster."),
|
||||||
Example: ` # Delete an image repository
|
Example: ` # Delete an image repository
|
||||||
flux delete image repository alpine`,
|
flux delete image repository alpine`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
var deleteImageUpdateCmd = &cobra.Command{
|
var deleteImageUpdateCmd = &cobra.Command{
|
||||||
Use: "update [name]",
|
Use: "update [name]",
|
||||||
Short: "Delete an ImageUpdateAutomation object",
|
Short: "Delete an ImageUpdateAutomation object",
|
||||||
Long: "The delete image update command deletes the given ImageUpdateAutomation from the cluster.",
|
Long: withPreviewNote(`The delete image update command deletes the given ImageUpdateAutomation from the cluster.`),
|
||||||
Example: ` # Delete an image update automation
|
Example: ` # Delete an image update automation
|
||||||
flux delete image update latest-images`,
|
flux delete image update latest-images`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteKsCmd = &cobra.Command{
|
var deleteKsCmd = &cobra.Command{
|
||||||
Use: "kustomization [name]",
|
Use: "kustomization [name]",
|
||||||
Aliases: []string{"ks"},
|
Aliases: []string{"ks"},
|
||||||
Short: "Delete a Kustomization resource",
|
Short: "Delete a Kustomization resource",
|
||||||
Long: "The delete kustomization command deletes the given Kustomization from the cluster.",
|
Long: `The delete kustomization command deletes the given Kustomization from the cluster.`,
|
||||||
Example: ` # Delete a kustomization and the Kubernetes resources created by it when prune is enabled
|
Example: ` # Delete a kustomization and the Kubernetes resources created by it when prune is enabled
|
||||||
flux delete kustomization podinfo`,
|
flux delete kustomization podinfo`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteReceiverCmd = &cobra.Command{
|
var deleteReceiverCmd = &cobra.Command{
|
||||||
Use: "receiver [name]",
|
Use: "receiver [name]",
|
||||||
Short: "Delete a Receiver resource",
|
Short: "Delete a Receiver resource",
|
||||||
Long: "The delete receiver command removes the given Receiver from the cluster.",
|
Long: `The delete receiver command removes the given Receiver from the cluster.`,
|
||||||
Example: ` # Delete an Receiver and the Kubernetes resources created by it
|
Example: ` # Delete an Receiver and the Kubernetes resources created by it
|
||||||
flux delete receiver main`,
|
flux delete receiver main`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
var deleteSourceCmd = &cobra.Command{
|
var deleteSourceCmd = &cobra.Command{
|
||||||
Use: "source",
|
Use: "source",
|
||||||
Short: "Delete sources",
|
Short: "Delete sources",
|
||||||
Long: "The delete source sub-commands delete sources.",
|
Long: `The delete source sub-commands delete sources.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
var deleteSourceBucketCmd = &cobra.Command{
|
var deleteSourceBucketCmd = &cobra.Command{
|
||||||
Use: "bucket [name]",
|
Use: "bucket [name]",
|
||||||
Short: "Delete a Bucket source",
|
Short: "Delete a Bucket source",
|
||||||
Long: "The delete source bucket command deletes the given Bucket from the cluster.",
|
Long: withPreviewNote("The delete source bucket command deletes the given Bucket from the cluster."),
|
||||||
Example: ` # Delete a Bucket source
|
Example: ` # Delete a Bucket source
|
||||||
flux delete source bucket podinfo`,
|
flux delete source bucket podinfo`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)),
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ package main
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var deleteSourceGitCmd = &cobra.Command{
|
var deleteSourceGitCmd = &cobra.Command{
|
||||||
Use: "git [name]",
|
Use: "git [name]",
|
||||||
Short: "Delete a GitRepository source",
|
Short: "Delete a GitRepository source",
|
||||||
Long: "The delete source git command deletes the given GitRepository from the cluster.",
|
Long: `The delete source git command deletes the given GitRepository from the cluster.`,
|
||||||
Example: ` # Delete a Git repository
|
Example: ` # Delete a Git repository
|
||||||
flux delete source git podinfo`,
|
flux delete source git podinfo`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
var deleteSourceHelmCmd = &cobra.Command{
|
var deleteSourceHelmCmd = &cobra.Command{
|
||||||
Use: "helm [name]",
|
Use: "helm [name]",
|
||||||
Short: "Delete a HelmRepository source",
|
Short: "Delete a HelmRepository source",
|
||||||
Long: "The delete source helm command deletes the given HelmRepository from the cluster.",
|
Long: withPreviewNote("The delete source helm command deletes the given HelmRepository from the cluster."),
|
||||||
Example: ` # Delete a Helm repository
|
Example: ` # Delete a Helm repository
|
||||||
flux delete source helm podinfo`,
|
flux delete source helm podinfo`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)),
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
var deleteSourceOCIRepositoryCmd = &cobra.Command{
|
var deleteSourceOCIRepositoryCmd = &cobra.Command{
|
||||||
Use: "oci [name]",
|
Use: "oci [name]",
|
||||||
Short: "Delete an OCIRepository source",
|
Short: "Delete an OCIRepository source",
|
||||||
Long: "The delete source oci command deletes the given OCIRepository from the cluster.",
|
Long: withPreviewNote("The delete source oci command deletes the given OCIRepository from the cluster."),
|
||||||
Example: ` # Delete an OCIRepository
|
Example: ` # Delete an OCIRepository
|
||||||
flux delete source oci podinfo`,
|
flux delete source oci podinfo`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.OCIRepositoryKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.OCIRepositoryKind)),
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
var diffCmd = &cobra.Command{
|
var diffCmd = &cobra.Command{
|
||||||
Use: "diff",
|
Use: "diff",
|
||||||
Short: "Diff a flux resource",
|
Short: "Diff a flux resource",
|
||||||
Long: "The diff command is used to do a server-side dry-run on flux resources, then prints the diff.",
|
Long: `The diff command is used to do a server-side dry-run on flux resources, then prints the diff.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -21,16 +21,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
|
||||||
oci "github.com/fluxcd/pkg/oci/client"
|
oci "github.com/fluxcd/pkg/oci/client"
|
||||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||||
)
|
)
|
||||||
|
|
||||||
var diffArtifactCmd = &cobra.Command{
|
var diffArtifactCmd = &cobra.Command{
|
||||||
Use: "artifact",
|
Use: "artifact",
|
||||||
Short: "Diff Artifact",
|
Short: "Diff Artifact",
|
||||||
Long: `The diff artifact command computes the diff between the remote OCI artifact and a local directory or file`,
|
Long: withPreviewNote(`The diff artifact command computes the diff between the remote OCI artifact and a local directory or file`),
|
||||||
Example: `# Check if local files differ from remote
|
Example: `# Check if local files differ from remote
|
||||||
flux diff artifact oci://ghcr.io/stefanprodan/manifests:podinfo:6.2.0 --path=./kustomize`,
|
flux diff artifact oci://ghcr.io/stefanprodan/manifests:podinfo:6.2.0 --path=./kustomize`,
|
||||||
RunE: diffArtifactCmdRun,
|
RunE: diffArtifactCmdRun,
|
||||||
@@ -81,7 +82,7 @@ func diffArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
ociClient := oci.NewLocalClient()
|
ociClient := oci.NewClient(oci.DefaultOptions())
|
||||||
|
|
||||||
if diffArtifactArgs.provider.String() == sourcev1.GenericOCIProvider && diffArtifactArgs.creds != "" {
|
if diffArtifactArgs.provider.String() == sourcev1.GenericOCIProvider && diffArtifactArgs.creds != "" {
|
||||||
logger.Actionf("logging in to registry with credentials")
|
logger.Actionf("logging in to registry with credentials")
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/build"
|
"github.com/fluxcd/flux2/v2/internal/build"
|
||||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var diffKsCmd = &cobra.Command{
|
var diffKsCmd = &cobra.Command{
|
||||||
@@ -37,7 +37,13 @@ Exit status: 0 No differences were found. 1 Differences were found. >1 diff fail
|
|||||||
flux diff kustomization my-app --path ./path/to/local/manifests
|
flux diff kustomization my-app --path ./path/to/local/manifests
|
||||||
|
|
||||||
# Preview using a local flux kustomization file
|
# Preview using a local flux kustomization file
|
||||||
flux diff kustomization my-app --path ./path/to/local/manifests --kustomization-file ./path/to/local/my-app.yaml`,
|
flux diff kustomization my-app --path ./path/to/local/manifests \
|
||||||
|
--kustomization-file ./path/to/local/my-app.yaml
|
||||||
|
|
||||||
|
# Exclude files by providing a comma separated list of entries that follow the .gitignore pattern fromat.
|
||||||
|
flux diff kustomization my-app --path ./path/to/local/manifests \
|
||||||
|
--kustomization-file ./path/to/local/my-app.yaml \
|
||||||
|
--ignore-paths "/to_ignore/**/*.yaml,ignore.yaml"`,
|
||||||
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||||
RunE: diffKsCmdRun,
|
RunE: diffKsCmdRun,
|
||||||
}
|
}
|
||||||
@@ -45,6 +51,7 @@ flux diff kustomization my-app --path ./path/to/local/manifests --kustomization-
|
|||||||
type diffKsFlags struct {
|
type diffKsFlags struct {
|
||||||
kustomizationFile string
|
kustomizationFile string
|
||||||
path string
|
path string
|
||||||
|
ignorePaths []string
|
||||||
progressBar bool
|
progressBar bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +60,7 @@ var diffKsArgs diffKsFlags
|
|||||||
func init() {
|
func init() {
|
||||||
diffKsCmd.Flags().StringVar(&diffKsArgs.path, "path", "", "Path to a local directory that matches the specified Kustomization.spec.path.")
|
diffKsCmd.Flags().StringVar(&diffKsArgs.path, "path", "", "Path to a local directory that matches the specified Kustomization.spec.path.")
|
||||||
diffKsCmd.Flags().BoolVar(&diffKsArgs.progressBar, "progress-bar", true, "Boolean to set the progress bar. The default value is true.")
|
diffKsCmd.Flags().BoolVar(&diffKsArgs.progressBar, "progress-bar", true, "Boolean to set the progress bar. The default value is true.")
|
||||||
|
diffKsCmd.Flags().StringSliceVar(&diffKsArgs.ignorePaths, "ignore-paths", nil, "set paths to ignore in .gitignore format")
|
||||||
diffKsCmd.Flags().StringVar(&diffKsArgs.kustomizationFile, "kustomization-file", "", "Path to the Flux Kustomization YAML file.")
|
diffKsCmd.Flags().StringVar(&diffKsArgs.kustomizationFile, "kustomization-file", "", "Path to the Flux Kustomization YAML file.")
|
||||||
diffCmd.AddCommand(diffKsCmd)
|
diffCmd.AddCommand(diffKsCmd)
|
||||||
}
|
}
|
||||||
@@ -86,12 +94,16 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
||||||
build.WithTimeout(rootArgs.timeout),
|
build.WithTimeout(rootArgs.timeout),
|
||||||
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
|
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
|
||||||
build.WithProgressBar())
|
build.WithProgressBar(),
|
||||||
|
build.WithIgnore(diffKsArgs.ignorePaths),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
builder, err = build.NewBuilder(name, diffKsArgs.path,
|
builder, err = build.NewBuilder(name, diffKsArgs.path,
|
||||||
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
||||||
build.WithTimeout(rootArgs.timeout),
|
build.WithTimeout(rootArgs.timeout),
|
||||||
build.WithKustomizationFile(diffKsArgs.kustomizationFile))
|
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
|
||||||
|
build.WithIgnore(diffKsArgs.ignorePaths),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/build"
|
"github.com/fluxcd/flux2/v2/internal/build"
|
||||||
"github.com/fluxcd/pkg/ssa"
|
"github.com/fluxcd/pkg/ssa"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
)
|
)
|
||||||
@@ -91,6 +91,12 @@ func TestDiffKustomization(t *testing.T) {
|
|||||||
objectFile: "./testdata/diff-kustomization/stringdata-sops-secret.yaml",
|
objectFile: "./testdata/diff-kustomization/stringdata-sops-secret.yaml",
|
||||||
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-stringdata-sops-secret.golden"),
|
assert: assertGoldenFile("./testdata/diff-kustomization/diff-with-drifted-stringdata-sops-secret.golden"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "diff where kustomization file has multiple objects with the same name",
|
||||||
|
args: "diff kustomization podinfo --path ./testdata/build-kustomization/podinfo --progress-bar=false --kustomization-file ./testdata/diff-kustomization/flux-kustomization-multiobj.yaml",
|
||||||
|
objectFile: "",
|
||||||
|
assert: assertGoldenFile("./testdata/diff-kustomization/nothing-is-deployed.golden"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := map[string]string{
|
tmpl := map[string]string{
|
||||||
@@ -109,7 +115,9 @@ func TestDiffKustomization(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if tt.objectFile != "" {
|
if tt.objectFile != "" {
|
||||||
resourceManager.ApplyAll(context.Background(), createObjectFromFile(tt.objectFile, tmpl, t), ssa.DefaultApplyOptions())
|
if _, err := resourceManager.ApplyAll(context.Background(), createObjectFromFile(tt.objectFile, tmpl, t), ssa.DefaultApplyOptions()); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cmd := cmdTestCase{
|
cmd := cmdTestCase{
|
||||||
args: tt.args + " -n " + tmpl["fluxns"],
|
args: tt.args + " -n " + tmpl["fluxns"],
|
||||||
|
|||||||
547
cmd/flux/events.go
Normal file
547
cmd/flux/events.go
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Kubernetes Authors.
|
||||||
|
Copyright 2023 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/duration"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
|
runtimeresource "k8s.io/cli-runtime/pkg/resource"
|
||||||
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
|
helmv2 "github.com/fluxcd/helm-controller/api/v2beta2"
|
||||||
|
autov1 "github.com/fluxcd/image-automation-controller/api/v1beta1"
|
||||||
|
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta2"
|
||||||
|
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||||
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||||
|
notificationv1b3 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||||
|
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||||
|
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
|
"github.com/fluxcd/flux2/v2/pkg/printers"
|
||||||
|
)
|
||||||
|
|
||||||
|
var eventsCmd = &cobra.Command{
|
||||||
|
Use: "events",
|
||||||
|
Short: "Display Kubernetes events for Flux resources",
|
||||||
|
Long: withPreviewNote("The events sub-command shows Kubernetes events from Flux resources"),
|
||||||
|
Example: ` # Display events for flux resources in default namespace
|
||||||
|
flux events -n default
|
||||||
|
|
||||||
|
# Display events for flux resources in all namespaces
|
||||||
|
flux events -A
|
||||||
|
|
||||||
|
# Display events for a Kustomization named podinfo
|
||||||
|
flux events --for Kustomization/podinfo
|
||||||
|
|
||||||
|
# Display events for all Kustomizations in default namespace
|
||||||
|
flux events --for Kustomization -n default
|
||||||
|
|
||||||
|
# Display warning events for alert resources
|
||||||
|
flux events --for Alert/podinfo --types warning
|
||||||
|
`,
|
||||||
|
RunE: eventsCmdRun,
|
||||||
|
}
|
||||||
|
|
||||||
|
type eventFlags struct {
|
||||||
|
allNamespaces bool
|
||||||
|
watch bool
|
||||||
|
forSelector string
|
||||||
|
filterTypes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventArgs eventFlags
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
eventsCmd.Flags().BoolVarP(&eventArgs.allNamespaces, "all-namespaces", "A", false,
|
||||||
|
"display events from Flux resources across all namespaces")
|
||||||
|
eventsCmd.Flags().BoolVarP(&eventArgs.watch, "watch", "w", false,
|
||||||
|
"indicate if the events should be streamed")
|
||||||
|
eventsCmd.Flags().StringVar(&eventArgs.forSelector, "for", "",
|
||||||
|
"get events for a particular object")
|
||||||
|
eventsCmd.Flags().StringSliceVar(&eventArgs.filterTypes, "types", []string{}, "filter events for certain types (valid types are: Normal, Warning)")
|
||||||
|
rootCmd.AddCommand(eventsCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func eventsCmdRun(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := validateEventTypes(eventArgs.filterTypes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
kubeclient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace := *kubeconfigArgs.Namespace
|
||||||
|
if eventArgs.allNamespaces {
|
||||||
|
namespace = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var diffRefNs bool
|
||||||
|
clientListOpts := []client.ListOption{client.InNamespace(*kubeconfigArgs.Namespace)}
|
||||||
|
var refListOpts [][]client.ListOption
|
||||||
|
if eventArgs.forSelector != "" {
|
||||||
|
kind, name := getKindNameFromSelector(eventArgs.forSelector)
|
||||||
|
if kind == "" {
|
||||||
|
return fmt.Errorf("--for selector must be of format <kind>[/<name>]")
|
||||||
|
}
|
||||||
|
|
||||||
|
refInfoKind, err := fluxKindMap.getRefInfo(kind)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
clientListOpts = append(clientListOpts, getListOpt(refInfoKind.gvk.Kind, name))
|
||||||
|
if name != "" {
|
||||||
|
refs, err := getObjectRef(ctx, kubeclient, refInfoKind, name, *kubeconfigArgs.Namespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ref := range refs {
|
||||||
|
refKind, refName, refNs := utils.ParseObjectKindNameNamespace(ref)
|
||||||
|
if refNs != namespace {
|
||||||
|
diffRefNs = true
|
||||||
|
}
|
||||||
|
refOpt := []client.ListOption{getListOpt(refKind, refName), client.InNamespace(refNs)}
|
||||||
|
refListOpts = append(refListOpts, refOpt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showNamespace := namespace == "" || diffRefNs
|
||||||
|
if eventArgs.watch {
|
||||||
|
return eventsCmdWatchRun(ctx, kubeclient, clientListOpts, refListOpts, showNamespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := getRows(ctx, kubeclient, clientListOpts, refListOpts, showNamespace)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(rows) == 0 {
|
||||||
|
if eventArgs.allNamespaces {
|
||||||
|
logger.Failuref("No events found.")
|
||||||
|
} else {
|
||||||
|
logger.Failuref("No events found in %s namespace.", *kubeconfigArgs.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
headers := getHeaders(showNamespace)
|
||||||
|
return printers.TablePrinter(headers).Print(cmd.OutOrStdout(), rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRows(ctx context.Context, kubeclient client.Client, clientListOpts []client.ListOption, refListOpts [][]client.ListOption, showNs bool) ([][]string, error) {
|
||||||
|
el := &corev1.EventList{}
|
||||||
|
if err := addEventsToList(ctx, kubeclient, el, clientListOpts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, refOpts := range refListOpts {
|
||||||
|
if err := addEventsToList(ctx, kubeclient, el, refOpts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(SortableEvents(el.Items))
|
||||||
|
|
||||||
|
var rows [][]string
|
||||||
|
for _, item := range el.Items {
|
||||||
|
if ignoreEvent(item) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rows = append(rows, getEventRow(item, showNs))
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addEventsToList(ctx context.Context, kubeclient client.Client, el *corev1.EventList, clientListOpts []client.ListOption) error {
|
||||||
|
listOpts := &metav1.ListOptions{}
|
||||||
|
clientListOpts = append(clientListOpts, client.Limit(cmdutil.DefaultChunkSize))
|
||||||
|
err := runtimeresource.FollowContinue(listOpts,
|
||||||
|
func(options metav1.ListOptions) (runtime.Object, error) {
|
||||||
|
newEvents := &corev1.EventList{}
|
||||||
|
if err := kubeclient.List(ctx, newEvents, clientListOpts...); err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting events: %w", err)
|
||||||
|
}
|
||||||
|
el.Items = append(el.Items, newEvents.Items...)
|
||||||
|
return newEvents, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListOpt(kind, name string) client.ListOption {
|
||||||
|
var sel fields.Selector
|
||||||
|
if name == "" {
|
||||||
|
sel = fields.OneTermEqualSelector("involvedObject.kind", kind)
|
||||||
|
} else {
|
||||||
|
sel = fields.AndSelectors(
|
||||||
|
fields.OneTermEqualSelector("involvedObject.kind", kind),
|
||||||
|
fields.OneTermEqualSelector("involvedObject.name", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
return client.MatchingFieldsSelector{Selector: sel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func eventsCmdWatchRun(ctx context.Context, kubeclient client.WithWatch, listOpts []client.ListOption, refListOpts [][]client.ListOption, showNs bool) error {
|
||||||
|
event := &corev1.EventList{}
|
||||||
|
listOpts = append(listOpts, client.Limit(cmdutil.DefaultChunkSize))
|
||||||
|
eventWatch, err := kubeclient.Watch(ctx, event, listOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
firstIteration := true
|
||||||
|
|
||||||
|
handleEvent := func(e watch.Event) error {
|
||||||
|
if e.Type == watch.Deleted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
event, ok := e.Object.(*corev1.Event)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ignoreEvent(*event) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rows := getEventRow(*event, showNs)
|
||||||
|
var hdr []string
|
||||||
|
if firstIteration {
|
||||||
|
hdr = getHeaders(showNs)
|
||||||
|
firstIteration = false
|
||||||
|
}
|
||||||
|
return printers.TablePrinter(hdr).Print(os.Stdout, [][]string{rows})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, refOpts := range refListOpts {
|
||||||
|
refEventWatch, err := kubeclient.Watch(ctx, event, refOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if err := receiveEventChan(ctx, refEventWatch, handleEvent); err != nil {
|
||||||
|
logger.Failuref("error watching events: %s", err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
return receiveEventChan(ctx, eventWatch, handleEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveEventChan(ctx context.Context, eventWatch watch.Interface, f func(e watch.Event) error) error {
|
||||||
|
defer eventWatch.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case e, ok := <-eventWatch.ResultChan():
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := f(e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHeaders(showNs bool) []string {
|
||||||
|
headers := []string{"Last seen", "Type", "Reason", "Object", "Message"}
|
||||||
|
if showNs {
|
||||||
|
headers = append(namespaceHeader, headers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEventRow(e corev1.Event, showNs bool) []string {
|
||||||
|
var row []string
|
||||||
|
if showNs {
|
||||||
|
row = []string{e.Namespace}
|
||||||
|
}
|
||||||
|
row = append(row, getLastSeen(e), e.Type, e.Reason, fmt.Sprintf("%s/%s", e.InvolvedObject.Kind, e.InvolvedObject.Name), e.Message)
|
||||||
|
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
// getObjectRef is used to get the metadata of a resource that the selector(in the format <kind/name>) references.
|
||||||
|
// It returns an empty string if the resource doesn't reference any resource
|
||||||
|
// and a string with the format `<kind>/<name>.<namespace>` if it does.
|
||||||
|
func getObjectRef(ctx context.Context, kubeclient client.Client, ref refInfo, name, ns string) ([]string, error) {
|
||||||
|
// the resource has no source ref
|
||||||
|
if len(ref.field) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := &unstructured.Unstructured{}
|
||||||
|
obj.SetGroupVersionKind(schema.GroupVersionKind{
|
||||||
|
Kind: ref.gvk.Kind,
|
||||||
|
Version: ref.gvk.Version,
|
||||||
|
Group: ref.gvk.Group,
|
||||||
|
})
|
||||||
|
objName := types.NamespacedName{
|
||||||
|
Namespace: ns,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := kubeclient.Get(ctx, objName, obj); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
refKind := ref.kind
|
||||||
|
if refKind == "" {
|
||||||
|
kindField := append(ref.field, "kind")
|
||||||
|
specKind, ok, err := unstructured.NestedString(obj.Object, kindField...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("field '%s' for '%s' not found", strings.Join(kindField, "."), objName)
|
||||||
|
}
|
||||||
|
refKind = specKind
|
||||||
|
}
|
||||||
|
|
||||||
|
nameField := append(ref.field, "name")
|
||||||
|
refName, ok, err := unstructured.NestedString(obj.Object, nameField...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("field '%s' for '%s' not found", strings.Join(nameField, "."), objName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var allRefs []string
|
||||||
|
refNamespace := ns
|
||||||
|
if ref.crossNamespaced {
|
||||||
|
namespaceField := append(ref.field, "namespace")
|
||||||
|
namespace, ok, err := unstructured.NestedString(obj.Object, namespaceField...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
refNamespace = namespace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allRefs = append(allRefs, fmt.Sprintf("%s/%s.%s", refKind, refName, refNamespace))
|
||||||
|
if ref.otherRefs != nil {
|
||||||
|
for _, otherRef := range ref.otherRefs(ns, name) {
|
||||||
|
allRefs = append(allRefs, fmt.Sprintf("%s.%s", otherRef, refNamespace))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allRefs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type refMap map[string]refInfo
|
||||||
|
|
||||||
|
func (r refMap) getRefInfo(kind string) (refInfo, error) {
|
||||||
|
for key, ref := range r {
|
||||||
|
if strings.EqualFold(key, kind) {
|
||||||
|
return ref, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return refInfo{}, fmt.Errorf("'%s' is not a recognized Flux kind", kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r refMap) hasKind(kind string) bool {
|
||||||
|
_, err := r.getRefInfo(kind)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateEventTypes checks that the event types passed into the function
|
||||||
|
// is either equal to `Normal` or `Warning` which are currently the two supported types.
|
||||||
|
// https://github.com/kubernetes/kubernetes/blob/a8a1abc25cad87333840cd7d54be2efaf31a3177/staging/src/k8s.io/api/core/v1/types.go#L6212
|
||||||
|
func validateEventTypes(eventTypes []string) error {
|
||||||
|
for _, t := range eventTypes {
|
||||||
|
if !strings.EqualFold(corev1.EventTypeWarning, t) && !strings.EqualFold(corev1.EventTypeNormal, t) {
|
||||||
|
return fmt.Errorf("type '%s' not supported. Supported types are Normal, Warning", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type refInfo struct {
|
||||||
|
// gvk is the group version kind of the resource
|
||||||
|
gvk schema.GroupVersionKind
|
||||||
|
// kind is the kind that the resource references if it's not static
|
||||||
|
kind string
|
||||||
|
// crossNamespaced indicates if this resource uses cross namespaced references
|
||||||
|
crossNamespaced bool
|
||||||
|
// otherRefs returns other reference that might not be directly accessible
|
||||||
|
// from the spec of the object
|
||||||
|
otherRefs func(namespace, name string) []string
|
||||||
|
field []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var fluxKindMap = refMap{
|
||||||
|
kustomizev1.KustomizationKind: {
|
||||||
|
gvk: kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind),
|
||||||
|
crossNamespaced: true,
|
||||||
|
field: []string{"spec", "sourceRef"},
|
||||||
|
},
|
||||||
|
helmv2.HelmReleaseKind: {
|
||||||
|
gvk: helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind),
|
||||||
|
crossNamespaced: true,
|
||||||
|
otherRefs: func(namespace, name string) []string {
|
||||||
|
return []string{fmt.Sprintf("%s/%s-%s", sourcev1b2.HelmChartKind, namespace, name)}
|
||||||
|
},
|
||||||
|
field: []string{"spec", "chart", "spec", "sourceRef"},
|
||||||
|
},
|
||||||
|
notificationv1b3.AlertKind: {
|
||||||
|
gvk: notificationv1b3.GroupVersion.WithKind(notificationv1b3.AlertKind),
|
||||||
|
kind: notificationv1b3.ProviderKind,
|
||||||
|
crossNamespaced: false,
|
||||||
|
field: []string{"spec", "providerRef"},
|
||||||
|
},
|
||||||
|
notificationv1.ReceiverKind: {gvk: notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)},
|
||||||
|
notificationv1b3.ProviderKind: {gvk: notificationv1b3.GroupVersion.WithKind(notificationv1b3.ProviderKind)},
|
||||||
|
imagev1.ImagePolicyKind: {
|
||||||
|
gvk: imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind),
|
||||||
|
kind: imagev1.ImageRepositoryKind,
|
||||||
|
crossNamespaced: true,
|
||||||
|
field: []string{"spec", "imageRepositoryRef"},
|
||||||
|
},
|
||||||
|
sourcev1b2.HelmChartKind: {
|
||||||
|
gvk: sourcev1b2.GroupVersion.WithKind(sourcev1b2.HelmChartKind),
|
||||||
|
crossNamespaced: true,
|
||||||
|
field: []string{"spec", "sourceRef"},
|
||||||
|
},
|
||||||
|
sourcev1.GitRepositoryKind: {gvk: sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)},
|
||||||
|
sourcev1b2.OCIRepositoryKind: {gvk: sourcev1b2.GroupVersion.WithKind(sourcev1b2.OCIRepositoryKind)},
|
||||||
|
sourcev1b2.BucketKind: {gvk: sourcev1b2.GroupVersion.WithKind(sourcev1b2.BucketKind)},
|
||||||
|
sourcev1b2.HelmRepositoryKind: {gvk: sourcev1b2.GroupVersion.WithKind(sourcev1b2.HelmRepositoryKind)},
|
||||||
|
autov1.ImageUpdateAutomationKind: {gvk: autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)},
|
||||||
|
imagev1.ImageRepositoryKind: {gvk: imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)},
|
||||||
|
}
|
||||||
|
|
||||||
|
func ignoreEvent(e corev1.Event) bool {
|
||||||
|
if !fluxKindMap.hasKind(e.InvolvedObject.Kind) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(eventArgs.filterTypes) > 0 {
|
||||||
|
_, equal := utils.ContainsEqualFoldItemString(eventArgs.filterTypes, e.Type)
|
||||||
|
if !equal {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKindNameFromSelector(selector string) (string, string) {
|
||||||
|
kind, name := utils.ParseObjectKindName(selector)
|
||||||
|
// if there's no slash in the selector utils.ParseObjectKindName returns the
|
||||||
|
// input string as the name but here we want it as the kind instead
|
||||||
|
if kind == "" && name != "" {
|
||||||
|
kind = name
|
||||||
|
name = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return kind, name
|
||||||
|
}
|
||||||
|
|
||||||
|
// The functions below are copied from: https://github.com/kubernetes/kubectl/blob/4ecd7bd0f0799f191335a331ca3c6a397a888233/pkg/cmd/events/events.go#L294
|
||||||
|
|
||||||
|
// SortableEvents implements sort.Interface for []api.Event by time
|
||||||
|
type SortableEvents []corev1.Event
|
||||||
|
|
||||||
|
func (list SortableEvents) Len() int {
|
||||||
|
return len(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (list SortableEvents) Swap(i, j int) {
|
||||||
|
list[i], list[j] = list[j], list[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the time that should be used for sorting, which can come from
|
||||||
|
// various places in corev1.Event.
|
||||||
|
func eventTime(event corev1.Event) time.Time {
|
||||||
|
if event.Series != nil {
|
||||||
|
return event.Series.LastObservedTime.Time
|
||||||
|
}
|
||||||
|
if !event.LastTimestamp.Time.IsZero() {
|
||||||
|
return event.LastTimestamp.Time
|
||||||
|
}
|
||||||
|
return event.EventTime.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (list SortableEvents) Less(i, j int) bool {
|
||||||
|
return eventTime(list[i]).Before(eventTime(list[j]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLastSeen(e corev1.Event) string {
|
||||||
|
var interval string
|
||||||
|
firstTimestampSince := translateMicroTimestampSince(e.EventTime)
|
||||||
|
if e.EventTime.IsZero() {
|
||||||
|
firstTimestampSince = translateTimestampSince(e.FirstTimestamp)
|
||||||
|
}
|
||||||
|
if e.Series != nil {
|
||||||
|
interval = fmt.Sprintf("%s (x%d over %s)", translateMicroTimestampSince(e.Series.LastObservedTime), e.Series.Count, firstTimestampSince)
|
||||||
|
} else if e.Count > 1 {
|
||||||
|
interval = fmt.Sprintf("%s (x%d over %s)", translateTimestampSince(e.LastTimestamp), e.Count, firstTimestampSince)
|
||||||
|
} else {
|
||||||
|
interval = firstTimestampSince
|
||||||
|
}
|
||||||
|
|
||||||
|
return interval
|
||||||
|
}
|
||||||
|
|
||||||
|
// translateMicroTimestampSince returns the elapsed time since timestamp in
|
||||||
|
// human-readable approximation.
|
||||||
|
func translateMicroTimestampSince(timestamp metav1.MicroTime) string {
|
||||||
|
if timestamp.IsZero() {
|
||||||
|
return "<unknown>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return duration.HumanDuration(time.Since(timestamp.Time))
|
||||||
|
}
|
||||||
|
|
||||||
|
// translateTimestampSince returns the elapsed time since timestamp in
|
||||||
|
// human-readable approximation.
|
||||||
|
func translateTimestampSince(timestamp metav1.Time) string {
|
||||||
|
if timestamp.IsZero() {
|
||||||
|
return "<unknown>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return duration.HumanDuration(time.Since(timestamp.Time))
|
||||||
|
}
|
||||||
438
cmd/flux/events_test.go
Normal file
438
cmd/flux/events_test.go
Normal file
@@ -0,0 +1,438 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 The Kubernetes Authors.
|
||||||
|
Copyright 2023 The Flux authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
|
|
||||||
|
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||||
|
"github.com/fluxcd/pkg/ssa"
|
||||||
|
|
||||||
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var objects = `
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: flux-system
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 5m0s
|
||||||
|
path: ./infrastructure/
|
||||||
|
prune: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
---
|
||||||
|
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||||
|
kind: Kustomization
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
interval: 5m0s
|
||||||
|
path: ./infrastructure/
|
||||||
|
prune: true
|
||||||
|
sourceRef:
|
||||||
|
kind: GitRepository
|
||||||
|
name: flux-system
|
||||||
|
namespace: flux-system
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1
|
||||||
|
kind: GitRepository
|
||||||
|
metadata:
|
||||||
|
name: flux-system
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 5m0s
|
||||||
|
ref:
|
||||||
|
branch: main
|
||||||
|
secretRef:
|
||||||
|
name: flux-system
|
||||||
|
timeout: 1m0s
|
||||||
|
url: ssh://git@github.com/example/repo
|
||||||
|
---
|
||||||
|
apiVersion: helm.toolkit.fluxcd.io/v2beta2
|
||||||
|
kind: HelmRelease
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
chart:
|
||||||
|
spec:
|
||||||
|
chart: podinfo
|
||||||
|
reconcileStrategy: ChartVersion
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
version: '*'
|
||||||
|
interval: 5m0s
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||||
|
kind: HelmRepository
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
interval: 1m0s
|
||||||
|
url: https://stefanprodan.github.io/podinfo
|
||||||
|
---
|
||||||
|
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||||
|
kind: HelmChart
|
||||||
|
metadata:
|
||||||
|
name: default-podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
chart: podinfo
|
||||||
|
interval: 1m0s
|
||||||
|
reconcileStrategy: ChartVersion
|
||||||
|
sourceRef:
|
||||||
|
kind: HelmRepository
|
||||||
|
name: podinfo-chart
|
||||||
|
version: '*'
|
||||||
|
---
|
||||||
|
apiVersion: notification.toolkit.fluxcd.io/v1beta3
|
||||||
|
kind: Alert
|
||||||
|
metadata:
|
||||||
|
name: webapp
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
eventSeverity: info
|
||||||
|
eventSources:
|
||||||
|
- kind: GitRepository
|
||||||
|
name: '*'
|
||||||
|
providerRef:
|
||||||
|
name: slack
|
||||||
|
---
|
||||||
|
apiVersion: notification.toolkit.fluxcd.io/v1beta3
|
||||||
|
kind: Provider
|
||||||
|
metadata:
|
||||||
|
name: slack
|
||||||
|
namespace: flux-system
|
||||||
|
spec:
|
||||||
|
address: https://hooks.slack.com/services/mock
|
||||||
|
type: slack
|
||||||
|
---
|
||||||
|
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||||
|
kind: ImagePolicy
|
||||||
|
metadata:
|
||||||
|
name: podinfo
|
||||||
|
namespace: default
|
||||||
|
spec:
|
||||||
|
imageRepositoryRef:
|
||||||
|
name: acr-podinfo
|
||||||
|
namespace: flux-system
|
||||||
|
policy:
|
||||||
|
semver:
|
||||||
|
range: 5.0.x
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: flux-system`
|
||||||
|
|
||||||
|
func Test_getObjectRef(t *testing.T) {
|
||||||
|
g := NewWithT(t)
|
||||||
|
objs, err := ssa.ReadObjects(strings.NewReader(objects))
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
|
||||||
|
builder := fake.NewClientBuilder().WithScheme(utils.NewScheme())
|
||||||
|
for _, obj := range objs {
|
||||||
|
builder = builder.WithObjects(obj)
|
||||||
|
}
|
||||||
|
c := builder.Build()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
selector string
|
||||||
|
namespace string
|
||||||
|
want []string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Source Ref for Kustomization",
|
||||||
|
selector: "Kustomization/flux-system",
|
||||||
|
namespace: "flux-system",
|
||||||
|
want: []string{"GitRepository/flux-system.flux-system"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Crossnamespace Source Ref for Kustomization",
|
||||||
|
selector: "Kustomization/podinfo",
|
||||||
|
namespace: "default",
|
||||||
|
want: []string{"GitRepository/flux-system.flux-system"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Source Ref for HelmRelease",
|
||||||
|
selector: "HelmRelease/podinfo",
|
||||||
|
namespace: "default",
|
||||||
|
want: []string{"HelmRepository/podinfo.flux-system", "HelmChart/default-podinfo.flux-system"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Source Ref for Alert",
|
||||||
|
selector: "Alert/webapp",
|
||||||
|
namespace: "flux-system",
|
||||||
|
want: []string{"Provider/slack.flux-system"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Source Ref for ImagePolicy",
|
||||||
|
selector: "ImagePolicy/podinfo",
|
||||||
|
namespace: "default",
|
||||||
|
want: []string{"ImageRepository/acr-podinfo.flux-system"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Source Ref for ImagePolicy (lowercased)",
|
||||||
|
selector: "imagepolicy/podinfo",
|
||||||
|
namespace: "default",
|
||||||
|
want: []string{"ImageRepository/acr-podinfo.flux-system"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Empty Ref for Provider",
|
||||||
|
selector: "Provider/slack",
|
||||||
|
namespace: "flux-system",
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Non flux resource",
|
||||||
|
selector: "Namespace/flux-system",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
g := NewWithT(t)
|
||||||
|
kind, name := getKindNameFromSelector(tt.selector)
|
||||||
|
infoRef, err := fluxKindMap.getRefInfo(kind)
|
||||||
|
if tt.wantErr {
|
||||||
|
g.Expect(err).To(HaveOccurred())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
got, err := getObjectRef(context.Background(), c, infoRef, name, tt.namespace)
|
||||||
|
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
g.Expect(got).To(Equal(tt.want))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getRows(t *testing.T) {
|
||||||
|
g := NewWithT(t)
|
||||||
|
objs, err := ssa.ReadObjects(strings.NewReader(objects))
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
|
||||||
|
builder := fake.NewClientBuilder().WithScheme(utils.NewScheme())
|
||||||
|
for _, obj := range objs {
|
||||||
|
builder = builder.WithObjects(obj)
|
||||||
|
}
|
||||||
|
eventList := &corev1.EventList{}
|
||||||
|
for _, obj := range objs {
|
||||||
|
infoEvent := createEvent(obj, eventv1.EventSeverityInfo, "Info Message", "Info Reason")
|
||||||
|
warningEvent := createEvent(obj, eventv1.EventSeverityError, "Error Message", "Error Reason")
|
||||||
|
eventList.Items = append(eventList.Items, infoEvent, warningEvent)
|
||||||
|
}
|
||||||
|
builder = builder.WithLists(eventList)
|
||||||
|
builder.WithIndex(&corev1.Event{}, "involvedObject.kind/name", kindNameIndexer)
|
||||||
|
builder.WithIndex(&corev1.Event{}, "involvedObject.kind", kindIndexer)
|
||||||
|
c := builder.Build()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
selector string
|
||||||
|
refSelector string
|
||||||
|
namespace string
|
||||||
|
refNs string
|
||||||
|
expected [][]string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "events from all namespaces",
|
||||||
|
selector: "",
|
||||||
|
namespace: "",
|
||||||
|
expected: [][]string{
|
||||||
|
{"default", "<unknown>", "error", "Error Reason", "HelmRelease/podinfo", "Error Message"},
|
||||||
|
{"default", "<unknown>", "info", "Info Reason", "HelmRelease/podinfo", "Info Message"},
|
||||||
|
{"default", "<unknown>", "error", "Error Reason", "ImagePolicy/podinfo", "Error Message"},
|
||||||
|
{"default", "<unknown>", "info", "Info Reason", "ImagePolicy/podinfo", "Info Message"},
|
||||||
|
{"default", "<unknown>", "error", "Error Reason", "Kustomization/podinfo", "Error Message"},
|
||||||
|
{"default", "<unknown>", "info", "Info Reason", "Kustomization/podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "Alert/webapp", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "Alert/webapp", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "GitRepository/flux-system", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "GitRepository/flux-system", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "HelmChart/default-podinfo", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "HelmChart/default-podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "HelmRepository/podinfo", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "HelmRepository/podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "Kustomization/flux-system", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "Kustomization/flux-system", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "Provider/slack", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "Provider/slack", "Info Message"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "events from default namespaces",
|
||||||
|
selector: "",
|
||||||
|
namespace: "default",
|
||||||
|
expected: [][]string{
|
||||||
|
{"<unknown>", "error", "Error Reason", "HelmRelease/podinfo", "Error Message"},
|
||||||
|
{"<unknown>", "info", "Info Reason", "HelmRelease/podinfo", "Info Message"},
|
||||||
|
{"<unknown>", "error", "Error Reason", "ImagePolicy/podinfo", "Error Message"},
|
||||||
|
{"<unknown>", "info", "Info Reason", "ImagePolicy/podinfo", "Info Message"},
|
||||||
|
{"<unknown>", "error", "Error Reason", "Kustomization/podinfo", "Error Message"},
|
||||||
|
{"<unknown>", "info", "Info Reason", "Kustomization/podinfo", "Info Message"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Kustomization with crossnamespaced GitRepository",
|
||||||
|
selector: "Kustomization/podinfo",
|
||||||
|
namespace: "default",
|
||||||
|
expected: [][]string{
|
||||||
|
{"default", "<unknown>", "error", "Error Reason", "Kustomization/podinfo", "Error Message"},
|
||||||
|
{"default", "<unknown>", "info", "Info Reason", "Kustomization/podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "GitRepository/flux-system", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "GitRepository/flux-system", "Info Message"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "All Kustomization (lowercased selector)",
|
||||||
|
selector: "kustomization",
|
||||||
|
expected: [][]string{
|
||||||
|
{"default", "<unknown>", "error", "Error Reason", "Kustomization/podinfo", "Error Message"},
|
||||||
|
{"default", "<unknown>", "info", "Info Reason", "Kustomization/podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "Kustomization/flux-system", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "Kustomization/flux-system", "Info Message"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HelmRelease with crossnamespaced HelmRepository",
|
||||||
|
selector: "HelmRelease/podinfo",
|
||||||
|
namespace: "default",
|
||||||
|
expected: [][]string{
|
||||||
|
{"default", "<unknown>", "error", "Error Reason", "HelmRelease/podinfo", "Error Message"},
|
||||||
|
{"default", "<unknown>", "info", "Info Reason", "HelmRelease/podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "HelmRepository/podinfo", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "HelmRepository/podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "HelmChart/default-podinfo", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "HelmChart/default-podinfo", "Info Message"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HelmRelease with crossnamespaced HelmRepository (lowercased)",
|
||||||
|
selector: "helmrelease/podinfo",
|
||||||
|
namespace: "default",
|
||||||
|
expected: [][]string{
|
||||||
|
{"default", "<unknown>", "error", "Error Reason", "HelmRelease/podinfo", "Error Message"},
|
||||||
|
{"default", "<unknown>", "info", "Info Reason", "HelmRelease/podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "HelmRepository/podinfo", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "HelmRepository/podinfo", "Info Message"},
|
||||||
|
{"flux-system", "<unknown>", "error", "Error Reason", "HelmChart/default-podinfo", "Error Message"},
|
||||||
|
{"flux-system", "<unknown>", "info", "Info Reason", "HelmChart/default-podinfo", "Info Message"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
g := NewWithT(t)
|
||||||
|
|
||||||
|
var refs []string
|
||||||
|
var refNs, refKind, refName string
|
||||||
|
var clientOpts = []client.ListOption{client.InNamespace(tt.namespace)}
|
||||||
|
if tt.selector != "" {
|
||||||
|
kind, name := getKindNameFromSelector(tt.selector)
|
||||||
|
infoRef, err := fluxKindMap.getRefInfo(kind)
|
||||||
|
clientOpts = append(clientOpts, getTestListOpt(infoRef.gvk.Kind, name))
|
||||||
|
if name != "" {
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
refs, err = getObjectRef(context.Background(), c, infoRef, name, tt.namespace)
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
|
||||||
|
var refOpts [][]client.ListOption
|
||||||
|
for _, ref := range refs {
|
||||||
|
refKind, refName, refNs = utils.ParseObjectKindNameNamespace(ref)
|
||||||
|
refOpts = append(refOpts, []client.ListOption{client.InNamespace(refNs), getTestListOpt(refKind, refName)})
|
||||||
|
}
|
||||||
|
|
||||||
|
showNs := tt.namespace == "" || (refNs != "" && refNs != tt.namespace)
|
||||||
|
rows, err := getRows(context.Background(), c, clientOpts, refOpts, showNs)
|
||||||
|
g.Expect(err).To(Not(HaveOccurred()))
|
||||||
|
g.Expect(rows).To(ConsistOf(tt.expected))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTestListOpt(kind, name string) client.ListOption {
|
||||||
|
var sel fields.Selector
|
||||||
|
if name == "" {
|
||||||
|
sel = fields.OneTermEqualSelector("involvedObject.kind", kind)
|
||||||
|
} else {
|
||||||
|
sel = fields.OneTermEqualSelector("involvedObject.kind/name", fmt.Sprintf("%s/%s", kind, name))
|
||||||
|
}
|
||||||
|
return client.MatchingFieldsSelector{Selector: sel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createEvent(obj client.Object, eventType, msg, reason string) corev1.Event {
|
||||||
|
return corev1.Event{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: obj.GetNamespace(),
|
||||||
|
// name of event needs to be unique
|
||||||
|
Name: obj.GetNamespace() + obj.GetNamespace() + obj.GetObjectKind().GroupVersionKind().Kind + eventType,
|
||||||
|
},
|
||||||
|
Reason: reason,
|
||||||
|
Message: msg,
|
||||||
|
Type: eventType,
|
||||||
|
InvolvedObject: corev1.ObjectReference{
|
||||||
|
Kind: obj.GetObjectKind().GroupVersionKind().Kind,
|
||||||
|
Namespace: obj.GetNamespace(),
|
||||||
|
Name: obj.GetName(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindNameIndexer(obj client.Object) []string {
|
||||||
|
e, ok := obj.(*corev1.Event)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("Expected a Event, got %T", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{fmt.Sprintf("%s/%s", e.InvolvedObject.Kind, e.InvolvedObject.Name)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindIndexer(obj client.Object) []string {
|
||||||
|
e, ok := obj.(*corev1.Event)
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("Expected a Event, got %T", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{e.InvolvedObject.Kind}
|
||||||
|
}
|
||||||
@@ -26,13 +26,13 @@ import (
|
|||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
"sigs.k8s.io/yaml"
|
"sigs.k8s.io/yaml"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportCmd = &cobra.Command{
|
var exportCmd = &cobra.Command{
|
||||||
Use: "export",
|
Use: "export",
|
||||||
Short: "Export resources in YAML format",
|
Short: "Export resources in YAML format",
|
||||||
Long: "The export sub-commands export resources in YAML format.",
|
Long: `The export sub-commands export resources in YAML format.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
type exportFlags struct {
|
type exportFlags struct {
|
||||||
@@ -122,5 +122,6 @@ func printExport(export interface{}) error {
|
|||||||
func resourceToString(data []byte) string {
|
func resourceToString(data []byte) string {
|
||||||
data = bytes.Replace(data, []byte(" creationTimestamp: null\n"), []byte(""), 1)
|
data = bytes.Replace(data, []byte(" creationTimestamp: null\n"), []byte(""), 1)
|
||||||
data = bytes.Replace(data, []byte("status: {}\n"), []byte(""), 1)
|
data = bytes.Replace(data, []byte("status: {}\n"), []byte(""), 1)
|
||||||
|
data = bytes.TrimSpace(data)
|
||||||
return string(data)
|
return string(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var exportAlertCmd = &cobra.Command{
|
var exportAlertCmd = &cobra.Command{
|
||||||
Use: "alert [name]",
|
Use: "alert [name]",
|
||||||
Short: "Export Alert resources in YAML format",
|
Short: "Export Alert resources in YAML format",
|
||||||
Long: "The export alert command exports one or all Alert resources in YAML format.",
|
Long: withPreviewNote("The export alert command exports one or all Alert resources in YAML format."),
|
||||||
Example: ` # Export all Alert resources
|
Example: ` # Export all Alert resources
|
||||||
flux export alert --all > alerts.yaml
|
flux export alert --all > alerts.yaml
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user