Compare commits
539 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c5c15ea21 | ||
|
|
775838d3ad | ||
|
|
eaae83efa4 | ||
|
|
c1f7f20454 | ||
|
|
bba9e0952d | ||
|
|
0ea68fd43b | ||
|
|
492b50e4f0 | ||
|
|
f46f59bbba | ||
|
|
2eb52e314a | ||
|
|
06dd89e160 | ||
|
|
2ac481c889 | ||
|
|
f434fed50f | ||
|
|
01f43fc109 | ||
|
|
c369c06731 | ||
|
|
9b3958825a | ||
|
|
f581add81c | ||
|
|
107dbd09ab | ||
|
|
ec034c0c3c | ||
|
|
012782448e | ||
|
|
6be9ce2d4e | ||
|
|
0a380b1495 | ||
|
|
aae92ee097 | ||
|
|
459f6f2c24 | ||
|
|
986e405ada | ||
|
|
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 |
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}
|
||||
url = https://fluxcd.io/
|
||||
arch = x86_64
|
||||
arch = armv6h
|
||||
arch = armv7h
|
||||
arch = aarch64
|
||||
license = APACHE
|
||||
optdepends = bash-completion: auto-completion for flux in Bash
|
||||
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}
|
||||
source_armv6h = ${PKGNAME}-${PKGVER}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${PKGVER}/flux_${PKGVER}_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
|
||||
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_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}
|
||||
|
||||
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
|
||||
pkgver=${PKGVER}
|
||||
pkgrel=${PKGREL}
|
||||
_srcname=flux
|
||||
_srcver=${VERSION}
|
||||
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
||||
url="https://fluxcd.io/"
|
||||
arch=("x86_64" "armv6h" "armv7h" "aarch64")
|
||||
arch=("x86_64" "armv7h" "aarch64")
|
||||
license=("APACHE")
|
||||
optdepends=('bash-completion: auto-completion for flux in Bash'
|
||||
'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_armv6h=(
|
||||
"${pkgname}-${pkgver}.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${pkgver}/flux_${pkgver}_linux_arm.tar.gz"
|
||||
"${pkgname}-${pkgver}_linux_amd64.tar.gz::https://github.com/fluxcd/flux2/releases/download/v${_srcver}/flux_${_srcver}_linux_amd64.tar.gz"
|
||||
)
|
||||
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=(
|
||||
"${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=(
|
||||
${SHA256SUM_AMD64}
|
||||
)
|
||||
sha256sums_armv6h=(
|
||||
${SHA256SUM_ARM}
|
||||
)
|
||||
sha256sums_armv7h=(
|
||||
${SHA256SUM_ARM}
|
||||
)
|
||||
sha256sums_aarch64=(
|
||||
${SHA256SUM_ARM64}
|
||||
)
|
||||
_srcname=flux
|
||||
|
||||
package() {
|
||||
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_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
||||
|
||||
# Transform pre-release to AUR compatible version format
|
||||
export PKGVER=${VERSION/-/}
|
||||
|
||||
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
||||
@@ -36,12 +37,12 @@ else
|
||||
export PKGREL=1
|
||||
fi
|
||||
|
||||
export SHA256SUM_ARM=$(sha256sum ${ROOT}/dist/flux_${PKGVER}_linux_arm.tar.gz | awk '{ print $1 }')
|
||||
export SHA256SUM_ARM64=$(sha256sum ${ROOT}/dist/flux_${PKGVER}_linux_arm64.tar.gz | awk '{ print $1 }')
|
||||
export SHA256SUM_AMD64=$(sha256sum ${ROOT}/dist/flux_${PKGVER}_linux_amd64.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_${VERSION}_linux_arm64.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 '$PKGVER $PKGREL $SHA256SUM_AMD64 $SHA256SUM_ARM $SHA256SUM_ARM64' < PKGBUILD.template > $GITDIR/PKGBUILD
|
||||
envsubst '$VERSION $PKGVER $PKGREL $SHA256SUM_AMD64 $SHA256SUM_ARM $SHA256SUM_ARM64' < .SRCINFO.template > $GITDIR/.SRCINFO
|
||||
envsubst '$VERSION $PKGVER $PKGREL $SHA256SUM_AMD64 $SHA256SUM_ARM $SHA256SUM_ARM64' < PKGBUILD.template > $GITDIR/PKGBUILD
|
||||
|
||||
cd $GITDIR
|
||||
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}
|
||||
url = https://fluxcd.io/
|
||||
arch = x86_64
|
||||
arch = armv6h
|
||||
arch = armv7h
|
||||
arch = aarch64
|
||||
license = APACHE
|
||||
@@ -13,6 +12,6 @@ pkgbase = flux-go
|
||||
provides = flux-bin
|
||||
conflicts = flux-bin
|
||||
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
|
||||
|
||||
19
.github/aur/flux-go/PKGBUILD.template
vendored
19
.github/aur/flux-go/PKGBUILD.template
vendored
@@ -4,43 +4,44 @@
|
||||
pkgname=flux-go
|
||||
pkgver=${PKGVER}
|
||||
pkgrel=${PKGREL}
|
||||
_srcname=flux
|
||||
_srcver=${VERSION}
|
||||
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
||||
url="https://fluxcd.io/"
|
||||
arch=("x86_64" "armv6h" "armv7h" "aarch64")
|
||||
arch=("x86_64" "armv7h" "aarch64")
|
||||
license=("APACHE")
|
||||
provides=("flux-bin")
|
||||
conflicts=("flux-bin")
|
||||
replaces=("flux-cli")
|
||||
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',
|
||||
'zsh-completions: auto-completion for flux in ZSH')
|
||||
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=(
|
||||
${SHA256SUM}
|
||||
)
|
||||
_srcname=flux
|
||||
|
||||
build() {
|
||||
cd "flux2-${pkgver}"
|
||||
cd "flux2-${_srcver}"
|
||||
export CGO_LDFLAGS="$LDFLAGS"
|
||||
export CGO_CFLAGS="$CFLAGS"
|
||||
export CGO_CXXFLAGS="$CXXFLAGS"
|
||||
export CGO_CPPFLAGS="$CPPFLAGS"
|
||||
export GOFLAGS="-buildmode=pie -trimpath -mod=readonly -modcacherw"
|
||||
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() {
|
||||
cd "flux2-${pkgver}"
|
||||
cd "flux2-${_srcver}"
|
||||
case $CARCH in
|
||||
aarch64)
|
||||
export ENVTEST_ARCH=arm64
|
||||
;;
|
||||
armv6h|armv7h)
|
||||
armv7h)
|
||||
export ENVTEST_ARCH=arm
|
||||
;;
|
||||
esac
|
||||
@@ -48,7 +49,7 @@ check() {
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "flux2-${pkgver}"
|
||||
cd "flux2-${_srcver}"
|
||||
install -Dm755 ${_srcname} "${pkgdir}/usr/bin/${_srcname}"
|
||||
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_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
||||
|
||||
# Transform pre-release to AUR compatible version format
|
||||
export PKGVER=${VERSION/-/}
|
||||
|
||||
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
||||
@@ -36,10 +37,10 @@ else
|
||||
export PKGREL=1
|
||||
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 '$PKGVER $PKGREL $SHA256SUM' < PKGBUILD.template > $GITDIR/PKGBUILD
|
||||
envsubst '$VERSION $PKGVER $PKGREL $SHA256SUM' < .SRCINFO.template > $GITDIR/.SRCINFO
|
||||
envsubst '$VERSION $PKGVER $PKGREL $SHA256SUM' < PKGBUILD.template > $GITDIR/PKGBUILD
|
||||
|
||||
cd $GITDIR
|
||||
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}
|
||||
url = https://fluxcd.io/
|
||||
arch = x86_64
|
||||
arch = armv6h
|
||||
arch = armv7h
|
||||
arch = aarch64
|
||||
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
|
||||
pkgver=${PKGVER}
|
||||
pkgrel=${PKGREL}
|
||||
_srcname=flux
|
||||
pkgdesc="Open and extensible continuous delivery solution for Kubernetes"
|
||||
url="https://fluxcd.io/"
|
||||
arch=("x86_64" "armv6h" "armv7h" "aarch64")
|
||||
arch=("x86_64" "armv7h" "aarch64")
|
||||
license=("APACHE")
|
||||
provides=("flux-bin")
|
||||
conflicts=("flux-bin")
|
||||
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',
|
||||
'zsh-completions: auto-completion for flux in ZSH')
|
||||
source=(
|
||||
"git+https://github.com/fluxcd/flux2.git"
|
||||
)
|
||||
md5sums=('SKIP')
|
||||
_srcname=flux
|
||||
|
||||
pkgver() {
|
||||
cd "flux2"
|
||||
@@ -42,7 +42,7 @@ check() {
|
||||
aarch64)
|
||||
export ENVTEST_ARCH=arm64
|
||||
;;
|
||||
armv6h|armv7h)
|
||||
armv7h)
|
||||
export ENVTEST_ARCH=arm
|
||||
;;
|
||||
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_PKGREL=$(cat $GITDIR/.SRCINFO | grep pkgrel | awk '{ print $3 }')
|
||||
|
||||
# Transform pre-release to AUR compatible version format
|
||||
export PKGVER=${VERSION/-/}
|
||||
|
||||
if [[ "${CURRENT_PKGVER}" == "${PKGVER}" ]]; then
|
||||
|
||||
11
.github/dependabot.yml
vendored
11
.github/dependabot.yml
vendored
@@ -3,7 +3,14 @@ version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
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:
|
||||
# by default this will be on a monday.
|
||||
# By default, this will be on a monday.
|
||||
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@addffea45a2f0b5682f1d5ba0506f45bc18bf174 # v2.3.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:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ main, update-components, e2e-arm64* ]
|
||||
branches: [ 'main', 'update-components', 'e2e-*', 'release/**' ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -16,14 +16,19 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
# 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, 1.29.0 ]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
|
||||
102
.github/workflows/e2e-azure.yaml
vendored
102
.github/workflows/e2e-azure.yaml
vendored
@@ -3,9 +3,19 @@ name: e2e-azure
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 6 * * *'
|
||||
- cron: '0 6 * * *'
|
||||
push:
|
||||
branches: [ azure* ]
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'tests/**'
|
||||
- '.github/workflows/e2e-azure.yaml'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'tests/**'
|
||||
- '.github/workflows/e2e-azure.yaml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -13,33 +23,32 @@ permissions:
|
||||
jobs:
|
||||
e2e-amd64-aks:
|
||||
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:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
- name: Restore Go cache
|
||||
uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go1.18-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go1.18-
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
cache-dependency-path: tests/azure/go.sum
|
||||
- name: Setup Flux CLI
|
||||
run: |
|
||||
make build
|
||||
mkdir -p $HOME/.local/bin
|
||||
mv ./bin/flux $HOME/.local/bin
|
||||
working-directory: ./
|
||||
- name: Setup SOPS
|
||||
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
|
||||
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
|
||||
uses: hashicorp/setup-terraform@633666f66e0061ca3b725c73b2ec20cd13a8fdd1 # v2
|
||||
uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v2
|
||||
with:
|
||||
terraform_version: 1.2.8
|
||||
terraform_wrapper: false
|
||||
@@ -53,9 +62,64 @@ jobs:
|
||||
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
|
||||
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
|
||||
run: |
|
||||
echo $HOME
|
||||
echo $PATH
|
||||
ls $HOME/.local/bin
|
||||
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 .
|
||||
|
||||
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
|
||||
|
||||
33
.github/workflows/e2e-bootstrap.yaml
vendored
33
.github/workflows/e2e-bootstrap.yaml
vendored
@@ -3,9 +3,10 @@ name: e2e-bootstrap
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches: [ 'main', 'release/**' ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
branches: [ 'main', 'release/**' ]
|
||||
paths-ignore: [ 'docs/**', 'rfcs/**' ]
|
||||
|
||||
permissions:
|
||||
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]'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
- name: Restore Go cache
|
||||
uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go1.18-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go1.18-
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Setup Kubernetes
|
||||
uses: helm/kind-action@d8ccf8fb623ce1bb360ae2f45f323d9d5c5e9f00 # v1.5.0
|
||||
uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0
|
||||
with:
|
||||
version: v0.17.0
|
||||
version: v0.20.0
|
||||
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
|
||||
uses: fluxcd/pkg//actions/kustomize@main
|
||||
uses: fluxcd/pkg/actions/kustomize@main
|
||||
- name: Build
|
||||
run: |
|
||||
make cmd/flux/.manifests.done
|
||||
@@ -99,7 +99,6 @@ jobs:
|
||||
--path=test-cluster \
|
||||
--read-write-key
|
||||
/tmp/flux reconcile image repository podinfo
|
||||
/tmp/flux reconcile image update flux-system
|
||||
/tmp/flux get images all
|
||||
|
||||
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
|
||||
46
.github/workflows/e2e.yaml
vendored
46
.github/workflows/e2e.yaml
vendored
@@ -3,9 +3,10 @@ name: e2e
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches: [ 'main', 'release/**' ]
|
||||
pull_request:
|
||||
branches: [ main, oci ]
|
||||
branches: [ 'main', 'release/**' ]
|
||||
paths-ignore: [ 'docs/**', 'rfcs/**' ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -20,31 +21,30 @@ jobs:
|
||||
- 5000:5000
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
- name: Restore Go cache
|
||||
uses: actions/cache@6998d139ddd3e68c71e9e398d8e40b71a2f39812
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go1.18-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go1.18-
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Setup Kubernetes
|
||||
uses: helm/kind-action@d8ccf8fb623ce1bb360ae2f45f323d9d5c5e9f00 # v1.5.0
|
||||
uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0
|
||||
with:
|
||||
version: v0.17.0
|
||||
version: v0.20.0
|
||||
cluster_name: kind
|
||||
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
|
||||
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
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg//actions/kustomize@main
|
||||
uses: fluxcd/pkg/actions/kustomize@main
|
||||
- name: Run tests
|
||||
run: make test
|
||||
- name: Run e2e tests
|
||||
@@ -78,12 +78,12 @@ jobs:
|
||||
run: |
|
||||
/tmp/flux create source git podinfo \
|
||||
--url https://github.com/stefanprodan/podinfo \
|
||||
--tag-semver=">=3.2.3"
|
||||
--tag-semver=">=6.3.5"
|
||||
- name: flux create source git export apply
|
||||
run: |
|
||||
/tmp/flux create source git podinfo-export \
|
||||
--url https://github.com/stefanprodan/podinfo \
|
||||
--tag-semver=">=3.2.3" \
|
||||
--tag-semver=">=6.3.5" \
|
||||
--export | kubectl apply -f -
|
||||
/tmp/flux delete source git podinfo-export --silent
|
||||
- name: flux get sources git
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
--target-namespace=default \
|
||||
--source=HelmRepository/podinfo.flux-system \
|
||||
--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
|
||||
run: |
|
||||
/tmp/flux create hr podinfo-git \
|
||||
@@ -184,11 +184,11 @@ jobs:
|
||||
run: |
|
||||
/tmp/flux create source oci podinfo-oci \
|
||||
--url oci://ghcr.io/stefanprodan/manifests/podinfo \
|
||||
--tag-semver 6.1.x \
|
||||
--tag-semver 6.3.x \
|
||||
--interval 10m
|
||||
/tmp/flux create kustomization podinfo-oci \
|
||||
--source=OCIRepository/podinfo-oci \
|
||||
--path="./kustomize" \
|
||||
--path="./" \
|
||||
--prune=true \
|
||||
--interval=5m \
|
||||
--target-namespace=default \
|
||||
@@ -209,7 +209,7 @@ jobs:
|
||||
/tmp/flux -n apps create hr podinfo-helm \
|
||||
--source=HelmRepository/podinfo \
|
||||
--chart=podinfo \
|
||||
--chart-version="5.0.x" \
|
||||
--chart-version="6.3.x" \
|
||||
--service-account=dev-team
|
||||
- name: flux2-kustomize-helm-example
|
||||
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@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
|
||||
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
|
||||
98
.github/workflows/release.yaml
vendored
98
.github/workflows/release.yaml
vendored
@@ -9,6 +9,10 @@ permissions:
|
||||
|
||||
jobs:
|
||||
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
|
||||
permissions:
|
||||
contents: write # needed to write releases
|
||||
@@ -16,32 +20,33 @@ jobs:
|
||||
packages: write # needed for ghcr access
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
cache: false
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@e81a89b1732b9c48d79cd809d8d81d79c4647a18 # v2
|
||||
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
|
||||
- name: Setup Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@f03ac48505955848960e80bbb68046aa35c7b9e7 # v2
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
- 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
|
||||
uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
|
||||
uses: sigstore/cosign-installer@9614fae9e5c5eddabb09f90a270fcb487c9f7149 # v3.3.0
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg//actions/kustomize@main
|
||||
uses: fluxcd/pkg/actions/kustomize@main
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: fluxcdbot
|
||||
password: ${{ secrets.GHCR_TOKEN }}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||
with:
|
||||
username: fluxcdbot
|
||||
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||
@@ -54,7 +59,7 @@ jobs:
|
||||
run: |
|
||||
kustomize build manifests/crds > all-crds.yaml
|
||||
- name: Generate OpenAPI JSON schemas from CRDs
|
||||
uses: fluxcd/pkg//actions/crdjsonschema@main
|
||||
uses: fluxcd/pkg/actions/crdjsonschema@main
|
||||
with:
|
||||
crd: all-crds.yaml
|
||||
output: schemas
|
||||
@@ -73,7 +78,8 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v3
|
||||
id: run-goreleaser
|
||||
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
|
||||
with:
|
||||
version: latest
|
||||
args: release --release-notes=output/notes.md --skip-validate
|
||||
@@ -81,6 +87,22 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
needs: release-flux-cli
|
||||
@@ -88,7 +110,7 @@ jobs:
|
||||
id-token: write
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg/actions/kustomize@main
|
||||
- name: Setup Flux CLI
|
||||
@@ -99,13 +121,13 @@ jobs:
|
||||
VERSION=$(flux version --client | awk '{ print $NF }')
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: fluxcdbot
|
||||
password: ${{ secrets.GHCR_TOKEN }}
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||
with:
|
||||
username: fluxcdbot
|
||||
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||
@@ -133,13 +155,13 @@ jobs:
|
||||
--path="./flux-system" \
|
||||
--source=${{ github.repositoryUrl }} \
|
||||
--revision="${{ github.ref_name }}@sha1:${{ github.sha }}"
|
||||
- uses: sigstore/cosign-installer@9becc617647dfa20ae7b1151972e9b3a2c338a2b # v2.8.1
|
||||
- uses: sigstore/cosign-installer@9614fae9e5c5eddabb09f90a270fcb487c9f7149 # v3.3.0
|
||||
- name: Sign manifests
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: 1
|
||||
run: |
|
||||
cosign sign ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }}
|
||||
cosign sign docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }}
|
||||
cosign sign --yes ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }}
|
||||
cosign sign --yes docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }}
|
||||
- name: Tag manifests
|
||||
run: |
|
||||
flux tag artifact oci://ghcr.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }} \
|
||||
@@ -147,3 +169,43 @@ jobs:
|
||||
|
||||
flux tag artifact oci://docker.io/fluxcd/flux-manifests:${{ steps.prep.outputs.version }} \
|
||||
--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 }}
|
||||
|
||||
43
.github/workflows/scan.yaml
vendored
43
.github/workflows/scan.yaml
vendored
@@ -3,9 +3,9 @@ name: scan
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches: [ 'main', 'release/**' ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
branches: [ 'main', 'release/**' ]
|
||||
schedule:
|
||||
- cron: '18 10 * * 3'
|
||||
|
||||
@@ -17,9 +17,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor != 'dependabot[bot]'
|
||||
steps:
|
||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Run FOSSA scan and upload build data
|
||||
uses: fossa-contrib/fossa-action@6728dc6fe9a068c648d080c33829ffbe56565023 # v1
|
||||
uses: fossa-contrib/fossa-action@cdc5065bcdee31a32e47d4585df72d66e8e941c2 # v3.0.0
|
||||
with:
|
||||
# FOSSA Push-Only API Token
|
||||
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
|
||||
@@ -31,18 +31,21 @@ jobs:
|
||||
security-events: write
|
||||
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]'
|
||||
steps:
|
||||
- uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup Kustomize
|
||||
uses: fluxcd/pkg//actions/kustomize@main
|
||||
uses: fluxcd/pkg/actions/kustomize@main
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Download modules and build manifests
|
||||
run: |
|
||||
make tidy
|
||||
make cmd/flux/.manifests.done
|
||||
- uses: snyk/actions/setup@806182742461562b67788a64410098c9d9b96adb
|
||||
- uses: snyk/actions/setup@b98d498629f1c368650224d6d212bf7dfa89e4bf
|
||||
- name: Run Snyk to check for vulnerabilities
|
||||
continue-on-error: true
|
||||
run: |
|
||||
@@ -50,7 +53,7 @@ jobs:
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
- name: Upload result to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2
|
||||
uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||
with:
|
||||
sarif_file: snyk.sarif
|
||||
|
||||
@@ -61,16 +64,22 @@ jobs:
|
||||
if: github.actor != 'dependabot[bot]'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2
|
||||
uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||
with:
|
||||
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
|
||||
uses: github/codeql-action/autobuild@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2
|
||||
uses: github/codeql-action/autobuild@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # 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
|
||||
13
.github/workflows/update.yaml
vendored
13
.github/workflows/update.yaml
vendored
@@ -18,11 +18,14 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
|
||||
with:
|
||||
go-version: 1.19.x
|
||||
go-version: 1.20.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Update component versions
|
||||
id: update
|
||||
run: |
|
||||
@@ -81,7 +84,7 @@ jobs:
|
||||
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@2b011faafdcbc9ceb11414d64d0573f37c774b04 # v4
|
||||
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
|
||||
with:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
commit-message: |
|
||||
@@ -96,7 +99,7 @@ jobs:
|
||||
body: |
|
||||
${{ steps.update.outputs.pr_body }}
|
||||
labels: |
|
||||
area/build
|
||||
dependencies
|
||||
reviewers: ${{ secrets.ASSIGNEES }}
|
||||
|
||||
- name: Check output
|
||||
|
||||
@@ -65,6 +65,7 @@ signs:
|
||||
certificate: '${artifact}.pem'
|
||||
args:
|
||||
- sign-blob
|
||||
- "--yes"
|
||||
- '--output-certificate=${certificate}'
|
||||
- '--output-signature=${signature}'
|
||||
- '${artifact}'
|
||||
@@ -82,14 +83,7 @@ brews:
|
||||
install: |
|
||||
bin.install "flux"
|
||||
|
||||
bash_output = Utils.safe_popen_read(bin/"flux", "completion", "bash")
|
||||
(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
|
||||
generate_completions_from_executable(bin/"flux", "completion")
|
||||
test: |
|
||||
system "#{bin}/flux --version"
|
||||
publishers:
|
||||
@@ -175,6 +169,7 @@ docker_signs:
|
||||
- COSIGN_EXPERIMENTAL=1
|
||||
args:
|
||||
- sign
|
||||
- "--yes"
|
||||
- '${artifact}'
|
||||
artifacts: all
|
||||
output: true
|
||||
|
||||
@@ -67,9 +67,9 @@ for source changes.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
* go >= 1.19
|
||||
* kubectl >= 1.20
|
||||
* kustomize >= 4.4
|
||||
* go >= 1.20
|
||||
* kubectl >= 1.24
|
||||
* kustomize >= 5.0
|
||||
* coreutils (on Mac OS)
|
||||
|
||||
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
|
||||
|
||||
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 \
|
||||
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl && \
|
||||
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
|
||||
|
||||
|
||||
5
Makefile
5
Makefile
@@ -17,8 +17,9 @@ rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2
|
||||
all: test build
|
||||
|
||||
tidy:
|
||||
go mod tidy -compat=1.19
|
||||
cd tests/azure && go mod tidy -compat=1.19
|
||||
go mod tidy -compat=1.20
|
||||
cd tests/azure && go mod tidy -compat=1.20
|
||||
cd tests/integration && go mod tidy -compat=1.20
|
||||
|
||||
fmt:
|
||||
go fmt ./...
|
||||
|
||||
16
README.md
16
README.md
@@ -2,8 +2,10 @@
|
||||
|
||||
[](https://github.com/fluxcd/flux2/releases)
|
||||
[](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://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
|
||||
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/)
|
||||
- [Manage Helm Releases](https://fluxcd.io/flux/guides/helmreleases/)
|
||||
- [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/)**.
|
||||
|
||||
@@ -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
|
||||
automation tooling.
|
||||
|
||||

|
||||

|
||||
|
||||
You can use the toolkit to extend Flux, or to build your own systems
|
||||
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/)
|
||||
- [Bucket CRD](https://fluxcd.io/flux/components/source/buckets/)
|
||||
- [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/)
|
||||
- [HelmRelease CRD](https://fluxcd.io/flux/components/helm/helmreleases/)
|
||||
- [Notification Controller](https://fluxcd.io/flux/components/notification/)
|
||||
- [Provider CRD](https://fluxcd.io/flux/components/notification/provider/)
|
||||
- [Alert CRD](https://fluxcd.io/flux/components/notification/alert/)
|
||||
- [Receiver CRD](https://fluxcd.io/flux/components/notification/receiver/)
|
||||
- [Provider CRD](https://fluxcd.io/flux/components/notification/providers/)
|
||||
- [Alert CRD](https://fluxcd.io/flux/components/notification/alerts/)
|
||||
- [Receiver CRD](https://fluxcd.io/flux/components/notification/receivers/)
|
||||
- [Image Automation Controllers](https://fluxcd.io/flux/components/image/)
|
||||
- [ImageRepository CRD](https://fluxcd.io/flux/components/image/imagerepositories/)
|
||||
- [ImagePolicy CRD](https://fluxcd.io/flux/components/image/imagepolicies/)
|
||||
- [ImageUpdateAutomation CRD](https://fluxcd.io/flux/components/image/imageupdateautomations/)
|
||||
|
||||
|
||||
## Community
|
||||
|
||||
Need help or want to contribute? Please see the links below. The Flux project is always looking for
|
||||
|
||||
220
action/README.md
220
action/README.md
@@ -1,216 +1,22 @@
|
||||
# Flux GitHub Action
|
||||
|
||||
Usage:
|
||||
To install the latest Flux CLI on Linux, macOS or Windows GitHub runners:
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: Setup Flux CLI
|
||||
uses: fluxcd/flux2/action@main
|
||||
- name: Run Flux commands
|
||||
run: flux -v
|
||||
steps:
|
||||
- name: Setup Flux CLI
|
||||
uses: fluxcd/flux2/action@main
|
||||
with:
|
||||
version: 'latest'
|
||||
- name: Run Flux CLI
|
||||
run: flux version --client
|
||||
```
|
||||
|
||||
The latest stable version of the `flux` binary is downloaded from
|
||||
GitHub [releases](https://github.com/fluxcd/flux2/releases)
|
||||
and placed at `/usr/local/bin/flux`.
|
||||
The Flux GitHub Action can be used to automate various tasks in CI, such as:
|
||||
|
||||
Note that this action can only be used on GitHub **Linux** runners.
|
||||
You can change the arch (defaults to `amd64`) with:
|
||||
- [Automate Flux upgrades on clusters via Pull Requests](https://fluxcd.io/flux/flux-gh-action/#automate-flux-updates)
|
||||
- [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
|
||||
steps:
|
||||
- name: Setup Flux CLI
|
||||
uses: fluxcd/flux2/action@main
|
||||
with:
|
||||
arch: arm64 # can be amd64, arm64 or arm
|
||||
```
|
||||
For more information, please see the [Flux GitHub Action documentation](https://fluxcd.io/flux/flux-gh-action/).
|
||||
|
||||
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 * * * *"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
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)@sha1:$(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)@sha1:$(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
|
||||
description: A GitHub Action for running Flux commands
|
||||
author: Stefan Prodan
|
||||
description: A GitHub Action for installing the Flux CLI
|
||||
author: Flux project
|
||||
branding:
|
||||
color: blue
|
||||
icon: command
|
||||
inputs:
|
||||
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
|
||||
arch:
|
||||
description: "arch can be amd64, arm64 or arm"
|
||||
required: true
|
||||
default: "amd64"
|
||||
required: false
|
||||
deprecationMessage: "No longer required, action will now detect runner arch."
|
||||
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
|
||||
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
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: "Download flux binary to tmp"
|
||||
- name: "Download the binary to the runner's cache dir"
|
||||
shell: bash
|
||||
run: |
|
||||
ARCH=${{ inputs.arch }}
|
||||
VERSION=${{ inputs.version }}
|
||||
TOKEN=${{ inputs.token }}
|
||||
|
||||
if [ -z "${VERSION}" ]; then
|
||||
if [ -n "${TOKEN}" ]; then
|
||||
VERSION_SLUG=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest --silent --location --header "Authorization: token ${TOKEN}" | grep tag_name)
|
||||
else
|
||||
# With no GITHUB_TOKEN you will experience occasional failures due to rate limiting
|
||||
# Ref: https://github.com/fluxcd/flux2/issues/3509#issuecomment-1400820992
|
||||
VERSION_SLUG=$(curl https://api.github.com/repos/fluxcd/flux2/releases/latest --silent --location | grep tag_name)
|
||||
TOKEN=${{ inputs.token }}
|
||||
if [[ -z "$TOKEN" ]]; then
|
||||
TOKEN=${{ github.token }}
|
||||
fi
|
||||
|
||||
if [[ -z "$VERSION" ]] || [[ "$VERSION" = "latest" ]]; then
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
BIN_URL="https://github.com/fluxcd/flux2/releases/download/v${VERSION}/flux_${VERSION}_linux_${ARCH}.tar.gz"
|
||||
curl --silent --fail --location "${BIN_URL}" --output /tmp/flux.tar.gz
|
||||
mkdir -p /tmp/flux
|
||||
tar -C /tmp/flux/ -zxvf /tmp/flux.tar.gz
|
||||
- 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"
|
||||
echo "Adding flux to path"
|
||||
echo "$FLUX_TOOL_DIR" >> "$GITHUB_PATH"
|
||||
|
||||
- name: "Print installed flux version"
|
||||
shell: bash
|
||||
run: |
|
||||
flux -v
|
||||
|
||||
@@ -19,7 +19,7 @@ package main
|
||||
import (
|
||||
"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
|
||||
|
||||
@@ -19,7 +19,7 @@ package main
|
||||
import (
|
||||
"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
|
||||
|
||||
@@ -17,16 +17,20 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/elliptic"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
"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/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
)
|
||||
|
||||
var bootstrapCmd = &cobra.Command{
|
||||
@@ -72,6 +76,8 @@ type bootstrapFlags struct {
|
||||
gpgPassphrase string
|
||||
gpgKeyID string
|
||||
|
||||
force bool
|
||||
|
||||
commitMessageAppendix string
|
||||
}
|
||||
|
||||
@@ -129,6 +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().BoolVar(&bootstrapArgs.force, "force", false, "override existing Flux installation if it's managed by a diffrent tool such as Helm")
|
||||
bootstrapCmd.PersistentFlags().MarkHidden("manifests")
|
||||
|
||||
rootCmd.AddCommand(bootstrapCmd)
|
||||
@@ -188,3 +195,27 @@ func mapTeamSlice(s []string, defaultPermission string) map[string]string {
|
||||
|
||||
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,14 +26,14 @@ import (
|
||||
"github.com/fluxcd/pkg/git/gogit"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/bootstrap"
|
||||
"github.com/fluxcd/flux2/pkg/bootstrap/provider"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
||||
"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 bootstrapBServerCmd = &cobra.Command{
|
||||
@@ -56,7 +56,7 @@ the bootstrap command will perform an upgrade if needed.`,
|
||||
# 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 --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 --path=clusters/my-cluster`,
|
||||
RunE: bootstrapBServerCmdRun,
|
||||
}
|
||||
@@ -124,6 +124,13 @@ func bootstrapBServerCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bootstrapArgs.force {
|
||||
err = confirmBootstrap(ctx, kubeClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Manifest base
|
||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||
return err
|
||||
|
||||
@@ -28,13 +28,13 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/bootstrap"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
||||
"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/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"
|
||||
"github.com/fluxcd/pkg/git"
|
||||
"github.com/fluxcd/pkg/git/gogit"
|
||||
)
|
||||
@@ -134,7 +134,7 @@ func bootstrapGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
if !bootstrapArgs.force {
|
||||
err = confirmBootstrap(ctx, kubeClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Manifest base
|
||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||
return err
|
||||
|
||||
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,14 +26,14 @@ import (
|
||||
"github.com/fluxcd/pkg/git/gogit"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/bootstrap"
|
||||
"github.com/fluxcd/flux2/pkg/bootstrap/provider"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
||||
"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 bootstrapGitHubCmd = &cobra.Command{
|
||||
@@ -128,6 +128,13 @@ func bootstrapGitHubCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bootstrapArgs.force {
|
||||
err = confirmBootstrap(ctx, kubeClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Manifest base
|
||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||
return err
|
||||
|
||||
@@ -28,14 +28,14 @@ import (
|
||||
"github.com/fluxcd/pkg/git/gogit"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/bootstrap"
|
||||
"github.com/fluxcd/flux2/pkg/bootstrap/provider"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sync"
|
||||
"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 bootstrapGitLabCmd = &cobra.Command{
|
||||
@@ -64,8 +64,12 @@ the bootstrap command will perform an upgrade if needed.`,
|
||||
# Run bootstrap for a private repository hosted on a GitLab server
|
||||
flux bootstrap gitlab --owner=<group> --repository=<repository name> --hostname=<domain> --token-auth
|
||||
|
||||
# Run bootstrap for a an existing repository with a branch named main
|
||||
flux bootstrap gitlab --owner=<organization> --repository=<repository name> --branch=main --token-auth`,
|
||||
# Run bootstrap for an existing repository with a branch named main
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -77,16 +81,17 @@ const (
|
||||
)
|
||||
|
||||
type gitlabFlags struct {
|
||||
owner string
|
||||
repository string
|
||||
interval time.Duration
|
||||
personal bool
|
||||
private bool
|
||||
hostname string
|
||||
path flags.SafeRelativePath
|
||||
teams []string
|
||||
readWriteKey bool
|
||||
reconcile bool
|
||||
owner string
|
||||
repository string
|
||||
interval time.Duration
|
||||
personal bool
|
||||
private bool
|
||||
hostname string
|
||||
path flags.SafeRelativePath
|
||||
teams []string
|
||||
readWriteKey bool
|
||||
reconcile bool
|
||||
deployTokenAuth bool
|
||||
}
|
||||
|
||||
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().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.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)
|
||||
}
|
||||
@@ -123,6 +129,10 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
@@ -135,6 +145,13 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bootstrapArgs.force {
|
||||
err = confirmBootstrap(ctx, kubeClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Manifest base
|
||||
if ver, err := getVersion(bootstrapArgs.version); err != nil {
|
||||
return err
|
||||
@@ -225,6 +242,9 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
secretOpts.Username = "git"
|
||||
secretOpts.Password = glToken
|
||||
secretOpts.CAFile = caBundle
|
||||
} else if gitlabArgs.deployTokenAuth {
|
||||
// the actual deploy token will be reconciled later
|
||||
secretOpts.CAFile = caBundle
|
||||
} else {
|
||||
keypair, err := sourcesecret.LoadKeyPairFromPath(bootstrapArgs.privateKeyFile, gitArgs.password)
|
||||
if err != nil {
|
||||
@@ -274,9 +294,12 @@ func bootstrapGitLabCmdRun(cmd *cobra.Command, args []string) error {
|
||||
if bootstrapArgs.sshHostname != "" {
|
||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSSHHostname(bootstrapArgs.sshHostname))
|
||||
}
|
||||
if bootstrapArgs.tokenAuth {
|
||||
if bootstrapArgs.tokenAuth || gitlabArgs.deployTokenAuth {
|
||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithSyncTransportType("https"))
|
||||
}
|
||||
if gitlabArgs.deployTokenAuth {
|
||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithDeployTokenAuth())
|
||||
}
|
||||
if !gitlabArgs.private {
|
||||
bootstrapOpts = append(bootstrapOpts, bootstrap.WithProviderRepositoryConfig("", "", "public"))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
var buildCmd = &cobra.Command{
|
||||
Use: "build",
|
||||
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() {
|
||||
|
||||
@@ -33,7 +33,8 @@ import (
|
||||
var buildArtifactCmd = &cobra.Command{
|
||||
Use: "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
|
||||
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)
|
||||
|
||||
ociClient := oci.NewLocalClient()
|
||||
ociClient := oci.NewClient(oci.DefaultOptions())
|
||||
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)
|
||||
|
||||
@@ -21,10 +21,12 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"github.com/fluxcd/pkg/ssa"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/build"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||
|
||||
"github.com/fluxcd/flux2/v2/internal/build"
|
||||
)
|
||||
|
||||
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.
|
||||
# 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)),
|
||||
RunE: buildKsCmdRun,
|
||||
}
|
||||
@@ -52,6 +61,7 @@ flux build kustomization my-app --path ./path/to/local/manifests --kustomization
|
||||
type buildKsFlags struct {
|
||||
kustomizationFile string
|
||||
path string
|
||||
ignorePaths []string
|
||||
dryRun bool
|
||||
}
|
||||
|
||||
@@ -60,6 +70,7 @@ var buildKsArgs buildKsFlags
|
||||
func init() {
|
||||
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().StringSliceVar(&buildKsArgs.ignorePaths, "ignore-paths", nil, "set paths to ignore in .gitignore format")
|
||||
buildKsCmd.Flags().BoolVar(&buildKsArgs.dryRun, "dry-run", false, "Dry run mode.")
|
||||
buildCmd.AddCommand(buildKsCmd)
|
||||
}
|
||||
@@ -95,12 +106,14 @@ func buildKsCmdRun(cmd *cobra.Command, args []string) (err error) {
|
||||
build.WithKustomizationFile(buildKsArgs.kustomizationFile),
|
||||
build.WithDryRun(buildKsArgs.dryRun),
|
||||
build.WithNamespace(*kubeconfigArgs.Namespace),
|
||||
build.WithIgnore(buildKsArgs.ignorePaths),
|
||||
)
|
||||
} else {
|
||||
builder, err = build.NewBuilder(name, buildKsArgs.path,
|
||||
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
||||
build.WithTimeout(rootArgs.timeout),
|
||||
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)
|
||||
go func() {
|
||||
manifests, err := builder.Build()
|
||||
objects, err := builder.Build()
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
cmd.Print(string(manifests))
|
||||
manifests, err := ssa.ObjectsToYAML(objects)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
cmd.Print(manifests)
|
||||
errChan <- nil
|
||||
}()
|
||||
|
||||
|
||||
@@ -63,6 +63,12 @@ func TestBuildKustomization(t *testing.T) {
|
||||
resultFile: "./testdata/build-kustomization/podinfo-with-var-substitution-result.yaml",
|
||||
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{
|
||||
@@ -92,7 +98,7 @@ func TestBuildKustomization(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBuildLocalKustomization(t *testing.T) {
|
||||
podinfo := `apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
|
||||
podinfo := `apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -153,6 +159,7 @@ spec:
|
||||
tmpl := map[string]string{
|
||||
"fluxns": allocateNamespace("flux-system"),
|
||||
}
|
||||
setup(t, tmpl)
|
||||
|
||||
testEnv.CreateObjectFile("./testdata/build-kustomization/podinfo-source.yaml", tmpl, t)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -26,21 +27,22 @@ import (
|
||||
v1 "k8s.io/api/apps/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/version"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
||||
"github.com/fluxcd/flux2/pkg/status"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/install"
|
||||
"github.com/fluxcd/flux2/v2/pkg/status"
|
||||
)
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Check requirements and installation",
|
||||
Long: `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.`,
|
||||
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.`),
|
||||
Example: ` # Run pre-installation checks
|
||||
flux check --pre
|
||||
|
||||
@@ -57,7 +59,7 @@ type checkFlags struct {
|
||||
}
|
||||
|
||||
var kubernetesConstraints = []string{
|
||||
">=1.20.6-0",
|
||||
">=1.26.0-0",
|
||||
}
|
||||
|
||||
var checkArgs checkFlags
|
||||
@@ -80,7 +82,20 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -92,13 +107,18 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Actionf("checking version in cluster")
|
||||
if !fluxClusterVersionCheck(ctx, kubeClient) {
|
||||
checkFailed = true
|
||||
}
|
||||
|
||||
logger.Actionf("checking controllers")
|
||||
if !componentsCheck() {
|
||||
if !componentsCheck(ctx, kubeClient) {
|
||||
checkFailed = true
|
||||
}
|
||||
|
||||
logger.Actionf("checking crds")
|
||||
if !crdsCheck() {
|
||||
if !crdsCheck(ctx, kubeClient) {
|
||||
checkFailed = true
|
||||
}
|
||||
|
||||
@@ -129,17 +149,11 @@ func fluxCheck() {
|
||||
return
|
||||
}
|
||||
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 {
|
||||
cfg, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
|
||||
if err != nil {
|
||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
func kubernetesCheck(cfg *rest.Config, constraints []string) bool {
|
||||
clientSet, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
||||
@@ -178,21 +192,8 @@ func kubernetesCheck(constraints []string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func componentsCheck() bool {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
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)
|
||||
func componentsCheck(ctx context.Context, kubeClient client.Client) bool {
|
||||
statusChecker, err := status.NewStatusCheckerWithClient(kubeClient, checkArgs.pollInterval, rootArgs.timeout, logger)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -222,15 +223,7 @@ func componentsCheck() bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func crdsCheck() bool {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
func crdsCheck(ctx context.Context, kubeClient client.Client) bool {
|
||||
ok := true
|
||||
selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
|
||||
var list apiextensionsv1.CustomResourceDefinitionList
|
||||
@@ -253,3 +246,17 @@ func crdsCheck() bool {
|
||||
}
|
||||
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"
|
||||
"testing"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
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"
|
||||
"strings"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
var completionCmd = &cobra.Command{
|
||||
Use: "completion",
|
||||
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() {
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
var completionBashCmd = &cobra.Command{
|
||||
Use: "bash",
|
||||
Short: "Generates bash completion scripts",
|
||||
Long: `The completion sub-command generates completion scripts for bash.`,
|
||||
Example: `To load completion run
|
||||
|
||||
. <(flux completion bash)
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
var completionFishCmd = &cobra.Command{
|
||||
Use: "fish",
|
||||
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:
|
||||
|
||||
flux completion fish > ~/.config/fish/completions/flux.fish
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
var completionPowerShellCmd = &cobra.Command{
|
||||
Use: "powershell",
|
||||
Short: "Generates powershell completion scripts",
|
||||
Long: `The completion sub-command generates completion scripts for powershell.`,
|
||||
Example: `To load completion run
|
||||
|
||||
. <(flux completion powershell)
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
var completionZshCmd = &cobra.Command{
|
||||
Use: "zsh",
|
||||
Short: "Generates zsh completion scripts",
|
||||
Long: `The completion sub-command generates completion scripts for zsh.`,
|
||||
Example: `To load completion run
|
||||
|
||||
. <(flux completion zsh)
|
||||
|
||||
@@ -30,13 +30,13 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"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{
|
||||
Use: "create",
|
||||
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 {
|
||||
@@ -131,8 +131,8 @@ func (names apiType) upsertAndWait(object upsertWaitable, mutate func() error) e
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for %s reconciliation", names.kind)
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isReady(ctx, kubeClient, namespacedName, object)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isObjectReadyConditionFunc(kubeClient, namespacedName, object.asClientObject())); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("%s reconciliation completed", names.kind)
|
||||
@@ -165,6 +165,6 @@ func parseLabels() (map[string]string, error) {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -22,22 +22,22 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"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/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
var createAlertCmd = &cobra.Command{
|
||||
Use: "alert [name]",
|
||||
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
|
||||
flux create alert \
|
||||
--event-severity info \
|
||||
@@ -96,13 +96,13 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
||||
logger.Generatef("generating Alert")
|
||||
}
|
||||
|
||||
alert := notificationv1.Alert{
|
||||
alert := notificationv1b3.Alert{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: *kubeconfigArgs.Namespace,
|
||||
Labels: sourceLabels,
|
||||
},
|
||||
Spec: notificationv1.AlertSpec{
|
||||
Spec: notificationv1b3.AlertSpec{
|
||||
ProviderRef: meta.LocalObjectReference{
|
||||
Name: alertArgs.providerRef,
|
||||
},
|
||||
@@ -131,8 +131,8 @@ func createAlertCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for Alert reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isAlertReady(ctx, kubeClient, namespacedName, &alert)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isStaticObjectReadyConditionFunc(kubeClient, namespacedName, &alert)); err != nil {
|
||||
return err
|
||||
}
|
||||
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,
|
||||
alert *notificationv1.Alert) (types.NamespacedName, error) {
|
||||
alert *notificationv1b3.Alert) (types.NamespacedName, error) {
|
||||
namespacedName := types.NamespacedName{
|
||||
Namespace: alert.GetNamespace(),
|
||||
Name: alert.GetName(),
|
||||
}
|
||||
|
||||
var existing notificationv1.Alert
|
||||
var existing notificationv1b3.Alert
|
||||
err := kubeClient.Get(ctx, namespacedName, &existing)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
@@ -169,23 +169,3 @@ func upsertAlert(ctx context.Context, kubeClient client.Client,
|
||||
logger.Successf("Alert updated")
|
||||
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"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"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/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
var createAlertProviderCmd = &cobra.Command{
|
||||
Use: "alert-provider [name]",
|
||||
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
|
||||
flux create alert-provider slack \
|
||||
--type slack \
|
||||
@@ -127,8 +126,8 @@ func createAlertProviderCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for Provider reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isAlertProviderReady(ctx, kubeClient, namespacedName, &provider)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isStaticObjectReadyConditionFunc(kubeClient, namespacedName, &provider)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -167,23 +166,3 @@ func upsertAlertProvider(ctx context.Context, kubeClient client.Client,
|
||||
logger.Successf("Provider updated")
|
||||
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"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/transform"
|
||||
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta2"
|
||||
)
|
||||
|
||||
var createHelmReleaseCmd = &cobra.Command{
|
||||
Use: "helmrelease [name]",
|
||||
Aliases: []string{"hr"},
|
||||
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
|
||||
flux create hr podinfo \
|
||||
--interval=10m \
|
||||
@@ -83,9 +83,9 @@ var createHelmReleaseCmd = &cobra.Command{
|
||||
|
||||
# Create a HelmRelease with a custom release name
|
||||
flux create hr podinfo \
|
||||
--release-name=podinfo-dev
|
||||
--release-name=podinfo-dev \
|
||||
--source=HelmRepository/podinfo \
|
||||
--chart=podinfo \
|
||||
--chart=podinfo
|
||||
|
||||
# Create a HelmRelease targeting another namespace than the resource
|
||||
flux create hr podinfo \
|
||||
@@ -200,7 +200,7 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if helmReleaseArgs.kubeConfigSecretRef != "" {
|
||||
helmRelease.Spec.KubeConfig = &helmv2.KubeConfig{
|
||||
helmRelease.Spec.KubeConfig = &meta.KubeConfigReference{
|
||||
SecretRef: meta.SecretKeyReference{
|
||||
Name: helmReleaseArgs.kubeConfigSecretRef,
|
||||
},
|
||||
@@ -303,8 +303,8 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for HelmRelease reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isHelmReleaseReady(ctx, kubeClient, namespacedName, &helmRelease)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isObjectReadyConditionFunc(kubeClient, namespacedName, &helmRelease)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("HelmRelease %s is ready", name)
|
||||
@@ -344,23 +344,6 @@ func upsertHelmRelease(ctx context.Context, kubeClient client.Client,
|
||||
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 {
|
||||
allowedStrategy := []string{"Revision", "ChartVersion"}
|
||||
|
||||
|
||||
@@ -20,14 +20,12 @@ import (
|
||||
"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{
|
||||
Use: "image",
|
||||
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() {
|
||||
|
||||
@@ -34,12 +34,12 @@ import (
|
||||
var createImagePolicyCmd = &cobra.Command{
|
||||
Use: "policy [name]",
|
||||
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
|
||||
repository and a policy, e.g., semver.
|
||||
|
||||
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
|
||||
flux create image policy podinfo \
|
||||
--image-ref=podinfo \
|
||||
@@ -54,13 +54,12 @@ the status of the object.`,
|
||||
RunE: createImagePolicyRun}
|
||||
|
||||
type imagePolicyFlags struct {
|
||||
imageRef string
|
||||
semver string
|
||||
alpha string
|
||||
numeric string
|
||||
filterRegex string
|
||||
filterExtract string
|
||||
filterNumerical string
|
||||
imageRef string
|
||||
semver string
|
||||
alpha string
|
||||
numeric string
|
||||
filterRegex string
|
||||
filterExtract string
|
||||
}
|
||||
|
||||
var imagePolicyArgs = imagePolicyFlags{}
|
||||
@@ -183,7 +182,6 @@ func validateExtractStr(template string, capNames []string) error {
|
||||
name, num, rest, ok := extract(template)
|
||||
if !ok {
|
||||
// Malformed extract string, assume user didn't want this
|
||||
template = template[1:]
|
||||
return fmt.Errorf("--filter-extract is malformed")
|
||||
}
|
||||
template = rest
|
||||
|
||||
@@ -32,8 +32,8 @@ import (
|
||||
var createImageRepositoryCmd = &cobra.Command{
|
||||
Use: "repository [name]",
|
||||
Short: "Create or update an ImageRepository object",
|
||||
Long: `The create image repository command generates an ImageRepository resource.
|
||||
An ImageRepository object specifies an image repository to scan.`,
|
||||
Long: withPreviewNote(`The create image repository command generates an ImageRepository resource.
|
||||
An ImageRepository object specifies an image repository to scan.`),
|
||||
Example: ` # Create an ImageRepository object to scan the alpine image repository:
|
||||
flux create image repository alpine-repo --image alpine --interval 20m
|
||||
|
||||
|
||||
@@ -23,15 +23,15 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
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{
|
||||
Use: "update [name]",
|
||||
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
|
||||
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
|
||||
flux create image update flux-system \
|
||||
--git-repo-ref=flux-system \
|
||||
|
||||
@@ -24,25 +24,24 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta2"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
var createKsCmd = &cobra.Command{
|
||||
Use: "kustomization [name]",
|
||||
Aliases: []string{"ks"},
|
||||
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
|
||||
flux create kustomization kyverno \
|
||||
--source=GitRepository/kyverno \
|
||||
@@ -97,6 +96,7 @@ type kustomizationFlags struct {
|
||||
targetNamespace string
|
||||
wait bool
|
||||
kubeConfigSecretRef string
|
||||
retryInterval time.Duration
|
||||
}
|
||||
|
||||
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.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().DurationVar(&kustomizationArgs.retryInterval, "retry-interval", 0, "the interval at which to retry a previously failed reconciliation")
|
||||
|
||||
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 {
|
||||
return printExport(exportKs(&kustomization))
|
||||
}
|
||||
@@ -257,8 +262,8 @@ func createKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for Kustomization reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isKustomizationReady(ctx, kubeClient, namespacedName, &kustomization)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isObjectReadyConditionFunc(kubeClient, namespacedName, &kustomization)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("Kustomization %s is ready", name)
|
||||
@@ -297,28 +302,3 @@ func upsertKustomization(ctx context.Context, kubeClient client.Client,
|
||||
logger.Successf("Kustomization updated")
|
||||
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"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"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/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
var createReceiverCmd = &cobra.Command{
|
||||
Use: "receiver [name]",
|
||||
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
|
||||
flux create receiver github-receiver \
|
||||
--type github \
|
||||
@@ -139,13 +138,13 @@ func createReceiverCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for Receiver reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isReceiverReady(ctx, kubeClient, namespacedName, &receiver)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isObjectReadyConditionFunc(kubeClient, namespacedName, &receiver)); err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -179,23 +178,3 @@ func upsertReceiver(ctx context.Context, kubeClient client.Client,
|
||||
logger.Successf("Receiver updated")
|
||||
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{
|
||||
Use: "secret",
|
||||
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() {
|
||||
|
||||
@@ -27,17 +27,19 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
)
|
||||
|
||||
var createSecretGitCmd = &cobra.Command{
|
||||
Use: "git [name]",
|
||||
Short: "Create or update a Kubernetes secret for Git authentication",
|
||||
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 HTTP/S, the provided basic authentication credentials are stored in the secret.`,
|
||||
For Git over SSH, the host and SSH keys are automatically generated and stored
|
||||
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
|
||||
|
||||
flux create secret git podinfo-auth \
|
||||
@@ -86,7 +88,9 @@ type secretGitFlags struct {
|
||||
rsaBits flags.RSAKeyBits
|
||||
ecdsaCurve flags.ECDSACurve
|
||||
caFile string
|
||||
caCrtFile string
|
||||
privateKeyFile string
|
||||
bearerToken string
|
||||
}
|
||||
|
||||
var secretGitArgs = NewSecretGitFlags()
|
||||
@@ -99,7 +103,9 @@ func init() {
|
||||
createSecretGitCmd.Flags().Var(&secretGitArgs.rsaBits, "ssh-rsa-bits", secretGitArgs.rsaBits.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.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.bearerToken, "bearer-token", "", "bearer authentication token")
|
||||
|
||||
createSecretCmd.AddCommand(createSecretGitCmd)
|
||||
}
|
||||
@@ -147,17 +153,27 @@ func createSecretGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
opts.ECDSACurve = secretGitArgs.ecdsaCurve.Curve
|
||||
opts.Password = secretGitArgs.password
|
||||
case "http", "https":
|
||||
if secretGitArgs.username == "" || secretGitArgs.password == "" {
|
||||
return fmt.Errorf("for Git over HTTP/S the username and password are required")
|
||||
if (secretGitArgs.username == "" || secretGitArgs.password == "") && secretGitArgs.bearerToken == "" {
|
||||
return fmt.Errorf("for Git over HTTP/S the username and password, or a bearer token is required")
|
||||
}
|
||||
opts.Username = secretGitArgs.username
|
||||
opts.Password = secretGitArgs.password
|
||||
if secretGitArgs.caFile != "" {
|
||||
caBundle, err := os.ReadFile(secretGitArgs.caFile)
|
||||
opts.BearerToken = secretGitArgs.bearerToken
|
||||
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 {
|
||||
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||
}
|
||||
opts.CAFile = caBundle
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("git URL scheme '%s' not supported, can be: ssh, http and https", u.Scheme)
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
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 {
|
||||
name 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",
|
||||
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 {
|
||||
|
||||
@@ -25,14 +25,14 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
)
|
||||
|
||||
var createSecretHelmCmd = &cobra.Command{
|
||||
Use: "helm [name]",
|
||||
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
|
||||
flux create secret helm repo-auth \
|
||||
--namespace=my-namespace \
|
||||
@@ -41,15 +41,8 @@ var createSecretHelmCmd = &cobra.Command{
|
||||
--export > repo-auth.yaml
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -62,9 +55,16 @@ type secretHelmFlags struct {
|
||||
var secretHelmArgs secretHelmFlags
|
||||
|
||||
func init() {
|
||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.username, "username", "u", "", "basic authentication username")
|
||||
createSecretHelmCmd.Flags().StringVarP(&secretHelmArgs.password, "password", "p", "", "basic authentication password")
|
||||
initSecretTLSFlags(createSecretHelmCmd.Flags(), &secretHelmArgs.secretTLSFlags)
|
||||
flags := createSecretHelmCmd.Flags()
|
||||
flags.StringVarP(&secretHelmArgs.username, "username", "u", "", "basic authentication username")
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
var createSecretOCICmd = &cobra.Command{
|
||||
Use: "oci [name]",
|
||||
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
|
||||
flux create secret oci podinfo-auth \
|
||||
--url=ghcr.io \
|
||||
|
||||
@@ -26,20 +26,21 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
)
|
||||
|
||||
var createSecretTLSCmd = &cobra.Command{
|
||||
Use: "tls [name]",
|
||||
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.
|
||||
# Files are expected to be PEM-encoded.
|
||||
flux create secret tls certs \
|
||||
--namespace=my-namespace \
|
||||
--cert-file=./client.crt \
|
||||
--key-file=./client.key \
|
||||
--tls-crt-file=./client.crt \
|
||||
--tls-key-file=./client.key \
|
||||
--ca-crt-file=./ca.crt \
|
||||
--export > certs.yaml
|
||||
|
||||
sops --encrypt --encrypted-regex '^(data|stringData)$' \
|
||||
@@ -48,22 +49,37 @@ var createSecretTLSCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
type secretTLSFlags struct {
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
caCrtFile string
|
||||
tlsKeyFile string
|
||||
tlsCrtFile string
|
||||
}
|
||||
|
||||
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.keyFile, "key-file", "", "TLS authentication key 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() {
|
||||
flags := createSecretTLSCmd.Flags()
|
||||
initSecretDeprecatedTLSFlags(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)
|
||||
}
|
||||
|
||||
@@ -75,33 +91,40 @@ func createSecretTLSCmdRun(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
caBundle := []byte{}
|
||||
if secretTLSArgs.caFile != "" {
|
||||
var err error
|
||||
caBundle, err = os.ReadFile(secretTLSArgs.caFile)
|
||||
opts := sourcesecret.Options{
|
||||
Name: name,
|
||||
Namespace: *kubeconfigArgs.Namespace,
|
||||
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 {
|
||||
return fmt.Errorf("unable to read TLS CA file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var certFile, keyFile []byte
|
||||
if secretTLSArgs.certFile != "" && secretTLSArgs.keyFile != "" {
|
||||
if certFile, err = os.ReadFile(secretTLSArgs.certFile); err != nil {
|
||||
if secretTLSArgs.tlsCrtFile != "" && secretTLSArgs.tlsKeyFile != "" {
|
||||
if opts.TLSCrt, err = os.ReadFile(secretTLSArgs.tlsCrtFile); err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
opts := sourcesecret.Options{
|
||||
Name: name,
|
||||
Namespace: *kubeconfigArgs.Namespace,
|
||||
Labels: labels,
|
||||
CAFile: caBundle,
|
||||
CertFile: certFile,
|
||||
KeyFile: keyFile,
|
||||
}
|
||||
secret, err := sourcesecret.Generate(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateTlsSecretNoArgs(t *testing.T) {
|
||||
func TestCreateTlsSecret(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
@@ -15,9 +15,13 @@ func TestCreateTlsSecretNoArgs(t *testing.T) {
|
||||
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"),
|
||||
},
|
||||
{
|
||||
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 {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
var createSourceCmd = &cobra.Command{
|
||||
Use: "source",
|
||||
Short: "Create or update sources",
|
||||
Long: "The create source sub-commands generate sources.",
|
||||
Long: `The create source sub-commands generate sources.`,
|
||||
}
|
||||
|
||||
type createSourceFlags struct {
|
||||
|
||||
@@ -31,19 +31,18 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
var createSourceBucketCmd = &cobra.Command{
|
||||
Use: "bucket [name]",
|
||||
Short: "Create or update a Bucket source",
|
||||
Long: `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.`,
|
||||
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.`),
|
||||
Example: ` # Create a source for a Bucket using static authentication
|
||||
flux create source bucket podinfo \
|
||||
--bucket-name=podinfo \
|
||||
@@ -204,8 +203,8 @@ func createSourceBucketCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for Bucket source reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isBucketReady(ctx, kubeClient, namespacedName, bucket)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isObjectReadyConditionFunc(kubeClient, namespacedName, bucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("Bucket source reconciliation completed")
|
||||
@@ -247,30 +246,3 @@ func upsertBucket(ctx context.Context, kubeClient client.Client,
|
||||
logger.Successf("Bucket source updated")
|
||||
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"
|
||||
|
||||
"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/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
)
|
||||
|
||||
type sourceGitFlags struct {
|
||||
@@ -49,6 +48,8 @@ type sourceGitFlags struct {
|
||||
branch string
|
||||
tag string
|
||||
semver string
|
||||
refName string
|
||||
commit string
|
||||
username string
|
||||
password string
|
||||
keyAlgorithm flags.PublicKeyAlgorithm
|
||||
@@ -129,6 +130,8 @@ func init() {
|
||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.branch, "branch", "", "git branch")
|
||||
createSourceGitCmd.Flags().StringVar(&sourceGitArgs.tag, "tag", "", "git tag")
|
||||
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.password, "password", "p", "", "basic authentication password")
|
||||
createSourceGitCmd.Flags().Var(&sourceGitArgs.keyAlgorithm, "ssh-key-algorithm", sourceGitArgs.keyAlgorithm.Description())
|
||||
@@ -168,8 +171,8 @@ 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)
|
||||
}
|
||||
|
||||
if sourceGitArgs.branch == "" && sourceGitArgs.tag == "" && sourceGitArgs.semver == "" {
|
||||
return fmt.Errorf("a Git ref is required, use one of the following: --branch, --tag or --tag-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, --commit, --ref-name or --tag-semver")
|
||||
}
|
||||
|
||||
if sourceGitArgs.caFile != "" && u.Scheme == "ssh" {
|
||||
@@ -214,7 +217,12 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
gitRepository.Spec.Timeout = &metav1.Duration{Duration: createSourceArgs.fetchTimeout}
|
||||
}
|
||||
|
||||
if sourceGitArgs.semver != "" {
|
||||
if sourceGitArgs.commit != "" {
|
||||
gitRepository.Spec.Reference.Commit = sourceGitArgs.commit
|
||||
gitRepository.Spec.Reference.Branch = sourceGitArgs.branch
|
||||
} else if sourceGitArgs.refName != "" {
|
||||
gitRepository.Spec.Reference.Name = sourceGitArgs.refName
|
||||
} else if sourceGitArgs.semver != "" {
|
||||
gitRepository.Spec.Reference.SemVer = sourceGitArgs.semver
|
||||
} else if sourceGitArgs.tag != "" {
|
||||
gitRepository.Spec.Reference.Tag = sourceGitArgs.tag
|
||||
@@ -316,8 +324,8 @@ func createSourceGitCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for GitRepository source reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isGitRepositoryReady(ctx, kubeClient, namespacedName, &gitRepository)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isObjectReadyConditionFunc(kubeClient, namespacedName, &gitRepository)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("GitRepository source reconciliation completed")
|
||||
@@ -359,30 +367,3 @@ func upsertGitRepository(ctx context.Context, kubeClient client.Client,
|
||||
logger.Successf("GitRepository source updated")
|
||||
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"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"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
|
||||
@@ -98,6 +99,41 @@ func TestCreateSourceGitExport(t *testing.T) {
|
||||
command,
|
||||
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 {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -141,13 +177,25 @@ func TestCreateSourceGit(t *testing.T) {
|
||||
repo.Status.Artifact = &sourcev1.Artifact{
|
||||
Path: "some-path",
|
||||
Revision: "v1",
|
||||
LastUpdateTime: metav1.Time{
|
||||
Time: time.Now(),
|
||||
},
|
||||
}
|
||||
repo.Status.ObservedGeneration = repo.GetGeneration()
|
||||
},
|
||||
}, {
|
||||
"Failed",
|
||||
command,
|
||||
assertError("failed message"),
|
||||
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{
|
||||
Type: meta.ReadyCondition,
|
||||
Status: metav1.ConditionFalse,
|
||||
@@ -156,6 +204,7 @@ func TestCreateSourceGit(t *testing.T) {
|
||||
ObservedGeneration: repo.GetGeneration(),
|
||||
}
|
||||
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
||||
repo.Status.ObservedGeneration = repo.GetGeneration()
|
||||
},
|
||||
}, {
|
||||
"NoArtifact",
|
||||
@@ -171,6 +220,7 @@ func TestCreateSourceGit(t *testing.T) {
|
||||
ObservedGeneration: repo.GetGeneration(),
|
||||
}
|
||||
apimeta.SetStatusCondition(&repo.Status.Conditions, newCondition)
|
||||
repo.Status.ObservedGeneration = repo.GetGeneration()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -35,15 +34,15 @@ import (
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/pkg/manifestgen/sourcesecret"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/pkg/manifestgen/sourcesecret"
|
||||
)
|
||||
|
||||
var createSourceHelmCmd = &cobra.Command{
|
||||
Use: "helm [name]",
|
||||
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.
|
||||
For private Helm repositories, the basic authentication credentials are stored in a Kubernetes secret.`,
|
||||
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.`),
|
||||
Example: ` # Create a source for an HTTPS public Helm repository
|
||||
flux create source helm 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
|
||||
flux create source helm podinfo \
|
||||
--url=oci://ghcr.io/stefanprodan/charts/podinfo
|
||||
--url=oci://ghcr.io/stefanprodan/charts/podinfo \
|
||||
--username=username \
|
||||
--password=password
|
||||
|
||||
# Create a source for an OCI Helm repository using an existing secret with basic auth or dockerconfig credentials
|
||||
flux create source helm podinfo \
|
||||
--url=oci://ghcr.io/stefanprodan/charts/podinfo
|
||||
--url=oci://ghcr.io/stefanprodan/charts/podinfo \
|
||||
--secret-ref=docker-config`,
|
||||
RunE: createSourceHelmCmdRun,
|
||||
}
|
||||
@@ -83,6 +82,7 @@ type sourceHelmFlags struct {
|
||||
keyFile string
|
||||
caFile string
|
||||
secretRef string
|
||||
ociProvider string
|
||||
passCredentials bool
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ func init() {
|
||||
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().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")
|
||||
|
||||
createSourceCmd.AddCommand(createSourceHelmCmd)
|
||||
@@ -143,6 +144,7 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
if url.Scheme == sourcev1.HelmRepositoryTypeOCI {
|
||||
helmRepository.Spec.Type = sourcev1.HelmRepositoryTypeOCI
|
||||
helmRepository.Spec.Provider = sourceHelmArgs.ociProvider
|
||||
}
|
||||
|
||||
if createSourceArgs.fetchTimeout > 0 {
|
||||
@@ -228,8 +230,12 @@ func createSourceHelmCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for HelmRepository source reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isHelmRepositoryReady(ctx, kubeClient, namespacedName, helmRepository)); err != nil {
|
||||
readyConditionFunc := isObjectReadyConditionFunc(kubeClient, namespacedName, helmRepository)
|
||||
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
|
||||
}
|
||||
logger.Successf("HelmRepository source reconciliation completed")
|
||||
@@ -276,30 +282,3 @@ func upsertHelmRepository(ctx context.Context, kubeClient client.Client,
|
||||
logger.Successf("source updated")
|
||||
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"
|
||||
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
var createSourceOCIRepositoryCmd = &cobra.Command{
|
||||
Use: "oci [name]",
|
||||
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
|
||||
flux create source oci podinfo \
|
||||
--url=oci://ghcr.io/stefanprodan/manifests/podinfo \
|
||||
@@ -51,16 +50,18 @@ var createSourceOCIRepositoryCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
type sourceOCIRepositoryFlags struct {
|
||||
url string
|
||||
tag string
|
||||
semver string
|
||||
digest string
|
||||
secretRef string
|
||||
serviceAccount string
|
||||
certSecretRef string
|
||||
ignorePaths []string
|
||||
provider flags.SourceOCIProvider
|
||||
insecure bool
|
||||
url string
|
||||
tag string
|
||||
semver string
|
||||
digest string
|
||||
secretRef string
|
||||
serviceAccount string
|
||||
certSecretRef string
|
||||
verifyProvider flags.SourceOCIVerifyProvider
|
||||
verifySecretRef string
|
||||
ignorePaths []string
|
||||
provider flags.SourceOCIProvider
|
||||
insecure bool
|
||||
}
|
||||
|
||||
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.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().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().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 {
|
||||
return printExport(exportOCIRepository(repository))
|
||||
}
|
||||
@@ -175,8 +191,8 @@ func createSourceOCIRepositoryCmdRun(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
logger.Waitingf("waiting for OCIRepository reconciliation")
|
||||
if err := wait.PollImmediate(rootArgs.pollInterval, rootArgs.timeout,
|
||||
isOCIRepositoryReady(ctx, kubeClient, namespacedName, repository)); err != nil {
|
||||
if err := wait.PollUntilContextTimeout(ctx, rootArgs.pollInterval, rootArgs.timeout, true,
|
||||
isObjectReadyConditionFunc(kubeClient, namespacedName, repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Successf("OCIRepository reconciliation completed")
|
||||
@@ -218,30 +234,3 @@ func upsertOCIRepository(ctx context.Context, kubeClient client.Client,
|
||||
logger.Successf("OCIRepository updated")
|
||||
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",
|
||||
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",
|
||||
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"),
|
||||
},
|
||||
{
|
||||
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"),
|
||||
},
|
||||
{
|
||||
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 {
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
"github.com/spf13/cobra"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
@@ -37,8 +37,8 @@ import (
|
||||
var createTenantCmd = &cobra.Command{
|
||||
Use: "tenant",
|
||||
Short: "Create or update a tenant",
|
||||
Long: `The create tenant command generates namespaces, service accounts and role bindings to limit the
|
||||
reconcilers scope to the tenant namespaces.`,
|
||||
Long: withPreviewNote(`The create tenant command generates namespaces, service accounts and role bindings to limit the
|
||||
reconcilers scope to the tenant namespaces.`),
|
||||
Example: ` # Create a tenant with access to a namespace
|
||||
flux create tenant dev-team \
|
||||
--with-namespace=frontend \
|
||||
|
||||
@@ -24,13 +24,13 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
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 {
|
||||
|
||||
@@ -19,13 +19,13 @@ package main
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
)
|
||||
|
||||
var deleteAlertCmd = &cobra.Command{
|
||||
Use: "alert [name]",
|
||||
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
|
||||
flux delete alert main`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.AlertKind)),
|
||||
|
||||
@@ -19,13 +19,13 @@ package main
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
)
|
||||
|
||||
var deleteAlertProviderCmd = &cobra.Command{
|
||||
Use: "alert-provider [name]",
|
||||
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
|
||||
flux delete alert-provider slack`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ProviderKind)),
|
||||
|
||||
@@ -19,14 +19,14 @@ package main
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2beta2"
|
||||
)
|
||||
|
||||
var deleteHelmReleaseCmd = &cobra.Command{
|
||||
Use: "helmrelease [name]",
|
||||
Aliases: []string{"hr"},
|
||||
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
|
||||
flux delete hr podinfo`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(helmv2.GroupVersion.WithKind(helmv2.HelmReleaseKind)),
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
var deleteImageCmd = &cobra.Command{
|
||||
Use: "image",
|
||||
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() {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
var deleteImagePolicyCmd = &cobra.Command{
|
||||
Use: "policy [name]",
|
||||
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
|
||||
flux delete image policy alpine3.x`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImagePolicyKind)),
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
var deleteImageRepositoryCmd = &cobra.Command{
|
||||
Use: "repository [name]",
|
||||
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
|
||||
flux delete image repository alpine`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(imagev1.GroupVersion.WithKind(imagev1.ImageRepositoryKind)),
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
var deleteImageUpdateCmd = &cobra.Command{
|
||||
Use: "update [name]",
|
||||
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
|
||||
flux delete image update latest-images`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(autov1.GroupVersion.WithKind(autov1.ImageUpdateAutomationKind)),
|
||||
|
||||
@@ -19,14 +19,14 @@ package main
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||
)
|
||||
|
||||
var deleteKsCmd = &cobra.Command{
|
||||
Use: "kustomization [name]",
|
||||
Aliases: []string{"ks"},
|
||||
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
|
||||
flux delete kustomization podinfo`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(kustomizev1.GroupVersion.WithKind(kustomizev1.KustomizationKind)),
|
||||
|
||||
@@ -19,13 +19,13 @@ package main
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1beta2"
|
||||
notificationv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||
)
|
||||
|
||||
var deleteReceiverCmd = &cobra.Command{
|
||||
Use: "receiver [name]",
|
||||
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
|
||||
flux delete receiver main`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(notificationv1.GroupVersion.WithKind(notificationv1.ReceiverKind)),
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
var deleteSourceCmd = &cobra.Command{
|
||||
Use: "source",
|
||||
Short: "Delete sources",
|
||||
Long: "The delete source sub-commands delete sources.",
|
||||
Long: `The delete source sub-commands delete sources.`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
var deleteSourceBucketCmd = &cobra.Command{
|
||||
Use: "bucket [name]",
|
||||
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
|
||||
flux delete source bucket podinfo`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.BucketKind)),
|
||||
|
||||
@@ -19,13 +19,13 @@ package main
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1"
|
||||
)
|
||||
|
||||
var deleteSourceGitCmd = &cobra.Command{
|
||||
Use: "git [name]",
|
||||
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
|
||||
flux delete source git podinfo`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.GitRepositoryKind)),
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
var deleteSourceHelmCmd = &cobra.Command{
|
||||
Use: "helm [name]",
|
||||
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
|
||||
flux delete source helm podinfo`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.HelmRepositoryKind)),
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
var deleteSourceOCIRepositoryCmd = &cobra.Command{
|
||||
Use: "oci [name]",
|
||||
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
|
||||
flux delete source oci podinfo`,
|
||||
ValidArgsFunction: resourceNamesCompletionFunc(sourcev1.GroupVersion.WithKind(sourcev1.OCIRepositoryKind)),
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
var diffCmd = &cobra.Command{
|
||||
Use: "diff",
|
||||
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() {
|
||||
|
||||
@@ -21,16 +21,17 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/flags"
|
||||
oci "github.com/fluxcd/pkg/oci/client"
|
||||
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxcd/flux2/v2/internal/flags"
|
||||
)
|
||||
|
||||
var diffArtifactCmd = &cobra.Command{
|
||||
Use: "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
|
||||
flux diff artifact oci://ghcr.io/stefanprodan/manifests:podinfo:6.2.0 --path=./kustomize`,
|
||||
RunE: diffArtifactCmdRun,
|
||||
@@ -81,7 +82,7 @@ func diffArtifactCmdRun(cmd *cobra.Command, args []string) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||
defer cancel()
|
||||
|
||||
ociClient := oci.NewLocalClient()
|
||||
ociClient := oci.NewClient(oci.DefaultOptions())
|
||||
|
||||
if diffArtifactArgs.provider.String() == sourcev1.GenericOCIProvider && diffArtifactArgs.creds != "" {
|
||||
logger.Actionf("logging in to registry with credentials")
|
||||
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/build"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
|
||||
"github.com/fluxcd/flux2/v2/internal/build"
|
||||
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
# 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)),
|
||||
RunE: diffKsCmdRun,
|
||||
}
|
||||
@@ -45,6 +51,7 @@ flux diff kustomization my-app --path ./path/to/local/manifests --kustomization-
|
||||
type diffKsFlags struct {
|
||||
kustomizationFile string
|
||||
path string
|
||||
ignorePaths []string
|
||||
progressBar bool
|
||||
}
|
||||
|
||||
@@ -53,6 +60,7 @@ var diffKsArgs diffKsFlags
|
||||
func init() {
|
||||
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().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.")
|
||||
diffCmd.AddCommand(diffKsCmd)
|
||||
}
|
||||
@@ -86,12 +94,16 @@ func diffKsCmdRun(cmd *cobra.Command, args []string) error {
|
||||
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
||||
build.WithTimeout(rootArgs.timeout),
|
||||
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
|
||||
build.WithProgressBar())
|
||||
build.WithProgressBar(),
|
||||
build.WithIgnore(diffKsArgs.ignorePaths),
|
||||
)
|
||||
} else {
|
||||
builder, err = build.NewBuilder(name, diffKsArgs.path,
|
||||
build.WithClientConfig(kubeconfigArgs, kubeclientOptions),
|
||||
build.WithTimeout(rootArgs.timeout),
|
||||
build.WithKustomizationFile(diffKsArgs.kustomizationFile))
|
||||
build.WithKustomizationFile(diffKsArgs.kustomizationFile),
|
||||
build.WithIgnore(diffKsArgs.ignorePaths),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/build"
|
||||
"github.com/fluxcd/flux2/v2/internal/build"
|
||||
"github.com/fluxcd/pkg/ssa"
|
||||
"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",
|
||||
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{
|
||||
|
||||
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/yaml"
|
||||
|
||||
"github.com/fluxcd/flux2/internal/utils"
|
||||
"github.com/fluxcd/flux2/v2/internal/utils"
|
||||
)
|
||||
|
||||
var exportCmd = &cobra.Command{
|
||||
Use: "export",
|
||||
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 {
|
||||
@@ -122,5 +122,6 @@ func printExport(export interface{}) error {
|
||||
func resourceToString(data []byte) string {
|
||||
data = bytes.Replace(data, []byte(" creationTimestamp: null\n"), []byte(""), 1)
|
||||
data = bytes.Replace(data, []byte("status: {}\n"), []byte(""), 1)
|
||||
data = bytes.TrimSpace(data)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
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{
|
||||
Use: "alert [name]",
|
||||
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
|
||||
flux export alert --all > alerts.yaml
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
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 exportAlertProviderCmd = &cobra.Command{
|
||||
Use: "alert-provider [name]",
|
||||
Short: "Export Provider resources in YAML format",
|
||||
Long: "The export alert-provider command exports one or all Provider resources in YAML format.",
|
||||
Long: withPreviewNote("The export alert-provider command exports one or all Provider resources in YAML format."),
|
||||
Example: ` # Export all Provider resources
|
||||
flux export alert-provider --all > alert-providers.yaml
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user