181 Commits

Author SHA1 Message Date
OpenShift Merge Robot
69a2963525 Merge pull request #66 from s1061123/dev/remove-git-action
Bug 1835037: Dev/remove git action
2020-05-12 17:42:14 -07:00
OpenShift Merge Robot
96aa1de042 Merge pull request #64 from dougbtv/downstream-45-api-timeout
Bug 1826084: [backport 4.5] Sets the Kubernetes API calls timeout to 60 seconds
2020-05-11 17:56:48 +02:00
dougbtv
16eaacbba9 Sets the Kubernetes API calls timeout to 60 seconds 2020-05-05 14:24:42 -04:00
Tomofumi Hayashi
4ef1da49f3 Remove github action that is not worked in openshift
This change removes github action that is not supported with cgo.
2020-04-24 19:27:22 +09:00
OpenShift Merge Robot
f2c97bf2cc Merge pull request #60 from dougbtv/downstream-master-pick-ns-isolation-default
Bug 1827377: Allows allow pods in any namespace refer to net-attach-defs in default namespace
2020-04-24 11:05:07 +02:00
dougbtv
fb29016d92 Allows namespaceIsolation to allow pods in any namespace refer to the default namespace 2020-04-22 14:48:56 -04:00
Tomofumi Hayashi
fec92e59d0 Merge remote-tracking branch 'upstream/master' 2020-04-22 13:23:27 +09:00
OpenShift Merge Robot
1683374fde Merge pull request #59 from s1061123/fix/1822803
Bug 1822803: Add nil check for pod in case of Eventf()
2020-04-21 13:46:39 -04:00
Tomofumi Hayashi
e12749766d Add error message in case of unexpected situation
To troubleshooting #481, adds error message for further analysis.
2020-04-21 23:24:35 +09:00
Tomofumi Hayashi
0f5abb5bbd Add error message in case of unexpected situation
To troubleshooting #481, adds error message for further analysis.
2020-04-21 22:34:50 +09:00
Ted Yu
c1c3193633 Check Pod parameter against nil before calling Eventf
Signed-off-by: Ted Yu <yuzhihong@gmail.com>
2020-04-21 22:34:24 +09:00
OpenShift Merge Robot
f6cf49b5e5 Merge pull request #58 from openshift/owners-fields
Update OWNERS fields
2020-04-15 18:01:01 +02:00
dougbtv
8635270249 Update OWNERS fields 2020-04-14 13:31:46 -04:00
OpenShift Merge Robot
673cbc4062 Merge pull request #57 from dougbtv/multus-april-04-2020
Sync Multus upstream for OCP 4.5
2020-04-06 16:06:21 +02:00
dougbtv
90b6307473 Sync Multus master branch for downstream OCP 4.5 2020-04-03 11:14:16 -04:00
OpenShift Merge Robot
25d0d0ae3c Merge pull request #54 from dougbtv/test-readiness-on-del
Adds readinessindicatorfile check on CNI DEL
2020-04-03 00:39:41 +02:00
dougbtv
cb47afa633 Adds readinessindicatorfile check on CNI DEL 2020-03-27 08:20:25 -04:00
OpenShift Merge Robot
7219de04e6 Merge pull request #50 from dougbtv/test-no-config-invalidation
Removes configuration invalidation
2020-03-02 19:23:28 +01:00
Tomofumi Hayashi
50da6381cf Removes configuration invalidation 2020-03-02 10:10:11 -05:00
OpenShift Merge Robot
31cad25b97 Merge pull request #47 from dougbtv/readiness-indicator-poll-master
Exposes readinessindicatorfile and uses wait.PollImmediate
2020-02-19 20:50:50 +01:00
dougbtv
cd9980fcd9 Changes wait.ExponentialBackoff to wait.PollImmediate and exposes readinessindicatorfile via entrypoint parameter 2020-02-18 15:50:05 -05:00
OpenShift Merge Robot
a4bcbc043c Merge pull request #43 from dougbtv/master-fix-readiness
Bug 1794142: Fixes unnecessary wait when readinessindicatorfile is not present
2020-02-12 16:28:16 +01:00
Dan Williams
be46ee25b6 multus: print pod namespace/name in cmdAdd/cmdDel error messages
Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-02-11 10:58:14 -05:00
dougbtv
4ca7e30bf5 [bugfix] Fixes unnecessary wait when readinessindicatorfile is not present 2020-02-11 10:51:17 -05:00
OpenShift Merge Robot
1cb7d0f9c0 Merge pull request #40 from dougbtv/rhel8-set-commit
Adds COMMIT environment variable, also required along with VERSION
2019-11-11 22:57:13 +01:00
dougbtv
cae7b7887a Adds COMMIT environment variable, also required along with VERSION 2019-11-11 10:06:27 -05:00
OpenShift Merge Robot
6d83c7f552 Merge pull request #39 from dougbtv/rhel8-skip-git
sets a VERSION environment variable for RHEL8
2019-11-09 03:31:16 +01:00
dougbtv
ae42a3a107 sets a VERSION environment variable for RHEL8 to avoid using git in build process 2019-11-08 17:19:09 -05:00
OpenShift Merge Robot
aaa7a07909 Merge pull request #38 from dougbtv/dockerfile-install-git
Installs git via yum for ART builds
2019-11-08 19:29:19 +01:00
dougbtv
3f7aa2cf2f Installs git via YUM for OSBS builds 2019-11-08 12:01:04 -05:00
OpenShift Merge Robot
dc0b5c2a93 Merge pull request #36 from dougbtv/entrypoint-skip-binary-copy
Bug 1725832: Skips binary copy in entrypoint with new parameter
2019-11-07 18:44:16 +01:00
dougbtv
d1213c2dec Skips binary copy in entrypoint with new parameter 2019-11-07 11:02:08 -05:00
OpenShift Merge Robot
c884bb77af Merge pull request #34 from lsm5/multi-userspace
build for rhel7 and rhel8 userspaces
2019-11-07 15:34:25 +01:00
Lokesh Mandvekar
0f1e336b62 build for rhel7 and rhel8 userspaces
Dockerfile template from Doug Smith <dosmith@redhat.com>

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2019-11-06 14:14:22 -05:00
OpenShift Merge Robot
a7cfc2d781 Merge pull request #33 from dougbtv/ocp-43-features-resolved
Update to Multus v3.3
2019-10-30 00:51:00 +01:00
dougbtv
83987a0d73 Merge branch 'ocp-43-features' into ocp-43-features-resolved
Resolves conflicts for a large number of files (particularly vendored) as this is an overdue update to Multus v3.3

Primarily this resolution takes --theirs, but build in particular was manually massaged.
2019-10-29 15:11:35 -04:00
OpenShift Merge Robot
3ad0d98bce Merge pull request #31 from lsm5/openssl
RE: #1725832 - openssl support, dynamically link glibc
2019-10-28 18:15:55 +01:00
Lokesh Mandvekar
ca50f32648 RE: #1725832 - openssl support, dynamically link glibc
Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2019-10-28 08:13:36 -04:00
OpenShift Merge Robot
d3a1815632 Merge pull request #29 from dougbtv/updates-09-29-19-additional-bin-dir
Bug 1752453: Adds additional bin dir functionality
2019-09-19 18:25:16 +02:00
dougbtv
c758877033 [entrypoint] Adds --additional-bin-dir option to entrypoint to generate binDir configuration option 2019-09-19 10:28:20 -04:00
dougbtv
04311d3594 Appends binDir to CNI_PATH so that invoke.DelegateAdd/Del can find CNI plugins in alternate paths 2019-09-19 10:28:20 -04:00
OpenShift Merge Robot
30f0ae914f Merge pull request #25 from dougbtv/grpc-dependency-updates-CVE-2019-9511-4.2
Bug 1746645: Grpc dependency updates cve 2019 9511 4.2
2019-08-31 21:11:57 -07:00
dougbtv
10692b4538 [dockerfile] Use ENV GO111MODULE=off when building from openshift/origin-release:golang-1.10 2019-08-31 08:36:07 -04:00
OpenShift Merge Robot
abb8cd406f Merge pull request #23 from dougbtv/entrypoint-updates-aug29-2019
Bug 1732598: Adds one-shot CRIO restart
2019-08-30 19:25:08 -07:00
dougbtv
25c25916b5 [entrypoint] Adds script parameter to optionally rename source CNI config file to .old 2019-08-30 18:07:16 -04:00
dougbtv
707017a5cd [deps] Update grpc for CVE-2019-9511 https://nvd.nist.gov/vuln/detail/CVE-2019-9511 2019-08-30 17:17:42 -04:00
Tomofumi Hayashi
81636bc0c6 Bumpup CNI repo version 2019-08-30 15:21:27 -04:00
Tomofumi Hayashi
2f70e96401 Change .goreleaser.yml to support go module build 2019-08-30 15:21:09 -04:00
Tomofumi Hayashi
89b82c1264 Support GOPATH mode build/test for its transitional situation 2019-08-30 15:21:06 -04:00
Tomofumi Hayashi
b615316342 Migrate go modules from glide 2019-08-30 15:20:48 -04:00
dougbtv
c42108e40a [build] Adds CGO_ENABLED=0 and -tags no_openssl to build script 2019-08-30 15:20:22 -04:00
OpenShift Merge Robot
4b88a0293a Merge pull request #24 from openshift/jeremyeder-patch-1
Update OWNERS
2019-08-30 05:18:07 -07:00
Jeremy Eder
89a3ffb08e Update OWNERS
Removing myself.
2019-08-29 21:33:38 -04:00
dougbtv
1a2f8ccc9b [entrypoint] Adds one-shot CRIO restart, plus renames source CNI config to .old when not using the watch loop 2019-08-29 10:43:15 -04:00
OpenShift Merge Robot
d7b233d0f4 Merge pull request #21 from dougbtv/release-v3-changes-aug21
Bug 1732598: Entrypoint changes for watch loop & CRIO restart
2019-08-21 20:28:53 -07:00
dougbtv
240ec14e3c [entrypoint] Adds CRIO restart
Co-authored-by: s1061123 <tohayash@redhat.com>
2019-08-21 10:55:26 -04:00
dougbtv
f89e7e47bb [entrypoint] Adds --cleanup-config-on-exit to the entrypoint 2019-08-21 10:55:22 -04:00
Tomofumi Hayashi
e6a9fe8b11 Add daemonset for crio again
This change introduces multus-daemonset-crio again to support crio.
The change also introduce '--override-network-name' to use previous
master name in CNI json for multus CNI json.
2019-08-21 10:54:36 -04:00
OpenShift Merge Robot
b20f528de1 Merge pull request #17 from s1061123/fix/revert-nossl
Revert 'no_openssl' flag.
2019-07-17 16:41:15 +02:00
Tomofumi Hayashi
52ccadc1d4 Revert 'no_openssl' flag. 2019-07-17 21:59:24 +09:00
OpenShift Merge Robot
13932b1cdd Merge pull request #16 from s1061123/fix/nossl
Add '-tags no_openssl' option in build script
2019-07-17 14:56:27 +02:00
Tomofumi Hayashi
124ff4ee70 Add '-tags no_openssl' option in build script 2019-07-17 15:37:43 +09:00
OpenShift Merge Robot
c8dc6316c9 Merge pull request #15 from dougbtv/release-v3-may-30-resolved
Entrypoint options, SR-IOV functionality, CNI libs update
2019-06-03 19:09:05 +02:00
Zenghui Shi
9bf83c28d3 assign device id to pciBusID in delegated config
This allows host-device plugin to recognize
Device PCI address passed from Multus.
It is related to the change in host-device which
enables use of device pci address as a config option:
https://github.com/containernetworking/plugins/pull/300
2019-05-31 10:10:01 -04:00
Zenghui Shi
09fde13d79 assign device id to pciBusID in delegated config
This allows host-device plugin to recognize
Device PCI address passed from Multus.
It is related to the change in host-device which
enables use of device pci address as a config option:
https://github.com/containernetworking/plugins/pull/300
2019-05-31 09:53:38 -04:00
dougbtv
7e0637f6a9 Merge to resolve conflicts in vendored packages 2019-05-30 14:33:14 -04:00
Tomofumi Hayashi
a4dbe7b102 CNIVersion fix for conflist
This change introduces two fix about CNIVersion. One is to add CNIVersion into
delegated CNI conflist in case of DEL if CNIVersion is missing. ParseVersion() in libcni
checks cniVersion as mandatory field and sometimes user/daemonset miss it.
The latest flannel daemonset yaml (of flannel github) does not have CNIVersion, for example.
This change adds CNIVersion from multus config if cniVersion is missing (empty) in delegated
CNI config.

