Compare commits
984 Commits
feature/pi
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b6d161cc8 | ||
|
|
d0a7911b95 | ||
|
|
d5cf8b16a2 | ||
|
|
6272d1f081 | ||
|
|
8a843c4936 | ||
|
|
7f06fecdac | ||
|
|
28bfd4bbeb | ||
|
|
c3183ac051 | ||
|
|
c823ffee55 | ||
|
|
3c1a368360 | ||
| b2f1cff95b | |||
|
|
5b902a4d4e | ||
|
|
ddccdbdc49 | ||
|
|
b259c6d46b | ||
|
|
2b9c972202 | ||
|
|
ce4f291e08 | ||
|
|
3ba994be05 | ||
|
|
413659558b | ||
|
|
a6e9ff2b65 | ||
|
|
9cfca159b3 | ||
|
|
d6c2aedd76 | ||
|
|
fb6fb35a12 | ||
|
|
b5101a1320 | ||
|
|
a521a7b914 | ||
|
|
21ced4a417 | ||
|
|
eeee467d20 | ||
|
|
5c7f64accb | ||
|
|
74c9b85c46 | ||
|
|
cff9aeb535 | ||
|
|
c2c0cbe283 | ||
|
|
6b945f85b3 | ||
|
|
bfd3399db5 | ||
|
|
b365e1c493 | ||
| 85d7713e30 | |||
|
|
51e101bc31 | ||
|
|
bfda6cd908 | ||
|
|
e068749261 | ||
|
|
07cb3fa61f | ||
| 79d47cad60 | |||
| 4ed9dcc48d | |||
| f0fa9404af | |||
| 93f586efe6 | |||
| 678daecda5 | |||
| 6029ff4d53 | |||
|
|
ec6deb1370 | ||
|
|
0d25683632 | ||
|
|
0e222d0cbb | ||
|
|
e87206a455 | ||
|
|
7af32c84d6 | ||
|
|
57edd366ac | ||
| 93c9539fd2 | |||
| aeb345d4e6 | |||
| dc2db00c79 | |||
| adf9603077 | |||
| 07fe56a4aa | |||
|
|
528c727874 | ||
|
|
ad920ef9c9 | ||
|
|
05e147d5d8 | ||
|
|
a34e72aa7f | ||
|
|
1a75277373 | ||
|
|
6883aec12e | ||
|
|
e8fa38d611 | ||
|
|
9eeb7a0974 | ||
|
|
07a97a74d6 | ||
|
|
5821650240 | ||
|
|
5466037a8d | ||
|
|
4ddb82947e | ||
|
|
3c55b4d69b | ||
|
|
000c9154a1 | ||
|
|
27fb19a900 | ||
|
|
1e92deaf23 | ||
|
|
7a676e01a3 | ||
|
|
c682e5158b | ||
|
|
0ee0070d14 | ||
|
|
adc9601228 | ||
|
|
9aec012c42 | ||
|
|
622dbc650d | ||
|
|
6a6ef8a22b | ||
|
|
e8fe7f1146 | ||
|
|
fc5edbaa83 | ||
|
|
16698074bf | ||
|
|
531bc440d5 | ||
|
|
0d8571b7a6 | ||
|
|
083e9e1148 | ||
|
|
6833c3fe61 | ||
|
|
07073970cf | ||
|
|
249f20091f | ||
|
|
63c869bf42 | ||
|
|
8fcc61cae9 | ||
|
|
7885a70ee1 | ||
|
|
42069b0f23 | ||
|
|
5db9dd54fc | ||
|
|
530f813ebd | ||
|
|
599e973d68 | ||
|
|
6a40f40932 | ||
|
|
363e564002 | ||
|
|
2985a7d12c | ||
|
|
4f7777522e | ||
|
|
832c025c80 | ||
|
|
28356e89fc | ||
|
|
ea6a10dd7f | ||
|
|
654900b8a2 | ||
|
|
e1d091eb14 | ||
|
|
23225c366b | ||
|
|
eb1163f1ab | ||
|
|
48fce69f96 | ||
|
|
d219995052 | ||
|
|
0318c3fe08 | ||
|
|
c09d88a06e | ||
|
|
7c3aadd4c6 | ||
|
|
e0d88aa265 | ||
|
|
9667f0e606 | ||
|
|
3fb72623e9 | ||
|
|
d6acb9bf78 | ||
|
|
312a299af5 | ||
|
|
81efc1c723 | ||
|
|
a903948ac6 | ||
|
|
1c26fe1377 | ||
|
|
d760defdf0 | ||
|
|
4555b6e25a | ||
|
|
3c00906357 | ||
|
|
9f4fa3537f | ||
|
|
82486d1408 | ||
|
|
b8c844dbac | ||
|
|
0532fe2634 | ||
|
|
2b0e2764c6 | ||
|
|
f701ae1628 | ||
|
|
0e211f599c | ||
|
|
282da9cf49 | ||
|
|
3d552432a4 | ||
|
|
d97cdf2c2c | ||
|
|
828af81405 | ||
|
|
d6ec355cde | ||
|
|
a5d297e8e0 | ||
|
|
1146000c7d | ||
|
|
6bcbab91d7 | ||
|
|
6324292f3b | ||
|
|
bf2d4e1a62 | ||
|
|
c255749410 | ||
|
|
4fd8a00d4a | ||
|
|
75d002dc88 | ||
|
|
f93e3e6050 | ||
|
|
ed5504b072 | ||
|
|
22eb0cc3f4 | ||
|
|
6b53caf57a | ||
|
|
2712600a1e | ||
|
|
c916a8c862 | ||
|
|
7158c32c06 | ||
|
|
a2ecdef536 | ||
|
|
dce9d5c131 | ||
|
|
ec2ab28cec | ||
|
|
c13169e95b | ||
|
|
2559752654 | ||
|
|
18c7fd77b4 | ||
|
|
9d138a65a9 | ||
|
|
9e33541a24 | ||
|
|
fe0339647b | ||
|
|
fd7ec39a15 | ||
|
|
82fe618be9 | ||
|
|
09e64c8ca4 | ||
|
|
f2ad8cca4c | ||
|
|
bd6000f956 | ||
|
|
97a31b1d23 | ||
|
|
c175602541 | ||
|
|
694a9eb87e | ||
|
|
dc8551710a | ||
|
|
847ed53214 | ||
|
|
8fe590ab93 | ||
|
|
41738a71bd | ||
|
|
cc09315576 | ||
|
|
ef14c57105 | ||
|
|
e7c8453c0a | ||
|
|
65db67b70a | ||
|
|
4f79f09dad | ||
|
|
bdf695691b | ||
|
|
5f0d62fd95 | ||
|
|
ab557b9985 | ||
|
|
000e20861c | ||
|
|
1528a35634 | ||
|
|
c724faf33f | ||
|
|
869fb6aca2 | ||
|
|
0677bb7500 | ||
|
|
82cfa87921 | ||
|
|
4c6f00dd32 | ||
|
|
d6a975423f | ||
|
|
eb8052615d | ||
|
|
7ec411e21c | ||
|
|
79665b0185 | ||
|
|
a42b5c5c19 | ||
|
|
3c42a7759a | ||
|
|
43218b7584 | ||
|
|
faf3f5bc69 | ||
|
|
625b9d3191 | ||
|
|
47a492ac40 | ||
|
|
bce683752c | ||
|
|
6e9d25d93e | ||
|
|
f403defecb | ||
|
|
eba2170314 | ||
|
|
50c53fcf59 | ||
|
|
f1f6ef22dd | ||
|
|
92db3b155c | ||
|
|
f9811b88bf | ||
|
|
df37013d8a | ||
|
|
1e343fb135 | ||
|
|
84b2f646fc | ||
|
|
52305fa446 | ||
|
|
8e900f89f9 | ||
|
|
c9a8aa816e | ||
|
|
7f1d430dca | ||
|
|
76ddc811f9 | ||
|
|
812db05a06 | ||
|
|
eb44827abb | ||
|
|
14c2b129d2 | ||
|
|
1d28ab9aa4 | ||
|
|
f959b98797 | ||
|
|
9c4fcfffed | ||
|
|
5100e37471 | ||
|
|
39342e7910 | ||
|
|
c3d37fc203 | ||
|
|
2859e0f0dd | ||
|
|
f62664c419 | ||
|
|
fae4f2bbcd | ||
|
|
0e00181cb4 | ||
|
|
f50de4cd49 | ||
|
|
cf8baafed1 | ||
|
|
c7edc81239 | ||
|
|
46c3e97688 | ||
|
|
5bce6c4c04 | ||
|
|
ee5688f297 | ||
|
|
c54c7b4452 | ||
|
|
17dc9a6e52 | ||
|
|
155d7d020e | ||
|
|
f383818f93 | ||
|
|
1fe125b8b3 | ||
|
|
361a4decb3 | ||
|
|
2aea5f4ace | ||
|
|
ce13ac054c | ||
|
|
a19a19fbd5 | ||
|
|
f1a72d64fd | ||
|
|
ac9c481ce7 | ||
|
|
2ff55289a8 | ||
|
|
2d8405d299 | ||
|
|
5e27384ea2 | ||
|
|
ec972a52f1 | ||
|
|
10eed46e81 | ||
|
|
d21b61f6d9 | ||
|
|
b367c6dea3 | ||
|
|
6388ef5c6d | ||
|
|
4ce5a67b94 | ||
|
|
1375bac117 | ||
|
|
570b1212d7 | ||
|
|
ea21e106cf | ||
|
|
1c50af1d72 | ||
|
|
b5dc723e02 | ||
|
|
3f24fbdc6d | ||
|
|
1cd9fd18f4 | ||
|
|
e7ad2c3955 | ||
|
|
fd3da0e2ae | ||
|
|
5513608b1a | ||
|
|
72e4dcd84b | ||
|
|
26af225f06 | ||
|
|
e368927a0e | ||
|
|
825a7a7f37 | ||
|
|
0719b5317f | ||
|
|
f0ed508277 | ||
|
|
ca2ffd52ab | ||
|
|
a4a75a5dda | ||
|
|
b44915d158 | ||
|
|
80bc7be00b | ||
|
|
0eba74d9b3 | ||
|
|
0f84be5083 | ||
|
|
19f477ccc8 | ||
|
|
3f6970aa1a | ||
|
|
0ff3342ea6 | ||
|
|
807a31679c | ||
|
|
f064c5b47b | ||
|
|
d89fec8ae5 | ||
|
|
1332b611a3 | ||
|
|
4e82df6891 | ||
|
|
6042d8f714 | ||
|
|
c69ef0064f | ||
|
|
939231dd6a | ||
|
|
e3c05095f8 | ||
|
|
d9955af899 | ||
|
|
39db0471d7 | ||
|
|
323bf85c12 | ||
|
|
38b140580b | ||
|
|
74fb699bac | ||
|
|
e3305b7ddd | ||
|
|
4cb49d97f4 | ||
|
|
dfa53aec9e | ||
|
|
608386a820 | ||
|
|
d94535d828 | ||
|
|
c3dcf60145 | ||
|
|
fb7dd5e5d3 | ||
|
|
df960fb519 | ||
|
|
9544b59380 | ||
|
|
c75902a8ef | ||
|
|
8fbe82eb5b | ||
|
|
409295f8cb | ||
|
|
6240133fb4 | ||
|
|
25ca8f92a3 | ||
|
|
85468110b3 | ||
|
|
ccf76f2c7d | ||
|
|
c5dc6a6c80 | ||
|
|
89345cfddc | ||
|
|
1470cea862 | ||
|
|
9f61cff34e | ||
|
|
e0707b68c6 | ||
|
|
b67120ef79 | ||
|
|
f3a1037dcd | ||
|
|
a408b0bd43 | ||
|
|
66594b70c8 | ||
|
|
830883777d | ||
|
|
eed588cab0 | ||
|
|
ad99b399f6 | ||
|
|
c51078c6a3 | ||
|
|
3d1af76df7 | ||
|
|
440aec861e | ||
|
|
1fc7233267 | ||
|
|
af8a163e70 | ||
|
|
5ec561f620 | ||
|
|
eb4a197eb7 | ||
|
|
f010d0547f | ||
|
|
d822c93829 | ||
|
|
5a547f6f01 | ||
|
|
6fae3edd67 | ||
|
|
ed81d52dd9 | ||
|
|
8ce8b1aac2 | ||
|
|
400077436b | ||
|
|
f2ae3c1b0c | ||
|
|
974955ac83 | ||
|
|
0ea80a8a19 | ||
|
|
1c6c3992cf | ||
|
|
109698a2e3 | ||
|
|
e380b65eb9 | ||
|
|
3a06d29387 | ||
|
|
b70afe2f03 | ||
|
|
fe37f12e32 | ||
|
|
f9641a22b8 | ||
|
|
55d554f22b | ||
|
|
238ceb5f9b | ||
|
|
e7a3266143 | ||
|
|
17ce769284 | ||
|
|
ae47cd9de5 | ||
|
|
c0bb270087 | ||
|
|
8b1dd7cb2b | ||
|
|
f80044258f | ||
|
|
c633079532 | ||
|
|
6032f6daef | ||
|
|
bf2c0c5e4d | ||
|
|
47be1e7c70 | ||
|
|
df56762342 | ||
|
|
afb3955116 | ||
|
|
b261834537 | ||
|
|
777f4abe69 | ||
|
|
3cb3a39b49 | ||
|
|
26629205fb | ||
|
|
a914b2fd05 | ||
|
|
4a197c870e | ||
|
|
974dd84938 | ||
|
|
8fba90f8df | ||
|
|
178e523bc2 | ||
|
|
0115f5f684 | ||
|
|
269136bee9 | ||
|
|
3676fe058f | ||
|
|
75cd2eb39f | ||
|
|
48e434a028 | ||
|
|
d2202b6955 | ||
|
|
8e706d9900 | ||
|
|
a30d0fffa4 | ||
|
|
878f9ed9b8 | ||
|
|
e3112ccb6a | ||
|
|
e7145094d9 | ||
|
|
8511c90178 | ||
|
|
50e20a7805 | ||
|
|
594e02a518 | ||
|
|
b04726b6e2 | ||
|
|
36487543bb | ||
|
|
265d9df2ac | ||
|
|
287699b3db | ||
|
|
54ec6ebdca | ||
|
|
617186bd20 | ||
|
|
8d45fa9e2b | ||
|
|
63a1b7bb4f | ||
|
|
84ecb09328 | ||
|
|
fe172e0cc6 | ||
|
|
9b3c7244cc | ||
|
|
b879064146 | ||
|
|
ae4ceef711 | ||
|
|
5230341d21 | ||
|
|
5bf7a5ac20 | ||
|
|
8bf9e12b4e | ||
|
|
b648a66f9a | ||
|
|
dd20678c46 | ||
|
|
2c4b1b9cc9 | ||
|
|
ea2071bbc5 | ||
|
|
a32995b1a1 | ||
|
|
07a2dfbb92 | ||
|
|
0a59d6f26e | ||
|
|
7688673072 | ||
|
|
1776a3266c | ||
|
|
c667c97089 | ||
|
|
44bfb5a68e | ||
|
|
aadc93e681 | ||
|
|
dd4aff8861 | ||
|
|
3142d35403 | ||
|
|
887023eaeb | ||
|
|
e8fb92a44f | ||
|
|
3a8a53133c | ||
|
|
1fcb12bcb8 | ||
|
|
1bc58e10c0 | ||
|
|
b7caf4cfec | ||
|
|
2464c61339 | ||
|
|
c923be8ff1 | ||
|
|
4bd83a1aa8 | ||
|
|
69ff5b8bb2 | ||
|
|
92c2cf2127 | ||
|
|
baa8e96fcc | ||
|
|
37fe1d5d24 | ||
|
|
24920c8a56 | ||
|
|
ba84082a1e | ||
|
|
e3e8a046e4 | ||
|
|
8806739d3d | ||
|
|
bf908556bf | ||
|
|
4be03e1514 | ||
|
|
a8a17e7978 | ||
|
|
c982b86136 | ||
|
|
10a5776c79 | ||
|
|
399efa46e4 | ||
|
|
2d46a2b8fb | ||
|
|
0ae76bf1ca | ||
|
|
a3e14ce0f2 | ||
|
|
c75e0d1b88 | ||
|
|
eb003f5b32 | ||
|
|
5a356e8aed | ||
|
|
e29299a90d | ||
|
|
c898e71242 | ||
|
|
afcffc6903 | ||
|
|
8bf3d63bae | ||
|
|
5d80f882ae | ||
|
|
ba9b72312a | ||
|
|
813d057c6d | ||
|
|
9789ff5338 | ||
|
|
6ca3449f76 | ||
|
|
84ebdd3e56 | ||
|
|
d83bb17cdf | ||
|
|
764df923a0 | ||
|
|
3102862ee9 | ||
|
|
3d043424b4 | ||
|
|
cc2a98b0a2 | ||
|
|
28f401cce1 | ||
|
|
0de90d622a | ||
|
|
17628a060f | ||
|
|
aa750f18b0 | ||
|
|
968ab0ff6e | ||
|
|
00c35d93ee | ||
|
|
3f843f9a18 | ||
|
|
30b8affe5b | ||
|
|
4a38d0eef2 | ||
|
|
9fa5bb6225 | ||
|
|
7eed659692 | ||
|
|
6ed0a1f18e | ||
|
|
50b76f56d7 | ||
|
|
a721546d61 | ||
|
|
b0b500a4f6 | ||
|
|
404e12c05d | ||
|
|
f48ed0cd7d | ||
|
|
ea3366c913 | ||
|
|
52bee00432 | ||
|
|
eb39335d59 | ||
|
|
690c82e61b | ||
|
|
3e8667d48a | ||
|
|
674f953e55 | ||
|
|
bc54865f5c | ||
|
|
2c91470411 | ||
|
|
65c6e123cf | ||
|
|
4a32ad5fe5 | ||
|
|
de1940ae00 | ||
|
|
35eeb39bdc | ||
|
|
fd490d69f5 | ||
|
|
2d88aab3a3 | ||
|
|
a69a21f05d | ||
|
|
8a13b9d4e7 | ||
|
|
cbf345cfcf | ||
|
|
b5a79e8091 | ||
|
|
c404a967d0 | ||
|
|
42ce51baad | ||
|
|
7f209fbbc9 | ||
|
|
65298d7357 | ||
|
|
7dc03eefce | ||
|
|
e0c92aa49d | ||
|
|
588cc3aa14 | ||
|
|
ad86195436 | ||
|
|
8d4ed6b584 | ||
|
|
df429a57f2 | ||
|
|
c43f7d84e8 | ||
|
|
7bb2b90a13 | ||
|
|
a739f14a86 | ||
|
|
02e8e633a5 | ||
|
|
160c960345 | ||
|
|
86034e0aac | ||
|
|
c18b43c294 | ||
|
|
5210a700bf | ||
|
|
fbf768e90f | ||
|
|
4238262ad3 | ||
|
|
3492b6026e | ||
|
|
5eef2e9ba3 | ||
|
|
5d01b3a60d | ||
|
|
2ede953580 | ||
|
|
1cfc846ffc | ||
|
|
8fb5831e00 | ||
|
|
b6c921b291 | ||
|
|
b4229c5a8f | ||
|
|
f0520d652a | ||
|
|
959a60b73a | ||
|
|
1c4c24724f | ||
|
|
29881676e8 | ||
|
|
4527f29e7e | ||
|
|
a2d5c9c83e | ||
|
|
792ac2b946 | ||
|
|
944a778c0a | ||
|
|
2b9cb84383 | ||
|
|
045d144268 | ||
|
|
b794e3b514 | ||
|
|
3a39d37995 | ||
|
|
8d8b3fc821 | ||
|
|
b18df4caad | ||
|
|
cf20efed66 | ||
|
|
6adbe457c4 | ||
|
|
0c11a64d25 | ||
|
|
c79489d0b8 | ||
|
|
67253315f0 | ||
|
|
fa8ab0840b | ||
|
|
bf5550762e | ||
|
|
39e023e8f3 | ||
|
|
fd0d748c33 | ||
|
|
77956ab811 | ||
|
|
3ea233abcb | ||
|
|
93bc3dfbe5 | ||
|
|
4ca62f6fb5 | ||
|
|
6914b92e67 | ||
|
|
613d496491 | ||
|
|
570c077190 | ||
|
|
b401a4e49f | ||
|
|
559bdf2a72 | ||
|
|
f3a7fe58c4 | ||
|
|
46ab392e97 | ||
|
|
352e136621 | ||
|
|
1b265f43d5 | ||
|
|
ecfead7193 | ||
|
|
53f5968f8f | ||
|
|
8dadb36b97 | ||
|
|
74668938cc | ||
|
|
9def813324 | ||
|
|
6811958b52 | ||
|
|
d1cdb4fd13 | ||
|
|
50580623db | ||
|
|
7340762622 | ||
|
|
2102a5ec76 | ||
|
|
850ed8abf6 | ||
|
|
05ba76ecaa | ||
|
|
6db1c3f5da | ||
|
|
63429fff1d | ||
|
|
de3f7fea69 | ||
|
|
1e5ef8dbd1 | ||
|
|
385d21056a | ||
|
|
3c5fa4bbe2 | ||
|
|
58adb757c4 | ||
|
|
d01cfe9066 | ||
|
|
d522af7bb7 | ||
|
|
e6dd39b4c7 | ||
|
|
4404454cb9 | ||
|
|
59613d500f | ||
|
|
afeae15443 | ||
|
|
ba0155ad3b | ||
|
|
2c048cdeda | ||
|
|
f307c7f2af | ||
|
|
a90d84f796 | ||
|
|
dad9e4e8f2 | ||
|
|
eb57c1fe0f | ||
|
|
e7213d9d1c | ||
|
|
7b656dbaeb | ||
|
|
01af181442 | ||
|
|
192a36cf8a | ||
|
|
7f7dde01de | ||
|
|
32ffe30145 | ||
|
|
521eda1c00 | ||
|
|
49948621d0 | ||
|
|
28b77781d1 | ||
|
|
adfbe4ed64 | ||
|
|
92fbe0ebdf | ||
|
|
b0bd29696e | ||
|
|
496b933c65 | ||
|
|
da7ee45366 | ||
|
|
ffdc4bef36 | ||
|
|
3aaa96a673 | ||
|
|
2f1eb38551 | ||
|
|
cdda5be827 | ||
|
|
52682b98f5 | ||
|
|
749fa16fca | ||
|
|
b0372c41c2 | ||
|
|
e96e8943c9 | ||
|
|
acfaa2c3c0 | ||
|
|
7fb0be3487 | ||
|
|
fd91537982 | ||
|
|
a64d4cee56 | ||
|
|
ba3e24548a | ||
|
|
4beb08f1cf | ||
|
|
e2cbbd6963 | ||
|
|
c46764e80c | ||
|
|
b81053aaec | ||
|
|
9e659b790b | ||
|
|
c07220253e | ||
|
|
39fb0e91e0 | ||
|
|
6243021ade | ||
| 4a6b54b4c3 | |||
| 6c816e9fad | |||
| 2b5c7ca10b | |||
| 45b145667a | |||
| 9fb8dd4839 | |||
|
|
6352e0d976 | ||
|
|
d4ff5d482e | ||
|
|
b303add71c | ||
|
|
a42e61de61 | ||
|
|
6eb0158c6c | ||
|
|
0171ffad38 | ||
|
|
84934a6d1c | ||
|
|
98a2ade86d | ||
|
|
738a5184cb | ||
|
|
488c2694e3 | ||
|
|
015d99dc5f | ||
|
|
b80745dc2d | ||
|
|
0fa1b38f95 | ||
|
|
49e714c88c | ||
|
|
ff0b9762b1 | ||
|
|
ce36ff099b | ||
|
|
6c4a7dea29 | ||
|
|
04a80c1168 | ||
|
|
8179bd85db | ||
|
|
c08499b52d | ||
|
|
eca9e494ad | ||
|
|
ab0e68f9f3 | ||
|
|
0566a47e35 | ||
|
|
133597bfd0 | ||
|
|
ccf318f977 | ||
|
|
8affc052bf | ||
|
|
0cf5043977 | ||
|
|
f2ffc6c1ef | ||
|
|
e7c770b10b | ||
|
|
0ac3c97f90 | ||
|
|
3e5e37d65a | ||
|
|
2acbcbff51 | ||
|
|
70b382bc80 | ||
|
|
d0191361d4 | ||
|
|
59bb0bef78 | ||
|
|
4b456cf54a | ||
|
|
91c6023d25 | ||
|
|
85d15cd3e1 | ||
|
|
c0a4cbf03e | ||
|
|
fad895efbb | ||
|
|
47b31ebcf4 | ||
|
|
88d2225774 | ||
|
|
a1f6758b95 | ||
|
|
23146aaa8a | ||
|
|
cc757ba082 | ||
|
|
c3c8b60671 | ||
|
|
15792b1cf3 | ||
|
|
e75a5d5675 | ||
|
|
4282810602 | ||
|
|
8a58132dd4 | ||
|
|
be0d3e4300 | ||
|
|
ba6848a67a | ||
|
|
23beb08e5e | ||
| 5d560d962d | |||
| 51ade59a46 | |||
| 7f91be27f9 | |||
| 63cd159151 | |||
| 443c70d01b | |||
|
|
9f0ea1683a | ||
|
|
55df293e00 | ||
| 3168ffe027 | |||
| abdefbbd05 | |||
|
|
ead503d71e | ||
|
|
f54bdf8483 | ||
|
|
80cb4c257f | ||
|
|
228e8a9772 | ||
| 15c798b915 | |||
| 2ded2eb23d | |||
|
|
e6bb015ef2 | ||
|
|
ead7c276b4 | ||
| bfad9c19c5 | |||
| 439a44bc85 | |||
|
|
13f179d842 | ||
| c0e5df30d5 | |||
|
|
79fbf2644b | ||
| 0eca6adbbb | |||
| 5801633b30 | |||
| fac139fd0e | |||
|
|
2df830f01b | ||
|
|
26fab34de5 | ||
|
|
e29d0fe349 | ||
|
|
77f7620eca | ||
| fb0dd60954 | |||
|
|
4401c26496 | ||
| 9682a17a82 | |||
| 55d87c0c14 | |||
| 379f20efc5 | |||
| 7883593166 | |||
|
|
5509dd86d5 | ||
| 06b27c9b9a | |||
|
|
a927affb1f | ||
|
|
fab182e91e | ||
| d5be9e1ae9 | |||
| fb48d473d2 | |||
| 5e5cffbdc7 | |||
| e1d804dbb0 | |||
|
|
2086427b72 | ||
| e811c0cabf | |||
|
|
b68c002e2d | ||
| cb7e0238dc | |||
|
|
043a2e75c8 | ||
| 6ac375f82e | |||
|
|
8c1a26ead6 | ||
|
|
d119f838e9 | ||
|
|
ae2356de6a | ||
|
|
c1ac36df17 | ||
|
|
cc79f3ebcd | ||
|
|
1f991fc43d | ||
|
|
b62980b76d | ||
|
|
26da4945ea | ||
|
|
d599a162a9 | ||
|
|
e53adc17b3 | ||
|
|
7cd40d457d | ||
|
|
d559d03bea | ||
|
|
691dc3c71b | ||
|
|
e81ecdd716 | ||
|
|
74e385ad8b | ||
|
|
fecd095717 | ||
|
|
caa02806c0 | ||
|
|
c6c6f90d26 | ||
|
|
e4efb89466 | ||
|
|
8584885ddd | ||
|
|
6aeacaf872 | ||
|
|
0146b92cc1 | ||
|
|
981fca6cb4 | ||
|
|
6dab28081d | ||
|
|
6ebc475da2 | ||
|
|
fff26ebacb | ||
|
|
e3bebaa10b | ||
|
|
df16f03e46 | ||
|
|
b5243e8566 | ||
|
|
4501bbf8f0 | ||
|
|
5331d7149a | ||
|
|
c4b0389892 | ||
|
|
387e104359 | ||
|
|
5ebc320843 | ||
|
|
006f79658f | ||
|
|
9451bb9c61 | ||
|
|
655c26c589 | ||
|
|
607d8c21fa | ||
|
|
b7f6cbd87c | ||
|
|
a07b49a05f | ||
|
|
1d4227beec | ||
|
|
57306201cf | ||
|
|
7437ec5929 | ||
|
|
710ec96990 | ||
|
|
cb1c41c6ea | ||
|
|
e8823197f8 | ||
|
|
c5b1302ff6 | ||
|
|
f02db9801c | ||
|
|
7d113291c9 | ||
|
|
47d5416dde | ||
|
|
f2c4204bab | ||
|
|
71cfdce862 | ||
|
|
d4112e5a74 | ||
|
|
6d2c72ff98 | ||
|
|
c8f7cd6ec2 | ||
|
|
bd85143aa0 | ||
|
|
cb992d1c53 | ||
|
|
7be6cfb9cb | ||
|
|
b848e6b6d8 | ||
|
|
849bba8f5d | ||
|
|
86c492d8c1 | ||
|
|
1ed8b7233d | ||
|
|
ddabda06bf | ||
|
|
881c724725 | ||
|
|
2db4952c39 | ||
|
|
57432e01a3 | ||
|
|
97bc0cea8c | ||
|
|
e930aac039 | ||
|
|
13ec9b2d7d | ||
| d8f07c2b70 | |||
| 20a255252c | |||
| 376e68ec31 | |||
|
|
7497f8d4e0 | ||
| b3270e7231 | |||
| 1dce63fb9b | |||
| 96f3844677 | |||
| 65edbd9ed9 | |||
| 29138b8a51 | |||
|
|
aede5aa899 | ||
| 12293c9d11 | |||
| 2d0360be3b | |||
| f9d7694f25 | |||
| 9e3cc0f760 | |||
| 32410555cd | |||
| 347e7ccc84 | |||
| e47a877169 | |||
| 592d037522 | |||
| 3ccc2a1100 | |||
| 9a20f4f854 | |||
| 9a8c454123 | |||
|
|
e1f430455d | ||
| 01fe20fe68 | |||
| 2221a2d279 | |||
|
|
20305a7181 | ||
| 10c813d583 | |||
| 1b041aa813 | |||
| 8f2b247b5f | |||
| 1f3ce453fb | |||
| ff11f7ee65 | |||
| 11d9c5eae3 | |||
| 95dd0bbd56 | |||
| 72e7a39373 | |||
| 09d438e8b4 | |||
| 6752e4c0e5 | |||
| e7f3edb4bf | |||
| c55d5ac3b5 | |||
| fb43b02b2a | |||
| 55fa72d446 | |||
| 496f7a12dd | |||
| 6b75ae7dcc | |||
| 50a9bda808 | |||
| c573012a7c | |||
| 8ac428f816 | |||
| 99e7dababd | |||
| 8db72c9475 | |||
| 2db8e1423d | |||
|
|
3e440ba7cd | ||
| e437f55d87 | |||
| 3bbd0a6f90 | |||
| cf988e361b | |||
|
|
7f676fdc70 | ||
|
|
f2830ce940 | ||
| a05a6a0e88 | |||
| 30acfe39c4 | |||
|
|
ac62a43815 | ||
| 4bcb1cc940 | |||
| d0abf9a70d | |||
|
|
69ab8805a9 | ||
| 18666d5aec | |||
| d847a731fb | |||
| 9f9b00a6fb | |||
| 28756ceda8 | |||
| 56cca6df83 | |||
| aa935984a8 | |||
| a2172f56ec | |||
| db701b89c2 | |||
| ef352cbdc1 | |||
| f6b97ac82e | |||
| 0a28cf07c2 | |||
| 3dd0bc875d | |||
| cf30f63fb4 | |||
| 2ae886ec74 | |||
| 4d10919ead | |||
| c06ba41d0d | |||
|
|
1ed1d6cf80 | ||
| f26d7afbbc | |||
| e5ffa94c1d | |||
|
|
c2048fa594 | ||
| 08cec8be77 | |||
| a6ff6122b0 | |||
| 0ffe1e1905 | |||
| 4e9b232a4f | |||
| b25422f1b4 | |||
| 50c9852cff | |||
| 3d2f5c0778 | |||
|
|
206daf156a | ||
| f3e77ea994 | |||
| fbb4736d4a | |||
| f02a782991 | |||
| 6f96f7b78f | |||
| 4fb0b371ff | |||
|
|
4c671a5396 | ||
|
|
3c675fd887 | ||
| 2243072be2 | |||
| 19d6ffcf2a | |||
| 53a20a8560 | |||
| f1bb65cb73 | |||
| 0576de7a61 | |||
| c409c7ca80 | |||
| f2aab54884 | |||
| e6785f7db1 | |||
| d514fb35e5 | |||
| 41a5add906 | |||
| 00fe5e8a0f | |||
| 3a148c63e4 | |||
| f17fa41207 | |||
| d642deb4f4 | |||
| 51e35b8643 | |||
| e53933ece7 | |||
| 4efd28c956 | |||
| a1ab78b0c9 | |||
| 287c339aa0 | |||
| dc1f1cbb7c | |||
| 4a10163b10 | |||
| f45217f98e | |||
| 66da1b3aab | |||
| 8d30fddd7d | |||
| a0f1149bbb | |||
| d2672300a3 | |||
| ed5a59f21d | |||
| 66bd705971 | |||
| 4b78e67036 | |||
| 3a4bdbd42f | |||
| e222344cd9 | |||
| a1257b65ff | |||
| 299a68ad95 | |||
| 049a0deb04 | |||
| 7d3b12c774 | |||
| ac71b4621c | |||
| 3271369e2d | |||
| 931ee5944d | |||
| 08077f46c6 | |||
| b9b9308500 | |||
| 3096e0d7de | |||
| 9f5c9bfb86 | |||
| 6b0d6b017c | |||
| de3272e160 | |||
| 8a413c0024 | |||
| aa24e08744 | |||
| cb27592272 | |||
| f67ca30f94 | |||
| b6b1e533ed | |||
| 58ccbfb130 | |||
| 5bf01bb8e6 | |||
| a20fd995a1 | |||
| e2fbff3315 | |||
| c3ef14c269 | |||
| 82cab1ce2a | |||
| c325744540 | |||
| 241a405c05 | |||
| 6a44a56c38 | |||
| 091e743d0e | |||
| 4864939eef | |||
| 01ecb75c5b | |||
| fa30ea0ac2 | |||
| 2509d8876a | |||
| 8331411b93 | |||
| 369e738cf3 | |||
| 4da6b0da3a | |||
| 6276bd037c | |||
| 4f6ca60521 | |||
| 3774b600ee | |||
| 246424762e | |||
| 63e89b51f0 | |||
| 3cb1582adc | |||
| 3ea296b552 | |||
| 5e39164fcd | |||
| 131c34012b | |||
| 8cd170736a | |||
| 52d4709dd9 | |||
| 84d6edf684 | |||
| 5172129ae9 | |||
| fbdda16f55 | |||
| 15dfbb728c | |||
| 370ece5b60 | |||
| 9f088577b1 | |||
| b723382ff4 | |||
| 9485541d2c | |||
| 434b586970 | |||
| 34e0183b48 | |||
| 3bc1a7eb40 | |||
| 9ca75d3fb3 | |||
| fb510e89ee | |||
| 301d084695 | |||
| 628e204fc5 | |||
| 914a48e4f5 | |||
| bebe91d39b | |||
| d65b8e7a32 | |||
| 62aae6ffb2 | |||
| 32b6e55467 | |||
| 99eda351df |
346
Jenkinsfile
vendored
346
Jenkinsfile
vendored
@ -8,12 +8,51 @@ apiVersion: v1
|
||||
kind: Pod
|
||||
spec:
|
||||
nodeSelector:
|
||||
hardware: rpi5
|
||||
kubernetes.io/arch: arm64
|
||||
node-role.kubernetes.io/worker: "true"
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-04
|
||||
- titan-06
|
||||
- titan-11
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-13
|
||||
- titan-15
|
||||
- titan-17
|
||||
- titan-19
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
jenkins/jenkins-jenkins-agent: "true"
|
||||
containers:
|
||||
- name: jnlp
|
||||
image: jenkins/inbound-agent:3355.v388858a_47b_33-2-jdk21
|
||||
resources:
|
||||
requests:
|
||||
cpu: "25m"
|
||||
memory: "256Mi"
|
||||
- name: python
|
||||
image: python:3.12-slim
|
||||
image: registry.bstein.dev/bstein/python:3.12-slim
|
||||
command:
|
||||
- cat
|
||||
tty: true
|
||||
- name: quality-tools
|
||||
image: registry.bstein.dev/bstein/quality-tools:sonar8.0.1-trivy0.70.0-db20260422-arm64
|
||||
command:
|
||||
- cat
|
||||
tty: true
|
||||
@ -23,8 +62,21 @@ spec:
|
||||
environment {
|
||||
PIP_DISABLE_PIP_VERSION_CHECK = '1'
|
||||
PYTHONUNBUFFERED = '1'
|
||||
SUITE_NAME = 'titan-iac'
|
||||
SUITE_NAME = 'titan_iac'
|
||||
PUSHGATEWAY_URL = 'http://platform-quality-gateway.monitoring.svc.cluster.local:9091'
|
||||
SONARQUBE_HOST_URL = 'http://sonarqube.quality.svc.cluster.local:9000'
|
||||
SONARQUBE_PROJECT_KEY = 'titan_iac'
|
||||
SONARQUBE_TOKEN = credentials('sonarqube-token')
|
||||
VM_URL = 'http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428'
|
||||
QUALITY_GATE_SONARQUBE_ENFORCE = '0'
|
||||
QUALITY_GATE_SONARQUBE_REPORT = 'build/sonarqube-quality-gate.json'
|
||||
QUALITY_GATE_IRONBANK_ENFORCE = '1'
|
||||
QUALITY_GATE_IRONBANK_REQUIRED = '0'
|
||||
QUALITY_GATE_IRONBANK_REPORT = 'build/ironbank-compliance.json'
|
||||
}
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
buildDiscarder(logRotator(daysToKeepStr: '30', numToKeepStr: '200', artifactDaysToKeepStr: '30', artifactNumToKeepStr: '120'))
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
@ -34,7 +86,175 @@ spec:
|
||||
}
|
||||
stage('Install deps') {
|
||||
steps {
|
||||
sh 'pip install --no-cache-dir -r ci/requirements.txt'
|
||||
sh '''
|
||||
set -eu
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends git ca-certificates
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
pip install --no-cache-dir -r ci/requirements.txt
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Prepare local quality evidence') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
mkdir -p build
|
||||
set +e
|
||||
python3 -m testing.quality_gate --profile local --build-dir build
|
||||
local_quality_rc=$?
|
||||
set -e
|
||||
printf '%s\n' "${local_quality_rc}" > build/local-quality-gate.rc
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Collect SonarQube evidence') {
|
||||
steps {
|
||||
container('quality-tools') {
|
||||
sh '''#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
mkdir -p build
|
||||
args=(
|
||||
"-Dsonar.host.url=${SONARQUBE_HOST_URL}"
|
||||
"-Dsonar.login=${SONARQUBE_TOKEN}"
|
||||
"-Dsonar.projectKey=${SONARQUBE_PROJECT_KEY}"
|
||||
"-Dsonar.projectName=${SONARQUBE_PROJECT_KEY}"
|
||||
"-Dsonar.sources=."
|
||||
"-Dsonar.exclusions=**/.git/**,**/build/**,**/dist/**,**/node_modules/**,**/.venv/**,**/__pycache__/**,**/coverage/**,**/test-results/**,**/playwright-report/**,services/monitoring/dashboards/**,services/monitoring/grafana-dashboard-*.yaml,services/game-stream/**"
|
||||
"-Dsonar.test.inclusions=**/tests/**,**/testing/**,**/*_test.go,**/*.test.ts,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx"
|
||||
)
|
||||
[ -f build/coverage-unit.xml ] && args+=("-Dsonar.python.coverage.reportPaths=build/coverage-unit.xml")
|
||||
set +e
|
||||
sonar-scanner "${args[@]}" | tee build/sonar-scanner.log
|
||||
rc=${PIPESTATUS[0]}
|
||||
set -e
|
||||
printf '%s\n' "${rc}" > build/sonarqube-analysis.rc
|
||||
'''
|
||||
}
|
||||
sh '''
|
||||
set -eu
|
||||
mkdir -p build
|
||||
python3 - <<'PY'
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
host = os.getenv('SONARQUBE_HOST_URL', '').strip().rstrip('/')
|
||||
project_key = os.getenv('SONARQUBE_PROJECT_KEY', '').strip()
|
||||
token = os.getenv('SONARQUBE_TOKEN', '').strip()
|
||||
report_path = os.getenv('QUALITY_GATE_SONARQUBE_REPORT', 'build/sonarqube-quality-gate.json')
|
||||
|
||||
payload = {
|
||||
"status": "ERROR",
|
||||
"note": "missing SONARQUBE_HOST_URL and/or SONARQUBE_PROJECT_KEY",
|
||||
}
|
||||
if host and project_key:
|
||||
task_file = Path('.scannerwork/report-task.txt')
|
||||
task_id = ''
|
||||
if task_file.exists():
|
||||
for line in task_file.read_text(encoding='utf-8').splitlines():
|
||||
key, _, value = line.partition('=')
|
||||
if key == 'ceTaskId':
|
||||
task_id = value.strip()
|
||||
break
|
||||
if task_id:
|
||||
ce_query = urllib.parse.urlencode({"id": task_id})
|
||||
deadline = time.monotonic() + 180
|
||||
while time.monotonic() < deadline:
|
||||
ce_request = urllib.request.Request(f"{host}/api/ce/task?{ce_query}", method="GET")
|
||||
if token:
|
||||
encoded = base64.b64encode(f"{token}:".encode("utf-8")).decode("utf-8")
|
||||
ce_request.add_header("Authorization", f"Basic {encoded}")
|
||||
try:
|
||||
with urllib.request.urlopen(ce_request, timeout=12) as response:
|
||||
ce_payload = json.loads(response.read().decode("utf-8"))
|
||||
except Exception:
|
||||
time.sleep(3)
|
||||
continue
|
||||
status = str(ce_payload.get("task", {}).get("status", "")).upper()
|
||||
if status in {"SUCCESS", "FAILED", "CANCELED"}:
|
||||
break
|
||||
time.sleep(3)
|
||||
|
||||
query = urllib.parse.urlencode({"projectKey": project_key})
|
||||
request = urllib.request.Request(
|
||||
f"{host}/api/qualitygates/project_status?{query}",
|
||||
method="GET",
|
||||
)
|
||||
if token:
|
||||
encoded = base64.b64encode(f"{token}:".encode("utf-8")).decode("utf-8")
|
||||
request.add_header("Authorization", f"Basic {encoded}")
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=12) as response:
|
||||
payload = json.loads(response.read().decode("utf-8"))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
payload = {"status": "ERROR", "error": str(exc)}
|
||||
|
||||
with open(report_path, "w", encoding="utf-8") as handle:
|
||||
json.dump(payload, handle, indent=2, sort_keys=True)
|
||||
handle.write("\\n")
|
||||
PY
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Collect IronBank evidence') {
|
||||
steps {
|
||||
container('quality-tools') {
|
||||
sh '''#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
mkdir -p build
|
||||
set +e
|
||||
trivy fs --cache-dir "${TRIVY_CACHE_DIR}" --skip-db-update --skip-files clusters/atlas/flux-system/gotk-components.yaml --timeout 5m --no-progress --format json --output build/trivy-fs.json --scanners vuln,secret,misconfig --severity HIGH,CRITICAL .
|
||||
trivy_rc=$?
|
||||
set -e
|
||||
if [ ! -s build/trivy-fs.json ]; then
|
||||
cat > build/ironbank-compliance.json <<EOF
|
||||
{"status":"failed","compliant":false,"scanner":"trivy","scan_type":"filesystem","error":"trivy did not produce JSON output","trivy_rc":${trivy_rc}}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
'''
|
||||
}
|
||||
sh '''
|
||||
set -eu
|
||||
mkdir -p build
|
||||
if [ -s build/trivy-fs.json ]; then
|
||||
python3 ci/scripts/supply_chain_report.py --trivy-json build/trivy-fs.json --waivers ci/titan-iac-trivy-waivers.json --output build/ironbank-compliance.json
|
||||
exit 0
|
||||
fi
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
report_path = Path(os.getenv('QUALITY_GATE_IRONBANK_REPORT', 'build/ironbank-compliance.json'))
|
||||
if report_path.exists():
|
||||
raise SystemExit(0)
|
||||
|
||||
status = os.getenv('IRONBANK_COMPLIANCE_STATUS', '').strip()
|
||||
compliant = os.getenv('IRONBANK_COMPLIANT', '').strip().lower()
|
||||
payload = {
|
||||
"status": status or "unknown",
|
||||
"compliant": compliant in {"1", "true", "yes", "on"} if compliant else None,
|
||||
}
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
if "status" not in payload:
|
||||
payload["status"] = "unknown"
|
||||
payload["note"] = (
|
||||
"Set IRONBANK_COMPLIANCE_STATUS/IRONBANK_COMPLIANT "
|
||||
"or write build/ironbank-compliance.json in image-building repos."
|
||||
)
|
||||
|
||||
report_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
report_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\\n", encoding="utf-8")
|
||||
PY
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Run quality gate') {
|
||||
@ -64,8 +284,96 @@ spec:
|
||||
stage('Enforce quality gate') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
test "$(cat build/quality-gate.rc 2>/dev/null || echo 1)" -eq 0
|
||||
set -euo pipefail
|
||||
gate_rc="$(cat build/quality-gate.rc 2>/dev/null || echo 1)"
|
||||
fail=0
|
||||
if [ "${gate_rc}" -ne 0 ]; then
|
||||
echo "quality gate failed with rc=${gate_rc}" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
enabled() {
|
||||
case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
|
||||
1|true|yes|on) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if enabled "${QUALITY_GATE_SONARQUBE_ENFORCE:-1}"; then
|
||||
sonar_status="$(python3 - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
path = Path("build/sonarqube-quality-gate.json")
|
||||
if not path.exists():
|
||||
print("missing")
|
||||
raise SystemExit(0)
|
||||
try:
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
except Exception: # noqa: BLE001
|
||||
print("error")
|
||||
raise SystemExit(0)
|
||||
status = (payload.get("status") or payload.get("projectStatus", {}).get("status") or payload.get("qualityGate", {}).get("status") or "").strip().lower()
|
||||
print(status or "missing")
|
||||
PY
|
||||
)"
|
||||
case "${sonar_status}" in
|
||||
ok|pass|passed|success) ;;
|
||||
*)
|
||||
echo "sonarqube gate failed: ${sonar_status}" >&2
|
||||
fail=1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
ironbank_required="${QUALITY_GATE_IRONBANK_REQUIRED:-0}"
|
||||
if [ "${PUBLISH_IMAGES:-false}" = "true" ]; then
|
||||
ironbank_required=1
|
||||
fi
|
||||
if enabled "${QUALITY_GATE_IRONBANK_ENFORCE:-1}"; then
|
||||
supply_status="$(python3 - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
path = Path("build/ironbank-compliance.json")
|
||||
if not path.exists():
|
||||
print("missing")
|
||||
raise SystemExit(0)
|
||||
try:
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
except Exception: # noqa: BLE001
|
||||
print("error")
|
||||
raise SystemExit(0)
|
||||
compliant = payload.get("compliant")
|
||||
if compliant is True:
|
||||
print("ok")
|
||||
elif compliant is False:
|
||||
print("failed")
|
||||
else:
|
||||
status = str(payload.get("status") or payload.get("result") or payload.get("compliance") or "").strip().lower()
|
||||
print(status or "missing")
|
||||
PY
|
||||
)"
|
||||
case "${supply_status}" in
|
||||
ok|pass|passed|success|compliant) ;;
|
||||
not_applicable|na|n/a)
|
||||
if enabled "${ironbank_required}"; then
|
||||
echo "supply chain gate required but status=${supply_status}" >&2
|
||||
fail=1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if enabled "${ironbank_required}"; then
|
||||
echo "supply chain gate failed: ${supply_status}" >&2
|
||||
fail=1
|
||||
else
|
||||
echo "supply chain gate not passing (${supply_status}) but not required for this run" >&2
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
exit "${fail}"
|
||||
'''
|
||||
}
|
||||
}
|
||||
@ -74,7 +382,7 @@ spec:
|
||||
script {
|
||||
env.FLUX_BRANCH = sh(
|
||||
returnStdout: true,
|
||||
script: "awk '/branch:/{print $2; exit}' clusters/atlas/flux-system/gotk-sync.yaml"
|
||||
script: "grep -m1 '^\\s*branch:' clusters/atlas/flux-system/gotk-sync.yaml | sed 's/^\\s*branch:\\s*//'"
|
||||
).trim()
|
||||
if (!env.FLUX_BRANCH) {
|
||||
error('Flux branch not found in gotk-sync.yaml')
|
||||
@ -93,6 +401,20 @@ spec:
|
||||
steps {
|
||||
withCredentials([usernamePassword(credentialsId: 'gitea-pat', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_TOKEN')]) {
|
||||
sh '''
|
||||
set -euo pipefail
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
apk add --no-cache git >/dev/null
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update >/dev/null
|
||||
apt-get install -y git >/dev/null
|
||||
fi
|
||||
fi
|
||||
cd "${WORKSPACE:-$PWD}"
|
||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
echo "workspace is not a git checkout; skipping promote"
|
||||
exit 0
|
||||
fi
|
||||
set +x
|
||||
git config user.email "jenkins@bstein.dev"
|
||||
git config user.name "jenkins"
|
||||
@ -106,6 +428,7 @@ spec:
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
try {
|
||||
if (fileExists('build/junit-unit.xml') || fileExists('build/junit-glue.xml')) {
|
||||
try {
|
||||
junit allowEmptyResults: true, testResults: 'build/junit-*.xml'
|
||||
@ -113,8 +436,15 @@ spec:
|
||||
echo "junit step unavailable: ${err.class.simpleName}"
|
||||
}
|
||||
}
|
||||
}
|
||||
archiveArtifacts artifacts: 'build/**', allowEmptyArchive: true, fingerprint: true
|
||||
} catch (Throwable err) {
|
||||
if (err.class.simpleName == 'MissingContextVariableException') {
|
||||
echo 'workspace unavailable; skipping post-build artifact collection'
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,12 +7,51 @@ apiVersion: v1
|
||||
kind: Pod
|
||||
spec:
|
||||
nodeSelector:
|
||||
hardware: rpi5
|
||||
kubernetes.io/arch: arm64
|
||||
node-role.kubernetes.io/worker: "true"
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-04
|
||||
- titan-06
|
||||
- titan-11
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-13
|
||||
- titan-15
|
||||
- titan-17
|
||||
- titan-19
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: ScheduleAnyway
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
jenkins/jenkins-jenkins-agent: "true"
|
||||
containers:
|
||||
- name: jnlp
|
||||
image: jenkins/inbound-agent:3355.v388858a_47b_33-2-jdk21
|
||||
resources:
|
||||
requests:
|
||||
cpu: "25m"
|
||||
memory: "256Mi"
|
||||
- name: python
|
||||
image: python:3.12-slim
|
||||
image: registry.bstein.dev/bstein/python:3.12-slim
|
||||
command:
|
||||
- cat
|
||||
tty: true
|
||||
- name: quality-tools
|
||||
image: registry.bstein.dev/bstein/quality-tools:sonar8.0.1-trivy0.70.0-db20260422-arm64
|
||||
command:
|
||||
- cat
|
||||
tty: true
|
||||
@ -22,8 +61,21 @@ spec:
|
||||
environment {
|
||||
PIP_DISABLE_PIP_VERSION_CHECK = '1'
|
||||
PYTHONUNBUFFERED = '1'
|
||||
SUITE_NAME = 'titan-iac'
|
||||
SUITE_NAME = 'titan_iac'
|
||||
PUSHGATEWAY_URL = 'http://platform-quality-gateway.monitoring.svc.cluster.local:9091'
|
||||
SONARQUBE_HOST_URL = 'http://sonarqube.quality.svc.cluster.local:9000'
|
||||
SONARQUBE_PROJECT_KEY = 'titan_iac'
|
||||
SONARQUBE_TOKEN = credentials('sonarqube-token')
|
||||
VM_URL = 'http://victoria-metrics-single-server.monitoring.svc.cluster.local:8428'
|
||||
QUALITY_GATE_SONARQUBE_ENFORCE = '0'
|
||||
QUALITY_GATE_SONARQUBE_REPORT = 'build/sonarqube-quality-gate.json'
|
||||
QUALITY_GATE_IRONBANK_ENFORCE = '1'
|
||||
QUALITY_GATE_IRONBANK_REQUIRED = '0'
|
||||
QUALITY_GATE_IRONBANK_REPORT = 'build/ironbank-compliance.json'
|
||||
}
|
||||
options {
|
||||
disableConcurrentBuilds()
|
||||
buildDiscarder(logRotator(daysToKeepStr: '30', numToKeepStr: '200', artifactDaysToKeepStr: '30', artifactNumToKeepStr: '120'))
|
||||
}
|
||||
stages {
|
||||
stage('Checkout') {
|
||||
@ -33,7 +85,175 @@ spec:
|
||||
}
|
||||
stage('Install deps') {
|
||||
steps {
|
||||
sh 'pip install --no-cache-dir -r ci/requirements.txt'
|
||||
sh '''
|
||||
set -eu
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends git ca-certificates
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
pip install --no-cache-dir -r ci/requirements.txt
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Prepare local quality evidence') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
mkdir -p build
|
||||
set +e
|
||||
python3 -m testing.quality_gate --profile local --build-dir build
|
||||
local_quality_rc=$?
|
||||
set -e
|
||||
printf '%s\n' "${local_quality_rc}" > build/local-quality-gate.rc
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Collect SonarQube evidence') {
|
||||
steps {
|
||||
container('quality-tools') {
|
||||
sh '''#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
mkdir -p build
|
||||
args=(
|
||||
"-Dsonar.host.url=${SONARQUBE_HOST_URL}"
|
||||
"-Dsonar.login=${SONARQUBE_TOKEN}"
|
||||
"-Dsonar.projectKey=${SONARQUBE_PROJECT_KEY}"
|
||||
"-Dsonar.projectName=${SONARQUBE_PROJECT_KEY}"
|
||||
"-Dsonar.sources=."
|
||||
"-Dsonar.exclusions=**/.git/**,**/build/**,**/dist/**,**/node_modules/**,**/.venv/**,**/__pycache__/**,**/coverage/**,**/test-results/**,**/playwright-report/**,services/monitoring/dashboards/**,services/monitoring/grafana-dashboard-*.yaml,services/game-stream/**"
|
||||
"-Dsonar.test.inclusions=**/tests/**,**/testing/**,**/*_test.go,**/*.test.ts,**/*.test.tsx,**/*.spec.ts,**/*.spec.tsx"
|
||||
)
|
||||
[ -f build/coverage-unit.xml ] && args+=("-Dsonar.python.coverage.reportPaths=build/coverage-unit.xml")
|
||||
set +e
|
||||
sonar-scanner "${args[@]}" | tee build/sonar-scanner.log
|
||||
rc=${PIPESTATUS[0]}
|
||||
set -e
|
||||
printf '%s\n' "${rc}" > build/sonarqube-analysis.rc
|
||||
'''
|
||||
}
|
||||
sh '''
|
||||
set -eu
|
||||
mkdir -p build
|
||||
python3 - <<'PY'
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
host = os.getenv('SONARQUBE_HOST_URL', '').strip().rstrip('/')
|
||||
project_key = os.getenv('SONARQUBE_PROJECT_KEY', '').strip()
|
||||
token = os.getenv('SONARQUBE_TOKEN', '').strip()
|
||||
report_path = os.getenv('QUALITY_GATE_SONARQUBE_REPORT', 'build/sonarqube-quality-gate.json')
|
||||
|
||||
payload = {
|
||||
"status": "ERROR",
|
||||
"note": "missing SONARQUBE_HOST_URL and/or SONARQUBE_PROJECT_KEY",
|
||||
}
|
||||
if host and project_key:
|
||||
task_file = Path('.scannerwork/report-task.txt')
|
||||
task_id = ''
|
||||
if task_file.exists():
|
||||
for line in task_file.read_text(encoding='utf-8').splitlines():
|
||||
key, _, value = line.partition('=')
|
||||
if key == 'ceTaskId':
|
||||
task_id = value.strip()
|
||||
break
|
||||
if task_id:
|
||||
ce_query = urllib.parse.urlencode({"id": task_id})
|
||||
deadline = time.monotonic() + 180
|
||||
while time.monotonic() < deadline:
|
||||
ce_request = urllib.request.Request(f"{host}/api/ce/task?{ce_query}", method="GET")
|
||||
if token:
|
||||
encoded = base64.b64encode(f"{token}:".encode("utf-8")).decode("utf-8")
|
||||
ce_request.add_header("Authorization", f"Basic {encoded}")
|
||||
try:
|
||||
with urllib.request.urlopen(ce_request, timeout=12) as response:
|
||||
ce_payload = json.loads(response.read().decode("utf-8"))
|
||||
except Exception:
|
||||
time.sleep(3)
|
||||
continue
|
||||
status = str(ce_payload.get("task", {}).get("status", "")).upper()
|
||||
if status in {"SUCCESS", "FAILED", "CANCELED"}:
|
||||
break
|
||||
time.sleep(3)
|
||||
|
||||
query = urllib.parse.urlencode({"projectKey": project_key})
|
||||
request = urllib.request.Request(
|
||||
f"{host}/api/qualitygates/project_status?{query}",
|
||||
method="GET",
|
||||
)
|
||||
if token:
|
||||
encoded = base64.b64encode(f"{token}:".encode("utf-8")).decode("utf-8")
|
||||
request.add_header("Authorization", f"Basic {encoded}")
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=12) as response:
|
||||
payload = json.loads(response.read().decode("utf-8"))
|
||||
except Exception as exc: # noqa: BLE001
|
||||
payload = {"status": "ERROR", "error": str(exc)}
|
||||
|
||||
with open(report_path, "w", encoding="utf-8") as handle:
|
||||
json.dump(payload, handle, indent=2, sort_keys=True)
|
||||
handle.write("\\n")
|
||||
PY
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Collect IronBank evidence') {
|
||||
steps {
|
||||
container('quality-tools') {
|
||||
sh '''#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
mkdir -p build
|
||||
set +e
|
||||
trivy fs --cache-dir "${TRIVY_CACHE_DIR}" --skip-db-update --skip-files clusters/atlas/flux-system/gotk-components.yaml --timeout 5m --no-progress --format json --output build/trivy-fs.json --scanners vuln,secret,misconfig --severity HIGH,CRITICAL .
|
||||
trivy_rc=$?
|
||||
set -e
|
||||
if [ ! -s build/trivy-fs.json ]; then
|
||||
cat > build/ironbank-compliance.json <<EOF
|
||||
{"status":"failed","compliant":false,"scanner":"trivy","scan_type":"filesystem","error":"trivy did not produce JSON output","trivy_rc":${trivy_rc}}
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
'''
|
||||
}
|
||||
sh '''
|
||||
set -eu
|
||||
mkdir -p build
|
||||
if [ -s build/trivy-fs.json ]; then
|
||||
python3 ci/scripts/supply_chain_report.py --trivy-json build/trivy-fs.json --waivers ci/titan-iac-trivy-waivers.json --output build/ironbank-compliance.json
|
||||
exit 0
|
||||
fi
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
report_path = Path(os.getenv('QUALITY_GATE_IRONBANK_REPORT', 'build/ironbank-compliance.json'))
|
||||
if report_path.exists():
|
||||
raise SystemExit(0)
|
||||
|
||||
status = os.getenv('IRONBANK_COMPLIANCE_STATUS', '').strip()
|
||||
compliant = os.getenv('IRONBANK_COMPLIANT', '').strip().lower()
|
||||
payload = {
|
||||
"status": status or "unknown",
|
||||
"compliant": compliant in {"1", "true", "yes", "on"} if compliant else None,
|
||||
}
|
||||
payload = {k: v for k, v in payload.items() if v is not None}
|
||||
if "status" not in payload:
|
||||
payload["status"] = "unknown"
|
||||
payload["note"] = (
|
||||
"Set IRONBANK_COMPLIANCE_STATUS/IRONBANK_COMPLIANT "
|
||||
"or write build/ironbank-compliance.json in image-building repos."
|
||||
)
|
||||
|
||||
report_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
report_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\\n", encoding="utf-8")
|
||||
PY
|
||||
'''
|
||||
}
|
||||
}
|
||||
stage('Run quality gate') {
|
||||
@ -63,8 +283,96 @@ spec:
|
||||
stage('Enforce quality gate') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eu
|
||||
test "$(cat build/quality-gate.rc 2>/dev/null || echo 1)" -eq 0
|
||||
set -euo pipefail
|
||||
gate_rc="$(cat build/quality-gate.rc 2>/dev/null || echo 1)"
|
||||
fail=0
|
||||
if [ "${gate_rc}" -ne 0 ]; then
|
||||
echo "quality gate failed with rc=${gate_rc}" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
enabled() {
|
||||
case "$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]')" in
|
||||
1|true|yes|on) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if enabled "${QUALITY_GATE_SONARQUBE_ENFORCE:-1}"; then
|
||||
sonar_status="$(python3 - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
path = Path("build/sonarqube-quality-gate.json")
|
||||
if not path.exists():
|
||||
print("missing")
|
||||
raise SystemExit(0)
|
||||
try:
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
except Exception: # noqa: BLE001
|
||||
print("error")
|
||||
raise SystemExit(0)
|
||||
status = (payload.get("status") or payload.get("projectStatus", {}).get("status") or payload.get("qualityGate", {}).get("status") or "").strip().lower()
|
||||
print(status or "missing")
|
||||
PY
|
||||
)"
|
||||
case "${sonar_status}" in
|
||||
ok|pass|passed|success) ;;
|
||||
*)
|
||||
echo "sonarqube gate failed: ${sonar_status}" >&2
|
||||
fail=1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
ironbank_required="${QUALITY_GATE_IRONBANK_REQUIRED:-0}"
|
||||
if [ "${PUBLISH_IMAGES:-false}" = "true" ]; then
|
||||
ironbank_required=1
|
||||
fi
|
||||
if enabled "${QUALITY_GATE_IRONBANK_ENFORCE:-1}"; then
|
||||
supply_status="$(python3 - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
path = Path("build/ironbank-compliance.json")
|
||||
if not path.exists():
|
||||
print("missing")
|
||||
raise SystemExit(0)
|
||||
try:
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
except Exception: # noqa: BLE001
|
||||
print("error")
|
||||
raise SystemExit(0)
|
||||
compliant = payload.get("compliant")
|
||||
if compliant is True:
|
||||
print("ok")
|
||||
elif compliant is False:
|
||||
print("failed")
|
||||
else:
|
||||
status = str(payload.get("status") or payload.get("result") or payload.get("compliance") or "").strip().lower()
|
||||
print(status or "missing")
|
||||
PY
|
||||
)"
|
||||
case "${supply_status}" in
|
||||
ok|pass|passed|success|compliant) ;;
|
||||
not_applicable|na|n/a)
|
||||
if enabled "${ironbank_required}"; then
|
||||
echo "supply chain gate required but status=${supply_status}" >&2
|
||||
fail=1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
if enabled "${ironbank_required}"; then
|
||||
echo "supply chain gate failed: ${supply_status}" >&2
|
||||
fail=1
|
||||
else
|
||||
echo "supply chain gate not passing (${supply_status}) but not required for this run" >&2
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
exit "${fail}"
|
||||
'''
|
||||
}
|
||||
}
|
||||
@ -73,7 +381,7 @@ spec:
|
||||
script {
|
||||
env.FLUX_BRANCH = sh(
|
||||
returnStdout: true,
|
||||
script: "awk '/branch:/{print $2; exit}' clusters/atlas/flux-system/gotk-sync.yaml"
|
||||
script: "grep -m1 '^\\s*branch:' clusters/atlas/flux-system/gotk-sync.yaml | sed 's/^\\s*branch:\\s*//'"
|
||||
).trim()
|
||||
if (!env.FLUX_BRANCH) {
|
||||
error('Flux branch not found in gotk-sync.yaml')
|
||||
@ -92,6 +400,20 @@ spec:
|
||||
steps {
|
||||
withCredentials([usernamePassword(credentialsId: 'gitea-pat', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_TOKEN')]) {
|
||||
sh '''
|
||||
set -euo pipefail
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
if command -v apk >/dev/null 2>&1; then
|
||||
apk add --no-cache git >/dev/null
|
||||
elif command -v apt-get >/dev/null 2>&1; then
|
||||
apt-get update >/dev/null
|
||||
apt-get install -y git >/dev/null
|
||||
fi
|
||||
fi
|
||||
cd "${WORKSPACE:-$PWD}"
|
||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
echo "workspace is not a git checkout; skipping promote"
|
||||
exit 0
|
||||
fi
|
||||
set +x
|
||||
git config user.email "jenkins@bstein.dev"
|
||||
git config user.name "jenkins"
|
||||
@ -105,6 +427,7 @@ spec:
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
try {
|
||||
if (fileExists('build/junit-unit.xml') || fileExists('build/junit-glue.xml')) {
|
||||
try {
|
||||
junit allowEmptyResults: true, testResults: 'build/junit-*.xml'
|
||||
@ -112,8 +435,15 @@ spec:
|
||||
echo "junit step unavailable: ${err.class.simpleName}"
|
||||
}
|
||||
}
|
||||
}
|
||||
archiveArtifacts artifacts: 'build/**', allowEmptyArchive: true, fingerprint: true
|
||||
} catch (Throwable err) {
|
||||
if (err.class.simpleName == 'MissingContextVariableException') {
|
||||
echo 'workspace unavailable; skipping post-build artifact collection'
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,10 +6,14 @@ from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
|
||||
from ci.scripts import publish_test_metrics_quality as _quality_helpers
|
||||
|
||||
CANONICAL_CHECKS = _quality_helpers.CANONICAL_CHECKS
|
||||
@ -183,8 +187,10 @@ def _build_payload(
|
||||
failed_count: int,
|
||||
branch: str,
|
||||
build_number: str,
|
||||
jenkins_job: str,
|
||||
summary: dict | None = None,
|
||||
workspace_line_coverage_percent: float = 0.0,
|
||||
source_files_total: int = 0,
|
||||
source_lines_over_500: int = 0,
|
||||
check_statuses: dict[str, str] | None = None,
|
||||
) -> str:
|
||||
@ -195,8 +201,15 @@ def _build_payload(
|
||||
"suite": suite,
|
||||
"branch": branch or "unknown",
|
||||
"build_number": build_number or "unknown",
|
||||
"jenkins_job": jenkins_job or suite,
|
||||
}
|
||||
)
|
||||
test_case_base_labels = {
|
||||
"suite": suite,
|
||||
"branch": branch or "unknown",
|
||||
"build_number": build_number or "unknown",
|
||||
"jenkins_job": jenkins_job or suite,
|
||||
}
|
||||
lines = [
|
||||
"# TYPE platform_quality_gate_runs_total counter",
|
||||
f'platform_quality_gate_runs_total{{suite="{suite}",status="ok"}} {ok_count}',
|
||||
@ -209,10 +222,14 @@ def _build_payload(
|
||||
"# TYPE titan_iac_quality_gate_run_status gauge",
|
||||
f'titan_iac_quality_gate_run_status{{suite="{suite}",status="ok"}} {1 if status == "ok" else 0}',
|
||||
f'titan_iac_quality_gate_run_status{{suite="{suite}",status="failed"}} {1 if status == "failed" else 0}',
|
||||
"# TYPE platform_quality_gate_build_info gauge",
|
||||
f"platform_quality_gate_build_info{build_labels} 1",
|
||||
"# TYPE titan_iac_quality_gate_build_info gauge",
|
||||
f"titan_iac_quality_gate_build_info{build_labels} 1",
|
||||
"# TYPE platform_quality_gate_workspace_line_coverage_percent gauge",
|
||||
f'platform_quality_gate_workspace_line_coverage_percent{{suite="{suite}"}} {workspace_line_coverage_percent:.3f}',
|
||||
"# TYPE platform_quality_gate_source_files_total gauge",
|
||||
f'platform_quality_gate_source_files_total{{suite="{suite}"}} {source_files_total}',
|
||||
"# TYPE platform_quality_gate_source_lines_over_500_total gauge",
|
||||
f'platform_quality_gate_source_lines_over_500_total{{suite="{suite}"}} {source_lines_over_500}',
|
||||
]
|
||||
@ -226,12 +243,18 @@ def _build_payload(
|
||||
lines.append("# TYPE platform_quality_gate_test_case_result gauge")
|
||||
if test_cases:
|
||||
for test_name, test_status in test_cases:
|
||||
labels = {
|
||||
**test_case_base_labels,
|
||||
"test": test_name,
|
||||
"status": test_status,
|
||||
}
|
||||
lines.append(
|
||||
f'platform_quality_gate_test_case_result{{suite="{suite}",test="{_escape_label(test_name)}",status="{_escape_label(test_status)}"}} 1'
|
||||
f"platform_quality_gate_test_case_result{_label_str(labels)} 1"
|
||||
)
|
||||
else:
|
||||
labels = {**test_case_base_labels, "test": "__no_test_cases__", "status": "skipped"}
|
||||
lines.append(
|
||||
f'platform_quality_gate_test_case_result{{suite="{suite}",test="__no_test_cases__",status="skipped"}} 1'
|
||||
f"platform_quality_gate_test_case_result{_label_str(labels)} 1"
|
||||
)
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
@ -244,8 +267,11 @@ def main() -> int:
|
||||
junit_glob = os.getenv("JUNIT_GLOB", os.getenv("JUNIT_PATH", "build/junit-*.xml"))
|
||||
exit_code_path = os.getenv("QUALITY_GATE_EXIT_CODE_PATH", os.getenv("GLUE_EXIT_CODE_PATH", "build/quality-gate.rc"))
|
||||
summary_path = os.getenv("QUALITY_GATE_SUMMARY_PATH", "build/quality-gate-summary.json")
|
||||
branch = os.getenv("BRANCH_NAME", os.getenv("GIT_BRANCH", ""))
|
||||
branch = os.getenv("BRANCH_NAME") or os.getenv("GIT_BRANCH") or "unknown"
|
||||
if branch.startswith("origin/"):
|
||||
branch = branch[len("origin/") :]
|
||||
build_number = os.getenv("BUILD_NUMBER", "")
|
||||
jenkins_job = os.getenv("JOB_NAME", "titan-iac")
|
||||
|
||||
tests = _collect_junit_totals(junit_glob)
|
||||
test_cases = _collect_junit_cases(junit_glob)
|
||||
@ -255,12 +281,17 @@ def main() -> int:
|
||||
workspace_line_coverage_percent = _summary_float(summary, "workspace_line_coverage_percent")
|
||||
if workspace_line_coverage_percent <= 0:
|
||||
workspace_line_coverage_percent = _infer_workspace_coverage_percent(summary, "build/coverage-unit.xml")
|
||||
source_files_total = _summary_int(summary, "source_files_total")
|
||||
source_lines_over_500 = _summary_int(summary, "source_lines_over_500")
|
||||
if source_lines_over_500 <= 0:
|
||||
source_lines_over_500 = _infer_source_lines_over_500(summary)
|
||||
sonarqube_report = _load_optional_json(os.getenv("QUALITY_GATE_SONARQUBE_REPORT", "build/sonarqube-quality-gate.json"))
|
||||
supply_chain_report = _load_optional_json(os.getenv("QUALITY_GATE_IRONBANK_REPORT", "build/ironbank-compliance.json"))
|
||||
supply_chain_required = os.getenv("QUALITY_GATE_IRONBANK_REQUIRED", "0").strip().lower() in {"1", "true", "yes", "on"}
|
||||
truthy = {"1", "true", "yes", "on"}
|
||||
supply_chain_required = (
|
||||
os.getenv("QUALITY_GATE_IRONBANK_REQUIRED", "0").strip().lower() in truthy
|
||||
or os.getenv("PUBLISH_IMAGES", "false").strip().lower() in truthy
|
||||
)
|
||||
check_statuses = _build_check_statuses(
|
||||
summary=summary,
|
||||
tests=tests,
|
||||
@ -299,8 +330,10 @@ def main() -> int:
|
||||
failed_count=failed_count,
|
||||
branch=branch,
|
||||
build_number=build_number,
|
||||
jenkins_job=jenkins_job,
|
||||
summary=summary,
|
||||
workspace_line_coverage_percent=workspace_line_coverage_percent,
|
||||
source_files_total=source_files_total,
|
||||
source_lines_over_500=source_lines_over_500,
|
||||
check_statuses=check_statuses,
|
||||
)
|
||||
@ -318,6 +351,7 @@ def main() -> int:
|
||||
"failed_count": failed_count,
|
||||
"checks_recorded": len(check_statuses),
|
||||
"workspace_line_coverage_percent": workspace_line_coverage_percent,
|
||||
"source_files_total": source_files_total,
|
||||
"source_lines_over_500": source_lines_over_500,
|
||||
}
|
||||
print(json.dumps(summary, sort_keys=True))
|
||||
|
||||
@ -121,11 +121,15 @@ def _infer_supply_chain_status(report: dict, required: bool) -> str:
|
||||
return "failed" if required else "not_applicable"
|
||||
compliant = report.get("compliant")
|
||||
if isinstance(compliant, bool):
|
||||
return "ok" if compliant else "failed"
|
||||
if compliant:
|
||||
return "ok"
|
||||
return "failed" if required else "not_applicable"
|
||||
status = report.get("status")
|
||||
if status is None:
|
||||
return "failed" if required else "not_applicable"
|
||||
normalized = _normalize_result_status(str(status), default="failed")
|
||||
if normalized == "failed" and not required:
|
||||
return "not_applicable"
|
||||
if normalized == "not_applicable" and required:
|
||||
return "failed"
|
||||
return normalized
|
||||
|
||||
173
ci/scripts/supply_chain_report.py
Normal file
173
ci/scripts/supply_chain_report.py
Normal file
@ -0,0 +1,173 @@
|
||||
"""Build a titan-iac supply-chain compliance report from Trivy evidence."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime as dt
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
FAIL_SEVERITIES = {"HIGH", "CRITICAL"}
|
||||
|
||||
|
||||
def _read_json(path: Path) -> dict[str, Any]:
|
||||
"""Read a JSON object from disk for use as pipeline evidence."""
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError(f"{path} must contain a JSON object")
|
||||
return payload
|
||||
|
||||
|
||||
def _parse_day(raw: str | None) -> dt.date | None:
|
||||
"""Parse an ISO day while letting optional waiver dates stay optional."""
|
||||
if not raw:
|
||||
return None
|
||||
return dt.date.fromisoformat(raw)
|
||||
|
||||
|
||||
def _today(override: str | None = None) -> dt.date:
|
||||
"""Return the policy day so tests can pin expiry behavior."""
|
||||
return _parse_day(override) or dt.date.today()
|
||||
|
||||
|
||||
def _load_waiver_pairs(path: Path | None, policy_day: dt.date) -> tuple[set[tuple[str, str]], int]:
|
||||
"""Return active ``(misconfiguration id, target)`` waivers and expired count."""
|
||||
if path is None or not path.exists():
|
||||
return set(), 0
|
||||
|
||||
payload = _read_json(path)
|
||||
default_expires_at = payload.get("default_expires_at")
|
||||
active: set[tuple[str, str]] = set()
|
||||
expired = 0
|
||||
|
||||
for entry in payload.get("misconfigurations", []):
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
misconfiguration_id = str(entry.get("id") or "").strip()
|
||||
if not misconfiguration_id:
|
||||
continue
|
||||
expires_at = _parse_day(str(entry.get("expires_at") or default_expires_at or ""))
|
||||
targets = entry.get("targets", [])
|
||||
if not isinstance(targets, list):
|
||||
continue
|
||||
|
||||
if expires_at and expires_at < policy_day:
|
||||
expired += len(targets)
|
||||
continue
|
||||
|
||||
# Waivers are target-specific so a new unsafe manifest fails until it is
|
||||
# either fixed or deliberately accepted with a fresh expiration.
|
||||
for target in targets:
|
||||
if isinstance(target, str) and target:
|
||||
active.add((misconfiguration_id, target))
|
||||
|
||||
return active, expired
|
||||
|
||||
|
||||
def _iter_failed_misconfigurations(payload: dict[str, Any]):
|
||||
"""Yield failed high/critical Trivy misconfiguration records."""
|
||||
for result in payload.get("Results", []):
|
||||
if not isinstance(result, dict):
|
||||
continue
|
||||
target = str(result.get("Target") or "")
|
||||
for item in result.get("Misconfigurations") or []:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
if item.get("Status") != "FAIL":
|
||||
continue
|
||||
if str(item.get("Severity") or "").upper() not in FAIL_SEVERITIES:
|
||||
continue
|
||||
yield target, item
|
||||
|
||||
|
||||
def _count_vulnerabilities(payload: dict[str, Any], severity: str) -> int:
|
||||
"""Count Trivy vulnerabilities at a specific severity."""
|
||||
count = 0
|
||||
for result in payload.get("Results", []):
|
||||
if not isinstance(result, dict):
|
||||
continue
|
||||
for item in result.get("Vulnerabilities") or []:
|
||||
if isinstance(item, dict) and str(item.get("Severity") or "").upper() == severity:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def _count_secrets(payload: dict[str, Any]) -> int:
|
||||
"""Count detected secrets in the Trivy filesystem report."""
|
||||
count = 0
|
||||
for result in payload.get("Results", []):
|
||||
if isinstance(result, dict):
|
||||
count += len(result.get("Secrets") or [])
|
||||
return count
|
||||
|
||||
|
||||
def build_report(
|
||||
trivy_payload: dict[str, Any],
|
||||
waiver_path: Path | None = None,
|
||||
today_override: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Build the compliance summary consumed by the quality gate."""
|
||||
policy_day = _today(today_override)
|
||||
active_waivers, expired_waivers = _load_waiver_pairs(waiver_path, policy_day)
|
||||
|
||||
open_misconfigs: list[dict[str, str]] = []
|
||||
waived_misconfigs = 0
|
||||
for target, item in _iter_failed_misconfigurations(trivy_payload):
|
||||
misconfiguration_id = str(item.get("ID") or "")
|
||||
if (misconfiguration_id, target) in active_waivers:
|
||||
waived_misconfigs += 1
|
||||
continue
|
||||
open_misconfigs.append(
|
||||
{
|
||||
"id": misconfiguration_id,
|
||||
"target": target,
|
||||
"severity": str(item.get("Severity") or ""),
|
||||
"title": str(item.get("Title") or ""),
|
||||
}
|
||||
)
|
||||
|
||||
critical = _count_vulnerabilities(trivy_payload, "CRITICAL")
|
||||
high = _count_vulnerabilities(trivy_payload, "HIGH")
|
||||
secrets = _count_secrets(trivy_payload)
|
||||
status = "ok" if critical == 0 and secrets == 0 and not open_misconfigs else "failed"
|
||||
|
||||
return {
|
||||
"status": status,
|
||||
"compliant": status == "ok",
|
||||
"category": "artifact_security",
|
||||
"scan_type": "filesystem",
|
||||
"scanner": "trivy",
|
||||
"critical_vulnerabilities": critical,
|
||||
"high_vulnerabilities": high,
|
||||
"high_vulnerability_policy": "observe",
|
||||
"secrets": secrets,
|
||||
"high_or_critical_misconfigurations": len(open_misconfigs),
|
||||
"waived_misconfigurations": waived_misconfigs,
|
||||
"expired_waivers": expired_waivers,
|
||||
"waiver_file": str(waiver_path) if waiver_path else "",
|
||||
"open_misconfiguration_examples": open_misconfigs[:20],
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
"""CLI entrypoint used by Jenkins after the Trivy scan completes."""
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--trivy-json", required=True)
|
||||
parser.add_argument("--waivers")
|
||||
parser.add_argument("--output", required=True)
|
||||
parser.add_argument("--today")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
trivy_payload = _read_json(Path(args.trivy_json))
|
||||
waiver_path = Path(args.waivers) if args.waivers else None
|
||||
report = build_report(trivy_payload, waiver_path=waiver_path, today_override=args.today)
|
||||
output_path = Path(args.output)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
output_path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
raise SystemExit(main())
|
||||
@ -1,52 +1,18 @@
|
||||
max_success_age_hours: 48
|
||||
allow_suspended:
|
||||
- bstein-dev-home/vaultwarden-cred-sync
|
||||
- comms/guest-name-randomizer
|
||||
- comms/othrys-room-reset
|
||||
- comms/pin-othrys-invite
|
||||
- comms/seed-othrys-room
|
||||
- finance/firefly-user-sync
|
||||
- health/wger-admin-ensure
|
||||
- health/wger-user-sync
|
||||
- mailu-mailserver/mailu-sync-nightly
|
||||
- nextcloud/nextcloud-mail-sync
|
||||
- vault/vault-oidc-config
|
||||
ariadne_schedule_tasks:
|
||||
- task: schedule.mailu_sync
|
||||
check_last_success: false
|
||||
- task: schedule.nextcloud_sync
|
||||
check_last_success: true
|
||||
max_success_age_hours: 48
|
||||
- task: schedule.nextcloud_cron
|
||||
check_last_success: true
|
||||
max_success_age_hours: 48
|
||||
- task: schedule.nextcloud_maintenance
|
||||
check_last_success: false
|
||||
- task: schedule.vaultwarden_sync
|
||||
check_last_success: true
|
||||
max_success_age_hours: 48
|
||||
- task: schedule.wger_user_sync
|
||||
check_last_success: true
|
||||
max_success_age_hours: 48
|
||||
- task: schedule.wger_admin
|
||||
check_last_success: false
|
||||
- task: schedule.firefly_user_sync
|
||||
check_last_success: true
|
||||
max_success_age_hours: 48
|
||||
- task: schedule.firefly_cron
|
||||
check_last_success: false
|
||||
- task: schedule.vault_k8s_auth
|
||||
check_last_success: false
|
||||
- task: schedule.vault_oidc
|
||||
check_last_success: false
|
||||
- task: schedule.comms_guest_name
|
||||
check_last_success: true
|
||||
max_success_age_hours: 48
|
||||
- task: schedule.comms_pin_invite
|
||||
check_last_success: false
|
||||
- task: schedule.comms_reset_room
|
||||
check_last_success: false
|
||||
- task: schedule.comms_seed_room
|
||||
check_last_success: true
|
||||
max_success_age_hours: 48
|
||||
- task: schedule.pod_cleaner
|
||||
check_last_success: true
|
||||
max_success_age_hours: 6
|
||||
- task: schedule.opensearch_prune
|
||||
check_last_success: false
|
||||
- task: schedule.image_sweeper
|
||||
check_last_success: true
|
||||
max_success_age_hours: 18
|
||||
- task: schedule.metis_k3s_token_sync
|
||||
check_last_success: true
|
||||
max_success_age_hours: 12
|
||||
- task: schedule.platform_quality_suite_probe
|
||||
check_last_success: true
|
||||
max_success_age_hours: 2
|
||||
- schedule.mailu_sync
|
||||
- schedule.nextcloud_sync
|
||||
- schedule.vaultwarden_sync
|
||||
- schedule.wger_admin
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
"""Glue checks for Ariadne schedules exported to VictoriaMetrics."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
@ -26,11 +28,29 @@ def _query(promql: str) -> list[dict]:
|
||||
|
||||
def _expected_tasks() -> list[dict]:
|
||||
cfg = _load_config()
|
||||
tasks = cfg.get("ariadne_schedule_tasks", [])
|
||||
tasks = [
|
||||
_normalize_task(item, cfg)
|
||||
for item in cfg.get("ariadne_schedule_tasks", [])
|
||||
]
|
||||
assert tasks, "No Ariadne schedule tasks configured"
|
||||
return tasks
|
||||
|
||||
|
||||
def _normalize_task(item: object, cfg: dict) -> dict:
|
||||
if isinstance(item, str):
|
||||
return {
|
||||
"task": item,
|
||||
"check_last_success": True,
|
||||
"max_success_age_hours": cfg.get("max_success_age_hours", 48),
|
||||
}
|
||||
if isinstance(item, dict):
|
||||
normalized = dict(item)
|
||||
normalized.setdefault("check_last_success", True)
|
||||
normalized.setdefault("max_success_age_hours", cfg.get("max_success_age_hours", 48))
|
||||
return normalized
|
||||
raise TypeError(f"Unsupported Ariadne schedule task config entry: {item!r}")
|
||||
|
||||
|
||||
def _tracked_tasks(tasks: list[dict]) -> list[dict]:
|
||||
tracked = [item for item in tasks if item.get("check_last_success")]
|
||||
assert tracked, "No Ariadne schedule tasks are marked for success tracking"
|
||||
|
||||
46
ci/tests/glue/test_glue_cronjobs.py
Normal file
46
ci/tests/glue/test_glue_cronjobs.py
Normal file
@ -0,0 +1,46 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from kubernetes import client, config
|
||||
|
||||
|
||||
CONFIG_PATH = Path(__file__).with_name("config.yaml")
|
||||
|
||||
|
||||
def _load_config() -> dict:
|
||||
with CONFIG_PATH.open("r", encoding="utf-8") as handle:
|
||||
return yaml.safe_load(handle) or {}
|
||||
|
||||
|
||||
def _load_kube():
|
||||
try:
|
||||
config.load_incluster_config()
|
||||
except config.ConfigException:
|
||||
config.load_kube_config()
|
||||
|
||||
|
||||
def test_glue_cronjobs_recent_success():
|
||||
cfg = _load_config()
|
||||
max_age_hours = int(cfg.get("max_success_age_hours", 48))
|
||||
allow_suspended = set(cfg.get("allow_suspended", []))
|
||||
|
||||
_load_kube()
|
||||
batch = client.BatchV1Api()
|
||||
cronjobs = batch.list_cron_job_for_all_namespaces(label_selector="atlas.bstein.dev/glue=true").items
|
||||
|
||||
assert cronjobs, "No glue cronjobs found with atlas.bstein.dev/glue=true"
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
for cronjob in cronjobs:
|
||||
name = f"{cronjob.metadata.namespace}/{cronjob.metadata.name}"
|
||||
if cronjob.spec.suspend:
|
||||
assert name in allow_suspended, f"{name} is suspended but not in allow_suspended"
|
||||
continue
|
||||
|
||||
last_success = cronjob.status.last_successful_time
|
||||
assert last_success is not None, f"{name} has no lastSuccessfulTime"
|
||||
age_hours = (now - last_success).total_seconds() / 3600
|
||||
assert age_hours <= max_age_hours, f"{name} last success {age_hours:.1f}h ago"
|
||||
@ -1,3 +1,5 @@
|
||||
"""Glue checks for the metrics the quality-gate publishes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
@ -25,11 +27,29 @@ def _query(promql: str) -> list[dict]:
|
||||
|
||||
def _expected_tasks() -> list[dict]:
|
||||
cfg = _load_config()
|
||||
tasks = cfg.get("ariadne_schedule_tasks", [])
|
||||
tasks = [
|
||||
_normalize_task(item, cfg)
|
||||
for item in cfg.get("ariadne_schedule_tasks", [])
|
||||
]
|
||||
assert tasks, "No Ariadne schedule tasks configured"
|
||||
return tasks
|
||||
|
||||
|
||||
def _normalize_task(item: object, cfg: dict) -> dict:
|
||||
if isinstance(item, str):
|
||||
return {
|
||||
"task": item,
|
||||
"check_last_success": True,
|
||||
"max_success_age_hours": cfg.get("max_success_age_hours", 48),
|
||||
}
|
||||
if isinstance(item, dict):
|
||||
normalized = dict(item)
|
||||
normalized.setdefault("check_last_success", True)
|
||||
normalized.setdefault("max_success_age_hours", cfg.get("max_success_age_hours", 48))
|
||||
return normalized
|
||||
raise TypeError(f"Unsupported Ariadne schedule task config entry: {item!r}")
|
||||
|
||||
|
||||
def _tracked_tasks(tasks: list[dict]) -> list[dict]:
|
||||
tracked = [item for item in tasks if item.get("check_last_success")]
|
||||
assert tracked, "No Ariadne schedule tasks are marked for success tracking"
|
||||
|
||||
407
ci/titan-iac-trivy-waivers.json
Normal file
407
ci/titan-iac-trivy-waivers.json
Normal file
@ -0,0 +1,407 @@
|
||||
{
|
||||
"version": 1,
|
||||
"generated_from": "Jenkins titan-iac build 225 Trivy filesystem scan",
|
||||
"default_expires_at": "2026-05-22",
|
||||
"ticket": "atlas-quality-wave-k8s-hardening",
|
||||
"default_reason": "Existing Kubernetes manifest hardening baseline accepted only for the first quality-gate rollout; fix or renew explicitly before expiry.",
|
||||
"misconfigurations": [
|
||||
{
|
||||
"id": "DS-0002",
|
||||
"targets": [
|
||||
"dockerfiles/Dockerfile.ananke-node-helper"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0009",
|
||||
"targets": [
|
||||
"services/mailu/vip-controller.yaml",
|
||||
"services/maintenance/k3s-agent-restart-daemonset.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0010",
|
||||
"targets": [
|
||||
"services/maintenance/k3s-agent-restart-daemonset.yaml",
|
||||
"services/maintenance/metis-sentinel-amd64-daemonset.yaml",
|
||||
"services/maintenance/metis-sentinel-arm64-daemonset.yaml",
|
||||
"services/monitoring/jetson-tegrastats-exporter.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0014",
|
||||
"targets": [
|
||||
"infrastructure/cert-manager/cleanup/cert-manager-cleanup-job.yaml",
|
||||
"infrastructure/core/node-prefer-noschedule-cronjob.yaml",
|
||||
"infrastructure/core/ntp-sync-daemonset.yaml",
|
||||
"infrastructure/longhorn/adopt/longhorn-helm-adopt-job.yaml",
|
||||
"infrastructure/longhorn/core/longhorn-disk-tags-ensure-job.yaml",
|
||||
"infrastructure/longhorn/core/longhorn-settings-ensure-job.yaml",
|
||||
"infrastructure/longhorn/core/vault-sync-deployment.yaml",
|
||||
"infrastructure/longhorn/ui-ingress/oauth2-proxy-longhorn.yaml",
|
||||
"infrastructure/modules/profiles/components/device-plugin-jetson/daemonset.yaml",
|
||||
"infrastructure/modules/profiles/components/device-plugin-minipc/daemonset.yaml",
|
||||
"infrastructure/modules/profiles/components/device-plugin-tethys/daemonset.yaml",
|
||||
"infrastructure/postgres/statefulset.yaml",
|
||||
"infrastructure/vault-csi/vault-csi-provider.yaml",
|
||||
"services/ai-llm/deployment.yaml",
|
||||
"services/bstein-dev-home/backend-deployment.yaml",
|
||||
"services/bstein-dev-home/chat-ai-gateway-deployment.yaml",
|
||||
"services/bstein-dev-home/frontend-deployment.yaml",
|
||||
"services/bstein-dev-home/oneoffs/migrations/portal-migrate-job.yaml",
|
||||
"services/bstein-dev-home/oneoffs/portal-onboarding-e2e-test-job.yaml",
|
||||
"services/bstein-dev-home/vault-sync-deployment.yaml",
|
||||
"services/bstein-dev-home/vaultwarden-cred-sync-cronjob.yaml",
|
||||
"services/comms/atlasbot-deployment.yaml",
|
||||
"services/comms/coturn.yaml",
|
||||
"services/comms/element-call-deployment.yaml",
|
||||
"services/comms/guest-name-job.yaml",
|
||||
"services/comms/guest-register-deployment.yaml",
|
||||
"services/comms/livekit-token-deployment.yaml",
|
||||
"services/comms/livekit.yaml",
|
||||
"services/comms/mas-deployment.yaml",
|
||||
"services/comms/oneoffs/bstein-force-leave-job.yaml",
|
||||
"services/comms/oneoffs/comms-secrets-ensure-job.yaml",
|
||||
"services/comms/oneoffs/mas-admin-client-secret-ensure-job.yaml",
|
||||
"services/comms/oneoffs/mas-db-ensure-job.yaml",
|
||||
"services/comms/oneoffs/mas-local-users-ensure-job.yaml",
|
||||
"services/comms/oneoffs/othrys-kick-numeric-job.yaml",
|
||||
"services/comms/oneoffs/synapse-admin-ensure-job.yaml",
|
||||
"services/comms/oneoffs/synapse-seeder-admin-ensure-job.yaml",
|
||||
"services/comms/oneoffs/synapse-signingkey-ensure-job.yaml",
|
||||
"services/comms/oneoffs/synapse-user-seed-job.yaml",
|
||||
"services/comms/pin-othrys-job.yaml",
|
||||
"services/comms/reset-othrys-room-job.yaml",
|
||||
"services/comms/seed-othrys-room.yaml",
|
||||
"services/comms/vault-sync-deployment.yaml",
|
||||
"services/comms/wellknown.yaml",
|
||||
"services/crypto/monerod/deployment.yaml",
|
||||
"services/crypto/wallet-monero-temp/deployment.yaml",
|
||||
"services/crypto/xmr-miner/deployment.yaml",
|
||||
"services/crypto/xmr-miner/vault-sync-deployment.yaml",
|
||||
"services/crypto/xmr-miner/xmrig-daemonset.yaml",
|
||||
"services/finance/actual-budget-deployment.yaml",
|
||||
"services/finance/firefly-cronjob.yaml",
|
||||
"services/finance/firefly-deployment.yaml",
|
||||
"services/finance/firefly-user-sync-cronjob.yaml",
|
||||
"services/finance/oneoffs/finance-secrets-ensure-job.yaml",
|
||||
"services/gitea/deployment.yaml",
|
||||
"services/harbor/vault-sync-deployment.yaml",
|
||||
"services/health/wger-admin-ensure-cronjob.yaml",
|
||||
"services/health/wger-deployment.yaml",
|
||||
"services/health/wger-user-sync-cronjob.yaml",
|
||||
"services/jellyfin/deployment.yaml",
|
||||
"services/jellyfin/loader.yaml",
|
||||
"services/jenkins/deployment.yaml",
|
||||
"services/jenkins/vault-sync-deployment.yaml",
|
||||
"services/keycloak/deployment.yaml",
|
||||
"services/keycloak/oneoffs/actual-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/harbor-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/ldap-federation-job.yaml",
|
||||
"services/keycloak/oneoffs/logs-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/mas-secrets-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/metis-node-passwords-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/metis-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/metis-ssh-keys-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-admin-client-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-client-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-execute-actions-email-test-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-target-client-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-token-exchange-permissions-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-token-exchange-test-job.yaml",
|
||||
"services/keycloak/oneoffs/quality-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/realm-settings-job.yaml",
|
||||
"services/keycloak/oneoffs/soteria-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/synapse-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/user-overrides-job.yaml",
|
||||
"services/keycloak/oneoffs/vault-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/vault-sync-deployment.yaml",
|
||||
"services/logging/node-image-gc-rpi4-daemonset.yaml",
|
||||
"services/logging/node-image-prune-rpi5-daemonset.yaml",
|
||||
"services/logging/node-log-rotation-daemonset.yaml",
|
||||
"services/logging/oauth2-proxy.yaml",
|
||||
"services/logging/oneoffs/opensearch-dashboards-setup-job.yaml",
|
||||
"services/logging/oneoffs/opensearch-ism-job.yaml",
|
||||
"services/logging/oneoffs/opensearch-observability-setup-job.yaml",
|
||||
"services/logging/opensearch-prune-cronjob.yaml",
|
||||
"services/logging/vault-sync-deployment.yaml",
|
||||
"services/mailu/mailu-sync-cronjob.yaml",
|
||||
"services/mailu/mailu-sync-listener.yaml",
|
||||
"services/mailu/oneoffs/mailu-sync-job.yaml",
|
||||
"services/mailu/vault-sync-deployment.yaml",
|
||||
"services/mailu/vip-controller.yaml",
|
||||
"services/maintenance/ariadne-deployment.yaml",
|
||||
"services/maintenance/disable-k3s-traefik-daemonset.yaml",
|
||||
"services/maintenance/image-sweeper-cronjob.yaml",
|
||||
"services/maintenance/k3s-agent-restart-daemonset.yaml",
|
||||
"services/maintenance/metis-deployment.yaml",
|
||||
"services/maintenance/metis-k3s-token-sync-cronjob.yaml",
|
||||
"services/maintenance/metis-sentinel-amd64-daemonset.yaml",
|
||||
"services/maintenance/metis-sentinel-arm64-daemonset.yaml",
|
||||
"services/maintenance/node-image-sweeper-daemonset.yaml",
|
||||
"services/maintenance/node-nofile-daemonset.yaml",
|
||||
"services/maintenance/oauth2-proxy-metis.yaml",
|
||||
"services/maintenance/oauth2-proxy-soteria.yaml",
|
||||
"services/maintenance/oneoffs/ariadne-migrate-job.yaml",
|
||||
"services/maintenance/oneoffs/k3s-traefik-cleanup-job.yaml",
|
||||
"services/maintenance/oneoffs/titan-24-rootfs-sweep-job.yaml",
|
||||
"services/maintenance/pod-cleaner-cronjob.yaml",
|
||||
"services/maintenance/soteria-deployment.yaml",
|
||||
"services/maintenance/vault-sync-deployment.yaml",
|
||||
"services/monitoring/dcgm-exporter.yaml",
|
||||
"services/monitoring/jetson-tegrastats-exporter.yaml",
|
||||
"services/monitoring/oneoffs/grafana-org-bootstrap.yaml",
|
||||
"services/monitoring/oneoffs/grafana-user-dedupe-job.yaml",
|
||||
"services/monitoring/platform-quality-gateway-deployment.yaml",
|
||||
"services/monitoring/platform-quality-suite-probe-cronjob.yaml",
|
||||
"services/monitoring/postmark-exporter-deployment.yaml",
|
||||
"services/monitoring/vmalert-atlas-availability.yaml",
|
||||
"services/monitoring/vault-sync-deployment.yaml",
|
||||
"services/nextcloud-mail-sync/cronjob.yaml",
|
||||
"services/nextcloud/collabora.yaml",
|
||||
"services/nextcloud/cronjob.yaml",
|
||||
"services/nextcloud/deployment.yaml",
|
||||
"services/nextcloud/maintenance-cronjob.yaml",
|
||||
"services/oauth2-proxy/deployment.yaml",
|
||||
"services/openldap/statefulset.yaml",
|
||||
"services/outline/deployment.yaml",
|
||||
"services/outline/redis-deployment.yaml",
|
||||
"services/pegasus/deployment.yaml",
|
||||
"services/pegasus/vault-sync-deployment.yaml",
|
||||
"services/planka/deployment.yaml",
|
||||
"services/quality/oauth2-proxy-sonarqube.yaml",
|
||||
"services/quality/sonarqube-deployment.yaml",
|
||||
"services/quality/sonarqube-exporter-deployment.yaml",
|
||||
"services/sui-metrics/base/deployment.yaml",
|
||||
"services/typhon/vault-sync-deployment.yaml",
|
||||
"services/vault/k8s-auth-config-cronjob.yaml",
|
||||
"services/vault/oidc-config-cronjob.yaml",
|
||||
"services/vault/statefulset.yaml",
|
||||
"services/vaultwarden/deployment.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0017",
|
||||
"targets": [
|
||||
"infrastructure/modules/profiles/components/device-plugin-jetson/daemonset.yaml",
|
||||
"infrastructure/modules/profiles/components/device-plugin-minipc/daemonset.yaml",
|
||||
"infrastructure/modules/profiles/components/device-plugin-tethys/daemonset.yaml",
|
||||
"services/logging/node-image-gc-rpi4-daemonset.yaml",
|
||||
"services/logging/node-image-prune-rpi5-daemonset.yaml",
|
||||
"services/logging/node-log-rotation-daemonset.yaml",
|
||||
"services/maintenance/disable-k3s-traefik-daemonset.yaml",
|
||||
"services/maintenance/image-sweeper-cronjob.yaml",
|
||||
"services/maintenance/k3s-agent-restart-daemonset.yaml",
|
||||
"services/maintenance/metis-deployment.yaml",
|
||||
"services/maintenance/metis-sentinel-amd64-daemonset.yaml",
|
||||
"services/maintenance/metis-sentinel-arm64-daemonset.yaml",
|
||||
"services/maintenance/node-image-sweeper-daemonset.yaml",
|
||||
"services/maintenance/node-nofile-daemonset.yaml",
|
||||
"services/maintenance/oneoffs/titan-24-rootfs-sweep-job.yaml",
|
||||
"services/monitoring/dcgm-exporter.yaml",
|
||||
"services/monitoring/jetson-tegrastats-exporter.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0041",
|
||||
"targets": [
|
||||
"infrastructure/cert-manager/cleanup/cert-manager-cleanup-rbac.yaml",
|
||||
"infrastructure/longhorn/adopt/longhorn-adopt-rbac.yaml",
|
||||
"infrastructure/traefik/clusterrole.yaml",
|
||||
"services/bstein-dev-home/rbac.yaml",
|
||||
"services/comms/comms-secrets-ensure-rbac.yaml",
|
||||
"services/comms/mas-db-ensure-rbac.yaml",
|
||||
"services/comms/mas-secrets-ensure-rbac.yaml",
|
||||
"services/maintenance/soteria-rbac.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0047",
|
||||
"targets": [
|
||||
"services/monitoring/rbac.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0053",
|
||||
"targets": [
|
||||
"services/comms/comms-secrets-ensure-rbac.yaml",
|
||||
"services/comms/mas-db-ensure-rbac.yaml",
|
||||
"services/jenkins/serviceaccount.yaml",
|
||||
"services/maintenance/ariadne-rbac.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0056",
|
||||
"targets": [
|
||||
"infrastructure/cert-manager/cleanup/cert-manager-cleanup-rbac.yaml",
|
||||
"infrastructure/longhorn/adopt/longhorn-adopt-rbac.yaml",
|
||||
"services/jenkins/serviceaccount.yaml",
|
||||
"services/maintenance/disable-k3s-traefik-rbac.yaml",
|
||||
"services/maintenance/k3s-traefik-cleanup-rbac.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0114",
|
||||
"targets": [
|
||||
"infrastructure/cert-manager/cleanup/cert-manager-cleanup-rbac.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0118",
|
||||
"targets": [
|
||||
"infrastructure/cert-manager/cleanup/cert-manager-cleanup-job.yaml",
|
||||
"infrastructure/core/coredns-deployment.yaml",
|
||||
"infrastructure/core/node-prefer-noschedule-cronjob.yaml",
|
||||
"infrastructure/core/ntp-sync-daemonset.yaml",
|
||||
"infrastructure/longhorn/adopt/longhorn-helm-adopt-job.yaml",
|
||||
"infrastructure/longhorn/core/longhorn-disk-tags-ensure-job.yaml",
|
||||
"infrastructure/longhorn/core/longhorn-settings-ensure-job.yaml",
|
||||
"infrastructure/longhorn/core/vault-sync-deployment.yaml",
|
||||
"infrastructure/longhorn/ui-ingress/oauth2-proxy-longhorn.yaml",
|
||||
"infrastructure/modules/profiles/components/device-plugin-jetson/daemonset.yaml",
|
||||
"infrastructure/modules/profiles/components/device-plugin-minipc/daemonset.yaml",
|
||||
"infrastructure/modules/profiles/components/device-plugin-tethys/daemonset.yaml",
|
||||
"infrastructure/postgres/statefulset.yaml",
|
||||
"infrastructure/vault-csi/vault-csi-provider.yaml",
|
||||
"services/ai-llm/deployment.yaml",
|
||||
"services/bstein-dev-home/backend-deployment.yaml",
|
||||
"services/bstein-dev-home/chat-ai-gateway-deployment.yaml",
|
||||
"services/bstein-dev-home/frontend-deployment.yaml",
|
||||
"services/bstein-dev-home/oneoffs/migrations/portal-migrate-job.yaml",
|
||||
"services/bstein-dev-home/oneoffs/portal-onboarding-e2e-test-job.yaml",
|
||||
"services/bstein-dev-home/vault-sync-deployment.yaml",
|
||||
"services/bstein-dev-home/vaultwarden-cred-sync-cronjob.yaml",
|
||||
"services/comms/atlasbot-deployment.yaml",
|
||||
"services/comms/coturn.yaml",
|
||||
"services/comms/element-call-deployment.yaml",
|
||||
"services/comms/guest-name-job.yaml",
|
||||
"services/comms/livekit-token-deployment.yaml",
|
||||
"services/comms/livekit.yaml",
|
||||
"services/comms/mas-deployment.yaml",
|
||||
"services/comms/oneoffs/bstein-force-leave-job.yaml",
|
||||
"services/comms/oneoffs/comms-secrets-ensure-job.yaml",
|
||||
"services/comms/oneoffs/mas-admin-client-secret-ensure-job.yaml",
|
||||
"services/comms/oneoffs/mas-db-ensure-job.yaml",
|
||||
"services/comms/oneoffs/mas-local-users-ensure-job.yaml",
|
||||
"services/comms/oneoffs/othrys-kick-numeric-job.yaml",
|
||||
"services/comms/oneoffs/synapse-admin-ensure-job.yaml",
|
||||
"services/comms/oneoffs/synapse-seeder-admin-ensure-job.yaml",
|
||||
"services/comms/oneoffs/synapse-signingkey-ensure-job.yaml",
|
||||
"services/comms/oneoffs/synapse-user-seed-job.yaml",
|
||||
"services/comms/pin-othrys-job.yaml",
|
||||
"services/comms/reset-othrys-room-job.yaml",
|
||||
"services/comms/seed-othrys-room.yaml",
|
||||
"services/comms/vault-sync-deployment.yaml",
|
||||
"services/comms/wellknown.yaml",
|
||||
"services/crypto/monerod/deployment.yaml",
|
||||
"services/crypto/wallet-monero-temp/deployment.yaml",
|
||||
"services/crypto/xmr-miner/deployment.yaml",
|
||||
"services/crypto/xmr-miner/vault-sync-deployment.yaml",
|
||||
"services/crypto/xmr-miner/xmrig-daemonset.yaml",
|
||||
"services/finance/firefly-cronjob.yaml",
|
||||
"services/finance/firefly-deployment.yaml",
|
||||
"services/finance/firefly-user-sync-cronjob.yaml",
|
||||
"services/finance/oneoffs/finance-secrets-ensure-job.yaml",
|
||||
"services/gitea/deployment.yaml",
|
||||
"services/harbor/vault-sync-deployment.yaml",
|
||||
"services/health/wger-admin-ensure-cronjob.yaml",
|
||||
"services/health/wger-deployment.yaml",
|
||||
"services/health/wger-user-sync-cronjob.yaml",
|
||||
"services/jellyfin/loader.yaml",
|
||||
"services/jenkins/deployment.yaml",
|
||||
"services/jenkins/vault-sync-deployment.yaml",
|
||||
"services/keycloak/oneoffs/actual-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/harbor-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/ldap-federation-job.yaml",
|
||||
"services/keycloak/oneoffs/logs-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/mas-secrets-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/metis-node-passwords-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/metis-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/metis-ssh-keys-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-admin-client-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-client-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-execute-actions-email-test-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-target-client-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-token-exchange-permissions-job.yaml",
|
||||
"services/keycloak/oneoffs/portal-e2e-token-exchange-test-job.yaml",
|
||||
"services/keycloak/oneoffs/quality-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/realm-settings-job.yaml",
|
||||
"services/keycloak/oneoffs/soteria-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/synapse-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/oneoffs/user-overrides-job.yaml",
|
||||
"services/keycloak/oneoffs/vault-oidc-secret-ensure-job.yaml",
|
||||
"services/keycloak/vault-sync-deployment.yaml",
|
||||
"services/logging/node-image-gc-rpi4-daemonset.yaml",
|
||||
"services/logging/node-image-prune-rpi5-daemonset.yaml",
|
||||
"services/logging/node-log-rotation-daemonset.yaml",
|
||||
"services/logging/oauth2-proxy.yaml",
|
||||
"services/logging/oneoffs/opensearch-dashboards-setup-job.yaml",
|
||||
"services/logging/oneoffs/opensearch-ism-job.yaml",
|
||||
"services/logging/oneoffs/opensearch-observability-setup-job.yaml",
|
||||
"services/logging/opensearch-prune-cronjob.yaml",
|
||||
"services/logging/vault-sync-deployment.yaml",
|
||||
"services/mailu/mailu-sync-cronjob.yaml",
|
||||
"services/mailu/mailu-sync-listener.yaml",
|
||||
"services/mailu/oneoffs/mailu-sync-job.yaml",
|
||||
"services/mailu/vault-sync-deployment.yaml",
|
||||
"services/mailu/vip-controller.yaml",
|
||||
"services/maintenance/ariadne-deployment.yaml",
|
||||
"services/maintenance/disable-k3s-traefik-daemonset.yaml",
|
||||
"services/maintenance/image-sweeper-cronjob.yaml",
|
||||
"services/maintenance/k3s-agent-restart-daemonset.yaml",
|
||||
"services/maintenance/metis-deployment.yaml",
|
||||
"services/maintenance/metis-k3s-token-sync-cronjob.yaml",
|
||||
"services/maintenance/metis-sentinel-amd64-daemonset.yaml",
|
||||
"services/maintenance/metis-sentinel-arm64-daemonset.yaml",
|
||||
"services/maintenance/node-image-sweeper-daemonset.yaml",
|
||||
"services/maintenance/node-nofile-daemonset.yaml",
|
||||
"services/maintenance/oauth2-proxy-metis.yaml",
|
||||
"services/maintenance/oauth2-proxy-soteria.yaml",
|
||||
"services/maintenance/oneoffs/ariadne-migrate-job.yaml",
|
||||
"services/maintenance/oneoffs/k3s-traefik-cleanup-job.yaml",
|
||||
"services/maintenance/oneoffs/titan-24-rootfs-sweep-job.yaml",
|
||||
"services/maintenance/pod-cleaner-cronjob.yaml",
|
||||
"services/maintenance/soteria-deployment.yaml",
|
||||
"services/maintenance/vault-sync-deployment.yaml",
|
||||
"services/monitoring/dcgm-exporter.yaml",
|
||||
"services/monitoring/jetson-tegrastats-exporter.yaml",
|
||||
"services/monitoring/oneoffs/grafana-org-bootstrap.yaml",
|
||||
"services/monitoring/oneoffs/grafana-user-dedupe-job.yaml",
|
||||
"services/monitoring/platform-quality-gateway-deployment.yaml",
|
||||
"services/monitoring/platform-quality-suite-probe-cronjob.yaml",
|
||||
"services/monitoring/postmark-exporter-deployment.yaml",
|
||||
"services/monitoring/vmalert-atlas-availability.yaml",
|
||||
"services/monitoring/vault-sync-deployment.yaml",
|
||||
"services/nextcloud/collabora.yaml",
|
||||
"services/oauth2-proxy/deployment.yaml",
|
||||
"services/openldap/statefulset.yaml",
|
||||
"services/outline/deployment.yaml",
|
||||
"services/outline/redis-deployment.yaml",
|
||||
"services/pegasus/vault-sync-deployment.yaml",
|
||||
"services/quality/oauth2-proxy-sonarqube.yaml",
|
||||
"services/quality/sonarqube-deployment.yaml",
|
||||
"services/quality/sonarqube-exporter-deployment.yaml",
|
||||
"services/sui-metrics/base/deployment.yaml",
|
||||
"services/sui-metrics/overlays/atlas/patch-node-selector.yaml",
|
||||
"services/typhon/deployment.yaml",
|
||||
"services/typhon/vault-sync-deployment.yaml",
|
||||
"services/vault/k8s-auth-config-cronjob.yaml",
|
||||
"services/vault/oidc-config-cronjob.yaml",
|
||||
"services/vaultwarden/deployment.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "KSV-0121",
|
||||
"targets": [
|
||||
"services/logging/node-image-gc-rpi4-daemonset.yaml",
|
||||
"services/logging/node-image-prune-rpi5-daemonset.yaml",
|
||||
"services/logging/node-log-rotation-daemonset.yaml",
|
||||
"services/maintenance/disable-k3s-traefik-daemonset.yaml",
|
||||
"services/maintenance/image-sweeper-cronjob.yaml",
|
||||
"services/maintenance/metis-deployment.yaml",
|
||||
"services/maintenance/node-image-sweeper-daemonset.yaml",
|
||||
"services/maintenance/node-nofile-daemonset.yaml",
|
||||
"services/maintenance/oneoffs/titan-24-rootfs-sweep-job.yaml"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: ai-llm
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/ai-llm
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: bstein-dev-home-migrations
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/bstein-dev-home/oneoffs/migrations
|
||||
|
||||
@ -13,14 +13,14 @@ spec:
|
||||
git:
|
||||
checkout:
|
||||
ref:
|
||||
branch: feature/ariadne
|
||||
branch: main
|
||||
commit:
|
||||
author:
|
||||
email: ops@bstein.dev
|
||||
name: flux-bot
|
||||
messageTemplate: "chore(bstein-dev-home): automated image update"
|
||||
push:
|
||||
branch: feature/ariadne
|
||||
branch: main
|
||||
update:
|
||||
strategy: Setters
|
||||
path: services/bstein-dev-home
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: bstein-dev-home
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/bstein-dev-home
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: comms
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
prune: true
|
||||
@ -13,5 +15,3 @@ spec:
|
||||
path: ./services/comms
|
||||
targetNamespace: comms
|
||||
timeout: 2m
|
||||
dependsOn:
|
||||
- name: traefik
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: crypto
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/crypto
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: finance
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/finance
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
# clusters/atlas/flux-system/applications/game-stream/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: game-stream
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/game-stream
|
||||
targetNamespace: game-stream
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
dependsOn:
|
||||
- name: cert-manager
|
||||
- name: keycloak
|
||||
- name: traefik
|
||||
- name: vault
|
||||
healthChecks:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: oauth2-proxy-wolf
|
||||
namespace: game-stream
|
||||
wait: false
|
||||
timeout: 10m
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: gitea
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/gitea
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: harbor
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/harbor
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: health
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/health
|
||||
@ -15,7 +17,6 @@ spec:
|
||||
dependsOn:
|
||||
- name: keycloak
|
||||
- name: postgres
|
||||
- name: traefik
|
||||
- name: vault
|
||||
healthChecks:
|
||||
- apiVersion: apps/v1
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: jellyfin
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/jellyfin
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: jenkins
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/jenkins
|
||||
@ -14,7 +16,6 @@ spec:
|
||||
targetNamespace: jenkins
|
||||
dependsOn:
|
||||
- name: helm
|
||||
- name: traefik
|
||||
healthChecks:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: keycloak
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
prune: true
|
||||
|
||||
@ -21,10 +21,14 @@ resources:
|
||||
- sui-metrics/kustomization.yaml
|
||||
- openldap/kustomization.yaml
|
||||
- keycloak/kustomization.yaml
|
||||
- quality/kustomization.yaml
|
||||
- oauth2-proxy/kustomization.yaml
|
||||
- mailu/kustomization.yaml
|
||||
- jenkins/kustomization.yaml
|
||||
- ai-llm/kustomization.yaml
|
||||
- openclaw/kustomization.yaml
|
||||
- game-stream/kustomization.yaml
|
||||
- veles/kustomization.yaml
|
||||
- typhon/kustomization.yaml
|
||||
- nextcloud/kustomization.yaml
|
||||
- nextcloud-mail-sync/kustomization.yaml
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: mailu
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
sourceRef:
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: monerod
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/crypto/monerod
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: nextcloud-mail-sync
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
prune: true
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: nextcloud
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/nextcloud
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: oauth2-proxy
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
prune: true
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
# clusters/atlas/flux-system/applications/openclaw/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: openclaw
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/openclaw
|
||||
targetNamespace: openclaw
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
wait: true
|
||||
timeout: 30m
|
||||
healthChecks:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: openclaw-ollama
|
||||
namespace: openclaw
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: openclaw
|
||||
namespace: openclaw
|
||||
dependsOn:
|
||||
- name: cert-manager
|
||||
- name: core
|
||||
- name: longhorn
|
||||
- name: traefik
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: openldap
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
prune: true
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: outline
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/outline
|
||||
@ -15,7 +17,6 @@ spec:
|
||||
dependsOn:
|
||||
- name: keycloak
|
||||
- name: mailu
|
||||
- name: traefik
|
||||
healthChecks:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: pegasus
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/pegasus
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: planka
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/planka
|
||||
@ -15,7 +17,6 @@ spec:
|
||||
dependsOn:
|
||||
- name: keycloak
|
||||
- name: mailu
|
||||
- name: traefik
|
||||
healthChecks:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
# clusters/atlas/flux-system/applications/quality/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: quality
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/quality
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
targetNamespace: quality
|
||||
dependsOn:
|
||||
- name: cert-manager
|
||||
- name: keycloak
|
||||
- name: vault
|
||||
- name: postgres
|
||||
healthChecks:
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: sonarqube
|
||||
namespace: quality
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: sonarqube-exporter
|
||||
namespace: quality
|
||||
- apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: oauth2-proxy-sonarqube
|
||||
namespace: quality
|
||||
wait: false
|
||||
timeout: 20m
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: sui-metrics
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/sui-metrics/overlays/atlas
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: typhon
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/typhon
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: vault
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
sourceRef:
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: vaultwarden
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
suspend: false
|
||||
@ -17,4 +19,3 @@ spec:
|
||||
wait: true
|
||||
dependsOn:
|
||||
- name: helm
|
||||
- name: traefik
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
# clusters/atlas/flux-system/applications/veles/image-automation.yaml
|
||||
# Staged for the first Veles image rollout. Add this file to the parent
|
||||
# applications kustomization after the namespace exists and the Harbor repos
|
||||
# have initial tags.
|
||||
apiVersion: image.toolkit.fluxcd.io/v1
|
||||
kind: ImageUpdateAutomation
|
||||
metadata:
|
||||
name: veles
|
||||
namespace: veles
|
||||
spec:
|
||||
interval: 1m0s
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
git:
|
||||
checkout:
|
||||
ref:
|
||||
branch: main
|
||||
commit:
|
||||
author:
|
||||
email: ops@bstein.dev
|
||||
name: flux-bot
|
||||
messageTemplate: "chore(veles): automated image update"
|
||||
push:
|
||||
branch: main
|
||||
update:
|
||||
strategy: Setters
|
||||
path: services/veles
|
||||
@ -0,0 +1,28 @@
|
||||
# clusters/atlas/flux-system/applications/veles/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: veles
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/veles
|
||||
targetNamespace: veles
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
dependsOn:
|
||||
- name: cert-manager
|
||||
- name: core
|
||||
- name: keycloak
|
||||
- name: longhorn
|
||||
- name: traefik
|
||||
- name: vault
|
||||
- name: vault-csi
|
||||
- name: vault-injector
|
||||
wait: false
|
||||
timeout: 20m
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: wallet-monero-temp
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/crypto/wallet-monero-temp
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: xmr-miner
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/crypto/xmr-miner
|
||||
|
||||
@ -5966,6 +5966,9 @@ spec:
|
||||
- args:
|
||||
- --events-addr=http://notification-controller.$(RUNTIME_NAMESPACE).svc.cluster.local./
|
||||
- --watch-all-namespaces=true
|
||||
- --concurrent=1
|
||||
- --requeue-dependency=5s
|
||||
- --interval-jitter-percentage=30
|
||||
- --log-level=info
|
||||
- --log-encoding=json
|
||||
- --enable-leader-election
|
||||
|
||||
@ -7,7 +7,7 @@ metadata:
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 1m0s
|
||||
interval: 15m0s
|
||||
ref:
|
||||
branch: main
|
||||
secretRef:
|
||||
@ -20,7 +20,7 @@ metadata:
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
spec:
|
||||
interval: 10m0s
|
||||
interval: 1h0m0s
|
||||
path: ./clusters/atlas/flux-system
|
||||
prune: true
|
||||
sourceRef:
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: cert-manager-cleanup
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
path: ./infrastructure/cert-manager/cleanup
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: cert-manager
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
path: ./infrastructure/cert-manager
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: core
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./infrastructure/core
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
# clusters/atlas/flux-system/platform/descheduler/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: descheduler
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
path: ./infrastructure/descheduler
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
targetNamespace: kube-system
|
||||
dependsOn:
|
||||
- name: helm
|
||||
- name: core
|
||||
wait: true
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: gitops-ui
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
timeout: 10m
|
||||
@ -16,5 +18,4 @@ spec:
|
||||
targetNamespace: flux-system
|
||||
dependsOn:
|
||||
- name: helm
|
||||
- name: traefik
|
||||
wait: true
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: helm
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
sourceRef:
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
resources:
|
||||
- core/kustomization.yaml
|
||||
- helm/kustomization.yaml
|
||||
- descheduler/kustomization.yaml
|
||||
- resource-guardrails/kustomization.yaml
|
||||
- cert-manager/kustomization.yaml
|
||||
- metallb/kustomization.yaml
|
||||
- traefik/kustomization.yaml
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: logging
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/logging
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: longhorn-adopt
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
path: ./infrastructure/longhorn/adopt
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: longhorn-ui
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./infrastructure/longhorn/ui-ingress
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: longhorn
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
path: ./infrastructure/longhorn/core
|
||||
|
||||
@ -13,14 +13,14 @@ spec:
|
||||
git:
|
||||
checkout:
|
||||
ref:
|
||||
branch: feature/ariadne
|
||||
branch: main
|
||||
commit:
|
||||
author:
|
||||
email: ops@bstein.dev
|
||||
name: flux-bot
|
||||
messageTemplate: "chore(maintenance): automated image update"
|
||||
push:
|
||||
branch: feature/ariadne
|
||||
branch: main
|
||||
update:
|
||||
strategy: Setters
|
||||
path: services/maintenance
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: maintenance
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/maintenance
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: metallb
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
sourceRef:
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: monitoring
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./services/monitoring
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./infrastructure/postgres
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
# clusters/atlas/flux-system/platform/resource-guardrails/kustomization.yaml
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: resource-guardrails
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./infrastructure/resource-guardrails
|
||||
prune: true
|
||||
sourceRef:
|
||||
kind: GitRepository
|
||||
name: flux-system
|
||||
namespace: flux-system
|
||||
dependsOn:
|
||||
- name: core
|
||||
wait: true
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: traefik
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 10m
|
||||
path: ./infrastructure/traefik
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: vault-csi
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
sourceRef:
|
||||
|
||||
@ -4,6 +4,8 @@ kind: Kustomization
|
||||
metadata:
|
||||
name: vault-injector
|
||||
namespace: flux-system
|
||||
annotations:
|
||||
kustomize.toolkit.fluxcd.io/ssa: IfNotPresent
|
||||
spec:
|
||||
interval: 30m
|
||||
path: ./infrastructure/vault-injector
|
||||
|
||||
@ -2,4 +2,8 @@ FROM python:3.11-slim
|
||||
|
||||
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
RUN pip install --no-cache-dir requests psycopg2-binary
|
||||
RUN pip install --no-cache-dir requests psycopg2-binary \
|
||||
&& groupadd --system guest-tools \
|
||||
&& useradd --system --uid 65532 --gid guest-tools --home-dir /nonexistent --shell /usr/sbin/nologin guest-tools
|
||||
|
||||
USER guest-tools
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
FROM --platform=$BUILDPLATFORM opensearchproject/data-prepper:2.8.0 AS source
|
||||
|
||||
FROM --platform=$TARGETPLATFORM eclipse-temurin:17-jre
|
||||
# Use the mirrored Harbor artifact so CI does not depend on Docker Hub egress.
|
||||
FROM registry.bstein.dev/streaming/data-prepper@sha256:32ac6ad42e0f12da08bebee307e290b17d127b30def9b06eeaffbcbbc5033e83
|
||||
|
||||
ENV DATA_PREPPER_PATH=/usr/share/data-prepper
|
||||
|
||||
RUN useradd -u 10001 -M -U -d / -s /usr/sbin/nologin data_prepper \
|
||||
&& mkdir -p /var/log/data-prepper
|
||||
|
||||
COPY --from=source /usr/share/data-prepper /usr/share/data-prepper
|
||||
|
||||
RUN chown -R 10001:10001 /usr/share/data-prepper /var/log/data-prepper
|
||||
USER root
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends bc \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
USER 10001
|
||||
WORKDIR /usr/share/data-prepper
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
FROM ghcr.io/element-hq/lk-jwt-service:0.3.0 AS base
|
||||
|
||||
FROM alpine:3.20
|
||||
RUN apk add --no-cache ca-certificates
|
||||
RUN apk add --no-cache ca-certificates \
|
||||
&& addgroup -S livekit-token \
|
||||
&& adduser -S -D -H -u 65532 -G livekit-token livekit-token
|
||||
COPY --from=base /lk-jwt-service /lk-jwt-service
|
||||
COPY dockerfiles/vault-entrypoint.sh /entrypoint.sh
|
||||
RUN chmod 0755 /entrypoint.sh
|
||||
|
||||
USER livekit-token
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["/lk-jwt-service"]
|
||||
|
||||
@ -29,10 +29,12 @@ FROM ${DEBIAN_IMAGE}
|
||||
RUN set -eux; \
|
||||
apt-get update; \
|
||||
apt-get install -y --no-install-recommends ca-certificates; \
|
||||
update-ca-certificates; rm -rf /var/lib/apt/lists/*
|
||||
update-ca-certificates; rm -rf /var/lib/apt/lists/*; \
|
||||
groupadd --system p2pool; \
|
||||
useradd --system --uid 65532 --gid p2pool --home-dir /nonexistent --shell /usr/sbin/nologin p2pool
|
||||
COPY --from=fetch /out/p2pool /usr/local/bin/p2pool
|
||||
|
||||
RUN /usr/local/bin/p2pool --version || true
|
||||
EXPOSE 3333
|
||||
USER p2pool
|
||||
ENTRYPOINT ["/usr/local/bin/p2pool"]
|
||||
|
||||
|
||||
@ -26,9 +26,12 @@ RUN set -eux; \
|
||||
curl -fsSL "$URL" -o /opt/monero/monero.tar.bz2; \
|
||||
tar -xjf /opt/monero/monero.tar.bz2 -C /opt/monero --strip-components=1; \
|
||||
install -m 0755 /opt/monero/monero-wallet-rpc /usr/local/bin/monero-wallet-rpc; \
|
||||
rm -f /opt/monero/monero.tar.bz2
|
||||
rm -f /opt/monero/monero.tar.bz2; \
|
||||
groupadd --system monero; \
|
||||
useradd --system --uid 1000 --gid monero --home-dir /nonexistent --shell /usr/sbin/nologin monero
|
||||
|
||||
ENV PATH="/usr/local/bin:/usr/bin:/bin"
|
||||
RUN /usr/local/bin/monero-wallet-rpc --version || true
|
||||
|
||||
EXPOSE 18083
|
||||
USER monero
|
||||
|
||||
@ -23,10 +23,14 @@ RUN set -eux; \
|
||||
mkdir -p /opt/monero; \
|
||||
tar -xjf /tmp/monero.tar.bz2 -C /opt/monero --strip-components=1; \
|
||||
rm -f /tmp/monero.tar.bz2; \
|
||||
groupadd --system monero; \
|
||||
useradd --system --uid 1000 --gid monero --home-dir /nonexistent --shell /usr/sbin/nologin monero; \
|
||||
mkdir -p /data; \
|
||||
chown monero:monero /data; \
|
||||
chmod 0770 /data
|
||||
|
||||
ENV LD_LIBRARY_PATH=/opt/monero:/opt/monero/lib \
|
||||
PATH="/opt/monero:${PATH}"
|
||||
|
||||
USER monero
|
||||
CMD ["/opt/monero/monerod", "--version"]
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
FROM quay.io/oauth2-proxy/oauth2-proxy:v7.6.0 AS base
|
||||
|
||||
FROM alpine:3.20
|
||||
RUN apk add --no-cache ca-certificates
|
||||
RUN apk add --no-cache ca-certificates \
|
||||
&& addgroup -S oauth2-proxy \
|
||||
&& adduser -S -D -H -u 65532 -G oauth2-proxy oauth2-proxy
|
||||
COPY --from=base /bin/oauth2-proxy /bin/oauth2-proxy
|
||||
COPY dockerfiles/vault-entrypoint.sh /entrypoint.sh
|
||||
RUN chmod 0755 /entrypoint.sh
|
||||
|
||||
USER oauth2-proxy
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["/bin/oauth2-proxy"]
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
FROM registry.bstein.dev/streaming/pegasus:1.2.32 AS base
|
||||
|
||||
FROM alpine:3.20
|
||||
RUN apk add --no-cache ca-certificates
|
||||
RUN apk add --no-cache ca-certificates \
|
||||
&& addgroup -S pegasus \
|
||||
&& adduser -S -D -H -u 65532 -G pegasus pegasus
|
||||
COPY --from=base /pegasus /pegasus
|
||||
COPY dockerfiles/vault-entrypoint.sh /entrypoint.sh
|
||||
RUN chmod 0755 /entrypoint.sh
|
||||
|
||||
USER pegasus
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
CMD ["/pegasus"]
|
||||
|
||||
48
dockerfiles/Dockerfile.quality-tools
Normal file
48
dockerfiles/Dockerfile.quality-tools
Normal file
@ -0,0 +1,48 @@
|
||||
# dockerfiles/Dockerfile.quality-tools
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
ARG SONAR_SCANNER_VERSION=8.0.1.6346
|
||||
ARG TRIVY_VERSION=0.70.0
|
||||
ENV TRIVY_CACHE_DIR=/opt/trivy-cache
|
||||
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
git \
|
||||
jq \
|
||||
unzip \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& groupadd --system quality-tools \
|
||||
&& useradd --system --uid 65532 --gid quality-tools --home-dir /nonexistent --shell /usr/sbin/nologin quality-tools
|
||||
|
||||
RUN set -eux; \
|
||||
scanner_zip="sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux-aarch64.zip"; \
|
||||
base_url="https://binaries.sonarsource.com/Distribution/sonar-scanner-cli"; \
|
||||
curl -fsSL "${base_url}/${scanner_zip}" -o "/tmp/${scanner_zip}"; \
|
||||
curl -fsSL "${base_url}/${scanner_zip}.sha256" -o "/tmp/${scanner_zip}.sha256"; \
|
||||
printf '%s %s\n' "$(cat "/tmp/${scanner_zip}.sha256")" "/tmp/${scanner_zip}" | sha256sum -c -; \
|
||||
unzip -q "/tmp/${scanner_zip}" -d /opt; \
|
||||
ln -s "/opt/sonar-scanner-${SONAR_SCANNER_VERSION}-linux-aarch64/bin/sonar-scanner" /usr/local/bin/sonar-scanner; \
|
||||
rm -f "/tmp/${scanner_zip}" "/tmp/${scanner_zip}.sha256"
|
||||
|
||||
RUN set -eux; \
|
||||
trivy_tgz="trivy_${TRIVY_VERSION}_Linux-ARM64.tar.gz"; \
|
||||
curl -fsSL "https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/${trivy_tgz}" -o "/tmp/${trivy_tgz}"; \
|
||||
tar -C /usr/local/bin -xzf "/tmp/${trivy_tgz}" trivy; \
|
||||
rm -f "/tmp/${trivy_tgz}"; \
|
||||
trivy --version; \
|
||||
sonar-scanner -v
|
||||
|
||||
RUN set -eux; \
|
||||
mkdir -p "${TRIVY_CACHE_DIR}"; \
|
||||
trivy image --download-db-only --cache-dir "${TRIVY_CACHE_DIR}"; \
|
||||
chmod -R a+rX "${TRIVY_CACHE_DIR}"; \
|
||||
mkdir -p /workspace; \
|
||||
chown quality-tools:quality-tools /workspace
|
||||
|
||||
WORKDIR /workspace
|
||||
USER quality-tools
|
||||
@ -27,12 +27,53 @@ spec:
|
||||
timeout: 10m
|
||||
values:
|
||||
installCRDs: true
|
||||
replicaCount: 2
|
||||
podDisruptionBudget:
|
||||
enabled: true
|
||||
minAvailable: 1
|
||||
extraArgs:
|
||||
- --acme-http01-solver-nameservers=1.1.1.1:53,8.8.8.8:53
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
nodeSelector:
|
||||
node-role.kubernetes.io/worker: "true"
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: atlas.bstein.dev/spillover
|
||||
operator: DoesNotExist
|
||||
- weight: 95
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-13
|
||||
- titan-15
|
||||
- titan-17
|
||||
- titan-19
|
||||
- weight: 90
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: hardware
|
||||
operator: In
|
||||
values:
|
||||
- rpi5
|
||||
- weight: 50
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: hardware
|
||||
operator: In
|
||||
values:
|
||||
- rpi4
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
@ -42,10 +83,63 @@ spec:
|
||||
- rpi5
|
||||
- rpi4
|
||||
webhook:
|
||||
replicaCount: 2
|
||||
podDisruptionBudget:
|
||||
enabled: true
|
||||
minAvailable: 1
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
livenessProbe:
|
||||
failureThreshold: 8
|
||||
initialDelaySeconds: 90
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
readinessProbe:
|
||||
failureThreshold: 8
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
nodeSelector:
|
||||
node-role.kubernetes.io/worker: "true"
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: atlas.bstein.dev/spillover
|
||||
operator: DoesNotExist
|
||||
- weight: 95
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-13
|
||||
- titan-15
|
||||
- titan-17
|
||||
- titan-19
|
||||
- weight: 90
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: hardware
|
||||
operator: In
|
||||
values:
|
||||
- rpi5
|
||||
- weight: 50
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: hardware
|
||||
operator: In
|
||||
values:
|
||||
- rpi4
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
@ -55,10 +149,51 @@ spec:
|
||||
- rpi5
|
||||
- rpi4
|
||||
cainjector:
|
||||
replicaCount: 2
|
||||
podDisruptionBudget:
|
||||
enabled: true
|
||||
minAvailable: 1
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
nodeSelector:
|
||||
node-role.kubernetes.io/worker: "true"
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: atlas.bstein.dev/spillover
|
||||
operator: DoesNotExist
|
||||
- weight: 95
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-13
|
||||
- titan-15
|
||||
- titan-17
|
||||
- titan-19
|
||||
- weight: 90
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: hardware
|
||||
operator: In
|
||||
values:
|
||||
- rpi5
|
||||
- weight: 50
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: hardware
|
||||
operator: In
|
||||
values:
|
||||
- rpi4
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
|
||||
@ -10,6 +10,7 @@ data:
|
||||
errors
|
||||
cache 30
|
||||
hosts {
|
||||
192.168.22.9 agent.bstein.dev
|
||||
192.168.22.9 alerts.bstein.dev
|
||||
192.168.22.9 auth.bstein.dev
|
||||
192.168.22.9 bstein.dev
|
||||
@ -28,6 +29,7 @@ data:
|
||||
192.168.22.9 matrix.live.bstein.dev
|
||||
192.168.22.9 metrics.bstein.dev
|
||||
192.168.22.9 monero.bstein.dev
|
||||
192.168.22.9 moonlight.bstein.dev
|
||||
10.43.6.87 money.bstein.dev
|
||||
192.168.22.9 notes.bstein.dev
|
||||
192.168.22.9 office.bstein.dev
|
||||
@ -40,6 +42,7 @@ data:
|
||||
192.168.22.9 secret.bstein.dev
|
||||
192.168.22.9 sso.bstein.dev
|
||||
192.168.22.9 stream.bstein.dev
|
||||
192.168.22.9 wolf.bstein.dev
|
||||
192.168.22.9 tasks.bstein.dev
|
||||
192.168.22.9 vault.bstein.dev
|
||||
fallthrough
|
||||
|
||||
@ -4,8 +4,12 @@ kind: Kustomization
|
||||
resources:
|
||||
- ../modules/base
|
||||
- ../modules/profiles/atlas-ha
|
||||
- node-prefer-noschedule-serviceaccount.yaml
|
||||
- node-prefer-noschedule-rbac.yaml
|
||||
- node-prefer-noschedule-cronjob.yaml
|
||||
- coredns-custom.yaml
|
||||
- coredns-deployment.yaml
|
||||
- ntp-sync-daemonset.yaml
|
||||
- workload-profiles.yaml
|
||||
- ../sources/cert-manager/letsencrypt.yaml
|
||||
- ../sources/cert-manager/letsencrypt-prod.yaml
|
||||
|
||||
80
infrastructure/core/node-prefer-noschedule-cronjob.yaml
Normal file
80
infrastructure/core/node-prefer-noschedule-cronjob.yaml
Normal file
@ -0,0 +1,80 @@
|
||||
# infrastructure/core/node-prefer-noschedule-cronjob.yaml
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: node-prefer-noschedule
|
||||
namespace: kube-system
|
||||
spec:
|
||||
schedule: "* * * * *"
|
||||
concurrencyPolicy: Replace
|
||||
successfulJobsHistoryLimit: 1
|
||||
failedJobsHistoryLimit: 3
|
||||
jobTemplate:
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: node-prefer-noschedule
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: taint
|
||||
image: bitnami/kubectl@sha256:554ab88b1858e8424c55de37ad417b16f2a0e65d1607aa0f3fe3ce9b9f10b131
|
||||
command:
|
||||
- /usr/bin/env
|
||||
- bash
|
||||
- -ceu
|
||||
- |
|
||||
k() {
|
||||
kubectl --request-timeout=10s "$@"
|
||||
}
|
||||
|
||||
clear_worker() {
|
||||
local node="${1}"
|
||||
local hardware="${2}"
|
||||
if k get node "${node}" >/dev/null 2>&1; then
|
||||
k label node "${node}" node-role.kubernetes.io/worker=true "hardware=${hardware}" --overwrite=true || true
|
||||
k label node "${node}" atlas.bstein.dev/spillover- || true
|
||||
k taint node "${node}" node.kubernetes.io/unschedulable:NoSchedule- || true
|
||||
k uncordon "${node}" || true
|
||||
else
|
||||
echo "skipping missing node ${node}"
|
||||
fi
|
||||
}
|
||||
|
||||
clear_worker titan-04 rpi5
|
||||
clear_worker titan-05 rpi5
|
||||
clear_worker titan-07 rpi5
|
||||
clear_worker titan-08 rpi5
|
||||
clear_worker titan-11 rpi5
|
||||
clear_worker titan-12 rpi4
|
||||
clear_worker titan-14 rpi4
|
||||
clear_worker titan-18 rpi4
|
||||
clear_worker titan-22 amd64
|
||||
|
||||
if k get node titan-22 >/dev/null 2>&1; then
|
||||
k label node titan-22 atlas.bstein.dev/general-compute=last-resort --overwrite=true || true
|
||||
fi
|
||||
|
||||
if k get node titan-23 >/dev/null 2>&1; then
|
||||
k label node titan-23 \
|
||||
veles.bstein.dev/simulation=true \
|
||||
veles.bstein.dev/node-pool=oceanus \
|
||||
node-role.kubernetes.io/veles-sim=true \
|
||||
longhorn-host=true \
|
||||
hardware=oceanus \
|
||||
--overwrite=true || true
|
||||
k label node titan-23 node-role.kubernetes.io/worker- || true
|
||||
k taint node titan-23 veles.bstein.dev/simulation=true:NoSchedule --overwrite=true || true
|
||||
else
|
||||
echo "skipping missing node titan-23"
|
||||
fi
|
||||
|
||||
for node in titan-13 titan-15 titan-17 titan-19; do
|
||||
if k get node "${node}" >/dev/null 2>&1; then
|
||||
k label node "${node}" atlas.bstein.dev/spillover=true longhorn-host=true --overwrite=true || true
|
||||
k taint node "${node}" longhorn=true:PreferNoSchedule --overwrite=true || true
|
||||
k taint node "${node}" atlas.bstein.dev/spillover=true:PreferNoSchedule --overwrite=true || true
|
||||
else
|
||||
echo "skipping missing node ${node}"
|
||||
fi
|
||||
done
|
||||
22
infrastructure/core/node-prefer-noschedule-rbac.yaml
Normal file
22
infrastructure/core/node-prefer-noschedule-rbac.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
# infrastructure/core/node-prefer-noschedule-rbac.yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: node-prefer-noschedule
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["nodes"]
|
||||
verbs: ["get", "list", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: node-prefer-noschedule
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: node-prefer-noschedule
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: node-prefer-noschedule
|
||||
namespace: kube-system
|
||||
@ -0,0 +1,6 @@
|
||||
# infrastructure/core/node-prefer-noschedule-serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: node-prefer-noschedule
|
||||
namespace: kube-system
|
||||
27
infrastructure/core/workload-profiles.yaml
Normal file
27
infrastructure/core/workload-profiles.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
# infrastructure/core/workload-profiles.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: atlas-workload-profiles
|
||||
namespace: kube-system
|
||||
data:
|
||||
profiles.yaml: |
|
||||
profiles:
|
||||
tiny:
|
||||
request: { cpu: 25m, memory: 64Mi }
|
||||
limit: { cpu: 200m, memory: 256Mi }
|
||||
light:
|
||||
request: { cpu: 50m, memory: 128Mi }
|
||||
limit: { cpu: 500m, memory: 512Mi }
|
||||
standard:
|
||||
request: { cpu: 250m, memory: 512Mi }
|
||||
limit: { cpu: "1", memory: 1Gi }
|
||||
heavy:
|
||||
request: { cpu: 500m, memory: 1Gi }
|
||||
limit: { cpu: 1500m, memory: 3Gi }
|
||||
ci:
|
||||
request: { cpu: 512m, memory: 512Mi }
|
||||
limit: { cpu: 1500m, memory: 2Gi }
|
||||
scavenger:
|
||||
request: { cpu: 10m, memory: 32Mi }
|
||||
limit: { cpu: 250m, memory: 256Mi }
|
||||
97
infrastructure/descheduler/helmrelease.yaml
Normal file
97
infrastructure/descheduler/helmrelease.yaml
Normal file
@ -0,0 +1,97 @@
|
||||
# infrastructure/descheduler/helmrelease.yaml
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: descheduler
|
||||
namespace: kube-system
|
||||
spec:
|
||||
interval: 30m
|
||||
install:
|
||||
remediation:
|
||||
retries: 3
|
||||
upgrade:
|
||||
remediation:
|
||||
retries: 3
|
||||
chart:
|
||||
spec:
|
||||
chart: descheduler
|
||||
version: 0.33.0
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: descheduler
|
||||
namespace: flux-system
|
||||
values:
|
||||
kind: CronJob
|
||||
schedule: "*/20 * * * *"
|
||||
successfulJobsHistoryLimit: 1
|
||||
failedJobsHistoryLimit: 3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 50m
|
||||
memory: 96Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 256Mi
|
||||
deschedulerPolicyAPIVersion: descheduler/v1alpha2
|
||||
deschedulerPolicy:
|
||||
maxNoOfPodsToEvictPerNode: 2
|
||||
maxNoOfPodsToEvictPerNamespace: 2
|
||||
profiles:
|
||||
- name: atlas-rpi-balance
|
||||
pluginConfig:
|
||||
- name: DefaultEvictor
|
||||
args:
|
||||
nodeFit: true
|
||||
minPodAge: 10m
|
||||
ignorePvcPods: true
|
||||
evictLocalStoragePods: false
|
||||
- name: RemovePodsHavingTooManyRestarts
|
||||
args:
|
||||
podRestartThreshold: 12
|
||||
includingInitContainers: true
|
||||
- name: RemovePodsViolatingNodeAffinity
|
||||
args:
|
||||
nodeAffinityType:
|
||||
- requiredDuringSchedulingIgnoredDuringExecution
|
||||
- name: RemovePodsViolatingTopologySpreadConstraint
|
||||
- name: RemovePodsViolatingNodeTaints
|
||||
- name: LowNodeUtilization
|
||||
args:
|
||||
thresholds:
|
||||
cpu: 45
|
||||
memory: 45
|
||||
pods: 45
|
||||
targetThresholds:
|
||||
cpu: 75
|
||||
memory: 75
|
||||
pods: 75
|
||||
plugins:
|
||||
balance:
|
||||
enabled:
|
||||
- RemovePodsViolatingTopologySpreadConstraint
|
||||
- LowNodeUtilization
|
||||
deschedule:
|
||||
enabled:
|
||||
- RemovePodsHavingTooManyRestarts
|
||||
- RemovePodsViolatingNodeTaints
|
||||
- RemovePodsViolatingNodeAffinity
|
||||
priorityClassName: system-cluster-critical
|
||||
nodeSelector:
|
||||
node-role.kubernetes.io/control-plane: "true"
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- weight: 100
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-0a
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
- key: node-role.kubernetes.io/master
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
5
infrastructure/descheduler/kustomization.yaml
Normal file
5
infrastructure/descheduler/kustomization.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
# infrastructure/descheduler/kustomization.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- helmrelease.yaml
|
||||
@ -26,6 +26,9 @@ spec:
|
||||
cleanupOnFail: true
|
||||
timeout: 15m
|
||||
values:
|
||||
global:
|
||||
nodeSelector:
|
||||
longhorn-host: "true"
|
||||
service:
|
||||
ui:
|
||||
type: NodePort
|
||||
@ -78,3 +81,23 @@ spec:
|
||||
tag: v2.16.0
|
||||
defaultSettings:
|
||||
systemManagedPodsImagePullPolicy: Always
|
||||
taintToleration: veles.bstein.dev/simulation=true:NoSchedule
|
||||
longhornManager:
|
||||
tolerations:
|
||||
- key: veles.bstein.dev/simulation
|
||||
operator: Equal
|
||||
value: "true"
|
||||
effect: NoSchedule
|
||||
nodeSelector:
|
||||
longhorn-host: "true"
|
||||
longhornDriver:
|
||||
tolerations:
|
||||
- key: veles.bstein.dev/simulation
|
||||
operator: Equal
|
||||
value: "true"
|
||||
effect: NoSchedule
|
||||
nodeSelector:
|
||||
longhorn-host: "true"
|
||||
longhornUI:
|
||||
nodeSelector:
|
||||
longhorn-host: "true"
|
||||
|
||||
@ -7,7 +7,9 @@ resources:
|
||||
- secretproviderclass.yaml
|
||||
- vault-sync-deployment.yaml
|
||||
- helmrelease.yaml
|
||||
- veles-recurring-jobs.yaml
|
||||
- longhorn-settings-ensure-job.yaml
|
||||
- longhorn-csi-toleration-ensure-job.yaml
|
||||
- longhorn-disk-tags-ensure-job.yaml
|
||||
|
||||
configMapGenerator:
|
||||
|
||||
@ -0,0 +1,106 @@
|
||||
# infrastructure/longhorn/core/longhorn-csi-toleration-ensure-job.yaml
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: longhorn-csi-toleration-ensure-4
|
||||
namespace: longhorn-system
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
activeDeadlineSeconds: 240
|
||||
ttlSecondsAfterFinished: 3600
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: longhorn-service-account
|
||||
restartPolicy: Never
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: titan-11
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: kubernetes.io/arch
|
||||
operator: In
|
||||
values: ["arm64"]
|
||||
- key: node-role.kubernetes.io/worker
|
||||
operator: Exists
|
||||
containers:
|
||||
- name: patch
|
||||
image: bitnami/kubectl@sha256:554ab88b1858e8424c55de37ad417b16f2a0e65d1607aa0f3fe3ce9b9f10b131
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
set -euo pipefail
|
||||
|
||||
ns="longhorn-system"
|
||||
ds="longhorn-csi-plugin"
|
||||
key="veles.bstein.dev/simulation"
|
||||
value="true"
|
||||
effect="NoSchedule"
|
||||
|
||||
patch_daemonset() {
|
||||
target="$1"
|
||||
current="$(kubectl -n "${ns}" get daemonset "${target}" -o json)"
|
||||
if echo "${current}" | jq -e \
|
||||
--arg key "${key}" \
|
||||
--arg value "${value}" \
|
||||
--arg effect "${effect}" \
|
||||
'.spec.template.spec.tolerations[]? | select(.key == $key and .value == $value and .effect == $effect)' >/dev/null; then
|
||||
echo "${target} already tolerates ${key}=${value}:${effect}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
patch="$(echo "${current}" | jq -c \
|
||||
--arg key "${key}" \
|
||||
--arg value "${value}" \
|
||||
--arg effect "${effect}" \
|
||||
'{
|
||||
spec: {
|
||||
template: {
|
||||
spec: {
|
||||
tolerations: ((.spec.template.spec.tolerations // []) + [
|
||||
{key: $key, operator: "Equal", value: $value, effect: $effect}
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
}')"
|
||||
kubectl -n "${ns}" patch daemonset "${target}" --type=merge -p "${patch}"
|
||||
}
|
||||
|
||||
patch_daemonset "${ds}"
|
||||
engine_daemonsets="$(kubectl -n "${ns}" get daemonset -l longhorn.io/component=engine-image -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}')"
|
||||
for engine_ds in ${engine_daemonsets}; do
|
||||
patch_daemonset "${engine_ds}"
|
||||
done
|
||||
|
||||
csi_ready="false"
|
||||
for attempt in $(seq 1 90); do
|
||||
if kubectl get csinode titan-23 -o json | jq -e '.spec.drivers[]? | select(.name == "driver.longhorn.io")' >/dev/null; then
|
||||
echo "driver.longhorn.io registered on titan-23"
|
||||
csi_ready="true"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "${csi_ready}" != "true" ]; then
|
||||
echo "driver.longhorn.io did not register on titan-23 before timeout" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for engine_ds in ${engine_daemonsets}; do
|
||||
for attempt in $(seq 1 90); do
|
||||
if kubectl -n "${ns}" get pods -o json | jq -e \
|
||||
--arg engine_ds "${engine_ds}" \
|
||||
'.items[] | select(.spec.nodeName == "titan-23") | select(.metadata.ownerReferences[]?.name == $engine_ds) | select([.status.containerStatuses[]?.ready] | all)' >/dev/null; then
|
||||
echo "${engine_ds} ready on titan-23"
|
||||
break
|
||||
fi
|
||||
if [ "${attempt}" = "90" ]; then
|
||||
echo "${engine_ds} did not become ready on titan-23 before timeout" >&2
|
||||
exit 1
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
done
|
||||
@ -2,7 +2,7 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: longhorn-disk-tags-ensure-1
|
||||
name: longhorn-disk-tags-ensure-3
|
||||
namespace: longhorn-system
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
|
||||
@ -2,15 +2,18 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: longhorn-settings-ensure-4
|
||||
name: longhorn-settings-ensure-10
|
||||
namespace: longhorn-system
|
||||
spec:
|
||||
backoffLimit: 0
|
||||
activeDeadlineSeconds: 240
|
||||
ttlSecondsAfterFinished: 3600
|
||||
template:
|
||||
spec:
|
||||
serviceAccountName: longhorn-service-account
|
||||
restartPolicy: Never
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: titan-11
|
||||
volumes:
|
||||
- name: longhorn-settings-ensure-script
|
||||
configMap:
|
||||
|
||||
@ -17,10 +17,28 @@ import urllib.request
|
||||
|
||||
LONGHORN_NS = "longhorn-system"
|
||||
LONGHORN_API = "/apis/longhorn.io/v1beta2/namespaces/{namespace}/nodes"
|
||||
DESIRED_TAGS = {
|
||||
"/mnt/astreae": "astreae",
|
||||
"/mnt/asteria": "asteria",
|
||||
DESIRED_DISK_TAGS = {
|
||||
"/mnt/astreae": ["astreae"],
|
||||
"/mnt/asteria": ["asteria"],
|
||||
"/mnt/veles": ["veles-oceanus", "veles-db", "veles-artifacts"],
|
||||
"/mnt/veles-db": ["veles-oceanus", "veles-db"],
|
||||
"/mnt/veles-artifacts": ["veles-oceanus", "veles-artifacts"],
|
||||
}
|
||||
DESIRED_NODE_TAGS = {
|
||||
"titan-23": ["veles-oceanus"],
|
||||
}
|
||||
DESIRED_NODE_DISKS = {
|
||||
"titan-23": {
|
||||
"veles-oceanus": {
|
||||
"path": "/mnt/veles",
|
||||
"allowScheduling": True,
|
||||
"evictionRequested": False,
|
||||
"storageReserved": 0,
|
||||
"tags": ["veles-oceanus", "veles-db", "veles-artifacts"],
|
||||
}
|
||||
}
|
||||
}
|
||||
DISABLE_DEFAULT_DISK_NODES = {"titan-23"}
|
||||
|
||||
|
||||
def api_base() -> str:
|
||||
@ -63,8 +81,30 @@ def list_nodes() -> list[dict]:
|
||||
return data.get("items", [])
|
||||
|
||||
|
||||
def patch_disk_tags(node_name: str, disk_name: str, desired_tag: str) -> None:
|
||||
body = {"spec": {"disks": {disk_name: {"tags": [desired_tag]}}}}
|
||||
def merged_tags(current_tags: list[str], desired_tags: list[str]) -> list[str]:
|
||||
return sorted(dict.fromkeys([*current_tags, *desired_tags]))
|
||||
|
||||
|
||||
def patch_node_tags(node_name: str, desired_tags: list[str]) -> None:
|
||||
body = {"spec": {"tags": desired_tags}}
|
||||
request_json(
|
||||
"PATCH",
|
||||
f"{LONGHORN_API.format(namespace=LONGHORN_NS)}/{node_name}",
|
||||
body=body,
|
||||
)
|
||||
|
||||
|
||||
def patch_disk_tags(node_name: str, disk_name: str, desired_tags: list[str]) -> None:
|
||||
body = {"spec": {"disks": {disk_name: {"tags": desired_tags}}}}
|
||||
request_json(
|
||||
"PATCH",
|
||||
f"{LONGHORN_API.format(namespace=LONGHORN_NS)}/{node_name}",
|
||||
body=body,
|
||||
)
|
||||
|
||||
|
||||
def patch_disks(node_name: str, disks: dict) -> None:
|
||||
body = {"spec": {"disks": disks}}
|
||||
request_json(
|
||||
"PATCH",
|
||||
f"{LONGHORN_API.format(namespace=LONGHORN_NS)}/{node_name}",
|
||||
@ -78,18 +118,52 @@ def main() -> int:
|
||||
|
||||
for node in list_nodes():
|
||||
name = node.get("metadata", {}).get("name", "")
|
||||
desired_node_tags = DESIRED_NODE_TAGS.get(name)
|
||||
if desired_node_tags:
|
||||
current_node_tags = node.get("spec", {}).get("tags") or []
|
||||
next_node_tags = merged_tags(current_node_tags, desired_node_tags)
|
||||
if current_node_tags != next_node_tags:
|
||||
print(f"patching {name} node tags={current_node_tags!r} -> {next_node_tags!r}")
|
||||
patch_node_tags(name, next_node_tags)
|
||||
changed += 1
|
||||
else:
|
||||
skipped += 1
|
||||
|
||||
spec_disks = node.get("spec", {}).get("disks", {}) or {}
|
||||
desired_disks = DESIRED_NODE_DISKS.get(name, {})
|
||||
missing_disks = {
|
||||
disk_name: disk_spec
|
||||
for disk_name, disk_spec in desired_disks.items()
|
||||
if disk_name not in spec_disks
|
||||
}
|
||||
if missing_disks:
|
||||
print(f"adding {name} disks={sorted(missing_disks)}")
|
||||
patch_disks(name, missing_disks)
|
||||
changed += len(missing_disks)
|
||||
spec_disks = {**spec_disks, **missing_disks}
|
||||
|
||||
if name in DISABLE_DEFAULT_DISK_NODES:
|
||||
disable_patch = {}
|
||||
for disk_name, disk in spec_disks.items():
|
||||
disk_path = (disk.get("path") or "").rstrip("/")
|
||||
if disk_path == "/var/lib/longhorn" and disk.get("allowScheduling", True):
|
||||
disable_patch[disk_name] = {"allowScheduling": False}
|
||||
if disable_patch:
|
||||
print(f"disabling default Longhorn scheduling on {name} disks={sorted(disable_patch)}")
|
||||
patch_disks(name, disable_patch)
|
||||
changed += len(disable_patch)
|
||||
|
||||
for disk_name, disk in spec_disks.items():
|
||||
disk_path = disk.get("path")
|
||||
desired_tag = DESIRED_TAGS.get(disk_path)
|
||||
if not desired_tag:
|
||||
desired_disk_tags = DESIRED_DISK_TAGS.get(disk_path)
|
||||
if not desired_disk_tags:
|
||||
continue
|
||||
current_tags = disk.get("tags") or []
|
||||
if current_tags == [desired_tag]:
|
||||
if current_tags == desired_disk_tags:
|
||||
skipped += 1
|
||||
continue
|
||||
print(f"patching {name}:{disk_name} path={disk_path} tags={current_tags!r} -> {[desired_tag]!r}")
|
||||
patch_disk_tags(name, disk_name, desired_tag)
|
||||
print(f"patching {name}:{disk_name} path={disk_path} tags={current_tags!r} -> {desired_disk_tags!r}")
|
||||
patch_disk_tags(name, disk_name, desired_disk_tags)
|
||||
changed += 1
|
||||
|
||||
print(f"done: changed={changed} skipped={skipped}")
|
||||
|
||||
@ -4,11 +4,12 @@ set -eu
|
||||
# Longhorn blocks direct CR patches for some settings; use the internal API instead.
|
||||
|
||||
api_base="http://longhorn-backend.longhorn-system.svc:9500/v1/settings"
|
||||
curl_opts="-fsS --connect-timeout 3 --max-time 15"
|
||||
|
||||
wait_for_api() {
|
||||
attempts=30
|
||||
while [ "${attempts}" -gt 0 ]; do
|
||||
if curl -fsS "${api_base}" >/dev/null 2>&1; then
|
||||
if curl ${curl_opts} "${api_base}" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
attempts=$((attempts - 1))
|
||||
@ -22,17 +23,32 @@ update_setting() {
|
||||
name="$1"
|
||||
value="$2"
|
||||
|
||||
current="$(curl -fsS "${api_base}/${name}" || true)"
|
||||
current="$(curl ${curl_opts} "${api_base}/${name}" || true)"
|
||||
if echo "${current}" | grep -Fq "\"value\":\"${value}\""; then
|
||||
echo "Setting ${name} already set."
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Setting ${name} -> ${value}"
|
||||
curl -fsS -X PUT \
|
||||
out="$(mktemp)"
|
||||
if curl ${curl_opts} -o "${out}" -X PUT \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"value\":\"${value}\"}" \
|
||||
"${api_base}/${name}" >/dev/null
|
||||
"${api_base}/${name}"; then
|
||||
rm -f "${out}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
current="$(curl ${curl_opts} "${api_base}/${name}" || true)"
|
||||
if echo "${current}" | grep -Fq "\"value\":\"${value}\""; then
|
||||
echo "Setting ${name} stored; Longhorn will apply it when current state allows."
|
||||
rm -f "${out}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
cat "${out}" >&2 || true
|
||||
rm -f "${out}"
|
||||
return 1
|
||||
}
|
||||
|
||||
wait_for_api
|
||||
@ -40,3 +56,8 @@ update_setting default-engine-image "registry.bstein.dev/infra/longhorn-engine:v
|
||||
update_setting default-instance-manager-image "registry.bstein.dev/infra/longhorn-instance-manager:v1.8.2"
|
||||
update_setting default-backing-image-manager-image "registry.bstein.dev/infra/longhorn-backing-image-manager:v1.8.2"
|
||||
update_setting support-bundle-manager-image "registry.bstein.dev/infra/longhorn-support-bundle-kit:v0.0.56"
|
||||
update_setting taint-toleration "veles.bstein.dev/simulation=true:NoSchedule"
|
||||
# Keep storage-heavy nodes from getting hammered by rebuild storms and skew.
|
||||
update_setting replica-auto-balance "best-effort"
|
||||
update_setting concurrent-replica-rebuild-per-node-limit "2"
|
||||
update_setting node-down-pod-deletion-policy "delete-both-statefulset-and-deployment-pod"
|
||||
|
||||
@ -13,9 +13,27 @@ spec:
|
||||
- objectName: "harbor-pull__dockerconfigjson"
|
||||
secretPath: "kv/data/atlas/shared/harbor-pull"
|
||||
secretKey: "dockerconfigjson"
|
||||
- objectName: "longhorn-backup-b2__AWS_ACCESS_KEY_ID"
|
||||
secretPath: "kv/data/atlas/longhorn/backup-b2"
|
||||
secretKey: "AWS_ACCESS_KEY_ID"
|
||||
- objectName: "longhorn-backup-b2__AWS_SECRET_ACCESS_KEY"
|
||||
secretPath: "kv/data/atlas/longhorn/backup-b2"
|
||||
secretKey: "AWS_SECRET_ACCESS_KEY"
|
||||
- objectName: "longhorn-backup-b2__AWS_ENDPOINTS"
|
||||
secretPath: "kv/data/atlas/longhorn/backup-b2"
|
||||
secretKey: "AWS_ENDPOINTS"
|
||||
secretObjects:
|
||||
- secretName: longhorn-registry
|
||||
type: kubernetes.io/dockerconfigjson
|
||||
data:
|
||||
- objectName: harbor-pull__dockerconfigjson
|
||||
key: .dockerconfigjson
|
||||
- secretName: longhorn-backup-b2
|
||||
type: Opaque
|
||||
data:
|
||||
- objectName: longhorn-backup-b2__AWS_ACCESS_KEY_ID
|
||||
key: AWS_ACCESS_KEY_ID
|
||||
- objectName: longhorn-backup-b2__AWS_SECRET_ACCESS_KEY
|
||||
key: AWS_SECRET_ACCESS_KEY
|
||||
- objectName: longhorn-backup-b2__AWS_ENDPOINTS
|
||||
key: AWS_ENDPOINTS
|
||||
|
||||
@ -26,6 +26,16 @@ spec:
|
||||
- key: hardware
|
||||
operator: In
|
||||
values: ["rpi5", "rpi4"]
|
||||
- weight: 90
|
||||
preference:
|
||||
matchExpressions:
|
||||
- key: kubernetes.io/hostname
|
||||
operator: NotIn
|
||||
values:
|
||||
- titan-13
|
||||
- titan-15
|
||||
- titan-17
|
||||
- titan-19
|
||||
containers:
|
||||
- name: sync
|
||||
image: alpine:3.20
|
||||
|
||||
60
infrastructure/longhorn/core/veles-recurring-jobs.yaml
Normal file
60
infrastructure/longhorn/core/veles-recurring-jobs.yaml
Normal file
@ -0,0 +1,60 @@
|
||||
# infrastructure/longhorn/core/veles-recurring-jobs.yaml
|
||||
apiVersion: longhorn.io/v1beta2
|
||||
kind: RecurringJob
|
||||
metadata:
|
||||
name: veles-postgres-backup
|
||||
namespace: longhorn-system
|
||||
spec:
|
||||
name: veles-postgres-backup
|
||||
cron: "30 5 * * *"
|
||||
task: backup
|
||||
groups:
|
||||
- veles
|
||||
- veles-postgres
|
||||
retain: 7
|
||||
concurrency: 1
|
||||
---
|
||||
apiVersion: longhorn.io/v1beta2
|
||||
kind: RecurringJob
|
||||
metadata:
|
||||
name: veles-postgres-snapshot
|
||||
namespace: longhorn-system
|
||||
spec:
|
||||
name: veles-postgres-snapshot
|
||||
cron: "*/30 * * * *"
|
||||
task: snapshot
|
||||
groups:
|
||||
- veles
|
||||
- veles-postgres
|
||||
retain: 8
|
||||
concurrency: 1
|
||||
---
|
||||
apiVersion: longhorn.io/v1beta2
|
||||
kind: RecurringJob
|
||||
metadata:
|
||||
name: veles-artifacts-backup
|
||||
namespace: longhorn-system
|
||||
spec:
|
||||
name: veles-artifacts-backup
|
||||
cron: "45 5 * * *"
|
||||
task: backup
|
||||
groups:
|
||||
- veles
|
||||
- veles-artifacts
|
||||
retain: 7
|
||||
concurrency: 1
|
||||
---
|
||||
apiVersion: longhorn.io/v1beta2
|
||||
kind: RecurringJob
|
||||
metadata:
|
||||
name: veles-artifacts-snapshot
|
||||
namespace: longhorn-system
|
||||
spec:
|
||||
name: veles-artifacts-snapshot
|
||||
cron: "15 */6 * * *"
|
||||
task: snapshot
|
||||
groups:
|
||||
- veles
|
||||
- veles-artifacts
|
||||
retain: 8
|
||||
concurrency: 1
|
||||
@ -3,3 +3,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- scavenger.yaml
|
||||
- veles.yaml
|
||||
|
||||
17
infrastructure/modules/base/priorityclass/veles.yaml
Normal file
17
infrastructure/modules/base/priorityclass/veles.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
# infrastructure/modules/base/priorityclass/veles.yaml
|
||||
apiVersion: scheduling.k8s.io/v1
|
||||
kind: PriorityClass
|
||||
metadata:
|
||||
name: veles-core
|
||||
value: 500
|
||||
globalDefault: false
|
||||
description: "For Veles core database, API, and controller workloads"
|
||||
---
|
||||
apiVersion: scheduling.k8s.io/v1
|
||||
kind: PriorityClass
|
||||
metadata:
|
||||
name: veles-sim
|
||||
value: 50
|
||||
globalDefault: false
|
||||
preemptionPolicy: Never
|
||||
description: "For Veles simulation jobs; lower than core and non-preempting"
|
||||
@ -5,3 +5,6 @@ resources:
|
||||
- asteria.yaml
|
||||
- asteria-encrypted.yaml
|
||||
- astreae.yaml
|
||||
- veles-oceanus-db.yaml
|
||||
- veles-oceanus-artifacts.yaml
|
||||
- veles-oceanus-policy.yaml
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
# infrastructure/modules/base/storageclass/veles-oceanus-artifacts.yaml
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
name: veles-oceanus-artifacts
|
||||
annotations:
|
||||
veles.bstein.dev/allowed-namespace: veles
|
||||
provisioner: driver.longhorn.io
|
||||
parameters:
|
||||
nodeSelector: veles-oceanus
|
||||
diskSelector: veles-oceanus,veles-artifacts
|
||||
fromBackup: ""
|
||||
numberOfReplicas: "1"
|
||||
staleReplicaTimeout: "30"
|
||||
fsType: ext4
|
||||
replicaAutoBalance: disabled
|
||||
dataLocality: strict-local
|
||||
recurringJobSelector: '[{"name":"veles-artifacts-backup","isGroup":false},{"name":"veles-artifacts-snapshot","isGroup":false}]'
|
||||
reclaimPolicy: Retain
|
||||
allowVolumeExpansion: true
|
||||
volumeBindingMode: WaitForFirstConsumer
|
||||
@ -0,0 +1,21 @@
|
||||
# infrastructure/modules/base/storageclass/veles-oceanus-db.yaml
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
name: veles-oceanus-db
|
||||
annotations:
|
||||
veles.bstein.dev/allowed-namespace: veles
|
||||
provisioner: driver.longhorn.io
|
||||
parameters:
|
||||
nodeSelector: veles-oceanus
|
||||
diskSelector: veles-oceanus,veles-db
|
||||
fromBackup: ""
|
||||
numberOfReplicas: "1"
|
||||
staleReplicaTimeout: "30"
|
||||
fsType: ext4
|
||||
replicaAutoBalance: disabled
|
||||
dataLocality: strict-local
|
||||
recurringJobSelector: '[{"name":"veles-postgres-backup","isGroup":false},{"name":"veles-postgres-snapshot","isGroup":false}]'
|
||||
reclaimPolicy: Retain
|
||||
allowVolumeExpansion: true
|
||||
volumeBindingMode: WaitForFirstConsumer
|
||||
@ -0,0 +1,25 @@
|
||||
# infrastructure/modules/base/storageclass/veles-oceanus-policy.yaml
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingAdmissionPolicy
|
||||
metadata:
|
||||
name: veles-oceanus-storage-namespace
|
||||
spec:
|
||||
failurePolicy: Fail
|
||||
matchConstraints:
|
||||
resourceRules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE", "UPDATE"]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
validations:
|
||||
- expression: "!has(object.spec.storageClassName) || !(object.spec.storageClassName in ['veles-oceanus-db', 'veles-oceanus-artifacts']) || object.metadata.namespace == 'veles'"
|
||||
message: "Veles Oceanus storage classes are reserved for namespace veles"
|
||||
---
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingAdmissionPolicyBinding
|
||||
metadata:
|
||||
name: veles-oceanus-storage-namespace
|
||||
spec:
|
||||
policyName: veles-oceanus-storage-namespace
|
||||
validationActions:
|
||||
- Deny
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user