Compare commits
828 Commits
v2.1
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
|
0fe1c72175 | ||
|
4517063b79 | ||
|
4104fea90d | ||
|
528d4f150c | ||
|
fa3c7cfee3 | ||
|
55ef3b1f0b | ||
|
41321963b8 | ||
|
ef8f01b299 | ||
|
51752f1a6e | ||
|
1821311479 | ||
|
ccfd8f5fea | ||
|
2a91646eaf | ||
|
47e5153714 | ||
|
21f7282088 | ||
|
641f6a3b63 | ||
|
e156e815ad | ||
|
5892d705da | ||
|
431a735eca | ||
|
99d72d14a3 | ||
|
4a0b5073af | ||
|
5216844263 | ||
|
7eb9673a1a | ||
|
a439f91721 | ||
|
6d3d800226 | ||
|
fba1fea81e | ||
|
f186370654 | ||
|
fc72ddbd24 | ||
|
fb03b0f754 | ||
|
5338017bf6 | ||
|
4ff141c18d | ||
|
4fc16b3bb8 | ||
|
ddbcd2c4ef | ||
|
781ecdaecd | ||
|
808185b10f | ||
|
e1a0d2a3fd | ||
|
ecf5854ca9 | ||
|
adfb270991 | ||
|
b171bb702b | ||
|
f1e887e239 | ||
|
100766d1a4 | ||
|
e074c2a56b | ||
|
38d03eb816 | ||
|
b554c96160 | ||
|
92ff1b1ee8 | ||
|
31e77aafab | ||
|
dec0607a94 | ||
|
3c33f6f028 | ||
|
a28d1e4693 | ||
|
b9019016d7 | ||
|
ca21ef66d1 | ||
|
b11ea828e9 | ||
|
fd9736b527 | ||
|
6ade0ce262 | ||
|
f54693f501 | ||
|
bc6c8d5c76 | ||
|
04fb8190fe | ||
|
004f1e6a12 | ||
|
334fdce751 | ||
|
8587734174 | ||
|
9b4a0ce9a6 | ||
|
cd81346c1a | ||
|
41013e7580 | ||
|
849a3cd453 | ||
|
572e4e05e3 | ||
|
aff99fccc5 | ||
|
4757be12ac | ||
|
d23856b784 | ||
|
9f5c0239a8 | ||
|
d9f1c7c6e7 | ||
|
bb47b55999 | ||
|
75c0245020 | ||
|
181f56f026 | ||
|
c9d411c2c2 | ||
|
5a2597b329 | ||
|
c6a371b6bc | ||
|
5fe124932a | ||
|
4457289d1d | ||
|
541a8032c3 | ||
|
89188023ef | ||
|
633985d82f | ||
|
5a407e1a21 | ||
|
9c2771b842 | ||
|
897cb1a759 | ||
|
e214154f5f | ||
|
2c796b5956 | ||
|
202533cf1d | ||
|
40378cabd3 | ||
|
30d6aa06e5 | ||
|
b6206a0dbf | ||
|
d625d48231 | ||
|
0fd3fa7919 | ||
|
ad81dbf50f | ||
|
7ad0dd287a | ||
|
b09350cf1a | ||
|
ddc78f1244 | ||
|
5f0b4cdc6b | ||
|
a1915e1a8e | ||
|
53a68c35ff | ||
|
ca5a4c9aa9 | ||
|
03fcb34abe | ||
|
ba18cf5ab3 | ||
|
b271fbf84d | ||
|
748930239d | ||
|
c550826675 | ||
|
a337317533 | ||
|
8e5060b9a7 | ||
|
6c982f3fee | ||
|
1071115e90 | ||
|
493d421cf7 | ||
|
24b2d55c84 | ||
|
6812ce0ed6 | ||
|
6ac6fe675f | ||
|
3477c9c827 | ||
|
36ba3039ae | ||
|
40687759fb | ||
|
a70da3556a | ||
|
003fbd5785 | ||
|
6e4f62f2f2 | ||
|
197877d113 | ||
|
ab7d64e96f | ||
|
acfbd42719 | ||
|
c76db9c7a0 | ||
|
540a887651 | ||
|
d97514f841 | ||
|
e4404b2645 | ||
|
a373a2286d | ||
|
e2e8cfb677 | ||
|
b710020f7b | ||
|
46fe38e2c5 | ||
|
d7e391e006 | ||
|
6a0c905347 | ||
|
4d69fed8ad | ||
|
1dd4edded2 | ||
|
857d070679 | ||
|
e5d19fff6b | ||
|
acfdc64991 | ||
|
f8afd78120 | ||
|
ddb977f4b9 | ||
|
d9c06e99d1 | ||
|
b0df7dd5e3 | ||
|
fb4f4aa4c1 | ||
|
c2add82b93 | ||
|
8539a476fd | ||
|
4ade85669b | ||
|
50c0357467 | ||
|
cec1a53cd8 | ||
|
1605ffcad5 | ||
|
7c68481e43 | ||
|
6b8d24c1ef | ||
|
752f28c2bc | ||
|
fff8519517 | ||
|
02ce071abb | ||
|
7ea924b8f1 | ||
|
f6afc79b47 | ||
|
3a95111901 | ||
|
4456e91b5c | ||
|
c5a0002be4 | ||
|
159f2610c0 | ||
|
8d8aa80cd5 | ||
|
272b3ca8fa | ||
|
d5883bdbfa | ||
|
46d446f0e5 | ||
|
41d5d08686 | ||
|
bf79dc3269 | ||
|
82324a7795 | ||
|
91a82a1264 | ||
|
0bc8103654 | ||
|
fa60329105 | ||
|
99c4481e08 | ||
|
f03765681f | ||
|
22304806c8 | ||
|
649a4c5dd3 | ||
|
0c37bb043c | ||
|
da704f8f63 | ||
|
5d64ec3367 | ||
|
b05ff2db4e | ||
|
b9acfeb6b7 | ||
|
77aac95dfc | ||
|
72945e3679 | ||
|
1b01e3e486 | ||
|
fe14c17fe7 | ||
|
66b0c5c371 | ||
|
f3a371358a | ||
|
b4bea43f7e | ||
|
7c22973f9f | ||
|
487c6fcec4 | ||
|
c279938e21 | ||
|
855e8bee45 | ||
|
5bce250398 | ||
|
1add898a3c | ||
|
7ced7dd10c | ||
|
21f84c0162 | ||
|
7cf1c2f0c2 | ||
|
dcf92c8e94 | ||
|
6c2e09529b | ||
|
c129e72779 | ||
|
adf5e98b1b | ||
|
346a54ccd9 | ||
|
6d97cf9071 | ||
|
3680725cb0 | ||
|
e6299693aa | ||
|
43e2008107 | ||
|
f5b64339bf | ||
|
d1d0da1457 | ||
|
e692127d19 | ||
|
0ae7db9bbc | ||
|
95b45eff5d | ||
|
b660d69e18 | ||
|
873857273f | ||
|
442da10303 | ||
|
3ffa206abc | ||
|
971a110db7 | ||
|
d7a8f962da | ||
|
36952b8401 | ||
|
0a0e9b4db9 | ||
|
7a549fd9ac | ||
|
048438e0ef | ||
|
344fb0ae09 | ||
|
26902b29b6 | ||
|
d84add88b9 | ||
|
546e715c9e | ||
|
1454d912f4 | ||
|
6eda6d9deb | ||
|
33ce772b98 | ||
|
b8304cf7dc | ||
|
7ce5e9fbdc | ||
|
a9ace511d8 | ||
|
6c52c476bf | ||
|
c75d773248 | ||
|
82c09a98b7 | ||
|
103e70778b | ||
|
40d600b336 | ||
|
b6c50f2d07 | ||
|
4f91106f29 | ||
|
77e0150afe | ||
|
6f8fa8c286 | ||
|
8550fa62a5 | ||
|
a3f9694d09 | ||
|
fd893ab625 | ||
|
f530d3eb84 | ||
|
e43ed9274e | ||
|
f52aa691d6 | ||
|
46daed0654 | ||
|
e9bb5e5f48 | ||
|
fa8a6e6880 | ||
|
6f90a0f075 | ||
|
efa8c62ec4 | ||
|
7c619d64a6 | ||
|
1aac2431b8 | ||
|
f372831536 | ||
|
3e97437e31 | ||
|
b1aba95892 | ||
|
371123659b | ||
|
8da20f38ea | ||
|
d93a3bb736 | ||
|
e882d51b3c | ||
|
db7cc6bc80 | ||
|
33c0d1bd86 | ||
|
12c6c91586 | ||
|
2f12b5ce99 | ||
|
0c515497d7 | ||
|
68a9593f84 | ||
|
95c4d0c1c5 | ||
|
5498080119 | ||
|
3d9cec4ec9 | ||
|
505ab4567c | ||
|
8dee74d7b9 | ||
|
fba6d66720 | ||
|
7f7bb354c5 | ||
|
f4c0adf54c | ||
|
ac6757b9cc | ||
|
70c970cd6e | ||
|
3deb079546 | ||
|
9cb6fb57bf | ||
|
95adff55b0 | ||
|
99dd6678d5 | ||
|
6f5fdf64c7 | ||
|
9d67fbd520 | ||
|
ca8c9c5791 | ||
|
18a660ebc7 | ||
|
358f09bfe2 | ||
|
107624ccff | ||
|
13eb83a01c | ||
|
dc57189cf9 | ||
|
3cda380ad1 | ||
|
2bf8dae9a8 | ||
|
2993671acd | ||
|
a735987501 | ||
|
8bbb594dad | ||
|
fcc8e44f14 | ||
|
3572647e5b | ||
|
2eb5d2f653 | ||
|
be56f8dc30 | ||
|
580b72a5b2 | ||
|
a77d3cbedb | ||
|
d2a4b832f2 | ||
|
9f3e6b0da0 | ||
|
df903a757e | ||
|
779170a48e | ||
|
5ee0274b5b | ||
|
42a2642852 | ||
|
b5323e4144 | ||
|
485642c18f | ||
|
dcbc215b93 | ||
|
5ede36fb43 | ||
|
16bd359bc0 | ||
|
c6fa047212 | ||
|
59415ad0d6 | ||
|
ecc1482d50 | ||
|
b34486308c | ||
|
dd15abc9b1 | ||
|
10fcc49f4d | ||
|
706de7c2c6 | ||
|
ea0df58e7c | ||
|
4670f1f240 | ||
|
4ada0c3ae8 | ||
|
13e4b3a1c4 | ||
|
282b40a503 | ||
|
bf4d6c716c | ||
|
77ed17b392 | ||
|
6bcb6bf403 | ||
|
588ee8f192 | ||
|
ce533f01cc | ||
|
579d83f359 | ||
|
51c39205a8 | ||
|
4180f88442 | ||
|
d1046fa1c9 | ||
|
0a144e597a | ||
|
2f5af62a92 | ||
|
80693bde62 | ||
|
3abb21a80c | ||
|
e7aaf8f5d5 | ||
|
93ec0c121e | ||
|
ecc98be9c6 | ||
|
b8d4d46462 | ||
|
869b94ffaa | ||
|
d4a30c383d | ||
|
d4a3ea4fd0 | ||
|
fb31217e2c | ||
|
0453b52097 | ||
|
2d53334211 | ||
|
45428a53ce | ||
|
6c12dc8c4f | ||
|
7559625a38 | ||
|
450e1d3414 | ||
|
6dd45f38f9 | ||
|
843147aca0 | ||
|
191b8cb0ec | ||
|
9e79b79a89 | ||
|
2e474f4c95 | ||
|
27a86dafbc | ||
|
daf96bffb3 | ||
|
7a53c910f2 | ||
|
38b1cd1cec | ||
|
6adb4dc4c4 | ||
|
12df5bda72 | ||
|
ed18a1f175 | ||
|
d52f2b6a45 | ||
|
4d9731bd3a | ||
|
0da5449854 | ||
|
653c4b481d | ||
|
b9d0d93d6e | ||
|
84fde9d711 | ||
|
4e0e65044b | ||
|
70660236a8 | ||
|
130db696ca | ||
|
ada145ca5f | ||
|
25c46c84b8 | ||
|
32c952e501 | ||
|
7091831a00 | ||
|
1e43784d4c | ||
|
42fde2292d | ||
|
8ba2accb9f | ||
|
b56dd5f67f | ||
|
e8e99f1771 | ||
|
cb19a22cb9 | ||
|
392726842f | ||
|
76c31b0861 | ||
|
51a74efe57 | ||
|
bf0b37e010 | ||
|
efdc0a5c7d | ||
|
ae4a28b689 | ||
|
e1b0698eb2 | ||
|
35fdb29385 | ||
|
a28f5cb56c | ||
|
ad257698ef | ||
|
8b3bbf38c8 | ||
|
a506d7606c | ||
|
7e44bb6d21 | ||
|
9b45d4b211 | ||
|
f77c591d69 | ||
|
8c9f409032 | ||
|
0724c2b9b0 | ||
|
085870c4b5 | ||
|
26ef3c3eb4 | ||
|
1eeab9b589 | ||
|
7f5620db76 | ||
|
1e29a6b50c | ||
|
d09cc8e581 | ||
|
cd23938191 | ||
|
3b8aa66765 | ||
|
37adefba51 | ||
|
ec30b99534 | ||
|
a53233028b | ||
|
492ffec8c8 | ||
|
c25a39c5a3 | ||
|
3f1031e7b4 | ||
|
2339c11a15 | ||
|
2ba3d3cda2 | ||
|
6abe8ee06b | ||
|
a03b3e8c41 | ||
|
753d18740a | ||
|
1a0fa953f9 | ||
|
bd57d31780 | ||
|
91a7d22e2b | ||
|
8d555dba24 | ||
|
e566760a45 | ||
|
cd7492f79b | ||
|
9e3ad2ea1e | ||
|
a52680b3bf | ||
|
78cf9fced8 | ||
|
905b5d0e42 | ||
|
0ef8c27a67 | ||
|
e603d8dbb0 | ||
|
9e96c38ebe | ||
|
ad6f57f14d | ||
|
479e2bd78e | ||
|
ae56437297 | ||
|
dc4d6e6764 | ||
|
75acc10312 | ||
|
8d7308e6bb | ||
|
c4b9534529 | ||
|
c8e63996c8 | ||
|
5d98a0e0eb | ||
|
343a4e3687 | ||
|
feb52328d2 | ||
|
118cc629cf | ||
|
784fecfa02 | ||
|
f6298a3a29 | ||
|
20049dedfe | ||
|
20a5d5f34a | ||
|
b97c443a56 | ||
|
40a0faed5e | ||
|
5feb1343cd | ||
|
dc9315f125 | ||
|
5141eab28a | ||
|
c1166d2d3d | ||
|
de463cca0d | ||
|
02ad030899 | ||
|
e80696dc8e | ||
|
9bfb9b9b67 | ||
|
a0d292a0e8 | ||
|
fe42962eb5 | ||
|
094dcbe2c2 | ||
|
8bbd3fdcf2 | ||
|
e728da78bc | ||
|
9824963f79 | ||
|
cd9efbf703 | ||
|
91e4efcd68 | ||
|
63734fc026 | ||
|
bd90c262f6 | ||
|
d093709f94 | ||
|
f6df613c32 | ||
|
d4e8699825 | ||
|
62abb002dd | ||
|
bd9f2e9700 | ||
|
09190bce3e | ||
|
24386abae3 | ||
|
476c738c13 | ||
|
e924318e18 | ||
|
cfa0d64925 | ||
|
ae797801b1 | ||
|
f826461b06 | ||
|
fb557ab20f | ||
|
fd3f485e63 | ||
|
665c43a2cd | ||
|
2cd91b6bad | ||
|
e064679967 | ||
|
6a23d65fea | ||
|
1b58bed5a6 | ||
|
957e26674a | ||
|
36d3874c4c | ||
|
4bfc2b14f2 | ||
|
f62529d4ff | ||
|
36b5edff29 | ||
|
c8739f64b9 | ||
|
bc95a19c5d | ||
|
0e4b8be1e7 | ||
|
d13e06a5df | ||
|
ff3ee290c9 | ||
|
8cc60f7dd8 | ||
|
292d9a9af3 | ||
|
2a01aec396 | ||
|
17b24d5fd5 | ||
|
2dab3225de | ||
|
95cc1902c0 | ||
|
36f2fd64e0 | ||
|
be492e1778 | ||
|
4c271a57d5 | ||
|
e15f97860f | ||
|
78669323d2 | ||
|
c85b79f5ff | ||
|
801e1e8940 | ||
|
41fefbb001 | ||
|
4eb6ae1553 | ||
|
b5211cb0d2 | ||
|
29df24fa6c | ||
|
e4a8c9f639 | ||
|
2796202d8c | ||
|
46be700cb5 | ||
|
a167d54608 | ||
|
ecb79330c0 | ||
|
3882e405ef | ||
|
5e15ff18f8 | ||
|
28bb98f78b | ||
|
e7c8977423 | ||
|
fff8e490b9 | ||
|
809a16fbed | ||
|
f5dcb9d8be | ||
|
fdd7d30095 | ||
|
c976f6bceb | ||
|
8131923b14 | ||
|
4eac660359 | ||
|
ec5fd6c923 | ||
|
b8540e190d | ||
|
a4c2b67784 | ||
|
b2439f8279 | ||
|
c8fc357f05 | ||
|
92beb4bcdc | ||
|
931c12531d | ||
|
8dba1f4a37 | ||
|
aeaf78a310 | ||
|
ab073f88cd | ||
|
1df1e4f530 | ||
|
81297f5b74 | ||
|
3ddc6250de | ||
|
8fff5aac60 | ||
|
6ccf03e8b3 | ||
|
c0891354ff | ||
|
ea95133ef4 | ||
|
5931d24639 | ||
|
055f74fc61 | ||
|
14fb6be109 | ||
|
b6bfb75af0 | ||
|
7f50f5f175 | ||
|
ea72e56fab | ||
|
055a7568ad | ||
|
c7cd0ef822 | ||
|
f885b38332 | ||
|
cd1a76f919 | ||
|
079c853eba | ||
|
bfaf22964b | ||
|
1bc8064a5b | ||
|
eaf6ff6e20 | ||
|
5577822b36 | ||
|
32fe803221 | ||
|
1412caafbd | ||
|
bdece11192 | ||
|
32952b929c | ||
|
1042a5e25f | ||
|
9774ddade1 | ||
|
76c458228e | ||
|
4fdaf3f427 | ||
|
8334b73296 | ||
|
3e601c6ffe | ||
|
9c35b5ccf2 | ||
|
da9da81be9 | ||
|
36f630fd39 | ||
|
dfd19e5b10 | ||
|
c534b7d364 | ||
|
c35c4b7e97 | ||
|
b2bf154328 | ||
|
9874c14e23 | ||
|
f4f2f65d1d | ||
|
cb3f59e7e7 | ||
|
a3712815fd | ||
|
230f314877 | ||
|
a314f90dda | ||
|
ecc474a264 | ||
|
c7f957194b | ||
|
8c76fd62e0 | ||
|
3a9dd7ed76 | ||
|
165e23b72c | ||
|
84c348ce18 | ||
|
8bdb7104d7 | ||
|
3fbfe40e12 | ||
|
e8baccff16 | ||
|
2745e46ed8 | ||
|
adec211ae1 | ||
|
0a2f7b18d3 | ||
|
f4431cd010 | ||
|
731eeb3a78 | ||
|
ec3348c155 | ||
|
13d4157eb4 | ||
|
c60332e637 | ||
|
31ae5a77c0 | ||
|
32afefbb34 | ||
|
83556f49bd | ||
|
e2f75d4ee5 | ||
|
56f42e0b51 | ||
|
59a267363d | ||
|
c8a59dbb78 | ||
|
d9d72c7a4f | ||
|
05df28a58c | ||
|
b2e1098ab5 | ||
|
b38ab9771c | ||
|
7763f1593b | ||
|
9085c84672 | ||
|
6e8fac54a0 | ||
|
587bd8df32 | ||
|
3568756adb | ||
|
b117d61ce6 | ||
|
a1b852b5bf | ||
|
aeb7870c3f | ||
|
3c6812e438 | ||
|
971396edf6 | ||
|
989daf4091 | ||
|
d4a7aae89c | ||
|
074bd27c18 | ||
|
deeb7d9a8f | ||
|
f5524e0b9c | ||
|
b0e3b05b3c | ||
|
3853c6c377 | ||
|
a1c575d12e | ||
|
89d3b3e0b5 | ||
|
22b992ebe3 | ||
|
2fe42c11c7 | ||
|
33f077ce1b | ||
|
d134ac8485 | ||
|
ac21a96804 | ||
|
de1c1c78e9 | ||
|
df58b74329 | ||
|
1f8b44c575 | ||
|
aaaad9d481 | ||
|
7a531b5f74 | ||
|
f00ac9bfc8 | ||
|
e723aabca8 | ||
|
6c23cad08d | ||
|
91d9964d07 | ||
|
1a32c636ff | ||
|
1d4ac10f3d | ||
|
9b587c6089 | ||
|
ce11203f5c | ||
|
d863864323 | ||
|
e6964dc4b4 | ||
|
bf61002b4c | ||
|
f11c851d97 | ||
|
69ac1a5935 | ||
|
d7fd3a6770 | ||
|
7656080a53 | ||
|
175f7b2f04 | ||
|
d3c92b4aa2 | ||
|
8ee7eb335e | ||
|
c319f6b52c | ||
|
08a2623b8e | ||
|
362a7d0bd6 | ||
|
5e27b3cafb | ||
|
61416cbd40 | ||
|
102cfc349d | ||
|
5dc774a547 | ||
|
f0a43ca0a5 | ||
|
560d07f007 | ||
|
93db092895 | ||
|
0010cd99ff | ||
|
260316398f | ||
|
9b41f7635d | ||
|
8bf358071a | ||
|
1fd6e131ac | ||
|
49d55d6f45 | ||
|
f0bc4fb475 | ||
|
ec9dff343c | ||
|
515e7eb92c | ||
|
7984e7b007 | ||
|
d9188e4463 | ||
|
34231166ae | ||
|
063a3593b8 | ||
|
f229cbe47f | ||
|
747d31bb30 | ||
|
cd6f9880ac | ||
|
3ae7af14be | ||
|
c6c9706855 | ||
|
66595e8172 | ||
|
a4951bbd0d | ||
|
d7e8809cf8 | ||
|
ac3f0d155e | ||
|
66ca933a2f | ||
|
abdfc70c0d | ||
|
6a46d54161 | ||
|
344e74f971 | ||
|
9b3491f9f7 | ||
|
9db8f38743 | ||
|
fe6b68d58b | ||
|
6880d9eb3b | ||
|
cef999f394 | ||
|
0d8296c54c | ||
|
73f629b2fe | ||
|
e55832cae0 | ||
|
1558896125 | ||
|
de4bed7a60 | ||
|
6e335441ab | ||
|
f157f424b5 | ||
|
e5e020f6a3 | ||
|
dd9355c3aa | ||
|
a9b40c7841 | ||
|
6bf296f54d | ||
|
e05de6260b | ||
|
887a9f42dd | ||
|
4e3a5fe180 | ||
|
8d89700cac | ||
|
96217dd16e | ||
|
c3be74d7d6 | ||
|
555a734e21 | ||
|
4aa1d212f1 | ||
|
97a4996546 | ||
|
10abd05b1e | ||
|
85293b3305 | ||
|
be1be19f11 | ||
|
3723fc3b53 | ||
|
4a4b9d6ebd | ||
|
426e9b2581 | ||
|
b13b3c9e65 | ||
|
1a78cb5766 | ||
|
f4068d18bd | ||
|
d1c8a3d93f | ||
|
6ffd60d289 | ||
|
eb0eaf5099 | ||
|
cbf737b917 | ||
|
bf893189ef | ||
|
0903a11dc6 | ||
|
de954a68dd | ||
|
b59a82d6fb | ||
|
d9e9e7b5dd | ||
|
82b3549c5d | ||
|
3f19f95fca | ||
|
4471a16a9d | ||
|
5aecd09331 | ||
|
ec543570b5 | ||
|
5ef87bfd71 | ||
|
9f00ea47f5 | ||
|
41348f2de1 | ||
|
dccb303d6e | ||
|
f4f187d71d | ||
|
60dfae14ea | ||
|
e505174d94 | ||
|
0812a8f7d7 | ||
|
3b073d7eb6 | ||
|
deb2f2f64c | ||
|
d03379d768 | ||
|
550f68024f | ||
|
5782dc1916 | ||
|
8b94703a4c | ||
|
4b231c6855 | ||
|
3673a07229 | ||
|
5871c8744a | ||
|
aa7e000251 | ||
|
67e661f932 | ||
|
5eca507387 | ||
|
bdf901c122 | ||
|
447c6e0c2d | ||
|
ad50ff56ff | ||
|
17ae9c2e18 | ||
|
4f45004710 | ||
|
21cdfe5485 | ||
|
1caddaef4f | ||
|
cee576c4d0 | ||
|
a2022fa0d2 | ||
|
68da5dfb0d | ||
|
9d5e7e7abf | ||
|
6e6c4c6cea | ||
|
632804ce51 | ||
|
56cc7fb6b1 | ||
|
e3d14b2732 | ||
|
93621f7ada | ||
|
46a0f7590c | ||
|
91eaf6d36c | ||
|
fb809ebf74 | ||
|
adbea5a638 | ||
|
a4f5fd4bf0 | ||
|
635a275746 | ||
|
517f2d4b7a | ||
|
194c27fadf | ||
|
96d4d79d1f | ||
|
7d6cdab105 | ||
|
88bd2eb653 | ||
|
8d38b3c6af | ||
|
86af6ab69f | ||
|
e43f06b61d | ||
|
4d5ae295cc | ||
|
f0f1d506c4 | ||
|
b01f784504 | ||
|
6d3216c340 | ||
|
8e4c092192 | ||
|
7d3626a5c0 | ||
|
def72938cd | ||
|
dd4e93c7d4 | ||
|
f30c688775 | ||
|
1ad25a890d | ||
|
88759d29de | ||
|
d71fe3447f | ||
|
02255b40fa | ||
|
5c1286b27b | ||
|
6780518e5f | ||
|
ca15454d5b | ||
|
bd747d1402 | ||
|
01c70d5259 | ||
|
ed0e4b76a8 | ||
|
b4a1a2ec67 | ||
|
8bb09518df | ||
|
583cbe476a | ||
|
be1c08bfa5 | ||
|
707007abba | ||
|
7372922617 | ||
|
42e6894962 | ||
|
926910af08 | ||
|
2f96fa302a | ||
|
a4567df76a | ||
|
6e440b4fa8 | ||
|
4f8c285520 | ||
|
c8fbcf3358 | ||
|
9f7c91e56d | ||
|
61959e0477 | ||
|
3e1c6ac9bd | ||
|
371b27b16a | ||
|
ea79e126f9 | ||
|
6e33bf9921 | ||
|
a2bc6f8bf1 | ||
|
b9446232cd | ||
|
359dc16285 |
28
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug encountered
|
||||
|
||||
---
|
||||
<!-- Please use this template while reporting a bug and provide as much info as possible. Not doing so may result in your bug not being addressed in a timely manner. Thanks!-->
|
||||
|
||||
|
||||
**What happend**:
|
||||
|
||||
**What you expected to happen**:
|
||||
|
||||
**How to reproduce it (as minimally and precisely as possible)**:
|
||||
|
||||
**Anything else we need to know?**:
|
||||
|
||||
**Environment**:
|
||||
|
||||
- Multus version
|
||||
image path and image ID (from 'docker images')
|
||||
- Kubernetes version (use `kubectl version`):
|
||||
- Primary CNI for Kubernetes cluster:
|
||||
- OS (e.g. from /etc/os-release):
|
||||
- File of '/etc/cni/net.d/'
|
||||
- File of '/etc/cni/multus/net.d'
|
||||
- NetworkAttachment info (use `kubectl get net-attach-def -o yaml`)
|
||||
- Target pod yaml info (with annotation, use `kubectl get pod <podname> -o yaml`)
|
||||
- Other log outputs (if you use multus logging)
|
10
.github/ISSUE_TEMPLATE/enhancement.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Enhancement Request
|
||||
about: Suggest an enhancement to multus
|
||||
|
||||
---
|
||||
<!-- Please only use this template for submitting enhancement requests -->
|
||||
|
||||
**What would you like to be added**:
|
||||
|
||||
**Why is this needed**:
|
5
.github/ISSUE_TEMPLATE/support.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
name: Support Request
|
||||
about: Support request or question relating to multus-cni
|
||||
|
||||
---
|
24
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
on: [push, pull_request]
|
||||
name: Build
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22.x, 1.23.x]
|
||||
goarch: [386, amd64, arm, arm64, ppc64le, s390x]
|
||||
os: [ubuntu-latest] #, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
GOOS: ${{ matrix.goos }}
|
||||
run: ./hack/build-go.sh
|
41
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: "46 8 * * 0"
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ go ]
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
98
.github/workflows/image-build.yml
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
name: Image build
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
build-thin:
|
||||
name: Image build thin plugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# note: disable sbom/provenance for now (gchr.io does not managed well yet)
|
||||
- name: Build container image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: ghcr.io/${{ github.repository }}:latest
|
||||
file: images/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
# note: disable sbom/provenance for now (gchr.io does not managed well yet)
|
||||
- name: Build container debug image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: ghcr.io/${{ github.repository }}:latest
|
||||
file: images/Dockerfile.debug
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
build-thick:
|
||||
name: Image thick plugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build container image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: ghcr.io/${{ github.repository }}:latest-thick
|
||||
file: images/Dockerfile.thick
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
- name: Run Trivy vulnerability scanner
|
||||
uses: aquasecurity/trivy-action@0.29.0
|
||||
with:
|
||||
image-ref: ghcr.io/${{ github.repository }}:latest-thick
|
||||
ignore-unfixed: true
|
||||
vuln-type: 'os,library'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
format: 'sarif'
|
||||
output: 'trivy-results.sarif'
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
if: always()
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
|
||||
build-origin:
|
||||
name: Image build/origin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Download OKD Builder Dockerfile
|
||||
run: curl https://raw.githubusercontent.com/okd-project/images/main/builder/Dockerfile -o images/okd-builder.Dockerfile
|
||||
|
||||
- name: Patch OKD Builder Dockerfile to workaround error
|
||||
run: sed -i -e "s/yum install -y yum-utils/rpm --import \/etc\/pki\/rpm-gpg\/*;yum install -y yum-utils/" images/okd-builder.Dockerfile
|
||||
|
||||
- name: Create root for builder
|
||||
run: mkdir root
|
||||
|
||||
- name: Organically build golang builder image
|
||||
run: docker build -t local/okdbuilder:latest -f images/okd-builder.Dockerfile .
|
||||
|
||||
- name: Organically build Multus origin image
|
||||
run: docker build -t local/multus-cni:latest-origin -f images/Dockerfile.openshift .
|
116
.github/workflows/image-push-master.yml
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
name: Image push for master
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
env:
|
||||
image-push-owner: 'k8snetworkplumbingwg'
|
||||
jobs:
|
||||
push-thick:
|
||||
name: Image push thick image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Push container image for thick plugin
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:latest-thick
|
||||
ghcr.io/${{ github.repository }}:snapshot-thick
|
||||
file: images/Dockerfile.thick
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
push-thin:
|
||||
name: Image push thin image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Push thin container image
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:latest
|
||||
ghcr.io/${{ github.repository }}:snapshot
|
||||
file: images/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
- name: Push thin container debug image
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:latest-debug
|
||||
ghcr.io/${{ github.repository }}:snapshot-debug
|
||||
file: images/Dockerfile.debug
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
# TODO: need to fix this action
|
||||
# push-origin:
|
||||
# name: Image push/origin
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Check out code into the Go module directory
|
||||
# uses: actions/checkout@v4
|
||||
#
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v3
|
||||
#
|
||||
# - name: Login to GitHub Container Registry
|
||||
# if: github.repository_owner == 'k8snetworkplumbingwg'
|
||||
# uses: docker/login-action@v3
|
||||
# with:
|
||||
# registry: ghcr.io
|
||||
# username: ${{ github.repository_owner }}
|
||||
# password: ${{ secrets.GITHUB_TOKEN }}
|
||||
#
|
||||
# - name: Push container image
|
||||
# if: github.repository_owner == 'k8snetworkplumbingwg'
|
||||
# uses: docker/build-push-action@v5
|
||||
# with:
|
||||
# context: .
|
||||
# push: true
|
||||
# tags: |
|
||||
# ghcr.io/${{ github.repository }}:latest-origin
|
||||
# ghcr.io/${{ github.repository }}:snapshot-origin
|
||||
# file: images/Dockerfile.openshift
|
||||
|
138
.github/workflows/image-push-release.yml
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
name: Image push release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
env:
|
||||
image-push-owner: 'k8snetworkplumbingwg'
|
||||
jobs:
|
||||
push-thick:
|
||||
name: Image push thick image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
flavor: |
|
||||
latest=false
|
||||
|
||||
- name: Push container image for thick plugin
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:stable-thick
|
||||
${{ steps.docker_meta.outputs.tags }}-thick
|
||||
file: images/Dockerfile.thick
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
push-thin:
|
||||
name: Image push thin image/amd64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
flavor: |
|
||||
latest=false
|
||||
|
||||
- name: Push thin container image
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:stable
|
||||
${{ steps.docker_meta.outputs.tags }}
|
||||
file: images/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
- name: Push thin container debug image
|
||||
if: ${{ github.repository_owner == env.image-push-owner }}
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:stable-debug
|
||||
${{ steps.docker_meta.outputs.tags }}-debug
|
||||
file: images/Dockerfile.debug
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v8,linux/ppc64le,linux/s390x
|
||||
sbom: false
|
||||
provenance: false
|
||||
|
||||
# TODO: need to fix this action
|
||||
# push-origin:
|
||||
# name: Image push/origin
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: Check out code into the Go module directory
|
||||
# uses: actions/checkout@v4
|
||||
#
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v3
|
||||
#
|
||||
# - name: Login to GitHub Container Registry
|
||||
# if: github.repository_owner == 'k8snetworkplumbingwg'
|
||||
# uses: docker/login-action@v3
|
||||
# with:
|
||||
# registry: ghcr.io
|
||||
# username: ${{ github.repository_owner }}
|
||||
# password: ${{ secrets.GITHUB_TOKEN }}
|
||||
#
|
||||
# - name: Docker meta
|
||||
# id: docker_meta
|
||||
# uses: crazy-max/ghaction-docker-meta@v1
|
||||
# with:
|
||||
# images: ghcr.io/${{ github.repository }}
|
||||
# tag-latest: false
|
||||
#
|
||||
# - name: Push container image
|
||||
# if: github.repository_owner == 'k8snetworkplumbingwg'
|
||||
# uses: docker/build-push-action@v5
|
||||
# with:
|
||||
# context: .
|
||||
# push: true
|
||||
# tags: |
|
||||
# ghcr.io/${{ github.repository }}:stable-origin
|
||||
# ${{ steps.docker_meta.outputs.tags }}-origin
|
||||
# file: images/Dockerfile.openshift
|
117
.github/workflows/kind-e2e.yml
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
name: e2e-kind
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
e2e-kind:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- docker-file: images/Dockerfile.thick
|
||||
cni-version: "0.3.1"
|
||||
multus-manifest: multus-daemonset-thick.yml
|
||||
- docker-file: images/Dockerfile
|
||||
cni-version: "0.3.1"
|
||||
multus-manifest: multus-daemonset.yml
|
||||
- docker-file: images/Dockerfile.thick
|
||||
cni-version: "0.4.0"
|
||||
multus-manifest: multus-daemonset-thick.yml
|
||||
- docker-file: images/Dockerfile
|
||||
cni-version: "0.4.0"
|
||||
multus-manifest: multus-daemonset.yml
|
||||
# need to wait kind to support CNI 1.0.0 (now kind 0.11 supports up to 0.4.0)
|
||||
# - docker-file: images/Dockerfile.thick
|
||||
# cni-version: "1.0.0"
|
||||
# multus-manifest: multus-thick-daemonset.yml
|
||||
# - docker-file: images/Dockerfile
|
||||
# cni-version: "1.0.0"
|
||||
# multus-manifest: multus-daemonset.yml
|
||||
env:
|
||||
JOB_NAME: "${{ matrix.cni-version }}-${{ matrix.multus-manifest }}"
|
||||
|
||||
if: >
|
||||
(( github.event.pull_request.head.repo.owner.login != github.event.pull_request.base.repo.owner.login ) &&
|
||||
github.event_name == 'pull_request' ) || (github.event_name == 'push' && github.event.commits != '[]' )
|
||||
steps:
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Setup j2cli
|
||||
run: |
|
||||
sudo apt-get install -y j2cli
|
||||
echo $(j2 --version)
|
||||
|
||||
- name: Build latest-amd64
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
load: true
|
||||
tags: localhost:5000/multus:e2e
|
||||
file: ${{ matrix.docker-file }}
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Get kind/kubectl/koko
|
||||
working-directory: ./e2e
|
||||
run: ./get_tools.sh
|
||||
|
||||
- name: generate yaml files
|
||||
working-directory: ./e2e
|
||||
run: env CNI_VERSION=${{ matrix.cni-version }} ./generate_yamls.sh
|
||||
|
||||
- name: Setup cluster
|
||||
working-directory: ./e2e
|
||||
run: MULTUS_MANIFEST=${{ matrix.multus-manifest }} MULTUS_DOCKERFILE=none ./setup_cluster.sh
|
||||
|
||||
- name: Test simple pod
|
||||
working-directory: ./e2e
|
||||
run: ./test-simple-pod.sh
|
||||
|
||||
- name: Test macvlan1
|
||||
working-directory: ./e2e
|
||||
run: ./test-simple-macvlan1.sh
|
||||
|
||||
- name: Test static pod
|
||||
working-directory: ./e2e
|
||||
run: ./test-static-pod.sh
|
||||
|
||||
- name: Test default route1
|
||||
working-directory: ./e2e
|
||||
run: ./test-default-route1.sh
|
||||
|
||||
# - name: Test DRA integration
|
||||
# working-directory: ./e2e
|
||||
# run: ./test-dra-integration.sh
|
||||
|
||||
- name: Test subdirectory CNI chaining
|
||||
if: ${{ matrix.multus-manifest == 'multus-daemonset-thick.yml' }}
|
||||
working-directory: ./e2e
|
||||
run: ./test-subdirectory-chaining.sh
|
||||
|
||||
- name: Test subdirectory CNI chaining with passthru CNI / auxiliaryCNIChainName
|
||||
if: ${{ matrix.multus-manifest == 'multus-daemonset-thick.yml' }}
|
||||
working-directory: ./e2e
|
||||
run: ./test-subdirectory-chaining-passthru.sh
|
||||
|
||||
- name: Export kind logs
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p /tmp/kind/logs
|
||||
kind export logs /tmp/kind/logs -v 2147483647
|
||||
|
||||
- name: Upload kind logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }}
|
||||
path: /tmp/kind/logs
|
||||
|
||||
- name: cleanup cluster and registry
|
||||
run: |
|
||||
kind delete cluster
|
26
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Release binaries
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
15
.github/workflows/stale-issues-prs.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: 'Close stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
||||
stale-pr-message: 'This pull request is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
48
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
on: [push, pull_request]
|
||||
name: Test
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.22.x, 1.23.x]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Revive Action by pulling pre-built image
|
||||
uses: docker://morphy/revive-action:v2
|
||||
with:
|
||||
exclude: "./vendor/..."
|
||||
|
||||
- name: Run go fmt
|
||||
run: go fmt ./...
|
||||
#run: diff -u <(echo -n) <(gofmt -d -s .)
|
||||
|
||||
- name: Run go vet
|
||||
run: go vet ./...
|
||||
|
||||
- name: Test
|
||||
run: sudo ./hack/test-go.sh
|
||||
|
||||
- name: Send coverage
|
||||
uses: shogo82148/actions-goveralls@v1
|
||||
with:
|
||||
path-to-profile: coverage.out
|
||||
flag-name: Go-${{ matrix.go }}
|
||||
parallel: true
|
||||
|
||||
# notifies that all test jobs are finished.
|
||||
finish:
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: shogo82148/actions-goveralls@v1
|
||||
with:
|
||||
parallel-finished: true
|
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Binary output dir
|
||||
bin/
|
||||
e2e/bin/
|
||||
e2e/yamls/
|
||||
e2e/repos/
|
||||
|
||||
# GOPATH created by the build script
|
||||
gopath/
|
||||
|
||||
# Editor paths
|
||||
.swp*
|
||||
.swo*
|
||||
.idea*
|
||||
|
||||
# Test outputs
|
||||
*.out
|
||||
*.test
|
63
.goreleaser.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
id: multus
|
||||
binary: multus
|
||||
main: ./cmd/multus
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- s390x
|
||||
ldflags:
|
||||
- -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.version={{ .Tag }} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.commit={{ .Commit }} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.date={{ .Date }}
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
id: multus-daemon
|
||||
binary: multus-daemon
|
||||
main: ./cmd/multus-daemon
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- s390x
|
||||
ldflags:
|
||||
- -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.version={{ .Tag }} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.commit={{ .Commit }} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.date={{ .Date }}
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
id: multus-shim
|
||||
binary: multus-shim
|
||||
main: ./cmd/multus-shim
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
- arm
|
||||
- arm64
|
||||
- s390x
|
||||
ldflags:
|
||||
- -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.version={{ .Tag }} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.commit={{ .Commit }} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.date={{ .Date }}
|
||||
archives:
|
||||
- wrap_in_directory: true
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-snapshot"
|
||||
#release:
|
||||
# draft: true
|
||||
changelog:
|
||||
skip: true
|
55
.travis.yml
@@ -1,55 +0,0 @@
|
||||
language: go
|
||||
# see https://docs.travis-ci.com/user/reference/overview/#Virtualization-environments
|
||||
# for the detail
|
||||
# sudo: requried
|
||||
dist: trusty
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
|
||||
install:
|
||||
# workaround golint install error in https://github.com/golang/lint/issues/288
|
||||
- mkdir -p $GOPATH/src/golang.org/x
|
||||
- pushd $GOPATH/src/golang.org/x
|
||||
- git clone https://github.com/golang/tools.git
|
||||
- git clone https://github.com/golang/lint.git
|
||||
- go get github.com/golang/lint/golint
|
||||
- popd
|
||||
|
||||
before_script:
|
||||
- golint ./multus/... | grep -v ALL_CAPS | xargs -r false
|
||||
- go fmt ./multus/...
|
||||
- go vet ./multus/...
|
||||
# - gocyclo -over 15 ./multus
|
||||
|
||||
script:
|
||||
- ./build
|
||||
- sudo ./test.sh
|
||||
- mkdir -p ${TRAVIS_BUILD_DIR}/dist
|
||||
- tar cvfz ${TRAVIS_BUILD_DIR}/dist/multus-cni_amd64.tar.gz --warning=no-file-changed --exclude="dist" --exclude="vendor" .
|
||||
|
||||
before_deploy:
|
||||
- go get -u github.com/laher/goxc
|
||||
- mkdir -p $TRAVIS_BUILD_DIR/dist
|
||||
- goxc -d=$TRAVIS_BUILD_DIR/dist -pv=$TRAVIS_TAG -bc=linux -tasks=clean-destination,xc,archive,rmbin
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: "iy7eqzXNvb/juc+5eVPQ/pFYDTCqDt8Zjt63n+zEK856Qzr2aEZwwOguMWs78XFDMFXagCs5PRTvtvZz8apoTfHX7Wkss3kRyEziAkuldQbH5yGDvpGyHsGBw78N95hauMoogefE7NuuLG3qRSWPeVz8RAKGhP7ADwEVyyfQKKYdum3Bqrz0D89HqKbCQqs3eZae7ppDIler3lab9WAQGuKNJ2HL6mqREVe48kb8sdsuSr+yV4qwVrBDNhXxQDxAT6LYuMXbknE7qTde2vViP13ZHpptbuZqiZG2ytzReIIs/iC9AWoIQXr3XTXl9z8fqlC3VljPCikBWVcmxDFA2aANYzx3M/7fMOO/DniwNhlZc9+pYfAkUrpoQPfPOWNqf45Qz0jP3wk49xy5hxEqe/rfmo5lipSsqeUsk+j3pT8kjVIAnDLrQpxSx7xwnijPLgtm34UwROVowfwLlOhE/7mUOFCbYlzEo3CKvjDN3Kmn35yHEueuu//Gv5jesVYvgcNPBHqaTKb5AXVTqymNBtA43PchLJ8gCC1mNukzSZifQP996vzbV5c9AxzBLjWbiDJ3lOFIpNhF8Sed0m0C0RylrTXHTX5TSrlMdXXffzYwbjJ96J+cFPBTpJNfSn+3N7hiart1r1k1bSXoPqYW4+94M8E1eZ5LjszoeiZbRrI="
|
||||
file_glob: true
|
||||
file: "$TRAVIS_BUILD_DIR/dist/*/*.gz"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
all_branches: true
|
||||
condition: "$TRAVIS_TAG =~ ^v[0-9].*$"
|
||||
|
||||
after_success:
|
||||
# put build tgz to bintray
|
||||
- curl -T ${TRAVIS_BUILD_DIR}/dist/multus-cni_amd64.tar.gz -u${BINTRAY_USER}:${BINTRAY_APIKEY} https://api.bintray.com/content/redhat-nfvpe/multus-cni-crd-snapshots/snapshot/snapshot-${TRAVIS_COMMIT}/multus-cni_amd64-${TRAVIS_COMMIT}.tar.gz
|
||||
# publish uploaded file
|
||||
- curl -X POST -u${BINTRAY_USER}:${BINTRAY_APIKEY} https://api.bintray.com/content/redhat-nfvpe/multus-cni-crd-snapshots/snapshot/snapshot-${TRAVIS_COMMIT}/publish
|
||||
# put it in bintray download list
|
||||
- sleep 20
|
||||
- "curl -X PUT -H 'Accept: application/json' -H 'Content-type: application/json' -u${BINTRAY_USER}:${BINTRAY_APIKEY} https://api.bintray.com/file_metadata/redhat-nfvpe/multus-cni-crd-snapshots/multus-cni_amd64-${TRAVIS_COMMIT}.tar.gz -d '{\"list_in_downloads\":true}'"
|
130
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Multus CNI Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[The Multus Slack Page](https://intel-corp.herokuapp.com/).
|
||||
All complaints will be reviewed and investigated promptly and fairly. Or you
|
||||
may specifically contact Doug Smith (dosmith@redhat.com) via email.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
2
NOTICE
Normal file
@@ -0,0 +1,2 @@
|
||||
Copyright 2016 Intel Corporation
|
||||
Copyright 2021 Multus Authors
|
568
README.md
@@ -1,558 +1,66 @@
|
||||

|
||||
# Multus-CNI
|
||||
|
||||
* [MULTUS CNI plugin](#multus-cni-plugin)
|
||||
* [Multi-Homed pod](#multi-homed-pod)
|
||||
* [Build](#build)
|
||||
* [Work flow](#work-flow)
|
||||
* [Usage with Kubernetes CRD based network objects](#usage-with-kubernetes-crd-based-network-objects)
|
||||
* [Creating "Network" resources in Kubernetes](#creating-network-resources-in-kubernetes)
|
||||
* [<strong>CRD based Network objects</strong>](#crd-based-network-objects)
|
||||
* [Creating network resources in Kubernetes](#creating-network-resources-in-kubernetes-1)
|
||||
* [Configuring Multus to use the kubeconfig](#configuring-multus-to-use-the-kubeconfig)
|
||||
* [Configuring Multus to use kubeconfig and a default network](#configuring-multus-to-use-kubeconfig-and-a-default-network)
|
||||
* [Configuring Pod to use the CRD network objects](#configuring-pod-to-use-the-crd-network-objects)
|
||||
* [Verifying Pod network interfaces](#verifying-pod-network-interfaces)
|
||||
* [Using with Multus conf file](#using-with-multus-conf-file)
|
||||
* [Testing Multus CNI](#testing-multus-cni)
|
||||
* [Multiple flannel networks](#multiple-flannel-networks)
|
||||
* [Configure Kubernetes with CNI](#configure-kubernetes-with-cni)
|
||||
* [Launching workloads in Kubernetes](#launching-workloads-in-kubernetes)
|
||||
* [Multus additional plugins](#multus-additional-plugins)
|
||||
* [NFV based networking in Kubernetes](#nfv-based-networking-in-kubernetes)
|
||||
* [Need help](#need-help)
|
||||
* [Contacts](#contacts)
|
||||

|
||||
|
||||
# MULTUS CNI plugin
|
||||
- _Multus_ is a latin word for "Multi"
|
||||
- As the name suggests, it acts as a Multi plugin in Kubernetes and provides the multiple network interface support in a pod
|
||||
- Multus supports all [reference plugins](https://github.com/containernetworking/plugins) (eg. [Flannel](https://github.com/containernetworking/plugins/tree/master/plugins/meta/flannel), [DHCP](https://github.com/containernetworking/plugins/tree/master/plugins/ipam/dhcp), [Macvlan](https://github.com/containernetworking/plugins/tree/master/plugins/main/macvlan)) that implement the CNI specification and all 3rd party plugins (eg. [Calico](https://github.com/projectcalico/cni-plugin), [Weave](https://github.com/weaveworks/weave), [Cilium](https://github.com/cilium/cilium), [Contiv](https://github.com/contiv/netplugin)). In addition to it, Multus supports [SRIOV](https://github.com/hustcat/sriov-cni), [SRIOV-DPDK](https://github.com/Intel-Corp/sriov-cni), [OVS-DPDK & VPP](https://github.com/intel/vhost-user-net-plugin) workloads in Kubernetes with both cloud native and NFV based applications in Kubernetes
|
||||
- It is a contact between the container runtime and other plugins, and it doesn't have any of its own net configuration, it calls other plugins like flannel/calico to do the real net conf job.
|
||||
- Multus reuses the concept of invoking delegates as used in flannel by grouping multiple plugins into delegates and invoking them in the sequential order of the CNI configuration file provided in json format
|
||||
- The default network gets "eth0" and additional network Pod interface name as “net0”, “net1”,… “netX and so on. Multus also support interface names from the user.
|
||||
- Multus is one of the projects in the [Baremetal Container Experience kit](https://networkbuilders.intel.com/network-technologies/container-experience-kits).
|
||||
[](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/build.yml)[](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/test.yml)[](https://goreportcard.com/report/github.com/k8snetworkplumbingwg/multus-cni)[](https://coveralls.io/github/k8snetworkplumbingwg/multus-cni)
|
||||
|
||||
Please check the [CNI](https://github.com/containernetworking/cni) documentation for more information on container networking.
|
||||
Multus CNI enables attaching multiple network interfaces to pods in Kubernetes.
|
||||
|
||||
# Kubernetes Network Custom Resource Definition De-facto Standard - Reference implementation
|
||||
## How it works
|
||||
|
||||
* This project is a reference implementation for Kubernetes Network Custom Resource Definition De-facto Standard. For more information refer [Network Plumbing Working Group Agenda](https://docs.google.com/document/d/1oE93V3SgOGWJ4O1zeD1UmpeToa0ZiiO6LqRAmZBPFWM/edit)
|
||||
* Kubernetes Network Custom Resource Definition De-facto Standard [documentation link](https://docs.google.com/document/d/1Ny03h6IDVy_e_vmElOqR7UdTPAG_RNydhVE1Kx54kFQ/edit)
|
||||
* Reference implementation support following modes
|
||||
* CNI config JSON in network object
|
||||
* Not using CNI config (“thick” plugin usecase)
|
||||
* CNI configuration stored in on-disk file
|
||||
> refer the section 3.2 Network Object Definition for more details in Kubernetes Network Custom Resource Definition De-facto Standard
|
||||
* Refer the reference implemenation presentation and demo details - [link](https://docs.google.com/presentation/d/1dbCin6MnhK-BjjcVun5YiPTL99VA2uSiyWAtWAPNlIc/edit?usp=sharing)
|
||||
* Release version from v2.0 is not compatible with v1.1 and v1.2 network CRD specifications.
|
||||
Multus CNI is a container network interface (CNI) plugin for Kubernetes that enables attaching multiple network interfaces to pods. Typically, in Kubernetes each pod only has one network interface (apart from a loopback) -- with Multus you can create a multi-homed pod that has multiple interfaces. This is accomplished by Multus acting as a "meta-plugin", a CNI plugin that can call multiple other CNI plugins.
|
||||
|
||||
## Multi-Homed pod
|
||||
<p align="center">
|
||||
<img src="doc/images/multus_cni_pod.png" width="1008" />
|
||||
</p>
|
||||
Multus CNI follows the [Kubernetes Network Custom Resource Definition De-facto Standard](https://docs.google.com/document/d/1Ny03h6IDVy_e_vmElOqR7UdTPAG_RNydhVE1Kx54kFQ/edit) to provide a standardized method by which to specify the configurations for additional network interfaces. This standard is put forward by the Kubernetes [Network Plumbing Working Group](https://docs.google.com/document/d/1oE93V3SgOGWJ4O1zeD1UmpeToa0ZiiO6LqRAmZBPFWM/edit).
|
||||
|
||||
## Build
|
||||
Multus is one of the projects in the [Baremetal Container Experience kit](https://networkbuilders.intel.com/network-technologies/container-experience-kits)
|
||||
|
||||
**This plugin requires Go 1.8 to build.**
|
||||
### Multi-Homed pod
|
||||
|
||||
Go 1.5 users will need to set GO15VENDOREXPERIMENT=1 to get vendored dependencies. This flag is set by default in 1.6.
|
||||
```
|
||||
#./build
|
||||
```
|
||||
## Work flow
|
||||
<p align="center">
|
||||
<img src="doc/images/workflow.png" width="1008" />
|
||||
</p>
|
||||
## Network configuration reference
|
||||
Here's an illustration of the network interfaces attached to a pod, as provisioned by Multus CNI. The diagram shows the pod with three interfaces: `eth0`, `net0` and `net1`. `eth0` connects kubernetes cluster network to connect with kubernetes server/services (e.g. kubernetes api-server, kubelet and so on). `net0` and `net1` are additional network attachments and connect to other networks by using [other CNI plugins](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) (e.g. vlan/vxlan/ptp).
|
||||
|
||||
- name (string, required): the name of the network
|
||||
- type (string, required): "multus"
|
||||
- kubeconfig (string, optional): kubeconfig file for the out of cluster communication with kube-apiserver. See the example [kubeconfig](https://github.com/intel/multus-cni/blob/master/doc/node-kubeconfig.yaml)
|
||||
- delegates (([]map,required): number of delegate details in the Multus
|
||||

|
||||
|
||||
## Usage with Kubernetes CRD based network objects
|
||||
## Quickstart Installation Guide
|
||||
|
||||
Kubelet is responsible for establishing network interfaces for pods; it does this by invoking its configured CNI plugin. When Multus is invoked it retrieves network references from Pod annotation. Multus then uses these network references to get network configurations. Network configurations are defined as Kubernetes Custom Resource Object (CRD). These configurations describe which CNI plugins to invoke and what their configurations are. The order of plugin invocation is important as it identifies the primary plugin. This order is taken from network object references given in a Pod spec.
|
||||
The quickstart installation method for Multus requires that you have first installed a Kubernetes CNI plugin to serve as your pod-to-pod network, which we refer to as your "default network" (a network interface that every pod will be created with). Each network attachment created by Multus will be in addition to this default network interface. For more detail on installing a default network CNI plugin, refer to our [quick-start guide](docs/quickstart.md).
|
||||
|
||||
<p align="center">
|
||||
<img src="doc/images/multus_crd_usage_diagram.JPG" width="1008" />
|
||||
</p>
|
||||
|
||||
### Creating "Network" resources in Kubernetes
|
||||
|
||||
Multus is compatible to work with both CRD and TPR(deprecated in K8s 1.7).
|
||||
|
||||
##### **CRD based Network objects**
|
||||
|
||||
1. Create a Custom Resource Definition "crdnetwork.yaml" for the network object as shown below:
|
||||
To use latest features try command below which applies a daemonset and installs thick Multus using `kubectl`:
|
||||
|
||||
```
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
# name must match the spec fields below, and be in the form: <plural>.<group>
|
||||
name: networks.kubernetes.cni.cncf.io
|
||||
spec:
|
||||
# group name to use for REST API: /apis/<group>/<version>
|
||||
group: kubernetes.cni.cncf.io
|
||||
# version name to use for REST API: /apis/<group>/<version>
|
||||
version: v1
|
||||
# either Namespaced or Cluster
|
||||
scope: Namespaced
|
||||
names:
|
||||
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
|
||||
plural: networks
|
||||
# singular name to be used as an alias on the CLI and for display
|
||||
singular: network
|
||||
# kind is normally the CamelCased singular type. Your resource manifests use this.
|
||||
kind: Network
|
||||
# shortNames allow shorter string to match your resource on the CLI
|
||||
shortNames:
|
||||
- net
|
||||
```
|
||||
2. Run kubectl create command for the Custom Resource Definition
|
||||
```
|
||||
# kubectl create -f ./crdnetwork.yaml
|
||||
customresourcedefinition "networks.kubernetes.cni.cncf.io" created
|
||||
```
|
||||
3. Run kubectl get command to check the Network CRD creation
|
||||
```
|
||||
# kubectl get CustomResourceDefinition
|
||||
NAME KIND
|
||||
networks.kubernetes.cni.cncf.io CustomResourceDefinition.v1beta1.apiextensions.k8s.io
|
||||
```
|
||||
For Kubernetes v1.7 and above use CRD to create network object. For version older than 1.7 use TPR based objects as shown below:
|
||||
|
||||
Note: Both TPR and CRD will have same selfLink :
|
||||
|
||||
*/apis/kubernetes.cni.cncf.io/v1/namespaces/default/networks/*
|
||||
|
||||
|
||||
#### TPR based Network objects
|
||||
|
||||
1. Create a Third Party Resource "tprnetwork.yaml" for the network object as shown below:
|
||||
|
||||
```
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: ThirdPartyResource
|
||||
metadata:
|
||||
name: network.kubernetes.cni.cncf.io
|
||||
description: "A specification of a Network obj in the kubernetes"
|
||||
versions:
|
||||
- name: v1
|
||||
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset-thick.yml
|
||||
```
|
||||
|
||||
2. Run kubectl create command for the Third Party Resource
|
||||
This will configure your systems to be ready to use Multus CNI, but, to get started with adding additional interfaces to your pods, refer to our complete [quick-start guide](docs/quickstart.md)
|
||||
|
||||
## Thin Plugin v.s Thick Plugin
|
||||
|
||||
With the multus 4.0 release, we introduce a new client/server-style plugin deployment. This new deployment is called ['thick plugin'](docs/thick-plugin.md), in contrast to deployments in previous versions, which is now called a 'thin plugin'. The new thick plugin consists of two binaries, multus-daemon and multus-shim CNI plugin. The 'multus-daemon' will be deployed to all nodes as a local agent and supports additional features, such as metrics, which were not available with the 'thin plugin' deployment before. Due to these additional features, the 'thick plugin' comes with the trade-off of consuming more resources than the 'thin plugin'.
|
||||
|
||||
We recommend using the thick plugin in most environments, but if you wish to run the thin plugin, or are in a resource-constrained environment, you may do so with:
|
||||
|
||||
```
|
||||
# kubectl create -f ./tprnetwork.yaml
|
||||
thirdpartyresource "network.kubernetes.cni.cncf.io" created
|
||||
```
|
||||
3. Run kubectl get command to check the Network TPR creation
|
||||
```
|
||||
# kubectl get thirdpartyresource
|
||||
NAME DESCRIPTION VERSION(S)
|
||||
network.kubernetes.cni.cncf.io A specification of a Network obj in the kubernetes v1
|
||||
```
|
||||
### Creating network resources in Kubernetes
|
||||
|
||||
1. After creating CRD network object you can create network resources in Kubernetes. These network resources may contain additional underlying CNI plugin parameters given in JSON format. In the following example shown below the args field contains parameters that will be passed into “flannel” plugin.
|
||||
|
||||
2. Save the following YAML to flannel-network.yaml
|
||||
|
||||
```
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: flannel-networkobj
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "flannel",
|
||||
"delegate": {
|
||||
"isDefaultGateway": true
|
||||
}
|
||||
}'
|
||||
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml
|
||||
```
|
||||
|
||||
3. Create the custom resource definition
|
||||
## Additional Installation Options
|
||||
|
||||
```
|
||||
# kubectl create -f customCRD/flannel-network.yaml
|
||||
network "flannel-networkobj" created
|
||||
```
|
||||
```
|
||||
# kubectl get network
|
||||
NAME AGE
|
||||
flannel-networkobj 26s
|
||||
```
|
||||
4. Get the custom network object details
|
||||
```
|
||||
apiVersion: kubernetes.cni.cncf.io/v1
|
||||
kind: Network
|
||||
metadata:
|
||||
clusterName: ""
|
||||
creationTimestamp: 2018-05-17T09:13:20Z
|
||||
deletionGracePeriodSeconds: null
|
||||
deletionTimestamp: null
|
||||
initializers: null
|
||||
name: flannel-networkobj
|
||||
namespace: default
|
||||
resourceVersion: "21176114"
|
||||
selfLink: /apis/kubernetes.cni.cncf.io/v1/namespaces/default/networks/flannel-networkobj
|
||||
uid: 8ac8f873-59b2-11e8-8308-a4bf01024e6f
|
||||
spec:
|
||||
config: '{ "cniVersion": "0.3.0", "type": "flannel", "delegate": { "isDefaultGateway":
|
||||
true } }'
|
||||
```
|
||||
5. Save the following YAML to sriov-network.yaml to creating sriov network object. ( Refer to [Intel - SR-IOV CNI](https://github.com/Intel-Corp/sriov-cni) or contact @kural in [Intel-Corp Slack](https://intel-corp.herokuapp.com/) for running the DPDK based workloads in Kubernetes)
|
||||
```
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: sriov-conf
|
||||
spec:
|
||||
config: '{
|
||||
"type": "sriov",
|
||||
"if0": "enp12s0f1",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.56.217.0/24",
|
||||
"rangeStart": "10.56.217.171",
|
||||
"rangeEnd": "10.56.217.181",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
],
|
||||
"gateway": "10.56.217.1"
|
||||
}
|
||||
}'
|
||||
```
|
||||
6. Likewise save the following YAML to sriov-vlanid-l2enable-network.yaml to create another sriov based network object:
|
||||
In addition to the [quick-start guide](docs/quickstart.md), you may:
|
||||
|
||||
```
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: sriov-vlanid-l2enable-conf
|
||||
spec:
|
||||
config: '{
|
||||
"type": "sriov",
|
||||
"if0": "enp2s0",
|
||||
"vlan": 210,
|
||||
"l2enable": true
|
||||
}'
|
||||
```
|
||||
7. Follow step 3 above to create "sriov-vlanid-l2enable-conf" and "sriov-conf" network objects
|
||||
8. View network objects using kubectl
|
||||
```
|
||||
# kubectl get network
|
||||
NAME AGE
|
||||
flannel-networkobj 29m
|
||||
sriov-conf 6m
|
||||
sriov-vlanid-l2enable-conf 2m
|
||||
- Download binaries from [release page](https://github.com/k8snetworkplumbingwg/multus-cni/releases)
|
||||
- By Docker image from [GitHub Container Registry](https://github.com/orgs/k8snetworkplumbingwg/packages/container/package/multus-cni)
|
||||
- Or, roll-your-own and build from source
|
||||
- See [Development](docs/development.md)
|
||||
|
||||
```
|
||||
### Configuring Multus to use the kubeconfig
|
||||
1. Create a Mutlus CNI configuration file on each Kubernetes node. This file should be created in: /etc/cni/net.d/multus-cni.conf with the content shown below. Use only the absolute path to point to the kubeconfig file (as it may change depending upon your cluster env). We are assuming all CNI plugin binaries are default location (`\opt\cni\bin dir`)
|
||||
```
|
||||
{
|
||||
"name": "node-cni-network",
|
||||
"type": "multus",
|
||||
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml"
|
||||
}
|
||||
```
|
||||
2. Restart kubelet service
|
||||
```
|
||||
# systemctl restart kubelet
|
||||
```
|
||||
### Configuring Multus to use kubeconfig and a default network
|
||||
## Comprehensive Documentation
|
||||
|
||||
1. Many users want Kubernetes default networking feature along with network objects. Refer to issues [#14](https://github.com/intel/multus-cni/issues/14) & [#17](https://github.com/intel/multus-cni/issues/17) for more information. In the following Multus configuration, Weave act as the default network in the absence of network field in the pod metadata annotation.
|
||||
- [How to use](docs/how-to-use.md)
|
||||
- [Quick Start Guide](docs/quickstart.md)
|
||||
- [Configuration](docs/configuration.md)
|
||||
- [Development and Support Information](docs/development.md)
|
||||
- [Thick Plugin](docs/thick-plugin.md)
|
||||
|
||||
```
|
||||
{
|
||||
"name": "node-cni-network",
|
||||
"type": "multus",
|
||||
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
|
||||
"delegates": [{
|
||||
"type": "weave-net",
|
||||
"hairpinMode": true,
|
||||
"masterplugin": true
|
||||
}]
|
||||
}
|
||||
```
|
||||
2. Restart kubelet service
|
||||
## Contact Us
|
||||
|
||||
```
|
||||
# systemctl restart kubelet
|
||||
```
|
||||
For any questions about Multus CNI, open up a GitHub issue or feel free to ask a question in #general in the [NPWG Slack](https://npwg-team.slack.com/).
|
||||
|
||||
### Configuring Pod to use the CRD network objects
|
||||
|
||||
1. Save the following YAML to pod-multi-network.yaml. In this case flannel-conf network object acts as the primary network.
|
||||
```
|
||||
# cat pod-multi-network.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: multus-multi-net-poc
|
||||
annotations:
|
||||
kubernetes.v1.cni.cncf.io/networks: '[
|
||||
{ "name": "flannel-conf" },
|
||||
{ "name": "sriov-conf" },
|
||||
{ "name": "sriov-vlanid-l2enable-conf",
|
||||
"interfaceRequest": "north" }
|
||||
]'
|
||||
spec: # specification of the pod's contents
|
||||
containers:
|
||||
- name: multus-multi-net-poc
|
||||
image: "busybox"
|
||||
command: ["top"]
|
||||
stdin: true
|
||||
tty: true
|
||||
```
|
||||
|
||||
2. Create Multiple network based pod from the master node
|
||||
|
||||
```
|
||||
# kubectl create -f ./pod-multi-network.yaml
|
||||
pod "multus-multi-net-poc" created
|
||||
```
|
||||
|
||||
3. Get the details of the running pod from the master
|
||||
|
||||
```
|
||||
# kubectl get pods
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
multus-multi-net-poc 1/1 Running 0 30s
|
||||
```
|
||||
|
||||
### Verifying Pod network interfaces
|
||||
|
||||
1. Run "ifconfig" command in Pod:
|
||||
```
|
||||
# kubectl exec -it multus-multi-net-poc -- ifconfig
|
||||
eth0 Link encap:Ethernet HWaddr C6:43:7C:09:B4:9C
|
||||
inet addr:10.128.0.4 Bcast:0.0.0.0 Mask:255.255.255.0
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
|
||||
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:0
|
||||
RX bytes:648 (648.0 B) TX bytes:42 (42.0 B)
|
||||
|
||||
lo Link encap:Local Loopback
|
||||
inet addr:127.0.0.1 Mask:255.0.0.0
|
||||
inet6 addr: ::1/128 Scope:Host
|
||||
UP LOOPBACK RUNNING MTU:65536 Metric:1
|
||||
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1
|
||||
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
||||
|
||||
net0 Link encap:Ethernet HWaddr 06:21:91:2D:74:B9
|
||||
inet addr:192.168.42.3 Bcast:0.0.0.0 Mask:255.255.255.0
|
||||
inet6 addr: fe80::421:91ff:fe2d:74b9/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
|
||||
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:0
|
||||
RX bytes:0 (0.0 B) TX bytes:648 (648.0 B)
|
||||
|
||||
net1 Link encap:Ethernet HWaddr D2:94:98:82:00:00
|
||||
inet addr:10.56.217.171 Bcast:0.0.0.0 Mask:255.255.255.0
|
||||
inet6 addr: fe80::d094:98ff:fe82:0/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:2 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:120 (120.0 B) TX bytes:648 (648.0 B)
|
||||
|
||||
north Link encap:Ethernet HWaddr BE:F2:48:42:83:12
|
||||
inet6 addr: fe80::bcf2:48ff:fe42:8312/64 Scope:Link
|
||||
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
|
||||
RX packets:1420 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:1276 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:1000
|
||||
RX bytes:95956 (93.7 KiB) TX bytes:82200 (80.2 KiB)
|
||||
```
|
||||
| Interface name | Description |
|
||||
| --- | --- |
|
||||
| lo | loopback |
|
||||
| eth0 | weave network interface |
|
||||
| net0 | Flannel network tap interface |
|
||||
| net1 | VF0 of NIC 1 assigned to the container by [Intel - SR-IOV CNI](https://github.com/intel/sriov-cni) plugin |
|
||||
| north | VF0 of NIC 2 assigned with VLAN ID 210 to the container by SR-IOV CNI plugin |
|
||||
|
||||
2. Check the vlan ID of the NIC 2 VFs
|
||||
|
||||
```
|
||||
# ip link show enp2s0
|
||||
20: enp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
|
||||
link/ether 24:8a:07:e8:7d:40 brd ff:ff:ff:ff:ff:ff
|
||||
vf 0 MAC 00:00:00:00:00:00, vlan 210, spoof checking off, link-state auto
|
||||
vf 1 MAC 00:00:00:00:00:00, vlan 4095, spoof checking off, link-state auto
|
||||
vf 2 MAC 00:00:00:00:00:00, vlan 4095, spoof checking off, link-state auto
|
||||
vf 3 MAC 00:00:00:00:00:00, vlan 4095, spoof checking off, link-state auto
|
||||
```
|
||||
|
||||
## Using with Multus conf file
|
||||
|
||||
Given the following network configuration:
|
||||
|
||||
```
|
||||
# tee /etc/cni/net.d/multus-cni.conf <<-'EOF'
|
||||
{
|
||||
"name": "multus-demo-network",
|
||||
"type": "multus",
|
||||
"delegates": [
|
||||
{
|
||||
"type": "sriov",
|
||||
#part of sriov plugin conf
|
||||
"if0": "enp12s0f0",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.56.217.0/24",
|
||||
"rangeStart": "10.56.217.131",
|
||||
"rangeEnd": "10.56.217.190",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
],
|
||||
"gateway": "10.56.217.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "ptp",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.168.1.0/24",
|
||||
"rangeStart": "10.168.1.11",
|
||||
"rangeEnd": "10.168.1.20",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
],
|
||||
"gateway": "10.168.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "flannel",
|
||||
"masterplugin": true,
|
||||
"delegate": {
|
||||
"isDefaultGateway": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
## Testing Multus CNI
|
||||
|
||||
### Multiple flannel networks
|
||||
|
||||
Github user [YYGCui](https://github.com/YYGCui) has used multiple flannel network to work with Multus CNI plugin. Please refer to this [closed issue](https://github.com/intel/multus-cni/issues/7) for ,multiple overlay network support with Multus CNI.
|
||||
|
||||
Make sure that the multus, [sriov](https://github.com/Intel-Corp/sriov-cni), [flannel](https://github.com/containernetworking/cni/blob/master/Documentation/flannel.md), and [ptp](https://github.com/containernetworking/cni/blob/master/Documentation/ptp.md) binaries are in the /opt/cni/bin directories and follow the steps as mentioned in the [CNI](https://github.com/containernetworking/cni/#running-a-docker-container-with-network-namespace-set-up-by-cni-plugins)
|
||||
|
||||
#### Configure Kubernetes with CNI
|
||||
Kubelet must be configured to run with the CNI network plugin. Edit /etc/kubernetes/kubelet file and add "--network-plugin=cni" flags in KUBELET\_OPTS as shown below:
|
||||
|
||||
```
|
||||
KUBELET_OPTS="...
|
||||
--network-plugin-dir=/etc/cni/net.d
|
||||
--network-plugin=cni
|
||||
"
|
||||
```
|
||||
Refer to the Kubernetes User Guide and network plugin for more information.
|
||||
- [Single Node](https://kubernetes.io/docs/getting-started-guides/fedora/fedora_manual_config/)
|
||||
- [Multi Node](https://kubernetes.io/docs/getting-started-guides/fedora/flannel_multi_node_cluster/)
|
||||
- [Network plugin](https://kubernetes.io/docs/admin/network-plugins/)
|
||||
|
||||
Restart kubelet:
|
||||
```
|
||||
# systemctl restart kubelet.service
|
||||
```
|
||||
#### Launching workloads in Kubernetes
|
||||
|
||||
With Multus CNI configured as described in sections above each workload launched via a Kubernetes Pod will have multiple network interfacesLaunch the workload using yaml file in the kubernetes master, with above configuration in the multus CNI, each pod should have multiple interfaces.
|
||||
|
||||
Note: To verify whether Multus CNI plugin is working correctly, create a pod containing one "busybox" container and execute "ip link" command to check if interfaces management follows configuration.
|
||||
|
||||
1. Create "multus-test.yaml" file containing below configuration. Created pod will consist of one "busybox" container running "top" command.
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: multus-test
|
||||
spec: # specification of the pod's contents
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: test1
|
||||
image: "busybox"
|
||||
command: ["top"]
|
||||
stdin: true
|
||||
tty: true
|
||||
|
||||
```
|
||||
2. Create pod using command:
|
||||
|
||||
```
|
||||
# kubectl create -f multus-test.yaml
|
||||
pod "multus-test" created
|
||||
```
|
||||
3. Run "ip link" command inside the container:
|
||||
|
||||
```
|
||||
# 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
3: eth0@if41: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
|
||||
link/ether 26:52:6b:d8:44:2d brd ff:ff:ff:ff:ff:ff
|
||||
20: net0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq qlen 1000
|
||||
link/ether f6:fb:21:4f:1d:63 brd ff:ff:ff:ff:ff:ff
|
||||
21: net1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq qlen 1000
|
||||
link/ether 76:13:b1:60:00:00 brd ff:ff:ff:ff:ff:ff
|
||||
```
|
||||
|
||||
| Interface name | Description |
|
||||
| --- | --- |
|
||||
| lo | loopback |
|
||||
| eth0@if41 | Flannel network tap interface |
|
||||
| net0 | VF assigned to the container by [SR-IOV CNI](https://github.com/Intel-Corp/sriov-cni) plugin |
|
||||
| net1 | ptp localhost interface |
|
||||
|
||||
## Multus additional plugins
|
||||
|
||||
- [DPDK-SRIOV CNI](https://github.com/Intel-Corp/sriov-cni)
|
||||
- [Vhostuser CNI](https://github.com/intel/vhost-user-net-plugin) - a Dataplane network plugin - Supports OVS-DPDK & VPP
|
||||
- [Bond CNI](https://github.com/Intel-Corp/bond-cni) - For fail-over and high availability of networking
|
||||
|
||||
## NFV based networking in Kubernetes
|
||||
|
||||
- KubeCon workshop on ["Enabling NFV features in Kubernetes"](https://kccncna17.sched.com/event/Cvnw/enabling-nfv-features-in-kubernetes-hosted-by-kuralamudhan-ramakrishnan-ivan-coughlan-intel) presentation [slide deck](https://www.slideshare.net/KuralamudhanRamakris/enabling-nfv-features-in-kubernetes-83923352)
|
||||
- Feature brief
|
||||
- [Multiple Network Interface Support in Kubernetes](https://builders.intel.com/docs/networkbuilders/multiple-network-interfaces-support-in-kubernetes-feature-brief.pdf)
|
||||
- [Enhanced Platform Awareness in Kubernetes](https://builders.intel.com/docs/networkbuilders/enhanced-platform-awareness-feature-brief.pdf)
|
||||
- Application note
|
||||
- [Multiple Network Interfaces in Kubernetes and Container Bare Metal](https://builders.intel.com/docs/networkbuilders/multiple-network-interfaces-in-kubernetes-application-note.pdf)
|
||||
- [Enhanced Platform Awareness Features in Kubernetes](https://builders.intel.com/docs/networkbuilders/enhanced-platform-awareness-in-kubernetes-application-note.pdf)
|
||||
- White paper
|
||||
- [Enabling New Features with Kubernetes for NFV](https://builders.intel.com/docs/networkbuilders/enabling_new_features_in_kubernetes_for_NFV.pdf)
|
||||
- Multus's related project github pages
|
||||
- [Multus](https://github.com/Intel-Corp/multus-cni)
|
||||
- [SRIOV - DPDK CNI](https://github.com/Intel-Corp/sriov-cni)
|
||||
- [Vhostuser - VPP & OVS - DPDK CNI](https://github.com/intel/vhost-user-net-plugin)
|
||||
- [Bond CNI](https://github.com/Intel-Corp/bond-cni)
|
||||
- [Node Feature Discovery](https://github.com/kubernetes-incubator/node-feature-discovery)
|
||||
- [CPU Manager for Kubernetes](https://github.com/Intel-Corp/CPU-Manager-for-Kubernetes)
|
||||
|
||||
|
||||
## Need help
|
||||
|
||||
- Read [Containers Experience Kits](https://networkbuilders.intel.com/network-technologies/container-experience-kits)
|
||||
- Try our container exp kit demo - KubeCon workshop on [Enabling NFV Features in Kubernetes](https://github.com/intel/container-experience-kits-demo-area/)
|
||||
- Join us on [#intel-sddsg-slack](https://intel-corp.herokuapp.com/) slack channel and ask question in [#general-discussion](https://intel-corp-team.slack.com/messages/C4C5RSEER)
|
||||
- You can also [email](mailto:kuralamudhan.ramakrishnan@intel.com) us
|
||||
- Feel free to [submit](https://github.com/Intel-Corp/multus-cni/issues/new) an issue
|
||||
|
||||
Please fill in the Questions/feedback - [google-form](https://goo.gl/forms/upBWyGs8Wmq69IEi2)!
|
||||
## Contacts
|
||||
For any questions about Multus CNI, please reach out on github issue or feel free to contact the developer @kural in our [Intel-Corp Slack](https://intel-corp.herokuapp.com/)
|
||||
To be invited, use [this slack invite link](https://join.slack.com/t/npwg-team/shared_invite/zt-1u2vmsn2b-tKdOokdPY73zn9B32JoAOg).
|
||||
|
17
build
@@ -1,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ORG_PATH="github.com/intel"
|
||||
REPO_PATH="${ORG_PATH}/multus-cni"
|
||||
|
||||
if [ ! -h gopath/src/${REPO_PATH} ]; then
|
||||
mkdir -p gopath/src/${ORG_PATH}
|
||||
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
|
||||
fi
|
||||
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
export GOBIN=${PWD}/bin
|
||||
export GOPATH=${PWD}/gopath
|
||||
|
||||
echo "Building plugins"
|
||||
go install "$@" ${REPO_PATH}/multus
|
371
cmd/cert-approver/main.go
Normal file
@@ -0,0 +1,371 @@
|
||||
// Copyright (c) 2023 Network Plumbing Working Group
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is Kubernetes controller which approves CSR submitted by multus.
|
||||
// This command is required only if multus runs with per-node certificate.
|
||||
package main
|
||||
|
||||
// Note: cert-approver should be simple, just approve multus' CSR, hence
|
||||
// this go code should not have any dependencies from pkg/, if possible,
|
||||
// to keep its code simplicity.
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
certificatesv1 "k8s.io/api/certificates/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/certificate/csr"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
|
||||
// CertController object
|
||||
type CertController struct {
|
||||
clientset kubernetes.Interface
|
||||
queue workqueue.RateLimitingInterface
|
||||
informer cache.SharedIndexInformer
|
||||
broadcaster record.EventBroadcaster
|
||||
recorder record.EventRecorder
|
||||
commonNamePrefixes string
|
||||
}
|
||||
|
||||
const (
|
||||
maxDuration = time.Hour * 24 * 365
|
||||
resyncPeriod time.Duration = time.Second * 3600 // resync every one hour, default is 10 hour
|
||||
maxRetries = 5
|
||||
)
|
||||
|
||||
var (
|
||||
// ControllerName provides controller name
|
||||
ControllerName = "csr-approver"
|
||||
// NamePrefix specifies which name in certification request should be target to approve
|
||||
NamePrefix = "system:multus"
|
||||
// Organization specifies which org in certification request should be target to approve
|
||||
Organization = []string{"system:multus"}
|
||||
// Groups specifies which group in certification request should be target to approve
|
||||
Groups = sets.New[string]("system:nodes", "system:multus", "system:authenticated")
|
||||
// UserPrefixes specifies which name prefix in certification request should be target to approve
|
||||
UserPrefixes = sets.New[string]("system:node", NamePrefix)
|
||||
// Usages specifies which usage in certification request should be target to approve
|
||||
Usages = sets.New[certificatesv1.KeyUsage](
|
||||
certificatesv1.UsageDigitalSignature,
|
||||
certificatesv1.UsageClientAuth)
|
||||
)
|
||||
|
||||
// NewCertController creates certcontroller
|
||||
func NewCertController() (*CertController, error) {
|
||||
var clientset kubernetes.Interface
|
||||
// setup Kubernetes API client
|
||||
config, err := rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientset, err = kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
informer := cache.NewSharedIndexInformer(
|
||||
cache.NewListWatchFromClient(
|
||||
clientset.CertificatesV1().RESTClient(),
|
||||
"certificatesigningrequests", corev1.NamespaceAll, fields.Everything()),
|
||||
&certificatesv1.CertificateSigningRequest{},
|
||||
resyncPeriod,
|
||||
nil)
|
||||
|
||||
broadcaster := record.NewBroadcaster()
|
||||
broadcaster.StartLogging(klog.Infof)
|
||||
broadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: clientset.CoreV1().Events("")})
|
||||
recorder := broadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: "cert-approver"})
|
||||
queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
|
||||
c := &CertController{
|
||||
clientset: clientset,
|
||||
informer: informer,
|
||||
queue: queue,
|
||||
commonNamePrefixes: NamePrefix,
|
||||
broadcaster: broadcaster,
|
||||
recorder: recorder,
|
||||
}
|
||||
|
||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
if csr, ok := obj.(*certificatesv1.CertificateSigningRequest); ok {
|
||||
if c.filterCSR(csr) {
|
||||
key, err := cache.MetaNamespaceKeyFunc(obj)
|
||||
if err == nil {
|
||||
queue.Add(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Run starts controller
|
||||
func (c *CertController) Run(stopCh <-chan struct{}) {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.queue.ShutDown()
|
||||
|
||||
klog.Info("Starting cert approver")
|
||||
|
||||
go c.informer.Run(stopCh)
|
||||
if !cache.WaitForCacheSync(stopCh, c.HasSynced) {
|
||||
utilruntime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
|
||||
return
|
||||
}
|
||||
|
||||
klog.Info("cert approver synced and ready")
|
||||
wait.Until(c.runWorker, time.Second, stopCh)
|
||||
}
|
||||
|
||||
// HasSynced is required for the cache.Controller interface.
|
||||
func (c *CertController) HasSynced() bool {
|
||||
return c.informer.HasSynced()
|
||||
}
|
||||
|
||||
// LastSyncResourceVersion is required for the cache.Controller interface.
|
||||
func (c *CertController) LastSyncResourceVersion() string {
|
||||
return c.informer.LastSyncResourceVersion()
|
||||
}
|
||||
|
||||
func (c *CertController) runWorker() {
|
||||
for c.processNextItem() {
|
||||
// continue looping
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CertController) processNextItem() bool {
|
||||
// Wait until there is a new item in the working queue
|
||||
key, quit := c.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
// Tell the queue that we are done with processing this key. This unblocks the key for other workers
|
||||
// This allows safe parallel processing because two pods with the same key are never processed in
|
||||
// parallel.
|
||||
defer c.queue.Done(key)
|
||||
|
||||
// Invoke the method containing the business logic
|
||||
err := c.processItem(key.(string))
|
||||
// Handle the error if something went wrong during the execution of the business logic
|
||||
c.handleErr(err, key)
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// handleErr checks if an error happened and makes sure we will retry later.
|
||||
func (c *CertController) handleErr(err error, key interface{}) {
|
||||
if err == nil {
|
||||
// Forget about the #AddRateLimited history of the key on every successful synchronization.
|
||||
// This ensures that future processing of updates for this key is not delayed because of
|
||||
// an outdated error history.
|
||||
c.queue.Forget(key)
|
||||
return
|
||||
}
|
||||
|
||||
// This controller retries 5 times if something goes wrong. After that, it stops trying.
|
||||
if c.queue.NumRequeues(key) < maxRetries {
|
||||
klog.Infof("Error syncing csr %s: %v", key, err)
|
||||
// Re-enqueue the key rate limited. Based on the rate limiter on the
|
||||
// queue and the re-enqueue history, the key will be processed later again.
|
||||
c.queue.AddRateLimited(key)
|
||||
return
|
||||
}
|
||||
|
||||
c.queue.Forget(key)
|
||||
// Report to an external entity that, even after several retries, we could not successfully process this key
|
||||
utilruntime.HandleError(err)
|
||||
klog.Infof("Dropping csr %q out of the queue: %v", key, err)
|
||||
}
|
||||
|
||||
func (c *CertController) processItem(key string) error {
|
||||
startTime := time.Now()
|
||||
|
||||
obj, _, err := c.informer.GetIndexer().GetByKey(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error fetching object with key %s from store: %v", key, err)
|
||||
}
|
||||
|
||||
req, _ := obj.(*certificatesv1.CertificateSigningRequest)
|
||||
|
||||
nodeName := "unknown"
|
||||
defer func() {
|
||||
klog.Infof("Finished syncing CSR %s for %s node in %v", req.Name, nodeName, time.Since(startTime))
|
||||
}()
|
||||
|
||||
if len(req.Status.Certificate) > 0 {
|
||||
klog.V(5).Infof("CSR %s is already signed", req.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
if isApprovedOrDenied(&req.Status) {
|
||||
klog.V(5).Infof("CSR %s is already approved/denied", req.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
csrPEM, _ := pem.Decode(req.Spec.Request)
|
||||
if csrPEM == nil {
|
||||
return fmt.Errorf("failed to PEM-parse the CSR block in .spec.request: no CSRs were found")
|
||||
}
|
||||
|
||||
x509CSR, err := x509.ParseCertificateRequest(csrPEM.Bytes)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse the CSR bytes: %v", err)
|
||||
}
|
||||
|
||||
i := strings.LastIndex(req.Spec.Username, ":")
|
||||
if i == -1 || i == len(req.Spec.Username)-1 {
|
||||
return fmt.Errorf("failed to parse the username: %s", req.Spec.Username)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
prefix := req.Spec.Username[:i]
|
||||
nodeName = req.Spec.Username[i+1:]
|
||||
if !UserPrefixes.Has(prefix) {
|
||||
return c.denyCSR(ctx, req, fmt.Sprintf("CSR %q was created by an unexpected user: %q", req.Name, req.Spec.Username))
|
||||
}
|
||||
|
||||
if errs := validation.IsDNS1123Subdomain(nodeName); len(errs) != 0 {
|
||||
return c.denyCSR(ctx, req, fmt.Sprintf("extracted node name %q is not a valid DNS subdomain %v", nodeName, errs))
|
||||
}
|
||||
|
||||
if usages := sets.New[certificatesv1.KeyUsage](req.Spec.Usages...); !usages.Equal(Usages) {
|
||||
return c.denyCSR(ctx, req, fmt.Sprintf("CSR %q was created with unexpected usages: %v", req.Name, usages.UnsortedList()))
|
||||
}
|
||||
|
||||
if !Groups.HasAll(req.Spec.Groups...) {
|
||||
return c.denyCSR(ctx, req, fmt.Sprintf("CSR %q was created by a user with unexpected groups: %v", req.Name, req.Spec.Groups))
|
||||
}
|
||||
|
||||
expectedSubject := fmt.Sprintf("%s:%s", c.commonNamePrefixes, nodeName)
|
||||
if x509CSR.Subject.CommonName != expectedSubject {
|
||||
return c.denyCSR(ctx, req, fmt.Sprintf("expected the CSR's commonName to be %q, but it is %q", expectedSubject, x509CSR.Subject.CommonName))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(x509CSR.Subject.Organization, Organization) {
|
||||
return c.denyCSR(ctx, req, fmt.Sprintf("expected the CSR's organization to be %v, but it is %v", Organization, x509CSR.Subject.Organization))
|
||||
}
|
||||
|
||||
if req.Spec.ExpirationSeconds == nil {
|
||||
return c.denyCSR(ctx, req, fmt.Sprintf("CSR %q was created without specyfying the expirationSeconds", req.Name))
|
||||
}
|
||||
|
||||
if csr.ExpirationSecondsToDuration(*req.Spec.ExpirationSeconds) > maxDuration {
|
||||
return c.denyCSR(ctx, req, fmt.Sprintf("CSR %q was created with invalid expirationSeconds value: %d", req.Name, *req.Spec.ExpirationSeconds))
|
||||
}
|
||||
|
||||
return c.approveCSR(ctx, req)
|
||||
}
|
||||
|
||||
// CSR specific functions
|
||||
|
||||
func (c *CertController) filterCSR(csr *certificatesv1.CertificateSigningRequest) bool {
|
||||
nsName := types.NamespacedName{Namespace: csr.Namespace, Name: csr.Name}
|
||||
csrPEM, _ := pem.Decode(csr.Spec.Request)
|
||||
if csrPEM == nil {
|
||||
klog.Errorf("Failed to PEM-parse the CSR block in .spec.request: no CSRs were found in %s", nsName)
|
||||
return false
|
||||
}
|
||||
|
||||
x509CSR, err := x509.ParseCertificateRequest(csrPEM.Bytes)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to parse the CSR .spec.request of %q: %v", nsName, err)
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.HasPrefix(x509CSR.Subject.CommonName, c.commonNamePrefixes) &&
|
||||
csr.Spec.SignerName == certificatesv1.KubeAPIServerClientSignerName
|
||||
}
|
||||
|
||||
func (c *CertController) approveCSR(ctx context.Context, csr *certificatesv1.CertificateSigningRequest) error {
|
||||
csr.Status.Conditions = append(csr.Status.Conditions,
|
||||
certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateApproved,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "AutoApproved",
|
||||
Message: fmt.Sprintf("Auto-approved CSR %q", csr.Name),
|
||||
})
|
||||
|
||||
c.recorder.Eventf(csr, corev1.EventTypeNormal, "CSRApproved", "CSR %q has been approved by %s", csr.Name, ControllerName)
|
||||
_, err := c.clientset.CertificatesV1().CertificateSigningRequests().UpdateApproval(ctx, csr.Name, csr, metav1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *CertController) denyCSR(ctx context.Context, csr *certificatesv1.CertificateSigningRequest, message string) error {
|
||||
csr.Status.Conditions = append(csr.Status.Conditions,
|
||||
certificatesv1.CertificateSigningRequestCondition{
|
||||
Type: certificatesv1.CertificateDenied,
|
||||
Status: corev1.ConditionTrue,
|
||||
Reason: "CSRDenied",
|
||||
Message: message,
|
||||
},
|
||||
)
|
||||
|
||||
c.recorder.Eventf(csr, corev1.EventTypeWarning, "CSRDenied", "The CSR %q has been denied by: %s", csr.Name, ControllerName, message)
|
||||
_, err := c.clientset.CertificatesV1().CertificateSigningRequests().Update(ctx, csr, metav1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
func isApprovedOrDenied(status *certificatesv1.CertificateSigningRequestStatus) bool {
|
||||
for _, c := range status.Conditions {
|
||||
if c.Type == certificatesv1.CertificateApproved || c.Type == certificatesv1.CertificateDenied {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
klog.Infof("starting cert-approver")
|
||||
|
||||
// Start watching for pod creations
|
||||
certController, err := NewCertController()
|
||||
if err != nil {
|
||||
klog.Fatal(err)
|
||||
}
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
go certController.Run(stopCh)
|
||||
|
||||
sigterm := make(chan os.Signal, 1)
|
||||
signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
|
||||
<-sigterm
|
||||
}
|
67
cmd/install_multus/main.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2023 Multus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is a install tool for multus plugins
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/cmdutils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
typeFlag := pflag.StringP("type", "t", "", "specify installer type (thick/thin)")
|
||||
destDir := pflag.StringP("dest-dir", "d", "/host/opt/cni/bin", "destination directory")
|
||||
helpFlag := pflag.BoolP("help", "h", false, "show help message and quit")
|
||||
|
||||
pflag.Parse()
|
||||
if *helpFlag {
|
||||
pflag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
multusFileName := ""
|
||||
switch *typeFlag {
|
||||
case "thick":
|
||||
multusFileName = "multus-shim"
|
||||
case "thin":
|
||||
multusFileName = "multus"
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "--type is missing or --type has invalid value\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err := cmdutils.CopyFileAtomic(fmt.Sprintf("/usr/src/multus-cni/bin/%s", multusFileName), *destDir, fmt.Sprintf("%s.temp", multusFileName), multusFileName)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to copy file %s: %v\n", multusFileName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("multus %s copy succeeded!\n", multusFileName)
|
||||
|
||||
// Copy the passthru CNI
|
||||
passthruPath := "/usr/src/multus-cni/bin/passthru"
|
||||
err = cmdutils.CopyFileAtomic(passthruPath, *destDir, fmt.Sprintf("%s.temp", "passthru"), "passthru")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to copy file %s: %v\n", multusFileName, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("passthru cni %s copy succeeded!\n", passthruPath)
|
||||
|
||||
}
|
145
cmd/kubeconfig_generator/main.go
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright (c) 2023 Multus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This binary submit CSR for kube controll access for multus thin plugin
|
||||
// and generate Kubeconfig
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/k8sclient"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
var kubeConfigTemplate = `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: {{.CADATA}}
|
||||
server: {{.K8S_APISERVER}}
|
||||
name: default-cluster
|
||||
contexts:
|
||||
- context:
|
||||
cluster: default-cluster
|
||||
namespace: default
|
||||
user: default-auth
|
||||
name: default-context
|
||||
current-context: default-context
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users:
|
||||
- name: default-auth
|
||||
user:
|
||||
client-certificate: {{.CERTDIR}}/multus-client-current.pem
|
||||
client-key: {{.CERTDIR}}/multus-client-current.pem
|
||||
`
|
||||
|
||||
func main() {
|
||||
certDir := pflag.StringP("certdir", "", "/tmp", "specify cert directory")
|
||||
bootstrapConfig := pflag.StringP("bootstrap-config", "", "/tmp/kubeconfig", "specify bootstrap kubernetes config")
|
||||
kubeconfigPathRaw := pflag.StringP("kubeconfig", "", "/run/multus/kubeconfig", "specify output kubeconfig path")
|
||||
certDurationString := pflag.StringP("cert-duration", "", "10m", "specify certificate duration")
|
||||
helpFlag := pflag.BoolP("help", "h", false, "show help message and quit")
|
||||
|
||||
kubeconfigPath, err := filepath.Abs(*kubeconfigPathRaw)
|
||||
if err != nil {
|
||||
klog.Fatalf("illegal path %s in kubeconfigPath %s: %v", kubeconfigPath, *kubeconfigPathRaw, err)
|
||||
}
|
||||
|
||||
pflag.Parse()
|
||||
if *helpFlag {
|
||||
pflag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// check variables
|
||||
if _, err := os.Stat(*bootstrapConfig); err != nil {
|
||||
klog.Fatalf("failed to read bootstrap config %q", *bootstrapConfig)
|
||||
}
|
||||
st, err := os.Stat(*certDir)
|
||||
if err != nil {
|
||||
klog.Fatalf("failed to find cert directory %q", *certDir)
|
||||
}
|
||||
if !st.IsDir() {
|
||||
klog.Fatalf("cert directory %q is not directory", *certDir)
|
||||
}
|
||||
certDuration, err := time.ParseDuration(*certDurationString)
|
||||
if err != nil {
|
||||
klog.Fatalf("failed to parse duration %q: %v", *certDurationString, err)
|
||||
}
|
||||
|
||||
nodeName := os.Getenv("MULTUS_NODE_NAME")
|
||||
if nodeName == "" {
|
||||
klog.Fatalf("cannot identify node name from MULTUS_NODE_NAME env variables")
|
||||
}
|
||||
|
||||
// retrieve API server from bootstrapConfig()
|
||||
config, err := clientcmd.BuildConfigFromFlags("", *bootstrapConfig)
|
||||
if err != nil {
|
||||
klog.Fatalf("cannot get in-cluster config: %v", err)
|
||||
}
|
||||
apiServer := fmt.Sprintf("%s%s", config.Host, config.APIPath)
|
||||
caData := base64.StdEncoding.EncodeToString(config.CAData)
|
||||
|
||||
// run certManager to create certification
|
||||
if _, err = k8sclient.PerNodeK8sClient(nodeName, *bootstrapConfig, certDuration, *certDir); err != nil {
|
||||
klog.Fatalf("failed to start cert manager: %v", err)
|
||||
}
|
||||
|
||||
fp, err := os.OpenFile(kubeconfigPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
klog.Fatalf("cannot create kubeconfig file %q: %v", kubeconfigPath, err)
|
||||
}
|
||||
|
||||
// render kubeconfig
|
||||
templateKubeconfig, err := template.New("kubeconfig").Parse(kubeConfigTemplate)
|
||||
if err != nil {
|
||||
klog.Fatalf("template parse error: %v", err)
|
||||
}
|
||||
templateData := map[string]string{
|
||||
"CADATA": caData,
|
||||
"CERTDIR": *certDir,
|
||||
"K8S_APISERVER": apiServer,
|
||||
}
|
||||
// genearate kubeconfig from template
|
||||
if err = templateKubeconfig.Execute(fp, templateData); err != nil {
|
||||
klog.Fatalf("cannot create kubeconfig: %v", err)
|
||||
}
|
||||
if err = fp.Close(); err != nil {
|
||||
klog.Fatalf("cannot save kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
klog.Infof("kubeconfig %q is saved", kubeconfigPath)
|
||||
|
||||
// wait for signal
|
||||
sigterm := make(chan os.Signal, 1)
|
||||
signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
|
||||
<-sigterm
|
||||
klog.Infof("signal received. remove kubeconfig %q and quit.", kubeconfigPath)
|
||||
err = os.Remove(kubeconfigPath)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to remove kubeconfig %q: %v", kubeconfigPath, err)
|
||||
}
|
||||
}
|
219
cmd/multus-daemon/main.go
Normal file
@@ -0,0 +1,219 @@
|
||||
// Copyright (c) 2021 Multus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This binary works as a server that receives requests from multus-shim
|
||||
// CNI plugin and creates network interface for kubernets pods.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
utilwait "k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging"
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus"
|
||||
srv "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/server"
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/server/api"
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/server/config"
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
|
||||
// keep in command line option
|
||||
version := flag.Bool("version", false, "Show version")
|
||||
|
||||
configFilePath := flag.String("config", srv.DefaultMultusDaemonConfigFile, "Specify the path to the multus-daemon configuration")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *version {
|
||||
fmt.Printf("multus-daemon: %s\n", multus.PrintVersionString())
|
||||
os.Exit(4)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
daemonConf, err := cniServerConfig(*configFilePath)
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
multusConf, err := config.ParseMultusConfig(*configFilePath)
|
||||
if err != nil {
|
||||
logging.Panicf("startMultusDaemon failed to load the multus configuration: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
logging.Verbosef("multus-daemon started")
|
||||
|
||||
if multusConf.ReadinessIndicatorFile != "" {
|
||||
// Check readinessindicator file before daemon launch
|
||||
logging.Verbosef("Readiness Indicator file check")
|
||||
if err := types.GetReadinessIndicatorFile(multusConf.ReadinessIndicatorFile); err != nil {
|
||||
_ = logging.Errorf("have you checked that your default network is ready? still waiting for readinessindicatorfile @ %v. pollimmediate error: %v", multusConf.ReadinessIndicatorFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logging.Verbosef("Readiness Indicator file check done!")
|
||||
}
|
||||
|
||||
var configManager *config.Manager
|
||||
var ignoreReadinessIndicator bool
|
||||
if multusConf.MultusConfigFile == "auto" {
|
||||
if multusConf.CNIVersion == "" {
|
||||
_ = logging.Errorf("the CNI version is a mandatory parameter when the '-multus-config-file=auto' option is used")
|
||||
}
|
||||
|
||||
// Generate multus CNI config from current CNI config
|
||||
configManager, err = config.NewManager(*multusConf)
|
||||
if err != nil {
|
||||
_ = logging.Errorf("failed to create the configuration manager for the primary CNI plugin: %v", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
// ConfigManager watches the readiness indicator file (if configured)
|
||||
// and exits the daemon when that is removed. The CNIServer does
|
||||
// not need to re-do that check every CNI operation
|
||||
ignoreReadinessIndicator = true
|
||||
} else {
|
||||
if err := copyUserProvidedConfig(multusConf.MultusConfigFile, multusConf.CniConfigDir); err != nil {
|
||||
logging.Errorf("failed to copy the user provided configuration %s: %v", multusConf.MultusConfigFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := startMultusDaemon(ctx, daemonConf, ignoreReadinessIndicator); err != nil {
|
||||
logging.Panicf("failed start the multus thick-plugin listener: %v", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
// Wait until daemon ready
|
||||
logging.Verbosef("API readiness check")
|
||||
if api.WaitUntilAPIReady(daemonConf.SocketDir) != nil {
|
||||
logging.Panicf("failed to ready multus-daemon socket: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logging.Verbosef("API readiness check done!")
|
||||
|
||||
signalCh := make(chan os.Signal, 16)
|
||||
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
for sig := range signalCh {
|
||||
logging.Verbosef("caught %v, stopping...", sig)
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
if configManager != nil {
|
||||
if err := configManager.Start(ctx, &wg); err != nil {
|
||||
_ = logging.Errorf("failed to start config manager: %v", err)
|
||||
os.Exit(3)
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
logging.Verbosef("multus daemon is exited")
|
||||
}
|
||||
|
||||
func startMultusDaemon(ctx context.Context, daemonConfig *srv.ControllerNetConf, ignoreReadinessIndicator bool) error {
|
||||
if user, err := user.Current(); err != nil || user.Uid != "0" {
|
||||
return fmt.Errorf("failed to run multus-daemon with root: %v, now running in uid: %s", err, user.Uid)
|
||||
}
|
||||
|
||||
if err := srv.FilesystemPreRequirements(daemonConfig.SocketDir); err != nil {
|
||||
return fmt.Errorf("failed to prepare the cni-socket for communicating with the shim: %w", err)
|
||||
}
|
||||
|
||||
server, err := srv.NewCNIServer(daemonConfig, daemonConfig.ConfigFileContents, ignoreReadinessIndicator)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create the server: %v", err)
|
||||
}
|
||||
|
||||
if daemonConfig.MetricsPort != nil {
|
||||
go utilwait.UntilWithContext(ctx, func(_ context.Context) {
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
logging.Debugf("metrics port: %d", *daemonConfig.MetricsPort)
|
||||
logging.Debugf("metrics: %s", http.ListenAndServe(fmt.Sprintf(":%d", *daemonConfig.MetricsPort), nil))
|
||||
}, 0)
|
||||
}
|
||||
|
||||
l, err := srv.GetListener(api.SocketPath(daemonConfig.SocketDir))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start the CNI server using socket %s. Reason: %+v", api.SocketPath(daemonConfig.SocketDir), err)
|
||||
}
|
||||
|
||||
server.Start(ctx, l)
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
server.Shutdown(context.Background())
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cniServerConfig(configFilePath string) (*srv.ControllerNetConf, error) {
|
||||
path, err := filepath.Abs(configFilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("illegal path %s in server config path %s: %w", path, configFilePath, err)
|
||||
}
|
||||
|
||||
configFileContents, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return srv.LoadDaemonNetConf(configFileContents)
|
||||
}
|
||||
|
||||
func copyUserProvidedConfig(multusConfigPath string, cniConfigDir string) error {
|
||||
path, err := filepath.Abs(multusConfigPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("illegal path %s in multusConfigPath %s: %w", path, multusConfigPath, err)
|
||||
}
|
||||
|
||||
srcFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open (READ only) file %s: %w", path, err)
|
||||
}
|
||||
|
||||
dstFileName := cniConfigDir + "/" + filepath.Base(multusConfigPath)
|
||||
dstFile, err := os.Create(dstFileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating copying file %s: %w", dstFileName, err)
|
||||
}
|
||||
nBytes, err := io.Copy(dstFile, srcFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying file: %w", err)
|
||||
}
|
||||
srcFileInfo, err := srcFile.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stat the file: %w", err)
|
||||
} else if nBytes != srcFileInfo.Size() {
|
||||
return fmt.Errorf("error copying file - copied only %d bytes out of %d", nBytes, srcFileInfo.Size())
|
||||
}
|
||||
return nil
|
||||
}
|
66
cmd/multus-shim/main.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2022 Multus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is a "Multi-plugin".The delegate concept referred from CNI project
|
||||
// It reads other plugin netconf, and then invoke them, e.g.
|
||||
// flannel or sriov plugin.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
cniversion "github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus"
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/server/api"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Init command line flags to clear vendored packages' one, especially in init()
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
|
||||
// add version flag
|
||||
versionOpt := false
|
||||
flag.BoolVar(&versionOpt, "version", false, "Show application version")
|
||||
flag.BoolVar(&versionOpt, "v", false, "Show application version")
|
||||
|
||||
flag.Parse()
|
||||
if versionOpt {
|
||||
fmt.Printf("multus-shim: %s\n", multus.PrintVersionString())
|
||||
return
|
||||
}
|
||||
|
||||
skel.PluginMainFuncs(
|
||||
skel.CNIFuncs{
|
||||
Add: func(args *skel.CmdArgs) error {
|
||||
return api.CmdAdd(args)
|
||||
},
|
||||
Check: func(args *skel.CmdArgs) error {
|
||||
return api.CmdCheck(args)
|
||||
},
|
||||
Del: func(args *skel.CmdArgs) error {
|
||||
return api.CmdDel(args)
|
||||
},
|
||||
GC: func(args *skel.CmdArgs) error {
|
||||
return api.CmdGC(args)
|
||||
},
|
||||
Status: func(args *skel.CmdArgs) error {
|
||||
return api.CmdStatus(args)
|
||||
},
|
||||
},
|
||||
cniversion.All, "meta-plugin that delegates to other CNI plugins")
|
||||
}
|
69
cmd/multus/main.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2016 Intel Corporation
|
||||
// Copyright (c) 2021 Multus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is a "Multi-plugin".The delegate concept referred from CNI project
|
||||
// It reads other plugin netconf, and then invoke them, e.g.
|
||||
// flannel or sriov plugin.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
cniversion "github.com/containernetworking/cni/pkg/version"
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Init command line flags to clear vendored packages' one, especially in init()
|
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||
|
||||
// add version flag
|
||||
versionOpt := false
|
||||
flag.BoolVar(&versionOpt, "version", false, "Show application version")
|
||||
flag.BoolVar(&versionOpt, "v", false, "Show application version")
|
||||
flag.Parse()
|
||||
if versionOpt {
|
||||
fmt.Printf("multus: %s\n", multus.PrintVersionString())
|
||||
return
|
||||
}
|
||||
|
||||
skel.PluginMainFuncs(
|
||||
skel.CNIFuncs{
|
||||
Add: func(args *skel.CmdArgs) error {
|
||||
result, err := multus.CmdAdd(args, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return result.Print()
|
||||
},
|
||||
Del: func(args *skel.CmdArgs) error {
|
||||
return multus.CmdDel(args, nil, nil)
|
||||
},
|
||||
Check: func(args *skel.CmdArgs) error {
|
||||
return multus.CmdCheck(args, nil, nil)
|
||||
},
|
||||
GC: func(args *skel.CmdArgs) error {
|
||||
return multus.CmdGC(args, nil, nil)
|
||||
},
|
||||
Status: func(args *skel.CmdArgs) error {
|
||||
return multus.CmdStatus(args, nil, nil)
|
||||
},
|
||||
},
|
||||
cniversion.All, "meta-plugin that delegates to other CNI plugins")
|
||||
}
|
58
cmd/passthru-cni/main.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Package: passthru-cni
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
cniTypes "github.com/containernetworking/cni/pkg/types"
|
||||
current "github.com/containernetworking/cni/pkg/types/100"
|
||||
cniVersion "github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
// NetConf is a CNI configuration structure
|
||||
type NetConf struct {
|
||||
cniTypes.NetConf
|
||||
}
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(
|
||||
cmdAdd,
|
||||
nil,
|
||||
cmdDel,
|
||||
cniVersion.PluginSupports("0.3.0", "0.3.1", "0.4.0", "1.0.0", "1.1.0"),
|
||||
"Passthrough CNI Plugin v1.0",
|
||||
)
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
n, err := loadNetConf(args.StdinData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("passthru cni: error parsing CNI configuration: %s", err)
|
||||
}
|
||||
|
||||
// Create an empty but valid CNI result
|
||||
result := ¤t.Result{
|
||||
CNIVersion: n.CNIVersion,
|
||||
Interfaces: []*current.Interface{},
|
||||
IPs: []*current.IPConfig{},
|
||||
Routes: []*cniTypes.Route{},
|
||||
DNS: cniTypes.DNS{},
|
||||
}
|
||||
|
||||
return cniTypes.PrintResult(result, n.CNIVersion)
|
||||
}
|
||||
|
||||
func cmdDel(_ *skel.CmdArgs) error {
|
||||
// Nothing to do for DEL command, just return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadNetConf(bytes []byte) (*NetConf, error) {
|
||||
n := &NetConf{}
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, fmt.Errorf("passthru cni: failed to load netconf: %s", err)
|
||||
}
|
||||
return n, nil
|
||||
}
|
683
cmd/thin_entrypoint/main.go
Normal file
@@ -0,0 +1,683 @@
|
||||
// Copyright (c) 2023 Multus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This is a entrypoint for thin (stand-alone) images.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
b64 "encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/cmdutils"
|
||||
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/signals"
|
||||
)
|
||||
|
||||
// Options stores command line options
|
||||
type Options struct {
|
||||
CNIBinDir string
|
||||
CNIConfDir string
|
||||
CNIVersion string
|
||||
MultusConfFile string
|
||||
MultusBinFile string // may be hidden or remove?
|
||||
MultusCNIConfDir string
|
||||
SkipMultusBinaryCopy bool
|
||||
MultusKubeConfigFileHost string
|
||||
MultusMasterCNIFileName string
|
||||
NamespaceIsolation bool
|
||||
GlobalNamespaces string
|
||||
MultusAutoconfigDir string
|
||||
MultusLogToStderr bool
|
||||
MultusLogLevel string
|
||||
MultusLogFile string
|
||||
OverrideNetworkName bool
|
||||
CleanupConfigOnExit bool
|
||||
RenameConfFile bool
|
||||
ReadinessIndicatorFile string
|
||||
AdditionalBinDir string
|
||||
ForceCNIVersion bool
|
||||
SkipTLSVerify bool
|
||||
SkipMultusConfWatch bool
|
||||
}
|
||||
|
||||
const (
|
||||
serviceAccountTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
|
||||
serviceAccountCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
||||
)
|
||||
|
||||
func (o *Options) addFlags() {
|
||||
pflag.ErrHelp = nil // suppress error message for help
|
||||
fs := pflag.CommandLine
|
||||
fs.StringVar(&o.CNIBinDir, "cni-bin-dir", "/host/opt/cni/bin", "CNI binary directory")
|
||||
fs.StringVar(&o.CNIConfDir, "cni-conf-dir", "/host/etc/cni/net.d", "CNI config directory")
|
||||
fs.StringVar(&o.CNIVersion, "cni-version", "", "CNI version for multus CNI config (e.g. '0.3.1')")
|
||||
fs.StringVar(&o.MultusConfFile, "multus-conf-file", "auto", "multus CNI config file")
|
||||
fs.StringVar(&o.MultusBinFile, "multus-bin-file", "/usr/src/multus-cni/bin/multus", "multus binary file path")
|
||||
fs.StringVar(&o.MultusCNIConfDir, "multus-cni-conf-dir", "/host/etc/cni/multus/net.d", "multus specific CNI config directory")
|
||||
fs.BoolVar(&o.SkipMultusBinaryCopy, "skip-multus-binary-copy", false, "skip multus binary file copy")
|
||||
|
||||
fs.StringVar(&o.MultusKubeConfigFileHost, "multus-kubeconfig-file-host", "/etc/cni/net.d/multus.d/multus.kubeconfig", "kubeconfig for multus (used only with --multus-conf-file=auto)")
|
||||
fs.StringVar(&o.MultusMasterCNIFileName, "multus-master-cni-file-name", "", "master CNI file in multus-autoconfig-dir")
|
||||
fs.BoolVar(&o.NamespaceIsolation, "namespace-isolation", false, "namespace isolation")
|
||||
fs.StringVar(&o.GlobalNamespaces, "global-namespaces", "", "global namespaces, comma separated (used only with --namespace-isolation=true)")
|
||||
fs.StringVar(&o.MultusAutoconfigDir, "multus-autoconfig-dir", "/host/etc/cni/net.d", "multus autoconfig dir (used only with --multus-conf-file=auto)")
|
||||
fs.BoolVar(&o.MultusLogToStderr, "multus-log-to-stderr", true, "log to stderr")
|
||||
fs.StringVar(&o.MultusLogLevel, "multus-log-level", "", "multus log level")
|
||||
fs.StringVar(&o.MultusLogFile, "multus-log-file", "", "multus log file")
|
||||
fs.BoolVar(&o.OverrideNetworkName, "override-network-name", false, "override network name from master cni file (used only with --multus-conf-file=auto)")
|
||||
fs.BoolVar(&o.CleanupConfigOnExit, "cleanup-config-on-exit", false, "cleanup config file on exit")
|
||||
fs.BoolVar(&o.SkipMultusConfWatch, "skip-config-watch", false, "dont watch for config (master cni and kubeconfig) changes (used only with --multus-conf-file=auto)")
|
||||
fs.BoolVar(&o.RenameConfFile, "rename-conf-file", false, "rename master config file to invalidate (used only with --multus-conf-file=auto)")
|
||||
fs.StringVar(&o.ReadinessIndicatorFile, "readiness-indicator-file", "", "readiness indicator file (used only with --multus-conf-file=auto)")
|
||||
fs.StringVar(&o.AdditionalBinDir, "additional-bin-dir", "", "adds binDir option to configuration (used only with --multus-conf-file=auto)")
|
||||
fs.BoolVar(&o.SkipTLSVerify, "skip-tls-verify", false, "skip TLS verify")
|
||||
fs.BoolVar(&o.ForceCNIVersion, "force-cni-version", false, "force cni version to '--cni-version' (only for e2e-kind testing)")
|
||||
fs.MarkHidden("force-cni-version")
|
||||
fs.MarkHidden("skip-tls-verify")
|
||||
}
|
||||
|
||||
func (o *Options) verifyFileExists() error {
|
||||
// CNIConfDir
|
||||
if _, err := os.Stat(o.CNIConfDir); err != nil {
|
||||
return fmt.Errorf("cni-conf-dir is not found: %v", err)
|
||||
}
|
||||
|
||||
// CNIBinDir
|
||||
if _, err := os.Stat(o.CNIBinDir); err != nil {
|
||||
return fmt.Errorf("cni-bin-dir is not found: %v", err)
|
||||
}
|
||||
|
||||
// MultusBinFile
|
||||
if _, err := os.Stat(o.MultusBinFile); err != nil {
|
||||
return fmt.Errorf("multus-bin-file is not found: %v", err)
|
||||
}
|
||||
|
||||
if o.MultusConfFile != "auto" {
|
||||
// MultusConfFile
|
||||
if _, err := os.Stat(o.MultusConfFile); err != nil {
|
||||
return fmt.Errorf("multus-conf-file is not found: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const kubeConfigTemplate = `# Kubeconfig file for Multus CNI plugin.
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: local
|
||||
cluster:
|
||||
server: {{ .KubeConfigHost }}
|
||||
{{ .KubeServerTLS }}
|
||||
users:
|
||||
- name: multus
|
||||
user:
|
||||
token: "{{ .KubeServiceAccountToken }}"
|
||||
contexts:
|
||||
- name: multus-context
|
||||
context:
|
||||
cluster: local
|
||||
user: multus
|
||||
current-context: multus-context
|
||||
`
|
||||
|
||||
func getFileAndHash(filepath string) ([]byte, []byte, error) {
|
||||
if _, err := os.Stat(filepath); err != nil {
|
||||
return nil, nil, fmt.Errorf("file %s not found: %v", filepath, err)
|
||||
}
|
||||
content, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot read %s file: %v", filepath, err)
|
||||
}
|
||||
|
||||
hash := sha256.New()
|
||||
hash.Write(content)
|
||||
return content, hash.Sum(nil), nil
|
||||
}
|
||||
|
||||
func (o *Options) createKubeConfig(prevCAHash, prevSATokenHash []byte) ([]byte, []byte, error) {
|
||||
caFileByte, caHash, err := getFileAndHash(serviceAccountCAFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
saTokenByte, saTokenHash, err := getFileAndHash(serviceAccountTokenFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
caUnchanged := prevCAHash != nil && bytes.Equal(prevCAHash, caHash)
|
||||
saUnchanged := prevSATokenHash != nil && bytes.Equal(prevSATokenHash, saTokenHash)
|
||||
|
||||
if o.SkipTLSVerify {
|
||||
if saUnchanged {
|
||||
return caHash, saTokenHash, nil
|
||||
}
|
||||
} else {
|
||||
if caUnchanged && saUnchanged {
|
||||
return caHash, saTokenHash, nil
|
||||
}
|
||||
}
|
||||
|
||||
if prevSATokenHash != nil {
|
||||
// don't log "recreating" on first function execution
|
||||
fmt.Printf("CA (%v) or SA token (%v) changed - recreating kubeconfig\n", !caUnchanged, !saUnchanged)
|
||||
}
|
||||
|
||||
// create multus.d directory
|
||||
if err := os.MkdirAll(fmt.Sprintf("%s/multus.d", o.CNIConfDir), 0755); err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot create multus.d directory: %v", err)
|
||||
}
|
||||
|
||||
// create multus cni conf directory
|
||||
if err := os.MkdirAll(o.MultusCNIConfDir, 0755); err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot create multus-cni-conf-dir(%s) directory: %v", o.MultusCNIConfDir, err)
|
||||
}
|
||||
|
||||
// get Kubernetes service protocol/host/port
|
||||
kubeProtocol := os.Getenv("KUBERNETES_SERVICE_PROTOCOL")
|
||||
if kubeProtocol == "" {
|
||||
kubeProtocol = "https"
|
||||
}
|
||||
kubeHost := os.Getenv("KUBERNETES_SERVICE_HOST")
|
||||
kubePort := os.Getenv("KUBERNETES_SERVICE_PORT")
|
||||
|
||||
// check tlsConfig
|
||||
tlsConfig := ""
|
||||
if o.SkipTLSVerify {
|
||||
tlsConfig = "insecure-skip-tls-verify: true"
|
||||
} else {
|
||||
// create tlsConfig by service account CA file
|
||||
caFileB64 := bytes.ReplaceAll([]byte(b64.StdEncoding.EncodeToString(caFileByte)), []byte("\n"), []byte(""))
|
||||
tlsConfig = fmt.Sprintf("certificate-authority-data: %s", string(caFileB64))
|
||||
}
|
||||
|
||||
// create kubeconfig by template and replace it by atomic
|
||||
tempKubeConfigFile := fmt.Sprintf("%s/multus.d/multus.kubeconfig.new", o.CNIConfDir)
|
||||
multusKubeConfig := fmt.Sprintf("%s/multus.d/multus.kubeconfig", o.CNIConfDir)
|
||||
fp, err := os.OpenFile(tempKubeConfigFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot create kubeconfig temp file: %v", err)
|
||||
}
|
||||
|
||||
templateKubeconfig, err := template.New("kubeconfig").Parse(kubeConfigTemplate)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("template parse error: %v", err)
|
||||
}
|
||||
templateData := map[string]string{
|
||||
"KubeConfigHost": fmt.Sprintf("%s://[%s]:%s", kubeProtocol, kubeHost, kubePort),
|
||||
"KubeServerTLS": tlsConfig,
|
||||
"KubeServiceAccountToken": string(saTokenByte),
|
||||
}
|
||||
|
||||
// generate kubeconfig from template
|
||||
if err = templateKubeconfig.Execute(fp, templateData); err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot create kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
if err := fp.Sync(); err != nil {
|
||||
os.Remove(fp.Name())
|
||||
return nil, nil, fmt.Errorf("cannot flush kubeconfig temp file: %v", err)
|
||||
}
|
||||
if err := fp.Close(); err != nil {
|
||||
os.Remove(fp.Name())
|
||||
return nil, nil, fmt.Errorf("cannot close kubeconfig temp file: %v", err)
|
||||
}
|
||||
|
||||
// replace file with tempfile
|
||||
if err := os.Rename(tempKubeConfigFile, multusKubeConfig); err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot replace %q with temp file %q: %v", multusKubeConfig, tempKubeConfigFile, err)
|
||||
}
|
||||
|
||||
fmt.Printf("kubeconfig is created in %s\n", multusKubeConfig)
|
||||
return caHash, saTokenHash, nil
|
||||
}
|
||||
|
||||
const multusConflistTemplate = `{
|
||||
"cniVersion": "{{ .CNIVersion }}",
|
||||
"name": "{{ .MasterPluginNetworkName }}",
|
||||
"plugins": [ {
|
||||
"type": "multus",{{
|
||||
.NestedCapabilities
|
||||
}}{{
|
||||
.NamespaceIsolationConfig
|
||||
}}{{
|
||||
.GlobalNamespacesConfig
|
||||
}}{{
|
||||
.LogToStderrConfig
|
||||
}}{{
|
||||
.LogLevelConfig
|
||||
}}{{
|
||||
.LogFileConfig
|
||||
}}{{
|
||||
.AdditionalBinDirConfig
|
||||
}}{{
|
||||
.MultusCNIConfDirConfig
|
||||
}}{{
|
||||
.ReadinessIndicatorFileConfig
|
||||
}}
|
||||
"kubeconfig": "{{ .MultusKubeConfigFileHost }}",
|
||||
"delegates": [
|
||||
{{ .MasterPluginJSON }}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const multusConfTemplate = `{
|
||||
"cniVersion": "{{ .CNIVersion }}",
|
||||
"name": "{{ .MasterPluginNetworkName }}",
|
||||
"type": "multus",{{
|
||||
.NestedCapabilities
|
||||
}}{{
|
||||
.NamespaceIsolationConfig
|
||||
}}{{
|
||||
.GlobalNamespacesConfig
|
||||
}}{{
|
||||
.LogToStderrConfig
|
||||
}}{{
|
||||
.LogLevelConfig
|
||||
}}{{
|
||||
.LogFileConfig
|
||||
}}{{
|
||||
.AdditionalBinDirConfig
|
||||
}}{{
|
||||
.MultusCNIConfDirConfig
|
||||
}}{{
|
||||
.ReadinessIndicatorFileConfig
|
||||
}}
|
||||
"kubeconfig": "{{ .MultusKubeConfigFileHost }}",
|
||||
"delegates": [
|
||||
{{ .MasterPluginJSON }}
|
||||
]
|
||||
}
|
||||
`
|
||||
|
||||
func (o *Options) getMasterConfigPath() (string, error) {
|
||||
// Master config file is specified
|
||||
if o.MultusMasterCNIFileName != "" {
|
||||
return filepath.Join(o.MultusAutoconfigDir, o.MultusMasterCNIFileName), nil
|
||||
}
|
||||
|
||||
// Pick the alphabetically first config file from MultusAutoconfigDir
|
||||
files, err := libcni.ConfFiles(o.MultusAutoconfigDir, []string{".conf", ".conflist"})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot find master CNI config in %q: %v", o.MultusAutoconfigDir, err)
|
||||
}
|
||||
|
||||
for _, filename := range files {
|
||||
if !strings.HasPrefix(filepath.Base(filename), "00-multus.conf") {
|
||||
return filename, nil
|
||||
}
|
||||
}
|
||||
|
||||
// No config file found
|
||||
return "", fmt.Errorf("cannot find valid master CNI config in %q", o.MultusAutoconfigDir)
|
||||
}
|
||||
|
||||
func (o *Options) createMultusConfig(prevMasterConfigFileHash []byte) (string, []byte, error) {
|
||||
masterConfigPath, err := o.getMasterConfigPath()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
masterConfigBytes, masterConfigFileHash, err := getFileAndHash(masterConfigPath)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if prevMasterConfigFileHash != nil && bytes.Equal(prevMasterConfigFileHash, masterConfigFileHash) {
|
||||
return masterConfigPath, masterConfigFileHash, nil
|
||||
}
|
||||
|
||||
if prevMasterConfigFileHash != nil {
|
||||
// don't log "recreating" on first function execution
|
||||
fmt.Printf("master config changed - recreating multus config\n")
|
||||
}
|
||||
|
||||
masterConfig := map[string]interface{}{}
|
||||
if err = json.Unmarshal(masterConfigBytes, &masterConfig); err != nil {
|
||||
return "", nil, fmt.Errorf("cannot read master CNI config json: %v", err)
|
||||
}
|
||||
|
||||
// check CNIVersion
|
||||
masterCNIVersionElem, ok := masterConfig["cniVersion"]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("cannot get cniVersion in master CNI config file %q: %v", masterConfigPath, err)
|
||||
}
|
||||
|
||||
if o.ForceCNIVersion {
|
||||
masterConfig["cniVersion"] = o.CNIVersion
|
||||
fmt.Printf("force CNI version to %q\n", o.CNIVersion)
|
||||
} else {
|
||||
masterCNIVersion := masterCNIVersionElem.(string)
|
||||
if o.CNIVersion != "" && masterCNIVersion != o.CNIVersion {
|
||||
return "", nil, fmt.Errorf("Multus cni version is %q while master plugin cni version is %q", o.CNIVersion, masterCNIVersion)
|
||||
}
|
||||
o.CNIVersion = masterCNIVersion
|
||||
}
|
||||
cniVersionConfig := o.CNIVersion
|
||||
|
||||
// check OverrideNetworkName (if true, get master plugin name, otherwise 'multus-cni-network'
|
||||
masterPluginNetworkName := "multus-cni-network"
|
||||
if o.OverrideNetworkName {
|
||||
masterPluginNetworkElem, ok := masterConfig["name"]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("cannot get name in master CNI config file %q: %v", masterConfigPath, err)
|
||||
}
|
||||
|
||||
masterPluginNetworkName = masterPluginNetworkElem.(string)
|
||||
fmt.Printf("master plugin name is overrided to %q\n", masterPluginNetworkName)
|
||||
}
|
||||
|
||||
// check capabilities (from master conf, top and 'plugins')
|
||||
masterCapabilities := map[string]bool{}
|
||||
_, isMasterConfList := masterConfig["plugins"]
|
||||
|
||||
if isMasterConfList {
|
||||
masterPluginsElem, ok := masterConfig["plugins"]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("cannot get 'plugins' field in master CNI config file %q: %v", masterConfigPath, err)
|
||||
}
|
||||
masterPlugins := masterPluginsElem.([]interface{})
|
||||
for _, v := range masterPlugins {
|
||||
pluginFields := v.(map[string]interface{})
|
||||
capabilitiesElem, ok := pluginFields["capabilities"]
|
||||
if ok {
|
||||
capabilities := capabilitiesElem.(map[string]interface{})
|
||||
for k, v := range capabilities {
|
||||
masterCapabilities[k] = v.(bool)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf("master capabilities is get from conflist\n")
|
||||
} else {
|
||||
masterCapabilitiesElem, ok := masterConfig["capabilities"]
|
||||
if ok {
|
||||
for k, v := range masterCapabilitiesElem.(map[string]interface{}) {
|
||||
masterCapabilities[k] = v.(bool)
|
||||
}
|
||||
}
|
||||
fmt.Printf("master capabilities is get from conffile\n")
|
||||
}
|
||||
nestedCapabilitiesConf := ""
|
||||
if len(masterCapabilities) != 0 {
|
||||
capabilitiesByte, err := json.Marshal(masterCapabilities)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("cannot get capabilities map: %v", err)
|
||||
}
|
||||
nestedCapabilitiesConf = fmt.Sprintf("\n \"capabilities\": %s,", string(capabilitiesByte))
|
||||
}
|
||||
|
||||
// check NamespaceIsolation
|
||||
namespaceIsolationConfig := ""
|
||||
if o.NamespaceIsolation {
|
||||
namespaceIsolationConfig = "\n \"namespaceIsolation\": true,"
|
||||
}
|
||||
|
||||
// check GlobalNamespaces
|
||||
globalNamespaceConfig := ""
|
||||
if o.GlobalNamespaces != "" {
|
||||
globalNamespaceConfig = fmt.Sprintf("\n \"globalNamespaces\": %q,", o.GlobalNamespaces)
|
||||
}
|
||||
|
||||
// check MultusLogToStderr
|
||||
logToStderrConfig := ""
|
||||
if !o.MultusLogToStderr {
|
||||
logToStderrConfig = "\n \"logToStderr\": false,"
|
||||
}
|
||||
|
||||
// check MultusLogLevel (debug/error/panic/verbose) and reject others
|
||||
logLevelConfig := ""
|
||||
logLevelStr := strings.ToLower(o.MultusLogLevel)
|
||||
switch logLevelStr {
|
||||
case "debug", "error", "panic", "verbose":
|
||||
logLevelConfig = fmt.Sprintf("\n \"logLevel\": %q,", logLevelStr)
|
||||
case "":
|
||||
// no logLevel config, skipped
|
||||
default:
|
||||
return "", nil, fmt.Errorf("Log levels should be one of: debug/verbose/error/panic, did not understand: %q", o.MultusLogLevel)
|
||||
}
|
||||
|
||||
// check MultusLogFile
|
||||
logFileConfig := ""
|
||||
if o.MultusLogFile != "" {
|
||||
logFileConfig = fmt.Sprintf("\n \"logFile\": %q,", o.MultusLogFile)
|
||||
}
|
||||
|
||||
// check AdditionalBinDir
|
||||
additionalBinDirConfig := ""
|
||||
if o.AdditionalBinDir != "" {
|
||||
additionalBinDirConfig = fmt.Sprintf("\n \"binDir\": %q,", o.AdditionalBinDir)
|
||||
}
|
||||
|
||||
// check MultusCNIConfDir
|
||||
multusCNIConfDirConfig := ""
|
||||
if o.MultusCNIConfDir != "" {
|
||||
multusCNIConfDirConfig = fmt.Sprintf("\n \"cniConf\": %q,", o.MultusCNIConfDir)
|
||||
}
|
||||
|
||||
// check ReadinessIndicatorFile
|
||||
readinessIndicatorFileConfig := ""
|
||||
if o.ReadinessIndicatorFile != "" {
|
||||
readinessIndicatorFileConfig = fmt.Sprintf("\n \"readinessindicatorfile\": %q,", o.ReadinessIndicatorFile)
|
||||
}
|
||||
|
||||
// fill .MasterPluginJSON
|
||||
masterPluginByte, err := json.Marshal(masterConfig)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("cannot encode master CNI config: %v", err)
|
||||
}
|
||||
|
||||
// generate multus config
|
||||
tempFileName := fmt.Sprintf("%s/00-multus.conf.new", o.CNIConfDir)
|
||||
fp, err := os.OpenFile(tempFileName, os.O_WRONLY|os.O_CREATE, 0600)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("cannot create multus cni temp file: %v", err)
|
||||
}
|
||||
|
||||
// use conflist template if cniVersionConfig == "1.0.0"
|
||||
multusConfFilePath := fmt.Sprintf("%s/00-multus.conf", o.CNIConfDir)
|
||||
templateMultusConfig, err := template.New("multusCNIConfig").Parse(multusConfTemplate)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("template parse error: %v", err)
|
||||
}
|
||||
|
||||
if o.CNIVersion == "1.0.0" { //Check 1.0.0 or above!
|
||||
multusConfFilePath = fmt.Sprintf("%s/00-multus.conflist", o.CNIConfDir)
|
||||
templateMultusConfig, err = template.New("multusCNIConfig").Parse(multusConflistTemplate)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("template parse error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
templateData := map[string]string{
|
||||
"CNIVersion": cniVersionConfig,
|
||||
"MasterPluginNetworkName": masterPluginNetworkName,
|
||||
"NestedCapabilities": nestedCapabilitiesConf,
|
||||
"NamespaceIsolationConfig": namespaceIsolationConfig,
|
||||
"GlobalNamespacesConfig": globalNamespaceConfig,
|
||||
"LogToStderrConfig": logToStderrConfig,
|
||||
"LogLevelConfig": logLevelConfig,
|
||||
"LogFileConfig": logFileConfig,
|
||||
"AdditionalBinDirConfig": additionalBinDirConfig,
|
||||
"MultusCNIConfDirConfig": multusCNIConfDirConfig,
|
||||
"ReadinessIndicatorFileConfig": readinessIndicatorFileConfig,
|
||||
"MultusKubeConfigFileHost": o.MultusKubeConfigFileHost, // be fixed?
|
||||
"MasterPluginJSON": string(masterPluginByte),
|
||||
}
|
||||
if err = templateMultusConfig.Execute(fp, templateData); err != nil {
|
||||
return "", nil, fmt.Errorf("cannot create multus cni config: %v", err)
|
||||
}
|
||||
|
||||
if err := fp.Sync(); err != nil {
|
||||
os.Remove(tempFileName)
|
||||
return "", nil, fmt.Errorf("cannot flush multus cni config: %v", err)
|
||||
}
|
||||
if err := fp.Close(); err != nil {
|
||||
os.Remove(tempFileName)
|
||||
return "", nil, fmt.Errorf("cannot close multus cni config: %v", err)
|
||||
}
|
||||
|
||||
if err := os.Rename(tempFileName, multusConfFilePath); err != nil {
|
||||
return "", nil, fmt.Errorf("cannot replace %q with temp file %q: %v", multusConfFilePath, tempFileName, err)
|
||||
}
|
||||
|
||||
if o.RenameConfFile {
|
||||
//masterConfigPath
|
||||
renamedMasterConfigPath := fmt.Sprintf("%s.old", masterConfigPath)
|
||||
if err := os.Rename(masterConfigPath, renamedMasterConfigPath); err != nil {
|
||||
return "", nil, fmt.Errorf("cannot move original master file to %q", renamedMasterConfigPath)
|
||||
}
|
||||
fmt.Printf("Original master file moved to %q\n", renamedMasterConfigPath)
|
||||
}
|
||||
|
||||
return masterConfigPath, masterConfigFileHash, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
opt := Options{}
|
||||
opt.addFlags()
|
||||
helpFlag := pflag.BoolP("help", "h", false, "show help message and quit")
|
||||
|
||||
pflag.Parse()
|
||||
if *helpFlag {
|
||||
pflag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err := opt.verifyFileExists()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// copy multus binary
|
||||
if !opt.SkipMultusBinaryCopy {
|
||||
// Copy
|
||||
if err = cmdutils.CopyFileAtomic(opt.MultusBinFile, opt.CNIBinDir, "_multus", "multus"); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed at multus copy: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var masterConfigHash, caHash, saTokenHash []byte
|
||||
var masterConfigFilePath string
|
||||
// copy user specified multus conf to CNI conf directory
|
||||
if opt.MultusConfFile != "auto" {
|
||||
caHash, saTokenHash, err = opt.createKubeConfig(nil, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to create multus kubeconfig: %v\n", err)
|
||||
return
|
||||
}
|
||||
confFileName := filepath.Base(opt.MultusConfFile)
|
||||
tempConfFileName := fmt.Sprintf("%s.temp", confFileName)
|
||||
if err = cmdutils.CopyFileAtomic(opt.MultusConfFile, opt.CNIConfDir, tempConfFileName, confFileName); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed at copy multus conf file: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("multus config file %s is copied.\n", opt.MultusConfFile)
|
||||
} else { // auto generate multus config
|
||||
caHash, saTokenHash, err = opt.createKubeConfig(nil, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to create multus kubeconfig: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("kubeconfig file is created.\n")
|
||||
masterConfigFilePath, masterConfigHash, err = opt.createMultusConfig(nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to create multus config: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("multus config file is created.\n")
|
||||
}
|
||||
|
||||
ctx := signals.SetupSignalHandler()
|
||||
|
||||
if opt.CleanupConfigOnExit {
|
||||
defer cleanupMultusConf(&opt)
|
||||
}
|
||||
|
||||
watchChanges := opt.CleanupConfigOnExit && opt.MultusConfFile == "auto" && !opt.SkipMultusConfWatch
|
||||
if watchChanges {
|
||||
fmt.Printf("Entering watch loop...\n")
|
||||
masterConfigExists := true
|
||||
|
||||
outer:
|
||||
for range time.Tick(1 * time.Second) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// signal received break from loop
|
||||
break outer
|
||||
default:
|
||||
// Check kubeconfig and update if different (i.e. service account updated)
|
||||
caHash, saTokenHash, err = opt.createKubeConfig(caHash, saTokenHash)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to update multus kubeconfig: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: should we watch master CNI config (by fsnotify? https://github.com/fsnotify/fsnotify)
|
||||
_, err = os.Stat(masterConfigFilePath)
|
||||
|
||||
// if masterConfigFilePath is no longer exists
|
||||
if os.IsNotExist(err) {
|
||||
if masterConfigExists {
|
||||
fmt.Printf("Master plugin @ %q has been deleted. waiting for its restoration...\n", masterConfigFilePath)
|
||||
}
|
||||
masterConfigExists = false
|
||||
continue
|
||||
}
|
||||
|
||||
if !masterConfigExists {
|
||||
fmt.Printf("Master plugin @ %q was restored. Regenerating given configuration.\n", masterConfigFilePath)
|
||||
masterConfigExists = true
|
||||
}
|
||||
|
||||
masterConfigFilePath, masterConfigHash, err = opt.createMultusConfig(masterConfigHash)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to create multus config: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// wait until signal received
|
||||
<-ctx.Done()
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupMultusConf(opt *Options) {
|
||||
// try remove multus conf
|
||||
if opt.MultusConfFile == "auto" {
|
||||
multusConfFilePath := fmt.Sprintf("%s/00-multus.conf", opt.CNIConfDir)
|
||||
_ = os.Remove(multusConfFilePath)
|
||||
|
||||
multusConfFilePath = fmt.Sprintf("%s/00-multus.conflist", opt.CNIConfDir)
|
||||
_ = os.Remove(multusConfFilePath)
|
||||
} else {
|
||||
confFileName := filepath.Base(opt.MultusConfFile)
|
||||
_ = os.Remove(filepath.Join(opt.CNIConfDir, confFileName))
|
||||
}
|
||||
|
||||
}
|
544
cmd/thin_entrypoint/main_test.go
Normal file
@@ -0,0 +1,544 @@
|
||||
package main
|
||||
|
||||
// disable dot-imports only for testing
|
||||
//revive:disable:dot-imports
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2" //nolint:golint
|
||||
. "github.com/onsi/gomega" //nolint:golint
|
||||
)
|
||||
|
||||
// chrootTestHelper performs chroot syscall, returns func to get back to original root or error if occurred
|
||||
func chrootTestHelper(path string) (func() error, error) {
|
||||
root, err := os.Open("/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := syscall.Chroot(path); err != nil {
|
||||
root.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func() error {
|
||||
defer root.Close()
|
||||
if err := root.Chdir(); err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.Chroot(".")
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestThinEntrypoint(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "thin_entrypoint")
|
||||
}
|
||||
|
||||
var _ = Describe("thin entrypoint testing", func() {
|
||||
It("always pass just example", func() {
|
||||
a := 10
|
||||
Expect(a).To(Equal(10))
|
||||
})
|
||||
|
||||
It("Run verifyFileExists() with expected environment, autoconfig", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf_dir", tmpDir)
|
||||
cniBinDir := fmt.Sprintf("%s/cni_bin_dir", tmpDir)
|
||||
multusBinFile := fmt.Sprintf("%s/multus_bin", tmpDir)
|
||||
multusConfFile := fmt.Sprintf("%s/multus_conf", tmpDir)
|
||||
|
||||
// CNIConfDir
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// CNIBinDir
|
||||
Expect(os.Mkdir(cniBinDir, 0755)).To(Succeed())
|
||||
|
||||
// MultusBinFile
|
||||
Expect(os.WriteFile(multusBinFile, nil, 0744)).To(Succeed())
|
||||
|
||||
// MultusConfFile
|
||||
Expect(os.WriteFile(multusConfFile, nil, 0744)).To(Succeed())
|
||||
|
||||
err = (&Options{
|
||||
CNIConfDir: cniConfDir,
|
||||
CNIBinDir: cniBinDir,
|
||||
MultusBinFile: multusBinFile,
|
||||
MultusConfFile: multusConfFile,
|
||||
}).verifyFileExists()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run verifyFileExists() with invalid environmentMultusConfFile", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf_dir", tmpDir)
|
||||
cniBinDir := fmt.Sprintf("%s/cni_bin_dir", tmpDir)
|
||||
multusBinFile := fmt.Sprintf("%s/multus_bin", tmpDir)
|
||||
multusConfFile := fmt.Sprintf("%s/multus_conf", tmpDir)
|
||||
|
||||
// CNIConfDir
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// CNIBinDir
|
||||
Expect(os.Mkdir(cniBinDir, 0755)).To(Succeed())
|
||||
|
||||
// MultusConfFile
|
||||
Expect(os.WriteFile(multusConfFile, nil, 0744)).To(Succeed())
|
||||
|
||||
err = (&Options{
|
||||
CNIConfDir: cniConfDir,
|
||||
CNIBinDir: cniBinDir,
|
||||
MultusBinFile: multusBinFile,
|
||||
MultusConfFile: multusConfFile,
|
||||
}).verifyFileExists()
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), default, conf", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conf", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, masterConfigHash, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
Expect(masterConfigHash).NotTo(Equal(""))
|
||||
|
||||
expectedResult := `{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"0.3.1","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conf", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), capabilities, conf", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "test1",
|
||||
"capabilities": { "bandwidth": true },
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conf", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, masterConfigHash, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
Expect(masterConfigHash).NotTo(Equal(""))
|
||||
|
||||
expectedResult := `{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"capabilities": {"bandwidth":true},
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"capabilities":{"bandwidth":true},"cniVersion":"0.3.1","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conf", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), with options, conf", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
err = os.WriteFile(fmt.Sprintf("%s/10-testcni.conf", multusAutoConfigDir), []byte(masterCNIConfig), 0755)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
masterConfigPath, masterConfigHash, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
NamespaceIsolation: true,
|
||||
GlobalNamespaces: "foobar,barfoo",
|
||||
MultusLogToStderr: true,
|
||||
MultusLogLevel: "DEBUG",
|
||||
MultusLogFile: "/tmp/foobar.log",
|
||||
AdditionalBinDir: "/tmp/add_bin_dir",
|
||||
MultusCNIConfDir: "/tmp/multus/net.d",
|
||||
ReadinessIndicatorFile: "/var/lib/foobar_indicator",
|
||||
}).createMultusConfig(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
Expect(masterConfigHash).NotTo(Equal(""))
|
||||
|
||||
expectedResult := `{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"namespaceIsolation": true,
|
||||
"globalNamespaces": "foobar,barfoo",
|
||||
"logLevel": "debug",
|
||||
"logFile": "/tmp/foobar.log",
|
||||
"binDir": "/tmp/add_bin_dir",
|
||||
"cniConf": "/tmp/multus/net.d",
|
||||
"readinessindicatorfile": "/var/lib/foobar_indicator",
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"0.3.1","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conf", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), default, conflist", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conf", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, masterConfigHash, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
Expect(masterConfigHash).NotTo(Equal(""))
|
||||
|
||||
expectedResult :=
|
||||
`{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "multus-cni-network",
|
||||
"plugins": [ {
|
||||
"type": "multus",
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"1.0.0","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conflist", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), capabilities, conflist", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "test1",
|
||||
"capabilities": { "bandwidth": true },
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conflist", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, masterConfigHash, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
Expect(masterConfigHash).NotTo(Equal(""))
|
||||
|
||||
expectedResult :=
|
||||
`{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "multus-cni-network",
|
||||
"plugins": [ {
|
||||
"type": "multus",
|
||||
"capabilities": {"bandwidth":true},
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"capabilities":{"bandwidth":true},"cniVersion":"1.0.0","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conflist", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), with options, conflist", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/10-testcni.conflist", multusAutoConfigDir), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, masterConfigHash, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
NamespaceIsolation: true,
|
||||
GlobalNamespaces: "foobar,barfoo",
|
||||
MultusLogToStderr: true,
|
||||
MultusLogLevel: "DEBUG",
|
||||
MultusLogFile: "/tmp/foobar.log",
|
||||
AdditionalBinDir: "/tmp/add_bin_dir",
|
||||
MultusCNIConfDir: "/tmp/multus/net.d",
|
||||
ReadinessIndicatorFile: "/var/lib/foobar_indicator",
|
||||
}).createMultusConfig(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
Expect(masterConfigHash).NotTo(Equal(""))
|
||||
|
||||
expectedResult :=
|
||||
`{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "multus-cni-network",
|
||||
"plugins": [ {
|
||||
"type": "multus",
|
||||
"namespaceIsolation": true,
|
||||
"globalNamespaces": "foobar,barfoo",
|
||||
"logLevel": "debug",
|
||||
"logFile": "/tmp/foobar.log",
|
||||
"binDir": "/tmp/add_bin_dir",
|
||||
"cniConf": "/tmp/multus/net.d",
|
||||
"readinessindicatorfile": "/var/lib/foobar_indicator",
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"1.0.0","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conflist", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createMultusConfig(), with options, conflist", func() {
|
||||
// create directory and files
|
||||
tmpDir, err := os.MkdirTemp("", "multus_thin_entrypoint_tmp")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
multusAutoConfigDir := fmt.Sprintf("%s/auto_conf", tmpDir)
|
||||
cniConfDir := fmt.Sprintf("%s/cni_conf", tmpDir)
|
||||
|
||||
Expect(os.Mkdir(multusAutoConfigDir, 0755)).To(Succeed())
|
||||
Expect(os.Mkdir(cniConfDir, 0755)).To(Succeed())
|
||||
|
||||
// create master CNI config
|
||||
masterCNIConfigFileName := "10-testcni.conf"
|
||||
masterCNIConfig := `
|
||||
{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "test1",
|
||||
"type": "cnitesttype"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/%s", multusAutoConfigDir, masterCNIConfigFileName), []byte(masterCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
// create another CNI config
|
||||
anotherCNIConfigFileName := "09-test2cni.conf" // Alphabetically before masterCNIConfigFileName
|
||||
anotherCNIConfig := `
|
||||
{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "test2",
|
||||
"type": "cnitest2type"
|
||||
}`
|
||||
Expect(os.WriteFile(fmt.Sprintf("%s/%s", multusAutoConfigDir, anotherCNIConfigFileName), []byte(anotherCNIConfig), 0755)).To(Succeed())
|
||||
|
||||
masterConfigPath, masterConfigHash, err := (&Options{
|
||||
MultusAutoconfigDir: multusAutoConfigDir,
|
||||
MultusMasterCNIFileName: masterCNIConfigFileName,
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusKubeConfigFileHost: "/etc/foobar_kubeconfig",
|
||||
}).createMultusConfig(nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(masterConfigPath).NotTo(Equal(""))
|
||||
Expect(masterConfigHash).NotTo(Equal(""))
|
||||
|
||||
expectedResult :=
|
||||
`{
|
||||
"cniVersion": "1.0.0",
|
||||
"name": "multus-cni-network",
|
||||
"plugins": [ {
|
||||
"type": "multus",
|
||||
"logToStderr": false,
|
||||
"kubeconfig": "/etc/foobar_kubeconfig",
|
||||
"delegates": [
|
||||
{"cniVersion":"1.0.0","name":"test1","type":"cnitesttype"}
|
||||
]
|
||||
}]
|
||||
}
|
||||
`
|
||||
conf, err := os.ReadFile(fmt.Sprintf("%s/00-multus.conflist", cniConfDir))
|
||||
Expect(string(conf)).To(Equal(expectedResult))
|
||||
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Run createKubeConfig()", func() {
|
||||
// create temp dir and files
|
||||
tmpDir := GinkgoT().TempDir()
|
||||
|
||||
cniConfDir := "/cni_conf"
|
||||
Expect(os.Mkdir(filepath.Join(tmpDir, cniConfDir), 0755)).To(Succeed())
|
||||
|
||||
multusConfDir := "/multus_conf"
|
||||
Expect(os.Mkdir(filepath.Join(tmpDir, multusConfDir), 0755)).To(Succeed())
|
||||
|
||||
// Create service account CA file and token file with dummy data
|
||||
svcAccountPath := filepath.Join(tmpDir, "var/run/secrets/kubernetes.io/serviceaccount")
|
||||
Expect(os.MkdirAll(svcAccountPath, 0755)).ToNot(HaveOccurred())
|
||||
svcAccountCAFile := filepath.Join(tmpDir, serviceAccountCAFile)
|
||||
svcAccountTokenFile := filepath.Join(tmpDir, serviceAccountTokenFile)
|
||||
Expect(os.WriteFile(svcAccountCAFile, []byte("dummy-ca-content"), 0644)).To(Succeed())
|
||||
Expect(os.WriteFile(svcAccountTokenFile, []byte("dummy-token-content"), 0644)).To(Succeed())
|
||||
|
||||
// Set up the Options struct
|
||||
options := &Options{
|
||||
CNIConfDir: cniConfDir,
|
||||
MultusCNIConfDir: multusConfDir,
|
||||
}
|
||||
|
||||
// Run the createKubeConfig function in a chroot env
|
||||
back, err := chrootTestHelper(tmpDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
caHash, saTokenHash, err := options.createKubeConfig(nil, nil)
|
||||
Expect(back()).ToNot(HaveOccurred())
|
||||
// back to original root
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(caHash).NotTo(BeNil())
|
||||
Expect(saTokenHash).NotTo(BeNil())
|
||||
|
||||
// Verify the kubeconfig file was created successfully
|
||||
kubeConfigPath := filepath.Join(tmpDir, cniConfDir, "multus.d", "multus.kubeconfig")
|
||||
content, err := os.ReadFile(kubeConfigPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(content).NotTo(BeEmpty())
|
||||
|
||||
// Cleanup
|
||||
Expect(os.RemoveAll(tmpDir)).To(Succeed())
|
||||
})
|
||||
|
||||
})
|
227
deployments/multus-daemonset-crio.yml
Normal file
@@ -0,0 +1,227 @@
|
||||
# Note:
|
||||
# This deployment file is designed for 'quickstart' of multus, easy installation to test it,
|
||||
# hence this deployment yaml does not care about following things intentionally.
|
||||
# - various configuration options
|
||||
# - minor deployment scenario
|
||||
# - upgrade/update/uninstall scenario
|
||||
# Multus team understand users deployment scenarios are diverse, hence we do not cover
|
||||
# comprehensive deployment scenario. We expect that it is covered by each platform deployment.
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: network-attachment-definitions.k8s.cni.cncf.io
|
||||
spec:
|
||||
group: k8s.cni.cncf.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: network-attachment-definitions
|
||||
singular: network-attachment-definition
|
||||
kind: NetworkAttachmentDefinition
|
||||
shortNames:
|
||||
- net-attach-def
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'NetworkAttachmentDefinition is a CRD schema specified by the Network Plumbing
|
||||
Working Group to express the intent for attaching pods to one or more logical or physical
|
||||
networks. More information available at: https://github.com/k8snetworkplumbingwg/multi-net-spec'
|
||||
type: object
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this represen
|
||||
tation of an object. Servers should convert recognized schemas to the
|
||||
latest internal value, and may reject unrecognized values. More info:
|
||||
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: 'NetworkAttachmentDefinition spec defines the desired state of a network attachment'
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
description: 'NetworkAttachmentDefinition config is a JSON-formatted CNI configuration'
|
||||
type: string
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
rules:
|
||||
- apiGroups: ["k8s.cni.cncf.io"]
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/status
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
- events.k8s.io
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: multus
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: multus-cni-config
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
data:
|
||||
# NOTE: If you'd prefer to manually apply a configuration file, you may create one here.
|
||||
# In the case you'd like to customize the Multus installation, you should change the arguments to the Multus pod
|
||||
# change the "args" line below from
|
||||
# - "--multus-conf-file=auto"
|
||||
# to:
|
||||
# "--multus-conf-file=/tmp/multus-conf/70-multus.conf"
|
||||
# Additionally -- you should ensure that the name "70-multus.conf" is the alphabetically first name in the
|
||||
# /etc/cni/net.d/ directory on each node, otherwise, it will not be used by the Kubelet.
|
||||
cni-conf.json: |
|
||||
{
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"capabilities": {
|
||||
"portMappings": true
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "default-cni-network",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "flannel",
|
||||
"name": "flannel.1",
|
||||
"delegate": {
|
||||
"isDefaultGateway": true,
|
||||
"hairpinMode": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "portmap",
|
||||
"capabilities": {
|
||||
"portMappings": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig"
|
||||
}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-multus-ds
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
name: multus
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
hostNetwork: true
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
- operator: Exists
|
||||
effect: NoExecute
|
||||
serviceAccountName: multus
|
||||
containers:
|
||||
- name: kube-multus
|
||||
# crio support requires multus:latest for now. support 3.3 or later.
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
|
||||
command: ["/entrypoint.sh"]
|
||||
args:
|
||||
- "--cni-version=0.3.1"
|
||||
- "--cni-bin-dir=/host/usr/libexec/cni"
|
||||
- "--multus-conf-file=auto"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
capabilities:
|
||||
add: ["SYS_ADMIN"]
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- name: run
|
||||
mountPath: /run
|
||||
mountPropagation: HostToContainer
|
||||
- name: cni
|
||||
mountPath: /host/etc/cni/net.d
|
||||
- name: cnibin
|
||||
mountPath: /host/usr/libexec/cni
|
||||
- name: multus-cfg
|
||||
mountPath: /tmp/multus-conf
|
||||
terminationGracePeriodSeconds: 10
|
||||
volumes:
|
||||
- name: run
|
||||
hostPath:
|
||||
path: /run
|
||||
- name: cni
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
- name: cnibin
|
||||
hostPath:
|
||||
path: /usr/libexec/cni
|
||||
- name: multus-cfg
|
||||
configMap:
|
||||
name: multus-cni-config
|
||||
items:
|
||||
- key: cni-conf.json
|
||||
path: 70-multus.conf
|
249
deployments/multus-daemonset-thick.yml
Normal file
@@ -0,0 +1,249 @@
|
||||
# Note:
|
||||
# This deployment file is designed for 'quickstart' of multus, easy installation to test it,
|
||||
# hence this deployment yaml does not care about following things intentionally.
|
||||
# - various configuration options
|
||||
# - minor deployment scenario
|
||||
# - upgrade/update/uninstall scenario
|
||||
# Multus team understand users deployment scenarios are diverse, hence we do not cover
|
||||
# comprehensive deployment scenario. We expect that it is covered by each platform deployment.
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: network-attachment-definitions.k8s.cni.cncf.io
|
||||
spec:
|
||||
group: k8s.cni.cncf.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: network-attachment-definitions
|
||||
singular: network-attachment-definition
|
||||
kind: NetworkAttachmentDefinition
|
||||
shortNames:
|
||||
- net-attach-def
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'NetworkAttachmentDefinition is a CRD schema specified by the Network Plumbing
|
||||
Working Group to express the intent for attaching pods to one or more logical or physical
|
||||
networks. More information available at: https://github.com/k8snetworkplumbingwg/multi-net-spec'
|
||||
type: object
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this represen
|
||||
tation of an object. Servers should convert recognized schemas to the
|
||||
latest internal value, and may reject unrecognized values. More info:
|
||||
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: 'NetworkAttachmentDefinition spec defines the desired state of a network attachment'
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
description: 'NetworkAttachmentDefinition config is a JSON-formatted CNI configuration'
|
||||
type: string
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
rules:
|
||||
- apiGroups: ["k8s.cni.cncf.io"]
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/status
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
- events.k8s.io
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: multus
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: multus-daemon-config
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
data:
|
||||
daemon-config.json: |
|
||||
{
|
||||
"chrootDir": "/hostroot",
|
||||
"cniVersion": "0.3.1",
|
||||
"logLevel": "verbose",
|
||||
"logToStderr": true,
|
||||
"cniConfigDir": "/host/etc/cni/net.d",
|
||||
"multusAutoconfigDir": "/host/etc/cni/net.d",
|
||||
"multusConfigFile": "auto",
|
||||
"socketDir": "/host/run/multus/"
|
||||
}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-multus-ds
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
name: multus
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
- operator: Exists
|
||||
effect: NoExecute
|
||||
serviceAccountName: multus
|
||||
containers:
|
||||
- name: kube-multus
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:snapshot-thick
|
||||
command: [ "/usr/src/multus-cni/bin/multus-daemon" ]
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- name: cni
|
||||
mountPath: /host/etc/cni/net.d
|
||||
# multus-daemon expects that cnibin path must be identical between pod and container host.
|
||||
# e.g. if the cni bin is in '/opt/cni/bin' on the container host side, then it should be mount to '/opt/cni/bin' in multus-daemon,
|
||||
# not to any other directory, like '/opt/bin' or '/usr/bin'.
|
||||
- name: cnibin
|
||||
mountPath: /opt/cni/bin
|
||||
- name: host-run
|
||||
mountPath: /host/run
|
||||
- name: host-var-lib-cni-multus
|
||||
mountPath: /var/lib/cni/multus
|
||||
- name: host-var-lib-kubelet
|
||||
mountPath: /var/lib/kubelet
|
||||
mountPropagation: HostToContainer
|
||||
- name: host-run-k8s-cni-cncf-io
|
||||
mountPath: /run/k8s.cni.cncf.io
|
||||
- name: host-run-netns
|
||||
mountPath: /run/netns
|
||||
mountPropagation: HostToContainer
|
||||
- name: multus-daemon-config
|
||||
mountPath: /etc/cni/net.d/multus.d
|
||||
readOnly: true
|
||||
- name: hostroot
|
||||
mountPath: /hostroot
|
||||
mountPropagation: HostToContainer
|
||||
env:
|
||||
- name: MULTUS_NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
initContainers:
|
||||
- name: install-multus-binary
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:snapshot-thick
|
||||
command:
|
||||
- "sh"
|
||||
- "-c"
|
||||
- "cp /usr/src/multus-cni/bin/multus-shim /host/opt/cni/bin/multus-shim && cp /usr/src/multus-cni/bin/passthru /host/opt/cni/bin/passthru"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "10m"
|
||||
memory: "15Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- name: cnibin
|
||||
mountPath: /host/opt/cni/bin
|
||||
mountPropagation: Bidirectional
|
||||
terminationGracePeriodSeconds: 10
|
||||
volumes:
|
||||
- name: cni
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
- name: cnibin
|
||||
hostPath:
|
||||
path: /opt/cni/bin
|
||||
- name: hostroot
|
||||
hostPath:
|
||||
path: /
|
||||
- name: multus-daemon-config
|
||||
configMap:
|
||||
name: multus-daemon-config
|
||||
items:
|
||||
- key: daemon-config.json
|
||||
path: daemon-config.json
|
||||
- name: host-run
|
||||
hostPath:
|
||||
path: /run
|
||||
- name: host-var-lib-cni-multus
|
||||
hostPath:
|
||||
path: /var/lib/cni/multus
|
||||
- name: host-var-lib-kubelet
|
||||
hostPath:
|
||||
path: /var/lib/kubelet
|
||||
- name: host-run-k8s-cni-cncf-io
|
||||
hostPath:
|
||||
path: /run/k8s.cni.cncf.io
|
||||
- name: host-run-netns
|
||||
hostPath:
|
||||
path: /run/netns/
|
236
deployments/multus-daemonset.yml
Normal file
@@ -0,0 +1,236 @@
|
||||
# Note:
|
||||
# This deployment file is designed for 'quickstart' of multus, easy installation to test it,
|
||||
# hence this deployment yaml does not care about following things intentionally.
|
||||
# - various configuration options
|
||||
# - minor deployment scenario
|
||||
# - upgrade/update/uninstall scenario
|
||||
# Multus team understand users deployment scenarios are diverse, hence we do not cover
|
||||
# comprehensive deployment scenario. We expect that it is covered by each platform deployment.
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: network-attachment-definitions.k8s.cni.cncf.io
|
||||
spec:
|
||||
group: k8s.cni.cncf.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: network-attachment-definitions
|
||||
singular: network-attachment-definition
|
||||
kind: NetworkAttachmentDefinition
|
||||
shortNames:
|
||||
- net-attach-def
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: 'NetworkAttachmentDefinition is a CRD schema specified by the Network Plumbing
|
||||
Working Group to express the intent for attaching pods to one or more logical or physical
|
||||
networks. More information available at: https://github.com/k8snetworkplumbingwg/multi-net-spec'
|
||||
type: object
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this represen
|
||||
tation of an object. Servers should convert recognized schemas to the
|
||||
latest internal value, and may reject unrecognized values. More info:
|
||||
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: 'NetworkAttachmentDefinition spec defines the desired state of a network attachment'
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
description: 'NetworkAttachmentDefinition config is a JSON-formatted CNI configuration'
|
||||
type: string
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
rules:
|
||||
- apiGroups: ["k8s.cni.cncf.io"]
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/status
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
- events.k8s.io
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: multus
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: multus-cni-config
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
data:
|
||||
# NOTE: If you'd prefer to manually apply a configuration file, you may create one here.
|
||||
# In the case you'd like to customize the Multus installation, you should change the arguments to the Multus pod
|
||||
# change the "args" line below from
|
||||
# - "--multus-conf-file=auto"
|
||||
# to:
|
||||
# "--multus-conf-file=/tmp/multus-conf/70-multus.conf"
|
||||
# Additionally -- you should ensure that the name "70-multus.conf" is the alphabetically first name in the
|
||||
# /etc/cni/net.d/ directory on each node, otherwise, it will not be used by the Kubelet.
|
||||
cni-conf.json: |
|
||||
{
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"capabilities": {
|
||||
"portMappings": true
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "default-cni-network",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "flannel",
|
||||
"name": "flannel.1",
|
||||
"delegate": {
|
||||
"isDefaultGateway": true,
|
||||
"hairpinMode": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "portmap",
|
||||
"capabilities": {
|
||||
"portMappings": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig"
|
||||
}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-multus-ds
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
name: multus
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
hostNetwork: true
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
- operator: Exists
|
||||
effect: NoExecute
|
||||
serviceAccountName: multus
|
||||
containers:
|
||||
- name: kube-multus
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:snapshot
|
||||
command: ["/thin_entrypoint"]
|
||||
args:
|
||||
- "--multus-conf-file=auto"
|
||||
- "--multus-autoconfig-dir=/host/etc/cni/net.d"
|
||||
- "--cni-conf-dir=/host/etc/cni/net.d"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- name: cni
|
||||
mountPath: /host/etc/cni/net.d
|
||||
- name: cnibin
|
||||
mountPath: /host/opt/cni/bin
|
||||
- name: multus-cfg
|
||||
mountPath: /tmp/multus-conf
|
||||
initContainers:
|
||||
- name: install-multus-binary
|
||||
image: ghcr.io/k8snetworkplumbingwg/multus-cni:snapshot
|
||||
command: ["/install_multus"]
|
||||
args:
|
||||
- "--type"
|
||||
- "thin"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "10m"
|
||||
memory: "15Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
terminationMessagePolicy: FallbackToLogsOnError
|
||||
volumeMounts:
|
||||
- name: cnibin
|
||||
mountPath: /host/opt/cni/bin
|
||||
mountPropagation: Bidirectional
|
||||
terminationGracePeriodSeconds: 10
|
||||
volumes:
|
||||
- name: cni
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
- name: cnibin
|
||||
hostPath:
|
||||
path: /opt/cni/bin
|
||||
- name: multus-cfg
|
||||
configMap:
|
||||
name: multus-cni-config
|
||||
items:
|
||||
- key: cni-conf.json
|
||||
path: 70-multus.conf
|
428
docs/configuration.md
Normal file
@@ -0,0 +1,428 @@
|
||||
# Multus-cni Configuration Reference
|
||||
|
||||
## Introduction
|
||||
|
||||
Aside from setting options for Multus, one of the goals of configuration is to set the configuration for your *default network*. The default network is also sometimes referred as the "primary CNI plugin", the "primary network", or a "default CNI plugin" and is the CNI plugin that is used to implement [the Kubernetes networking model](https://kubernetes.io/docs/concepts/services-networking/#the-kubernetes-network-model) in your cluster. Common examples include Flannel, Weave, Calico, Cillium, and OVN-Kubernetes, among others.
|
||||
|
||||
Here we will refer to this as your default CNI plugin or default network.
|
||||
|
||||
## Example configuration
|
||||
|
||||
Following is the example of multus config file, in `/etc/cni/net.d/`.
|
||||
|
||||
Example configuration using `clusterNetwork` (see also [using delegates](#using-delegates))
|
||||
|
||||
```
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "node-cni-network",
|
||||
"type": "multus",
|
||||
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
|
||||
"confDir": "/etc/cni/multus/net.d",
|
||||
"cniDir": "/var/lib/cni/multus",
|
||||
"binDir": "/opt/cni/bin",
|
||||
"logFile": "/var/log/multus.log",
|
||||
"logLevel": "debug",
|
||||
"logOptions": {
|
||||
"maxAge": 5,
|
||||
"maxSize": 100,
|
||||
"maxBackups": 5,
|
||||
"compress": true
|
||||
},
|
||||
"capabilities": {
|
||||
"portMappings": true
|
||||
},
|
||||
"namespaceIsolation": false,
|
||||
"clusterNetwork": "/etc/cni/net.d/99-flannel.conf",
|
||||
"defaultNetworks": ["sidecarCRD", "exampleNetwork"],
|
||||
"systemNamespaces": ["kube-system", "admin"],
|
||||
"multusNamespace": "kube-system",
|
||||
"auxiliaryCNIChainName": "cni-chain-config",
|
||||
allowTryDeleteOnErr: false
|
||||
}
|
||||
```
|
||||
|
||||
## Index of configuration options
|
||||
|
||||
This is a general index of options, however note that you must set either the `clusterNetwork` or `delegates` options, see the following sections after the index for details.
|
||||
|
||||
* `name` (string, required): The name of the network
|
||||
* `type` (string, required): Must be set to the value of "multus"
|
||||
* `confDir` (string, optional): directory for CNI config file that multus reads. default `/etc/cni/multus/net.d`
|
||||
* `cniDir` (string, optional): Multus CNI data directory, default `/var/lib/cni/multus`
|
||||
* `binDir` (string, optional): additional directory for CNI plugins which multus calls, in addition to the default (the default is typically set to `/opt/cni/bin`)
|
||||
* `kubeconfig` (string, optional): kubeconfig file for the out of cluster communication with kube-apiserver. See the example [kubeconfig](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/node-kubeconfig.yaml). If you would like to use CRD (i.e. network attachment definition), this is required
|
||||
* [`logToStderr`](#Logging-via-STDERR) (bool, optional): Enable or disable logging to `STDERR`. Defaults to true.
|
||||
* [`logFile`](#Writing-to-a-Log-File) (string, optional): file path for log file. multus puts log in given file
|
||||
* [`logLevel`](#Logging-Level) (string, optional): logging level (values in decreasing order of verbosity: "debug", "error", "verbose", or "panic")
|
||||
* [`logOptions`](#Logging-Options) (object, optional): logging option, More detailed log configuration
|
||||
* [`namespaceIsolation`](#Namespace-Isolation) (boolean, optional): Enables a security feature where pods are only allowed to access `NetworkAttachmentDefinitions` in the namespace where the pod resides. Defaults to false.
|
||||
* [`globalNamespaces`](#Allow-specific-namespaces-to-be-used-across-namespaces-when-using-namespace-isolation): (string, optional): Used only when `namespaceIsolation` is true, allows specification of comma-delimited list of namespaces which may be referred to outside of namespace isolation.
|
||||
* `capabilities` ({}list, optional): [capabilities](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md#dynamic-plugin-specific-fields-capabilities--runtime-configuration) supported by at least one of the delegates. (NOTE: Multus only supports portMappings/Bandwidth capability for cluster networks).
|
||||
* [`readinessindicatorfile`](#Default-Network-Readiness-Indicator): The path to a file whose existence denotes that the default network is ready
|
||||
message to next when some missing error. Defaults to false.
|
||||
* `systemNamespaces` ([]string, optional): list of namespaces for Kubernetes system (namespaces listed here will not have `defaultNetworks` added)
|
||||
* `multusNamespace` (string, optional): namespace for `clusterNetwork`/`defaultNetworks` (the default value is `kube-system`)
|
||||
* `retryDeleteOnError` (bool, optional): Enable or disable delegate DEL
|
||||
* [`auxiliaryCNIChainName`](#auxiliaryCNIChainName) (string, optional): Enable loading CNI configurations from disk as chained plugins in an auxiliary CNI chain
|
||||
|
||||
### Using `clusterNetwork`
|
||||
|
||||
Using the `clusterNetwork` option and the `delegates` are **mutually exclusive**. If `clusterNetwork` is set, the `delegates` field is *ignored*.
|
||||
|
||||
You **must** set one or the other.
|
||||
|
||||
Therefore:
|
||||
|
||||
* Set `clusterNetwork` and if this is set, optionally set the `defaultNetworks`.
|
||||
* OR you **must** set `delegates`.
|
||||
|
||||
Options:
|
||||
|
||||
* `clusterNetwork` (string, required if not using `delegates`): the default CNI plugin to be executed.
|
||||
* `defaultNetworks` ([]string, optional): Additional / secondary network attachment that is always attached to each pod.
|
||||
|
||||
The following values are valid for both `clusterNetwork` and `defaultNetworks` and are processed in the following order:
|
||||
|
||||
* The name of a `NetworkAttachmentDefinition` custom resource in the namespace specified by the `multusNamespace` configuration option
|
||||
* The `"name"` value in the contents of a CNI JSON configuration file in the CNI configuration directory,
|
||||
* The given name for `clusterNetwork` should match the value for `name` key in the contents of the CNI JSON file (e.g. `"name": "test"` in `my.conf` when `"clusterNetwork": "test"`)
|
||||
* A path to a directory containing CNI json configuration files. The alphabetically first file will be used.
|
||||
* Absolute file path for CNI config file
|
||||
* If none of the above are found using the value, Multus will raise an error.
|
||||
|
||||
If for example you have `defaultNetworks` set as:
|
||||
|
||||
```
|
||||
"defaultNetworks": ["sidecarNetwork", "exampleNetwork"],
|
||||
```
|
||||
|
||||
In this example, the values in the expression refer to `NetworkAttachmentDefinition` custom resource names. Therefore, there must be `NetworkAttachmentDefinitions` already created with the names `sidecarNetwork` and `exampleNetwork`.
|
||||
|
||||
This means that in addition to the cluster network, each pod would be assigned two additional networks by default, and the pod would present three interfaces, e.g. `eth0`, `net1`, and `net2`, with `net1` and `net2` being set by the above described `NetworkAttachmentDefinitions`. Additional attachments as made by setting `k8s.v1.cni.cncf.io/networks` on pods will be made in addition to those set in the `defaultNetworks` configuration option.
|
||||
|
||||
### Using `delegates`
|
||||
|
||||
If `clusterNetwork` is not set, you **must** use `delegates`.
|
||||
|
||||
* `delegates` ([]map, required if not using `clusterNetwork`). List of CNI configurations to be used as your default CNI plugin(s).
|
||||
|
||||
Example configuration using `delegates`:
|
||||
|
||||
```
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "node-cni-network",
|
||||
"type": "multus",
|
||||
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
|
||||
"confDir": "/etc/cni/multus/net.d",
|
||||
"cniDir": "/var/lib/cni/multus",
|
||||
"binDir": "/opt/cni/bin",
|
||||
"delegates": [{
|
||||
"type": "weave-net",
|
||||
"hairpinMode": true
|
||||
}, {
|
||||
"type": "macvlan",
|
||||
... (snip)
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Option Details
|
||||
|
||||
### Default Network Readiness Indicator
|
||||
|
||||
You may desire that your default network becomes ready before attaching networks with Multus. This is disabled by default and not used unless you set the `readinessindicatorfile` option to a non-blank value.
|
||||
|
||||
For example, if you use Flannel as a default network, the recommended method for Flannel to be installed is via a daemonset that also drops a configuration file in `/etc/cni/net.d/`. This may apply to other plugins that place that configuration file upon their readiness, therefore, Multus uses their configuration filename as a semaphore and optionally waits to attach networks to pods until that file exists.
|
||||
|
||||
In this manner, you may prevent pods from crash looping, and instead wait for that default network to be ready.
|
||||
|
||||
Only one option is necessary to configure this functionality:
|
||||
|
||||
* `readinessindicatorfile`: The path to a file whose existence denotes that the default network is ready.
|
||||
|
||||
*NOTE*: If `readinessindicatorfile` is unset, or is an empty string, this functionality will be disabled, and is disabled by default.
|
||||
|
||||
|
||||
### Logging
|
||||
|
||||
You may wish to enable some enhanced logging for Multus, especially during the process where you're configuring Multus and need to understand what is or isn't working with your particular configuration.
|
||||
|
||||
#### Logging via STDERR
|
||||
|
||||
By default, Multus will log via `STDERR`, which is the standard method by which CNI plugins communicate errors, and these errors are logged by the Kubelet.
|
||||
|
||||
Optionally, you may disable this method by setting the `logToStderr` option in your CNI configuration:
|
||||
|
||||
```
|
||||
"logToStderr": false,
|
||||
```
|
||||
|
||||
#### Writing to a Log File
|
||||
|
||||
Optionally, you may have Multus log to a file on the filesystem. This file will be written locally on each node where Multus is executed. You may configure this via the `LogFile` option in the CNI configuration. By default this additional logging to a flat file is disabled.
|
||||
|
||||
For example in your CNI configuration, you may set:
|
||||
|
||||
```
|
||||
"logFile": "/var/log/multus.log",
|
||||
```
|
||||
|
||||
#### Logging Level
|
||||
|
||||
The default logging level is set as `panic` -- this will log only the most critical errors, and is the least verbose logging level.
|
||||
|
||||
The available logging level values, in decreasing order of verbosity are:
|
||||
|
||||
* `debug`
|
||||
* `verbose`
|
||||
* `error`
|
||||
* `panic`
|
||||
|
||||
You may configure the logging level by using the `LogLevel` option in your CNI configuration. For example:
|
||||
|
||||
```
|
||||
"logLevel": "debug",
|
||||
```
|
||||
|
||||
#### Logging Options
|
||||
|
||||
If you want a more detailed configuration of the logging, This includes the following parameters:
|
||||
|
||||
* `maxAge` the maximum number of days to retain old log files in their filename
|
||||
* `maxSize` the maximum size in megabytes of the log file before it gets rotated
|
||||
* `maxBackups` the maximum number of days to retain old log files in their filename
|
||||
* `compress` compress determines if the rotated log files should be compressed using gzip
|
||||
|
||||
For example in your CNI configuration, you may set:
|
||||
|
||||
```
|
||||
"logOptions": {
|
||||
"maxAge": 5,
|
||||
"maxSize": 100,
|
||||
"maxBackups": 5,
|
||||
"compress": true
|
||||
}
|
||||
```
|
||||
|
||||
### Namespace Isolation
|
||||
|
||||
The functionality provided by the `namespaceIsolation` configuration option enables a mode where Multus only allows pods to access custom resources (the `NetworkAttachmentDefinitions`) within the namespace where that pod resides. In other words, the `NetworkAttachmentDefinitions` are isolated to usage within the namespace in which they're created.
|
||||
|
||||
**NOTE**: The default namespace is special in this scenario. Even with namespace isolation enabled, any pod, in any namespace is allowed to refer to `NetworkAttachmentDefinitions` in the default namespace. This allows you to create commonly used unprivileged `NetworkAttachmentDefinitions` without having to put them in all namespaces. For example, if you had a `NetworkAttachmentDefinition` named `foo` the default namespace, you may reference it in an annotation with: `default/foo`.
|
||||
|
||||
**NOTE**: You can also add additional namespaces which can be referred to globally using the `global-namespaces` option (see next section).
|
||||
|
||||
For example, if a pod is created in the namespace called `development`, Multus will not allow networks to be attached when defined by custom resources created in a different namespace, say in the `default` network.
|
||||
|
||||
Consider the situation where you have a system that has users of different privilege levels -- as an example, a platform which has two administrators: a Senior Administrator and a Junior Administrator. The Senior Administrator may have access to all namespaces, and some network configurations as used by Multus are considered to be privileged in that they allow access to some protected resources available on the network. However, the Junior Administrator has access to only a subset of namespaces, and therefore it should be assumed that the Junior Administrator cannot create pods in their limited subset of namespaces. The `namespaceIsolation` feature provides for this isolation, allowing pods created in given namespaces to only access custom resources in the same namespace as the pod.
|
||||
|
||||
Namespace Isolation is disabled by default.
|
||||
|
||||
#### Configuration example
|
||||
|
||||
```
|
||||
"namespaceIsolation": true,
|
||||
```
|
||||
|
||||
#### Usage example
|
||||
|
||||
Let's setup an example where we:
|
||||
|
||||
* Create a custom resource in a namespace called `privileged`
|
||||
* Create a pod in a namespace called `development`, and have annotations that reference a custom resource in the `privileged` namespace. The creation of this pod should be disallowed by Multus (as we'll have the use of the custom resources limited only to those custom resources created within the same namespace as the pod).
|
||||
|
||||
Given the above scenario with a Junior & Senior Administrator. You may assume that the Senior Administrator has access to all namespaces, whereas the Junior Administrator has access only to the `development` namespace.
|
||||
|
||||
Firstly, we show that we have a number of namespaces available:
|
||||
|
||||
```
|
||||
# List the available namespaces
|
||||
[user@kube-master ~]$ kubectl get namespaces
|
||||
NAME STATUS AGE
|
||||
default Active 7h27m
|
||||
development Active 3h
|
||||
kube-public Active 7h27m
|
||||
kube-system Active 7h27m
|
||||
privileged Active 4s
|
||||
```
|
||||
|
||||
We'll create a `NetworkAttachmentDefinition` in the `privileged` namespace.
|
||||
|
||||
```
|
||||
# Show the network attachment definition we're creating.
|
||||
[user@kube-master ~]$ cat cr.yml
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: macvlan-conf
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth0",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "192.168.1.0/24",
|
||||
"rangeStart": "192.168.1.200",
|
||||
"rangeEnd": "192.168.1.216",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
],
|
||||
"gateway": "192.168.1.1"
|
||||
}
|
||||
}'
|
||||
|
||||
# Create that network attachment definition in the privileged namespace
|
||||
[user@kube-master ~]$ kubectl create -f cr.yml -n privileged
|
||||
networkattachmentdefinition.k8s.cni.cncf.io/macvlan-conf created
|
||||
|
||||
# List the available network attachment definitions in the privileged namespace.
|
||||
[user@kube-master ~]$ kubectl get networkattachmentdefinition.k8s.cni.cncf.io -n privileged
|
||||
NAME AGE
|
||||
macvlan-conf 11s
|
||||
```
|
||||
|
||||
Next, we'll create a pod with an annotation that references the privileged namespace. Pay particular attention to the annotation that reads `k8s.v1.cni.cncf.io/networks: privileged/macvlan-conf` -- where it contains a reference to a `namespace/configuration-name` formatted network attachment name. In this case referring to the `macvlan-conf` in the namespace called `privileged`.
|
||||
|
||||
```
|
||||
# Show the yaml for a pod.
|
||||
[user@kube-master ~]$ cat example.pod.yml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: samplepod
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: privileged/macvlan-conf
|
||||
spec:
|
||||
containers:
|
||||
- name: samplepod
|
||||
command: ["/bin/bash", "-c", "sleep 2000000000000"]
|
||||
image: dougbtv/centos-network
|
||||
|
||||
# Create that pod.
|
||||
[user@kube-master ~]$ kubectl create -f example.pod.yml -n development
|
||||
pod/samplepod created
|
||||
```
|
||||
|
||||
You'll note that pod fails to spawn successfully. If you check the Multus logs, you'll see an entry such as:
|
||||
|
||||
```
|
||||
2018-12-18T21:41:32Z [error] GetNetworkDelegates: namespace isolation enabled, annotation violates permission, pod is in namespace development but refers to target namespace privileged
|
||||
```
|
||||
|
||||
This error expresses that the pod resides in the namespace named `development` but refers to a `NetworkAttachmentDefinition` outside of that namespace, in this case, the namespace named `privileged`.
|
||||
|
||||
In a positive example, you'd instead create the `NetworkAttachmentDefinition` in the `development` namespace, and you'd have an annotation that either A. does not reference a namespace, or B. refers to the same annotation.
|
||||
|
||||
A positive example may be:
|
||||
|
||||
```
|
||||
# Create the same NetworkAttachmentDefinition as above, however in the development namespace
|
||||
[user@kube-master ~]$ kubectl create -f cr.yml -n development
|
||||
networkattachmentdefinition.k8s.cni.cncf.io/macvlan-conf created
|
||||
|
||||
# Show the yaml for a sample pod which references macvlan-conf without a namspace/ format
|
||||
[user@kube-master ~]$ cat positive.example.pod
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: samplepod
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: macvlan-conf
|
||||
spec:
|
||||
containers:
|
||||
- name: samplepod
|
||||
command: ["/bin/bash", "-c", "sleep 2000000000000"]
|
||||
image: dougbtv/centos-network
|
||||
|
||||
# Create that pod.
|
||||
[user@kube-master ~]$ kubectl create -f positive.example.pod -n development
|
||||
pod/samplepod created
|
||||
|
||||
# We can see that this pod has been launched successfully.
|
||||
[user@kube-master ~]$ kubectl get pods -n development
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
samplepod 1/1 Running 0 31s
|
||||
```
|
||||
|
||||
### Allow specific namespaces to be used across namespaces when using namespace isolation
|
||||
|
||||
The `globalNamespaces` configuration option is only used when `namespaceIsolation` is set to true. `globalNamespaces` specifies a comma-delimited list of namespaces which can be referred to from outside of any given namespace in which a pod resides.
|
||||
|
||||
```
|
||||
"globalNamespaces": "default,namespace-a,namespace-b",
|
||||
```
|
||||
|
||||
Note that when using `globalNamespaces` the `default` namespace must be specified in the list if you wish to use that namespace, when `globalNamespaces` is not set, the `default` namespace is implied to be used across namespaces.
|
||||
|
||||
### Specify default cluster network in Pod annotations
|
||||
|
||||
Users may also specify the default network for any given pod (via annotation), for cases where there are multiple cluster networks available within a Kubernetes cluster.
|
||||
|
||||
Example use cases may include:
|
||||
|
||||
1. During a migration from one default network to another (e.g. from Flannel to Calico), it may be practical if both network solutions are able to operate in parallel. Users can then control which network a pod should attach to during the transition period.
|
||||
2. Some users may deploy multiple cluster networks for the sake of their security considerations, and may desire to specify the default network for individual pods.
|
||||
|
||||
Follow these steps to specify the default network on a pod-by-pod basis:
|
||||
|
||||
1. First, you need to define all your cluster networks as network-attachment-definition objects.
|
||||
|
||||
2. Next, you can specify the network you want in pods with the `v1.multus-cni.io/default-network` annotation. Pods which do not specify this annotation will keep using the CNI as defined in the Multus config file.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-example
|
||||
annotations:
|
||||
v1.multus-cni.io/default-network: calico-conf
|
||||
...
|
||||
```
|
||||
|
||||
### `auxiliaryCNIChainName`
|
||||
|
||||
`auxiliaryCNIChainName` (of value string) is used to express the name of an additional auxiliary CNI chain that will execute in order to composably execute chained CNI plugins from configurations on the host's disk in a subdirectory of the CNI configuration directory.
|
||||
|
||||
**NOTE**: The path used to determine the base for the subdirectory is the pathname of the `clusterNetwork` value, which must be set to a file in order to use this functionality.
|
||||
|
||||
When this string is set, Multus will execute an additional CNI chain, outside of the default network, on its own independent CNI chain (as to not interfere with default network functionality that might be hampered by CNI chaining and to otherwise isolate this execution) and will load CNI configurations from a subdirectory of the same name in the CNI configuration directory.
|
||||
|
||||
This feature is based on [improvements made to libcni for "safe subdirectory-based plugin conf loading"](https://github.com/containernetworking/cni/pull/1052).
|
||||
|
||||
`auxiliaryCNIChainName` is meant to be set as a CNI configuration name, this name is arbitrary but must match the subdirectory name.
|
||||
|
||||
Consider this [daemon configuration](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/deployments/multus-daemonset-thick.yml#L113):
|
||||
|
||||
```
|
||||
{
|
||||
"cniConfigDir": "/host/etc/cni/net.d",
|
||||
"multusAutoconfigDir": "/host/etc/cni/net.d",
|
||||
"multusConfigFile": "auto",
|
||||
"socketDir": "/host/run/multus/",
|
||||
"auxiliaryCNIChainName": "cni-chain-config"
|
||||
}
|
||||
```
|
||||
|
||||
Here we have set `"auxiliaryCNIChainName": "cni-chain-config"`, and we have expressed that our CNI configurations are on `/etc/cni/net.d/` on the host.
|
||||
|
||||
In this case, we would also have a directory named in `/etc/cni/net.d/cni-chain-config`
|
||||
|
||||
One could add any number of CNI configurations to be used as part of this chain, consider this example if we added a tuning CNI configuration called `/etc/cni/net.d/cni-chain-config/mytuning.conf` with these contents:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "mytuning",
|
||||
"type": "tuning",
|
||||
"sysctl": {
|
||||
"net.ipv4.conf.IFNAME.arp_filter": "1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
With the given configuration, plus this configuration, this would be executed for every pod launched by Multus CNI.
|
||||
|
||||
If this is unset, no auxiliary chain will be executed. However, if the default network CNI configuration is loaded from disk and is a conflist format, the libcni functionality for loading from a subdirectory will still apply.
|
69
docs/development.md
Normal file
@@ -0,0 +1,69 @@
|
||||
## Development/Support Information
|
||||
|
||||
## Which Kubernetes version is supported in multus?
|
||||
|
||||
Currently multus team supports Kubernetes that Kubernetes community maintains.
|
||||
See [Version Skew Policy](https://kubernetes.io/releases/version-skew-policy/) for the details.
|
||||
|
||||
## How to debug multus-cni thin image?
|
||||
|
||||
Latest multus uses [distroless](https://github.com/GoogleContainerTools/distroless) container image for its base,
|
||||
hence there is no shell command. If you want to execute shell in multus pod, please use `-debug` image (e.g. ghcr.io/k8snetworkplumbingwg/multus-cni:snapshot-debug), which has shell.
|
||||
|
||||
## How to utilize multus-cni code as library?
|
||||
|
||||
Multus now uses [gopkg.in](http://gopkg.in/) to expose its code as library.
|
||||
You can use following command to import our code into your go code.
|
||||
|
||||
```
|
||||
go get gopkg.in/k8snetworkplumbingwg/multus-cni.v4
|
||||
```
|
||||
|
||||
## How do I submit an issue?
|
||||
|
||||
Use GitHub as normally, you'll be presented with an option to submit a issue or enhancement request.
|
||||
|
||||
Issues are considered stale after 90 days. After which, the maintainers reserve the right to close an issue.
|
||||
|
||||
Typically, we'll tag the submitter and ask for more information if necessary before closing.
|
||||
|
||||
If an issue is closed that you don't feel is sufficiently resolved, please feel free to re-open the issue and provide any necessary information.
|
||||
|
||||
## How do I build multus-cni?
|
||||
|
||||
You can use the built in `./hack/build-go.sh` script!
|
||||
|
||||
```
|
||||
git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
|
||||
cd multus-cni
|
||||
./hack/build-go.sh
|
||||
```
|
||||
|
||||
## How do I run the unit tests?
|
||||
|
||||
Multus has go unit tests (based on ginkgo framework).The following commands drive CI tests manually in your environment:
|
||||
|
||||
```
|
||||
sudo ./hack/test-go.sh
|
||||
```
|
||||
|
||||
## How do I run the e2e tests?
|
||||
|
||||
Check the `README.md` in the `./e2e/` folder.
|
||||
|
||||
## What are the best practices for logging?
|
||||
|
||||
The following are the best practices for multus logging:
|
||||
|
||||
* Add `logging.Debugf()` at the beginning of functions
|
||||
* In case of error handling, use `logging.Errorf()` with given error info
|
||||
* `logging.Panicf()` only be used for critical errors (it should NOT normally be used)
|
||||
|
||||
|
||||
## Multus release schedule
|
||||
|
||||
On the first maintainer's meeting, twice yearly, after January 1st and July 1st, if a new version has not been tagged, a new version will tagged.
|
||||
|
||||
## Multi-arch builds
|
||||
|
||||
Multus is currently built for a number of architectures, however, our testing and validation is only performed against x86 architectures. Our x86 architecture has end to end testing, however, for other architectures, only supported via best effort community contributions.
|
767
docs/how-to-use.md
Normal file
@@ -0,0 +1,767 @@
|
||||
## Multus CNI usage guide
|
||||
|
||||
### Prerequisites
|
||||
|
||||
* Kubelet configured to use CNI
|
||||
* Kubernetes version with CRD support (generally )
|
||||
|
||||
Your Kubelet(s) must be configured to run with the CNI network plugin. Please see [Kubernetes document for CNI](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/#cni) for more details.
|
||||
|
||||
### Install Multus
|
||||
|
||||
Generally we recommend two options: Manually place a Multus binary in your `/opt/cni/bin`, or use our [quick-start method](quickstart.md) -- which creates a daemonset that has an opinionated way of how to install & configure Multus CNI (recommended).
|
||||
|
||||
*Copy Multus Binary into place*
|
||||
|
||||
You may acquire the Multus binary via compilation (see the [developer guide](development.md)) or download the a binary from the [GitHub releases](https://github.com/k8snetworkplumbingwg/multus-cni/releases) page. Copy multus binary into CNI binary directory, usually `/opt/cni/bin`. Perform this on all nodes in your cluster (master and nodes).
|
||||
|
||||
cp multus /opt/cni/bin
|
||||
|
||||
*Via Daemonset method*
|
||||
|
||||
As a [quickstart](quickstart.md), you may apply these YAML files. Run this command (typically you would run this on the master, or wherever you have access to the `kubectl` command to manage your cluster).
|
||||
|
||||
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml # thin deployment
|
||||
|
||||
or
|
||||
|
||||
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset-thick.yml # thick (client/server) deployment
|
||||
|
||||
If you need more comprehensive detail, continue along with this guide, otherwise, you may wish to either [follow the quickstart guide]() or skip to the ['Create network attachment definition'](#create-network-attachment-definition) section.
|
||||
|
||||
### Set up conf file in /etc/cni/net.d/ (Installed automatically by Daemonset)
|
||||
|
||||
**If you use daemonset to install multus, skip this section and go to "Create network attachment"**
|
||||
|
||||
You put CNI config file in `/etc/cni/net.d`. Kubernetes CNI runtime uses the alphabetically first file in the directory. (`"NOTE1"`, `"NOTE2"` are just comments, you can remove them at your configuration)
|
||||
|
||||
Execute following commands at all Kubernetes nodes (i.e. master and minions)
|
||||
|
||||
```
|
||||
mkdir -p /etc/cni/net.d
|
||||
cat >/etc/cni/net.d/00-multus.conf <<EOF
|
||||
{
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"readinessindicatorfile": "/run/flannel/subnet.env",
|
||||
"delegates": [
|
||||
{
|
||||
"NOTE1": "This is example, wrote your CNI config in delegates",
|
||||
"NOTE2": "If you use flannel, you also need to run flannel daemonset before!",
|
||||
"type": "flannel",
|
||||
"name": "flannel.1",
|
||||
"delegate": {
|
||||
"isDefaultGateway": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
For the detail, please take a look into [Configuration Reference](configuration.md)
|
||||
|
||||
**NOTE: You can use "clusterNetwork"/"defaultNetworks" instead of "delegates", see []() for the detail**
|
||||
|
||||
As above config, you need to set `"kubeconfig"` in the config file for NetworkAttachmentDefinition(CRD).
|
||||
|
||||
##### Which network will be used for "Pod IP"?
|
||||
|
||||
In case of "delegates", the first delegates network will be used for "Pod IP". Otherwise, "clusterNetwork" will be used for "Pod IP".
|
||||
|
||||
#### Create ServiceAccount, ClusterRole and its binding
|
||||
|
||||
Create resources for multus to access CRD objects as following command:
|
||||
|
||||
```
|
||||
# Execute following commands at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: multus
|
||||
rules:
|
||||
- apiGroups: ["k8s.cni.cncf.io"]
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/status
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: multus
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: multus
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Set up kubeconfig file
|
||||
|
||||
Create kubeconfig at master node as following commands:
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
mkdir -p /etc/cni/net.d/multus.d
|
||||
SERVICEACCOUNT_CA=$(kubectl get secrets -n=kube-system -o json | jq -r '.items[]|select(.metadata.annotations."kubernetes.io/service-account.name"=="multus")| .data."ca.crt"')
|
||||
SERVICEACCOUNT_TOKEN=$(kubectl get secrets -n=kube-system -o json | jq -r '.items[]|select(.metadata.annotations."kubernetes.io/service-account.name"=="multus")| .data.token' | base64 -d )
|
||||
KUBERNETES_SERVICE_PROTOCOL=$(kubectl get all -o json | jq -r .items[0].spec.ports[0].name)
|
||||
KUBERNETES_SERVICE_HOST=$(kubectl get all -o json | jq -r .items[0].spec.clusterIP)
|
||||
KUBERNETES_SERVICE_PORT=$(kubectl get all -o json | jq -r .items[0].spec.ports[0].port)
|
||||
cat > /etc/cni/net.d/multus.d/multus.kubeconfig <<EOF
|
||||
# Kubeconfig file for Multus CNI plugin.
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: local
|
||||
cluster:
|
||||
server: ${KUBERNETES_SERVICE_PROTOCOL:-https}://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}
|
||||
certificate-authority-data: ${SERVICEACCOUNT_CA}
|
||||
users:
|
||||
- name: multus
|
||||
user:
|
||||
token: "${SERVICEACCOUNT_TOKEN}"
|
||||
contexts:
|
||||
- name: multus-context
|
||||
context:
|
||||
cluster: local
|
||||
user: multus
|
||||
current-context: multus-context
|
||||
EOF
|
||||
```
|
||||
|
||||
Copy `/etc/cni/net.d/multus.d/multus.kubeconfig` into other Kubernetes nodes
|
||||
**NOTE: Recommend to exec 'chmod 600 /etc/cni/net.d/multus.d/multus.kubeconfig' to keep secure**
|
||||
|
||||
```
|
||||
scp /etc/cni/net.d/multus.d/multus.kubeconfig ...
|
||||
```
|
||||
|
||||
### Setup CRDs (daemonset automatically does)
|
||||
|
||||
**If you use daemonset to install multus, skip this section and go to "Create network attachment"**
|
||||
|
||||
Create CRD definition in Kubernetes as following command at master node:
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: network-attachment-definitions.k8s.cni.cncf.io
|
||||
spec:
|
||||
group: k8s.cni.cncf.io
|
||||
version: v1
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: network-attachment-definitions
|
||||
singular: network-attachment-definition
|
||||
kind: NetworkAttachmentDefinition
|
||||
shortNames:
|
||||
- net-attach-def
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
spec:
|
||||
properties:
|
||||
config:
|
||||
type: string
|
||||
EOF
|
||||
```
|
||||
|
||||
### Create network attachment definition
|
||||
|
||||
The 'NetworkAttachmentDefinition' is used to setup the network attachment, i.e. secondary interface for the pod, There are two ways to configure the 'NetworkAttachmentDefinition' as following:
|
||||
|
||||
- NetworkAttachmentDefinition with json CNI config
|
||||
- NetworkAttachmentDefinition with CNI config file
|
||||
|
||||
#### NetworkAttachmentDefinition with json CNI config:
|
||||
|
||||
Following command creates NetworkAttachmentDefinition. CNI config is in `config:` field.
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: macvlan-conf-1
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"ranges": [
|
||||
[ {
|
||||
"subnet": "10.10.0.0/16",
|
||||
"rangeStart": "10.10.1.20",
|
||||
"rangeEnd": "10.10.3.50",
|
||||
"gateway": "10.10.0.254"
|
||||
} ]
|
||||
]
|
||||
}
|
||||
}'
|
||||
EOF
|
||||
```
|
||||
|
||||
#### NetworkAttachmentDefinition with CNI config file:
|
||||
|
||||
If NetworkAttachmentDefinition has no spec, multus find a file in defaultConfDir ('/etc/cni/multus/net.d', with same name in the 'name' field of CNI config.
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: macvlan-conf-2
|
||||
EOF
|
||||
```
|
||||
|
||||
```
|
||||
# Execute following commands at all Kubernetes nodes (i.e. master and minions)
|
||||
cat <<EOF > /etc/cni/multus/net.d/macvlan2.conf
|
||||
{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"name": "macvlan-conf-2",
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"ranges": [
|
||||
[ {
|
||||
"subnet": "11.10.0.0/16",
|
||||
"rangeStart": "11.10.1.20",
|
||||
"rangeEnd": "11.10.3.50"
|
||||
} ]
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
### Run pod with network annotation
|
||||
|
||||
#### Launch pod with text annotation
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-01
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: macvlan-conf-1, macvlan-conf-2
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-01
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Launch pod with text annotation for NetworkAttachmentDefinition in different namespace
|
||||
|
||||
You can also specify NetworkAttachmentDefinition with its namespace as adding `<namespace>/`
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: macvlan-conf-3
|
||||
namespace: testns1
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"ranges": [
|
||||
[ {
|
||||
"subnet": "12.10.0.0/16",
|
||||
"rangeStart": "12.10.1.20",
|
||||
"rangeEnd": "12.10.3.50"
|
||||
} ]
|
||||
]
|
||||
}
|
||||
}'
|
||||
EOF
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-02
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: testns1/macvlan-conf-3
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-02
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Launch pod with text annotation with interface name
|
||||
|
||||
You can also specify interface name as adding `@<ifname>`.
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-03
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: macvlan-conf-1@macvlan1
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-03
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Launch pod with json annotation
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-04
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[
|
||||
{ "name" : "macvlan-conf-1" },
|
||||
{ "name" : "macvlan-conf-2" }
|
||||
]'
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-04
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Launch pod with json annotation for NetworkAttachmentDefinition in different namespace
|
||||
|
||||
You can also specify NetworkAttachmentDefinition with its namespace as adding `"namespace": "<namespace>"`.
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-05
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[
|
||||
{ "name" : "macvlan-conf-1",
|
||||
"namespace": "testns1" }
|
||||
]'
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-05
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Launch pod with json annotation with interface
|
||||
|
||||
You can also specify interface name as adding `"interface": "<ifname>"`.
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-06
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[
|
||||
{ "name" : "macvlan-conf-1",
|
||||
"interface": "macvlan1" },
|
||||
{ "name" : "macvlan-conf-2" }
|
||||
]'
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-06
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
||||
EOF
|
||||
```
|
||||
|
||||
### Verifying pod network
|
||||
|
||||
Following the example of `ip -d address` output of above pod, "pod-case-06":
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
kubectl exec -it pod-case-06 -- ip -d address
|
||||
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
|
||||
inet 127.0.0.1/8 scope host lo
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 ::1/128 scope host
|
||||
valid_lft forever preferred_lft forever
|
||||
3: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
|
||||
link/ether 0a:58:0a:f4:02:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
|
||||
veth numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
|
||||
inet 10.244.2.6/24 scope global eth0
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::ac66:45ff:fe7c:3a19/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
4: macvlan1@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
|
||||
link/ether 4e:6d:7a:4e:14:87 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
|
||||
macvlan mode bridge numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
|
||||
inet 10.10.1.22/16 scope global macvlan1
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::4c6d:7aff:fe4e:1487/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
5: net2@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default
|
||||
link/ether 6e:e3:71:7f:86:f7 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
|
||||
macvlan mode bridge numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
|
||||
inet 11.10.1.22/16 scope global net2
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 fe80::6ce3:71ff:fe7f:86f7/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
| Interface name | Description |
|
||||
| --- | --- |
|
||||
| lo | loopback |
|
||||
| eth0 | Default network interface (flannel) |
|
||||
| macvlan1 | macvlan interface (macvlan-conf-1) |
|
||||
| net2 | macvlan interface (macvlan-conf-2) |
|
||||
|
||||
## Specifying a default route for a specific attachment
|
||||
|
||||
Typically, the default route for a pod will route traffic over the `eth0` and therefore over the cluster-wide default network. You may wish to specify that a different network attachment will have the default route.
|
||||
|
||||
You can achieve this by using the JSON formatted annotation and specifying a `default-route` key.
|
||||
|
||||
*NOTE*: It's important that you consider that this may impact some functionality of getting traffic to route over the cluster-wide default network.
|
||||
|
||||
For example, we have a this configuration for macvlan:
|
||||
|
||||
```
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: macvlan-conf
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth0",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "192.168.2.0/24",
|
||||
"rangeStart": "192.168.2.200",
|
||||
"rangeEnd": "192.168.2.216",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
],
|
||||
"gateway": "192.168.2.1"
|
||||
}
|
||||
}'
|
||||
EOF
|
||||
```
|
||||
|
||||
We can then create a pod which uses the `default-route` key in the JSON formatted `k8s.v1.cni.cncf.io/networks` annotation.
|
||||
|
||||
```
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: samplepod
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[{
|
||||
"name": "macvlan-conf",
|
||||
"default-route": ["192.168.2.1"]
|
||||
}]'
|
||||
spec:
|
||||
containers:
|
||||
- name: samplepod
|
||||
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
|
||||
image: dougbtv/centos-network
|
||||
EOF
|
||||
```
|
||||
|
||||
This will set `192.168.2.1` as the default route over the `net1` interface, such as:
|
||||
|
||||
```
|
||||
kubectl exec -it samplepod -- ip route
|
||||
|
||||
default via 192.168.2.1 dev net1
|
||||
10.244.0.0/24 dev eth0 proto kernel scope link src 10.244.0.169
|
||||
10.244.0.0/16 via 10.244.0.1 dev eth0
|
||||
```
|
||||
|
||||
## Entrypoint Parameters
|
||||
|
||||
Multus CNI, when installed using the daemonset-style installation uses an entrypoint script which copies the Multus binary into place, places CNI configurations. This entrypoint takes a variety of parameters for customization.
|
||||
|
||||
Typically, you'd modified the daemonset YAML itself to specify these parameters.
|
||||
|
||||
For example, the `command` and `args` parameters in the `containers` section of the DaemonSet may look something like:
|
||||
|
||||
```
|
||||
command: ["/thin_entrypoint"]
|
||||
args:
|
||||
- "--multus-conf-file=auto"
|
||||
- "--namespace-isolation=true"
|
||||
- "--multus-log-level=verbose"
|
||||
```
|
||||
|
||||
Note that some of the defaults have directories inside the root directory named `/host/`, this is because it is deployed as a container and we have host file system locations mapped into this directory inside the container. If you use other directories, you may have to change the mounted volumes.
|
||||
|
||||
### Entrypoint script parameters
|
||||
|
||||
Each parameter is shown with the default as the value.
|
||||
|
||||
--cni-conf-dir=/host/etc/cni/net.d
|
||||
|
||||
This is the configuration directory where Multus will write its configuration file.
|
||||
|
||||
--cni-bin-dir=/host/opt/cni/bin
|
||||
|
||||
This the directory in which the Multus binary will be installed.
|
||||
|
||||
--namespace-isolation=false
|
||||
|
||||
Setting this option to true enables the Namespace isolation feature, which insists that custom resources must be created in the same namespace as the pods, otherwise it will refuse to attach those definitions as additional interfaces. See (the configuration guide for more information)[configuration.md].
|
||||
|
||||
--global-namespaces=default,foo,bar
|
||||
|
||||
The `--global-namespaces` works only when `--namespace-isolation=true`. This takes a comma-separated list of namespaces which can be referred to globally when namespace isolation is enabled. See (the configuration guide for more information)[configuration.md].
|
||||
|
||||
--multus-bin-file=/usr/src/multus-cni/bin/multus
|
||||
|
||||
This option lets you set which binary executable to copy from the container onto the host (into the directory specified by `--cni-bin-dir`), allowing one to copy an alternate version or build of Multus CNI.
|
||||
|
||||
--multus-conf-file=/usr/src/multus-cni/images/70-multus.conf
|
||||
|
||||
The `--multus-conf-file` is one of two options; it can be set to a source file to be copied into the location specified by `--cni-conf-dir`. Or, to a value of `auto`, that is: `--multus-conf-file=auto`.
|
||||
|
||||
The automatic configuration option is used to automatically generate Multus configurations given existing on-disk CNI configurations for your default network.
|
||||
|
||||
In the case that `--multus-conf-file=auto` -- The entrypoint script will look at the `--multus-autoconfig-dir` (by default, the same as the `--cni-conf-dir`). Multus will take the alphabetically first configuration there and wrap that into a Multus configuration.
|
||||
|
||||
--multus-autoconfig-dir=/host/etc/cni/net.d
|
||||
|
||||
Used only with `--multus-conf-file=auto`. This option allows one to set which directory will be used to generate configuration files.
|
||||
|
||||
This can be used if you have your CNI configuration stored in an alternate location, or, you have constraints on race conditions where you'd like to generate your default network configuration first, and then only have Multus write its configuration when it finds that configuration -- allowing only Multus to write the CNI configuration in the `--cni-conf-dir`, therefore notifying the Kubelet that the node is in a ready state.
|
||||
|
||||
--multus-kubeconfig-file-host=/etc/cni/net.d/multus.d/multus.kubeconfig
|
||||
|
||||
Used only with `--multus-conf-file=auto`. Allows you to specify an alternate path to the Kubeconfig.
|
||||
|
||||
--multus-master-cni-file-name=
|
||||
|
||||
The `--multus-master-cni-file-name` can be used to select the cni file as the master cni, rather than the first file in cni-conf-dir. For example, `--multus-master-cni-file-name=10-calico.conflist`.
|
||||
|
||||
--multus-log-level=
|
||||
--multus-log-file=
|
||||
|
||||
Used only with `--multus-conf-file=auto`. See the [documentation for logging](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/configuration.md#logging) for which values are permitted.
|
||||
|
||||
Used only with `--multus-conf-file=auto`. Allows you to specify CNI spec version. Please set if you need to specify CNI spec version.
|
||||
|
||||
--cni-version=
|
||||
|
||||
In some cases, the original CNI configuration that the Multus configuration was generated from (using `--multus-conf-file=auto`) may be used as a sort of semaphor for network readiness -- as this model is used by the Kubelet itself. If you need to disable Multus' availability, you may wish to clean out the generated configuration file when the source file for autogeneration of the config file is no longer present. You can use this functionality by setting:
|
||||
|
||||
--cleanup-config-on-exit=true
|
||||
|
||||
When specifying `--cleanup-config-on-exit=true` the entrypoint script will delete any generated/copied Multus configuration files when entrypoint script
|
||||
exits (upon Pod termination). This allows Multus to be safely removed from the cluster when its no longer needed.
|
||||
|
||||
In addition, when both `--cleanup-config-on-exit=true` and `--multus-conf-file=auto` are specified, the entrypoint script will watch for changes of the
|
||||
master CNI configuration and kubeconfig. when such change detected, the script will re-genereate Multus configuration. Watch can be skipped by setting:
|
||||
|
||||
--skip-config-watch
|
||||
|
||||
Additionally when using CRIO, you may wish to have the CNI config file that's used as the source for `--multus-conf-file=auto` renamed. This boolean option when set to true automatically renames the file with a `.old` suffix to the original filename.
|
||||
|
||||
--rename-conf-file=true
|
||||
|
||||
When using `--multus-conf-file=auto` you may also care to specify a `binDir` in the configuration, this can be accomplished using the `--additional-bin-dir` option.
|
||||
|
||||
--additional-bin-dir=/opt/multus/bin
|
||||
|
||||
Sometimes, you may wish to not have the entrypoint copy the binary file onto the host. Potentially, you have another way to copy in a specific version of Multus, for example. By default, it's always copied, but you may disable the copy with:
|
||||
|
||||
--skip-multus-binary-copy=true
|
||||
|
||||
If you wish to have auto configuration use the `readinessindicatorfile` in the configuration, you can use the `--readiness-indicator-file` to express which file should be used as the readiness indicator.
|
||||
|
||||
--readiness-indicator-file=/path/to/file
|
||||
|
||||
### Run pod with network annotation and Dynamic Resource Allocation driver
|
||||
|
||||
> :warning: Dynamic Resource Allocation (DRA) is [currently an alpha](https://kubernetes.io/docs/concepts/scheduling-eviction/dynamic-resource-allocation/),
|
||||
> and is subject to change. Please consider this functionality as a preview. The architecture and usage of DRA in
|
||||
> Multus CNI may change in the future as this technology matures.
|
||||
>
|
||||
> The current DRA integration is based on the DRA API for Kubernetes 1.26 to 1.30. With Kubernetes 1.31, the DRA API
|
||||
> will change and multus doesn't integrate with the new API yet.
|
||||
|
||||
Dynamic Resource Allocation is alternative mechanism to device plugin which allows to requests pod and container
|
||||
resources.
|
||||
|
||||
The following sections describe how to use DRA with multus and NVIDIA DRA driver. Other DRA networking driver vendors
|
||||
should follow similar concepts to make use of multus DRA support.
|
||||
|
||||
#### Prerequisite
|
||||
|
||||
1. Kubernetes 1.27
|
||||
2. Container Runtime with CDI support enabled
|
||||
3. Kubernetes runtime-config=resource.k8s.io/v1alpha2
|
||||
4. Kubernetes feature-gates=DynamicResourceAllocation=True,KubeletPodResourcesDynamicResources=true
|
||||
|
||||
#### Install DRA driver
|
||||
|
||||
The current example uses NVIDIA DRA driver for networking. This DRA driver is not publicly available. An alternative to
|
||||
this DRA driver is available at [dra-example-driver](https://github.com/kubernetes-sigs/dra-example-driver).
|
||||
|
||||
#### Create dynamic resource class with NVIDIA network DRA driver
|
||||
|
||||
The `ResourceClass` defines the resource pool of `sf-pool-1`.
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: resource.k8s.io/v1alpha2
|
||||
kind: ResourceClass
|
||||
metadata:
|
||||
name: sf-pool-1
|
||||
driverName: net.resource.nvidia.com
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Create network attachment definition with resource name
|
||||
|
||||
The `k8s.v1.cni.cncf.io/resourceName` should match the `ResourceClass` name defined in the section above.
|
||||
In this example it is `sf-pool-1`. Multus query the K8s PodResource API to fetch the `resourceClass` name and also
|
||||
query the NetworkAttachmentDefinition `k8s.v1.cni.cncf.io/resourceName`. If both has the same name multus send the
|
||||
CDI device name in the DeviceID argument.
|
||||
|
||||
##### NetworkAttachmentDefinition for ovn-kubernetes example:
|
||||
|
||||
Following command creates NetworkAttachmentDefinition. CNI config is in `config:` field.
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: default
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/resourceName: sf-pool-1
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.4.0",
|
||||
"dns": {},
|
||||
"ipam": {},
|
||||
"logFile": "/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log",
|
||||
"logLevel": "4",
|
||||
"logfile-maxage": 5,
|
||||
"logfile-maxbackups": 5,
|
||||
"logfile-maxsize": 100,
|
||||
"name": "ovn-kubernetes",
|
||||
"type": "ovn-k8s-cni-overlay"
|
||||
}'
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Create DRA Resource Claim
|
||||
|
||||
Following command creates `ResourceClaim` `sf` which request resource from `ResourceClass` `sf-pool-1`.
|
||||
|
||||
```
|
||||
# Execute following command at Kubernetes master
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: resource.k8s.io/v1alpha2
|
||||
kind: ResourceClaim
|
||||
metadata:
|
||||
namespace: default
|
||||
name: sf
|
||||
spec:
|
||||
spec:
|
||||
resourceClassName: sf-pool-1
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Launch pod with DRA Resource Claim
|
||||
|
||||
Following command Launch a Pod with primiry network `default` and `ResourceClaim` `sf`.
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
namespace: default
|
||||
name: test-sf-claim
|
||||
annotations:
|
||||
v1.multus-cni.io/default-network: default
|
||||
spec:
|
||||
restartPolicy: Always
|
||||
containers:
|
||||
- name: with-resource
|
||||
image: docker.io/library/ubuntu:22.04
|
||||
command: ["/bin/sh", "-ec", "while :; do echo '.'; sleep 5 ; done"]
|
||||
resources:
|
||||
claims:
|
||||
- name: resource
|
||||
resourceClaims:
|
||||
- name: resource
|
||||
source:
|
||||
resourceClaimName: sf
|
||||
```
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
1
docs/images/multus-pod-image.svg
Normal file
After Width: | Height: | Size: 190 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
240
docs/quickstart.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# Quickstart Guide
|
||||
|
||||
This guide is intended as a way to get you off the ground, using Multus CNI to create Kubernetes pods with multiple interfaces. If you're already using Multus and need more detail, see the [comprehensive usage guide](how-to-use.md). This document is a quickstart and a getting started guide in one, intended for your first run-through of Multus CNI.
|
||||
|
||||
We'll first install Multus CNI, and then we'll setup some configurations so that you can see how multiple interfaces are created for pods.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
Two things we'll refer to a number of times through this document are:
|
||||
|
||||
* "Default network" -- This is your pod-to-pod network. This is how pods communicate among one another in your cluster, how they have connectivity. Generally speaking, this is presented as the interface named `eth0`. This interface is always attached to your pods, so that they can have connectivity among themselves. We'll add interfaces in addition to this.
|
||||
* "CRDs" -- Custom Resource Definitions. Custom Resources are a way that the Kubernetes API is extended. We use these here to store some information that Multus can read. Primarily, we use these to store the configurations for each of the additional interfaces that are attached to your pods.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Our installation method requires that you first have installed Kubernetes and have configured a default network -- that is, a CNI plugin that's used for your pod-to-pod connectivity.
|
||||
|
||||
We support Kubernetes versions that Kubernetes community supports. Please see [Supported versions](https://kubernetes.io/releases/version-skew-policy/#supported-versions) in Kubernetes document.
|
||||
|
||||
To install Kubernetes, you may decide to use [kubeadm](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/), or potentially [kubespray](https://github.com/kubernetes-sigs/kubespray).
|
||||
|
||||
After installing Kubernetes, you must install a default network CNI plugin. If you're using kubeadm, refer to the "[Installing a pod network add-on](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#pod-network)" section in the kubeadm documentation. If it's your first time, we generally recommend using Flannel for the sake of simplicity.
|
||||
|
||||
Alternatively, for advanced use cases, for installing Multus and a default network plugin at the same time, you may refer to the [Kubernetes Network Plumbing Group's Reference Deployments](https://github.com/k8snetworkplumbingwg/reference-deployment).
|
||||
|
||||
To verify that you default network is ready, you may list your Kubernetes nodes with:
|
||||
|
||||
```
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
In the case that your default network is ready you will see the `STATUS` column also switch to `Ready` for each node.
|
||||
|
||||
```
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
master-0 Ready master 1h v1.17.1
|
||||
master-1 Ready master 1h v1.17.1
|
||||
master-2 Ready master 1h v1.17.1
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
Our recommended quickstart method to deploy Multus is to deploy using a Daemonset (a method of running pods on each nodes in your cluster), this spins up pods which install a Multus binary and configure Multus for usage.
|
||||
|
||||
We'll apply a YAML file with `kubectl` from this repo, which installs the Multus components.
|
||||
|
||||
Recommended installation:
|
||||
|
||||
```
|
||||
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset-thick.yml
|
||||
```
|
||||
See the [thick plugin docs](./thick-plugin.md) for more information about this architecture.
|
||||
|
||||
Alternatively, you may install the thin-plugin with:
|
||||
|
||||
```
|
||||
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml
|
||||
```
|
||||
|
||||
### What the Multus daemonset does
|
||||
|
||||
* Starts a Multus daemonset, this runs a pod on each node which places a Multus binary on each node in `/opt/cni/bin`
|
||||
* Reads the lexicographically (alphabetically) first configuration file in `/etc/cni/net.d`, and creates a new configuration file for Multus on each node as `/etc/cni/net.d/00-multus.conf`, this configuration is auto-generated and is based on the default network configuration (which is assumed to be the alphabetically first configuration)
|
||||
* Creates a `/etc/cni/net.d/multus.d` directory on each node with authentication information for Multus to access the Kubernetes API.
|
||||
|
||||
|
||||
### Validating your installation
|
||||
|
||||
Generally, the first step in validating your installation is to ensure that the Multus pods have run without error, you may see an overview of those by looking at:
|
||||
|
||||
```
|
||||
kubectl get pods --all-namespaces | grep -i multus
|
||||
```
|
||||
|
||||
You may further validate that it has ran by looking at the `/etc/cni/net.d/` directory and ensure that the auto-generated `/etc/cni/net.d/00-multus.conf` exists corresponding to the alphabetically first configuration file.
|
||||
|
||||
## Creating additional interfaces
|
||||
|
||||
The first thing we'll do is create configurations for each of the additional interfaces that we attach to pods. We'll do this by creating Custom Resources. Part of the quickstart installation creates a "CRD" -- a custom resource definition that is the home where we keep these custom resources -- we'll store our configurations for each interface in these.
|
||||
|
||||
### CNI Configurations
|
||||
|
||||
Each configuration we'll add is a CNI configuration. If you're not familiar with them, let's break them down quickly. Here's an example CNI configuration:
|
||||
|
||||
```
|
||||
{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "loopback",
|
||||
"additional": "information"
|
||||
}
|
||||
```
|
||||
|
||||
CNI configurations are JSON, and we have a structure here that has a few things we're interested in:
|
||||
|
||||
1. `cniVersion`: Tells each CNI plugin which version is being used and can give the plugin information if it's using a too late (or too early) version.
|
||||
2. `type`: This tells CNI which binary to call on disk. Each CNI plugin is a binary that's called. Typically, these binaries are stored in `/opt/cni/bin` on each node, and CNI executes this binary. In this case we've specified the `loopback` binary (which create a loopback-type network interface). If this is your first time installing Multus, you might want to verify that the plugins that are in the "type" field are actually on disk in the `/opt/cni/bin` directory.
|
||||
3. `additional`: This field is put here as an example, each CNI plugin can specify whatever configuration parameters they'd like in JSON. These are specific to the binary you're calling in the `type` field.
|
||||
|
||||
For an even further example -- take a look at the [bridge CNI plugin README](https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge) which shows additional details.
|
||||
|
||||
If you'd like more information about CNI configuration, you can read [the entire CNI specification](https://github.com/containernetworking/cni/blob/master/SPEC.md). It might also be useful to look at the [CNI reference plugins](https://github.com/containernetworking/plugins) and see how they're configured.
|
||||
|
||||
You do not need to reload or refresh the Kubelets when CNI configurations change. These are read on each creation & deletion of pods. So if you change a configuration, it'll apply the next time a pod is created. Existing pods may need to be restarted if they need the new configuration.
|
||||
|
||||
### Storing a configuration as a Custom Resource
|
||||
|
||||
So, we want to create an additional interface. Let's create a macvlan interface for pods to use. We'll create a custom resource that defines the CNI configuration for interfaces.
|
||||
|
||||
Note in the following command that there's a `kind: NetworkAttachmentDefinition`. This is our fancy name for our configuration -- it's a custom extension of Kubernetes that defines how we attach networks to our pods.
|
||||
|
||||
Secondarily, note the `config` field. You'll see that this is a CNI configuration just like we explained earlier.
|
||||
|
||||
Lastly but *very* importantly, note under `metadata` the `name` field -- here's where we give this configuration a name, and it's how we tell pods to use this configuration. The name here is `macvlan-conf` -- as we're creating a configuration for macvlan.
|
||||
|
||||
Here's the command to create this example configuration:
|
||||
|
||||
```
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: macvlan-conf
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth0",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "192.168.1.0/24",
|
||||
"rangeStart": "192.168.1.200",
|
||||
"rangeEnd": "192.168.1.216",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
],
|
||||
"gateway": "192.168.1.1"
|
||||
}
|
||||
}'
|
||||
EOF
|
||||
```
|
||||
|
||||
*NOTE*: This example uses `eth0` as the `master` parameter, this master parameter should match the interface name on the hosts in your cluster.
|
||||
|
||||
You can see which configurations you've created using `kubectl` here's how you can do that:
|
||||
|
||||
```
|
||||
kubectl get network-attachment-definitions
|
||||
```
|
||||
|
||||
You can get more detail by describing them:
|
||||
|
||||
```
|
||||
kubectl describe network-attachment-definitions macvlan-conf
|
||||
```
|
||||
|
||||
### Creating a pod that attaches an additional interface
|
||||
|
||||
We're going to create a pod. This will look familiar as any pod you might have created before, but, we'll have a special `annotations` field -- in this case we'll have an annotation called `k8s.v1.cni.cncf.io/networks`. This field takes a comma delimited list of the names of your `NetworkAttachmentDefinition`s as we created above. Note in the command below that we have the annotation of `k8s.v1.cni.cncf.io/networks: macvlan-conf` where `macvlan-conf` is the name we used above when we created our configuration.
|
||||
|
||||
Let's go ahead and create a pod (that just sleeps for a really long time) with this command:
|
||||
|
||||
```
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: samplepod
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: macvlan-conf
|
||||
spec:
|
||||
containers:
|
||||
- name: samplepod
|
||||
command: ["/bin/ash", "-c", "trap : TERM INT; sleep infinity & wait"]
|
||||
image: alpine
|
||||
EOF
|
||||
```
|
||||
|
||||
You may now inspect the pod and see what interfaces are attached, like so:
|
||||
|
||||
```
|
||||
kubectl exec -it samplepod -- ip a
|
||||
```
|
||||
|
||||
You should note that there are 3 interfaces:
|
||||
|
||||
* `lo` a loopback interface
|
||||
* `eth0` our default network
|
||||
* `net1` the new interface we created with the macvlan configuration.
|
||||
|
||||
### Network Status Annotations
|
||||
|
||||
For additional confirmation, use `kubectl describe pod samplepod` and there will be an annotations section, similar to the following:
|
||||
|
||||
```
|
||||
Annotations: k8s.v1.cni.cncf.io/networks: macvlan-conf
|
||||
k8s.v1.cni.cncf.io/network-status:
|
||||
[{
|
||||
"name": "cbr0",
|
||||
"ips": [
|
||||
"10.244.1.73"
|
||||
],
|
||||
"default": true,
|
||||
"dns": {}
|
||||
},{
|
||||
"name": "macvlan-conf",
|
||||
"interface": "net1",
|
||||
"ips": [
|
||||
"192.168.1.205"
|
||||
],
|
||||
"mac": "86:1d:96:ff:55:0d",
|
||||
"dns": {}
|
||||
}]
|
||||
```
|
||||
|
||||
This metadata tells us that we have two CNI plugins running successfully.
|
||||
|
||||
### What if I want more interfaces?
|
||||
|
||||
You can add more interfaces to a pod by creating more custom resources and then referring to them in pod's annotation. You can also reuse configurations, so for example, to attach two macvlan interfaces to a pod, you could create a pod like so:
|
||||
|
||||
```
|
||||
cat <<EOF | kubectl create -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: samplepod
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: macvlan-conf,macvlan-conf
|
||||
spec:
|
||||
containers:
|
||||
- name: samplepod
|
||||
command: ["/bin/ash", "-c", "trap : TERM INT; sleep infinity & wait"]
|
||||
image: alpine
|
||||
EOF
|
||||
```
|
||||
|
||||
Note that the annotation now reads `k8s.v1.cni.cncf.io/networks: macvlan-conf,macvlan-conf`. Where we have the same configuration used twice, separated by a comma.
|
||||
|
||||
If you were to create another custom resource with the name `foo` you could use that such as: `k8s.v1.cni.cncf.io/networks: foo,macvlan-conf`, and use any number of attachments.
|
114
docs/thick-plugin.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# Multus Thick plugin
|
||||
|
||||
Multus CNI can also be deployed using a thick plugin architecture, which is
|
||||
characterized by a client/server architecture.
|
||||
|
||||
The client - which will be referred to as "shim" - is a binary executable
|
||||
located on the Kubernetes node's file-system that
|
||||
[speaks CNI](https://github.com/containernetworking/cni/blob/master/SPEC.md#section-2-execution-protocol):
|
||||
the runtime - Kubernetes - passes parameters to the plugin via environment
|
||||
variables and configuration - which is passed via stdin.
|
||||
The plugin returns a result on stdout on success, or an error on stderr if the
|
||||
operation fails. Configuration and results are a JSON encoded string.
|
||||
|
||||
Once the shim is invoked by the runtime (Kubernetes) it will contact the
|
||||
multus-daemon (server) via a unix domain socket which is bind mounted to the
|
||||
host's file-system; the multus-daemon is the one that will do all the
|
||||
heavy-pulling: fetch the delegate CNI configuration from the corresponding
|
||||
`net-attach-def`, compute the `RuntimeConfig`, and finally, invoke the delegate.
|
||||
|
||||
It will then return the result of the operation back to the client.
|
||||
|
||||
Please refer to the diagram below for a visual representation of the flow
|
||||
described above:
|
||||
|
||||
```
|
||||
┌─────────┐ ┌───────┐ ┌────────┐ ┌──────────┐
|
||||
│ │ cni ADD/DEL │ │ REST POST │ │ cni ADD/DEL │ │
|
||||
│ runtime ├────────────►│ shim │===========│ daemon ├────────────►│ delegate │
|
||||
│ │<------------│ │ │ │<------------│ │
|
||||
└─────────┘ └───────┘ └────────┘ └──────────┘
|
||||
```
|
||||
|
||||
## How to use it
|
||||
|
||||
### Configure Deployment
|
||||
|
||||
If your delegate CNI plugin requires some files which is in container host, please update
|
||||
update `deployments/multus-daemonset-thick.yml` to add directory into multus-daemon pod.
|
||||
For example, flannel requires `/run/flannel/subnet.env`, so you need to mount this directory
|
||||
into the multus-daemon pod.
|
||||
|
||||
Required directory/files are different for each CNI plugin, so please refer your CNI plugin.
|
||||
|
||||
### Deployment
|
||||
|
||||
There is a dedicated multus daemonset specification for users wanting to use
|
||||
this thick plugin variant. This reference deployment spec of multus can be
|
||||
deployed by following these commands:
|
||||
|
||||
```bash
|
||||
kubectl apply -f deployments/multus-daemonset-thick.yml
|
||||
```
|
||||
|
||||
### Command line parameters
|
||||
|
||||
The available command line parameters are:
|
||||
|
||||
- `config`: Defaults to `"/etc/cni/net.d/multus.d/daemon-config.json"`
|
||||
- `version`: Prints the daemon config version and exits
|
||||
|
||||
### Server / Daemon configuration
|
||||
|
||||
The server configuration is encoded in JSON, and allows the following keys:
|
||||
|
||||
- `"chrootDir"`: Specify the directory which points to host root from the pod. See 'Chroot configuration' section for the details.
|
||||
- `"socketDir"`: Specify the location where the unix domain socket used
|
||||
for client/server communication will be located. This is the location where the
|
||||
**Daemon** will read the configuration from. Defaults to `"/run/multus"`.
|
||||
- `"metricsPort"`: Metrics port (of multus' metric exporter); by default, no port
|
||||
is provided.
|
||||
- `"logFile"`: the path to where the daemon logs will be persisted.
|
||||
- `"logLevel"`: the logging level for the multus daemon logs.
|
||||
- `"logToStderr"`: enable this to have the daemon multus logs echoed to stderr
|
||||
as well. By default, it is disabled.
|
||||
- `"auxiliaryCNIChainName"`: set a value to execute chained cni configurations from disk in an auxiliary CNI chain (see details in [configuration.md](configuration.md))
|
||||
|
||||
In addition, you can add any configuration which is in [configuration reference](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/configuration.md#multus-cni-configuration-reference). Server configuration override multus CNI configuration (e.g. `/etc/cni/net.d/00-multus.conf`)
|
||||
|
||||
Below you can see an example of the daemon configuration:
|
||||
```json
|
||||
{
|
||||
"chrootDir": "/hostroot",
|
||||
"confDir": "/host/etc/cni/net.d",
|
||||
"logToStderr": true,
|
||||
"logLevel": "verbose",
|
||||
"logFile": "/tmp/multus.log",
|
||||
"binDir": "/opt/cni/bin",
|
||||
"cniDir": "/var/lib/cni/multus",
|
||||
"socketDir": "/host/run/multus/",
|
||||
"cniVersion": "0.3.1",
|
||||
"cniConfigDir": "/host/etc/cni/net.d",
|
||||
"multusConfigFile": "auto",
|
||||
"multusAutoconfigDir": "/host/etc/cni/net.d"
|
||||
}
|
||||
```
|
||||
|
||||
### Client / Shim configuration
|
||||
|
||||
The multus shim configuration is encoded in JSON, and essentially is just a
|
||||
regular CNI configuration, usually available in `/etc/cni/net.d/00-multus.conf`.
|
||||
|
||||
It allows the following keys:
|
||||
|
||||
- `"cniVersion"`: the CNI version for the Multus CNI plugin.
|
||||
- `"logFile"`: the path to where the daemon logs will be persisted.
|
||||
- `"logLevel"`: the logging level for the multus daemon logs.
|
||||
- `"logToStderr"`: enable this to have the daemon multus logs echoed to stderr
|
||||
as well. By default, it is disabled.
|
||||
|
||||
#### Chroot configuration
|
||||
|
||||
In thick plugin case, delegate CNI plugin is executed by multus-daemon from Pod, hence if the delegate CNI requires resources in container host, for example unix socket or even file, then CNI plugin is failed to execute because multus-daemon runs in Pod. Multus-daemon supports "chrootDir" option which executes delegate CNI under chroot (to container host).
|
||||
|
||||
This configuration is enabled in deployments/multus-daemonset-thick.yml as default.
|
42
e2e/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
## Multus e2e test with kind
|
||||
|
||||
### Prerequisite
|
||||
|
||||
To run the e2e test, you need the following components:
|
||||
|
||||
- curl
|
||||
- jinjanator (optional)
|
||||
- docker
|
||||
|
||||
### How to test e2e
|
||||
|
||||
```
|
||||
$ git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
|
||||
$ cd multus-cni/e2e
|
||||
$ ./get_tools.sh
|
||||
```
|
||||
|
||||
If you have `jinjanator` you can generate the YAML with:
|
||||
|
||||
```
|
||||
$ ./generate_yamls.sh
|
||||
```
|
||||
|
||||
Alternatively, if you have trouble with it, use the `sed` script.
|
||||
|
||||
```
|
||||
$ ./e2e/sed_generate_yaml.sh
|
||||
```
|
||||
|
||||
Then, setup the cluster
|
||||
|
||||
```
|
||||
$ ./setup_cluster.sh
|
||||
$ ./test-simple-macvlan1.sh
|
||||
```
|
||||
|
||||
### How to teardown cluster
|
||||
|
||||
```
|
||||
$ ./teardown.sh
|
||||
```
|
17
e2e/generate_yamls.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ ! -d yamls ]; then
|
||||
mkdir yamls
|
||||
fi
|
||||
|
||||
# specify CNI version (default: 0.4.0)
|
||||
export CNI_VERSION=${CNI_VERSION:-0.4.0}
|
||||
|
||||
templates_dir="$(dirname $(readlink -f $0))/templates"
|
||||
|
||||
# generate yaml files based on templates/*.j2 to yamls directory
|
||||
for i in `ls templates/`; do
|
||||
echo $i
|
||||
j2 -e CNI_VERSION ${templates_dir}/$i -o yamls/${i%.j2}
|
||||
done
|
||||
unset CNI_VERSION
|
16
e2e/get_tools.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
set -o errexit
|
||||
|
||||
if [ ! -d bin ]; then
|
||||
mkdir bin
|
||||
fi
|
||||
|
||||
curl -Lo ./bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/v0.27.0/kind-$(uname)-amd64"
|
||||
chmod +x ./bin/kind
|
||||
curl -Lo ./bin/kubectl https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
|
||||
chmod +x ./bin/kubectl
|
||||
curl -Lo ./bin/koko https://github.com/redhat-nfvpe/koko/releases/download/v0.83/koko_0.83_linux_amd64
|
||||
chmod +x ./bin/koko
|
||||
curl -Lo ./bin/jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
|
||||
chmod +x ./bin/jq
|
||||
wget -qO- https://get.helm.sh/helm-v3.14.3-linux-amd64.tar.gz | tar xvzf - --strip-components=1 -C ./bin linux-amd64/helm
|
17
e2e/sed_generate_yaml.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ ! -d yamls ]; then
|
||||
mkdir yamls
|
||||
fi
|
||||
|
||||
# specify CNI version (default: 0.4.0)
|
||||
CNI_VERSION=${CNI_VERSION:-0.4.0}
|
||||
|
||||
templates_dir="$(dirname $(readlink -f $0))/templates"
|
||||
|
||||
# generate yaml files based on templates/*.j2 to yamls directory
|
||||
for i in `ls ${templates_dir}/*.j2`; do
|
||||
echo "Processing $i..."
|
||||
# Use sed to replace the placeholder with the CNI_VERSION variable
|
||||
sed "s/{{ CNI_VERSION }}/$CNI_VERSION/g" $i > yamls/$(basename ${i%.j2})
|
||||
done
|
71
e2e/setup_cluster.sh
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/bin/sh
|
||||
set -o errexit
|
||||
|
||||
export PATH=${PATH}:./bin
|
||||
|
||||
# define the OCI binary to be used. Acceptable values are `docker`, `podman`.
|
||||
# Defaults to `docker`.
|
||||
OCI_BIN="${OCI_BIN:-docker}"
|
||||
|
||||
# define the deployment spec to use when deploying multus.
|
||||
# Acceptable values are `multus-daemonset.yml`. `multus-daemonset-thick.yml`.
|
||||
# Defaults to `multus-daemonset-thick.yml`.
|
||||
MULTUS_MANIFEST="${MULTUS_MANIFEST:-multus-daemonset-thick.yml}"
|
||||
# define the dockerfile to build multus.
|
||||
# Acceptable values are `Dockerfile`. `Dockerfile.thick`.
|
||||
# Defaults to `Dockerfile.thick`.
|
||||
MULTUS_DOCKERFILE="${MULTUS_DOCKERFILE:-Dockerfile.thick}"
|
||||
|
||||
kind_network='kind'
|
||||
if [ "${MULTUS_DOCKERFILE}" != "none" ]; then
|
||||
$OCI_BIN build -t localhost:5000/multus:e2e -f ../images/${MULTUS_DOCKERFILE} ..
|
||||
fi
|
||||
|
||||
# deploy cluster with kind
|
||||
cat <<EOF | kind create cluster --config=-
|
||||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
nodes:
|
||||
- role: control-plane
|
||||
- role: worker
|
||||
kubeadmConfigPatches:
|
||||
- |
|
||||
kind: InitConfiguration
|
||||
nodeRegistration:
|
||||
kubeletExtraArgs:
|
||||
pod-manifest-path: "/etc/kubernetes/manifests/"
|
||||
feature-gates: "DynamicResourceAllocation=true,DRAResourceClaimDeviceStatus=true,KubeletPodResourcesDynamicResources=true"
|
||||
- role: worker
|
||||
# Required by DRA Integration
|
||||
##
|
||||
featureGates:
|
||||
DynamicResourceAllocation: true
|
||||
DRAResourceClaimDeviceStatus: true
|
||||
KubeletPodResourcesDynamicResources: true
|
||||
runtimeConfig:
|
||||
"api/beta": "true"
|
||||
containerdConfigPatches:
|
||||
# Enable CDI as described in
|
||||
# https://github.com/container-orchestrated-devices/container-device-interface#containerd-configuration
|
||||
- |-
|
||||
[plugins."io.containerd.grpc.v1.cri"]
|
||||
enable_cdi = true
|
||||
##
|
||||
EOF
|
||||
|
||||
# load multus image from container host to kind node
|
||||
kind load docker-image localhost:5000/multus:e2e
|
||||
|
||||
worker1_pid=$($OCI_BIN inspect --format "{{ .State.Pid }}" kind-worker)
|
||||
worker2_pid=$($OCI_BIN inspect --format "{{ .State.Pid }}" kind-worker2)
|
||||
|
||||
kind export kubeconfig
|
||||
sudo env PATH=${PATH} koko -p "$worker1_pid,eth1" -p "$worker2_pid,eth1"
|
||||
sleep 1
|
||||
kubectl -n kube-system wait --for=condition=available deploy/coredns --timeout=300s
|
||||
kubectl create -f yamls/$MULTUS_MANIFEST
|
||||
sleep 1
|
||||
kubectl -n kube-system wait --for=condition=ready -l name=multus pod --timeout=300s
|
||||
kubectl create -f yamls/cni-install.yml
|
||||
sleep 1
|
||||
kubectl -n kube-system wait --for=condition=ready -l name=cni-plugins pod --timeout=300s
|
11
e2e/simple-static-pod.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: static-web
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: "bridge-nad"
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
image: centos:8
|
||||
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
|
15
e2e/static-pod-nad.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: bridge-nad
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "testnet",
|
||||
"type": "bridge",
|
||||
"bridge": "testnet0",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.10.0.0/16"
|
||||
}
|
||||
}'
|
7
e2e/teardown.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
#set -o errexit
|
||||
|
||||
export PATH=${PATH}:./bin
|
||||
|
||||
# delete cluster kind
|
||||
kind delete cluster
|
64
e2e/templates/cni-install.yml.j2
Normal file
@@ -0,0 +1,64 @@
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: cni-install-sh
|
||||
namespace: kube-system
|
||||
data:
|
||||
install_cni.sh: |
|
||||
cd /tmp
|
||||
wget https://github.com/containernetworking/plugins/releases/download/v1.4.0/cni-plugins-linux-amd64-v1.4.0.tgz
|
||||
cd /host/opt/cni/bin
|
||||
tar xvfzp /tmp/cni-plugins-linux-amd64-v1.4.0.tgz
|
||||
sleep infinite
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: install-cni-plugins
|
||||
namespace: kube-system
|
||||
labels:
|
||||
name: cni-plugins
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
name: cni-plugins
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
name: cni-plugins
|
||||
spec:
|
||||
hostNetwork: true
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: amd64
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
containers:
|
||||
- name: install-cni-plugins
|
||||
image: alpine
|
||||
command: ["/bin/sh", "/scripts/install_cni.sh"]
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: cni-bin
|
||||
mountPath: /host/opt/cni/bin
|
||||
- name: scripts
|
||||
mountPath: /scripts
|
||||
volumes:
|
||||
- name: cni-bin
|
||||
hostPath:
|
||||
path: /opt/cni/bin
|
||||
- name: scripts
|
||||
configMap:
|
||||
name: cni-install-sh
|
||||
items:
|
||||
- key: install_cni.sh
|
||||
path: install_cni.sh
|
57
e2e/templates/default-route1.yml.j2
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: default-route-config
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "{{ CNI_VERSION }}",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "macvlan",
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "static"
|
||||
}
|
||||
} ]
|
||||
}'
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: default-route-worker1
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[
|
||||
{ "name": "default-route-config",
|
||||
"ips": [ "10.1.1.21/24" ] ,
|
||||
"default-route": [ "10.1.1.254" ] }
|
||||
]'
|
||||
labels:
|
||||
app: default-route1
|
||||
spec:
|
||||
containers:
|
||||
- name: default-route-worker1
|
||||
image: centos:8
|
||||
command: ["/bin/sleep", "10000"]
|
||||
securityContext:
|
||||
privileged: true
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: default-route-worker2
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[
|
||||
{ "name": "default-route-config",
|
||||
"ips": [ "10.1.1.22/24" ] }
|
||||
]'
|
||||
labels:
|
||||
app: default-route1
|
||||
spec:
|
||||
containers:
|
||||
- name: default-route-worker2
|
||||
image: centos:8
|
||||
command: ["/bin/sleep", "10000"]
|
||||
securityContext:
|
||||
privileged: true
|
49
e2e/templates/dra-integration.yml.j2
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
apiVersion: resource.k8s.io/v1alpha2
|
||||
kind: ResourceClaimTemplate
|
||||
metadata:
|
||||
name: gpu.example.com
|
||||
spec:
|
||||
spec:
|
||||
resourceClassName: gpu.example.com
|
||||
---
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: dra-net
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/resourceName: gpu.example.com
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "{{ CNI_VERSION }}",
|
||||
"plugins": [{
|
||||
"name": "mynet",
|
||||
"type": "dummy",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24"
|
||||
}
|
||||
}]
|
||||
}'
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: dra-integration
|
||||
labels:
|
||||
app: dra-integration
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: default/dra-net
|
||||
spec:
|
||||
containers:
|
||||
- name: ctr0
|
||||
image: ubuntu:22.04
|
||||
command: ["bash", "-c"]
|
||||
args: ["export; sleep 9999"]
|
||||
resources:
|
||||
claims:
|
||||
- name: gpu
|
||||
resourceClaims:
|
||||
- name: gpu
|
||||
source:
|
||||
resourceClaimTemplateName: gpu.example.com
|
210
e2e/templates/multus-daemonset-thick.yml.j2
Normal file
@@ -0,0 +1,210 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: network-attachment-definitions.k8s.cni.cncf.io
|
||||
spec:
|
||||
group: k8s.cni.cncf.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: network-attachment-definitions
|
||||
singular: network-attachment-definition
|
||||
kind: NetworkAttachmentDefinition
|
||||
shortNames:
|
||||
- net-attach-def
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
type: string
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
rules:
|
||||
- apiGroups: ["k8s.cni.cncf.io"]
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/status
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
- events.k8s.io
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: multus
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: multus-daemon-config
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
data:
|
||||
daemon-config.json: |
|
||||
{
|
||||
"confDir": "/host/etc/cni/net.d",
|
||||
"logToStderr": true,
|
||||
"logLevel": "debug",
|
||||
"logFile": "/tmp/multus.log",
|
||||
"binDir": "/host/opt/cni/bin",
|
||||
"cniDir": "/var/lib/cni/multus",
|
||||
"socketDir": "/host/run/multus",
|
||||
"cniVersion": "{{ CNI_VERSION }}",
|
||||
"cniConfigDir": "/host/etc/cni/net.d",
|
||||
"multusConfigFile": "auto",
|
||||
"forceCNIVersion": true,
|
||||
"multusAutoconfigDir": "/host/etc/cni/net.d"
|
||||
}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-multus-ds-amd64
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
name: multus
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: amd64
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
serviceAccountName: multus
|
||||
containers:
|
||||
- name: kube-multus
|
||||
image: localhost:5000/multus:e2e
|
||||
command: [ "/usr/src/multus-cni/bin/multus-daemon" ]
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: cni
|
||||
mountPath: /host/etc/cni/net.d
|
||||
- name: cnibin
|
||||
mountPath: /host/opt/cni/bin
|
||||
- name: host-run
|
||||
mountPath: /host/run
|
||||
- name: host-var-lib-cni-multus
|
||||
mountPath: /var/lib/cni/multus
|
||||
- name: host-run-netns
|
||||
mountPath: /run/netns
|
||||
mountPropagation: HostToContainer
|
||||
- name: multus-daemon-config
|
||||
mountPath: /etc/cni/net.d/multus.d
|
||||
readOnly: true
|
||||
- name: kubelet-pod-resources
|
||||
mountPath: /var/lib/kubelet/pod-resources
|
||||
readOnly: true
|
||||
env:
|
||||
- name: MULTUS_NODE_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: spec.nodeName
|
||||
initContainers:
|
||||
- name: install-multus-shim
|
||||
image: localhost:5000/multus:e2e
|
||||
command:
|
||||
- "sh"
|
||||
- "-c"
|
||||
- "cp /usr/src/multus-cni/bin/multus-shim /host/opt/cni/bin/multus-shim && cp /usr/src/multus-cni/bin/passthru /host/opt/cni/bin/passthru"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "10m"
|
||||
memory: "15Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: cnibin
|
||||
mountPath: /host/opt/cni/bin
|
||||
mountPropagation: Bidirectional
|
||||
volumes:
|
||||
- name: cni
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
- name: cnibin
|
||||
hostPath:
|
||||
path: /opt/cni/bin
|
||||
- name: kubelet-pod-resources
|
||||
hostPath:
|
||||
path: /var/lib/kubelet/pod-resources
|
||||
- name: multus-daemon-config
|
||||
configMap:
|
||||
name: multus-daemon-config
|
||||
items:
|
||||
- key: daemon-config.json
|
||||
path: daemon-config.json
|
||||
- name: host-run
|
||||
hostPath:
|
||||
path: /run
|
||||
- name: host-var-lib-cni-multus
|
||||
hostPath:
|
||||
path: /var/lib/cni/multus
|
||||
- name: host-run-netns
|
||||
hostPath:
|
||||
path: /run/netns/
|
198
e2e/templates/multus-daemonset.yml.j2
Normal file
@@ -0,0 +1,198 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: network-attachment-definitions.k8s.cni.cncf.io
|
||||
spec:
|
||||
group: k8s.cni.cncf.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
plural: network-attachment-definitions
|
||||
singular: network-attachment-definition
|
||||
kind: NetworkAttachmentDefinition
|
||||
shortNames:
|
||||
- net-attach-def
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
spec:
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
type: string
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
rules:
|
||||
- apiGroups: ["k8s.cni.cncf.io"]
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- pods/status
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: multus
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: multus
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: multus
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: multus-cni-config
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
data:
|
||||
# NOTE: If you'd prefer to manually apply a configuration file, you may create one here.
|
||||
# In the case you'd like to customize the Multus installation, you should change the arguments to the Multus pod
|
||||
# change the "args" line below from
|
||||
# - "--multus-conf-file=auto"
|
||||
# to:
|
||||
# "--multus-conf-file=/tmp/multus-conf/70-multus.conf"
|
||||
# Additionally -- you should ensure that the name "70-multus.conf" is the alphabetically first name in the
|
||||
# /etc/cni/net.d/ directory on each node, otherwise, it will not be used by the Kubelet.
|
||||
cni-conf.json: |
|
||||
{
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"capabilities": {
|
||||
"portMappings": true
|
||||
},
|
||||
"delegates": [
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "default-cni-network",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "flannel",
|
||||
"name": "flannel.1",
|
||||
"delegate": {
|
||||
"isDefaultGateway": true,
|
||||
"hairpinMode": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "portmap",
|
||||
"capabilities": {
|
||||
"portMappings": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig"
|
||||
}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-multus-ds-amd64
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
name: multus
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
name: multus
|
||||
spec:
|
||||
hostNetwork: true
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: amd64
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
serviceAccountName: multus
|
||||
containers:
|
||||
- name: kube-multus
|
||||
image: localhost:5000/multus:e2e
|
||||
command: ["/thin_entrypoint"]
|
||||
args:
|
||||
- "--multus-conf-file=auto"
|
||||
- "--force-cni-version=true"
|
||||
- "--cni-version={{ CNI_VERSION }}"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: cni
|
||||
mountPath: /host/etc/cni/net.d
|
||||
- name: cnibin
|
||||
mountPath: /host/opt/cni/bin
|
||||
- name: multus-cfg
|
||||
mountPath: /tmp/multus-conf
|
||||
initContainers:
|
||||
- name: install-multus-binary
|
||||
image: localhost:5000/multus:e2e
|
||||
command: ["/install_multus"]
|
||||
args:
|
||||
- "--type"
|
||||
- "thin"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "10m"
|
||||
memory: "15Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: cnibin
|
||||
mountPath: /host/opt/cni/bin
|
||||
mountPropagation: Bidirectional
|
||||
volumes:
|
||||
- name: cni
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
- name: cnibin
|
||||
hostPath:
|
||||
path: /opt/cni/bin
|
||||
- name: multus-cfg
|
||||
configMap:
|
||||
name: multus-cni-config
|
||||
items:
|
||||
- key: cni-conf.json
|
||||
path: 70-multus.conf
|
63
e2e/templates/simple-macvlan1.yml.j2
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: macvlan1-config
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "{{ CNI_VERSION }}",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "macvlan",
|
||||
"capabilities": { "ips": true },
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "static"
|
||||
}
|
||||
}, {
|
||||
"type": "tuning"
|
||||
} ]
|
||||
}'
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: macvlan1-worker1
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[
|
||||
{ "name": "macvlan1-config",
|
||||
"ips": [ "10.1.1.11/24" ] }
|
||||
]'
|
||||
labels:
|
||||
app: macvlan
|
||||
spec:
|
||||
containers:
|
||||
- name: macvlan-worker1
|
||||
image: centos:8
|
||||
command: ["/bin/sleep", "10000"]
|
||||
securityContext:
|
||||
privileged: true
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: kind-worker
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: macvlan1-worker2
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[
|
||||
{ "name": "macvlan1-config",
|
||||
"ips": [ "10.1.1.12/24" ] }
|
||||
]'
|
||||
labels:
|
||||
app: macvlan
|
||||
spec:
|
||||
containers:
|
||||
- name: macvlan-worker2
|
||||
image: centos:8
|
||||
command: ["/bin/sleep", "10000"]
|
||||
securityContext:
|
||||
privileged: true
|
||||
nodeSelector:
|
||||
kubernetes.io/hostname: kind-worker2
|
15
e2e/templates/simple-pod.yml.j2
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: simple-centos1
|
||||
annotations:
|
||||
labels:
|
||||
app: simple
|
||||
spec:
|
||||
containers:
|
||||
- name: simple-centos1
|
||||
image: centos:8
|
||||
command: ["/bin/sleep", "10000"]
|
||||
securityContext:
|
||||
privileged: true
|
@@ -0,0 +1,26 @@
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: multus-daemon-config
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
data:
|
||||
daemon-config.json: |
|
||||
{
|
||||
"confDir": "/host/etc/cni/net.d",
|
||||
"logToStderr": true,
|
||||
"logLevel": "debug",
|
||||
"logFile": "/tmp/multus.log",
|
||||
"binDir": "/host/opt/cni/bin",
|
||||
"cniDir": "/var/lib/cni/multus",
|
||||
"socketDir": "/host/run/multus",
|
||||
"cniVersion": "{{ CNI_VERSION }}",
|
||||
"cniConfigDir": "/host/etc/cni/net.d",
|
||||
"multusConfigFile": "auto",
|
||||
"forceCNIVersion": true,
|
||||
"multusAutoconfigDir": "/host/etc/cni/net.d",
|
||||
"auxiliaryCNIChainName": "vendor-cni-chain"
|
||||
}
|
94
e2e/templates/subdirectory-chaining-passthru.yml.j2
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cni-setup-script
|
||||
namespace: default
|
||||
data:
|
||||
setup.sh: |
|
||||
#!/bin/bash
|
||||
set -euxo pipefail
|
||||
|
||||
DEFAULT_NETWORK_CNI_NAME="vendor-cni-chain"
|
||||
|
||||
cleanup() {
|
||||
echo "Cleaning up..."
|
||||
rm -f /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}/sysctltwiddle.conf
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to remove sysctltwiddle.conf" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Cleanup completed successfully"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Create the chained CNI directory if it doesn't exist
|
||||
mkdir -p /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to create directory /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Write the chained tuning CNI config
|
||||
cat <<EOF > /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}/sysctltwiddle.conf
|
||||
{
|
||||
"cniVersion": "{{ CNI_VERSION }}",
|
||||
"name": "sysctltwiddle",
|
||||
"type": "tuning",
|
||||
"sysctl": {
|
||||
"net.ipv4.conf.eth0.arp_filter": "1"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to create chained CNI config" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "CNI chained setup completed successfully."
|
||||
sleep infinity
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: cni-setup-daemonset
|
||||
namespace: default
|
||||
labels:
|
||||
app: cni-setup
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cni-setup
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cni-setup
|
||||
spec:
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
- operator: Exists
|
||||
effect: NoExecute
|
||||
containers:
|
||||
- name: setup
|
||||
image: quay.io/fedora/fedora:40
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: cni-config
|
||||
mountPath: /host/etc/cni/net.d
|
||||
- name: script-volume
|
||||
mountPath: /scripts
|
||||
command: ["/bin/bash", "/scripts/setup.sh"]
|
||||
volumes:
|
||||
- name: cni-config
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
type: Directory
|
||||
- name: script-volume
|
||||
configMap:
|
||||
name: cni-setup-script
|
||||
items:
|
||||
- key: setup.sh
|
||||
path: setup.sh
|
11
e2e/templates/subdirectory-chaining-pod.yml.j2
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: sysctl-modified
|
||||
spec:
|
||||
containers:
|
||||
- name: sysctl
|
||||
image: quay.io/dosmith/fedora-procps
|
||||
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
|
||||
securityContext:
|
||||
privileged: true
|
95
e2e/templates/subdirectory-chaining.yml.j2
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cni-setup-script
|
||||
namespace: default
|
||||
data:
|
||||
setup.sh: |
|
||||
#!/bin/bash
|
||||
set -euxo pipefail
|
||||
|
||||
DEFAULT_NETWORK_CNI_NAME="kindnet"
|
||||
|
||||
cleanup() {
|
||||
echo "Cleaning up..."
|
||||
rm -f /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}/sysctltwiddle.conf
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to remove sysctltwiddle.conf" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Cleanup completed successfully"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Create the chained CNI directory if it doesn't exist
|
||||
mkdir -p /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to create directory /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Write the chained tuning CNI config
|
||||
cat <<EOF > /host/etc/cni/net.d/${DEFAULT_NETWORK_CNI_NAME}/sysctltwiddle.conf
|
||||
{
|
||||
"cniVersion": "{{ CNI_VERSION }}",
|
||||
"name": "sysctltwiddle",
|
||||
"type": "tuning",
|
||||
"sysctl": {
|
||||
"net.ipv4.conf.IFNAME.arp_filter": "1"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Failed to create chained CNI config" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "CNI chained setup completed successfully."
|
||||
sleep infinity
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: cni-setup-daemonset
|
||||
namespace: default
|
||||
labels:
|
||||
app: cni-setup
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: cni-setup
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: cni-setup
|
||||
spec:
|
||||
hostNetwork: true
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
- operator: Exists
|
||||
effect: NoExecute
|
||||
containers:
|
||||
- name: setup
|
||||
image: quay.io/fedora/fedora:40
|
||||
securityContext:
|
||||
privileged: true
|
||||
volumeMounts:
|
||||
- name: cni-config
|
||||
mountPath: /host/etc/cni/net.d
|
||||
- name: script-volume
|
||||
mountPath: /scripts
|
||||
command: ["/bin/bash", "/scripts/setup.sh"]
|
||||
volumes:
|
||||
- name: cni-config
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
type: Directory
|
||||
- name: script-volume
|
||||
configMap:
|
||||
name: cni-setup-script
|
||||
items:
|
||||
- key: setup.sh
|
||||
path: setup.sh
|
50
e2e/test-default-route1.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/sh
|
||||
set -o errexit
|
||||
|
||||
export PATH=${PATH}:./bin
|
||||
|
||||
kubectl create -f yamls/default-route1.yml
|
||||
kubectl wait --for=condition=ready -l app=default-route1 --timeout=300s pod
|
||||
|
||||
echo "check default-route-worker1 interface: net1"
|
||||
kubectl exec default-route-worker1 -- ip a show dev net1
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "check default-route-worker1 interface address: net1"
|
||||
ipaddr=$(kubectl exec default-route-worker1 -- ip -j a show | jq -r \
|
||||
'.[]|select(.ifname =="net1")|.addr_info[]|select(.family=="inet").local')
|
||||
if [ $ipaddr != "10.1.1.21" ]; then
|
||||
echo "default-route-worker1 IP address is different: ${ipaddr}"
|
||||
fi
|
||||
|
||||
echo "check default-route-worker1 default route"
|
||||
ipaddr=$(kubectl exec default-route-worker1 -- ip -j route | jq -r \
|
||||
'.[]|select(.dst=="default")|.gateway')
|
||||
if [ $ipaddr != "10.1.1.254" ]; then
|
||||
echo "default-route-worker1 default route is different: ${ipaddr}"
|
||||
fi
|
||||
|
||||
echo "check default-route-worker2 interface: net1"
|
||||
kubectl exec default-route-worker2 -- ip a show dev net1
|
||||
if [ $? -ne 0 ];then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "check default-route-worker2 interface address: net1"
|
||||
ipaddr=$(kubectl exec default-route-worker2 -- ip -j a show | jq -r \
|
||||
'.[]|select(.ifname =="net1")|.addr_info[]|select(.family=="inet").local')
|
||||
if [ $ipaddr != "10.1.1.22" ]; then
|
||||
echo "default-route-worker2 IP address is different: ${ipaddr}"
|
||||
fi
|
||||
|
||||
echo "check default-route-worker2 default route"
|
||||
ipaddr=$(kubectl exec default-route-worker2 -- ip -j route | jq -r \
|
||||
'.[]|select(.dst=="default")|.gateway')
|
||||
if [ $ipaddr != "10.244.1.1" ]; then
|
||||
echo "default-route-worker2 default route is different: ${ipaddr}"
|
||||
fi
|
||||
|
||||
echo "cleanup resources"
|
||||
kubectl delete -f yamls/default-route1.yml
|
60
e2e/test-dra-integration.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
set -o errexit
|
||||
|
||||
export PATH=${PATH}:./bin
|
||||
|
||||
# This test is using an example implementation of a DRA driver. This driver is mocking GPU resources. At our test we
|
||||
# don't care about what these resources are. We want to ensure that such resource is correctly passed in the Pod using
|
||||
# Multus configurations. A couple of notes:
|
||||
# - We explitictly pin the revision of the dra-example-driver to the branch `classic-dra` to indicate that the
|
||||
# integration continues to work even when the dra-example-driver is updated. We know that classic-dra is supported
|
||||
# in Kubernetes versions 1.26 to 1.30. Multus supports DRA in the aforementioned Kubernetes versions.
|
||||
# - The chart and latest is image is not published somewhere, therefore we have to build locally. This leads to slower
|
||||
# e2e suite runs.
|
||||
echo "installing dra-example-driver"
|
||||
repo_path="repos/dra-example-driver"
|
||||
|
||||
rm -rf $repo_path || true
|
||||
git clone --branch classic-dra https://github.com/kubernetes-sigs/dra-example-driver.git ${repo_path}
|
||||
${repo_path}/demo/build-driver.sh
|
||||
KIND_CLUSTER_NAME=kind ${repo_path}/demo/scripts/load-driver-image-into-kind.sh
|
||||
chart_path=${repo_path}/deployments/helm/dra-example-driver/
|
||||
overriden_values_path=${chart_path}/overriden_values.yaml
|
||||
|
||||
# With the thick plugin, in kind, the primary network on the control plane is not always working as expected. The pods
|
||||
# sometimes are not able to communicate with the control plane and the error looks like this:
|
||||
# failed to list *v1alpha2.PodSchedulingContext: Get "https://10.96.0.1:443/apis/resource.k8s.io/v1alpha2/podschedulingcontexts?limit=500&resourceVersion=0": dial tcp 10.96.0.1:443: connect: no route to host
|
||||
# We override the values here to schedule the controller on the worker nodes where the network is working as expected.
|
||||
cat <<EOF >> ${overriden_values_path}
|
||||
controller:
|
||||
nodeSelector: null
|
||||
tolerations: null
|
||||
EOF
|
||||
|
||||
helm install \
|
||||
-n dra-example-driver \
|
||||
--create-namespace \
|
||||
-f ${overriden_values_path} \
|
||||
dra-example-driver \
|
||||
${chart_path}
|
||||
|
||||
echo "installing testing pods"
|
||||
kubectl create -f yamls/dra-integration.yml
|
||||
kubectl wait --for=condition=ready -l app=dra-integration --timeout=300s pod
|
||||
|
||||
echo "check dra-integration pod for DRA injected environment variable"
|
||||
|
||||
# We can validate that the resource is correctly injected by checking an environment variable this dra driver is injecting
|
||||
# in the Pod.
|
||||
# https://github.com/kubernetes-sigs/dra-example-driver/blob/be2b8b1db47b8c757440e955ce5ced88c23bfe86/cmd/dra-example-kubeletplugin/cdi.go#L71C20-L71C44
|
||||
env_variable=$(kubectl exec dra-integration -- bash -c "echo \$DRA_RESOURCE_DRIVER_NAME | grep gpu.resource.example.com")
|
||||
if [ $? -eq 0 ];then
|
||||
echo "dra-integration pod has DRA injected environment variable"
|
||||
else
|
||||
echo "dra-integration pod doesn't have DRA injected environment variable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "cleanup resources"
|
||||
kubectl delete -f yamls/dra-integration.yml
|
||||
helm uninstall -n dra-example-driver dra-example-driver
|
42
e2e/test-simple-macvlan1.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
set -o errexit
|
||||
|
||||
export PATH=${PATH}:./bin
|
||||
|
||||
kubectl create -f yamls/simple-macvlan1.yml
|
||||
kubectl wait --for=condition=ready -l app=macvlan --timeout=300s pod
|
||||
|
||||
echo "check macvlan1-worker1 interface: net1"
|
||||
net=$(kubectl exec macvlan1-worker1 -- ip a show dev net1)
|
||||
if [ $? -eq 0 ];then
|
||||
echo "macvlan1-worker1 pod has net1 card"
|
||||
else
|
||||
echo "macvlan1-worker1 pod has no net1 card"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "check macvlan1-worker1 interface address: net1"
|
||||
ipaddr=$(kubectl exec macvlan1-worker1 -- ip -j a show | jq -r \
|
||||
'.[]|select(.ifname =="net1")|.addr_info[]|select(.family=="inet").local')
|
||||
if [ $ipaddr != "10.1.1.11" ]; then
|
||||
echo "macvlan1-worker1 IP address is different: ${ipaddr}"
|
||||
fi
|
||||
|
||||
echo "check macvlan1-worker2 interface: net1"
|
||||
net2=$(kubectl exec macvlan1-worker2 -- ip a show dev net1)
|
||||
if [ $? -eq 0 ];then
|
||||
echo "macvlan1-worker2 pod has net1 card"
|
||||
else
|
||||
echo "macvlan1-worker2 pod has no net1 card"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "check macvlan1-worker2 interface address: net1"
|
||||
ipaddr=$(kubectl exec macvlan1-worker2 -- ip -j a show | jq -r \
|
||||
'.[]|select(.ifname =="net1")|.addr_info[]|select(.family=="inet").local')
|
||||
if [ $ipaddr != "10.1.1.12" ]; then
|
||||
echo "macvlan1-worker2 IP address is different: ${ipaddr}"
|
||||
fi
|
||||
|
||||
echo "cleanup resources"
|
||||
kubectl delete -f yamls/simple-macvlan1.yml
|
10
e2e/test-simple-pod.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -o errexit
|
||||
|
||||
export PATH=${PATH}:./bin
|
||||
|
||||
kubectl create -f yamls/simple-pod.yml
|
||||
kubectl wait --for=condition=ready -l app=simple --timeout=300s pod
|
||||
|
||||
echo "cleanup resources"
|
||||
kubectl delete -f yamls/simple-pod.yml
|
22
e2e/test-static-pod.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -o errexit
|
||||
|
||||
echo "Creating network attachment definition"
|
||||
kubectl create -f static-pod-nad.yml
|
||||
|
||||
echo "Creating static pod config file"
|
||||
docker cp simple-static-pod.yml kind-worker:/etc/kubernetes/manifests/static-web.yaml
|
||||
|
||||
echo "Waiting for static pod to start"
|
||||
kubectl wait --for=condition=Ready --namespace=default pod/static-web-kind-worker
|
||||
|
||||
echo "Checking the pod annotation for net1 interface"
|
||||
kubectl exec static-web-kind-worker --namespace=default -- ip a show dev net1
|
||||
|
||||
echo "Deleting static pod"
|
||||
docker exec kind-worker /bin/bash -c "rm /etc/kubernetes/manifests/static-web.yaml"
|
||||
|
||||
echo "Deleting network attachment definition"
|
||||
kubectl delete -f static-pod-nad.yml
|
||||
|
||||
echo "Test complete"
|
81
e2e/test-subdirectory-chaining-passthru.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
export PATH=${PATH}:./bin
|
||||
|
||||
TEST_POD_NAME="sysctl-modified"
|
||||
EXPECTED_BINARIES="${EXPECTED_BINARIES:-/opt/cni/bin/ptp /opt/cni/bin/portmap /opt/cni/bin/tuning}"
|
||||
EXPECTED_CNI_DIR="/etc/cni/net.d"
|
||||
|
||||
# Reconfigure multus
|
||||
echo "Applying subdirectory chain passthru config..."
|
||||
kubectl apply -f yamls/subdirectory-chain-passthru-configupdate.yml
|
||||
|
||||
# Restart the multus daemonset to pick up the new config
|
||||
echo "Restarting Multus DaemonSet..."
|
||||
kubectl rollout restart daemonset kube-multus-ds-amd64 -n kube-system
|
||||
kubectl rollout status daemonset/kube-multus-ds-amd64 -n kube-system
|
||||
|
||||
# Debug: show CNI configs and binaries inside each Kind node
|
||||
echo "Checking CNI configs and binaries on nodes..."
|
||||
|
||||
for node in $(kubectl get nodes --no-headers | awk '{print $1}'); do
|
||||
container_name=$(docker ps --format '{{.Names}}' | grep "^${node}$")
|
||||
|
||||
echo "------"
|
||||
echo "Node: ${node} (container: ${container_name})"
|
||||
echo "Listing /opt/cni/bin contents..."
|
||||
docker exec "${container_name}" ls -l /opt/cni/bin || echo "WARNING: /opt/cni/bin missing!"
|
||||
|
||||
echo "Checking expected binaries..."
|
||||
for bin in $EXPECTED_BINARIES; do
|
||||
echo "Checking for ${bin}..."
|
||||
if docker exec "${container_name}" test -f "${bin}"; then
|
||||
echo "SUCCESS: ${bin} found."
|
||||
else
|
||||
echo "FAIL: ${bin} NOT found!"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Listing /etc/cni/net.d configs..."
|
||||
docker exec "${container_name}" ls -l ${EXPECTED_CNI_DIR} || echo "WARNING: ${EXPECTED_CNI_DIR} missing!"
|
||||
done
|
||||
echo "------"
|
||||
|
||||
# Deploy the daemonset that will lay down the chained CNI config
|
||||
echo "Applying CNI setup DaemonSet..."
|
||||
kubectl apply -f yamls/subdirectory-chaining-passthru.yml
|
||||
|
||||
# Wait for the daemonset pods to be ready (make sure they set up CNI config)
|
||||
echo "Waiting for CNI setup DaemonSet to be Ready..."
|
||||
kubectl rollout status daemonset/cni-setup-daemonset --timeout=300s
|
||||
|
||||
# Deploy a test pod that will get chained CNI applied
|
||||
echo "Applying test pod..."
|
||||
kubectl apply -f yamls/subdirectory-chaining-pod.yml
|
||||
|
||||
# Wait for the pod to be Ready
|
||||
echo "Waiting for test pod to be Ready..."
|
||||
kubectl wait --for=condition=ready pod/${TEST_POD_NAME} --timeout=300s
|
||||
|
||||
# Check that the sysctl got set
|
||||
echo "Verifying sysctl arp_filter is set to 1 on eth0..."
|
||||
|
||||
SYSCTL_VALUE=$(kubectl exec ${TEST_POD_NAME} -- sysctl -n net.ipv4.conf.eth0.arp_filter)
|
||||
|
||||
if [ "$SYSCTL_VALUE" != "1" ]; then
|
||||
echo "FAIL: net.ipv4.conf.eth0.arp_filter is not set to 1, got ${SYSCTL_VALUE}" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "SUCCESS: net.ipv4.conf.eth0.arp_filter is set correctly."
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
echo "Cleaning up test resources..."
|
||||
kubectl delete -f yamls/subdirectory-chaining-pod.yml
|
||||
kubectl delete -f yamls/subdirectory-chaining-passthru.yml
|
||||
|
||||
echo "Test completed successfully."
|
||||
exit 0
|
37
e2e/test-subdirectory-chaining.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
set -o errexit
|
||||
|
||||
export PATH=${PATH}:./bin
|
||||
|
||||
TEST_POD_NAME="sysctl-modified"
|
||||
|
||||
# Deploy the daemonset that will lay down the chained CNI config
|
||||
kubectl apply -f yamls/subdirectory-chaining.yml
|
||||
|
||||
# Wait for the daemonset pods to be ready (we need the config to be laid down)
|
||||
kubectl rollout status daemonset/cni-setup-daemonset
|
||||
|
||||
# Deploy a test pod that will get chained CNI applied
|
||||
kubectl apply -f yamls/subdirectory-chaining-pod.yml
|
||||
|
||||
# Wait for the pod to be Ready
|
||||
kubectl wait --for=condition=ready pod/sysctl-modified --timeout=300s
|
||||
|
||||
# Check that the sysctl got set properly inside the pod's eth0 interface
|
||||
echo "Verifying sysctl arp_filter is set to 1 on eth0"
|
||||
|
||||
SYSCTL_VALUE=$(kubectl exec sysctl-modified -- sysctl -n net.ipv4.conf.eth0.arp_filter)
|
||||
|
||||
if [ "$SYSCTL_VALUE" != "1" ]; then
|
||||
echo "FAIL: net.ipv4.conf.eth0.arp_filter is not set to 1, got ${SYSCTL_VALUE}" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "SUCCESS: net.ipv4.conf.eth0.arp_filter is set correctly."
|
||||
fi
|
||||
|
||||
# 6. Clean up
|
||||
echo "Cleaning up test resources"
|
||||
kubectl delete -f yamls/subdirectory-chaining-pod.yml
|
||||
kubectl delete -f yamls/subdirectory-chaining.yml
|
||||
|
||||
exit 0
|
@@ -4,7 +4,7 @@ In the `./examples` folder some example configurations are provided for using Mu
|
||||
|
||||
## Examples overview
|
||||
|
||||
Generally, the examples here show a setup using Multus with CRD support. The examples here demonstrate a setup with Multus as the meta-plugin used by Kubernetes, and delgating to either Flannel (which will be the default pod network), or to macvlan. The CRDs are intended to be alignment with the defacto standard.
|
||||
Generally, the examples here show a setup using Multus with CRD support. The examples here demonstrate a setup with Multus as the meta-plugin used by Kubernetes, and delegating to either Flannel (which will be the default pod network), or to macvlan. The CRDs are intended to be alignment with the defacto standard.
|
||||
|
||||
It is expected that aspects of your own setup will vary, at least in part, from some of what's demonstrated here. Namely, the IP address spaces, and likely the host ethernet interface names used in the macvlan part of the configuration.
|
||||
|
||||
@@ -12,7 +12,7 @@ More specifically, these examples show:
|
||||
|
||||
* Multus configured, using CNI a `.conf` file, with CRD support, specifying that we will use a "default network".
|
||||
* A resource definition with a daemonset that places the `.conf` on each node in the cluster.
|
||||
* A CRD definining the "networks" @ `networks.kubernetes.cni.cncf.io`
|
||||
* A CRD defining the "networks" @ `network-attachment-definitions.k8s.cni.cncf.io`
|
||||
* CRD objects containing the configuration for both Flannel & macvlan.
|
||||
|
||||
## Quick-start instructions
|
||||
@@ -37,7 +37,7 @@ More specifically, these examples show:
|
||||
|
||||
## RBAC configuration
|
||||
|
||||
You'll need to abnel the `system:node` users access to the API endpoints that will deliver the CRD objects to Multus.
|
||||
You'll need to enable the `system:node` users access to the API endpoints that will deliver the CRD objects to Multus.
|
||||
|
||||
Using these examples, you'll first create a cluster role with the provided sample:
|
||||
|
||||
@@ -60,3 +60,35 @@ A sample `cni-configuration.conf` is provided, typically this file is placed in
|
||||
## Other considerations
|
||||
|
||||
Primarily in this setup one thing that one should consider are the aspects of the `macvlan-conf.yml`, which is likely specific to the configuration of the node on which this resides.
|
||||
|
||||
## Passing down device information
|
||||
Some CNI plugins require specific device information which maybe pre-allocated by K8s device plugin. This could be indicated by providing `k8s.v1.cni.cncf.io/resourceName` annotation in its network attachment definition CRD. The file [`examples/sriov-net.yaml`](./sriov-net.yaml) shows an example on how to define a Network attachment definition with specific device allocation information. Multus will get allocated device information and make them available for CNI plugin to work on.
|
||||
|
||||
In this example (shown below), it is expected that an [SRIOV Device Plugin](https://github.com/intel/sriov-network-device-plugin/) making a pool of SRIOV VFs available to the K8s with `intel.com/sriov` as their resourceName. Any device allocated from this resource pool will be passed down by Multus to the [sriov-cni](https://github.com/intel/sriov-cni/tree/dev/k8s-deviceid-model) plugin in `deviceID` field. This is up to the sriov-cni plugin to capture this information and work with this specific device information.
|
||||
|
||||
```yaml
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: sriov-net-a
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/resourceName: intel.com/sriov
|
||||
spec:
|
||||
config: '{
|
||||
"type": "sriov",
|
||||
"vlan": 1000,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.56.217.0/24",
|
||||
"rangeStart": "10.56.217.171",
|
||||
"rangeEnd": "10.56.217.181",
|
||||
"routes": [{
|
||||
"dst": "0.0.0.0/0"
|
||||
}],
|
||||
"gateway": "10.56.217.1"
|
||||
}
|
||||
}'
|
||||
```
|
||||
The [sriov-pod.yml](./sriov-pod.yml) is an example Pod manifest file that requesting a SRIOV device from a host which is then configured using the above network attachment definition.
|
||||
|
||||
>For further information on how to configure SRIOV Device Plugin and SRIOV-CNI please refer to the links given above.
|
||||
|
@@ -1,16 +0,0 @@
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: multus-crd-overpowered
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- nonResourceURLs:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"delegates": [
|
||||
{
|
||||
"type": "flannel",
|
||||
"delegate": {
|
||||
"isDefaultGateway": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"kubeconfig": "/etc/kubernetes/kubelet.conf"
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
# name must match the spec fields below, and be in the form: <plural>.<group>
|
||||
name: networks.kubernetes.cni.cncf.io
|
||||
spec:
|
||||
# group name to use for REST API: /apis/<group>/<version>
|
||||
group: kubernetes.cni.cncf.io
|
||||
# version name to use for REST API: /apis/<group>/<version>
|
||||
version: v1
|
||||
# either Namespaced or Cluster
|
||||
scope: Namespaced
|
||||
names:
|
||||
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
|
||||
plural: networks
|
||||
# singular name to be used as an alias on the CLI and for display
|
||||
singular: network
|
||||
# kind is normally the CamelCased singular type. Your resource manifests use this.
|
||||
kind: Network
|
||||
# shortNames allow shorter string to match your resource on the CLI
|
||||
shortNames:
|
||||
- net
|
@@ -1,12 +0,0 @@
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: flannel-conf
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "flannel",
|
||||
"delegate": {
|
||||
"isDefaultGateway": true
|
||||
}
|
||||
}'
|
@@ -1,21 +0,0 @@
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: macvlan-conf
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth0",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "192.168.1.0/24",
|
||||
"rangeStart": "192.168.1.200",
|
||||
"rangeEnd": "192.168.1.216",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
],
|
||||
"gateway": "192.168.1.1"
|
||||
}
|
||||
}'
|
57
examples/macvlan-pod.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
# This net-attach-def defines macvlan-conf with
|
||||
# + ips capabilities to specify ip in pod annotation and
|
||||
# + mac capabilities to specify mac address in pod annotation
|
||||
# default gateway is defined as well
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: macvlan-conf
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.1",
|
||||
"plugins": [
|
||||
{
|
||||
"type": "macvlan",
|
||||
"capabilities": { "ips": true },
|
||||
"master": "eth0",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "static",
|
||||
"routes": [
|
||||
{
|
||||
"dst": "0.0.0.0/0",
|
||||
"gw": "10.1.1.1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}, {
|
||||
"capabilities": { "mac": true },
|
||||
"type": "tuning"
|
||||
}
|
||||
]
|
||||
}'
|
||||
---
|
||||
# Define a pod with macvlan-conf, defined above, with ip address and mac, and
|
||||
# "gateway" overrides default gateway to use macvlan-conf's one.
|
||||
# without "gateway" in k8s.v1.cni.cncf.io/networks, default route will be cluster
|
||||
# network interface, eth0, even tough macvlan-conf has default gateway config.
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: samplepod
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: '[
|
||||
{ "name": "macvlan-conf",
|
||||
"ips": [ "10.1.1.101/24" ],
|
||||
"mac": "c2:b0:57:49:47:f1",
|
||||
"gateway": [ "10.1.1.1" ]
|
||||
}]'
|
||||
spec:
|
||||
containers:
|
||||
- name: samplepod
|
||||
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
|
||||
image: dougbtv/centos-network
|
||||
ports:
|
||||
- containerPort: 80
|
||||
automountServiceAccountToken: false
|
@@ -1,162 +0,0 @@
|
||||
# -----------------------------------------------
|
||||
# - Example Configuration Deployment
|
||||
# -----------------------------------------------
|
||||
# - Deploys a .conf file on each node
|
||||
# - Configured for Multus + Flannel.
|
||||
# - As well as assets for Flannel
|
||||
# - Based on https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml
|
||||
# -----------------------------------------------
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: flannel
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes
|
||||
verbs:
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes/status
|
||||
verbs:
|
||||
- patch
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: flannel
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: flannel
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: flannel
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: flannel
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: kube-multus-cfg
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
data:
|
||||
cni-conf.json: |
|
||||
{
|
||||
"name": "multus-cni-network",
|
||||
"type": "multus",
|
||||
"delegates": [
|
||||
{
|
||||
"type": "flannel",
|
||||
"delegate": {
|
||||
"isDefaultGateway": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"kubeconfig": "/etc/kubernetes/kubelet.conf"
|
||||
}
|
||||
net-conf.json: |
|
||||
{
|
||||
"Network": "10.244.0.0/16",
|
||||
"Backend": {
|
||||
"Type": "vxlan"
|
||||
}
|
||||
}
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-multus-ds
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
tier: node
|
||||
app: multus
|
||||
spec:
|
||||
hostNetwork: true
|
||||
nodeSelector:
|
||||
beta.kubernetes.io/arch: amd64
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
serviceAccountName: flannel
|
||||
initContainers:
|
||||
- name: install-cni
|
||||
image: quay.io/coreos/flannel:v0.10.0-amd64
|
||||
command:
|
||||
- cp
|
||||
args:
|
||||
- -f
|
||||
- /etc/kube-flannel/cni-conf.json
|
||||
- /etc/cni/net.d/10-multus-with-flannel.conf
|
||||
volumeMounts:
|
||||
- name: cni
|
||||
mountPath: /etc/cni/net.d
|
||||
- name: multus-cfg
|
||||
mountPath: /etc/kube-flannel/
|
||||
containers:
|
||||
- name: kube-flannel
|
||||
image: quay.io/coreos/flannel:v0.10.0-amd64
|
||||
command:
|
||||
- /opt/bin/flanneld
|
||||
args:
|
||||
- --ip-masq
|
||||
- --kube-subnet-mgr
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
volumeMounts:
|
||||
- name: run
|
||||
mountPath: /run
|
||||
- name: multus-cfg
|
||||
mountPath: /etc/kube-flannel/
|
||||
volumes:
|
||||
- name: run
|
||||
hostPath:
|
||||
path: /run
|
||||
- name: cni
|
||||
hostPath:
|
||||
path: /etc/cni/net.d
|
||||
- name: multus-cfg
|
||||
configMap:
|
||||
name: kube-multus-cfg
|
@@ -1,22 +0,0 @@
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
# name must match the spec fields below, and be in the form: <plural>.<group>
|
||||
name: networks.kubernetes.cni.cncf.io
|
||||
spec:
|
||||
# group name to use for REST API: /apis/<group>/<version>
|
||||
group: kubernetes.cni.cncf.io
|
||||
# version name to use for REST API: /apis/<group>/<version>
|
||||
version: v1
|
||||
# either Namespaced or Cluster
|
||||
scope: Namespaced
|
||||
names:
|
||||
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
|
||||
plural: networks
|
||||
# singular name to be used as an alias on the CLI and for display
|
||||
singular: network
|
||||
# kind is normally the CamelCased singular type. Your resource manifests use this.
|
||||
kind: Network
|
||||
# shortNames allow shorter string to match your resource on the CLI
|
||||
shortNames:
|
||||
- net
|
@@ -1,16 +0,0 @@
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: multus-crd-overpowered
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
||||
- nonResourceURLs:
|
||||
- '*'
|
||||
verbs:
|
||||
- '*'
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: testns1
|
@@ -1,72 +0,0 @@
|
||||
---
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: macvlan-conf-1
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "static",
|
||||
"addresses": [
|
||||
{ "address": "10.1.1.101/24" }
|
||||
]
|
||||
}
|
||||
}'
|
||||
---
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: macvlan-conf-2
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "static",
|
||||
"addresses": [
|
||||
{ "address": "10.1.1.102/24" }
|
||||
]
|
||||
}
|
||||
}'
|
||||
---
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: macvlan-conf-3
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "static",
|
||||
"addresses": [
|
||||
{ "address": "10.1.1.103/24" }
|
||||
]
|
||||
}
|
||||
}'
|
||||
---
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: macvlan-conf-4
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "macvlan",
|
||||
"master": "eth1",
|
||||
"mode": "bridge",
|
||||
"ipam": {
|
||||
"type": "static",
|
||||
"addresses": [
|
||||
{ "address": "10.1.1.104/24" }
|
||||
]
|
||||
}
|
||||
}'
|
@@ -1,19 +0,0 @@
|
||||
---
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: vlan-conf-1-1
|
||||
namespace: testns1
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "vlan",
|
||||
"master": "eth1",
|
||||
"vlanid": 1,
|
||||
"ipam": {
|
||||
"type": "static",
|
||||
"addresses": [
|
||||
{ "address": "172.16.1.101/24"
|
||||
} ]
|
||||
}
|
||||
}'
|
@@ -1,194 +0,0 @@
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: flannel2
|
||||
namespace: kube-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes
|
||||
verbs:
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes/status
|
||||
verbs:
|
||||
- patch
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: flannel2
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: flannel2
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: flannel2
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ServiceAccount
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: flannel2
|
||||
namespace: kube-system
|
||||
---
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: kube-flannel2-cfg
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: flannel2
|
||||
data:
|
||||
flannel2-conf.json: |
|
||||
{
|
||||
"type": "flannel",
|
||||
"name": "flannel-2",
|
||||
"subnetFile": "/run/flannel/flannel2.env",
|
||||
"dataDir": "/var/lib/cni/flannel2",
|
||||
"delegate": {
|
||||
"bridge": "kbr1"
|
||||
}
|
||||
}
|
||||
net-conf.json: |
|
||||
{
|
||||
"Network": "10.144.0.0/16",
|
||||
"SubnetLen": 24,
|
||||
"SubnetMin": "10.144.0.0",
|
||||
"Backend": {
|
||||
"Type": "vxlan"
|
||||
}
|
||||
}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: flannel-etcd
|
||||
namespace: kube-system
|
||||
spec:
|
||||
containers:
|
||||
- command:
|
||||
- etcd
|
||||
- --advertise-client-urls=http://10.1.1.1:12379
|
||||
- --listen-client-urls=http://0.0.0.0:12379
|
||||
- --listen-peer-urls=http://localhost:12380
|
||||
image: quay.io/coreos/etcd:latest
|
||||
name: etcd
|
||||
hostNetwork: true
|
||||
nodeName: kube-master
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: flannel-etcdctl
|
||||
namespace: kube-system
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: flannel-etcdctl
|
||||
image: quay.io/coreos/etcd:latest
|
||||
command: ["etcdctl"]
|
||||
args: ["--endpoints=http://10.1.1.1:12379", "set", "/flannel2/network/config", '{ "Network": "10.5.0.0/16", "Backend": {"Type": "vxlan", "VNI": 2}}']
|
||||
hostNetwork: true
|
||||
nodeName: kube-master
|
||||
restartPolicy: Never
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: kube-flannel2-ds
|
||||
namespace: kube-system
|
||||
labels:
|
||||
tier: node
|
||||
app: flannel2
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
tier: node
|
||||
app: flannel2
|
||||
spec:
|
||||
hostNetwork: true
|
||||
nodeSelector:
|
||||
beta.kubernetes.io/arch: amd64
|
||||
tolerations:
|
||||
- key: node-role.kubernetes.io/master
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
serviceAccountName: flannel2
|
||||
initContainers:
|
||||
- name: install-cni
|
||||
image: quay.io/coreos/flannel:v0.10.0-amd64
|
||||
command:
|
||||
- cp
|
||||
args:
|
||||
- -f
|
||||
- /etc/kube-flannel/flannel2-conf.json
|
||||
- /etc/cni/multus/net.d/10-flannel.conf
|
||||
volumeMounts:
|
||||
- name: cni
|
||||
mountPath: /etc/cni/multus/net.d
|
||||
- name: flannel2-cfg
|
||||
mountPath: /etc/kube-flannel/
|
||||
containers:
|
||||
- name: kube-flannel2
|
||||
image: quay.io/coreos/flannel:v0.10.0-amd64
|
||||
command:
|
||||
- /opt/bin/flanneld
|
||||
args:
|
||||
- --ip-masq
|
||||
- --etcd-endpoints=http://10.1.1.1:12379
|
||||
- -iface=eth1
|
||||
- -subnet-file=/run/flannel/flannel2.env
|
||||
- -etcd-prefix=/flannel2/network
|
||||
resources:
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
limits:
|
||||
cpu: "100m"
|
||||
memory: "50Mi"
|
||||
securityContext:
|
||||
privileged: true
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
volumeMounts:
|
||||
- name: run
|
||||
mountPath: /run
|
||||
volumes:
|
||||
- name: run
|
||||
hostPath:
|
||||
path: /run
|
||||
- name: cni
|
||||
hostPath:
|
||||
path: /etc/cni/multus/net.d
|
||||
- name: flannel2-cfg
|
||||
configMap:
|
||||
name: kube-flannel2-cfg
|
||||
---
|
||||
apiVersion: "kubernetes.cni.cncf.io/v1"
|
||||
kind: Network
|
||||
metadata:
|
||||
name: flannel-2
|
@@ -1,13 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-01
|
||||
annotations:
|
||||
kubernetes.v1.cni.cncf.io/networks: macvlan-conf-1
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-01
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
@@ -1,18 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-02
|
||||
annotations:
|
||||
kubernetes.v1.cni.cncf.io/networks: '[
|
||||
{ "name": "macvlan-conf-2" },
|
||||
{ "name": "vlan-conf-1-1",
|
||||
"namespace": "testns1",
|
||||
"interfaceRequest": "vlan1-1" }
|
||||
]'
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-02
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
@@ -1,17 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-03
|
||||
annotations:
|
||||
kubernetes.v1.cni.cncf.io/networks: '[
|
||||
{ "name": "macvlan-conf-3" },
|
||||
{ "name": "macvlan-conf-4" },
|
||||
{ "name": "flannel-2" }
|
||||
]'
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-03
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
@@ -1,11 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-case-04
|
||||
spec:
|
||||
containers:
|
||||
- name: pod-case-04
|
||||
image: docker.io/centos/tools:latest
|
||||
command:
|
||||
- /sbin/init
|
@@ -1,14 +0,0 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: samplepod
|
||||
annotations:
|
||||
kubernetes.v1.cni.cncf.io/networks: macvlan-conf
|
||||
spec:
|
||||
containers:
|
||||
- name: samplepod
|
||||
command: ["/bin/bash", "-c", "sleep 2000000000000"]
|
||||
image: dougbtv/centos-network
|
||||
ports:
|
||||
- containerPort: 80
|
48
examples/sriov-pod.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
# This net-attach-def defines SR-IOV CNI config
|
||||
# Please see https://github.com/intel/sriov-cni and https://github.com/intel/sriov-network-device-plugin
|
||||
# for its detail.
|
||||
---
|
||||
apiVersion: "k8s.cni.cncf.io/v1"
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: sriov-net-a
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/resourceName: intel.com/sriov
|
||||
spec:
|
||||
config: '{
|
||||
"type": "sriov",
|
||||
"vlan": 1000,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.56.217.0/24",
|
||||
"rangeStart": "10.56.217.171",
|
||||
"rangeEnd": "10.56.217.181",
|
||||
"routes": [{
|
||||
"dst": "0.0.0.0/0"
|
||||
}],
|
||||
"gateway": "10.56.217.1"
|
||||
}
|
||||
}'
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: testpod1
|
||||
labels:
|
||||
env: test
|
||||
annotations:
|
||||
k8s.v1.cni.cncf.io/networks: sriov-net-a
|
||||
spec:
|
||||
containers:
|
||||
- name: appcntr1
|
||||
image: centos/tools
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: [ "/bin/bash", "-c", "--" ]
|
||||
args: [ "while true; do sleep 300000; done;" ]
|
||||
resources:
|
||||
requests:
|
||||
intel.com/sriov: '1'
|
||||
limits:
|
||||
intel.com/sriov: '1'
|
||||
restartPolicy: "Never"
|
||||
automountServiceAccountToken: false
|
241
glide.lock
generated
@@ -1,241 +0,0 @@
|
||||
hash: 9f567f5e6122fc0f7092f9373f1cfd491978315b0d9ff01ab81cebe269ffa048
|
||||
updated: 2018-05-08T04:22:31.321965197-04:00
|
||||
imports:
|
||||
- name: github.com/containernetworking/cni
|
||||
version: 07c1a6da47b7fbf8b357f4949ecce2113e598491
|
||||
subpackages:
|
||||
- libcni
|
||||
- pkg/invoke
|
||||
- pkg/ip
|
||||
- pkg/ipam
|
||||
- pkg/skel
|
||||
- pkg/types
|
||||
- pkg/types/020
|
||||
- pkg/types/current
|
||||
- pkg/version
|
||||
- name: github.com/containernetworking/plugins
|
||||
version: 2b8b1ac0af4568e928d96ccc5f47b075416eeabd
|
||||
subpackages:
|
||||
- pkg/ns
|
||||
- name: github.com/onsi/ginkgo
|
||||
version: 7f8ab55aaf3b86885aa55b762e803744d1674700
|
||||
- name: github.com/onsi/gomega
|
||||
version: 2152b45fa28a361beba9aab0885972323a444e28
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/docker/distribution
|
||||
version: cd27f179f2c10c5d300e6d09025b538c475b0d51
|
||||
subpackages:
|
||||
- digest
|
||||
- reference
|
||||
- name: github.com/emicklei/go-restful
|
||||
version: 09691a3b6378b740595c1002f40c34dd5f218a22
|
||||
subpackages:
|
||||
- log
|
||||
- swagger
|
||||
- name: github.com/ghodss/yaml
|
||||
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
|
||||
- name: github.com/go-openapi/jsonpointer
|
||||
version: 46af16f9f7b149af66e5d1bd010e3574dc06de98
|
||||
- name: github.com/go-openapi/jsonreference
|
||||
version: 13c6e3589ad90f49bd3e3bbe2c2cb3d7a4142272
|
||||
- name: github.com/go-openapi/spec
|
||||
version: 6aced65f8501fe1217321abf0749d354824ba2ff
|
||||
- name: github.com/go-openapi/swag
|
||||
version: 1d0bd113de87027671077d3c71eb3ac5d7dbba72
|
||||
- name: github.com/gogo/protobuf
|
||||
version: e18d7aa8f8c624c915db340349aad4c49b10d173
|
||||
subpackages:
|
||||
- proto
|
||||
- sortkeys
|
||||
- name: github.com/golang/glog
|
||||
version: 44145f04b68cf362d9c4df2182967c2275eaefed
|
||||
- name: github.com/google/gofuzz
|
||||
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
|
||||
- name: github.com/howeyc/gopass
|
||||
version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d
|
||||
- name: github.com/imdario/mergo
|
||||
version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
|
||||
- name: github.com/juju/ratelimit
|
||||
version: 77ed1c8a01217656d2080ad51981f6e99adaa177
|
||||
- name: github.com/mailru/easyjson
|
||||
version: d5b7844b561a7bc640052f1b935f7b800330d7e0
|
||||
subpackages:
|
||||
- buffer
|
||||
- jlexer
|
||||
- jwriter
|
||||
- name: github.com/PuerkitoBio/purell
|
||||
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
|
||||
- name: github.com/PuerkitoBio/urlesc
|
||||
version: 5bd2802263f21d8788851d5305584c82a5c75d7e
|
||||
- name: github.com/spf13/pflag
|
||||
version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7
|
||||
- name: github.com/ugorji/go
|
||||
version: ded73eae5db7e7a0ef6f55aace87a2873c5d2b74
|
||||
subpackages:
|
||||
- codec
|
||||
- name: github.com/vishvananda/netlink
|
||||
version: 4e28683688429fdf8413cc610d59fb1841986300
|
||||
subpackages:
|
||||
- nl
|
||||
- name: github.com/vishvananda/netns
|
||||
version: be1fbeda19366dea804f00efff2dd73a1642fdcc
|
||||
- name: golang.org/x/crypto
|
||||
version: d172538b2cfce0c13cee31e647d0367aa8cd2486
|
||||
subpackages:
|
||||
- ssh/terminal
|
||||
- name: golang.org/x/net
|
||||
version: e90d6d0afc4c315a0d87a568ae68577cc15149a0
|
||||
subpackages:
|
||||
- http2
|
||||
- http2/hpack
|
||||
- idna
|
||||
- lex/httplex
|
||||
- name: golang.org/x/sys
|
||||
version: 076b546753157f758b316e59bcb51e6807c04057
|
||||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/text
|
||||
version: 2910a502d2bf9e43193af9d68ca516529614eed3
|
||||
subpackages:
|
||||
- cases
|
||||
- internal/tag
|
||||
- language
|
||||
- runes
|
||||
- secure/bidirule
|
||||
- secure/precis
|
||||
- transform
|
||||
- unicode/bidi
|
||||
- unicode/norm
|
||||
- width
|
||||
- name: gopkg.in/inf.v0
|
||||
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: 53feefa2559fb8dfa8d81baad31be332c97d6c77
|
||||
- name: k8s.io/apimachinery
|
||||
version: b317fa7ec8e0e7d1f77ac63bf8c3ec7b29a2a215
|
||||
subpackages:
|
||||
- pkg/api/errors
|
||||
- pkg/api/meta
|
||||
- pkg/api/resource
|
||||
- pkg/apimachinery
|
||||
- pkg/apimachinery/announced
|
||||
- pkg/apimachinery/registered
|
||||
- pkg/apis/meta/v1
|
||||
- pkg/apis/meta/v1/unstructured
|
||||
- pkg/conversion
|
||||
- pkg/conversion/queryparams
|
||||
- pkg/fields
|
||||
- pkg/labels
|
||||
- pkg/openapi
|
||||
- pkg/runtime
|
||||
- pkg/runtime/schema
|
||||
- pkg/runtime/serializer
|
||||
- pkg/runtime/serializer/json
|
||||
- pkg/runtime/serializer/protobuf
|
||||
- pkg/runtime/serializer/recognizer
|
||||
- pkg/runtime/serializer/streaming
|
||||
- pkg/runtime/serializer/versioning
|
||||
- pkg/selection
|
||||
- pkg/types
|
||||
- pkg/util/errors
|
||||
- pkg/util/framer
|
||||
- pkg/util/intstr
|
||||
- pkg/util/json
|
||||
- pkg/util/net
|
||||
- pkg/util/rand
|
||||
- pkg/util/runtime
|
||||
- pkg/util/sets
|
||||
- pkg/util/validation
|
||||
- pkg/util/validation/field
|
||||
- pkg/util/wait
|
||||
- pkg/util/yaml
|
||||
- pkg/version
|
||||
- pkg/watch
|
||||
- third_party/forked/golang/reflect
|
||||
- name: k8s.io/client-go
|
||||
version: 4a3ab2f5be5177366f8206fd79ce55ca80e417fa
|
||||
subpackages:
|
||||
- discovery
|
||||
- kubernetes
|
||||
- kubernetes/scheme
|
||||
- kubernetes/typed/apps/v1beta1
|
||||
- kubernetes/typed/authentication/v1
|
||||
- kubernetes/typed/authentication/v1beta1
|
||||
- kubernetes/typed/authorization/v1
|
||||
- kubernetes/typed/authorization/v1beta1
|
||||
- kubernetes/typed/autoscaling/v1
|
||||
- kubernetes/typed/autoscaling/v2alpha1
|
||||
- kubernetes/typed/batch/v1
|
||||
- kubernetes/typed/batch/v2alpha1
|
||||
- kubernetes/typed/certificates/v1beta1
|
||||
- kubernetes/typed/core/v1
|
||||
- kubernetes/typed/extensions/v1beta1
|
||||
- kubernetes/typed/policy/v1beta1
|
||||
- kubernetes/typed/rbac/v1alpha1
|
||||
- kubernetes/typed/rbac/v1beta1
|
||||
- kubernetes/typed/settings/v1alpha1
|
||||
- kubernetes/typed/storage/v1
|
||||
- kubernetes/typed/storage/v1beta1
|
||||
- pkg/api
|
||||
- pkg/api/install
|
||||
- pkg/api/v1
|
||||
- pkg/apis/apps
|
||||
- pkg/apis/apps/install
|
||||
- pkg/apis/apps/v1beta1
|
||||
- pkg/apis/authentication
|
||||
- pkg/apis/authentication/install
|
||||
- pkg/apis/authentication/v1
|
||||
- pkg/apis/authentication/v1beta1
|
||||
- pkg/apis/authorization
|
||||
- pkg/apis/authorization/install
|
||||
- pkg/apis/authorization/v1
|
||||
- pkg/apis/authorization/v1beta1
|
||||
- pkg/apis/autoscaling
|
||||
- pkg/apis/autoscaling/install
|
||||
- pkg/apis/autoscaling/v1
|
||||
- pkg/apis/autoscaling/v2alpha1
|
||||
- pkg/apis/batch
|
||||
- pkg/apis/batch/install
|
||||
- pkg/apis/batch/v1
|
||||
- pkg/apis/batch/v2alpha1
|
||||
- pkg/apis/certificates
|
||||
- pkg/apis/certificates/install
|
||||
- pkg/apis/certificates/v1beta1
|
||||
- pkg/apis/extensions
|
||||
- pkg/apis/extensions/install
|
||||
- pkg/apis/extensions/v1beta1
|
||||
- pkg/apis/policy
|
||||
- pkg/apis/policy/install
|
||||
- pkg/apis/policy/v1beta1
|
||||
- pkg/apis/rbac
|
||||
- pkg/apis/rbac/install
|
||||
- pkg/apis/rbac/v1alpha1
|
||||
- pkg/apis/rbac/v1beta1
|
||||
- pkg/apis/settings
|
||||
- pkg/apis/settings/install
|
||||
- pkg/apis/settings/v1alpha1
|
||||
- pkg/apis/storage
|
||||
- pkg/apis/storage/install
|
||||
- pkg/apis/storage/v1
|
||||
- pkg/apis/storage/v1beta1
|
||||
- pkg/util
|
||||
- pkg/util/parsers
|
||||
- pkg/version
|
||||
- rest
|
||||
- rest/watch
|
||||
- tools/auth
|
||||
- tools/clientcmd
|
||||
- tools/clientcmd/api
|
||||
- tools/clientcmd/api/latest
|
||||
- tools/clientcmd/api/v1
|
||||
- tools/metrics
|
||||
- transport
|
||||
- util/cert
|
||||
- util/clock
|
||||
- util/flowcontrol
|
||||
- util/homedir
|
||||
- util/integer
|
||||
testImports: []
|
26
glide.yaml
@@ -1,26 +0,0 @@
|
||||
package: github.com/intel/multus-cni
|
||||
import:
|
||||
- package: github.com/containernetworking/cni
|
||||
version: 07c1a6da47b7fbf8b357f4949ecce2113e598491
|
||||
subpackages:
|
||||
- pkg/ip
|
||||
- pkg/ipam
|
||||
- pkg/skel
|
||||
- pkg/types
|
||||
- pkg/version
|
||||
- package: github.com/containernetworking/plugins
|
||||
version: 2b8b1ac0af4568e928d96ccc5f47b075416eeabd
|
||||
subpackages:
|
||||
- pkg/ns
|
||||
- package: github.com/onsi/ginkgo
|
||||
version: 7f8ab55aaf3b86885aa55b762e803744d1674700
|
||||
- package: github.com/onsi/gomega
|
||||
version: 2152b45fa28a361beba9aab0885972323a444e28
|
||||
- package: github.com/golang/glog
|
||||
- package: github.com/vishvananda/netlink
|
||||
- package: k8s.io/client-go
|
||||
version: 4a3ab2f5be5177366f8206fd79ce55ca80e417fa
|
||||
subpackages:
|
||||
- kubernetes
|
||||
- tools/clientcmd
|
||||
- package: github.com/vishvananda/netns
|
79
go.mod
Normal file
@@ -0,0 +1,79 @@
|
||||
module gopkg.in/k8snetworkplumbingwg/multus-cni.v4
|
||||
|
||||
go 1.21
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/containernetworking/cni v1.3.0
|
||||
github.com/containernetworking/plugins v1.1.0
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.6
|
||||
github.com/onsi/ginkgo/v2 v2.20.1
|
||||
github.com/onsi/gomega v1.34.1
|
||||
github.com/prometheus/client_golang v1.16.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/sys v0.31.0
|
||||
google.golang.org/grpc v1.58.3
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
k8s.io/api v0.29.0
|
||||
k8s.io/apimachinery v0.29.0
|
||||
k8s.io/client-go v0.29.0
|
||||
k8s.io/klog v1.0.0
|
||||
k8s.io/klog/v2 v2.110.1
|
||||
k8s.io/kubelet v0.27.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/imdario/mergo v0.3.11 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_model v0.4.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/prometheus/procfs v0.10.1 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/oauth2 v0.10.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
219
go.sum
Normal file
@@ -0,0 +1,219 @@
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
|
||||
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo=
|
||||
github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4=
|
||||
github.com/containernetworking/plugins v1.1.0 h1:kTIldaDo9SlbQsjhUKvDx0v9q7zyIFJH/Rm9F4xRBro=
|
||||
github.com/containernetworking/plugins v1.1.0/go.mod h1:Sr5TH/eBsGLXK/h71HeLfX19sZPp3ry5uHSkI4LPxV8=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU=
|
||||
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.6 h1:lhSaboKtal0XF2yqSw2BqNB1vUL4+a4BFe39I9G/yiM=
|
||||
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.7.6/go.mod h1:CM7HAH5PNuIsqjMN0fGc1ydM74Uj+0VZFhob620nklw=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo=
|
||||
github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
|
||||
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
|
||||
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
|
||||
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
|
||||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
|
||||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
|
||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5 h1:+UB2BJA852UkGH42H+Oee69djmxS3ANzl2b/JtT1YiA=
|
||||
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
|
||||
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
|
||||
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
|
||||
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A=
|
||||
k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA=
|
||||
k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o=
|
||||
k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis=
|
||||
k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8=
|
||||
k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38=
|
||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
|
||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
|
||||
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
|
||||
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
|
||||
k8s.io/kubelet v0.27.5 h1:uysO9NozKUi5zAde+hMXfCU1dWNjL/UBhRGVZk8uUJQ=
|
||||
k8s.io/kubelet v0.27.5/go.mod h1:xwIXdhJReWW2GuFQpAlj1qbaxD1O7JpGueItvc47tXg=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
84
hack/build-go.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
DEST_DIR="bin"
|
||||
|
||||
if [ ! -d ${DEST_DIR} ]; then
|
||||
mkdir ${DEST_DIR}
|
||||
fi
|
||||
|
||||
# Specify correspondingGOARCH from TARGETPLATFORM
|
||||
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then
|
||||
export GOARCH=amd64
|
||||
elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then
|
||||
export GOARCH=arm64
|
||||
elif [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then
|
||||
export GOARCH=arm
|
||||
elif [ "$TARGETPLATFORM" = "linux/ppc64le" ]; then
|
||||
export GOARCH=ppc64le
|
||||
elif [ "$TARGETPLATFORM" = "linux/s390x" ]; then
|
||||
export GOARCH=s390x
|
||||
fi
|
||||
|
||||
# version information
|
||||
hasGit=true
|
||||
git version > /dev/null 2>&1 || hasGit=false
|
||||
GIT_SHA=""
|
||||
GIT_TREE_STATE=""
|
||||
GIT_TAG=""
|
||||
GIT_TAG_LAST=""
|
||||
RELEASE_STATUS=""
|
||||
if $hasGit; then
|
||||
set +e
|
||||
GIT_SHA=$(git rev-parse --short HEAD)
|
||||
# Tree state is "dirty" if there are uncommitted changes, untracked files are ignored
|
||||
GIT_TREE_STATE=$(test -n "`git status --porcelain --untracked-files=no`" && echo "dirty" || echo "clean")
|
||||
# Empty string if we are not building a tag
|
||||
GIT_TAG=$(git describe --tags --abbrev=0 --exact-match 2>/dev/null || true)
|
||||
# Find most recent tag
|
||||
GIT_TAG_LAST=$(git describe --tags --abbrev=0 2>/dev/null || true)
|
||||
set -e
|
||||
fi
|
||||
|
||||
# VERSION override mechanism if needed
|
||||
VERSION=${VERSION:-}
|
||||
if [[ -n "${VERSION}" || -n "${GIT_TAG}" ]]; then
|
||||
RELEASE_STATUS=",released"
|
||||
fi
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION=$GIT_TAG_LAST
|
||||
fi
|
||||
# Add version/commit/date into binary
|
||||
DATE=$(date -u -d "@${SOURCE_DATE_EPOCH:-$(date +%s)}" --iso-8601=seconds)
|
||||
COMMIT=${COMMIT:-$(git rev-parse --verify HEAD)}
|
||||
LDFLAGS="-X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.version=${VERSION} \
|
||||
-X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.commit=${COMMIT} \
|
||||
-X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.gitTreeState=${GIT_TREE_STATE} \
|
||||
-X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.releaseStatus=${RELEASE_STATUS} \
|
||||
-X gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/multus.date=${DATE}"
|
||||
export CGO_ENABLED=${CGO_ENABLED:-0}
|
||||
|
||||
# build with go modules
|
||||
export GO111MODULE=on
|
||||
|
||||
if [ -n "$MODMODE" ]; then
|
||||
BUILD_ARGS=(-mod "$MODMODE")
|
||||
fi
|
||||
|
||||
echo "Building multus"
|
||||
go build -o ${DEST_DIR}/multus ${BUILD_ARGS} -ldflags "${LDFLAGS}" "$@" ./cmd/multus
|
||||
echo "Building multus-daemon"
|
||||
go build -o "${DEST_DIR}"/multus-daemon ${BUILD_ARGS} -ldflags "${LDFLAGS}" ./cmd/multus-daemon
|
||||
echo "Building multus-shim"
|
||||
go build -o "${DEST_DIR}"/multus-shim ${BUILD_ARGS} -ldflags "${LDFLAGS}" ./cmd/multus-shim
|
||||
echo "Building install_multus"
|
||||
go build -o "${DEST_DIR}"/install_multus ${BUILD_ARGS} -ldflags "${LDFLAGS}" ./cmd/install_multus
|
||||
echo "Building thin_entrypoint"
|
||||
go build -o "${DEST_DIR}"/thin_entrypoint ${BUILD_ARGS} -ldflags "${LDFLAGS}" ./cmd/thin_entrypoint
|
||||
echo "Building kubeconfig_generator"
|
||||
go build -o "${DEST_DIR}"/kubeconfig_generator ${BUILD_ARGS} -ldflags "${LDFLAGS}" ./cmd/kubeconfig_generator
|
||||
echo "Building cert-approver"
|
||||
go build -o "${DEST_DIR}"/cert-approver ${BUILD_ARGS} -ldflags "${LDFLAGS}" ./cmd/cert-approver
|
||||
echo "Building passthru CNI"
|
||||
go build -o "${DEST_DIR}"/passthru ${BUILD_ARGS} -ldflags "${LDFLAGS}" ./cmd/passthru-cni
|
23
hack/test-go.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# this if... will be removed when gomodules goes default
|
||||
if [ "$GO111MODULE" == "off" ]; then
|
||||
echo "Warning: this will be deprecated in near future so please use go modules!"
|
||||
|
||||
ORG_PATH="gopkg.in/k8snetworkplumbingwg"
|
||||
REPO_PATH="${ORG_PATH}/multus-cni"
|
||||
|
||||
if [ ! -h gopath/src/${REPO_PATH} ]; then
|
||||
mkdir -p gopath/src/${ORG_PATH}
|
||||
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
|
||||
fi
|
||||
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
export GOBIN=${PWD}/bin
|
||||
export GOPATH=${PWD}/gopath
|
||||
bash -c "umask 0; cd ${GOPATH}/src/${REPO_PATH}; PATH=${GOROOT}/bin:$(pwd)/bin:${PATH} go test -v -covermode=count -coverprofile=coverage.out ./..."
|
||||
else
|
||||
# test with go modules
|
||||
bash -c "umask 0; go test -v -race -covermode=atomic -coverprofile=coverage.out ./..."
|
||||
fi
|
22
images/Dockerfile
Normal file
@@ -0,0 +1,22 @@
|
||||
# This Dockerfile is used to build the image available on DockerHub
|
||||
FROM --platform=$BUILDPLATFORM golang:1.23 as build
|
||||
|
||||
# Add everything
|
||||
ADD . /usr/src/multus-cni
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
RUN cd /usr/src/multus-cni && \
|
||||
./hack/build-go.sh
|
||||
|
||||
FROM gcr.io/distroless/base-debian11:latest
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
WORKDIR /
|
||||
|
||||
COPY --from=build /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=build /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
COPY --from=build /usr/src/multus-cni/bin/kubeconfig_generator /
|
||||
COPY --from=build /usr/src/multus-cni/bin/cert-approver /
|
||||
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
22
images/Dockerfile.debug
Normal file
@@ -0,0 +1,22 @@
|
||||
# This Dockerfile is used to build the image available on DockerHub
|
||||
FROM --platform=$BUILDPLATFORM golang:1.23 as build
|
||||
|
||||
# Add everything
|
||||
ADD . /usr/src/multus-cni
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
RUN cd /usr/src/multus-cni && \
|
||||
./hack/build-go.sh
|
||||
|
||||
FROM gcr.io/distroless/base-debian11:debug
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
WORKDIR /
|
||||
|
||||
COPY --from=build /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=build /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
COPY --from=build /usr/src/multus-cni/bin/kubeconfig_generator /
|
||||
COPY --from=build /usr/src/multus-cni/bin/cert-approver /
|
||||
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
23
images/Dockerfile.openshift
Normal file
@@ -0,0 +1,23 @@
|
||||
# This dockerfile is specific to building Multus for OpenShift
|
||||
# The okd-builder image is locally built from https://raw.githubusercontent.com/okd-project/images/main/okd-builder.Dockerfile
|
||||
FROM local/okdbuilder:latest as builder
|
||||
|
||||
ADD . /usr/src/multus-cni
|
||||
|
||||
WORKDIR /usr/src/multus-cni
|
||||
ENV GO111MODULE=off
|
||||
RUN ./hack/build-go.sh
|
||||
|
||||
FROM quay.io/openshift/origin-base:latest
|
||||
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
RUN mkdir -p /usr/src/multus-cni/images && mkdir -p /usr/src/multus-cni/bin
|
||||
COPY --from=builder /usr/src/multus-cni/bin/multus /usr/src/multus-cni/bin
|
||||
COPY --from=builder /usr/src/multus-cni/bin/install_multus /
|
||||
COPY --from=builder /usr/src/multus-cni/bin/thin_entrypoint /
|
||||
|
||||
LABEL io.k8s.display-name="Multus CNI" \
|
||||
io.k8s.description="This is a component of OpenShift Container Platform and provides a meta CNI plugin." \
|
||||
io.openshift.tags="openshift" \
|
||||
maintainer="Doug Smith <dosmith@redhat.com>"
|
||||
|
||||
ENTRYPOINT ["/thin_entrypoint"]
|
18
images/Dockerfile.thick
Normal file
@@ -0,0 +1,18 @@
|
||||
# This Dockerfile is used to build the image available on DockerHub
|
||||
FROM --platform=$BUILDPLATFORM golang:1.23 as build
|
||||
|
||||
# Add everything
|
||||
ADD . /usr/src/multus-cni
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
RUN cd /usr/src/multus-cni && \
|
||||
./hack/build-go.sh
|
||||
|
||||
FROM debian:stable-slim
|
||||
LABEL org.opencontainers.image.source=https://github.com/k8snetworkplumbingwg/multus-cni
|
||||
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
|
||||
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
|
||||
COPY --from=build /usr/src/multus-cni/bin/cert-approver /
|
||||
WORKDIR /
|
||||
|
||||
ENTRYPOINT [ "/usr/src/multus-cni/bin/multus-daemon" ]
|
66
images/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
## Dockerfile build
|
||||
|
||||
This is used for distribution of Multus in a Docker image.
|
||||
|
||||
Typically you'd build this from the root of your Multus clone, as such:
|
||||
|
||||
```
|
||||
$ docker build -t dougbtv/multus -f images/Dockerfile .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Daemonset deployment
|
||||
|
||||
You may wish to deploy Multus as a daemonset, you can do so by starting with the example Daemonset shown here:
|
||||
|
||||
```
|
||||
$ kubectl create -f ./deployments/multus-daemonset.yml
|
||||
```
|
||||
|
||||
Note: The likely best practice here is to build your own image given the Dockerfile, and then push it to your preferred registry, and change the `image` fields in the Daemonset YAML to reference that image.
|
||||
|
||||
---
|
||||
|
||||
### `entrypoint.sh` parameters
|
||||
|
||||
The entrypoint takes named parameters for the configuration
|
||||
|
||||
You can get get help with the `--help` flag.
|
||||
|
||||
```
|
||||
$ ./entrypoint.sh --help
|
||||
|
||||
This is an entrypoint script for Multus CNI to overlay its binary and
|
||||
configuration into locations in a filesystem. The configuration & binary file
|
||||
will be copied to the corresponding configuration directory. When
|
||||
`--multus-conf-file=auto` is used, 00-multus.conf will be automatically
|
||||
generated from the CNI configuration file of the master plugin (the first file
|
||||
in lexicographical order in cni-conf-dir).
|
||||
|
||||
./entrypoint.sh
|
||||
-h --help
|
||||
--cni-conf-dir=/host/etc/cni/net.d
|
||||
--multus-conf-file=/usr/src/multus-cni/images/70-multus.conf
|
||||
--multus-kubeconfig-file-host=/etc/cni/net.d/multus.d/multus.kubeconfig
|
||||
```
|
||||
|
||||
You must use an `=` to delimit the parameter name and the value. For example you may set a custom `cni-conf-dir` like so:
|
||||
|
||||
```
|
||||
./entrypoint.sh --cni-conf-dir=/special/path/to/cni/configs/
|
||||
```
|
||||
|
||||
Note: You'll noticed that there's a `/host/...` directory from the root for the default for both the `cni-conf-dir` and `cni-bin-dir` as it's intended for the host volumes to be mounted specially under this directory to help in the semantics of which paths belong to the host or container.
|
||||
|
||||
---
|
||||
|
||||
### Development notes
|
||||
|
||||
Example docker run command:
|
||||
|
||||
```
|
||||
$ docker run -it -v /opt/cni/bin/:/host/opt/cni/bin/ -v /etc/cni/net.d/:/host/etc/cni/net.d/ --entrypoint=/bin/bash dougbtv/multus
|
||||
```
|
||||
|
||||
Originally inspired by and is a portmanteau of the [Flannel daemonset](https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml), the [Calico Daemonset](https://docs.projectcalico.org/manifests/calico.yaml), and the [Calico CNI install bash script](https://github.com/projectcalico/cni-plugin/blob/be4df4db2e47aa7378b1bdf6933724bac1f348d0/k8s-install/scripts/install-cni.sh#L104-L153).
|
@@ -1,356 +0,0 @@
|
||||
// Copyright (c) 2017 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package k8sclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
v1 "k8s.io/client-go/pkg/api/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
cnitypes "github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/intel/multus-cni/types"
|
||||
)
|
||||
|
||||
// NoK8sNetworkError indicates error, no network in kubernetes
|
||||
type NoK8sNetworkError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (e *NoK8sNetworkError) Error() string { return string(e.message) }
|
||||
|
||||
type defaultKubeClient struct {
|
||||
client kubernetes.Interface
|
||||
}
|
||||
|
||||
// defaultKubeClient implements KubeClient
|
||||
var _ KubeClient = &defaultKubeClient{}
|
||||
|
||||
func (d *defaultKubeClient) GetRawWithPath(path string) ([]byte, error) {
|
||||
return d.client.ExtensionsV1beta1().RESTClient().Get().AbsPath(path).DoRaw()
|
||||
}
|
||||
|
||||
func (d *defaultKubeClient) GetPod(namespace, name string) (*v1.Pod, error) {
|
||||
return d.client.Core().Pods(namespace).Get(name, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
func createK8sClient(kubeconfig string) (KubeClient, error) {
|
||||
// uses the current context in kubeconfig
|
||||
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("createK8sClient: failed to get context for the kubeconfig %v, refer Multus README.md for the usage guide: %v", kubeconfig, err)
|
||||
}
|
||||
|
||||
// creates the clientset
|
||||
client, err := kubernetes.NewForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &defaultKubeClient{client: client}, nil
|
||||
}
|
||||
|
||||
func getPodNetworkAnnotation(client KubeClient, k8sArgs types.K8sArgs) (string, string, error) {
|
||||
var err error
|
||||
|
||||
pod, err := client.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("getPodNetworkAnnotation: failed to query the pod %v in out of cluster comm: %v", string(k8sArgs.K8S_POD_NAME), err)
|
||||
}
|
||||
|
||||
return pod.Annotations["kubernetes.v1.cni.cncf.io/networks"], pod.ObjectMeta.Namespace, nil
|
||||
}
|
||||
|
||||
func parsePodNetworkObjectName(podnetwork string) (string, string, string, error) {
|
||||
var netNsName string
|
||||
var netIfName string
|
||||
var networkName string
|
||||
|
||||
slashItems := strings.Split(podnetwork, "/")
|
||||
if len(slashItems) == 2 {
|
||||
netNsName = strings.TrimSpace(slashItems[0])
|
||||
networkName = slashItems[1]
|
||||
} else if len(slashItems) == 1 {
|
||||
networkName = slashItems[0]
|
||||
} else {
|
||||
return "", "", "", fmt.Errorf("Invalid network object (failed at '/')")
|
||||
}
|
||||
|
||||
atItems := strings.Split(networkName, "@")
|
||||
networkName = strings.TrimSpace(atItems[0])
|
||||
if len(atItems) == 2 {
|
||||
netIfName = strings.TrimSpace(atItems[1])
|
||||
} else if len(atItems) != 1 {
|
||||
return "", "", "", fmt.Errorf("Invalid network object (failed at '@')")
|
||||
}
|
||||
|
||||
// Check and see if each item matches the specification for valid attachment name.
|
||||
// "Valid attachment names must be comprised of units of the DNS-1123 label format"
|
||||
// [a-z0-9]([-a-z0-9]*[a-z0-9])?
|
||||
// And we allow at (@), and forward slash (/) (units separated by commas)
|
||||
// It must start and end alphanumerically.
|
||||
allItems := []string{netNsName, networkName, netIfName}
|
||||
for i := range allItems {
|
||||
matched, _ := regexp.MatchString("^[a-z0-9]([-a-z0-9]*[a-z0-9])?$", allItems[i])
|
||||
if !matched && len([]rune(allItems[i])) > 0 {
|
||||
return "", "", "", fmt.Errorf(fmt.Sprintf("Failed to parse: one or more items did not match comma-delimited format (must consist of lower case alphanumeric characters). Must start and end with an alphanumeric character), mismatch @ '%v'", allItems[i]))
|
||||
}
|
||||
}
|
||||
|
||||
return netNsName, networkName, netIfName, nil
|
||||
}
|
||||
|
||||
func parsePodNetworkAnnotation(podNetworks, defaultNamespace string) ([]*types.NetworkSelectionElement, error) {
|
||||
var networks []*types.NetworkSelectionElement
|
||||
|
||||
if podNetworks == "" {
|
||||
return nil, fmt.Errorf("parsePodNetworkAnnotation: pod annotation not having \"network\" as key, refer Multus README.md for the usage guide")
|
||||
}
|
||||
|
||||
if strings.IndexAny(podNetworks, "[{\"") >= 0 {
|
||||
if err := json.Unmarshal([]byte(podNetworks), &networks); err != nil {
|
||||
return nil, fmt.Errorf("parsePodNetworkAnnotation: failed to parse pod Network Attachment Selection Annotation JSON format: %v", err)
|
||||
}
|
||||
} else {
|
||||
// Comma-delimited list of network attachment object names
|
||||
for _, item := range strings.Split(podNetworks, ",") {
|
||||
// Remove leading and trailing whitespace.
|
||||
item = strings.TrimSpace(item)
|
||||
|
||||
// Parse network name (i.e. <namespace>/<network name>@<ifname>)
|
||||
netNsName, networkName, netIfName, err := parsePodNetworkObjectName(item)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsePodNetworkAnnotation: %v", err)
|
||||
}
|
||||
|
||||
networks = append(networks, &types.NetworkSelectionElement{
|
||||
Name: networkName,
|
||||
Namespace: netNsName,
|
||||
InterfaceRequest: netIfName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, net := range networks {
|
||||
if net.Namespace == "" {
|
||||
net.Namespace = defaultNamespace
|
||||
}
|
||||
}
|
||||
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
func getCNIConfig(name string, ifname string, confdir string) (string, error) {
|
||||
|
||||
// In the absence of valid keys in a Spec, the runtime (or
|
||||
// meta-plugin) should load and execute a CNI .configlist
|
||||
// or .config (in that order) file on-disk whose JSON
|
||||
// “name” key matches this Network object’s name.
|
||||
|
||||
//Todo
|
||||
// support conflist for chaining mechanism
|
||||
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#getDefaultCNINetwork
|
||||
files, err := libcni.ConfFiles(confdir, []string{".conf", ".json"})
|
||||
switch {
|
||||
case err != nil:
|
||||
return "", fmt.Errorf("No networks found in %s", confdir)
|
||||
case len(files) == 0:
|
||||
return "", fmt.Errorf("No networks found in %s", confdir)
|
||||
}
|
||||
|
||||
for _, confFile := range files {
|
||||
conf, err := libcni.ConfFromFile(confFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error loading CNI config file %s: %v", confFile, err)
|
||||
}
|
||||
|
||||
if conf.Network.Name == name {
|
||||
// Ensure the config has a "type" so we know what plugin to run.
|
||||
// Also catches the case where somebody put a conflist into a conf file.
|
||||
if conf.Network.Type == "" {
|
||||
return "", fmt.Errorf("Error loading CNI config file %s: no 'type'; perhaps this is a .conflist?", confFile)
|
||||
}
|
||||
|
||||
return getConfig(string(conf.Bytes[:]), ifname), nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no network available in the name %s in cni dir %s", name, confdir)
|
||||
}
|
||||
|
||||
func getPlugin(plugin string, name string, ifname string) string {
|
||||
tmpconfig := []string{}
|
||||
|
||||
tmpconfig = append(tmpconfig, fmt.Sprintf(`{"cniVersion": "0.3.1" , "name": "%s", "type": "%s"`, name, plugin))
|
||||
|
||||
if ifname != "" {
|
||||
tmpconfig = append(tmpconfig, fmt.Sprintf(`, "ifnameRequest": "%s"`, ifname))
|
||||
}
|
||||
|
||||
tmpconfig = append(tmpconfig, "}")
|
||||
|
||||
return strings.Join(tmpconfig, "")
|
||||
|
||||
}
|
||||
|
||||
func getConfig(config string, ifname string) string {
|
||||
tmpconfig := []string{}
|
||||
|
||||
config = strings.TrimSpace(config)
|
||||
tmpconfig = append(tmpconfig, config[:1])
|
||||
|
||||
if ifname != "" {
|
||||
tmpconfig = append(tmpconfig, fmt.Sprintf(` "ifnameRequest": "%s",`, ifname))
|
||||
}
|
||||
|
||||
tmpconfig = append(tmpconfig, config[1:])
|
||||
|
||||
return strings.Join(tmpconfig, "")
|
||||
|
||||
}
|
||||
|
||||
func getNetSpec(ns types.NetworkSpec, name string, ifname string) (string, error) {
|
||||
|
||||
if ns.Plugin == "" && ns.Config == "" {
|
||||
return "", fmt.Errorf("Network Object spec plugin and config can't be empty")
|
||||
}
|
||||
|
||||
if ns.Plugin != "" && ns.Config != "" {
|
||||
return "", fmt.Errorf("Network Object spec can't have both plugin and config")
|
||||
}
|
||||
|
||||
if ns.Plugin != "" {
|
||||
// Plugin contains the name of a CNI plugin on-disk in a
|
||||
// runtime-defined path (eg /opt/cni/bin and/or other paths.
|
||||
// This plugin should be executed with a basic CNI JSON
|
||||
// configuration on stdin containing the Network object
|
||||
// name and the plugin:
|
||||
// { “cniVersion”: “0.3.1”, “type”: <Plugin>, “name”: <Network.Name> }
|
||||
// and any additional “runtimeConfig” field per the
|
||||
// CNI specification and conventions.
|
||||
return getPlugin(ns.Plugin, name, ifname), nil
|
||||
}
|
||||
|
||||
// Config contains a standard JSON-encoded CNI configuration
|
||||
// or configuration list which defines the plugin chain to
|
||||
// execute. If present, this key takes precedence over
|
||||
// ‘Plugin’.
|
||||
return getConfig(ns.Config, ifname), nil
|
||||
|
||||
}
|
||||
|
||||
func cniConfigFromNetworkResource(customResource *types.Network, net *types.NetworkSelectionElement, confdir string) (string, error) {
|
||||
var config string
|
||||
var err error
|
||||
|
||||
if (types.NetworkSpec{}) == customResource.Spec {
|
||||
// Network Spec empty; generate delegate from CNI JSON config
|
||||
// from the configuration directory that has the same network
|
||||
// name as the custom resource
|
||||
config, err = getCNIConfig(customResource.Metadata.Name, net.InterfaceRequest, confdir)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cniConfigFromNetworkResource: err in getCNIConfig: %v", err)
|
||||
}
|
||||
} else {
|
||||
// Generate delegate from CNI configuration embedded in the
|
||||
// custom resource
|
||||
config, err = getNetSpec(customResource.Spec, customResource.Metadata.Name, net.InterfaceRequest)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cniConfigFromNetworkResource: err in getNetSpec: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func getKubernetesDelegate(client KubeClient, net *types.NetworkSelectionElement, confdir string) (*types.DelegateNetConf, error) {
|
||||
rawPath := fmt.Sprintf("/apis/kubernetes.cni.cncf.io/v1/namespaces/%s/networks/%s", net.Namespace, net.Name)
|
||||
netData, err := client.GetRawWithPath(rawPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getKubernetesDelegate: failed to get network resource, refer Multus README.md for the usage guide: %v", err)
|
||||
}
|
||||
|
||||
customResource := &types.Network{}
|
||||
if err := json.Unmarshal(netData, customResource); err != nil {
|
||||
return nil, fmt.Errorf("getKubernetesDelegate: failed to get the netplugin data: %v", err)
|
||||
}
|
||||
|
||||
cniConfig, err := cniConfigFromNetworkResource(customResource, net, confdir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delegate, err := types.LoadDelegateNetConf([]byte(cniConfig))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return delegate, nil
|
||||
}
|
||||
|
||||
type KubeClient interface {
|
||||
GetRawWithPath(path string) ([]byte, error)
|
||||
GetPod(namespace, name string) (*v1.Pod, error)
|
||||
}
|
||||
|
||||
func GetK8sNetwork(args *skel.CmdArgs, kubeconfig string, k8sclient KubeClient, confdir string) ([]*types.DelegateNetConf, error) {
|
||||
k8sArgs := types.K8sArgs{}
|
||||
|
||||
err := cnitypes.LoadArgs(args.Args, &k8sArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if k8sclient == nil {
|
||||
k8sclient, err = createK8sClient(kubeconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
netAnnot, defaultNamespace, err := getPodNetworkAnnotation(k8sclient, k8sArgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(netAnnot) == 0 {
|
||||
return nil, &NoK8sNetworkError{"no kubernetes network found"}
|
||||
}
|
||||
|
||||
networks, err := parsePodNetworkAnnotation(netAnnot, defaultNamespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read all network objects referenced by 'networks'
|
||||
var delegates []*types.DelegateNetConf
|
||||
for _, net := range networks {
|
||||
delegate, err := getKubernetesDelegate(k8sclient, net, confdir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetK8sNetwork: failed getting the delegate: %v", err)
|
||||
}
|
||||
delegates = append(delegates, delegate)
|
||||
}
|
||||
|
||||
return delegates, nil
|
||||
}
|