In addition, this fix also adds '--cni-version' in entrypoint.sh to add cniVersion in multus
config in case of '--multus-conf-file=auto'.
2019-05-30 14:24:38 -04:00
Tomofumi Hayashi
bb69ac5058 Continue to process cmdDel() in case of no network namespace
K8s with docker runtime, cmdDel() is invoked with empty network
namespace in case of restart node. Currently multus just returns
but CNI spec mention that we should invoke CNI plugin to cleanup.

This PR deletes "return nil" and proceeds to invoke CNI plugins
DEL. Fix #323.
2019-05-30 14:24:16 -04:00
Tomofumi Hayashi
0b153cd941 Update libcni to 0.7.0 (Spec ver 0.4.0) 2019-05-30 14:23:53 -04:00
Dan Williams
41160c62d6 entrypoint.sh: add timestamps to log messages; log autogenerated config file
Example:

2019-05-09T15:41:20-05:00 Generating Multus configuration file ...
2019-05-09T15:41:20-05:00 Attemping to find master plugin configuration, attempt 0
2019-05-09T15:41:20-05:00 Attemping to find master plugin configuration, attempt 1
2019-05-09T15:41:20-05:00 Attemping to find master plugin configuration, attempt 10
2019-05-09T15:41:20-05:00 Attemping to find master plugin configuration, attempt 15
2019-05-09T15:41:20-05:00 Attemping to find master plugin configuration, attempt 20
2019-05-09T15:41:20-05:00 Config file created @ /host/etc/cni/net.d/00-multus.conf
{ "name": "multus-cni-network", "type": "multus", "namespaceIsolation": true, "logLevel": "verbose", "kubeconfig": "/etc/kubernetes/cni/net.d/multus.d/multus.kubeconfig", "delegates": [ { "cniVersion": "0.3.1", "name": "openshift-sdn", "type": "openshift-sdn" } ] }
2019-05-09T15:41:20-05:00 Entering sleep... (success)
2019-05-30 14:23:34 -04:00
dougbtv
381db908a2 [entrypoint][docs] Adds --multus-autoconfig-dir and entrypoint documentation 2019-05-30 14:23:15 -04:00
Zenghui Shi
f5bb00a5b1 add test cases for device id assignment 2019-05-30 14:22:45 -04:00
Tomofumi Hayashi
f242f38a60 Add version into binary and fix .travis.yml to run forked repo.
This changes introduce goreleaser, which does cross-compile and
package, as well as add version into go code. This change also
changes .travis.yml to allow to other users' forked repo.
2019-05-30 14:19:31 -04:00
OpenShift Merge Robot
e13c535019 Merge pull request #13 from dougbtv/release-v3-may03-02
[bugfix] Skip clearing the network status annotation if the pod sandbox is not found
2019-05-07 14:30:52 +02:00
dougbtv
5b6d4320a0 [bugfix] Skipped clearing the network status annotation if the pod sandbox is not found 2019-05-03 10:13:39 -04:00
dougbtv
31d87f615d [bugfix] Skipped clearing the network status annotation if the pod sandbox is not found 2019-05-03 09:51:52 -04:00
OpenShift Merge Robot
dd8e7c3abd Merge pull request #12 from openshift/fix-cgo
add CGO_ENABLED=0 to build
2019-04-17 11:49:42 -07:00
Doug Smith
0390c1720c adds -tags no_openssl 2019-04-17 12:23:11 -04:00
Doug Smith
e691b1fa46 add CGO_ENABLED=0 to build 2019-04-17 11:44:49 -04:00
OpenShift Merge Robot
a805f63a04 Merge pull request #10 from dougbtv/release-v3-mar26
fix missing deviceID in NetConfList
2019-03-27 06:12:01 -07:00
Abdul Halim
b6c8dfbffb fix missing deviceID in NetConfList
This patch fixes the issue described in #289 where deviceID for
delegate plugin was not adding properly if the plugin conf inside
NetConfList.

Change-Id: I1d221f6b0e60a5b888b8e823611dfe12635e6897
Signed-off-by: Abdul Halim <abdul.halim@intel.com>
2019-03-26 15:04:54 -04:00
Abdul Halim
05ba224c08 fix missing deviceID in NetConfList
This patch fixes the issue described in #289 where deviceID for
delegate plugin was not adding properly if the plugin conf inside
NetConfList.

Change-Id: I1d221f6b0e60a5b888b8e823611dfe12635e6897
Signed-off-by: Abdul Halim <abdul.halim@intel.com>
2019-03-26 14:54:36 -04:00
OpenShift Merge Robot
93fe981913 Merge pull request #9 from zshi-redhat/kubelet-client
add kubelet client for Pod resource info
2019-03-26 06:39:19 -07:00
Abdul Halim
d2a02a6e5d add kubelet client for Pod resource info
This change introduces kubelet client to get allocated device
information of a Pod from newly added Kubelet grpc service.
For more information please see:
[kubernetes/kubernetes#70508](https://github.com/kubernetes/kubernetes/pull/70508)

Change-Id: I11e58ccdd52662601f445fa24c7d55c225441efc
Signed-off-by: Abdul Halim <abdul.halim@intel.com>
(cherry picked from commit d3c92b4aa2)
2019-03-24 12:33:04 +08:00
Abdul Halim
877f00be30 update vendor dependencies for kubelet client
adding new imported package dependencies in vendor which is required
for Kubelet Pod Resource api client.

Change-Id: If6c74598e12af5f8659df69371e72dd064823f49
(cherry picked from commit 8ee7eb335e)
2019-03-24 12:32:30 +08:00
Abdul Halim
942ad4749e add kubelet client for Pod resource info
This change introduces kubelet client to get allocated device
information of a Pod from newly added Kubelet grpc service.
For more information please see:
[kubernetes/kubernetes#70508](https://github.com/kubernetes/kubernetes/pull/70508)

Change-Id: I11e58ccdd52662601f445fa24c7d55c225441efc
Signed-off-by: Abdul Halim <abdul.halim@intel.com>
(cherry picked from commit d3c92b4aa2)
2019-03-23 23:26:40 +09:00
Abdul Halim
310c8e914c update vendor dependencies for kubelet client
adding new imported package dependencies in vendor which is required
for Kubelet Pod Resource api client.

Change-Id: If6c74598e12af5f8659df69371e72dd064823f49
(cherry picked from commit 8ee7eb335e)
2019-03-23 23:26:40 +09:00
OpenShift Merge Robot
02913fb96f Merge pull request #8 from dougbtv/release-v3-mar-20
Release v3 updates - includes caching PR
2019-03-20 19:40:31 -07:00
maximshd
aff54bb1f7 Properly initialize kubeClient in SetNetworkStatus method (#283)
* Properly initialize kubeClient in SetNetworkStatus method

* Fix typo

* Update error message

* Extend logging for setNetworkStatus function
2019-03-20 16:20:46 -04:00
Tomofumi Hayashi
57f1d7afc7 Caches all pod delegates json for pods deletion without k8s info
This fixes #243 with following changes:
 + Optimize to fetch Pod from k8s client
 + Change to use cache always in DEL.
 + If failed to fetch the pod info from k8s clinet in deletion,
  use cached delegates as emergency bailout
 + Add test cases for cache

fixed conflicts in multus/multus.go & multus/multus_test.go
2019-03-20 16:20:10 -04:00
knight
363b7fbc06 refactor k8sclient: rename some val 2019-03-20 16:11:38 -04:00
OpenShift Merge Robot
7094c9675f Merge pull request #7 from dougbtv/release-v3-mar7
Release v3 updates
2019-03-11 08:33:13 -07:00
Doug Smith
4aecbd2133 [entrypoint] Add options to specify logfile & loglevel in entrypoint (#280) 2019-03-07 11:08:20 -05:00
Tomofumi Hayashi
1e4fe4f837 Add 'verbose' option to logging minimum information (#275)
This change address #274 to add 'verbose option which outputs
minimum information (for usual runs with a bit information than
'error').
2019-03-07 11:06:49 -05:00
Peng Liu
11fe266139 Generate Multus config file regardless 2019-03-07 11:06:18 -05:00
Tomofumi Hayashi
68c5d0dd70 Change ClusterNetwork/DefaultNetwork namespace to MultusNamespace
Fix #261.
2019-03-07 11:06:13 -05:00
OpenShift Merge Robot
f47677d637 Merge pull request #5 from openshift/release-v3
Release v3
2019-02-28 09:23:05 -08:00
OpenShift Merge Robot
8b806df35c Merge pull request #6 from dougbtv/owners-file
Add OWNERS file
2019-02-28 00:39:35 +01:00
dougbtv
faae208bd7 Add OWNERS file 2019-02-27 14:02:25 -05:00
dougbtv
61f9e08863 Allows cmdDel to finish if netns doesn't exist, omits deferred netns.Close() in such a case 2019-02-22 09:07:35 -05:00
Doug Smith
3a489263ea Changes configuration for kube api to use gRPC 2019-02-22 09:07:16 -05:00
Dan Williams
3a9cf4a9a2 multus: simplify teardown on add error and clarify error message
Signed-off-by: Dan Williams <dcbw@redhat.com>
2019-02-22 09:07:03 -05:00
Doug Smith
b622593956 Merge pull request #4 from openshift/noop
[noop]
2019-02-19 09:57:08 -05:00
dougbtv
2ecb1a01a5 [noop] 2019-02-19 09:55:52 -05:00
Tomofumi Hayashi
73e10fb999 Add multusNamespace/systemNamespaces config
This change provides new configuration parameters, multusNamespace
and systemNamespaces for flexible namespace management.
The change addresses issue #252 and issue #253.
2019-02-19 09:53:17 -05:00
dougbtv
972a7d18c6 [entrypoint] Updates entrypoint for atomic move of binary (for cleaner upgrade) 2019-01-22 10:46:24 -05:00
dougbtv
f276c023f8 [entrypoint] Adds option for namespaceIsolation in entrypoint 2019-01-15 13:48:17 -05:00
dougbtv
7174b9ac47 [dockerfile] Updates Dockerfile for OpenShift-style build 2019-01-07 11:39:34 -05:00
Mathieu Rohon
a51309c532 [dependency] Add portmap capability support (Other changes were dependent on changes made in this commit)
Signed-off-by: Mathieu Rohon <mathieu.rohon@orange.com>
2018-12-20 13:07:32 -05:00
dougbtv
8d2445bb4c [feature] Adds a namespace isolation security feature 2018-12-20 10:50:15 -05:00
Michal Rostecki
0a59253609 k8sclient: Add missing error check
Before this change, error returned by `libcni.ConfFiles` was
silently ignored.

Signed-off-by: Michal Rostecki <mrostecki@suse.de>
2018-12-19 16:04:54 -05:00
dougbtv
570a81d641 [dockerfile] Adds Dockerfile.rhel for OpenShift build 2018-12-19 16:04:54 -05:00
Tomofumi Hayashi
4ad64094b3 Fix go vet issue. 2018-12-19 16:04:54 -05:00
Tomofumi Hayashi
3e2c4b1528 Change the namespace to 'kube-system' 2018-12-19 16:04:54 -05:00
Tomofumi Hayashi
2e070bd4a5 clusterNetwork/defaultNetworks and namespace spec fixed
This fix is to add declaration about clusterNetwork/defaultNetwork
net-attach-def is in 'default' namesspace. In addition, this code
changes to skip defaultNetwork in case of 'kube-system' namespace
as well (#202).
2018-12-19 16:04:54 -05:00
Tomofumi Hayashi
dd8492c44c Fix Docker build issue around golang. 2018-12-19 16:04:53 -05:00
Michal Rostecki
09503c3dd3 Add .gitignore file
Prevent tracking of binary outputs, GOPATH and test outputs.

Signed-off-by: Michal Rostecki <mrostecki@suse.de>
2018-12-19 16:04:53 -05:00
dougbtv
1ee63551fa [bugfix] Delete all delegates instead of breaking out during deletion loop 2018-12-19 16:04:53 -05:00
Peng Liu
0e6cc2292a Change pod annotation name to 'v1.multus-cni.io/default-network' 2018-12-19 16:04:53 -05:00
Peng Liu
8c021bd46f Add more debug message 2018-12-19 16:04:53 -05:00
Peng Liu
50dfdb7036 Specify Pod default network in Annotations
Signed-off-by: Peng Liu <pliu@redhat.com>
2018-12-19 16:04:53 -05:00
Tomofumi Hayashi
5b5e5e5386 Add description how to use CRD in non-default namespaces 2018-12-19 16:04:53 -05:00
dougbtv
510c803d2c [docs] Updates to fix typos and extend information about CNI configurations generally per review 2018-12-19 16:04:53 -05:00
dougbtv
99dbdd9f57 [docs] Adds additional quickstart.md specific guide, some updates to usage guide 2018-12-19 16:04:53 -05:00
Tomofumi Hayashi
61801e07d4 Add comments in case of daemonset. 2018-12-19 16:04:53 -05:00
Tomofumi Hayashi
52f6892ba1 Add more paragraph. 2018-12-19 16:04:53 -05:00
Tomofumi Hayashi
62b9dc51c1 Add "NOTE:" and change "NOTE" from "Note" 2018-12-19 16:04:53 -05:00
Tomofumi Hayashi
80009b398c s/folloiwng/following/ 2018-12-19 16:04:53 -05:00
Tomofumi Hayashi
67380dbf9e Add 'skip in case of daemonset' at "SA, ClusterRole..." 2018-12-19 16:04:53 -05:00
Tomofumi Hayashi
7d551d100d Indent the paragraph at "install multus" 2018-12-19 16:04:53 -05:00
Tomofumi Hayashi
9d20b37904 Update README.md and split into several child documents
Fix #154 and #139. Thank you @dougbtv for reviewing the docs!
2018-12-19 16:04:49 -05:00
Alona Kaplan
c4feebc6c9 [divergence] Support IPRequest to specify IP address for interface 2018-12-19 16:03:08 -05:00
Tomofumi Hayashi
787586aca8 Fix the log message. 2018-12-19 16:02:36 -05:00
dougbtv
8c2798a32a [travis] Updates Travis to build ':snapshot' tagged image on each merge into master 2018-12-19 16:02:36 -05:00
Przemyslaw Lal
d87b7beeca fix indentation
Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Przemyslaw Lal
ecb3260866 webhook documentation updates
Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Przemyslaw Lal
20ae7c25e5 add more webhook tests
Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Przemyslaw Lal
e748969163 improve error handling in webhook
Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Przemyslaw Lal
63ff3f1992 run webhook as a deployment
Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Przemyslaw Lal
862eb5e8e2 Add proxy env variables to docker build script
Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Przemyslaw Lal
abcee9ecda Add documentation for validating admission webhook
Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Przemyslaw Lal
8b18175fc9 Add deployment files for validating admission webhook
* Add script for automated certtificates and secret generation
* Add pod, service and webhook configuration specification files

Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Przemyslaw Lal
5892b36b7a Add validating admission webhook
* Add validating admission webhook HTTP server application
* Handle incoming AdmissionReview requests and validate their correctness, handle errors if any
* Validate Network Attachment Definition objects
* Send AdmissionReview response with allowed/denied decision and its reason
* In case of any other errors (malformed HTTP request, empty body, etc.) send proper HTTP error code
* Use TLS encryption
* Add some basic unit tests for Network Attachment Definition objects validation
* Build Docker image with webhook application

Signed-off-by: Przemyslaw Lal <przemyslawx.lal@intel.com>
2018-12-19 16:02:36 -05:00
Alona Kaplan
fd5ac6801c Setting the MAC in CNI_ARGS shouldn't override the already existing CNI_ARGS 2018-12-19 16:02:36 -05:00
Dan Williams
d773dcfaad CRD: interfaceRequest -> interface (v1 spec conformance)
Change the Network Attachment Selection Annotation long-form
interface name request JSON key from 'interfaceRequest' to
'interface' to conform with the V1 NPWG spec.
2018-12-19 16:02:36 -05:00
Tomofumi Hayashi
bcc6e21587 Fix rebase conflicts. 2018-12-19 16:02:35 -05:00
Tomofumi Hayashi
818c8d7c99 [divergence] Fix multus_test. 2018-12-19 16:02:30 -05:00
Tomofumi Hayashi
dd9fedee2a Remove unnecessary else clause 2018-12-19 15:55:45 -05:00
Tomofumi Hayashi
87b7dbc378 Incorporate @dcbw's comment. 2018-12-19 15:55:45 -05:00
Tomofumi Hayashi
481c21b1e1 Add mac/interfaceRequest section in README.md 2018-12-19 15:55:45 -05:00
Tomofumi Hayashi
91a0e74a0d Change json field name to align with NPWG spec v1. 2018-12-19 15:55:45 -05:00
Tomofumi Hayashi
ac3731380c Fix the way to set MAC. 2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
0a0c8d042a Add debug messasge for MAC. 2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
dae85d7781 Support MacRequest to specify MAC address for interface 2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
6b573e8c4b Add issue template for {bug,enhance,support} 2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
606efe443f Fix example files (#171 and #183) 2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
3458934e12 Fix typo in README.md 2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
6b0266148b Add clusterNetwork/defaultNetwork into multus
To support CRD/file/directory, add clusterNetwork/defaultNetwork
in multus.conf file.
2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
55792b94d5 Add unit tests for clusterNetwork/defaultNetworks 2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
061f4f913e Add clusterNetwork/defaultNetwork into multus
To support CRD/file/directory, add clusterNetwork/defaultNetwork
in multus.conf file.
2018-12-19 15:55:44 -05:00
Tomofumi Hayashi
bb89e538eb [divergence] Add clusterNetwork/defaultNetwork into multus
To support CRD/file/directory, add clusterNetwork/defaultNetwork
in multus.conf file.
2018-12-19 15:55:37 -05:00
Michael Cambria
7f389e7e7c Make conflistDel() behave like conflistAdd()
conflistAdd() finds binaries differently than conflistDel().
Make the two call find binaries the same way.

Fixes #179

Signed-off-by: Michael Cambria <mcambria@redhat.com>
2018-12-19 15:51:36 -05:00
dougbtv
da20d1d875 [rbac] Tightens down RBAC for clusterrole 2018-12-19 15:51:36 -05:00
Michael Cambria
fa2e35c4d4 Fix logFile to match configuration json
The Logging Options section of README describes how to specify a file
to log to.  There is a typo, LogFile should be logFile to match the
json.

Fixes #177

Signed-off-by: Michael Cambria <mcambria@redhat.com>
2018-12-19 15:51:36 -05:00
dougbtv
35e4ee50a4 Fixes flannel daemonset stuck in pod queue in Kubernetes 1.12.x per #170 2018-12-19 15:51:36 -05:00
dougbtv
ae626b4aa5 [bugfix][divergence] Diverges from master in requiring change to logging.Debugf value changed from string 2018-11-01 22:41:56 +09:00
Shahar Klein
25fd3ca8bc Seems like the ENTRYPOINT value must be quoted
Signed-off-by: Shahar Klein <shaharklein@gmail.com>
2018-11-01 22:41:56 +09:00
Tomofumi Hayashi
5380edbe6e Fix TravisCI for the failure of 'go get golint' 2018-11-01 22:41:56 +09:00
Tomofumi Hayashi
980edbdf6c TravisCI yaml parameterized
This change fixes #143, to make some specific TravisCI args parameter.
2018-11-01 22:41:56 +09:00
Doug Smith
ae9a1a5c10 Default network readiness [NOTE: Conflict due to omitted commit during cherry pick for stable] 2018-11-01 22:41:56 +09:00
Kuralamudhan Ramakrishnan
959ffaac3b Update README.md 2018-11-01 22:41:56 +09:00
Kuralamudhan Ramakrishnan
346bed2d9d Update README.md 2018-11-01 22:41:56 +09:00
Abdul Halim
66361bfbb2 fixed some typos in comments
Change-Id: Ieb650479b6b0fef1a4ecaeb2c3c1a7c15fff43d5
2018-11-01 22:41:56 +09:00
Abdul Halim
1ba90bad00 added checkpoint tests file
Change-Id: I53551660ffd017fe170de58abdf7a96e29178000
2018-11-01 22:41:56 +09:00
Abdul Halim
484385456b refactoring checkpoint.go code to be testable
this changes will allow mocking checkpoint instance for unit tests

Change-Id: I72fb25d15d5c9f28577a0fcbfcd385df523a5e57
2018-11-01 22:41:56 +09:00
Abdul Halim
5988b7a82b only create resourceMap on demand
making resourceMap a singleton object and only initialize it once
if one or more CRDs have a resourceName annotation in them.

Added copyright header for checkpoint/checkpoint.go.
Replaced fmt.Errorf with logging.

Change-Id: I54628d69324833e70a75dcf6533e6642dedde9b5
2018-11-01 22:41:56 +09:00
Abdul Halim
2cfaa19dda updated examples/README.md
Change-Id: I650fec86659b3690e1dc4b15bf84b6574cb0baba
2018-11-01 22:41:56 +09:00
Abdul Halim
539e92b8b0 parse kubelet checkpoint file for pod devices
Enabling kubelete checkpoint file  parsing to get Pod device info
so that these device information can be passed into CNI plugins
that need specific device information to work on.

Change-Id: I6630f56adc0a8307f575fc09ce9090c1ffca0337
2018-11-01 22:41:56 +09:00
Louis Woods
93237e6161 Improve grep in entrypoint.sh to only find .conf and .conflist files 2018-11-01 22:41:56 +09:00
Louis Woods
bae3adf158 Add the option to auto generate 00-multus.conf
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).
2018-11-01 22:41:56 +09:00
Tomofumi Hayashi
e9aa18319d Add bracket [] in Dockerfile's entrypoint to parse argument correctly. 2018-11-01 22:41:56 +09:00
4522 changed files with 267952 additions and 840396 deletions

View File

@@ -1,24 +0,0 @@
on: [push, pull_request]
name: Build
jobs:
build:
strategy:
matrix:
go-version: [1.20.x, 1.21.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

View File

@@ -1,41 +0,0 @@
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 }}"

25
.github/workflows/go-build-arm64.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Go-build-ppc64le
on: [push, pull_request]
jobs:
build:
name: Build and test
runs-on: ubuntu-latest
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 != '[]' )
env:
GO111MODULE: on
TARGET: arm64
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build
run: GOARCH="${TARGET}" ./build

View File

@@ -0,0 +1,48 @@
name: Go-build-and-test-amd64
on: [push, pull_request]
jobs:
build:
name: Build and test
runs-on: ubuntu-latest
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 != '[]' )
env:
GO111MODULE: on
TARGET: amd64
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: install goberalls
run: go get github.com/mattn/goveralls
- name: install golint
run: go get -u golang.org/x/lint/golint
- name: golint
run: golint ./... | grep -v vendor | grep -v ALL_CAPS | xargs -r false
- name: gofmt
run: go fmt ./...
- name: go vet
run: go vet ./...
- name: Build
run: GOARCH="${TARGET}" ./build
- name: Go test
run: sudo ./test.sh
- name: goveralls
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: coverage.out

View File

@@ -1,82 +0,0 @@
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@v5
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@v5
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@v5
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
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 .

View File

@@ -1,116 +0,0 @@
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

View File

@@ -1,138 +0,0 @@
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

View File

@@ -1,107 +1,40 @@
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 }}"
build:
name: Build and test
runs-on: ubuntu-latest
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)
uses: actions/checkout@v2
- name: Setup registry
run: docker run -d --restart=always -p "5000:5000" --name "kind-registry" registry:2
- 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
run: docker build -t localhost:5000/multus:e2e .
- name: Push to local registry
run: docker push localhost:5000/multus:e2e
- 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
run: ./setup_cluster.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: Export kind logs
if: always()
run: |
mkdir -p /tmp/kind/logs
kind export logs --loglevel=debug /tmp/kind/logs
- 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
docker kill kind-registry
docker rm kind-registry

29
.github/workflows/release-binary.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Release-binaries
on:
push:
tags:
- v*
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Unshallow
run: git fetch --prune --unshallow
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.13.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,26 +0,0 @@
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.19.x
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,15 +0,0 @@
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

View File

@@ -1,48 +0,0 @@
on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.20.x, 1.21.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

3
.gitignore vendored
View File

@@ -1,8 +1,5 @@
# Binary output dir
bin/
e2e/bin/
e2e/yamls/
e2e/repos/
# GOPATH created by the build script
gopath/

View File

@@ -6,51 +6,17 @@ 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 }}
-
env:
- CGO_ENABLED=0
main: ./multus/
goos:
- linux
goarch:
- 386
- amd64
- arm
- arm64
archives:
- wrap_in_directory: true
checksum:

116
.travis.yml Normal file
View File

@@ -0,0 +1,116 @@
os: linux
language: go
# see https://docs.travis-ci.com/user/reference/overview/#Virtualization-environments
# for the detail
# sudo: requried
dist: bionic
services:
- docker
go:
- 1.13.x
env:
global:
- GO111MODULE=on
- REGISTRY_USER=${REGISTRY_USER:-nfvpe}
- REGISTRY_PASS=${REGISTRY_PASS}
- REPOSITORY_NAME=${REPOSITORY_NAME}
- REPOSITORY_USER=${REPOSITORY_USER}
- DOCKER_CLI_EXPERIMENTAL="enabled"
- secure: "${REGISTRY_SECURE}"
jobs:
- TARGET=amd64
- TARGET=ppc64le
before_install:
- if [ "${REPOSITORY_NAME}" = "" ]; then export REPOSITORY_NAME=multus; fi
- sudo apt-get update -qq
- go get github.com/mattn/goveralls
install:
- go get -u golang.org/x/lint/golint
before_script:
# Make gopath... to run golint/go fmt/go vet
# Suppress golint for fixing lint later.
- golint ./... | grep -v vendor | grep -v ALL_CAPS | xargs -r false
- go fmt ./...
- go vet ./...
# - gocyclo -over 15 ./multus
script:
- GOARCH="${TARGET}" ./build
- |
if [ "${TARGET}" == "amd64" ]; then
sudo env PATH=${PATH} ./test.sh
goveralls -coverprofile=coverage.out -service=travis-ci
docker build -t ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 .
docker build -t ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le -f Dockerfile.ppc64le .
docker build -t ${REPOSITORY_USER}/${REPOSITORY_NAME}-origin:latest -f Dockerfile.openshift .
fi
deploy:
# Release on versioned tag (e.g. v1.0)
- provider: script
#cleanup: false
script: curl -sL https://git.io/goreleaser
on:
tags: true
all_branches: true
condition: "$TARGET = amd64 && $TRAVIS_TAG =~ ^v[0-9].*$ && ! -z $GITHUB_TOKEN && $TRAVIS_OS_NAME = linux"
# Push images to Dockerhub on tag
- provider: script
cleanup: false
script: >
bash -c '
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-amd64;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:$TRAVIS_TAG;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-ppc64le;
docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASS";
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-amd64;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-ppc64le;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:$TRAVIS_TAG;
export DOCKER_CLI_EXPERIMENTAL="enabled";
docker manifest create ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 --arch amd64;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le --arch ppc64le;
docker manifest push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest;
docker manifest create ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-ppc64le;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-amd64 --arch amd64;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-ppc64le --arch ppc64le;
docker manifest push ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable;
echo done'
on:
tags: true
all_branches: true
condition: "$TRAVIS_TAG =~ ^v[0-9].*$ && -n $REGISTRY_USER && -n $REGISTRY_PASS && -n $REPOSITORY_NAME && -n $REPOSITORY_USER"
# Push images to Dockerhub on merge to master
- provider: script
on:
branch: master
condition: "-n $REGISTRY_USER && -n $REGISTRY_PASS && -n $REPOSITORY_NAME && -n $REPOSITORY_USER"
script: >
bash -c '
docker tag ${REPOSITORY_USER}/:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-amd64;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-ppc64le;
docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASS";
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-amd64;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-ppc64le;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le;
docker manifest create ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-ppc64le;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-amd64 --arch amd64;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-ppc64le --arch ppc64le;
docker manifest push ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot;
docker manifest create ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 --arch amd64;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le --arch ppc64le;
docker manifest push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest;
echo done'

View File

@@ -1,130 +0,0 @@
# 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.

20
Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# This Dockerfile is used to build the image available on DockerHub
FROM centos:centos7 as build
# Add everything
ADD . /usr/src/multus-cni
ENV INSTALL_PKGS "git golang"
RUN rpm --import https://mirror.go-repo.io/centos/RPM-GPG-KEY-GO-REPO && \
curl -s https://mirror.go-repo.io/centos/go-repo.repo | tee /etc/yum.repos.d/go-repo.repo && \
yum install -y $INSTALL_PKGS && \
rpm -V $INSTALL_PKGS && \
cd /usr/src/multus-cni && \
./build
FROM centos:centos7
COPY --from=build /usr/src/multus-cni /usr/src/multus-cni
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

20
Dockerfile.arm64 Normal file
View File

@@ -0,0 +1,20 @@
# This Dockerfile is used to build the image available on DockerHub
FROM golang:1.13.4 as build
# Add everything
ADD . /usr/src/multus-cni
ENV GOARCH "arm64"
ENV GOOS "linux"
RUN cd /usr/src/multus-cni && \
./build
# build arm64 container
FROM arm64v8/centos:7
COPY --from=build /usr/src/multus-cni /usr/src/multus-cni
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

41
Dockerfile.openshift Normal file
View File

@@ -0,0 +1,41 @@
# This dockerfile is specific to building Multus for OpenShift
FROM openshift/origin-release:rhel-8-golang-1.12 as rhel8
ADD . /usr/src/multus-cni
WORKDIR /usr/src/multus-cni
ENV CGO_ENABLED=1
ENV GO111MODULE=off
ENV VERSION=rhel8 COMMIT=unset
RUN ./build && \
cd /usr/src/multus-cni/bin
WORKDIR /
FROM openshift/origin-release:rhel-7-golang-1.12 as rhel7
ADD . /usr/src/multus-cni
WORKDIR /usr/src/multus-cni
ENV CGO_ENABLED=1
ENV GO111MODULE=off
RUN ./build && \
cd /usr/src/multus-cni/bin
WORKDIR /usr/src/multus-cni
ENV GO111MODULE=off
RUN ./build && \
cd /usr/src/multus-cni/bin
WORKDIR /
FROM openshift/origin-base
RUN mkdir -p /usr/src/multus-cni/images && \
mkdir -p /usr/src/multus-cni/bin && \
mkdir -p /usr/src/multus-cni/rhel7/bin && \
mkdir -p /usr/src/multus-cni/rhel8/bin
COPY --from=rhel7 /usr/src/multus-cni/bin/multus /usr/src/multus-cni/rhel7/bin
COPY --from=rhel7 /usr/src/multus-cni/bin/multus /usr/src/multus-cni/bin
COPY --from=rhel8 /usr/src/multus-cni/bin/multus /usr/src/multus-cni/rhel8/bin
ADD ./images/entrypoint.sh /
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 ["/entrypoint.sh"]

25
Dockerfile.ppc64le Normal file
View File

@@ -0,0 +1,25 @@
# This Dockerfile is used to build the image available on DockerHub
FROM centos:centos7 as build
# Add everything
ADD . /usr/src/multus-cni
ENV GOARCH "ppc64le"
ENV GOOS "linux"
ENV INSTALL_PKGS "git golang"
RUN rpm --import https://mirror.go-repo.io/centos/RPM-GPG-KEY-GO-REPO && \
curl -s https://mirror.go-repo.io/centos/go-repo.repo | tee /etc/yum.repos.d/go-repo.repo && \
yum install -y $INSTALL_PKGS && \
rpm -V $INSTALL_PKGS && \
cd /usr/src/multus-cni && \
./build
# build ppc container
FROM ppc64le/centos:latest
COPY --from=build /usr/src/multus-cni /usr/src/multus-cni
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

2
NOTICE
View File

@@ -1,2 +0,0 @@
Copyright 2016 Intel Corporation
Copyright 2021 Multus Authors

16
OWNERS Normal file
View File

@@ -0,0 +1,16 @@
reviewers:
- dougbtv
- dcbw
- squeed
- zshi
- fepan
- s1061123
approvers:
- dougbtv
- dcbw
- squeed
- zshi
- fepan
- s1061123
component: "Networking"
subcomponent: "multus"

View File

@@ -1,8 +1,8 @@
# Multus-CNI
![multus-cni Logo](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/images/Multus.png)
![multus-cni Logo](https://github.com/intel/multus-cni/blob/master/doc/images/Multus.png)
[![Build](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/build.yml/badge.svg)](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/build.yml)[![Test](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/test.yml/badge.svg)](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/test.yml)[![Go Report Card](https://goreportcard.com/badge/github.com/k8snetworkplumbingwg/multus-cni)](https://goreportcard.com/report/github.com/k8snetworkplumbingwg/multus-cni)[![Coverage Status](https://coveralls.io/repos/github/k8snetworkplumbingwg/multus-cni/badge.svg)](https://coveralls.io/github/k8snetworkplumbingwg/multus-cni)
[![Travis CI](https://travis-ci.org/intel/multus-cni.svg?branch=master)](https://travis-ci.org/intel/multus-cni/builds)[![Go Report Card](https://goreportcard.com/badge/github.com/intel/multus-cni)](https://goreportcard.com/report/github.com/intel/multus-cni)[![Coverage Status](https://coveralls.io/repos/github/intel/multus-cni/badge.svg)](https://coveralls.io/github/intel/multus-cni)
Multus CNI enables attaching multiple network interfaces to pods in Kubernetes.
@@ -18,49 +18,34 @@ Multus is one of the projects in the [Baremetal Container Experience kit](https:
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).
![multus-pod-image](docs/images/multus-pod-image.svg)
![multus-pod-image](doc/images/multus-pod-image.svg)
## Quickstart Installation Guide
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).
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 creatd 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 plugins, refer to our [quick-start guide](doc/quickstart.md).
To use latest features try command below which applies a daemonset and installs thick Multus using `kubectl`:
Clone this GitHub repository, we'll apply a daemonset which installs Multus using to `kubectl` from this repo. From the root directory of the clone, apply the daemonset YAML file:
```
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset-thick.yml
$ cat ./images/multus-daemonset.yml | kubectl apply -f -
```
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)
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](doc/quickstart.md)
## Thin Plugin v.s Thick Plugin
## Additional installation Options
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 apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml
```
## Additional Installation Options
In addition to the [quick-start guide](docs/quickstart.md), you may:
- 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)
- Install via daemonset using the quick-start guide, above.
- Download binaries from [release page](https://github.com/intel/multus-cni/releases)
- By Docker image from [Docker Hub](https://hub.docker.com/r/nfvpe/multus/tags/)
- Or, roll-you-own and build from source
- See [Development](doc/development.md)
## Comprehensive Documentation
- [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)
- [How to use](doc/how-to-use.md)
- [Configuration](doc/configuration.md)
- [Development](doc/development.md)
## Contact Us
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/).
To be invited, use [this slack invite link](https://join.slack.com/t/npwg-team/shared_invite/zt-1u2vmsn2b-tKdOokdPY73zn9B32JoAOg).
For any questions about Multus CNI, feel free to ask a question in #general in the [Intel-Corp Slack](https://intel-corp.herokuapp.com/), or open up a GitHub issue.

50
build Executable file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -e
DEST_DIR="bin"
if [ ! -d ${DEST_DIR} ]; then
mkdir ${DEST_DIR}
fi
# Add version/commit/date into binary
# In case of TravisCI, need to check error code of 'git describe'.
if [ -z "$VERSION" ]; then
set +e
git describe --tags --abbrev=0 > /dev/null 2>&1
if [ "$?" != "0" ]; then
VERSION="master"
else
VERSION=$(git describe --tags --abbrev=0)
fi
set -e
fi
DATE=$(date --iso-8601=seconds)
COMMIT=${COMMIT:-$(git rev-parse --verify HEAD)}
LDFLAGS="-X main.version=${VERSION:-master} -X main.commit=${COMMIT} -X main.date=${DATE}"
export CGO_ENABLED=1
# this if... will be removed when gomodules goes default
if [ "$GO111MODULE" == "off" ]; then
echo "Building plugin without go module"
echo "Warning: this will be deprecated in near future so please use go modules!"
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
go install -ldflags "${LDFLAGS}" "$@" ${REPO_PATH}/multus
else
# build with go modules
export GO111MODULE=on
echo "Building plugins"
go build -o ${DEST_DIR}/multus -ldflags "${LDFLAGS}" "$@" ./multus
fi

View File

@@ -1,5 +1,4 @@
// Copyright (c) 2018 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.
@@ -12,15 +11,16 @@
// 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 checkpoint
import (
"encoding/json"
"os"
"io/ioutil"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types"
"github.com/intel/multus-cni/logging"
"github.com/intel/multus-cni/types"
v1 "k8s.io/api/core/v1"
)
@@ -33,7 +33,7 @@ type PodDevicesEntry struct {
PodUID string
ContainerName string
ResourceName string
DeviceIDs map[int64][]string
DeviceIDs []string
AllocResp []byte
}
@@ -72,7 +72,7 @@ func getCheckpoint(filePath string) (types.ResourceClient, error) {
func (cp *checkpoint) getPodEntries() error {
cpd := &checkpointFileData{}
rawBytes, err := os.ReadFile(cp.fileName)
rawBytes, err := ioutil.ReadFile(cp.fileName)
if err != nil {
return logging.Errorf("getPodEntries: error reading file %s\n%v\n", checkPointfile, err)
}
@@ -86,7 +86,7 @@ func (cp *checkpoint) getPodEntries() error {
return nil
}
// GetPodResourceMap returns an instance of a map of ResourceInfo
// GetComputeDeviceMap returns an instance of a map of ResourceInfo
func (cp *checkpoint) GetPodResourceMap(pod *v1.Pod) (map[string]*types.ResourceInfo, error) {
podID := string(pod.UID)
resourceMap := make(map[string]*types.ResourceInfo)
@@ -97,14 +97,12 @@ func (cp *checkpoint) GetPodResourceMap(pod *v1.Pod) (map[string]*types.Resource
for _, pod := range cp.podEntires {
if pod.PodUID == podID {
entry, ok := resourceMap[pod.ResourceName]
if !ok {
// new entry
entry = &types.ResourceInfo{}
resourceMap[pod.ResourceName] = entry
}
for _, v := range pod.DeviceIDs {
if ok {
// already exists; append to it
entry.DeviceIDs = append(entry.DeviceIDs, v...)
entry.DeviceIDs = append(entry.DeviceIDs, pod.DeviceIDs...)
} else {
// new entry
resourceMap[pod.ResourceName] = &types.ResourceInfo{DeviceIDs: pod.DeviceIDs}
}
}
}

View File

@@ -1,32 +1,16 @@
// Copyright (c) 2018 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.
package checkpoint
// disable dot-imports only for testing
//revive:disable:dot-imports
import (
"fmt"
"os"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"io/ioutil"
"testing"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types"
"github.com/intel/multus-cni/types"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sTypes "k8s.io/apimachinery/pkg/types"
@@ -41,7 +25,7 @@ type fakeCheckpoint struct {
}
func (fc *fakeCheckpoint) WriteToFile(inBytes []byte) error {
return os.WriteFile(fc.fileName, inBytes, 0600)
return ioutil.WriteFile(fc.fileName, inBytes, 0600)
}
func (fc *fakeCheckpoint) DeleteFile() error {
@@ -61,11 +45,10 @@ var _ = BeforeSuite(func() {
"PodUID": "970a395d-bb3b-11e8-89df-408d5c537d23",
"ContainerName": "appcntr1",
"ResourceName": "intel.com/sriov_net_A",
"DeviceIDs": {"-1": [
"0000:03:02.3",
"0000:03:02.0"
]
},
"DeviceIDs": [
"0000:03:02.3",
"0000:03:02.0"
],
"AllocResp": "CikKC3NyaW92X25ldF9BEhogMDAwMDowMzowMi4zIDAwMDA6MDM6MDIuMA=="
}
],
@@ -160,10 +143,10 @@ var _ = Describe("Kubelet checkpoint data read operations", func() {
"PodUID": "970a395d-bb3b-11e8-89df-408d5c537d23",
"ContainerName": "appcntr1",
"ResourceName": "intel.com/sriov_net_A",
"DeviceIDs": { "-1": [
"DeviceIDs": [
"0000:03:02.3",
"0000:03:02.0"
] },
],
"AllocResp": "CikKC3NyaW92X25ldF9BEhogMDAwMDowMzowMi4zIDAwMDA6MDM6MDIuMA=="
}
],

View File

@@ -1,371 +0,0 @@
// 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
}

View File

@@ -1,56 +0,0 @@
// 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)
}

View File

@@ -1,145 +0,0 @@
// 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)
}
}

View File

@@ -1,219 +0,0 @@
// 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
}

View File

@@ -1,58 +0,0 @@
// 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.PluginMain(
func(args *skel.CmdArgs) error {
return api.CmdAdd(args)
},
func(args *skel.CmdArgs) error {
return api.CmdCheck(args)
},
func(args *skel.CmdArgs) error {
return api.CmdDel(args)
},
cniversion.All, "meta-plugin that delegates to other CNI plugins")
}

View File

@@ -1,59 +0,0 @@
// 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.PluginMain(
func(args *skel.CmdArgs) error {
result, err := multus.CmdAdd(args, nil, nil)
if err != nil {
return err
}
return result.Print()
},
func(args *skel.CmdArgs) error {
return multus.CmdCheck(args, nil, nil)
},
func(args *skel.CmdArgs) error { return multus.CmdDel(args, nil, nil) },
cniversion.All, "meta-plugin that delegates to other CNI plugins")
}

View File

@@ -1,683 +0,0 @@
// 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))
}
}

View File

@@ -1,544 +0,0 @@
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())
})
})

95
deployment/webhook/certs.sh Executable file
View File

@@ -0,0 +1,95 @@
#!/bin/bash
# Copyright (c) 2018 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.
# create temp dir to store intermediate files
tmp=$(mktemp -d)
# generate private key
echo "Generating private RSA key..."
openssl genrsa -out ${tmp}/webhook-key.pem 2048 >/dev/null 2>&1
# generate CSR
echo "Generating CSR configuration file..."
cat <<EOF >> ${tmp}/webhook.conf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = multus-webhook-service
DNS.2 = multus-webhook-service.default
DNS.3 = multus-webhook-service.default.svc
EOF
openssl req -new -key ${tmp}/webhook-key.pem -subj "/CN=multus-webhook-service.default.svc" -out ${tmp}/server.csr -config ${tmp}/webhook.conf
# push CSR to Kubernetes API server
echo "Sending CSR to Kubernetes..."
csr_name="multus-webhook-service.default"
kubectl delete csr ${csr_name} >/dev/null 2>&1
cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: ${csr_name}
spec:
request: $(cat ${tmp}/server.csr | base64 -w0)
groups:
- system:authenticated
usages:
- digital signature
- key encipherment
- server auth
EOF
# approve certificate
echo "Approving CSR..."
kubectl certificate approve ${csr_name}
# wait for the cert to be issued
echo -n "Waiting for the certificate to be issued..."
cert=""
for sec in $(seq 15); do
cert=$(kubectl get csr ${csr_name} -o jsonpath='{.status.certificate}')
if [[ $cert != "" ]]; then
echo -e "\nCertificate issued succesfully."
echo $cert | base64 --decode > ${tmp}/webhook-cert.pem
break
fi
echo -n "."; sleep 1
done
if [[ $cert == "" ]]; then
echo -e "\nError: certificate not issued. Verify that the API for signing certificates is enabled."
exit
fi
# create secret
echo "Creating secret..."
kubectl delete secret "multus-webhook-secret"
kubectl create secret generic --from-file=key.pem=${tmp}/webhook-key.pem --from-file=cert.pem=${tmp}/webhook-cert.pem "multus-webhook-secret"
# set cert in webhook configuration
echo "Patching configuration file with certificate..."
if [[ -f configuration-template.yaml ]]; then
sed "s/__CERT__/${cert}/" configuration-template.yaml > configuration.yaml
echo "File configuration.yaml patched."
else
echo -e "Error: validating configuration template file 'configuration-template.yaml' is missing. Please update it with cert.pem value from the secret manually."
fi

View File

@@ -0,0 +1,38 @@
# Copyright (c) 2018 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.
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
labels:
app: multus-webhook
name: multus-webhook-config
webhooks:
- clientConfig:
caBundle: __CERT__
service:
name: multus-webhook-service
namespace: default
path: /validate
failurePolicy: Fail
name: multus-webhook.k8s.cni.cncf.io
rules:
- apiGroups:
- k8s.cni.cncf.io
apiVersions:
- v1
resources:
- network-attachment-definitions
operations:
- CREATE

View File

@@ -0,0 +1,50 @@
# Copyright (c) 2018 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.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: multus-webhook
name: multus-webhook-deployment
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: multus-webhook
template:
metadata:
labels:
app: multus-webhook
spec:
containers:
- name: multus-webhook
image: multus-webhook
command:
- /webhook/webhook
args:
- --bind-address=0.0.0.0
- --port=443
- --tls-private-key-file=/webhook/tls/key.pem
- --tls-cert-file=/webhook/tls/cert.pem
volumeMounts:
- mountPath: /webhook/tls
name: multus-webhook-secret
readOnly: True
imagePullPolicy: IfNotPresent
volumes:
- name: multus-webhook-secret
secret:
secretName: multus-webhook-secret

View File

@@ -0,0 +1,27 @@
# Copyright (c) 2018 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.
apiVersion: v1
kind: Service
metadata:
name: multus-webhook-service
labels:
app: multus-webhook
namespace: default
spec:
ports:
- port: 443
targetPort: 443
selector:
app: multus-webhook

View File

@@ -1,227 +0,0 @@
# 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

View File

@@ -1,249 +0,0 @@
# 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:
- "cp"
- "/usr/src/multus-cni/bin/multus-shim"
- "/host/opt/cni/bin/multus-shim"
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/

View File

@@ -1,236 +0,0 @@
# 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

279
doc/configuration.md Normal file
View File

@@ -0,0 +1,279 @@
## Multus-cni Configuration Reference
Following is the example of multus config file, in `/etc/cni/net.d/`.
(`"Note1"` and `"Note2"` are just comments, so you can remove them at your configuration)
```
{
"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",
"capabilities": {
"portMappings": true
},
"readinessindicatorfile": "",
"namespaceIsolation": false,
"Note1":"NOTE: you can set clusterNetwork+defaultNetworks OR delegates!!",
"clusterNetwork": "defaultCRD",
"defaultNetworks": ["sidecarCRD", "flannel"],
"systemNamespaces": ["kube-system", "admin"],
"multusNamespace": "kube-system",
"Note2":"NOTE: If you use clusterNetwork/defaultNetworks, delegates is ignored",
"delegates": [{
"type": "weave-net",
"hairpinMode": true
}, {
"type": "macvlan",
... (snip)
}]
}
```
* `name` (string, required): the name of the network
* `type` (string, required): &quot;multus&quot;
* `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/intel/multus-cni/blob/master/doc/node-kubeconfig.yaml). If you would like to use CRD (i.e. network attachment definition), this is required
* `logFile` (string, optional): file path for log file. multus puts log in given file
* `logLevel` (string, optional): logging level ("debug", "error", "verbose", or "panic")
* `namespaceIsolation` (boolean, optional): Enables a security feature where pods are only allowed to access `NetworkAttachmentDefinitions` in the namespace where the pod resides. Defaults to false.
* `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 capability for now). See the [example](https://github.com/intel/multus-cni/blob/master/examples/multus-ptp-portmap.conf).
* `readinessindicatorfile`: The path to a file whose existance denotes that the default network is ready
User should chose following parameters combination (`clusterNetwork`+`defaultNetworks` or `delegates`):
* `clusterNetwork` (string, required): default CNI network for pods, used in kubernetes cluster (Pod IP and so on): name of network-attachment-definition, CNI json file name (without extention, .conf/.conflist) or directory for CNI config file
* `defaultNetworks` ([]string, required): default CNI network attachment: name of network-attachment-definition, CNI json file name (without extention, .conf/.conflist) or directory for CNI config file
* `systemNamespaces` ([]string, optional): list of namespaces for Kubernetes system (namespaces listed here will not have `defaultNetworks` added)
* `multusNamespace` (string, optional): namespace for `clusterNetwork`/`defaultNetworks`
* `delegates` ([]map,required): number of delegate details in the Multus
### Network selection flow of clusterNetwork/defaultNetworks
Multus will find network for clusterNetwork/defaultNetworks as following sequences:
1. CRD object for given network name, in 'kube-system' namespace
1. CNI json config file in `confDir`. Given name should be without extention, like .conf/.conflist. (e.g. "test" for "test.conf")
1. Directory for CNI json config file. Multus will find alphabetically first file for the network
1. Multus failed to find network. Multus raise error message
## Miscellaneous config
### Default Network Readiness Indicator
You may wish for your "default network" (that is, the CNI plugin & its configuration you specify as your default delegate) to become ready before you attach networks with Multus. This is disabled by default and not used unless you add the readiness check option(s) to your CNI configuration file.
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, hence, 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 existance 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.
Multus will always log via `STDERR`, which is the standard method by which CNI plugins communicate errors, and these errors are logged by the Kubelet. This method is always enabled.
#### 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",
```
### 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`.
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] GetPodNetwork: namespace isolation violation: podnamespace: development / 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
```
### 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
...
```

30
doc/development.md Normal file
View File

@@ -0,0 +1,30 @@
## Development Information
## How to build the multus-cni?
```
git clone https://github.com/intel/multus-cni.git
cd multus-cni
./build
```
## How to run CI tests?
Multus has go unit tests (based on ginkgo framework).The following commands drive CI tests manually in your environment:
```
sudo ./test.sh
```
## Logging Best Practices
Following are multus logging best practices:
* Add `logging.Debugf()` at the begining 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)
## CI Introduction
TBD

625
doc/how-to-use.md Normal file
View File

@@ -0,0 +1,625 @@
## 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/intel/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 (included in the clone of this repository). Run this command (typically you would run this on the master, or wherever you have access to the `kubectl` command to manage your cluster).
$ cat ./images/{multus-daemonset.yml,flannel-daemonset.yml} | kubectl apply -f -
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/30-multus.conf <<EOF
{
"name": "multus-cni-network",
"type": "multus",
"readinessindicatorfile": "/var/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_PROTO=$(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"
} ]
]
}
}
```
### Run pod with network annotation
#### Lauch 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
```
#### Lauch 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
```
#### Lauch 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
```
#### Lauch 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
```
#### Lauch 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
```
#### Lauch 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: ["/entrypoint.sh"]
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.
--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 wait (600 seconds) until there's a CNI configuration file there, and it will take the alphabetically first configuration there, and it will wrap that configuration 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-log-level=
--multus-log-file=
Used only with `--multus-conf-file=auto`. See the documentation for 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 speicfy 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' availablity, 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 using CRIO, you may need to restart CRIO to get the Multus configuration file to take -- this is rarely necessary.
--restart-crio=false
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

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 190 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 197 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -15,8 +15,6 @@ Two things we'll refer to a number of times through this document are:
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.
@@ -29,38 +27,35 @@ To verify that you default network is ready, you may list your Kubernetes nodes
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
```
In the case that your default network is ready
## 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:
Firstly, clone this GitHub repository.
```
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset-thick.yml
git clone https://github.com/intel/multus-cni.git && cd multus-cni
```
See the [thick plugin docs](./thick-plugin.md) for more information about this architecture.
Alternatively, you may install the thin-plugin with:
If you're using Kubernetes 1.16+, we'll apply a YAML file with `kubectl` from this repo.
```
kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset.yml
$ cat ./images/multus-daemonset.yml | kubectl apply -f -
```
Or, for Kubernetes versions < 1.16, use the prior version yaml:
```
$ cat ./images/multus-daemonset-pre-1.16.yml | kubectl apply -f -
```
### 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)
* Reads the lexicographically (alphabetically) first configuration file in `/etc/cni/net.d`, and creates a new configuration file for Multus 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.
@@ -69,7 +64,7 @@ kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-c
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
$ 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.
@@ -179,7 +174,7 @@ EOF
You may now inspect the pod and see what interfaces are attached, like so:
```
kubectl exec -it samplepod -- ip a
$ kubectl exec -it samplepod -- ip a
```
You should note that there are 3 interfaces:
@@ -194,7 +189,7 @@ For additional confirmation, use `kubectl describe pod samplepod` and there will
```
Annotations: k8s.v1.cni.cncf.io/networks: macvlan-conf
k8s.v1.cni.cncf.io/network-status:
k8s.v1.cni.cncf.io/networks-status:
[{
"name": "cbr0",
"ips": [

112
doc/webhook/webhook.md Normal file
View File

@@ -0,0 +1,112 @@
# Validating admission webhook
## Building Docker image
From the root directory of Multus execute:
```
cd webhook
./build
```
## Deploying webhook application
Change working directory. From the root directory of Multus execute:
```
cd deployment/webhook
```
Create key and certificate pair and patch configuration-template.yaml file with base64-encoded certificate file. Run:
```
./certs.sh
```
*Note: Verify that Kubernetes controller manager has --cluster-signing-cert-file and --cluster-signing-key-file parameters set to paths to your CA keypair,
to make sure that Certificates API is enabled in order to generate certificate signed by cluster CA.
Script generates private key and certificate signing request, which is then pushed to the Kubernetes API server.
Then script approves that CSR and API server issues the certificate. Certificate is obtained from the API server and used to create a secret.
Script also patches `configuration-template.yaml` file with base64-encoded certificate and creates `configuration.yaml` file containing
Validating Webhook Configuration specification, which is deployed in one of the next steps.
More details about TLS certificates management in a cluster available [here](https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/).*
Create service:
```
kubectl create -f service.yaml
```
Run deployment:
```
kubectl create -f deployment.yaml
```
Create Validating Webhook Configuration:
```
kubectl create -f configuration.yaml
```
## Verifying installation
Try to create invalid Network Attachment Definition resource:
```
cat <<EOF | kubectl create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: invalid-net-attach-def
spec:
config: '{
"invalid": "config"
}'
EOF
```
Webhook should deny the request:
```
Error from server: error when creating "STDIN": admission webhook "multus-webhook.k8s.cni.cncf.io" denied the request: Invalid network config spec
```
Now, try to create correctly defined one:
```
cat <<EOF | kubectl create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: correct-net-attach-def
spec:
config: '{
"cniVersion": "0.3.0",
"name": "a-bridge-network",
"type": "bridge",
"bridge": "br0",
"isGateway": true,
"ipam": {
"type": "host-local",
"subnet": "192.168.5.0/24",
"dataDir": "/mnt/cluster-ipam"
}
}'
EOF
```
Resource should be allowed and created:
```
networkattachmentdefinition.k8s.cni.cncf.io/correct-net-attach-def created
```
## Troubleshooting
Webhook server prints a lot of debug messages that could help to find the root cause of an issue.
To display logs run:
```
kubectl logs -l app=multus-webhook
```
Example output showing logs for handling requests generated in the "Verifying installation section":
```
# kubectl logs multus-webhook-pod
2018-08-22T13:33:09Z [debug] Starting Multus webhook server
2018-08-22T13:33:32Z [debug] Validating network config spec: { "invalid": "config" }
2018-08-22T13:33:32Z [debug] Spec is not a valid network config: error parsing configuration list: no name. Trying to parse into config list
2018-08-22T13:33:32Z [debug] Spec is not a valid network config list: error parsing configuration: missing 'type'
2018-08-22T13:33:32Z [error] Invalid config: error parsing configuration: missing 'type'
2018-08-22T13:33:32Z [debug] Sending response to the API server
2018-08-22T13:35:29Z [debug] Validating network config spec: { "cniVersion": "0.3.0", "name": "a-bridge-network", "type": "bridge", "bridge": "br0", "isGateway": true, "ipam": { "type": "host-local", "subnet": "192.168.5.0/24", "dataDir": "/mnt/cluster-ipam" } }
2018-08-22T13:35:29Z [debug] Spec is not a valid network config: error parsing configuration list: no 'plugins' key. Trying to parse into config list
2018-08-22T13:35:29Z [debug] Network Attachment Defintion is valid. Admission Review request allowed
2018-08-22T13:35:29Z [debug] Sending response to the API server
```

View File

@@ -1,382 +0,0 @@
# 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",
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 &quot;multus&quot;
* `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
### 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
...
```

View File

@@ -1,65 +0,0 @@
## 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 CI 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
```
## 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.

View File

@@ -1,767 +0,0 @@
## 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
```

View File

@@ -1,113 +0,0 @@
# 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.
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.

View File

@@ -1,26 +1,12 @@
## Multus e2e test with kind
### Prerequisite
To run the e2e test, you need the following components:
- curl
- jinjanator
- docker
### How to test e2e
```
$ git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
$ git clone https://github.com/intel/multus-cni.git
$ cd multus-cni/e2e
$ ./get_tools.sh
$ ./generate_yamls.sh
$ ./setup_cluster.sh
$ ./test-simple-macvlan1.sh
```
### How to teardown cluster
```
$ ./teardown.sh
```

64
e2e/cni-install.yml Normal file
View 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/v0.8.5/cni-plugins-linux-amd64-v0.8.5.tgz
cd /host/opt/cni/bin
tar xvfzp /tmp/cni-plugins-linux-amd64-v0.8.5.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

View File

@@ -1,17 +0,0 @@
#!/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

View File

@@ -5,12 +5,11 @@ if [ ! -d bin ]; then
mkdir bin
fi
curl -Lo ./bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/v0.22.0/kind-$(uname)-amd64"
curl -Lo ./bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/v0.7.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
curl -Lo ./bin/koko https://github.com/redhat-nfvpe/koko/releases/download/v0.82/koko_0.82_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

63
e2e/macvlan1.yml Normal file
View File

@@ -0,0 +1,63 @@
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan1-config
spec:
config: '{
"cniVersion": "0.3.1",
"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

247
e2e/multus-daemonset.yml Normal file
View File

@@ -0,0 +1,247 @@
---
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: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
- "--cni-version=0.3.1"
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
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
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds-ppc64le
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: ppc64le
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
# ppc64le support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest-ppc64le
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
- "--cni-version=0.3.1"
resources:
requests:
cpu: "100m"
memory: "90Mi"
limits:
cpu: "100m"
memory: "90Mi"
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
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

View File

@@ -3,67 +3,34 @@ 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} ..
reg_name='kind-registry'
reg_port='5000'
running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)"
if [ "${running}" != 'true' ]; then
docker run -d --restart=always -p "${reg_port}:5000" --name "${reg_name}" registry:2
fi
reg_ip="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' "${reg_name}")"
# deploy cluster with kind
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
endpoint = ["http://${reg_ip}:${reg_port}"]
nodes:
- role: control-plane
- role: worker
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
pod-manifest-path: "/etc/kubernetes/manifests/"
feature-gates: "DynamicResourceAllocation=true,KubeletPodResourcesDynamicResources=true"
- role: worker
# Required by DRA Integration
##
featureGates:
DynamicResourceAllocation: true
runtimeConfig:
"api/alpha": "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"
sudo env PATH=${PATH} koko -d kind-worker,eth1 -d kind-worker2,eth1
sleep 1
kubectl -n kube-system wait --for=condition=available deploy/coredns --timeout=300s
kubectl create -f yamls/$MULTUS_MANIFEST
kubectl create -f https://raw.githubusercontent.com/intel/multus-cni/master/images/multus-daemonset.yml
sleep 1
kubectl -n kube-system wait --for=condition=ready -l name=multus pod --timeout=300s
kubectl create -f yamls/cni-install.yml
kubectl create -f cni-install.yml
sleep 1
kubectl -n kube-system wait --for=condition=ready -l name=cni-plugins pod --timeout=300s

15
e2e/setup_multus.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
set -o errexit
export PATH=${PATH}:./bin
kind export kubeconfig
sudo koko -d kind-worker,eth1 -d kind-worker2,eth1
sleep 1
kubectl -n kube-system wait --for=condition=available deploy/coredns --timeout=300s
kubectl create -f https://raw.githubusercontent.com/intel/multus-cni/master/images/multus-daemonset.yml
sleep 1
kubectl -n kube-system wait --for=condition=ready -l name=multus pod --timeout=300s
kubectl create -f cni-install.yml
sleep 1
kubectl -n kube-system wait --for=condition=ready -l name=cni-plugins pod --timeout=300s

View File

@@ -1,11 +0,0 @@
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"]

View File

@@ -1,15 +0,0 @@
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"
}
}'

View File

@@ -1,7 +0,0 @@
#!/bin/sh
#set -o errexit
export PATH=${PATH}:./bin
# delete cluster kind
kind delete cluster

View File

@@ -1,64 +0,0 @@
---
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

View File

@@ -1,57 +0,0 @@
---
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

View File

@@ -1,49 +0,0 @@
---
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

View File

@@ -1,210 +0,0 @@
---
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:
- "cp"
- "/usr/src/multus-cni/bin/multus-shim"
- "/host/opt/cni/bin/multus-shim"
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/

View File

@@ -1,198 +0,0 @@
---
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

View File

@@ -1,63 +0,0 @@
---
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

View File

@@ -1,15 +0,0 @@
---
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

View File

@@ -1,50 +0,0 @@
#!/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

View File

@@ -1,60 +0,0 @@
#!/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

View File

@@ -3,17 +3,11 @@ set -o errexit
export PATH=${PATH}:./bin
kubectl create -f yamls/simple-macvlan1.yml
kubectl create -f 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
kubectl exec macvlan1-worker1 ip a show dev net1
echo "check macvlan1-worker1 interface address: net1"
ipaddr=$(kubectl exec macvlan1-worker1 -- ip -j a show | jq -r \
@@ -23,13 +17,7 @@ if [ $ipaddr != "10.1.1.11" ]; then
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
kubectl exec macvlan1-worker2 ip a show dev net1
echo "check macvlan1-worker2 interface address: net1"
ipaddr=$(kubectl exec macvlan1-worker2 -- ip -j a show | jq -r \
@@ -37,6 +25,3 @@ ipaddr=$(kubectl exec macvlan1-worker2 -- ip -j a show | jq -r \
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

View File

@@ -1,10 +0,0 @@
#!/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

View File

@@ -1,22 +0,0 @@
#!/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"

View File

@@ -62,9 +62,9 @@ A sample `cni-configuration.conf` is provided, typically this file is placed in
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.
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` annotaton 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.
In this exmaple (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"
@@ -89,6 +89,6 @@ spec:
}
}'
```
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.
The [net-resource-sample-pod.yaml](./net-resource-sample-pod.yaml) is an exmaple Pod manifest file that requesting a SRIOV device from a host which is then configured using the above network attachement definition.
>For further information on how to configure SRIOV Device Plugin and SRIOV-CNI please refer to the links given above.
>For further information on how to configure SRIOV Device Plugin and SRIOV-CNI please refer to the links given above.

19
examples/clusterrole.yml Normal file
View File

@@ -0,0 +1,19 @@
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: multus-crd-overpowered
rules:
- apiGroups: ["k8s.cni.cncf.io"]
resources:
- '*'
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods
- pods/status
verbs:
- get
- update

View File

@@ -0,0 +1,13 @@
{
"name": "multus-cni-network",
"type": "multus",
"delegates": [
{
"type": "flannel",
"delegate": {
"isDefaultGateway": true
}
}
],
"kubeconfig": "/etc/kubernetes/kubelet.conf"
}

21
examples/crd.yml Normal file
View File

@@ -0,0 +1,21 @@
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

12
examples/flannel-conf.yml Normal file
View File

@@ -0,0 +1,12 @@
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: flannel-conf
spec:
config: '{
"cniVersion": "0.3.0",
"type": "flannel",
"delegate": {
"isDefaultGateway": true
}
}'

21
examples/macvlan-conf.yml Normal file
View File

@@ -0,0 +1,21 @@
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"
}
}'

View File

@@ -1,57 +0,0 @@
---
# 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

View File

@@ -0,0 +1,36 @@
{
"name": "multus-cni-network",
"type": "multus"
"capabilities": {
"portMappings": true
},
"delegates": [
{
"cniVersion": "0.3.1",
"name": "ptp-tuning-conflist",
"plugins": [
{
"dns": {
"nameservers": [
"172.16.1.1"
]
},
"ipMasq": true,
"ipam": {
"subnet": "172.16.0.0/24",
"type": "host-local"
},
"mtu": 512,
"type": "ptp"
},
{
"capabilities": {
"portMappings": true
},
"externalSetMarkChain": "KUBE-MARK-MASQ",
"type": "portmap"
}
]
}
],
}

View File

@@ -0,0 +1,163 @@
# -----------------------------------------------
# - 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:
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: multus
spec:
hostNetwork: true
nodeSelector:
beta.kubernetes.io/arch: amd64
tolerations:
- 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

View File

@@ -0,0 +1,21 @@
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"

View File

@@ -0,0 +1,30 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
# name must match the spec fields below, and be in the form: <plural>.<group>
name: network-attachment-definitions.k8s.cni.cncf.io
spec:
# group name to use for REST API: /apis/<group>/<version>
group: k8s.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: network-attachment-definitions
# singular name to be used as an alias on the CLI and for display
singular: network-attachment-definition
# kind is normally the CamelCased singular type. Your resource manifests use this.
kind: NetworkAttachmentDefinition
# shortNames allow shorter string to match your resource on the CLI
shortNames:
- net-attach-def
validation:
openAPIV3Schema:
properties:
spec:
properties:
config:
type: string

View File

@@ -0,0 +1,16 @@
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: multus
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'

View File

@@ -0,0 +1,5 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: testns1

View File

@@ -0,0 +1,72 @@
---
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": "static",
"addresses": [
{ "address": "10.1.1.101/24" }
]
}
}'
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
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: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
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: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
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" }
]
}
}'

View File

@@ -0,0 +1,19 @@
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
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"
} ]
}
}'

View File

@@ -0,0 +1,196 @@
---
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:
updateStrategy:
type: RollingUpdate
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

View File

@@ -0,0 +1,30 @@
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: ptp-tuning-conflist
spec:
config: '{
"cniVersion": "0.3.1",
"name": "ptp-tuning-conflist",
"plugins": [{
"type": "ptp",
"ipMasq": true,
"mtu": 512,
"ipam": {
"type": "host-local",
"subnet": "172.16.0.0/24"
},
"dns": {
"nameservers": ["172.16.1.1"]
}
},
{
"name": "mytuning",
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}
]
}'

View File

@@ -0,0 +1,13 @@
---
apiVersion: v1
kind: Pod
metadata:
name: pod-case-01
annotations:
k8s.v1.cni.cncf.io/networks: macvlan-conf-1
spec:
containers:
- name: pod-case-01
image: docker.io/centos/tools:latest
command:
- /sbin/init

View File

@@ -0,0 +1,18 @@
---
apiVersion: v1
kind: Pod
metadata:
name: pod-case-02
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "macvlan-conf-2" },
{ "name": "vlan-conf-1-1",
"namespace": "testns1",
"interface": "vlan1-1" }
]'
spec:
containers:
- name: pod-case-02
image: docker.io/centos/tools:latest
command:
- /sbin/init

View File

@@ -0,0 +1,17 @@
---
apiVersion: v1
kind: Pod
metadata:
name: pod-case-03
annotations:
k8s.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

View File

@@ -0,0 +1,11 @@
---
apiVersion: v1
kind: Pod
metadata:
name: pod-case-04
spec:
containers:
- name: pod-case-04
image: docker.io/centos/tools:latest
command:
- /sbin/init

Some files were not shown because too many files have changed in this diff Show More