196 Commits

Author SHA1 Message Date
Doug Smith
991a1b2c7f Merge pull request #924 from k8snetworkplumbingwg/fix/image
Fix release 3.x thick image tag to isolate from 4.0
2022-10-11 13:44:19 -04:00
Tomofumi Hayashi
e9b87fb05a Fix release 3.x thick image tag to isolate from 4.0
Due to #900, image tag, ':thick', which is used in 4.0 and 3.x,
gets user confused because thick image is not compatible between
4.0 and 3.x. This fix provides strict version name in deployment
yaml to specify install image tag.
2022-10-12 01:31:15 +09:00
Doug Smith
25703fce21 Change container image to fedora from centos (#904)
This change introduce fedora container for multus image.

Co-authored-by: Tomofumi Hayashi <tohayash@redhat.com>
2022-08-27 16:09:54 +09:00
Doug Smith
6b38a213b1 Merge pull request #905 from s1061123/fix/revive
Fix revive in CI
2022-08-26 15:09:16 -04:00
Tomofumi Hayashi
2068ea9f77 Fix revive in CI 2022-08-27 04:03:58 +09:00
Doug Smith
f4c0adf54c Merge pull request #881 from chrisplo/detect-python3
Detect python3
2022-07-22 09:48:40 -04:00
Chris Plock
ac6757b9cc Detect python3
fixes https://github.com/k8snetworkplumbingwg/multus-cni/issues/880
2022-07-15 15:13:27 -07:00
Doug Smith
70c970cd6e Merge pull request #878 from tomkukral/print-kubeconfig
write output file for generated kubeconfig
2022-07-12 08:39:17 -04:00
Tomáš Kukrál
95adff55b0 write output file for generated kubeconfig
This change will make debugging easier in cases when non-default
location is used and user forgot to mount new directory.
2022-07-08 14:02:51 +02:00
Doug Smith
ca8c9c5791 Merge pull request #864 from s1061123/default-route-pointer
Use *[]net.IP for 'default-route' network selection element.
2022-06-21 15:47:09 -04:00
Doug Smith
18a660ebc7 Merge pull request #867 from mmirecki/downwardapi
Fix missing device-info in networks-status annotation for chained plugins
2022-06-21 14:38:29 -04:00
mmirecki
358f09bfe2 Merge remote-tracking branch 'upstream/master' into downwardapi 2022-06-21 20:14:34 +02:00
Tomofumi Hayashi
107624ccff Use *[]net.IP for 'default-route' network selection element. 2022-06-22 02:12:08 +09:00
Doug Smith
13eb83a01c Merge pull request #868 from s1061123/fix/revive-err
Fix revive error
2022-06-21 11:26:44 -04:00
Tomofumi Hayashi
dc57189cf9 Fix revive error 2022-06-21 23:39:18 +09:00
mmirecki
2bf8dae9a8 Fix missing device-info in networks-status annotation for chained plugins 2022-06-21 14:47:04 +02:00
Doug Smith
8bbb594dad Merge pull request #862 from s1061123/fix/cmddel-nostatus-update
Skip status update in CmdDel if getPod is failed
2022-06-13 13:22:17 -04:00
Tomofumi Hayashi
fcc8e44f14 Skip status update in CmdDel if getPod is failed
This change skips to update pod's network-status annotation
when getPod is failed at the beginning of CmdDel. If getPod is
failed, K8s api gets stucked in many cases, hence pod update
might be failed in most cases.
2022-06-14 02:14:43 +09:00
疯狂的小企鹅
be56f8dc30 Fixed that in.Delegates may remain in the CmdDel (#859)
Co-authored-by: jinda.ljd <jinda.ljd@alibaba-inc.com>
2022-06-08 21:03:14 +09:00
Miguel Duarte Barroso
d2a4b832f2 config, logging: correct the logging parameters (#856)
The logging parameters were listing using uppercase, which is wrong. 

According to multus configuration, they should be in camelCase - [0] and [1].

[0] - 779170a48e/pkg/types/types.go (L45)
[1] - 779170a48e/pkg/types/types.go (L46)
2022-06-06 22:33:05 +09:00
Nikhil Simha
779170a48e Added hyperlink for logging (#851)
* Added hyperlink for logging

This may make it easier for users to find info on logging parameters since there isn't a dedicated docs page for it.

* Fixed hyperlink

Linked to proper section
2022-05-26 09:33:22 -04:00
Doug Smith
5ee0274b5b Bumps net-attach-def client library to v1.1.1 (#846)
This accounts for CNI v1.0 formatted IP address information (no longer requires version field)
2022-05-12 22:58:23 +09:00
Doug Smith
42a2642852 Merge pull request #844 from danielmellado/fix-images-docs
Fix wrong location for daemonset in readme
2022-05-10 11:42:14 -04:00
Daniel Mellado
b5323e4144 Fix wrong location for daemonset in readme
This PR fixes an outdated reference in the docs where
multus-daemonset.yml was expected to be found under the images folder.
2022-05-10 17:06:47 +02:00
Doug Smith
dcbc215b93 The cachefile name should be the delegate configuration name (#841)
It was previously using the net-attach-def name, which doesn't align with the cache file. Causing default-route selection to not succeed.
2022-05-07 00:06:37 +09:00
Dan Williams
ecc1482d50 types: fix usage of strings.Split() for parsing CNI_ARGS (#836)
* types: fix usage of strings.Split() for parsing CNI_ARGS

strings.Split() returns a slice, in this case with two elements of
the key and value. As such we shouldn't range over the slice when
the code is expecting a 2-element slice of key/value.

Otherwise we get errors for valid CNI_ARGS like:

2022-04-22T11:53:54Z [error] CreateCNIRuntimeConf: CNI_ARGS K8S_POD_NAMESPACE=openshift-etcd K8S_POD_NAMESPACE 17 is not recognized as CNI arg, skipped
2022-04-22T11:53:54Z [error] CreateCNIRuntimeConf: CNI_ARGS K8S_POD_NAMESPACE=openshift-etcd openshift-etcd 14 is not recognized as CNI arg, skipped

Fixes: d7d2a99ab5 ("Replace setenv with runtimeConfig set")

Signed-off-by: Dan Williams <dcbw@redhat.com>

* types/conf: fix handling of CNI_ARGS additions to rt.Args

We want to set the CNI_ARGS value in rt.Args if the existing value
is empty, not if the key doesn't exist yet. Since the rt.Args array
is pre-created with the K8S args keys, empty values of those keys
couldn't be overwritten with the previous scheme that just checked
if the key existed.

If the CNI_ARGS key wasn't found in rt.Args then add it; previously
a typo ("isExists != false") prevented that with inverted logic.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2022-04-26 00:13:09 +09:00
Nikhil Simha
dd15abc9b1 Added a static pod e2e test. (#835)
* Added bash e2e test for static pods. Checks for net1 interface on pod.

Signed-off-by: nicklesimba <simha.nikhil@gmail.com>

* Enable static pod test in CI

Signed-off-by: nicklesimba <simha.nikhil@gmail.com>

* Addressed review comments

Signed-off-by: nicklesimba <simha.nikhil@gmail.com>
2022-04-22 00:35:39 +09:00
jinglina
706de7c2c6 delete type conversion (#833) 2022-04-18 22:34:40 +09:00
Doug Smith
ea0df58e7c Merge pull request #831 from s1061123/update-golang
Update golang version to 1.17
2022-04-18 09:33:43 -04:00
Tomofumi Hayashi
4ada0c3ae8 Update golang version to 1.17 2022-04-14 23:30:30 +09:00
Tomofumi Hayashi
77ed17b392 Fix letter 2022-04-12 15:06:44 +09:00
Cyclinder
6bcb6bf403 add some flags for customize the log settings (#817) 2022-04-12 00:22:48 +09:00
Doug Smith
588ee8f192 Fixes log message on CNI Check (#825) 2022-04-11 20:35:47 +09:00
Cyclinder
ce533f01cc update e2e tools version for kind and cni-plugins (#830) 2022-04-11 14:08:22 +09:00
Doug Smith
0453b52097 Merge pull request #823 from s1061123/fix/cmddel
Remove error handling for getPod to force to proceed cmdDel.
2022-04-04 15:41:36 -04:00
Tomofumi Hayashi
2d53334211 Remove error handling for getPod to force to proceed cmdDel.
In cmdDel, CNI Spec mentioned that plugin should proceed cmdDel
without any error, hence the change removes error returning
at cmdDel.

fix #822
2022-04-05 02:29:41 +09:00
Cyclinder
45428a53ce fix the usage of flag "overrideNetworkName" (#805) 2022-03-04 16:01:21 +09:00
Doug Smith
6c12dc8c4f crio: mount /run rslave (#802)
to prevent "unknown FS magic on "/var/run/netns/*": 1021994" errors

Signed-off-by: Peter Hunt <pehunt@redhat.com>

Co-authored-by: Peter Hunt <pehunt@redhat.com>
2022-03-04 02:02:37 +09:00
Doug Smith
7559625a38 only warn when netns can't be opened (#803) 2022-03-04 02:02:24 +09:00
Balazs Nemeth
450e1d3414 check version incompatibility (#762)
* multus: entrypoint: disallow incompatible cni versions

When top level CNI version is 0.4.0 or more, nested CNI version
can't be less than 0.4.0 since these are incompatible. This
closes issue #737.

Signed-off-by: Balazs Nemeth <bnemeth@redhat.com>

* multus: thick: disallow incompatible cni versions

Similarly to disallowing incompatible versions in entrypoint.sh,
add the same logic in go for the thick plugin.

Signed-off-by: Balazs Nemeth <bnemeth@redhat.com>

* multus: add unit test for incompatible cni versions

Signed-off-by: Balazs Nemeth <bnemeth@redhat.com>
2022-02-28 21:50:39 +09:00
Tomofumi Hayashi
6dd45f38f9 Replace setenv with runtimeConfig set (#785)
setenv refers environment variables, which is unique in process,
not unique to go routine. Hence it may causes some issue in multi
threaded case, hence it is replaced with libcni's runtimeConfig
value set to set these variables at libcni side, after process
fork.
2022-02-21 23:55:33 +09:00
Doug Smith
843147aca0 Merge pull request #775 from s1061123/fix/773
Suppress uid mismatch error/warning in case of static pod
2022-02-07 12:44:04 -05:00
Miguel Duarte Barroso
191b8cb0ec e2e tests: always pull new multus images (#783)
Currently, the local workflow is far from optimal, since for every
change on the multus images the developers are required to redeploy
the kind cluster.

A more efficient workflow would be to build a local image, upload it to
the kind cluster, and finally re-deploy (delete & re-provision) the
daemonset, which would be running the new version.

For this flow to be possible, the multus container `imagePullPolicy`
must be set to `Always` - [0] - otherwise, the image is not updated.

[0] - https://kubernetes.io/docs/concepts/containers/images/#updating-images

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2022-01-26 21:37:03 +09:00
Miguel Duarte Barroso
9e79b79a89 multus, e2e tests: allow event sending to k8s API (#776)
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2022-01-20 23:50:16 +09:00
Tomofumi Hayashi
2e474f4c95 Suppress uid mismatch error/warning in case of static pod
In static pod case, kube api returns mirror pod UID hence
uid must be mismatched. This fix suppress error/warning message
in such case.

Fix #773
2022-01-15 23:17:53 +09:00
Doug Smith
27a86dafbc Merge pull request #779 from nicklesimba/patch-3
Update development.md
2022-01-14 11:10:03 -05:00
Nikhil Simha
daf96bffb3 Update development.md
Fixed doc for how to test
2022-01-14 10:59:29 -05:00
Doug Smith
7a53c910f2 Merge pull request #774 from nicklesimba/patch-2
Update quickstart.md
2022-01-11 13:59:45 -05:00
Nikhil Simha
38b1cd1cec Update quickstart.md
Specified location of 00-multus.conf for clarity
2022-01-11 13:57:48 -05:00
Sho Shimizu
6adb4dc4c4 Add toleration against NoExecute (#768) 2022-01-07 00:06:19 +09:00
Miguel Duarte Barroso
12df5bda72 run gofmt on the code (#772)
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2022-01-05 01:33:58 +09:00
Doug Smith
ed18a1f175 Merge pull request #764 from s1061123/fix/default-route
Update libcni cache when default-route net selection is used
2021-12-15 12:28:40 -05:00
Tomofumi Hayashi
d52f2b6a45 Update libcni cache when default-route net selection is used
To keep consistency between actual network and CNI result in cache,
update libcni cache when multus add/del default routes by
`default-route` network selection.
2021-12-15 01:57:51 +09:00
Doug Smith
4d9731bd3a Merge pull request #757 from maiqueb/fix-flaky-config-regen-test
flaky: fix delegate CNI conf updates unit test
2021-12-14 10:06:01 -05:00
Miguel Duarte Barroso
0da5449854 thick, config regen, test: fix test
The test was just checking that a READ/WRITE fsnotify.Event for
the multus configuration was being seen; this patch changes this
behavior, and assures that the delegateCNI configuration update results
in turn on the update of the multus configuration file.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-12-14 10:10:15 +01:00
Miguel Duarte Barroso
653c4b481d thick, config regen, tests: use a unique path for multus config
Using a unique path for the config generation will lessen the
amount of events caught by the test that checks if the multus
configuration must be re-generated as a result a default cluster
configuration update.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-12-09 14:46:36 +01:00
Doug Smith
b9d0d93d6e Pod UID mismatches should only warn on CNI DEL (#763) 2021-11-23 17:52:45 +09:00
Balazs Nemeth
84fde9d711 Update images/README.md (#761) 2021-11-19 21:22:34 +09:00
Doug Smith
4e0e65044b Merge pull request #758 from maiqueb/fix-legacy-entrypoint-e2e-tests
CI, e2e tests: fix legacy
2021-11-15 08:18:59 -05:00
Miguel Duarte Barroso
70660236a8 CI, e2e tests: fix legacy
Our CI is currently mistakenly executing the thick img on the e2e
legacy lanes. Furthermore, the e2e daemonset spec provided features
(and uses) the kubeconfig / multus conf generation binaries
provided only on the thick image.

This commit addresses these by enabling the e2e `setup_cluster.sh`
script user to specify the path to the desired deployment
configuration.

Github workflows are updated accordingly.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-11-15 12:38:26 +01:00
Moritz Kröger
130db696ca Update quickstart.md (#754)
* Update quickstart.md

removed unnecassary dollarsign since it kills the quick copy functionality of Code in Github

* Update README.md

* Update quickstart.md

* Update how-to-use.md

Co-authored-by: Doug Smith <douglaskippsmith@gmail.com>
2021-11-12 00:24:06 +09:00
Doug Smith
ada145ca5f Merge pull request #753 from s1061123/fix/cni-bin-dir
Added --cni-bin-dir and --multus-bin-file for regression
2021-11-11 00:00:46 +09:00
Tomofumi Hayashi
25c46c84b8 Added --cni-bin-dir and --multus-bin-file for regression 2021-11-08 17:40:54 +09:00
Etienne Champetier
32c952e501 Fixup thick deployment, make images thiner (#747)
* deployment, thick: only use thick image

Signed-off-by: Etienne Champetier <e.champetier@ateme.com>

* images: only keep binaries and LICENSE

Signed-off-by: Etienne Champetier <e.champetier@ateme.com>
2021-11-02 14:20:37 +09:00
Tomofumi Hayashi
7091831a00 Remove dependency of go-dproxy (#746)
This change removes to dependency of go-dproxy from multus to
reducing library dependencies.
2021-10-28 12:15:11 -04:00
Tomofumi Hayashi
1e43784d4c Change the sort of image push. 2021-10-28 23:04:07 +09:00
Miguel Duarte Barroso
42fde2292d deployment, thick: remove extra multus-cni-config mountpoint (#745)
This volume was removed since multus now requires the default cluster
network CNI configuration to be available. As such, the volume as
removed, but we unfortunately forgot to remove to remove the volume
mount.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-10-28 09:37:35 -04:00
Miguel Duarte Barroso
8ba2accb9f Replace entrypoint script with initcontainers (#718)
* build: install the multus binary in an init container

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>

* build: generate kubeconfig via go

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>

* build: generate multus cni configuration via golang

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>

* build: provide a docker img for daemon based deployments

We will have 2 different images (only on amd64 archs):
- legacy entrypoint script based
- daemonized process

The `image-build` docker action is updated, to build these 2 images.

There will be 2 different deployment specs, along with e2e test
lanes, one for each of the aforementioned alternatives.

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>

* build: delegate CNI config watch loop via golang

For the thick-plugin alternative, provide the watch loop for
configuration regeneration via a golang binary.

Over time, this binary is expected to run the control loop to watch
out for pod updates.

To enable current multus users to chose when they upgrade to this new
deployment setup, these changes are provided in separate multus images,
having a different yaml spec files. Both of these alternatives are
tested e2e, since a new lane is introduced.

The following libraries are introduced, along with the motivation for
adding them:
- dproxy: allows traversing the default network configuration arbitrarily,
  similar to what an X path / JSON path tool provides.
  Repo is available at [0].
- fsnotify: watch for changes in the default CNI configuration file.
  Repo is available at [1].

The config map providing the default network CNI configuration is not
copied over, since originally, the user was not required to install a
default network CNI plugin first, but, nowadays, this is a required
step of multus.

As such, it is no longer required to provide a default CNI
configuration.

[0] - https://github.com/koron/go-dproxy
[1] - https://github.com/fsnotify/fsnotify

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>

* run gofmt

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>

* refactor: make the builder pattern more idiomatic to golang

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>

* build: update github actions to release new imgs

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-10-27 08:42:37 -04:00
Dan Williams
b56dd5f67f multus: test Pod UID scenarios when both passed and not passed by runtime
Because the tests fake out the OS exec functionality, the environment that
would usually be passed to a plugin instead gets passed as a string array
to the ExecPlugin() function in the tests. But when verifying the expected
environment this function was looking at the actual OS environment rather
than the passed string array. Fix that.

Use that to test various cases of the pod UID being passed to plugins.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2021-10-26 01:20:43 +09:00
Dan Williams
e8e99f1771 multus: log error marshaling delegate netconf
Fixes an unused variable warning too.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2021-10-26 01:20:43 +09:00
Dan Williams
cb19a22cb9 multus: fail if given pod UID does not match Kube API pod UID
If the runtime passes a pod UID via K8S_POD_UID (which both CRIO and
containerd do as of mid-2021) then fail if the pod we get from the
Kube API has a different UID. This would indicate that the pod was
deleted and recreated while Multus was attempting to set up
networking for the old pod instance's sandbox, and it's pointless
to continue setting up a sandbox for a dead pod instance.

Also pass the pod UID through to plugins so they can perform
additional checking and validation on the pods they get from the
Kube API.

Signed-off-by: Dan Williams <dcbw@redhat.com>
2021-10-26 01:20:43 +09:00
Miguel Duarte Barroso
392726842f e2e tests: allow podman OCI
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-10-20 20:58:32 +09:00
Tomofumi Hayashi
76c31b0861 Fix version string in '-v' option
Due to change the file layout, previous version command does not
work. This change fix it.
2021-10-15 00:07:20 +09:00
Doug Smith
51a74efe57 Merge pull request #729 from s1061123/dev/update-layout
Change file layout and mention the supported Kubernetes version
2021-09-29 10:19:02 -04:00
Tomofumi Hayashi
bf0b37e010 Change file layout and mention the supported Kubernetes version 2021-09-28 22:40:51 +09:00
Doug Smith
efdc0a5c7d Merge pull request #724 from s1061123/downgrade-k8s-vendor
Downgrade Kubernetes vendor version
2021-09-17 14:46:50 -04:00
Tomofumi Hayashi
ae4a28b689 Downgrade Kubernetes vendor version
Multus community decide that multus support the Kubernetes that
Kubernetes community supports, so changed vendor code to the
oldest version of currently supported.
2021-09-17 22:48:12 +09:00
Doug Smith
e1b0698eb2 Merge pull request #723 from s1061123/update-go-version
Update golang version (at least >= 1.16)
2021-09-15 08:13:42 -04:00
Tomofumi Hayashi
35fdb29385 Update golang version (at least >= 1.16)
Currently golang community supports 1.16 and later, hence, need
to update golang version in our build pipeline. This change
updates golang version to 1.17/1.16.
2021-09-14 16:53:35 +09:00
张祖建
a28f5cb56c Fix typos
Signed-off-by: 张祖建 <zhangzujian.7@gmail.com>
2021-09-14 00:25:01 +09:00
Doug Smith
ad257698ef Merge pull request #712 from nicklesimba/patch-1
Update quickstart.md
2021-08-25 16:48:33 -04:00
Nikhil Simha
8b3bbf38c8 Update quickstart.md
There is a typo in the expected resulting annotations
2021-08-25 13:27:18 -04:00
Doug Smith
a506d7606c Merge pull request #696 from cgchinmay/fix_entrypoint
Use MULTUS_MASTER_CNI_FILE_NAME as MASTER_PLUGIN as is, if specified
2021-07-16 15:25:51 -04:00
Chinmay Gadgil
7e44bb6d21 Use MULTUS_MASTER_CNI_FILE_NAME as MASTER_PLUGIN as is, if specified 2021-07-12 10:30:20 -07:00
Tomofumi Hayashi
9b45d4b211 Merge branch 'master' of https://github.com/k8snetworkplumbingwg/multus-cni 2021-07-08 22:52:30 +09:00
Doug Smith
f77c591d69 Merge pull request #695 from cgchinmay/update_golang_version
Update golang version for installing packages
2021-07-08 09:52:03 -04:00
Tomofumi Hayashi
8c9f409032 Fix github action 2021-07-08 22:52:02 +09:00
Chinmay Gadgil
0724c2b9b0 Update golang version for installing packages 2021-07-06 22:56:21 -07:00
Miguel Duarte Barroso
085870c4b5 Export poll[Interval|Timeout] as constants
Thus not repeating ourselves, nor shadowing the global variables
that had the same name (pollInterval / pollTimeout).

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-06-26 00:06:30 +09:00
Miguel Duarte Barroso
26ef3c3eb4 Cut the retry interval and timeout by half on pod retrieval
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-06-26 00:06:30 +09:00
Miguel Duarte Barroso
1eeab9b589 Retry pod retrieval on multiple error types
Also retry on the following error types (cmd ADD/DEL):
- IsInternalError
- IsConnectionReset
- IsConnectionRefused

Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-06-26 00:06:30 +09:00
Doug Smith
7f5620db76 Merge pull request #689 from maiqueb/fix-missing-cni-version-example
examples: missing cni version on version without delegates
2021-06-23 12:57:56 -04:00
Miguel Duarte Barroso
1e29a6b50c examples: missing cni version on version without delegates
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-06-23 18:23:30 +02:00
Peng Liu
d09cc8e581 Use the default socket path in GetResourceClient when unspecified
Signed-off-by: Peng Liu <pliu@redhat.com>
2021-06-08 23:05:54 +09:00
xieyanker
cd23938191 Update calico daemonset's url 2021-06-02 21:32:08 +09:00
xieyanker
3b8aa66765 Fix typo 2021-06-02 21:31:37 +09:00
Doug Smith
37adefba51 Merge pull request #679 from xieyanker/master_cni_file
Support the specific cni file name
2021-06-02 08:08:05 -04:00
xieyanker
ec30b99534 Update how-to-use --multus-master-cni-file-name 2021-06-02 14:24:41 +08:00
xieyanker
a53233028b Update ORG_PATH for test-go.sh 2021-06-01 21:58:05 +09:00
xieyanker
492ffec8c8 Support the specific cni file name 2021-05-29 15:29:37 +08:00
Doug Smith
c25a39c5a3 Merge pull request #672 from s1061123/dev/update-lib
Update vendor package
2021-05-26 14:04:02 -04:00
Tomofumi Hayashi
3f1031e7b4 Update vendor package 2021-05-27 02:25:34 +09:00
Tomofumi Hayashi
2339c11a15 Remove global variables from kubletclient
Fixes #673
2021-05-27 01:45:15 +09:00
Sho SHIMIZU
2ba3d3cda2 Describe additional condition when using clusterNetwork
When configuring `clsuterNetwork` in Multus CNI config, the value
for `clusterNetwork` needs to match the value for `name` key in
the file `clusterNetwork` setting refers to. This condition is
undocumented in doc/configuration.md. This PR adds description
on the condition for users to understand `clusterNetwork` config
more clearly.

Signed-off-by: Sho SHIMIZU <sho.shimizu@gmail.com>
2021-05-24 22:11:30 +09:00
dougbtv
6abe8ee06b The kubeconfig creation should be an atomic operation 2021-05-13 22:40:06 +09:00
Doug Smith
a03b3e8c41 Merge pull request #666 from s1061123/fix/update-nad-lib
Update net-attach-def client library
2021-05-12 12:11:14 -04:00
Tomofumi Hayashi
753d18740a Update net-attach-def client library 2021-05-11 01:29:26 +09:00
Doug Smith
1a0fa953f9 Merge pull request #661 from xieyanker/patch-1
update 30-multus.conf to 00-multus.conf
2021-04-29 09:39:45 -04:00
xieyanker
bd57d31780 update 30-multus.conf to 00-multus.conf 2021-04-28 15:50:41 +08:00
Doug Smith
91a7d22e2b Merge pull request #657 from s1061123/dev/add-cluster-net-name
Add cluster network name in pod network annotation
2021-04-20 15:01:55 -04:00
Doug Smith
8d555dba24 Merge pull request #658 from e0ne/fix-typo
Fix typo: s/Lauch/Launch
2021-04-20 15:01:00 -04:00
Ivan Kolodyazhny
e566760a45 Fix typo: s/Lauch/Launch 2021-04-20 21:18:04 +03:00
Tomofumi Hayashi
cd7492f79b Add cluster network name in pod network annotation
This addresses #656
2021-04-19 23:20:36 +09:00
Bjorn Svensson
9e3ad2ea1e Correcting Dockerfile path for manual e2e tests
Signed-off-by: Björn Svensson <bjorn.a.svensson@est.tech>
2021-04-19 17:34:30 +09:00
Doug Smith
a52680b3bf Merge pull request #651 from s1061123/dev/add-arm32
Add arm32v7 container image support
2021-04-07 11:37:00 -04:00
Tomofumi Hayashi
78cf9fced8 Add arm32v7 container image support
Currently multus-cni releases arm32 binary as release tar ball.
This changes also introduce container image for easy deployment.
Fix #639
2021-04-04 04:23:39 +09:00
dougbtv
905b5d0e42 Github action for closing stale issues 2021-04-03 18:12:58 +09:00
dougbtv
0ef8c27a67 [docs] Adds release policy 2021-04-01 22:53:15 +09:00
Doug Smith
e603d8dbb0 Merge pull request #644 from s1061123/dev/cleanup-dev-info
Skip to call device-info related function if not required
2021-04-01 09:52:17 -04:00
Tomofumi Hayashi
9e96c38ebe Skip to call device-info related function if not required
The case withot DeviceInfo/ResourceName, we could skip functions
related to device info. This change skips to call these functions
if delegates does not have DeviceInfo/ResourceName.
2021-04-01 03:55:57 +09:00
Doug Smith
ad6f57f14d Merge pull request #643 from s1061123/fix/no-network-error
[WIP] Change nil instead of error in case of no network annotation
2021-03-31 12:14:56 -04:00
Tomofumi Hayashi
479e2bd78e Change nil instead of error in case of no network annotation
fix #642
2021-03-31 23:06:55 +09:00
Doug Smith
ae56437297 Merge pull request #638 from s1061123/dev/use-non-default-libcni-cache
Change libcni's cache directory from default for multus delegates
2021-03-29 17:05:05 -04:00
Doug Smith
dc4d6e6764 Merge pull request #636 from s1061123/fix/error-on-syntax-error
Return error on annotation parsing error
2021-03-29 09:23:27 -04:00
Tomofumi Hayashi
75acc10312 Change libcni's cache directory from default for multus delegates
This changes CNI cache directory to non default places to avoid
conflicts among CNI runtimes.
2021-03-27 04:12:10 +09:00
Tomofumi Hayashi
8d7308e6bb Return error on annotation parsing error
Fix #635
2021-03-26 01:31:01 +09:00
Doug Smith
c4b9534529 Merge pull request #634 from s1061123/fix/delete_old_main
Delete main from pkg/multus because now it was moved to cmd/main.go
2021-03-24 17:02:45 -04:00
Tomofumi Hayashi
c8e63996c8 Delete main from pkg/multus because now it was moved to cmd/main.go 2021-03-25 03:02:50 +09:00
dougbtv
5d98a0e0eb Updates Readinessindicator error message to be actionable by users 2021-03-24 04:19:44 +09:00
Doug Smith
343a4e3687 Merge pull request #629 from s1061123/fix/change-org-name
Fix/change org name
2021-03-17 15:36:17 -04:00
Doug Smith
feb52328d2 Merge pull request #627 from s1061123/fix/change-image-url
Change image url to ghcr.io
2021-03-16 14:50:00 -04:00
Tomofumi Hayashi
118cc629cf Update organization name due to ownership change 2021-03-16 16:24:49 +09:00
Tomofumi Hayashi
784fecfa02 Change image url to ghcr.io 2021-03-13 05:05:17 +09:00
Doug Smith
f6298a3a29 Merge pull request #626 from s1061123/dev/use-ghcr
Change github action to use ghcr.io
2021-03-12 14:51:45 -05:00
Tomofumi Hayashi
20049dedfe Change github action to use ghcr.io 2021-03-13 04:38:21 +09:00
Doug Smith
20a5d5f34a Merge pull request #625 from e0ne/example-pod
Fix link to an example pod with SRIOV network
2021-03-12 14:33:59 -05:00
Ivan Kolodyazhny
b97c443a56 Fix link to an example pod with SRIOV network 2021-03-11 16:48:04 +02:00
Doug Smith
40a0faed5e Merge pull request #624 from s1061123/dev/bump-libcni
Bump libcni version
2021-03-10 15:53:14 -05:00
Tomofumi Hayashi
5feb1343cd Bump libcni version 2021-03-11 04:50:21 +09:00
Yun Zhou
dc9315f125 Provide the option to not to log via STDERR
Today, Multus will always log via STDERR, and these logs will then
logged by the Kubelet. If we also choose to have Multus log to a file by
setting the LogFile option in the CNI configuration, the same logs will
be logged twice.

This commit provide the option to disable logging to STDERR.

Signed-off-by: Yun Zhou <yunz@nvidia.com>
2021-03-10 17:13:12 +09:00
Kennelly, Martin
5141eab28a Fix invite to NPWG slack
Readding NPWG slack invite mechanism.

Signed-off-by: Kennelly, Martin <martin.kennelly@intel.com>
2021-03-02 04:39:51 +09:00
Doug Smith
c1166d2d3d Merge pull request #617 from s1061123/fix/check-tty
Conditional check for tty/non-tty in case of wait (i.e. read)
2021-02-25 10:22:17 -05:00
Tomofumi Hayashi
de463cca0d Conditional check for tty/non-tty in case of wait (i.e. read)
Fix #615.
2021-02-26 00:18:06 +09:00
Tomofumi Hayashi
02ad030899 Fix CI info 2021-02-24 15:32:46 +09:00
Doug Smith
e80696dc8e Merge pull request #613 from s1061123/dev/update-gh-actions
Update github actions CI pipeline including lint error fix
2021-02-23 10:05:55 -05:00
Tomofumi Hayashi
9bfb9b9b67 Update github actions CI pipeline including lint error fix 2021-02-23 20:01:34 +09:00
Doug Smith
a0d292a0e8 Merge pull request #606 from dougbtv/bump-protobuf
Bump protobuf to v1.3.2
2021-02-18 14:56:41 -05:00
dougbtv
fe42962eb5 [deps][vendor] Updates gogo/protobuf 2021-02-18 11:29:20 -05:00
Kennelly, Martin
094dcbe2c2 Update Slack URL in README following renaming
Signed-off-by: Kennelly, Martin <martin.kennelly@intel.com>
2021-02-18 23:42:26 +09:00
dougbtv
8bbd3fdcf2 Change to sleep infinity & read and omit & wait on sleep n (e.g. not infinity) 2021-02-18 23:42:00 +09:00
Doug Smith
e728da78bc Merge pull request #611 from yoheiueda/s390x
Add s390x support
2021-02-18 09:39:40 -05:00
Yohei Ueda
9824963f79 Add s390x support
This PR updates the GitHub Actions workflows to build multus
container images for the IBM Z architecture (s390x), and also
adds daemonsets for s390x nodes.

entrypoint.sh uses python3 when it is available, since centos images
are not available for s390x, and python2 is not installed by default
on debian-based images.

Signed-off-by: Yohei Ueda <yohei@jp.ibm.com>
2021-02-18 01:40:33 -05:00
Doug Smith
cd9efbf703 Merge pull request #607 from amorenoz/bug/runtime
conf: do not modify global runtimeconfig when merging
2021-02-16 14:23:34 -05:00
Adrian Moreno
91e4efcd68 conf: make a copy of global RuntimeConfig on merge
When we call mergeRuntimeConfig, the global RuntimeConfig gets
overwritten with the result of the merging, thus affecting the
subsequent delegates.

Do not modify the global RuntimeConfig and instead make a copy
when merging it.

Also, if a value has been provided for CNIDeviceInfoFile in the
delegate's runtimeconfig, overwrite it to avoid possible name
colissions.

Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
2021-02-11 20:39:41 +01:00
dougbtv
63734fc026 Adds a trap for SIGTERM in entrypoint script 2021-02-10 04:57:07 +09:00
Doug Smith
bd90c262f6 Merge pull request #603 from s1061123/fix/graceperiod
Add grace period to missing deployment template
2021-01-29 08:40:48 -05:00
Tomofumi Hayashi
d093709f94 Add grace period to missing deployment template 2021-01-28 15:25:45 +09:00
dougbtv
f6df613c32 Pods in daemonset should have grace period reduced.
Otherwise, it can take a long time for the daemonset to stop, and there's no real reason it needs to hang out for a long time.
2021-01-28 15:22:27 +09:00
dougbtv
d4e8699825 The readme links need to be updated after directory change.
This updates the links to point to "doc/" instead of "docs/"
2020-12-17 05:23:14 +09:00
Nikhil Simha
62abb002dd Restructured multus to use Go standard project layout. 2020-12-16 22:38:10 +09:00
Doug Smith
bd9f2e9700 Merge pull request #595 from Mmduh-483/capabilities-entry-fail
Fix entrypoint fail to read capabilities in non chain plugin config
2020-12-14 08:28:41 -05:00
Mamduh Alassi
09190bce3e Fix entrypoint fail to read capabilities in non chain plugin config
entrypoint script fails with error when try reading capabilities in
non chain plugin config file when using "--multus-conf-file=auto"

Signed-off-by: Mamduh Alassi <mamduhala@mellanox.com>
2020-12-14 12:58:35 +02:00
Doug Smith
24386abae3 Merge pull request #553 from venuiyer/vi-multus
Allow the default-route to be empty
2020-12-10 09:45:53 -05:00
dougbtv
476c738c13 Allows specifying "global namespaces" when using namespace isolation 2020-12-10 23:40:18 +09:00
Doug Smith
e924318e18 Merge pull request #577 from Billy99/billy99-device-info
Add DeviceInfo Data to NetworkStatus Annotation
2020-12-01 13:50:26 -05:00
dougbtv
cfa0d64925 Multus should exit zero on DEL when cache file is missing and pod cannot be found.
Otherwise, this can cause a pod to not be fully deleted, its sandbox may remain as the DEL continues to be retried due to exiting non-zero every time.

See: https://bugzilla.redhat.com/show_bug.cgi?id=1900835
2020-11-25 22:11:17 +09:00
Billy McFall
ae797801b1 Remove Multus version of NetworkStatus
NetworkStatus was moved to network-attachment-definition-client in previous
merges and the Multus version of the structure was left behind, and not
being used.

Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-11-19 16:38:42 -05:00
Billy McFall
f826461b06 Add DeviceInfo Data to Network Status Annotation
Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-11-19 16:38:42 -05:00
Billy McFall
fb557ab20f Add CNIDeviceFile in RuntimeConfig
DPDeviceFile is used by Device Plugins to pass device data to CNIs, as defined
in the device-info-spec. The name of the DPDeviceFile is defined by the
device-info-spec as:
 <ResourceName>-<DeviceID>-device.json

If the DPDeviceFile exists, the NPWG implementation makes a copy of the file
and passes the name of the file to the delegate CNI via capabilityArgs as
CNIDeviceFile. If the DPDeviceFile does not exist, the filename is still
passed to the CNI. The CNI can create the file and popluate it if a device
is created within the CNI.

The name of the CNIDeviceFile is not defined by device-info-spec, but to
ensure the name does not clash it is formed by the following unique triplet:
[networkName, PodUUID, ifName]

k8snetworkplumbingwg/network-attachment-definition-client repo has utility
functions to abstract some of this functionality so it can be reused across
Device Plugins, NPWG implementations and CNIs.

Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-11-19 16:38:30 -05:00
Billy McFall
fd3f485e63 Add deviceID support in runtimeConfig
Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-11-19 16:34:29 -05:00
Billy McFall
665c43a2cd Merge RuntimeConf on delPlugins
RuntimeConfig depends on the delegate configuration. The Netconf runtime
information should be merged with the delegate config and the result
added to each command that is sent to the delegates. That was being done
for all commands except for delPlugins.

Do not export MergeCNIRuntimeConfig and call it from
CreateCNIRuntimeConf that now accepts a delegate ptr.

Call it from delPlugins for each delegate

Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-11-19 16:34:15 -05:00
Billy McFall
2cd91b6bad Update vendor for network-attachment-definition-client update
Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-11-19 16:06:54 -05:00
Billy McFall
e064679967 Point to latest network-attachment-definition-client
Move Multus to use latest network-attachment-definition-client repo which
contains the device-info-spec changes.

Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-11-19 16:06:54 -05:00
Doug Smith
6a23d65fea Merge pull request #583 from aojea/cmdadddel
clarify cmdAdd error
2020-11-19 10:45:04 -05:00
Antonio Ojea
1b58bed5a6 clarify cmdAdd error
it can happen that a CNI Add operation is canceled because the
runtime sends a CNI Del too fast, so the pod was already deleted
when multus try to set the network status annotations.

Since multus is an executable and can not persist state, it can't
track the CNI requests that are done in parallel, but it can
assume that, if it was able to get the pod from the API, and in a
subsequent request the pod no longer exists, it is because it was
deleted.

Signed-off-by: Antonio Ojea <aojea@redhat.com>
2020-11-19 10:30:12 +01:00
Doug Smith
957e26674a Merge pull request #573 from rhdojun/patch-1
Update quickstart.md
2020-11-12 09:35:38 -05:00
Doug Smith
36d3874c4c Merge pull request #571 from Billy99/billy99-k8s-rebase
Bump kubernetes dependencies to v0.18.3
2020-10-27 15:36:55 -04:00
rhdojun
4bfc2b14f2 Update quickstart.md
Felt like the line wasn't finished yet.
2020-10-27 13:12:34 +01:00
Billy McFall
f62529d4ff Update source for k8s bump to v0.18.3
Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-10-23 15:49:49 -04:00
Billy McFall
36b5edff29 Update vendor, go.mod and go.sum for k8s bump to v0.18.3
Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-10-23 10:55:54 -04:00
Billy McFall
c8739f64b9 Run 'go fmt' on code base.
Signed-off-by: Billy McFall <22157057+Billy99@users.noreply.github.com>
2020-10-23 10:43:23 -04:00
Doug Smith
bc95a19c5d Merge pull request #559 from s1061123/fix/remove-travis
Remove travis.yml because CI is now switched to github/actions
2020-09-15 20:06:16 -04:00
Tomofumi Hayashi
0e4b8be1e7 Remove travis.yml because CI is now switched to github/actions 2020-09-15 16:47:14 +09:00
Doug Smith
d13e06a5df Merge pull request #556 from s1061123/add-default-route-e2e
Add e2e tests for default-route annotation
2020-09-14 13:31:45 -04:00
Tomofumi Hayashi
ff3ee290c9 Add e2e tests for default-route annotation 2020-09-14 12:56:12 +09:00
Shane Utt
8cc60f7dd8 Add ARM64 to images
This PR takes the existing ARM64v8 build
and creates Daemonsets in the install images
to deploy it for ARM based K8s clusters.

Fixes #554

Signed-off-by: Shane Utt <shaneutt@linux.com>
2020-09-14 12:47:34 +09:00
venu iyer
292d9a9af3 Allow the default-route to be empty
In
https://docs.google.com/document/d/1Ny03h6IDVy_e_vmElOqR7UdTPAG_RNydhVE1Kx54kFQ,
section 4.1.2.1.9,

"
4.1.2.1.9 “default-route” Default route selection for a particular attachment

This optional key with value of type string-array is used to explicitly select
which attachment will receive the default route. The value of items in the
“default-route” array are intended to be gateways, e.g. an IP address to which
packets that do not match any other routes are sent. This key must only be set
on one item in the Network Attachment Selection Annotation. This list may be empty.
"

However en empty list will fail currently; this change accommodates an
empty "default-route" by retaining the default route added by the
delegate.

Signed-off-by: venugopal iyer <venugopali@nvidia.com>
2020-09-08 14:53:25 -07:00
Doug Smith
2a01aec396 Merge pull request #552 from intel/fix/e2e
Update e2e
2020-09-03 13:50:14 -04:00
Tomofumi Hayashi
17b24d5fd5 Update e2e
This change contains following:
 - update kind version to 0.8.1
 - update setup script due to update
 - add teardown script
 - update README
2020-09-04 02:20:35 +09:00
Doug Smith
2dab3225de Merge pull request #550 from s1061123/fix/image-url
Specify image repo URL explicitly
2020-08-31 08:36:41 -04:00
Tomofumi Hayashi
95cc1902c0 Specify image repo URL explicitly
This fix supplies domain for container image repo because
in some runtime configuration, 'docker.io' is not specified as
default container repository url.
2020-08-31 14:58:10 +09:00
Mamduh Alassi
36f2fd64e0 Use latest image version 3.6 in daemonset files 2020-08-20 22:35:19 +09:00
Doug Smith
be492e1778 Merge pull request #545 from clementnuss/master
Pull underlying capabilities up to 00-multus.conf
2020-08-06 09:37:02 -04:00
Clément Nussbaumer
4c271a57d5 Pull underlying capabilities up to 00-multus.conf
Using Python, we analyze the content of the master plugin configuration,
and we integrate any capabilities from the underlying chained plugins
declaration. We only pull enabled capabilties from the underlying
(chained) plugin definitions.

Fixes intel#544

Signed-off-by: <Clement.Nussbaumer@Swisscom.com>
2020-08-05 10:29:05 +02:00
Doug Smith
e15f97860f Merge pull request #541 from s1061123/fix/crd
Fix CRD yaml file for right explan information
2020-07-28 10:39:13 -04:00
Tomofumi Hayashi
78669323d2 Fix CRD yaml file for right explan information 2020-07-28 11:56:08 +09:00
1985 changed files with 311948 additions and 177308 deletions

24
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
on: [push, pull_request]
name: Build
jobs:
build:
strategy:
matrix:
go-version: [1.17.x, 1.18.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@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Build
env:
GOARCH: ${{ matrix.goarch }}
GOOS: ${{ matrix.goos }}
run: ./hack/build-go.sh

View File

@@ -1,64 +0,0 @@
name: Docker-build-push-latest
on:
push:
branches:
- master
jobs:
build:
name: Docker build
runs-on: ubuntu-latest
env:
DOCKER_CLI_EXPERIMENTAL: enabled
GO111MODULE: on
TARGET: amd64
REPOSITORY: nfvpe/multus
REPOSITORY_USER: nfvperobot
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build latest-amd64
run: docker build -t ${REPOSITORY}:latest-amd64 .
- name: Build latest-ppc64le
run: docker build -t ${REPOSITORY}:latest-ppc64le -f Dockerfile.ppc64le .
- name: Build latest-arm64v8
run: docker build -t ${REPOSITORY}:latest-arm64v8 -f Dockerfile.arm64 .
- name: Build latest-origin
run: docker build -t ${REPOSITORY}:latest-origin -f Dockerfile.openshift .
- name: Tag snapshot
run: |
docker tag ${REPOSITORY}:latest-amd64 ${REPOSITORY}:snapshot-amd64
docker tag ${REPOSITORY}:latest-ppc64le ${REPOSITORY}:snapshot-ppc64le
docker tag ${REPOSITORY}:latest-arm64v8 ${REPOSITORY}:snapshot-arm64v8
- name: Login to registry
if: github.repository == 'intel/multus-cni'
run: docker login -u ${REPOSITORY_USER} -p ${{ secrets.REPOSITORY_PASS }}
- name: Push latest/snapshot images
if: github.repository == 'intel/multus-cni'
run: |
docker push ${REPOSITORY}:latest-amd64
docker push ${REPOSITORY}:latest-ppc64le
docker push ${REPOSITORY}:latest-arm64v8
docker push ${REPOSITORY}:snapshot-amd64
docker push ${REPOSITORY}:snapshot-ppc64le
docker push ${REPOSITORY}:snapshot-arm64v8
- name: Create manifest for multi-arch images
if: github.repository == 'intel/multus-cni'
run: |
docker manifest create ${REPOSITORY}:snapshot ${REPOSITORY}:snapshot-amd64 ${REPOSITORY}:snapshot-ppc64le ${REPOSITORY}:snapshot-arm64v8
docker manifest annotate ${REPOSITORY}:snapshot ${REPOSITORY}:snapshot-amd64 --arch amd64
docker manifest annotate ${REPOSITORY}:snapshot ${REPOSITORY}:snapshot-ppc64le --arch ppc64le
docker manifest annotate ${REPOSITORY}:snapshot ${REPOSITORY}:snapshot-arm64v8 --arch arm64
docker manifest push ${REPOSITORY}:snapshot
docker manifest create ${REPOSITORY}:latest ${REPOSITORY}:latest-amd64 ${REPOSITORY}:latest-ppc64le ${REPOSITORY}:latest-arm64v8
docker manifest annotate ${REPOSITORY}:latest ${REPOSITORY}:latest-amd64 --arch amd64
docker manifest annotate ${REPOSITORY}:latest ${REPOSITORY}:latest-ppc64le --arch ppc64le
docker manifest annotate ${REPOSITORY}:latest ${REPOSITORY}:latest-arm64v8 --arch arm64
docker manifest push ${REPOSITORY}:latest

View File

@@ -1,75 +0,0 @@
name: Docker-build-push-stable
on:
push:
tags:
- v*
jobs:
build:
name: Docker build
runs-on: ubuntu-latest
env:
DOCKER_CLI_EXPERIMENTAL: enabled
GO111MODULE: on
TARGET: amd64
REPOSITORY: nfvpe/multus
REPOSITORY_USER: nfvperobot
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build latest-amd64
run: docker build -t ${REPOSITORY}:latest-amd64 .
- name: Build latest-ppc64le
run: docker build -t ${REPOSITORY}:latest-ppc64le -f Dockerfile.ppc64le .
- name: Build latest-arm64v8
run: docker build -t ${REPOSITORY}:latest-arm64v8 -f Dockerfile.arm64 .
- name: Build latest-origin
run: docker build -t ${REPOSITORY}:latest-origin -f Dockerfile.openshift .
- name: Tag stable
run: |
docker tag ${REPOSITORY}:latest-amd64 ${REPOSITORY}:stable-amd64
docker tag ${REPOSITORY}:latest-ppc64le ${REPOSITORY}:stable-ppc64le
docker tag ${REPOSITORY}:latest-arm64v8 ${REPOSITORY}:stable-arm64v8
docker tag ${REPOSITORY}:latest-amd64 ${REPOSITORY}:${GITHUB_REF##*/}-amd64
docker tag ${REPOSITORY}:latest-ppc64le ${REPOSITORY}:${GITHUB_REF##*/}-ppc64le
docker tag ${REPOSITORY}:latest-arm64v8 ${REPOSITORY}:${GITHUB_REF##*/}-arm64v8
- name: Login to registry
if: github.repository == 'intel/multus-cni'
run: docker login -u ${REPOSITORY_USER} -p ${{ secrets. REPOSITORY_PASS }}
- name: Push latest/snapshot images
if: github.repository == 'intel/multus-cni'
run: |
docker push ${REPOSITORY}:latest-amd64
docker push ${REPOSITORY}:latest-ppc64le
docker push ${REPOSITORY}:latest-arm64v8
docker push ${REPOSITORY}:stable-amd64
docker push ${REPOSITORY}:stable-ppc64le
docker push ${REPOSITORY}:stable-arm64v8
docker push ${REPOSITORY}:${GITHUB_REF##*/}-amd64
docker push ${REPOSITORY}:${GITHUB_REF##*/}-ppc64le
docker push ${REPOSITORY}:${GITHUB_REF##*/}-arm64v8
- name: Create manifest for multi-arch images
if: github.repository == 'intel/multus-cni'
run: |
docker manifest create ${REPOSITORY}:stable ${REPOSITORY}:stable-amd64 ${REPOSITORY}:stable-ppc64le ${REPOSITORY}:stable-arm64v8
docker manifest annotate ${REPOSITORY}:stable ${REPOSITORY}:stable-amd64 --arch amd64
docker manifest annotate ${REPOSITORY}:stable ${REPOSITORY}:stable-ppc64le --arch ppc64le
docker manifest annotate ${REPOSITORY}:stable ${REPOSITORY}:stable-arm64v8 --arch arm64
docker manifest push ${REPOSITORY}:stable
docker manifest create ${REPOSITORY}:latest ${REPOSITORY}:latest-amd64 ${REPOSITORY}:latest-ppc64le ${REPOSITORY}:latest-arm64v8
docker manifest annotate ${REPOSITORY}:latest ${REPOSITORY}:latest-amd64 --arch amd64
docker manifest annotate ${REPOSITORY}:latest ${REPOSITORY}:latest-ppc64le --arch ppc64le
docker manifest annotate ${REPOSITORY}:latest ${REPOSITORY}:latest-arm64v8 --arch arm64
docker manifest push ${REPOSITORY}:latest
docker manifest create ${REPOSITORY}:${GITHUB_REF##*/} ${REPOSITORY}:${GITHUB_REF##*/}-amd64 ${REPOSITORY}:${GITHUB_REF##*/}-ppc64le ${REPOSITORY}:${GITHUB_REF##*/}-arm64v8
docker manifest annotate ${REPOSITORY}:${GITHUB_REF##*/} ${REPOSITORY}:${GITHUB_REF##*/}-amd64 --arch amd64
docker manifest annotate ${REPOSITORY}:${GITHUB_REF##*/} ${REPOSITORY}:${GITHUB_REF##*/}-ppc64le --arch ppc64le
docker manifest annotate ${REPOSITORY}:${GITHUB_REF##*/} ${REPOSITORY}:${GITHUB_REF##*/}-arm64v8 --arch arm64
docker manifest push ${REPOSITORY}:${GITHUB_REF##*/}

View File

@@ -1,22 +0,0 @@
name: Docker-build
on: [pull_request]
jobs:
build:
name: Docker build
runs-on: ubuntu-latest
env:
DOCKER_CLI_EXPERIMENTAL: enabled
GO111MODULE: on
REPOSITORY: nfvpe/multus
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build latest-amd64
run: docker build -t ${REPOSITORY}:latest-amd64 .
- name: Build latest-ppc64le
run: docker build -t ${REPOSITORY}:latest-ppc64le -f Dockerfile.ppc64le .
- name: Build latest-origin
run: docker build -t ${REPOSITORY}:latest-origin -f Dockerfile.openshift .

View File

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

@@ -1,25 +0,0 @@
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: ppc64le
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

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

128
.github/workflows/image-build.yml vendored Normal file
View File

@@ -0,0 +1,128 @@
name: Image build
on: [pull_request]
jobs:
ep-build-amd64:
name: Image build/amd64 LEGACY entrypoint
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build container image
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: ghcr.io/${{ github.repository }}:ep-latest-amd64
file: images/Dockerfile
build-amd64:
name: Image build/amd64 daemonized alternative
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build container image
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: ghcr.io/${{ github.repository }}:latest-amd64
file: images/Dockerfile.thick
build-arm64:
name: Image build/arm64
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build container image
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: ghcr.io/${{ github.repository }}:latest-arm64
file: images/Dockerfile.arm64
build-arm32:
name: Image build/arm32
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build container image
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: ghcr.io/${{ github.repository }}:latest-arm32
file: images/Dockerfile.arm32
build-ppc64le:
name: Image build/ppc64le
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build container image
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: ghcr.io/${{ github.repository }}:latest-ppc64le
file: images/Dockerfile.ppc64le
build-s390:
name: Image build/s390x
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build container image
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: ghcr.io/${{ github.repository }}:latest-s390x
file: images/Dockerfile.s390x
build-origin:
name: Image build/origin
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build container image
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: ghcr.io/${{ github.repository }}:latest-origin
file: images/Dockerfile.openshift

240
.github/workflows/image-push-master.yml vendored Normal file
View File

@@ -0,0 +1,240 @@
name: Image push for master
on:
push:
branches:
- master
jobs:
push-amd64:
name: Image push/amd64
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest-amd64
ghcr.io/${{ github.repository }}:snapshot-amd64
file: images/Dockerfile
- name: Push container image for daemon based deployment
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:thick-amd64
file: images/Dockerfile.thick
push-arm64:
name: Image push/arm64
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest-arm64
ghcr.io/${{ github.repository }}:snapshot-arm64
file: images/Dockerfile.arm64
push-arm32:
name: Image push/arm32
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest-arm32
ghcr.io/${{ github.repository }}:snapshot-arm32
file: images/Dockerfile.arm32
push-ppc64le:
name: Image push/ppc64le
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest-ppc64le
ghcr.io/${{ github.repository }}:snapshot-ppc64le
file: images/Dockerfile.ppc64le
push-s390x:
name: Image push/s390x
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest-s390x
ghcr.io/${{ github.repository }}:snapshot-s390x
file: images/Dockerfile.s390x
push-origin:
name: Image push/origin
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest-origin
ghcr.io/${{ github.repository }}:snapshot-origin
file: images/Dockerfile.openshift
push-manifest:
needs: [push-amd64, push-arm64, push-ppc64le, push-s390x]
runs-on: ubuntu-latest
env:
REPOSITORY: ghcr.io/${{ github.repository }}
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create manifest for multi-arch images
if: github.repository_owner == 'k8snetworkplumbingwg'
run: |
# get artifacts from previous steps
docker pull ${{ env.REPOSITORY }}:thick-amd64
docker manifest create ${{ env.REPOSITORY }}:thick ${{ env.REPOSITORY }}:thick-amd64
docker manifest annotate ${{ env.REPOSITORY }}:thick ${{ env.REPOSITORY }}:thick-amd64 --arch amd64
docker manifest push ${{ env.REPOSITORY }}:thick
docker pull ${{ env.REPOSITORY }}:snapshot-amd64
docker pull ${{ env.REPOSITORY }}:snapshot-arm64
docker pull ${{ env.REPOSITORY }}:snapshot-arm32
docker pull ${{ env.REPOSITORY }}:snapshot-ppc64le
docker pull ${{ env.REPOSITORY }}:snapshot-s390x
docker pull ${{ env.REPOSITORY }}:latest-amd64
docker pull ${{ env.REPOSITORY }}:latest-arm64
docker pull ${{ env.REPOSITORY }}:latest-arm32
docker pull ${{ env.REPOSITORY }}:latest-ppc64le
docker pull ${{ env.REPOSITORY }}:latest-s390x
docker manifest create ${{ env.REPOSITORY }}:snapshot ${{ env.REPOSITORY }}:snapshot-amd64 ${{ env.REPOSITORY }}:snapshot-arm64 ${{ env.REPOSITORY }}:snapshot-arm32 ${{ env.REPOSITORY }}:snapshot-ppc64le ${{ env.REPOSITORY }}:snapshot-s390x
docker manifest annotate ${{ env.REPOSITORY }}:snapshot ${{ env.REPOSITORY }}:snapshot-amd64 --arch amd64
docker manifest annotate ${{ env.REPOSITORY }}:snapshot ${{ env.REPOSITORY }}:snapshot-arm64 --arch arm64
docker manifest annotate ${{ env.REPOSITORY }}:snapshot ${{ env.REPOSITORY }}:snapshot-arm32 --arch arm
docker manifest annotate ${{ env.REPOSITORY }}:snapshot ${{ env.REPOSITORY }}:snapshot-ppc64le --arch ppc64le
docker manifest annotate ${{ env.REPOSITORY }}:snapshot ${{ env.REPOSITORY }}:snapshot-s390x --arch s390x
docker manifest push ${{ env.REPOSITORY }}:snapshot
docker manifest create ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-amd64 ${{ env.REPOSITORY }}:latest-arm64 ${{ env.REPOSITORY }}:latest-arm32 ${{ env.REPOSITORY }}:latest-ppc64le ${{ env.REPOSITORY }}:latest-s390x
docker manifest annotate ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-amd64 --arch amd64
docker manifest annotate ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-arm64 --arch arm64
docker manifest annotate ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-arm32 --arch arm
docker manifest annotate ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-ppc64le --arch ppc64le
docker manifest annotate ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-s390x --arch s390x
docker manifest push ${{ env.REPOSITORY }}:latest

285
.github/workflows/image-push-release.yml vendored Normal file
View File

@@ -0,0 +1,285 @@
name: Image push release
on:
push:
tags:
- v*
jobs:
push-amd64:
name: Image push/amd64
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:stable-amd64
${{ steps.docker_meta.outputs.tags }}-amd64
file: images/Dockerfile
- name: Push container image for daemon based deployment
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:stable-thick-amd64
${{ steps.docker_meta.outputs.tags }}-thick-amd64
file: images/Dockerfile.thick
push-arm64:
name: Image push/arm64
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:stable-arm64
${{ steps.docker_meta.outputs.tags }}-arm64
file: images/Dockerfile.arm64
push-arm32:
name: Image push/arm32
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:stable-arm32
${{ steps.docker_meta.outputs.tags }}-arm32
file: images/Dockerfile.arm32
push-ppc64le:
name: Image push/ppc64le
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:stable-ppc64le
${{ steps.docker_meta.outputs.tags }}-ppc64le
file: images/Dockerfile.ppc64le
push-s390x:
name: Image push/s390x
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:stable-s390x
${{ steps.docker_meta.outputs.tags }}-s390x
file: images/Dockerfile.s390x
push-origin:
name: Image push/origin
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
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@v2
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:stable-origin
${{ steps.docker_meta.outputs.tags }}-origin
file: images/Dockerfile.openshift
push-manifest:
needs: [push-amd64, push-arm64, push-ppc64le, push-s390x]
runs-on: ubuntu-latest
env:
REPOSITORY: ghcr.io/${{ github.repository }}
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: ghcr.io/${{ github.repository }}
tag-latest: false
- name: Login to GitHub Container Registry
if: github.repository_owner == 'k8snetworkplumbingwg'
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create manifest for multi-arch images
if: github.repository_owner == 'k8snetworkplumbingwg'
run: |
# get artifacts from previous steps
docker pull ${{ steps.docker_meta.outputs.tags }}-amd64
docker pull ${{ steps.docker_meta.outputs.tags }}-arm64
docker pull ${{ steps.docker_meta.outputs.tags }}-arm32
docker pull ${{ steps.docker_meta.outputs.tags }}-ppc64le
docker pull ${{ steps.docker_meta.outputs.tags }}-s390x
docker manifest create ${{ steps.docker_meta.outputs.tags }} ${{ steps.docker_meta.outputs.tags }}-amd64 ${{ steps.docker_meta.outputs.tags }}-arm64 ${{ steps.docker_meta.outputs.tags }}-arm32 ${{ steps.docker_meta.outputs.tags }}-ppc64le ${{ steps.docker_meta.outputs.tags }}-s390x
docker manifest annotate ${{ steps.docker_meta.outputs.tags }} ${{ steps.docker_meta.outputs.tags }}-amd64 --arch amd64
docker manifest annotate ${{ steps.docker_meta.outputs.tags }} ${{ steps.docker_meta.outputs.tags }}-arm64 --arch arm64
docker manifest annotate ${{ steps.docker_meta.outputs.tags }} ${{ steps.docker_meta.outputs.tags }}-arm32 --arch arm
docker manifest annotate ${{ steps.docker_meta.outputs.tags }} ${{ steps.docker_meta.outputs.tags }}-ppc64le --arch ppc64le
docker manifest annotate ${{ steps.docker_meta.outputs.tags }} ${{ steps.docker_meta.outputs.tags }}-s390x --arch s390x
docker manifest push ${{ steps.docker_meta.outputs.tags }}
docker pull ${{ env.REPOSITORY }}:stable-amd64
docker pull ${{ env.REPOSITORY }}:stable-arm64
docker pull ${{ env.REPOSITORY }}:stable-arm32
docker pull ${{ env.REPOSITORY }}:stable-ppc64le
docker pull ${{ env.REPOSITORY }}:stable-s390x
docker manifest create ${{ env.REPOSITORY }}:stable ${{ env.REPOSITORY }}:stable-amd64 ${{ env.REPOSITORY }}:stable-arm64 ${{ env.REPOSITORY }}:stable-arm32 ${{ env.REPOSITORY }}:stable-ppc64le ${{ env.REPOSITORY }}:stable-s390x
docker manifest annotate ${{ env.REPOSITORY }}:stable ${{ env.REPOSITORY }}:stable-amd64 --arch amd64
docker manifest annotate ${{ env.REPOSITORY }}:stable ${{ env.REPOSITORY }}:stable-arm64 --arch arm64
docker manifest annotate ${{ env.REPOSITORY }}:stable ${{ env.REPOSITORY }}:stable-arm32 --arch arm
docker manifest annotate ${{ env.REPOSITORY }}:stable ${{ env.REPOSITORY }}:stable-ppc64le --arch ppc64le
docker manifest annotate ${{ env.REPOSITORY }}:stable ${{ env.REPOSITORY }}:stable-s390x --arch s390x
docker manifest push ${{ env.REPOSITORY }}:stable

View File

@@ -1,9 +1,7 @@
name: e2e-kind
on: [push, pull_request]
jobs:
build:
name: Build and test
e2e-kind:
runs-on: ubuntu-latest
if: >
(( github.event.pull_request.head.repo.owner.login != github.event.pull_request.base.repo.owner.login ) &&
@@ -16,7 +14,7 @@ jobs:
run: docker run -d --restart=always -p "5000:5000" --name "kind-registry" registry:2
- name: Build latest-amd64
run: docker build -t localhost:5000/multus:e2e .
run: docker build -t localhost:5000/multus:e2e -f images/Dockerfile.thick .
- name: Push to local registry
run: docker push localhost:5000/multus:e2e
@@ -29,10 +27,22 @@ jobs:
working-directory: ./e2e
run: ./setup_cluster.sh
- name: Test simple pod
working-directory: ./e2e
run: ./test-simple-pod.sh
- name: Test macvlan1
working-directory: ./e2e
run: ./test-simple-macvlan1.sh
- name: Test static pod
working-directory: ./e2e
run: ./test-static-pod.sh
- name: Test default route1
working-directory: ./e2e
run: ./test-default-route1.sh
- name: cleanup cluster and registry
run: |
kind delete cluster

46
.github/workflows/legacy-kind-e2e.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: e2e-kind legacy installation with entrypoint script
on: [push, pull_request]
jobs:
e2e-kind:
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@v2
- name: Setup registry
run: docker run -d --restart=always -p "5000:5000" --name "kind-registry" registry:2
- name: Build latest-amd64
run: docker build -t localhost:5000/multus:e2e -f images/Dockerfile .
- 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: Setup cluster
working-directory: ./e2e
run: MULTUS_MANIFEST=legacy-multus-daemonset.yml ./setup_cluster.sh
- name: Test simple pod
working-directory: ./e2e
run: ./test-simple-pod.sh
- name: Test macvlan1
working-directory: ./e2e
run: ./test-simple-macvlan1.sh
- name: Test default route1
working-directory: ./e2e
run: ./test-default-route1.sh
- name: cleanup cluster and registry
run: |
kind delete cluster
docker kill kind-registry
docker rm kind-registry

View File

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

15
.github/workflows/stale-issues-prs.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v3
with:
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
stale-pr-message: 'This pull request is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
days-before-stale: 90
days-before-close: 7

48
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.17.x, 1.18.x]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- 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

1
.gitignore vendored
View File

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

View File

@@ -9,7 +9,7 @@ builds:
-
env:
- CGO_ENABLED=0
main: ./multus/
main: ./cmd/
goos:
- linux
goarch:
@@ -17,6 +17,9 @@ builds:
- amd64
- arm
- arm64
- s390x
ldflags:
- -X gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/multus.version={{ .Tag }} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/multus.commit={{ .Commit }} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/multus.date={{ .Date }}
archives:
- wrap_in_directory: true
checksum:

View File

@@ -41,10 +41,10 @@ before_script:
# - gocyclo -over 15 ./multus
script:
- GOARCH="${TARGET}" ./build
- GOARCH="${TARGET}" ./hack/build-go.sh
- |
if [ "${TARGET}" == "amd64" ]; then
sudo env PATH=${PATH} ./test.sh
sudo env PATH=${PATH} ./scripts/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 .

View File

@@ -1,20 +0,0 @@
# 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-1.13.10-0.el7.x86_64"
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"]

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
# Multus-CNI
![multus-cni Logo](https://github.com/intel/multus-cni/blob/master/doc/images/Multus.png)
![multus-cni Logo](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/images/Multus.png)
[![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)
[![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)
Multus CNI enables attaching multiple network interfaces to pods in Kubernetes.
@@ -18,34 +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](doc/images/multus-pod-image.svg)
![multus-pod-image](docs/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 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).
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 plugins, refer to our [quick-start guide](docs/quickstart.md).
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:
```
$ cat ./images/multus-daemonset.yml | kubectl apply -f -
cat ./deployments/multus-daemonset-thick-plugin.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](doc/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](docs/quickstart.md)
## Additional installation Options
- Install via daemonset using the quick-start guide, above.
- Download binaries from [release page](https://github.com/intel/multus-cni/releases)
- Download binaries from [release page](https://github.com/k8snetworkplumbingwg/multus-cni/releases)
- By Docker image from [Docker Hub](https://hub.docker.com/r/nfvpe/multus/tags/)
- Or, roll-your-own and build from source
- See [Development](doc/development.md)
- See [Development](docs/development.md)
## Comprehensive Documentation
- [How to use](doc/how-to-use.md)
- [Configuration](doc/configuration.md)
- [Development](doc/development.md)
- [How to use](docs/how-to-use.md)
- [Configuration](docs/configuration.md)
- [Development](docs/development.md)
## Contact Us
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.
For any questions about Multus CNI, feel free to ask a question in #general in the [NPWG Slack](https://npwg-team.slack.com/), or open up a GitHub issue. Request an invite to NPWG slack [here](https://intel-corp.herokuapp.com/).

View File

@@ -0,0 +1,148 @@
// 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 generates kubeconfig file for multus based on service account
package main
import (
"encoding/base64"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
)
const userRWPermission = 0600
const (
cniConfigDirVarName = "cni-config-dir"
k8sCAFilePathVarName = "kube-ca-file"
k8sServiceHostVarName = "k8s-service-host"
k8sServicePortVarName = "k8s-service-port"
serviceAccountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
skipTLSVerifyVarName = "skip-tls-verify"
)
const (
defaultCniConfigDir = "/host/etc/cni/net.d"
defaultK8sCAFilePath = ""
defaultK8sServiceHost = ""
defaultK8sServicePort = 0
defaultSkipTLSValue = false
)
func main() {
k8sServiceHost := flag.String(k8sServiceHostVarName, defaultK8sServiceHost, "Cluster IP of the kubernetes service")
k8sServicePort := flag.Int(k8sServicePortVarName, defaultK8sServicePort, "Port of the kubernetes service")
skipTLSVerify := flag.Bool(skipTLSVerifyVarName, defaultSkipTLSValue, "Should TLS verification be skipped")
kubeCAFilePath := flag.String(k8sCAFilePathVarName, defaultK8sCAFilePath, "Override the default kubernetes CA file path")
cniConfigDir := flag.String(cniConfigDirVarName, defaultCniConfigDir, "CNI config dir")
flag.Parse()
if *k8sServiceHost == defaultK8sServiceHost {
logInvalidArg("must provide the k8s service cluster port")
}
if *k8sServicePort == defaultK8sServicePort {
logInvalidArg("must provide the k8s service cluster port")
}
if *kubeCAFilePath == defaultK8sServiceHost {
*kubeCAFilePath = serviceAccountPath + "/ca.crt"
}
tlsCfg := "insecure-skip-tls-verify: true"
if !*skipTLSVerify {
kubeCAFileContents, err := k8sCAFileContentsBase64(*kubeCAFilePath)
if err != nil {
logError("failed grabbing CA file: %w", err)
}
tlsCfg = "certificate-authority-data: " + kubeCAFileContents
}
multusConfigDir := *cniConfigDir + "/multus.d/"
if err := prepareCNIConfigDir(multusConfigDir); err != nil {
logError("failed to create CNI config dir: %w", err)
}
kubeConfigFilePath := *cniConfigDir + "/multus.d/multus.kubeconfig"
serviceAccountToken, err := k8sKubeConfigToken(serviceAccountPath + "/token")
if err != nil {
logError("failed grabbing k8s token: %w", err)
}
if err := writeKubeConfig(kubeConfigFilePath, "https", *k8sServiceHost, *k8sServicePort, tlsCfg, serviceAccountToken); err != nil {
logError("failed generating kubeconfig: %w", err)
}
}
func k8sCAFileContentsBase64(pathCAFile string) (string, error) {
data, err := ioutil.ReadFile(pathCAFile)
if err != nil {
return "", fmt.Errorf("failed reading file %s: %w", pathCAFile, err)
}
return strings.Trim(base64.StdEncoding.EncodeToString(data), "\n"), nil
}
func k8sKubeConfigToken(tokenPath string) (string, error) {
data, err := ioutil.ReadFile(tokenPath)
if err != nil {
return "", fmt.Errorf("failed reading file %s: %w", tokenPath, err)
}
return string(data), nil
}
func writeKubeConfig(outputPath string, protocol string, k8sServiceIP string, k8sServicePort int, tlsConfig string, serviceAccountToken string) error {
kubeConfigTemplate := `
# Kubeconfig file for Multus CNI plugin.
apiVersion: v1
kind: Config
clusters:
- name: local
cluster:
server: %s://[%s]:%d
%s
users:
- name: multus
user:
token: "%s"
contexts:
- name: multus-context
context:
cluster: local
user: multus
current-context: multus-context
`
kubeconfig := fmt.Sprintf(kubeConfigTemplate, protocol, k8sServiceIP, k8sServicePort, tlsConfig, serviceAccountToken)
logInfo("Generated KubeConfig saved to %s: \n%s", outputPath, kubeconfig)
return ioutil.WriteFile(outputPath, []byte(kubeconfig), userRWPermission)
}
func prepareCNIConfigDir(cniConfigDirPath string) error {
return os.MkdirAll(cniConfigDirPath, userRWPermission)
}
func logInvalidArg(format string, values ...interface{}) {
log.Printf("ERROR: %s", fmt.Errorf(format, values...).Error())
flag.PrintDefaults()
os.Exit(1)
}
func logError(format string, values ...interface{}) {
log.Printf("ERROR: %s", fmt.Errorf(format, values...).Error())
os.Exit(1)
}
func logInfo(format string, values ...interface{}) {
log.Printf("INFO: %s", fmt.Sprintf(format, values...))
}

253
cmd/controller/main.go Normal file
View File

@@ -0,0 +1,253 @@
// 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 daemonized entrypoint process. which watches master config
// and generate multus CNI config
package main
import (
"flag"
"fmt"
"io"
"os"
"path/filepath"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/config"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/multus"
)
const (
multusPluginName = "multus"
multusConfigFileName = "00-multus.conf"
)
const (
defaultCniConfigDir = "/etc/cni/net.d"
defaultMultusAdditionalBinDir = ""
defaultMultusCNIVersion = ""
defaultMultusConfigFile = "auto"
defaultMultusGlobalNamespaces = ""
defaultMultusKubeconfigPath = "/etc/cni/net.d/multus.d/multus.kubeconfig"
defaultMultusLogFile = ""
defaultMultusLogMaxSize = 100 // megabytes
defaultMultusLogMaxAge = 5 // days
defaultMultusLogMaxBackups = 5
defaultMultusLogCompress = true
defaultMultusLogLevel = ""
defaultMultusLogToStdErr = false
defaultMultusMasterCNIFile = ""
defaultMultusNamespaceIsolation = false
defaultMultusReadinessIndicatorFile = ""
)
const (
cniConfigDirVarName = "cni-config-dir"
multusAdditionalBinDirVarName = "additional-bin-dir"
multusAutoconfigDirVarName = "multus-autoconfig-dir"
multusCNIVersion = "cni-version"
multusConfigFileVarName = "multus-conf-file"
multusGlobalNamespaces = "global-namespaces"
multusLogFile = "multus-log-file"
multusLogMaxSize = "multus-log-max-size"
multusLogMaxAge = "multus-log-max-age"
multusLogMaxBackups = "multus-log-max-backups"
multusLogCompress = "multus-log-compress"
multusLogLevel = "multus-log-level"
multusLogToStdErr = "multus-log-to-stderr"
multusKubeconfigPath = "multus-kubeconfig-file-host"
multusMasterCNIFileVarName = "multus-master-cni-file"
multusNamespaceIsolation = "namespace-isolation"
multusReadinessIndicatorFile = "readiness-indicator-file"
)
func main() {
versionOpt := false
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
cniConfigDir := flag.String(cniConfigDirVarName, defaultCniConfigDir, "CNI config dir")
multusConfigFile := flag.String(multusConfigFileVarName, defaultMultusConfigFile, "The multus configuration file to use. By default, a new configuration is generated.")
multusMasterCni := flag.String(multusMasterCNIFileVarName, defaultMultusMasterCNIFile, "The relative name of the configuration file of the cluster primary CNI.")
multusAutoconfigDir := flag.String(multusAutoconfigDirVarName, *cniConfigDir, "The directory path for the generated multus configuration.")
namespaceIsolation := flag.Bool(multusNamespaceIsolation, defaultMultusNamespaceIsolation, "If the network resources are only available within their defined namespaces.")
globalNamespaces := flag.String(multusGlobalNamespaces, defaultMultusGlobalNamespaces, "Comma-separated list of namespaces which can be referred to globally when namespace isolation is enabled.")
logToStdErr := flag.Bool(multusLogToStdErr, defaultMultusLogToStdErr, "If the multus logs are also to be echoed to stderr.")
logLevel := flag.String(multusLogLevel, defaultMultusLogLevel, "One of: debug/verbose/error/panic. Used only with --multus-conf-file=auto.")
logFile := flag.String(multusLogFile, defaultMultusLogFile, "Path where to multus will log. Used only with --multus-conf-file=auto.")
logMaxSize := flag.Int(multusLogMaxSize, defaultMultusLogMaxSize, "the maximum size in megabytes of the log file before it gets rotated")
logMaxAge := flag.Int(multusLogMaxAge, defaultMultusLogMaxAge, "the maximum number of days to retain old log files in their filename")
logMaxBackups := flag.Int(multusLogMaxBackups, defaultMultusLogMaxBackups, "the maximum number of old log files to retain")
logCompress := flag.Bool(multusLogCompress, defaultMultusLogCompress, "compress determines if the rotated log files should be compressed using gzip")
cniVersion := flag.String(multusCNIVersion, defaultMultusCNIVersion, "Allows you to specify CNI spec version. Used only with --multus-conf-file=auto.")
additionalBinDir := flag.String(multusAdditionalBinDirVarName, defaultMultusAdditionalBinDir, "Additional binary directory to specify in the configurations. Used only with --multus-conf-file=auto.")
readinessIndicator := flag.String(multusReadinessIndicatorFile, defaultMultusReadinessIndicatorFile, "Which file should be used as the readiness indicator. Used only with --multus-conf-file=auto.")
multusKubeconfig := flag.String(multusKubeconfigPath, defaultMultusKubeconfigPath, "The path to the kubeconfig")
overrideNetworkName := flag.Bool("override-network-name", false, "Used when we need overrides the name of the multus configuration with the name of the delegated primary CNI")
flag.BoolVar(&versionOpt, "version", false, "Show application version")
flag.BoolVar(&versionOpt, "v", false, "Show application version")
flag.Parse()
if versionOpt == true {
fmt.Printf("%s\n", multus.PrintVersionString())
return
}
if *logToStdErr {
logging.SetLogStderr(*logToStdErr)
}
if *logFile != defaultMultusLogFile {
logging.SetLogFile(*logFile)
}
if *logLevel != defaultMultusLogLevel {
logging.SetLogLevel(*logLevel)
}
if *multusConfigFile == defaultMultusConfigFile {
if *cniVersion == defaultMultusCNIVersion {
_ = logging.Errorf("the CNI version is a mandatory parameter when the '-multus-config-file=auto' option is used")
}
var configurationOptions []config.Option
if *namespaceIsolation {
configurationOptions = append(
configurationOptions, config.WithNamespaceIsolation())
}
if *globalNamespaces != defaultMultusGlobalNamespaces {
configurationOptions = append(
configurationOptions, config.WithGlobalNamespaces(*globalNamespaces))
}
if *logToStdErr != defaultMultusLogToStdErr {
configurationOptions = append(
configurationOptions, config.WithLogToStdErr())
}
if *logLevel != defaultMultusLogLevel {
configurationOptions = append(
configurationOptions, config.WithLogLevel(*logLevel))
}
if *logFile != defaultMultusLogFile {
configurationOptions = append(
configurationOptions, config.WithLogFile(*logFile))
}
if *additionalBinDir != defaultMultusAdditionalBinDir {
configurationOptions = append(
configurationOptions, config.WithAdditionalBinaryFileDir(*additionalBinDir))
}
if *readinessIndicator != defaultMultusReadinessIndicatorFile {
configurationOptions = append(
configurationOptions, config.WithReadinessFileIndicator(*readinessIndicator))
}
// logOptions
var logOptionFuncs []config.LogOptionFunc
if *logMaxAge != defaultMultusLogMaxAge {
logOptionFuncs = append(logOptionFuncs, config.WithLogMaxAge(logMaxAge))
}
if *logMaxSize != defaultMultusLogMaxSize {
logOptionFuncs = append(logOptionFuncs, config.WithLogMaxSize(logMaxSize))
}
if *logMaxBackups != defaultMultusLogMaxBackups {
logOptionFuncs = append(logOptionFuncs, config.WithLogMaxBackups(logMaxBackups))
}
if *logCompress != defaultMultusLogCompress {
logOptionFuncs = append(logOptionFuncs, config.WithLogCompress(logCompress))
}
if len(logOptionFuncs) > 0 {
logOptions := &config.LogOptions{}
config.MutateLogOptions(logOptions, logOptionFuncs...)
configurationOptions = append(configurationOptions, config.WithLogOptions(logOptions))
}
multusConfig, err := config.NewMultusConfig(multusPluginName, *cniVersion, *multusKubeconfig, configurationOptions...)
if err != nil {
_ = logging.Errorf("Failed to create multus config: %v", err)
os.Exit(3)
}
var configManager *config.Manager
if *multusMasterCni == "" {
configManager, err = config.NewManager(*multusConfig, *multusAutoconfigDir)
} else {
configManager, err = config.NewManagerWithExplicitPrimaryCNIPlugin(
*multusConfig, *multusAutoconfigDir, *multusMasterCni)
}
if err != nil {
_ = logging.Errorf("failed to create the configuration manager for the primary CNI plugin: %v", err)
os.Exit(2)
}
if *overrideNetworkName {
if err := configManager.OverrideNetworkName(); err != nil {
_ = logging.Errorf("could not override the network name: %v", err)
}
}
generatedMultusConfig, err := configManager.GenerateConfig()
if err != nil {
_ = logging.Errorf("failed to generated the multus configuration: %v", err)
}
logging.Verbosef("Generated MultusCNI config: %s", generatedMultusConfig)
if err := configManager.PersistMultusConfig(generatedMultusConfig); err != nil {
_ = logging.Errorf("failed to persist the multus configuration: %v", err)
}
configWatcherDoneChannel := make(chan struct{})
go func(stopChannel chan struct{}, doneChannel chan struct{}) {
defer func() {
stopChannel <- struct{}{}
}()
if err := configManager.MonitorDelegatedPluginConfiguration(stopChannel, configWatcherDoneChannel); err != nil {
_ = logging.Errorf("error watching file: %v", err)
}
}(make(chan struct{}), configWatcherDoneChannel)
<-configWatcherDoneChannel
} else {
if err := copyUserProvidedConfig(*multusConfigFile, *cniConfigDir); err != nil {
logging.Errorf("failed to copy the user provided configuration %s: %v", *multusConfigFile, err)
}
}
}
func copyUserProvidedConfig(multusConfigPath string, cniConfigDir string) error {
srcFile, err := os.Open(multusConfigPath)
if err != nil {
return fmt.Errorf("failed to open (READ only) file %s: %w", multusConfigPath, 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(srcFile, dstFile)
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
}

58
cmd/main.go Normal file
View File

@@ -0,0 +1,58 @@
// Copyright (c) 2017 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 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.v3/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 == true {
fmt.Printf("%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

@@ -154,7 +154,7 @@ spec:
containers:
- name: kube-multus
# crio support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest
image: nfvpe/multus:v3.6
command: ["/entrypoint.sh"]
args:
- "--cni-bin-dir=/host/usr/libexec/cni"

View File

@@ -134,7 +134,7 @@ spec:
serviceAccountName: multus
containers:
- name: kube-multus
image: nfvpe/multus:v3.4
image: nfvpe/multus:v3.6
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
@@ -229,4 +229,4 @@ spec:
name: multus-cni-config
items:
- key: cni-conf.json
path: 70-multus.conf
path: 70-multus.conf

View File

@@ -153,7 +153,7 @@ spec:
serviceAccountName: multus
containers:
- name: kube-multus
image: nfvpe/multus:v3.4.1
image: nfvpe/multus:v3.6
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"

View File

@@ -23,6 +23,19 @@ spec:
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
@@ -130,7 +143,7 @@ data:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds-amd64
name: kube-multus-ds
namespace: kube-system
labels:
tier: node
@@ -150,16 +163,16 @@ spec:
name: multus
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/arch: amd64
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: nfvpe/multus:latest
image: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
command: ["/entrypoint.sh"]
args:
- "--cni-version=0.3.1"
@@ -180,12 +193,14 @@ spec:
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:
@@ -202,72 +217,3 @@ spec:
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
# crio support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest-ppc64le
command: ["/entrypoint.sh"]
args:
- "--cni-version=0.3.1"
- "--cni-bin-dir=/host/usr/libexec/cni"
- "--multus-conf-file=auto"
- "--restart-crio=true"
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/usr/libexec/cni
- name: multus-cfg
mountPath: /tmp/multus-conf
volumes:
- 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

@@ -116,7 +116,7 @@ data:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds-amd64
name: kube-multus-ds
namespace: kube-system
labels:
tier: node
@@ -136,15 +136,15 @@ spec:
name: multus
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/arch: amd64
tolerations:
- operator: Exists
effect: NoSchedule
- operator: Exists
effect: NoExecute
serviceAccountName: multus
containers:
- name: kube-multus
image: nfvpe/multus:v3.4
image: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
@@ -179,71 +179,3 @@ spec:
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"
- "--cni-bin-dir=/host/home/kubernetes/bin"
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/home/kubernetes/bin
- name: multus-cfg
mountPath: /tmp/multus-conf
volumes:
- name: cni
hostPath:
path: /etc/cni/net.d
- name: cnibin
hostPath:
path: /home/kubernetes/bin
- name: multus-cfg
configMap:
name: multus-cni-config
items:
- key: cni-conf.json
path: 70-multus.conf

View File

@@ -0,0 +1,189 @@
---
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
---
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:v3.9.2-thick-amd64
command: [ "/usr/src/multus-cni/bin/multus-daemon" ]
args:
- "-cni-version=0.3.1"
- "-cni-config-dir=/host/etc/cni/net.d"
- "-multus-autoconfig-dir=/host/etc/cni/net.d"
- "-multus-log-to-stderr=true"
- "-multus-log-level=verbose"
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
initContainers:
- name: install-multus-binary
image: ghcr.io/k8snetworkplumbingwg/multus-cni:v3.9.2-thick-amd64
command:
- "cp"
- "/usr/src/multus-cni/bin/multus"
- "/host/opt/cni/bin/multus"
resources:
requests:
cpu: "10m"
memory: "15Mi"
securityContext:
privileged: true
volumeMounts:
- name: cnibin
mountPath: /host/opt/cni/bin
mountPropagation: Bidirectional
- name: generate-kubeconfig
image: ghcr.io/k8snetworkplumbingwg/multus-cni:v3.9.2-thick-amd64
command:
- "/usr/src/multus-cni/bin/generate-kubeconfig"
args:
- "-k8s-service-host=$(KUBERNETES_SERVICE_HOST)"
- "-k8s-service-port=$(KUBERNETES_SERVICE_PORT)"
resources:
requests:
cpu: "10m"
memory: "15Mi"
securityContext:
privileged: true
volumeMounts:
- name: cni
mountPath: /host/etc/cni/net.d
mountPropagation: Bidirectional
terminationGracePeriodSeconds: 10
volumes:
- name: cni
hostPath:
path: /etc/cni/net.d
- name: cnibin
hostPath:
path: /opt/cni/bin

View File

@@ -0,0 +1,225 @@
---
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:stable
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
initContainers:
- name: install-multus-binary
image: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
command:
- "cp"
- "/usr/src/multus-cni/bin/multus"
- "/host/opt/cni/bin/multus"
resources:
requests:
cpu: "10m"
memory: "15Mi"
securityContext:
privileged: true
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

View File

@@ -5,6 +5,7 @@ Following is the example of multus config file, in `/etc/cni/net.d/`.
```
{
"cniVersion": "0.3.1",
"name": "node-cni-network",
"type": "multus",
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
@@ -13,6 +14,12 @@ Following is the example of multus config file, in `/etc/cni/net.d/`.
"binDir": "/opt/cni/bin",
"logFile": "/var/log/multus.log",
"logLevel": "debug",
"logOptions": {
"maxAge": 5,
"maxSize": 100,
"maxBackups": 5,
"compress": true
},
"capabilities": {
"portMappings": true
},
@@ -39,17 +46,19 @@ Following is the example of multus config file, in `/etc/cni/net.d/`.
* `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
* `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` (bool, optional): Enable or disable logging to `STDERR`. Defaults to true.
* `logFile` (string, optional): file path for log file. multus puts log in given file
* `logLevel` (string, optional): logging level ("debug", "error", "verbose", or "panic")
* `logOptions` (object, optional): logging option, More detailed log configuration
* `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
* `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`: The path to a file whose existence 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
* `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 extension, .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 extension, .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
@@ -59,7 +68,7 @@ User should chose following parameters combination (`clusterNetwork`+`defaultNet
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. CNI json config file in `confDir`. Given name should be without extension, like .conf/.conflist. (e.g. "test" for "test.conf"). The given name for `clusterNetwork` should match the value for `name` key in the config file (e.g. `"name": "test"` in "test.conf" when `"clusterNetwork": "test"`)
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
@@ -75,7 +84,7 @@ In this manner, you may prevent pods from crash looping, and instead wait for th
Only one option is necessary to configure this functionality:
* `readinessindicatorfile`: The path to a file whose existance denotes that the default network is ready.
* `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.
@@ -84,7 +93,15 @@ Only one option is necessary to configure this functionality:
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.
#### 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
@@ -93,7 +110,7 @@ Optionally, you may have Multus log to a file on the filesystem. This file will
For example in your CNI configuration, you may set:
```
"LogFile": "/var/log/multus.log",
"logFile": "/var/log/multus.log",
```
#### Logging Level
@@ -110,7 +127,27 @@ The available logging level values, in decreasing order of verbosity are:
You may configure the logging level by using the `LogLevel` option in your CNI configuration. For example:
```
"LogLevel": "debug",
"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
@@ -119,6 +156,8 @@ The functionality provided by the `namespaceIsolation` configuration option enab
**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.
@@ -215,7 +254,7 @@ 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
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`.
@@ -253,6 +292,16 @@ 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.

View File

@@ -6,7 +6,7 @@ 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/intel/multus-cni.v3
go get gopkg.in/k8snetworkplumbingwg/multus-cni.v3
```
@@ -22,12 +22,12 @@ If an issue is closed that you don't feel is sufficiently resolved, please feel
## How do I build multus-cni?
You can use the built in `./build` script!
You can use the built in `./hack/build-go.sh` script!
```
git clone https://github.com/intel/multus-cni.git
git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
cd multus-cni
./build
./hack/build-go.sh
```
## How do I run CI tests?
@@ -35,18 +35,18 @@ cd multus-cni
Multus has go unit tests (based on ginkgo framework).The following commands drive CI tests manually in your environment:
```
sudo ./test.sh
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 begining of functions
* 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)
## CI Introduction
## Multus release schedule
TBD
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.

View File

@@ -13,15 +13,15 @@ Generally we recommend two options: Manually place a Multus binary in your `/opt
*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).
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
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 -
cat ./deployments/multus-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.
@@ -34,8 +34,8 @@ You put CNI config file in `/etc/cni/net.d`. Kubernetes CNI runtime uses the alp
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
mkdir -p /etc/cni/net.d
cat >/etc/cni/net.d/00-multus.conf <<EOF
{
"name": "multus-cni-network",
"type": "multus",
@@ -72,7 +72,7 @@ Create resources for multus to access CRD objects as following command:
```
# Execute following commands at Kubernetes master
$ cat <<EOF | kubectl create -f -
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ServiceAccount
metadata:
@@ -119,13 +119,13 @@ 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
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
@@ -151,7 +151,7 @@ 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 ...
scp /etc/cni/net.d/multus.d/multus.kubeconfig ...
```
### Setup CRDs (daemonset automatically does)
@@ -162,7 +162,7 @@ Create CRD definition in Kubernetes as following command at master node:
```
# Execute following command at Kubernetes master
$ cat <<EOF | kubectl create -f -
cat <<EOF | kubectl create -f -
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
@@ -200,7 +200,7 @@ Following command creates NetworkAttachmentDefinition. CNI config is in `config:
```
# Execute following command at Kubernetes master
$ cat <<EOF | kubectl create -f -
cat <<EOF | kubectl create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
@@ -232,7 +232,7 @@ If NetworkAttachmentDefinition has no spec, multus find a file in defaultConfDir
```
# Execute following command at Kubernetes master
$ cat <<EOF | kubectl create -f -
cat <<EOF | kubectl create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
@@ -242,7 +242,7 @@ EOF
```
# Execute following commands at all Kubernetes nodes (i.e. master and minions)
$ cat <<EOF > /etc/cni/multus/net.d/macvlan2.conf
cat <<EOF > /etc/cni/multus/net.d/macvlan2.conf
{
"cniVersion": "0.3.0",
"type": "macvlan",
@@ -264,11 +264,11 @@ $ cat <<EOF > /etc/cni/multus/net.d/macvlan2.conf
### Run pod with network annotation
#### Lauch pod with text annotation
#### Launch pod with text annotation
```
# Execute following command at Kubernetes master
$ cat <<EOF | kubectl create -f -
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -284,13 +284,13 @@ spec:
EOF
```
#### Lauch pod with text annotation for NetworkAttachmentDefinition in different namespace
#### 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 -
cat <<EOF | kubectl create -f -
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
@@ -314,7 +314,7 @@ spec:
}
}'
EOF
$ cat <<EOF | kubectl create -f -
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -330,13 +330,13 @@ spec:
EOF
```
#### Lauch pod with text annotation with interface name
#### 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 -
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -352,11 +352,11 @@ spec:
EOF
```
#### Lauch pod with json annotation
#### Launch pod with json annotation
```
# Execute following command at Kubernetes master
$ cat <<EOF | kubectl create -f -
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -375,13 +375,13 @@ spec:
EOF
```
#### Lauch pod with json annotation for NetworkAttachmentDefinition in different namespace
#### 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 -
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -400,13 +400,13 @@ spec:
EOF
```
#### Lauch pod with json annotation with interface
#### 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 -
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -432,7 +432,8 @@ 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
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
@@ -529,7 +530,8 @@ EOF
This will set `192.168.2.1` as the default route over the `net1` interface, such as:
```
$ kubectl exec -it samplepod -- ip route
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
@@ -567,7 +569,11 @@ 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.
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
@@ -591,16 +597,20 @@ This can be used if you have your CNI configuration stored in an alternate locat
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 for which values are permitted.
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 speicfy CNI spec version.
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' 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:
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

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,7 +15,7 @@ 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 recommend Kubernetes 1.16 or later.
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).
@@ -29,7 +29,14 @@ 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
In the case that your default network is ready you will see the `STATUS` column also switch to `Ready` for each node.
```
NAME STATUS ROLES AGE VERSION
master-0 Ready master 1h v1.17.1
master-1 Ready master 1h v1.17.1
master-2 Ready master 1h v1.17.1
```
## Installation
@@ -38,19 +45,19 @@ Our recommended quickstart method to deploy Multus is to deploy using a Daemonse
Firstly, clone this GitHub repository.
```
git clone https://github.com/intel/multus-cni.git && cd multus-cni
git clone https://github.com/k8snetworkplumbingwg/multus-cni.git && cd multus-cni
```
We'll apply a YAML file with `kubectl` from this repo.
```
$ cat ./images/multus-daemonset.yml | kubectl apply -f -
cat ./deployments/multus-daemonset-thick-plugin.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 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 on each node as `/etc/cni/net.d/00-multus.conf`, this configuration is auto-generated and is based on the default network configuration (which is assumed to be the alphabetically first configuration)
* Creates a `/etc/cni/net.d/multus.d` directory on each node with authentication information for Multus to access the Kubernetes API.
@@ -59,7 +66,7 @@ $ cat ./images/multus-daemonset.yml | kubectl apply -f -
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.
@@ -169,7 +176,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:
@@ -184,7 +191,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/networks-status:
k8s.v1.cni.cncf.io/network-status:
[{
"name": "cbr0",
"ips": [

View File

@@ -4,9 +4,15 @@
```
$ git clone https://github.com/intel/multus-cni.git
$ git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
$ cd multus-cni/e2e
$ ./get_tools.sh
$ ./setup_cluster.sh
$ ./test-simple-macvlan1.sh
```
### How to teardown cluster
```
$ ./teardown.sh
```

View File

@@ -7,9 +7,9 @@ metadata:
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
wget https://github.com/containernetworking/plugins/releases/download/v1.1.1/cni-plugins-linux-amd64-v1.1.1.tgz
cd /host/opt/cni/bin
tar xvfzp /tmp/cni-plugins-linux-amd64-v0.8.5.tgz
tar xvfzp /tmp/cni-plugins-linux-amd64-v1.1.1.tgz
sleep infinite
---
apiVersion: apps/v1

57
e2e/default-route1.yml Normal file
View File

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

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

View File

@@ -18,17 +18,12 @@ spec:
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:
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
@@ -49,15 +44,6 @@ rules:
verbs:
- get
- update
- apiGroups:
- ""
- events.k8s.io
resources:
- events
verbs:
- create
- patch
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -158,7 +144,7 @@ spec:
serviceAccountName: multus
containers:
- name: kube-multus
image: nfvpe/multus:v3.4.1
image: localhost:5000/multus:e2e
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
@@ -179,6 +165,23 @@ spec:
mountPath: /host/opt/cni/bin
- name: multus-cfg
mountPath: /tmp/multus-conf
initContainers:
- name: install-multus-binary
image: localhost:5000/multus:e2e
command:
- "cp"
- "/usr/src/multus-cni/bin/multus"
- "/host/opt/cni/bin/multus"
resources:
requests:
cpu: "10m"
memory: "15Mi"
securityContext:
privileged: true
volumeMounts:
- name: cnibin
mountPath: /host/opt/cni/bin
mountPropagation: Bidirectional
volumes:
- name: cni
hostPath:

View File

@@ -44,6 +44,15 @@ rules:
verbs:
- get
- update
- apiGroups:
- ""
- events.k8s.io
resources:
- events
verbs:
- create
- patch
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -145,10 +154,16 @@ spec:
containers:
- name: kube-multus
image: localhost:5000/multus:e2e
command: ["/entrypoint.sh"]
imagePullPolicy: Always
command: [ "/usr/src/multus-cni/bin/multus-daemon" ]
args:
- "--multus-conf-file=auto"
- "--cni-version=0.3.1"
- "-multus-conf-file=auto"
- "-cni-version=0.3.1"
- "-cni-config-dir=/host/etc/cni/net.d"
- "-multus-autoconfig-dir=/host/etc/cni/net.d"
- "-multus-log-to-stderr=true"
- "-multus-log-level=debug"
- "-multus-log-file=/tmp/multus.log"
resources:
requests:
cpu: "100m"
@@ -165,6 +180,40 @@ spec:
mountPath: /host/opt/cni/bin
- name: multus-cfg
mountPath: /tmp/multus-conf
initContainers:
- name: install-multus-binary
image: localhost:5000/multus:e2e
command:
- "cp"
- "/usr/src/multus-cni/bin/multus"
- "/host/opt/cni/bin/multus"
resources:
requests:
cpu: "10m"
memory: "15Mi"
securityContext:
privileged: true
volumeMounts:
- name: cnibin
mountPath: /host/opt/cni/bin
mountPropagation: Bidirectional
- name: generate-kubeconfig
image: localhost:5000/multus:e2e
command:
- "/usr/src/multus-cni/bin/generate-kubeconfig"
args:
- "-k8s-service-host=$(KUBERNETES_SERVICE_HOST)"
- "-k8s-service-port=$(KUBERNETES_SERVICE_PORT)"
resources:
requests:
cpu: "10m"
memory: "15Mi"
securityContext:
privileged: true
volumeMounts:
- name: cni
mountPath: /host/etc/cni/net.d
mountPropagation: Bidirectional
volumes:
- name: cni
hostPath:

View File

@@ -3,32 +3,82 @@ 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 `legacy-multus-daemonset.yml`. `multus-daemonset.yml`.
# Defaults to `multus-daemonset.yml`.
MULTUS_MANIFEST="${MULTUS_MANIFEST:-multus-daemonset.yml}"
kind_network='kind'
reg_name='kind-registry'
reg_port='5000'
running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)"
running="$($OCI_BIN 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
# run registry and push the multus image
$OCI_BIN run -d --restart=always -p "${reg_port}:5000" --name "${reg_name}" registry:2
$OCI_BIN build -t localhost:5000/multus:e2e -f ../images/Dockerfile ..
$OCI_BIN push localhost:5000/multus:e2e
fi
reg_ip="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' "${reg_name}")"
reg_host="${reg_name}"
if [ "${kind_network}" = "bridge" ]; then
reg_host="$($OCI_BIN inspect -f '{{.NetworkSettings.IPAddress}}' "${reg_name}")"
fi
echo "Registry Host: ${reg_host}"
# 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}"]
endpoint = ["http://${reg_host}:${reg_port}"]
nodes:
- role: control-plane
- role: worker
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
pod-manifest-path: "/etc/kubernetes/manifests/"
- role: worker
EOF
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${reg_port}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF
containers=$($OCI_BIN network inspect ${kind_network} -f "{{range .Containers}}{{.Name}} {{end}}")
needs_connect="true"
for c in $containers; do
if [ "$c" = "${reg_name}" ]; then
needs_connect="false"
fi
done
if [ "${needs_connect}" = "true" ]; then
$OCI_BIN network connect "${kind_network}" "${reg_name}" || true
fi
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 -d kind-worker,eth1 -d kind-worker2,eth1
sudo env PATH=${PATH} koko -p "$worker1_pid,eth1" -p "$worker2_pid,eth1"
sleep 1
kubectl -n kube-system wait --for=condition=available deploy/coredns --timeout=300s
kubectl create -f https://raw.githubusercontent.com/intel/multus-cni/master/images/multus-daemonset.yml
kubectl create -f "$MULTUS_MANIFEST"
sleep 1
kubectl -n kube-system wait --for=condition=ready -l name=multus pod --timeout=300s
kubectl create -f cni-install.yml

View File

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

15
e2e/simple-pod.yml Normal file
View File

@@ -0,0 +1,15 @@
---
apiVersion: v1
kind: Pod
metadata:
name: simple-centos1
annotations:
labels:
app: simple
spec:
containers:
- name: simple-centos1
image: centos:8
command: ["/bin/sleep", "10000"]
securityContext:
privileged: true

11
e2e/simple-static-pod.yml Normal file
View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: Pod
metadata:
name: static-web
annotations:
k8s.v1.cni.cncf.io/networks: "bridge-nad"
spec:
containers:
- name: web
image: centos:8
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]

15
e2e/static-pod-nad.yml Normal file
View File

@@ -0,0 +1,15 @@
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: bridge-nad
spec:
config: '{
"cniVersion": "0.3.1",
"name": "testnet",
"type": "bridge",
"bridge": "testnet0",
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16"
}
}'

10
e2e/teardown.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
#set -o errexit
reg_name='kind-registry'
export PATH=${PATH}:./bin
# delete cluster kind
kind delete cluster
docker kill ${reg_name}
docker rm ${reg_name}

44
e2e/test-default-route1.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/sh
set -o errexit
export PATH=${PATH}:./bin
kubectl create -f 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
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
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 default-route1.yml

View File

@@ -3,11 +3,11 @@ set -o errexit
export PATH=${PATH}:./bin
kubectl create -f macvlan1.yml
kubectl create -f simple-macvlan1.yml
kubectl wait --for=condition=ready -l app=macvlan --timeout=300s pod
echo "check macvlan1-worker1 interface: net1"
kubectl exec macvlan1-worker1 ip a show dev net1
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 \
@@ -17,7 +17,7 @@ if [ $ipaddr != "10.1.1.11" ]; then
fi
echo "check macvlan1-worker2 interface: net1"
kubectl exec macvlan1-worker2 ip a show dev net1
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 \
@@ -25,3 +25,6 @@ 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 simple-macvlan1.yml

10
e2e/test-simple-pod.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
set -o errexit
export PATH=${PATH}:./bin
kubectl create -f simple-pod.yml
kubectl wait --for=condition=ready -l app=simple --timeout=300s pod
echo "cleanup resources"
kubectl delete -f simple-pod.yml

22
e2e/test-static-pod.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -o errexit
echo "Creating network attachment definition"
kubectl create -f static-pod-nad.yml
echo "Creating static pod config file"
docker cp simple-static-pod.yml kind-worker:/etc/kubernetes/manifests/static-web.yaml
echo "Waiting for static pod to start"
kubectl wait --for=condition=Ready --namespace=default pod/static-web-kind-worker
echo "Checking the pod annotation for net1 interface"
kubectl exec static-web-kind-worker --namespace=default -- ip a show dev net1
echo "Deleting static pod"
docker exec kind-worker /bin/bash -c "rm /etc/kubernetes/manifests/static-web.yaml"
echo "Deleting network attachment definition"
kubectl delete -f static-pod-nad.yml
echo "Test complete"

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` 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.
Some CNI plugins require specific device information which maybe pre-allocated by K8s device plugin. This could be indicated by providing `k8s.v1.cni.cncf.io/resourceName` annotation in its network attachment definition CRD. The file [`examples/sriov-net.yaml`](./sriov-net.yaml) shows an example on how to define a Network attachment definition with specific device allocation information. Multus will get allocated device information and make them available for CNI plugin to work on.
In this 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.
In this example (shown below), it is expected that an [SRIOV Device Plugin](https://github.com/intel/sriov-network-device-plugin/) making a pool of SRIOV VFs available to the K8s with `intel.com/sriov` as their resourceName. Any device allocated from this resource pool will be passed down by Multus to the [sriov-cni](https://github.com/intel/sriov-cni/tree/dev/k8s-deviceid-model) plugin in `deviceID` field. This is up to the sriov-cni plugin to capture this information and work with this specific device information.
```yaml
apiVersion: "k8s.cni.cncf.io/v1"
@@ -89,6 +89,6 @@ spec:
}
}'
```
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.
The [sriov-pod.yml](./sriov-pod.yml) is an example Pod manifest file that requesting a SRIOV device from a host which is then configured using the above network attachment definition.
>For further information on how to configure SRIOV Device Plugin and SRIOV-CNI please refer to the links given above.
>For further information on how to configure SRIOV Device Plugin and SRIOV-CNI please refer to the links given above.

70
go.mod
View File

@@ -1,29 +1,53 @@
module gopkg.in/intel/multus-cni.v3
module gopkg.in/k8snetworkplumbingwg/multus-cni.v3
go 1.12
go 1.16
require (
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/containernetworking/cni v0.7.1
github.com/containernetworking/plugins v0.8.2
github.com/golang/protobuf v1.3.2 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/k8snetworkplumbingwg/network-attachment-definition-client v0.0.0-20200127152046-0ee521d56061
github.com/onsi/ginkgo v1.10.1
github.com/onsi/gomega v1.7.0
github.com/pkg/errors v0.8.1
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
golang.org/x/text v0.3.3 // indirect
google.golang.org/grpc v1.23.0
github.com/blang/semver v3.5.1+incompatible
github.com/containernetworking/cni v0.8.1
github.com/containernetworking/plugins v0.9.1
github.com/fsnotify/fsnotify v1.4.9
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.1.2-0.20220511184442-64cfb249bdbe
github.com/onsi/ginkgo v1.12.1
github.com/onsi/gomega v1.10.3
github.com/pkg/errors v0.9.1
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7
google.golang.org/grpc v1.27.1
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gopkg.in/yaml.v2 v2.2.8 // indirect
k8s.io/api v0.0.0-20181115043458-b799cb063522
k8s.io/apimachinery v0.0.0-20181110190943-2a7c93004028
k8s.io/client-go v0.0.0-20181115111358-9bea17718df8
k8s.io/api v0.20.10
k8s.io/apimachinery v0.20.10
k8s.io/client-go v0.20.10
k8s.io/klog v1.0.0
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c // indirect
k8s.io/kubernetes v1.13.0
k8s.io/kubelet v0.0.0
k8s.io/kubernetes v1.20.10
)
replace (
github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2
k8s.io/api => k8s.io/api v0.20.10
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.20.10
k8s.io/apimachinery => k8s.io/apimachinery v0.20.10
k8s.io/apiserver => k8s.io/apiserver v0.20.10
k8s.io/cli-runtime => k8s.io/cli-runtime v0.20.10
k8s.io/client-go => k8s.io/client-go v0.20.10
k8s.io/cloud-provider => k8s.io/cloud-provider v0.20.10
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.20.10
k8s.io/code-generator => k8s.io/code-generator v0.20.10
k8s.io/component-base => k8s.io/component-base v0.20.10
k8s.io/component-helpers => k8s.io/component-helpers v0.20.10
k8s.io/controller-manager => k8s.io/controller-manager v0.20.10
k8s.io/cri-api => k8s.io/cri-api v0.20.10
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.20.10
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.20.10
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.20.10
k8s.io/kube-proxy => k8s.io/kube-proxy v0.20.10
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.20.10
k8s.io/kubectl => k8s.io/kubectl v0.20.10
k8s.io/kubelet => k8s.io/kubelet v0.20.10
k8s.io/kubernetes => k8s.io/kubernetes v1.20.10
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.20.10
k8s.io/metrics => k8s.io/metrics v0.20.10
k8s.io/mount-utils => k8s.io/mount-utils v0.20.10
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.20.10
)

926
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@ if [ -z "$VERSION" ]; then
fi
DATE=$(date -u -d "@${SOURCE_DATE_EPOCH:-$(date +%s)}" --iso-8601=seconds)
COMMIT=${COMMIT:-$(git rev-parse --verify HEAD)}
LDFLAGS="-X main.version=${VERSION:-master} -X main.commit=${COMMIT} -X main.date=${DATE}"
LDFLAGS="-X gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/multus.version=${VERSION:-master} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/multus.commit=${COMMIT} -X gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/multus.date=${DATE}"
export CGO_ENABLED=0
# this if... will be removed when gomodules goes default
@@ -29,7 +29,7 @@ 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="gopkg.in/intel"
ORG_PATH="gopkg.in/k8snetworkplumbingwg"
REPO_PATH="${ORG_PATH}/multus-cni.v3"
if [ ! -h gopath/src/${REPO_PATH} ]; then
@@ -40,7 +40,9 @@ if [ "$GO111MODULE" == "off" ]; then
export GO15VENDOREXPERIMENT=1
export GOBIN=${PWD}/bin
export GOPATH=${PWD}/gopath
go install -tags no_openssl -ldflags "${LDFLAGS}" "$@" ${REPO_PATH}/multus
go build -o ${PWD}/bin/multus -tags no_openssl -ldflags "${LDFLAGS}" "$@" ${REPO_PATH}/cmd
go build -o ${PWD}/bin/generate-kubeconfig -tags no_openssl -ldflags "${LDFLAGS}" ${REPO_PATH}/cmd/config-generation
go build -o ${PWD}/bin/multus-daemon -tags no_openssl -ldflags "${LDFLAGS}" "$@" ${REPO_PATH}/cmd/controller/
else
# build with go modules
export GO111MODULE=on
@@ -50,5 +52,9 @@ else
fi
echo "Building plugins"
go build ${BUILD_ARGS[*]} -ldflags "${LDFLAGS}" "$@" ./multus
go build ${BUILD_ARGS[*]} -ldflags "${LDFLAGS}" "$@" ./cmd
echo "Building spec generators"
go build -o "${DEST_DIR}"/generate-kubeconfig -ldflags "${LDFLAGS}" ./cmd/config-generation
echo "Building multus controller"
go build -o "${DEST_DIR}"/multus-daemon -ldflags "${LDFLAGS}" ./cmd/controller/
fi

View File

@@ -5,7 +5,7 @@ set -e
if [ "$GO111MODULE" == "off" ]; then
echo "Warning: this will be deprecated in near future so please use go modules!"
ORG_PATH="github.com/intel"
ORG_PATH="gopkg.in/k8snetworkplumbingwg"
REPO_PATH="${ORG_PATH}/multus-cni"
if [ ! -h gopath/src/${REPO_PATH} ]; then

17
images/Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
# This Dockerfile is used to build the image available on DockerHub
FROM golang:1.17.9 as build
# Add everything
ADD . /usr/src/multus-cni
RUN cd /usr/src/multus-cni && \
./hack/build-go.sh
FROM python:slim
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

22
images/Dockerfile.arm32 Normal file
View File

@@ -0,0 +1,22 @@
# This Dockerfile is used to build the image available on DockerHub
FROM golang:1.17.9 as build
# Add everything
ADD . /usr/src/multus-cni
ENV GOARCH "arm"
ENV GOOS "linux"
RUN cd /usr/src/multus-cni && \
./hack/build-go.sh
# build arm container
FROM arm32v7/python:slim
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

22
images/Dockerfile.arm64 Normal file
View File

@@ -0,0 +1,22 @@
# This Dockerfile is used to build the image available on DockerHub
FROM golang:1.17.9 as build
# Add everything
ADD . /usr/src/multus-cni
ENV GOARCH "arm64"
ENV GOOS "linux"
RUN cd /usr/src/multus-cni && \
./hack/build-go.sh
# build arm64 container
FROM arm64v8/python:slim
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -1,13 +1,14 @@
# This dockerfile is specific to building Multus for OpenShift
FROM openshift/origin-release:golang-1.10 as builder
FROM openshift/origin-release:golang-1.16 as builder
ADD . /usr/src/multus-cni
WORKDIR /usr/src/multus-cni
ENV GO111MODULE=off
RUN ./build
RUN ./hack/build-go.sh
FROM openshift/origin-base
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
RUN mkdir -p /usr/src/multus-cni/images && mkdir -p /usr/src/multus-cni/bin
COPY --from=builder /usr/src/multus-cni/bin/multus /usr/src/multus-cni/bin
ADD ./images/entrypoint.sh /

22
images/Dockerfile.ppc64le Normal file
View File

@@ -0,0 +1,22 @@
# This Dockerfile is used to build the image available on DockerHub
FROM golang:1.17.9 as build
# Add everything
ADD . /usr/src/multus-cni
ENV GOARCH "ppc64le"
ENV GOOS "linux"
RUN cd /usr/src/multus-cni && \
./hack/build-go.sh
# build ppc container
FROM ppc64le/python:slim
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

21
images/Dockerfile.s390x Normal file
View File

@@ -0,0 +1,21 @@
# This Dockerfile is used to build the image available on DockerHub
FROM golang:1.17.9 as build
# Add everything
ADD . /usr/src/multus-cni
ENV GOARCH "s390x"
ENV GOOS "linux"
RUN cd /usr/src/multus-cni && \
./hack/build-go.sh
# build s390x container
FROM s390x/python:slim
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

16
images/Dockerfile.thick Normal file
View File

@@ -0,0 +1,16 @@
# This Dockerfile is used to build the image available on DockerHub
FROM golang:1.17.9 as build
# Add everything
ADD . /usr/src/multus-cni
RUN cd /usr/src/multus-cni && \
./hack/build-go.sh
FROM debian:stable-slim
LABEL org.opencontainers.image.source https://github.com/k8snetworkplumbingwg/multus-cni
COPY --from=build /usr/src/multus-cni/bin /usr/src/multus-cni/bin
COPY --from=build /usr/src/multus-cni/LICENSE /usr/src/multus-cni/LICENSE
WORKDIR /
ENTRYPOINT [ "/usr/src/multus-cni/bin/multus-daemon" ]

View File

@@ -5,7 +5,7 @@ This is used for distribution of Multus in a Docker image.
Typically you'd build this from the root of your Multus clone, as such:
```
$ docker build -t dougbtv/multus .
$ docker build -t dougbtv/multus -f images/Dockerfile .
```
---
@@ -15,7 +15,7 @@ $ docker build -t dougbtv/multus .
You may wish to deploy Multus as a daemonset, you can do so by starting with the example Daemonset shown here:
```
$ kubectl create -f ./images/multus-daemonset.yml
$ kubectl create -f ./deployments/multus-daemonset.yml
```
Note: The likely best practice here is to build your own image given the Dockerfile, and then push it to your preferred registry, and change the `image` fields in the Daemonset YAML to reference that image.
@@ -41,9 +41,7 @@ in lexicographical order in cni-conf-dir).
./entrypoint.sh
-h --help
--cni-conf-dir=/host/etc/cni/net.d
--cni-bin-dir=/host/opt/cni/bin
--multus-conf-file=/usr/src/multus-cni/images/70-multus.conf
--multus-bin-file=/usr/src/multus-cni/bin/multus
--multus-kubeconfig-file-host=/etc/cni/net.d/multus.d/multus.kubeconfig
```
@@ -65,4 +63,4 @@ Example docker run command:
$ docker run -it -v /opt/cni/bin/:/host/opt/cni/bin/ -v /etc/cni/net.d/:/host/etc/cni/net.d/ --entrypoint=/bin/bash dougbtv/multus
```
Originally inspired by and is a portmanteau of the [Flannel daemonset](https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml), the [Calico Daemonset](https://github.com/projectcalico/calico/blob/master/v2.0/getting-started/kubernetes/installation/hosted/k8s-backend-addon-manager/calico-daemonset.yaml), and the [Calico CNI install bash script](https://github.com/projectcalico/cni-plugin/blob/be4df4db2e47aa7378b1bdf6933724bac1f348d0/k8s-install/scripts/install-cni.sh#L104-L153).
Originally inspired by and is a portmanteau of the [Flannel daemonset](https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml), the [Calico Daemonset](https://docs.projectcalico.org/manifests/calico.yaml), and the [Calico CNI install bash script](https://github.com/projectcalico/cni-plugin/blob/be4df4db2e47aa7378b1bdf6933724bac1f348d0/k8s-install/scripts/install-cni.sh#L104-L153).

View File

@@ -3,6 +3,13 @@
# Always exit on errors.
set -e
# Trap sigterm
function exitonsigterm() {
echo "Trapped sigterm, exiting."
exit 0
}
trap exitonsigterm SIGTERM
# Set our known directories.
CNI_CONF_DIR="/host/etc/cni/net.d"
CNI_BIN_DIR="/host/opt/cni/bin"
@@ -11,7 +18,11 @@ MULTUS_CONF_FILE="/usr/src/multus-cni/images/70-multus.conf"
MULTUS_AUTOCONF_DIR="/host/etc/cni/net.d"
MULTUS_BIN_FILE="/usr/src/multus-cni/bin/multus"
MULTUS_KUBECONFIG_FILE_HOST="/etc/cni/net.d/multus.d/multus.kubeconfig"
MULTUS_TEMP_KUBECONFIG="/tmp/multus.kubeconfig"
MULTUS_MASTER_CNI_FILE_NAME=""
MULTUS_NAMESPACE_ISOLATION=false
MULTUS_GLOBAL_NAMESPACES=""
MULTUS_LOG_TO_STDERR=true
MULTUS_LOG_LEVEL=""
MULTUS_LOG_FILE=""
MULTUS_READINESS_INDICATOR_FILE=""
@@ -25,24 +36,28 @@ SKIP_BINARY_COPY=false
# Give help text for parameters.
function usage()
{
echo -e "This is an entrypoint script for Multus CNI to overlay its binary and "
echo -e "configuration into locations in a filesystem. The configuration & binary file "
echo -e "will be copied to the corresponding configuration directory. When "
echo -e "'--multus-conf-file=auto' is used, 00-multus.conf will be automatically "
echo -e "generated from the CNI configuration file of the master plugin (the first file "
echo -e "in lexicographical order in cni-conf-dir)."
echo -e "This is an entrypoint script for Multus CNI to overlay its configuration into"
echo -e "locations in a filesystem. The configuration file will be copied to the"
echo -e "corresponding configuration directory. When '--multus-conf-file=auto' is used,"
echo -e "00-multus.conf will be automatically generated from the CNI configuration file"
echo -e "of the master plugin (the first file in lexicographical order in cni-conf-dir)."
echo -e "When '--multus-master-cni-file-name' is used, 00-multus.conf will be"
echo -e "automatically generated from the specific file rather than the first file."
echo -e ""
echo -e "./entrypoint.sh"
echo -e "\t-h --help"
echo -e "\t--cni-conf-dir=$CNI_CONF_DIR"
echo -e "\t--cni-bin-dir=$CNI_BIN_DIR"
echo -e "\t--cni-conf-dir=$CNI_CONF_DIR"
echo -e "\t--cni-version=<cniVersion (e.g. 0.3.1)>"
echo -e "\t--multus-conf-file=$MULTUS_CONF_FILE"
echo -e "\t--multus-bin-file=$MULTUS_BIN_FILE"
echo -e "\t--skip-multus-binary-copy=$SKIP_BINARY_COPY"
echo -e "\t--multus-kubeconfig-file-host=$MULTUS_KUBECONFIG_FILE_HOST"
echo -e "\t--multus-master-cni-file-name=$MULTUS_MASTER_CNI_FILE_NAME (empty by default, example: 10-calico.conflist)"
echo -e "\t--namespace-isolation=$MULTUS_NAMESPACE_ISOLATION"
echo -e "\t--global-namespaces=$MULTUS_GLOBAL_NAMESPACES (used only with --namespace-isolation=true)"
echo -e "\t--multus-autoconfig-dir=$MULTUS_AUTOCONF_DIR (used only with --multus-conf-file=auto)"
echo -e "\t--multus-log-to-stderr=$MULTUS_LOG_TO_STDERR (empty by default, used only with --multus-conf-file=auto)"
echo -e "\t--multus-log-level=$MULTUS_LOG_LEVEL (empty by default, used only with --multus-conf-file=auto)"
echo -e "\t--multus-log-file=$MULTUS_LOG_FILE (empty by default, used only with --multus-conf-file=auto)"
echo -e "\t--override-network-name=false (used only with --multus-conf-file=auto)"
@@ -68,6 +83,25 @@ function warn()
log "WARN: {$1}"
}
function checkCniVersion {
cniversion_python_tmpfile=$(mktemp)
cat << EOF > $cniversion_python_tmpfile
import json, sys
def version(v):
return [int(x) for x in v.split(".")]
v_040 = version("0.4.0")
v_top_level = sys.argv[2]
with open(sys.argv[1], "r") as f:
v_nested = json.load(f)["cniVersion"]
if version(v_top_level) >= v_040 and version(v_nested) < v_040:
msg = "Multus cni version is %s while master plugin cni version is %s"
print(msg % (v_top_level, v_nested))
EOF
python3 $cniversion_python_tmpfile $1 $2
}
# Parse parameters given as arguments to this script.
while [ "$1" != "" ]; do
PARAM=`echo $1 | awk -F= '{print $1}'`
@@ -80,6 +114,9 @@ while [ "$1" != "" ]; do
--cni-version)
CNI_VERSION=$VALUE
;;
--cni-bin-dir)
CNI_BIN_DIR=$VALUE
;;
--cni-conf-dir)
CNI_CONF_DIR=$VALUE
;;
@@ -89,15 +126,21 @@ while [ "$1" != "" ]; do
--multus-conf-file)
MULTUS_CONF_FILE=$VALUE
;;
--multus-bin-file)
MULTUS_BIN_FILE=$VALUE
;;
--multus-kubeconfig-file-host)
MULTUS_KUBECONFIG_FILE_HOST=$VALUE
;;
--multus-master-cni-file-name)
MULTUS_MASTER_CNI_FILE_NAME=$VALUE
;;
--namespace-isolation)
MULTUS_NAMESPACE_ISOLATION=$VALUE
;;
--global-namespaces)
MULTUS_GLOBAL_NAMESPACES=$VALUE
;;
--multus-log-to-stderr)
MULTUS_LOG_TO_STDERR=$VALUE
;;
--multus-log-level)
MULTUS_LOG_LEVEL=$VALUE
;;
@@ -197,9 +240,10 @@ if [ -f "$SERVICE_ACCOUNT_PATH/token" ]; then
# to skip TLS verification for now. We should eventually support
# writing more complete kubeconfig files. This is only used
# if the provided CNI network config references it.
touch $MULTUS_KUBECONFIG
chmod ${KUBECONFIG_MODE:-600} $MULTUS_KUBECONFIG
cat > $MULTUS_KUBECONFIG <<EOF
touch $MULTUS_TEMP_KUBECONFIG
chmod ${KUBECONFIG_MODE:-600} $MULTUS_TEMP_KUBECONFIG
# Write the kubeconfig to a temp file first.
cat > $MULTUS_TEMP_KUBECONFIG <<EOF
# Kubeconfig file for Multus CNI plugin.
apiVersion: v1
kind: Config
@@ -220,6 +264,9 @@ contexts:
current-context: multus-context
EOF
# Atomically move the temp kubeconfig to its permanent home.
mv -f $MULTUS_TEMP_KUBECONFIG $MULTUS_KUBECONFIG
else
warn "Doesn't look like we're running in a kubernetes environment (no serviceaccount token)"
fi
@@ -234,11 +281,15 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
found_master=false
tries=0
while [ $found_master == false ]; do
MASTER_PLUGIN="$(ls $MULTUS_AUTOCONF_DIR | grep -E '\.conf(list)?$' | grep -Ev '00-multus\.conf' | head -1)"
if [ "$MULTUS_MASTER_CNI_FILE_NAME" != "" ]; then
MASTER_PLUGIN="$MULTUS_MASTER_CNI_FILE_NAME"
else
MASTER_PLUGIN="$(ls $MULTUS_AUTOCONF_DIR | grep -E '\.conf(list)?$' | grep -Ev '00-multus\.conf' | head -1)"
fi
if [ "$MASTER_PLUGIN" == "" ]; then
if [ $tries -lt 600 ]; then
if ! (($tries % 5)); then
log "Attemping to find master plugin configuration, attempt $tries"
log "Attempting to find master plugin configuration, attempt $tries"
fi
let "tries+=1"
sleep 1;
@@ -247,6 +298,7 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
exit 1;
fi
else
log "Using MASTER_PLUGIN: $MASTER_PLUGIN"
found_master=true
@@ -255,6 +307,17 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
ISOLATION_STRING="\"namespaceIsolation\": true,"
fi
GLOBAL_NAMESPACES_STRING=""
if [ ! -z "${MULTUS_GLOBAL_NAMESPACES// }" ]; then
GLOBAL_NAMESPACES_STRING="\"globalNamespaces\": \"$MULTUS_GLOBAL_NAMESPACES\","
fi
LOG_TO_STDERR_STRING=""
if [ "$MULTUS_LOG_TO_STDERR" == false ]; then
LOG_TO_STDERR_STRING="\"logToStderr\": false,"
fi
LOG_LEVEL_STRING=""
if [ ! -z "${MULTUS_LOG_LEVEL// }" ]; then
case "$MULTUS_LOG_LEVEL" in
@@ -269,7 +332,7 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
*)
error "Log levels should be one of: debug/verbose/error/panic, did not understand $MULTUS_LOG_LEVEL"
usage
exit 1
exit 1
esac
LOG_LEVEL_STRING="\"logLevel\": \"$MULTUS_LOG_LEVEL\","
fi
@@ -297,20 +360,50 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
if [ "$OVERRIDE_NETWORK_NAME" == "true" ]; then
MASTER_PLUGIN_NET_NAME="$(cat $MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN | \
python -c 'import json,sys;print json.load(sys.stdin)["name"]')"
python3 -c 'import json,sys;print(json.load(sys.stdin)["name"])')"
else
MASTER_PLUGIN_NET_NAME="multus-cni-network"
fi
capabilities_python_filter_tmpfile=$(mktemp)
cat << EOF > $capabilities_python_filter_tmpfile
import json,sys
conf = json.load(sys.stdin)
capabilities = {}
if 'plugins' in conf:
for capa in [p['capabilities'] for p in conf['plugins'] if 'capabilities' in p]:
capabilities.update({capability:enabled for (capability,enabled) in capa.items() if enabled})
elif 'capabilities' in conf:
capabilities.update({capability:enabled for (capability,enabled) in conf['capabilities'] if enabled})
if len(capabilities) > 0:
print("""\"capabilities\": """ + json.dumps(capabilities) + ",")
else:
print("")
EOF
NESTED_CAPABILITIES_STRING="$(cat $MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN | \
python3 $capabilities_python_filter_tmpfile)"
rm $capabilities_python_filter_tmpfile
log "Nested capabilities string: $NESTED_CAPABILITIES_STRING"
MASTER_PLUGIN_LOCATION=$MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN
MASTER_PLUGIN_JSON="$(cat $MASTER_PLUGIN_LOCATION)"
log "Using $MASTER_PLUGIN_LOCATION as a source to generate the Multus configuration"
CHECK_CNI_VERSION=$(checkCniVersion $MASTER_PLUGIN_LOCATION $CNI_VERSION)
if [ "$CHECK_CNI_VERSION" != "" ] ; then
error "$CHECK_CNI_VERSION"
exit 1
fi
CONF=$(cat <<-EOF
{
$CNI_VERSION_STRING
"name": "$MASTER_PLUGIN_NET_NAME",
"type": "multus",
$NESTED_CAPABILITIES_STRING
$ISOLATION_STRING
$GLOBAL_NAMESPACES_STRING
$LOG_TO_STDERR_STRING
$LOG_LEVEL_STRING
$LOG_FILE_STRING
$ADDITIONAL_BIN_DIR_STRING
@@ -327,7 +420,7 @@ EOF
mv $tmpfile $CNI_CONF_DIR/00-multus.conf
log "Config file created @ $CNI_CONF_DIR/00-multus.conf"
echo $CONF
# If we're not performing the cleanup on exit, we can safely rename the config file.
if [ "$RENAME_SOURCE_CONFIG_FILE" == true ]; then
mv ${MULTUS_AUTOCONF_DIR}/${MASTER_PLUGIN} ${MULTUS_AUTOCONF_DIR}/${MASTER_PLUGIN}.old
@@ -374,5 +467,9 @@ if [ "$MULTUS_CLEANUP_CONFIG_ON_EXIT" == true ]; then
done
else
log "Entering sleep (success)..."
sleep infinity
if tty -s; then
read
else
sleep infinity
fi
fi

View File

@@ -1,119 +0,0 @@
// Copyright (c) 2019 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 netutils
import (
"github.com/containernetworking/cni/pkg/skel"
cnitypes "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/vishvananda/netlink"
"gopkg.in/intel/multus-cni.v3/logging"
"net"
"strings"
)
// DeleteDefaultGW removes the default gateway from marked interfaces.
func DeleteDefaultGW(args *skel.CmdArgs, ifName string, res *cnitypes.Result) (*current.Result, error) {
result, err := current.NewResultFromResult(*res)
if err != nil {
return nil, logging.Errorf("DeleteDefaultGW: Error creating new from current CNI result: %v", err)
}
netns, err := ns.GetNS(args.Netns)
if err != nil {
return nil, logging.Errorf("DeleteDefaultGW: Error getting namespace %v", err)
}
defer netns.Close()
err = netns.Do(func(_ ns.NetNS) error {
var err error
link, _ := netlink.LinkByName(ifName)
routes, _ := netlink.RouteList(link, netlink.FAMILY_ALL)
for _, nlroute := range routes {
if nlroute.Dst == nil {
err = netlink.RouteDel(&nlroute)
}
}
return err
})
var newRoutes []*cnitypes.Route
for _, route := range result.Routes {
if mask, _ := route.Dst.Mask.Size(); mask != 0 {
newRoutes = append(newRoutes, route)
}
}
result.Routes = newRoutes
return result, err
}
// SetDefaultGW adds a default gateway on a specific interface
func SetDefaultGW(args *skel.CmdArgs, ifName string, gateways []net.IP, res *cnitypes.Result) (*current.Result, error) {
// Use the current CNI result...
result, err := current.NewResultFromResult(*res)
if err != nil {
return nil, logging.Errorf("SetDefaultGW: Error creating new CNI result from current: %v", err)
}
// This ensures we're acting within the net namespace for the pod.
netns, err := ns.GetNS(args.Netns)
if err != nil {
return nil, logging.Errorf("SetDefaultGW: Error getting namespace %v", err)
}
defer netns.Close()
var newResultDefaultRoutes []*cnitypes.Route
// Do this within the net namespace.
err = netns.Do(func(_ ns.NetNS) error {
var err error
// Pick up the link info as we need the index.
link, _ := netlink.LinkByName(ifName)
// Cycle through all the desired gateways.
for _, gw := range gateways {
// Create a new route (note: dst is nil by default)
logging.Debugf("SetDefaultGW: Adding default route on %v (index: %v) to %v", ifName, link.Attrs().Index, gw)
newDefaultRoute := netlink.Route{
LinkIndex: link.Attrs().Index,
Gw: gw,
}
// Build a new element for the results route
// Set a correct CIDR depending on IP type
_, dstipnet, _ := net.ParseCIDR("::0/0")
if strings.Count(gw.String(), ":") < 2 {
_, dstipnet, _ = net.ParseCIDR("0.0.0.0/0")
}
newResultDefaultRoutes = append(newResultDefaultRoutes, &cnitypes.Route{Dst: *dstipnet, GW: gw})
// Perform the creation of the default route....
err = netlink.RouteAdd(&newDefaultRoute)
if err != nil {
logging.Errorf("SetDefaultGW: Error adding route: %v", err)
}
}
return err
})
result.Routes = newResultDefaultRoutes
return result, err
}

View File

@@ -19,8 +19,8 @@ import (
"encoding/json"
"io/ioutil"
"gopkg.in/intel/multus-cni.v3/logging"
"gopkg.in/intel/multus-cni.v3/types"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
v1 "k8s.io/api/core/v1"
)

View File

@@ -10,7 +10,7 @@ import (
"io/ioutil"
"testing"
"gopkg.in/intel/multus-cni.v3/types"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sTypes "k8s.io/apimachinery/pkg/types"

17
pkg/checkpoint/doc.go Normal file
View File

@@ -0,0 +1,17 @@
// 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 is the package that contains the libraries that manipulates kubelet's
// checkpoint API
package checkpoint

17
pkg/config/doc.go Normal file
View File

@@ -0,0 +1,17 @@
// 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 config is the package that contains multus cni config related
// utilities.
package config

333
pkg/config/generator.go Normal file
View File

@@ -0,0 +1,333 @@
// 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 config
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"path/filepath"
"sort"
"strings"
"time"
"github.com/blang/semver"
)
const (
configListCapabilityKey = "plugins"
singleConfigCapabilityKey = "capabilities"
)
// LogOptionFunc mutates the `LoggingOptions` object
type LogOptionFunc func(logOptions *LogOptions)
// Option mutates the `conf` object
type Option func(conf *MultusConf)
// MultusConf holds the multus configuration, and persists it to disk
type MultusConf struct {
BinDir string `json:"binDir,omitempty"`
Capabilities map[string]bool `json:"capabilities,omitempty"`
CNIVersion string `json:"cniVersion"`
Delegates []interface{} `json:"delegates"`
LogFile string `json:"logFile,omitempty"`
LogLevel string `json:"logLevel,omitempty"`
LogToStderr bool `json:"logToStderr,omitempty"`
LogOptions *LogOptions `json:"logOptions,omitempty"`
Kubeconfig string `json:"kubeconfig"`
Name string `json:"name"`
NamespaceIsolation bool `json:"namespaceIsolation,omitempty"`
RawNonIsolatedNamespaces string `json:"globalNamespaces,omitempty"`
ReadinessIndicatorFile string `json:"readinessindicatorfile,omitempty"`
Type string `json:"type"`
}
// LogOptions specifies the configuration of the log
type LogOptions struct {
MaxAge *int `json:"maxAge,omitempty"`
MaxSize *int `json:"maxSize,omitempty"`
MaxBackups *int `json:"maxBackups,omitempty"`
Compress *bool `json:"compress,omitempty"`
}
// NewMultusConfig creates a basic configuration generator. It can be mutated
// via the `With...` methods.
func NewMultusConfig(pluginName string, cniVersion string, kubeconfig string, configurationOptions ...Option) (*MultusConf, error) {
multusConfig := &MultusConf{
Name: MultusDefaultNetworkName,
CNIVersion: cniVersion,
Type: pluginName,
Capabilities: map[string]bool{},
Kubeconfig: kubeconfig,
Delegates: []interface{}{},
}
err := multusConfig.Mutate(configurationOptions...)
return multusConfig, err
}
// CheckVersionCompatibility checks compatibilty of the
// top level cni version with the delegate cni version.
// Since version 0.4.0, CHECK was introduced, which
// causes incompatibility.
func CheckVersionCompatibility(mc *MultusConf) error {
const versionFmt = "delegate cni version is %s while top level cni version is %s"
v040, _ := semver.Make("0.4.0")
multusCNIVersion, err := semver.Make(mc.CNIVersion)
if err != nil {
return errors.New("couldn't get top level cni version")
}
if multusCNIVersion.GTE(v040) {
for _, delegate := range mc.Delegates {
delegatesMap, ok := delegate.(map[string]interface{})
if !ok {
return errors.New("couldn't get cni version of delegate")
}
delegateVersion, ok := delegatesMap["cniVersion"].(string)
if !ok {
return errors.New("couldn't get cni version of delegate")
}
v, err := semver.Make(delegateVersion)
if err != nil {
return err
}
if v.LT(v040) {
return fmt.Errorf(versionFmt, delegateVersion, mc.CNIVersion)
}
}
}
return nil
}
// Generate generates the multus configuration from whatever state is currently
// held
func (mc *MultusConf) Generate() (string, error) {
data, err := json.Marshal(mc)
return string(data), err
}
// Mutate updates the MultusConf attributes according to the provided
// configuration `Option`s
func (mc *MultusConf) Mutate(configurationOptions ...Option) error {
for _, configOption := range configurationOptions {
configOption(mc)
}
return CheckVersionCompatibility(mc)
}
// WithNamespaceIsolation mutates the inner state to enable the
// NamespaceIsolation attribute
func WithNamespaceIsolation() Option {
return func(conf *MultusConf) {
conf.NamespaceIsolation = true
}
}
// WithGlobalNamespaces mutates the inner state to set the
// RawNonIsolatedNamespaces attribute
func WithGlobalNamespaces(globalNamespaces string) Option {
return func(conf *MultusConf) {
conf.RawNonIsolatedNamespaces = globalNamespaces
}
}
// WithLogToStdErr mutates the inner state to enable the
// WithLogToStdErr attribute
func WithLogToStdErr() Option {
return func(conf *MultusConf) {
conf.LogToStderr = true
}
}
// WithLogLevel mutates the inner state to set the
// LogLevel attribute
func WithLogLevel(logLevel string) Option {
return func(conf *MultusConf) {
conf.LogLevel = logLevel
}
}
// WithLogFile mutates the inner state to set the
// logFile attribute
func WithLogFile(logFile string) Option {
return func(conf *MultusConf) {
conf.LogFile = logFile
}
}
// WithLogOptions mutates the inner state to set the
// LogOptions attribute
func WithLogOptions(logOptions *LogOptions) Option {
return func(conf *MultusConf) {
conf.LogOptions = logOptions
}
}
// WithReadinessFileIndicator mutates the inner state to set the
// ReadinessIndicatorFile attribute
func WithReadinessFileIndicator(path string) Option {
return func(conf *MultusConf) {
conf.ReadinessIndicatorFile = path
}
}
// WithAdditionalBinaryFileDir mutates the inner state to set the
// BinDir attribute
func WithAdditionalBinaryFileDir(directoryPath string) Option {
return func(conf *MultusConf) {
conf.BinDir = directoryPath
}
}
// WithOverriddenName mutates the inner state to set the
// Name attribute
func WithOverriddenName(networkName string) Option {
return func(conf *MultusConf) {
conf.Name = networkName
}
}
func withCapabilities(cniData interface{}) Option {
var enabledCapabilities []string
var pluginsList []interface{}
cniDataMap, ok := cniData.(map[string]interface{})
if ok {
if pluginsListEntry, ok := cniDataMap[configListCapabilityKey]; ok {
pluginsList = pluginsListEntry.([]interface{})
}
}
if len(pluginsList) > 0 {
for _, pluginData := range pluginsList {
enabledCapabilities = append(
enabledCapabilities,
extractCapabilities(pluginData)...)
}
} else {
enabledCapabilities = extractCapabilities(cniData)
}
return func(conf *MultusConf) {
for _, capability := range enabledCapabilities {
conf.Capabilities[capability] = true
}
}
}
func withDelegates(primaryCNIConfigData map[string]interface{}) Option {
return func(conf *MultusConf) {
conf.Delegates = []interface{}{primaryCNIConfigData}
}
}
// MutateLogOptions update the LoggingOptions of the MultusConf according
// to the provided configuration `loggingOptions`
func MutateLogOptions(logOption *LogOptions, logOptionFunc ...LogOptionFunc) {
for _, loggingOption := range logOptionFunc {
loggingOption(logOption)
}
}
// WithLogMaxSize mutates the inner state to set the
// logMaxSize attribute
func WithLogMaxSize(maxSize *int) LogOptionFunc {
return func(logOptions *LogOptions) {
logOptions.MaxSize = maxSize
}
}
// WithLogMaxAge mutates the inner state to set the
// logMaxAge attribute
func WithLogMaxAge(maxAge *int) LogOptionFunc {
return func(logOptions *LogOptions) {
logOptions.MaxAge = maxAge
}
}
// WithLogMaxBackups mutates the inner state to set the
// logMaxBackups attribute
func WithLogMaxBackups(maxBackups *int) LogOptionFunc {
return func(logOptions *LogOptions) {
logOptions.MaxBackups = maxBackups
}
}
// WithLogCompress mutates the inner state to set the
// logCompress attribute
func WithLogCompress(compress *bool) LogOptionFunc {
return func(logOptions *LogOptions) {
logOptions.Compress = compress
}
}
func extractCapabilities(capabilitiesInterface interface{}) []string {
capabilitiesMap, ok := capabilitiesInterface.(map[string]interface{})
if !ok {
return nil
}
capabilitiesMapEntry, ok := capabilitiesMap[singleConfigCapabilityKey]
if !ok {
return nil
}
capabilities, ok := capabilitiesMapEntry.(map[string]interface{})
if !ok {
return nil
}
var enabledCapabilities []string
if len(capabilities) > 0 {
for capName, isCapabilityEnabled := range capabilities {
if isCapabilityEnabled.(bool) {
enabledCapabilities = append(enabledCapabilities, capName)
}
}
}
return enabledCapabilities
}
func findMasterPlugin(cniConfigDirPath string, remainingTries int) (string, error) {
if remainingTries == 0 {
return "", fmt.Errorf("could not find a plugin configuration in %s", cniConfigDirPath)
}
var cniPluginConfigs []string
files, err := ioutil.ReadDir(cniConfigDirPath)
if err != nil {
return "", fmt.Errorf("error when listing the CNI plugin configurations: %w", err)
}
for _, file := range files {
if strings.HasPrefix(file.Name(), "00-multus") {
continue
}
fileExtension := filepath.Ext(file.Name())
if fileExtension == ".conf" || fileExtension == ".conflist" {
cniPluginConfigs = append(cniPluginConfigs, file.Name())
}
}
if len(cniPluginConfigs) == 0 {
time.Sleep(time.Second)
return findMasterPlugin(cniConfigDirPath, remainingTries-1)
}
sort.Strings(cniPluginConfigs)
return cniPluginConfigs[0], nil
}

View File

@@ -0,0 +1,371 @@
// 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 config
import (
"encoding/json"
"fmt"
testutils "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/testing"
"testing"
)
const (
primaryCNIName = "myCNI"
cniVersion = "0.4.0"
kubeconfig = "/a/b/c/kubeconfig.kubeconfig"
)
type testCase struct {
t *testing.T
configGenerationFunction func() (string, error)
}
var primaryCNIConfig = map[string]interface{}{
"cniVersion": "1.0.0",
"name": "ovn-kubernetes",
"type": "ovn-k8s-cni-overlay",
"ipam": "{}",
"dns": "{}",
"logFile": "/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log",
"logLevel": "5",
"logfile-maxsize": 100,
"logfile-maxbackups": 5,
"logfile-maxage": 5,
}
func newMultusConfigWithDelegates(pluginName string, cniVersion string, kubeconfig string, primaryCNIPluginConfig interface{}, configOptions ...Option) (*MultusConf, error) {
multusConfig, err := NewMultusConfig(pluginName, cniVersion, kubeconfig, configOptions...)
if err != nil {
return multusConfig, err
}
return multusConfig, multusConfig.Mutate(withDelegates(primaryCNIPluginConfig.(map[string]interface{})))
}
func TestBasicMultusConfig(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig)
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithNamespaceIsolation(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithNamespaceIsolation())
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"namespaceIsolation\":true,\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithReadinessIndicator(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithReadinessFileIndicator("/a/b/u/it-lives"))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"readinessindicatorfile\":\"/a/b/u/it-lives\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithLoggingConfiguration(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithLogLevel("notice"),
WithLogToStdErr(),
WithLogFile("/u/y/w/log.1"))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logFile\":\"/u/y/w/log.1\",\"logLevel\":\"notice\",\"logToStderr\":true,\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithLogOptionsConfiguration(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithLogOptions(&LogOptions{
MaxAge: testutils.Int(5),
MaxSize: testutils.Int(100),
MaxBackups: testutils.Int(5),
Compress: testutils.Bool(true),
}))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logOptions\":{\"maxAge\":5,\"maxSize\":100,\"maxBackups\":5,\"compress\":true},\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusLogOptionsWithLogMaxAge(t *testing.T) {
logOption := &LogOptions{}
MutateLogOptions(logOption, WithLogMaxAge(testutils.Int(5)))
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithLogOptions(logOption))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logOptions\":{\"maxAge\":5},\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusLogOptionsWithLogMaxSize(t *testing.T) {
logOption := &LogOptions{}
MutateLogOptions(logOption, WithLogMaxSize(testutils.Int(100)))
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithLogOptions(logOption))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logOptions\":{\"maxSize\":100},\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusLogOptionsWithLogBackups(t *testing.T) {
logOption := &LogOptions{}
MutateLogOptions(logOption, WithLogMaxBackups(testutils.Int(5)))
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithLogOptions(logOption))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logOptions\":{\"maxBackups\":5},\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusLogOptionsWithLogCompress(t *testing.T) {
logOption := &LogOptions{}
MutateLogOptions(logOption, WithLogCompress(testutils.Bool(true)))
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithLogOptions(logOption))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"logOptions\":{\"compress\":true},\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithGlobalNamespace(t *testing.T) {
const globalNamespace = "come-along-ns"
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithGlobalNamespaces(globalNamespace))
assertError(t, err, nil)
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"globalNamespaces\":\"come-along-ns\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithAdditionalBinDir(t *testing.T) {
const anotherCNIBinDir = "a-dir-somewhere"
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithAdditionalBinaryFileDir(anotherCNIBinDir))
assertError(t, err, nil)
expectedResult := "{\"binDir\":\"a-dir-somewhere\",\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithCapabilities(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
withCapabilities(
documentHelper(`{"capabilities": {"portMappings": true}}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithMultipleCapabilities(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
withCapabilities(
documentHelper(`{"capabilities": {"portMappings": true, "tuning": true}}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithMultipleCapabilitiesFilterOnlyEnabled(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
withCapabilities(
documentHelper(`{"capabilities": {"portMappings": true, "tuning": false}}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithMultipleCapabilitiesDefinedOnAPlugin(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
withCapabilities(
documentHelper(`{"plugins": [ {"capabilities": {"portMappings": true, "tuning": true}} ] }`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithCapabilitiesDefinedOnMultiplePlugins(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
withCapabilities(
documentHelper(`{"plugins": [ {"capabilities": { "portMappings": true }}, {"capabilities": { "tuning": true }} ]}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true,\"tuning\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func TestMultusConfigWithCapabilitiesDefinedOnMultiplePluginsFilterOnlyEnabled(t *testing.T) {
multusConfig, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
withCapabilities(
documentHelper(`
{
"plugins": [
{
"capabilities": {
"portMappings": true
}
},
{
"capabilities": {
"tuning": false
}
}
]
}`)))
assertError(t, err, nil)
expectedResult := "{\"capabilities\":{\"portMappings\":true},\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func assertError(t *testing.T, actual error, expected error) {
if actual != nil && expected != nil {
if actual.Error() != expected.Error() {
t.Fatalf("multus config generation failed.\nExpected:\n%v\nbut GOT:\n%v", expected.Error(), actual.Error())
}
}
if actual == nil && expected != nil {
t.Fatalf("multus config generation failed.\nExpected:\n%v\nbut didn't get error", expected.Error())
} else if actual != nil && expected == nil {
t.Fatalf("multus config generation failed.\nDidn't expect error\nbut GOT: %v\n", actual.Error())
}
}
func invalidDelegateCNIVersion(delegateCNIVersion, multusCNIVersion string) error {
return fmt.Errorf("delegate cni version is %s while top level cni version is %s", delegateCNIVersion, multusCNIVersion)
}
func TestVersionIncompatibility(t *testing.T) {
const delegateCNIVersion = "0.3.0"
primaryCNIConfigOld := primaryCNIConfig
tmpVer := primaryCNIConfig["cniVersion"]
primaryCNIConfig["cniVersion"] = delegateCNIVersion
_, err := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfigOld)
primaryCNIConfig["cniVersion"] = tmpVer
assertError(t, invalidDelegateCNIVersion(delegateCNIVersion, cniVersion), err)
}
func TestMultusConfigWithOverriddenName(t *testing.T) {
newNetworkName := "mega-net-2000"
multusConfig, _ := newMultusConfigWithDelegates(
primaryCNIName,
cniVersion,
kubeconfig,
primaryCNIConfig,
WithOverriddenName(newNetworkName))
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"1.0.0\",\"dns\":\"{}\",\"ipam\":\"{}\",\"logFile\":\"/var/log/ovn-kubernetes/ovn-k8s-cni-overlay.log\",\"logLevel\":\"5\",\"logfile-maxage\":5,\"logfile-maxbackups\":5,\"logfile-maxsize\":100,\"name\":\"ovn-kubernetes\",\"type\":\"ovn-k8s-cni-overlay\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"mega-net-2000\",\"type\":\"myCNI\"}"
newTestCase(t, multusConfig.Generate).assertResult(expectedResult)
}
func newTestCase(t *testing.T, configGenerationFunc func() (string, error)) *testCase {
return &testCase{
t: t,
configGenerationFunction: configGenerationFunc,
}
}
func (tc testCase) assertResult(expectedResult string) {
multusCNIConfig, err := tc.configGenerationFunction()
if err != nil {
tc.t.Fatalf("error generating multus configuration: %v", err)
}
if multusCNIConfig != expectedResult {
tc.t.Fatalf("multus config generation failed.\nExpected:\n%s\nbut GOT:\n%s", expectedResult, multusCNIConfig)
}
}
func documentHelper(pluginInfo string) interface{} {
dp, _ := documentCNIData([]byte(pluginInfo))
return dp
}
func documentCNIData(masterCNIConfigData []byte) (interface{}, error) {
var cniData interface{}
if err := json.Unmarshal(masterCNIConfigData, &cniData); err != nil {
return nil, fmt.Errorf("failed to unmarshall the delegate CNI configuration: %w", err)
}
return cniData, nil
}

229
pkg/config/manager.go Normal file
View File

@@ -0,0 +1,229 @@
// 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 config
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/fsnotify/fsnotify"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
)
// MultusDefaultNetworkName holds the default name of the multus network
const (
multusConfigFileName = "00-multus.conf"
MultusDefaultNetworkName = "multus-cni-network"
userRWPermission = 0600
)
// Manager monitors the configuration of the primary CNI plugin, and
// regenerates multus configuration whenever it gets updated.
type Manager struct {
cniConfigData map[string]interface{}
configWatcher *fsnotify.Watcher
multusConfig *MultusConf
multusConfigDir string
multusConfigFilePath string
primaryCNIConfigPath string
}
// NewManager returns a config manager object, configured to persist the
// configuration to `multusAutoconfigDir`. This constructor will auto-discover
// the primary CNI for which it will delegate.
func NewManager(config MultusConf, multusAutoconfigDir string) (*Manager, error) {
defaultCNIPluginName, err := primaryCNIPluginName(multusAutoconfigDir)
if err != nil {
_ = logging.Errorf("failed to find the primary CNI plugin: %v", err)
return nil, err
}
return newManager(config, multusAutoconfigDir, defaultCNIPluginName)
}
// NewManagerWithExplicitPrimaryCNIPlugin returns a config manager object,
// configured to persist the configuration to `multusAutoconfigDir`. This
// constructor will use the primary CNI plugin indicated by the user, via the
// primaryCNIPluginName variable.
func NewManagerWithExplicitPrimaryCNIPlugin(config MultusConf, multusAutoconfigDir string, primaryCNIPluginName string) (*Manager, error) {
return newManager(config, multusAutoconfigDir, primaryCNIPluginName)
}
func newManager(config MultusConf, multusConfigDir string, defaultCNIPluginName string) (*Manager, error) {
watcher, err := newWatcher(multusConfigDir)
if err != nil {
return nil, err
}
configManager := &Manager{
configWatcher: watcher,
multusConfig: &config,
multusConfigDir: multusConfigDir,
multusConfigFilePath: cniPluginConfigFilePath(multusConfigDir, multusConfigFileName),
primaryCNIConfigPath: cniPluginConfigFilePath(multusConfigDir, defaultCNIPluginName),
}
if err := configManager.loadPrimaryCNIConfigFromFile(); err != nil {
return nil, fmt.Errorf("failed to load the primary CNI configuration as a multus delegate with error '%v'", err)
}
return configManager, nil
}
func (m *Manager) loadPrimaryCNIConfigFromFile() error {
primaryCNIConfigData, err := primaryCNIData(m.primaryCNIConfigPath)
if err != nil {
return logging.Errorf("failed to access the primary CNI configuration from %s: %v", m.primaryCNIConfigPath, err)
}
return m.loadPrimaryCNIConfigurationData(primaryCNIConfigData)
}
// OverrideNetworkName overrides the name of the multus configuration with the
// name of the delegated primary CNI.
func (m *Manager) OverrideNetworkName() error {
name, ok := m.cniConfigData["name"]
if !ok {
return fmt.Errorf("failed to access delegate CNI plugin name")
}
networkName := name.(string)
if networkName == "" {
return fmt.Errorf("the primary CNI Configuration does not feature the network name: %v", m.cniConfigData)
}
return m.multusConfig.Mutate(WithOverriddenName(networkName))
}
func (m *Manager) loadPrimaryCNIConfigurationData(primaryCNIConfigData interface{}) error {
cniConfigData := primaryCNIConfigData.(map[string]interface{})
m.cniConfigData = cniConfigData
return m.multusConfig.Mutate(
withDelegates(cniConfigData),
withCapabilities(cniConfigData))
}
// GenerateConfig generates a multus configuration from its current state
func (m Manager) GenerateConfig() (string, error) {
if err := m.loadPrimaryCNIConfigFromFile(); err != nil {
_ = logging.Errorf("failed to read the primary CNI plugin config from %s", m.primaryCNIConfigPath)
return "", nil
}
return m.multusConfig.Generate()
}
// MonitorDelegatedPluginConfiguration monitors the configuration file pointed
// to by the primaryCNIPluginName attribute, and re-generates the multus
// configuration whenever the primary CNI config is updated.
func (m Manager) MonitorDelegatedPluginConfiguration(shutDown chan struct{}, done chan struct{}) error {
logging.Verbosef("started to watch file %s", m.primaryCNIConfigPath)
for {
select {
case event := <-m.configWatcher.Events:
// we're watching the DIR where the config sits, and the event
// does not concern the primary CNI config. Skip it.
if event.Name != m.primaryCNIConfigPath {
logging.Debugf("skipping un-related event %v", event)
continue
}
if !shouldRegenerateConfig(event) {
continue
}
updatedConfig, err := m.GenerateConfig()
if err != nil {
_ = logging.Errorf("failed to regenerate the multus configuration: %v", err)
}
logging.Debugf("Re-generated MultusCNI config: %s", updatedConfig)
if err := m.PersistMultusConfig(updatedConfig); err != nil {
_ = logging.Errorf("failed to persist the multus configuration: %v", err)
}
if err := m.loadPrimaryCNIConfigFromFile(); err != nil {
_ = logging.Errorf("failed to reload the updated config: %v", err)
}
case err := <-m.configWatcher.Errors:
if err == nil {
continue
}
logging.Errorf("CNI monitoring error %v", err)
case <-shutDown:
logging.Verbosef("Stopped monitoring, closing channel ...")
_ = m.configWatcher.Close()
done <- struct{}{}
return nil
}
}
}
// PersistMultusConfig persists the provided configuration to the disc, with
// Read / Write permissions. The output file path is `<multus auto config dir>/00-multus.conf`
func (m Manager) PersistMultusConfig(config string) error {
return ioutil.WriteFile(m.multusConfigFilePath, []byte(config), userRWPermission)
}
func primaryCNIPluginName(multusAutoconfigDir string) (string, error) {
masterCniConfigFileName, err := findMasterPlugin(multusAutoconfigDir, 120)
if err != nil {
return "", fmt.Errorf("failed to find the cluster master CNI plugin: %w", err)
}
return masterCniConfigFileName, nil
}
func cniPluginConfigFilePath(cniConfigDir string, cniConfigFileName string) string {
return cniConfigDir + fmt.Sprintf("/%s", cniConfigFileName)
}
func newWatcher(cniConfigDir string) (*fsnotify.Watcher, error) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, fmt.Errorf("failed to create new watcher for %q: %v", cniConfigDir, err)
}
defer func() {
// Close watcher on error
if err != nil {
watcher.Close()
}
}()
if err = watcher.Add(cniConfigDir); err != nil {
return nil, fmt.Errorf("failed to add watch on %q: %v", cniConfigDir, err)
}
return watcher, nil
}
func shouldRegenerateConfig(event fsnotify.Event) bool {
return event.Op&fsnotify.Write == fsnotify.Write ||
event.Op&fsnotify.Create == fsnotify.Create
}
func primaryCNIData(masterCNIPluginPath string) (interface{}, error) {
masterCNIConfigData, err := ioutil.ReadFile(masterCNIPluginPath)
if err != nil {
return nil, fmt.Errorf("failed to read the cluster primary CNI config %s: %w", masterCNIPluginPath, err)
}
var cniData interface{}
if err := json.Unmarshal(masterCNIConfigData, &cniData); err != nil {
return nil, fmt.Errorf("failed to unmarshall primary CNI config: %w", err)
}
return cniData, nil
}

133
pkg/config/manager_test.go Normal file
View File

@@ -0,0 +1,133 @@
// 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 config
import (
"fmt"
"io/ioutil"
"os"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const suiteName = "Configuration Manager"
func TestMultusConfigurationManager(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, suiteName)
}
var _ = Describe(suiteName, func() {
const (
primaryCNIPluginName = "00-mycni.conf"
primaryCNIPluginTemplate = `
{
"cniVersion": "0.4.0",
"name": "mycni-name",
"type": "mycni",
"ipam": {},
"dns": {}
}
`
)
var configManager *Manager
var multusConfigDir string
var defaultCniConfig string
BeforeEach(func() {
var err error
multusConfigDir, err = ioutil.TempDir("", "multus-config")
Expect(err).ToNot(HaveOccurred())
Expect(os.MkdirAll(multusConfigDir, 0755)).To(Succeed())
})
BeforeEach(func() {
defaultCniConfig = fmt.Sprintf("%s/%s", multusConfigDir, primaryCNIPluginName)
Expect(ioutil.WriteFile(defaultCniConfig, []byte(primaryCNIPluginTemplate), userRWPermission)).To(Succeed())
multusConf, _ := NewMultusConfig(
primaryCNIName,
cniVersion,
kubeconfig)
var err error
configManager, err = NewManagerWithExplicitPrimaryCNIPlugin(*multusConf, multusConfigDir, primaryCNIPluginName)
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(os.RemoveAll(multusConfigDir)).To(Succeed())
})
It("Generates a configuration, based on the contents of the delegated CNI config file", func() {
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"0.4.0\",\"dns\":{},\"ipam\":{},\"name\":\"mycni-name\",\"type\":\"mycni\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}"
config, err := configManager.GenerateConfig()
Expect(err).NotTo(HaveOccurred())
Expect(config).To(Equal(expectedResult))
})
Context("Updates to the delegate CNI configuration", func() {
var (
doneChannel chan struct{}
stopChannel chan struct{}
)
BeforeEach(func() {
doneChannel = make(chan struct{})
stopChannel = make(chan struct{})
go func() {
Expect(configManager.MonitorDelegatedPluginConfiguration(stopChannel, doneChannel)).To(Succeed())
}()
})
AfterEach(func() {
go func() { stopChannel <- struct{}{} }()
Eventually(<-doneChannel).Should(Equal(struct{}{}))
close(doneChannel)
close(stopChannel)
})
It("Trigger the re-generation of the Multus CNI configuration", func() {
newCNIConfig := "{\"cniVersion\":\"0.4.0\",\"dns\":{},\"ipam\":{},\"name\":\"yoyo-newnet\",\"type\":\"mycni\"}"
Expect(ioutil.WriteFile(defaultCniConfig, []byte(newCNIConfig), userRWPermission)).To(Succeed())
multusCniConfigFile := fmt.Sprintf("%s/%s", multusConfigDir, multusConfigFileName)
Eventually(func() (string, error) {
multusCniData, err := ioutil.ReadFile(multusCniConfigFile)
return string(multusCniData), err
}).Should(Equal(multusConfigFromDelegate(newCNIConfig)))
})
})
When("the user requests the name of the multus configuration to be overridden", func() {
BeforeEach(func() {
Expect(configManager.OverrideNetworkName()).To(Succeed())
})
It("Overrides the name of the multus configuration when requested", func() {
expectedResult := "{\"cniVersion\":\"0.4.0\",\"delegates\":[{\"cniVersion\":\"0.4.0\",\"dns\":{},\"ipam\":{},\"name\":\"mycni-name\",\"type\":\"mycni\"}],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"mycni-name\",\"type\":\"myCNI\"}"
config, err := configManager.GenerateConfig()
Expect(err).NotTo(HaveOccurred())
Expect(config).To(Equal(expectedResult))
})
})
})
func multusConfigFromDelegate(delegateConfig string) string {
return fmt.Sprintf("{\"cniVersion\":\"0.4.0\",\"delegates\":[%s],\"kubeconfig\":\"/a/b/c/kubeconfig.kubeconfig\",\"name\":\"multus-cni-network\",\"type\":\"myCNI\"}", delegateConfig)
}

16
pkg/k8sclient/doc.go Normal file
View File

@@ -0,0 +1,16 @@
// 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 k8sclient is the package that contains the Kubernetes client libraries.
package k8sclient

View File

@@ -15,6 +15,7 @@
package k8sclient
import (
"context"
"encoding/json"
"fmt"
"net"
@@ -40,9 +41,9 @@ import (
nettypes "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
netclient "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/clientset/versioned/typed/k8s.cni.cncf.io/v1"
netutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
"gopkg.in/intel/multus-cni.v3/kubeletclient"
"gopkg.in/intel/multus-cni.v3/logging"
"gopkg.in/intel/multus-cni.v3/types"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/kubeletclient"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
)
const (
@@ -66,22 +67,22 @@ type ClientInfo struct {
// AddPod adds pod into kubernetes
func (c *ClientInfo) AddPod(pod *v1.Pod) (*v1.Pod, error) {
return c.Client.Core().Pods(pod.ObjectMeta.Namespace).Create(pod)
return c.Client.CoreV1().Pods(pod.ObjectMeta.Namespace).Create(context.TODO(), pod, metav1.CreateOptions{})
}
// GetPod gets pod from kubernetes
func (c *ClientInfo) GetPod(namespace, name string) (*v1.Pod, error) {
return c.Client.Core().Pods(namespace).Get(name, metav1.GetOptions{})
return c.Client.CoreV1().Pods(namespace).Get(context.TODO(), name, metav1.GetOptions{})
}
// DeletePod deletes a pod from kubernetes
func (c *ClientInfo) DeletePod(namespace, name string) error {
return c.Client.Core().Pods(namespace).Delete(name, &metav1.DeleteOptions{})
return c.Client.CoreV1().Pods(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
}
// AddNetAttachDef adds net-attach-def into kubernetes
func (c *ClientInfo) AddNetAttachDef(netattach *nettypes.NetworkAttachmentDefinition) (*nettypes.NetworkAttachmentDefinition, error) {
return c.NetClient.NetworkAttachmentDefinitions(netattach.ObjectMeta.Namespace).Create(netattach)
return c.NetClient.NetworkAttachmentDefinitions(netattach.ObjectMeta.Namespace).Create(context.TODO(), netattach, metav1.CreateOptions{})
}
// Eventf puts event into kubernetes events
@@ -91,7 +92,7 @@ func (c *ClientInfo) Eventf(object runtime.Object, eventtype, reason, messageFmt
}
}
func (e *NoK8sNetworkError) Error() string { return string(e.message) }
func (e *NoK8sNetworkError) Error() string { return e.message }
// SetNetworkStatus sets network status into Pod annotation
func SetNetworkStatus(client *ClientInfo, k8sArgs *types.K8sArgs, netStatus []nettypes.NetworkStatus, conf *types.NetConf) error {
@@ -113,11 +114,16 @@ func SetNetworkStatus(client *ClientInfo, k8sArgs *types.K8sArgs, netStatus []ne
podName := string(k8sArgs.K8S_POD_NAME)
podNamespace := string(k8sArgs.K8S_POD_NAMESPACE)
podUID := string(k8sArgs.K8S_POD_UID)
pod, err := client.GetPod(podNamespace, podName)
if err != nil {
return logging.Errorf("SetNetworkStatus: failed to query the pod %v in out of cluster comm: %v", podName, err)
}
if podUID != "" && string(pod.UID) != podUID && !IsStaticPod(pod) {
return logging.Errorf("SetNetworkStatus: expected pod %s/%s UID %q but got %q from Kube API", podNamespace, podName, podUID, pod.UID)
}
if netStatus != nil {
err = netutils.SetNetworkStatus(client.Client, pod, netStatus)
if err != nil {
@@ -241,7 +247,7 @@ func parsePodNetworkAnnotation(podNetworks, defaultNamespace string) ([]*types.N
func getKubernetesDelegate(client *ClientInfo, net *types.NetworkSelectionElement, confdir string, pod *v1.Pod, resourceMap map[string]*types.ResourceInfo) (*types.DelegateNetConf, map[string]*types.ResourceInfo, error) {
logging.Debugf("getKubernetesDelegate: %v, %v, %s, %v, %v", client, net, confdir, pod, resourceMap)
customResource, err := client.NetClient.NetworkAttachmentDefinitions(net.Namespace).Get(net.Name, metav1.GetOptions{})
customResource, err := client.NetClient.NetworkAttachmentDefinitions(net.Namespace).Get(context.TODO(), net.Name, metav1.GetOptions{})
if err != nil {
errMsg := fmt.Sprintf("cannot find a network-attachment-definition (%s) in namespace (%s): %v", net.Name, net.Namespace, err)
if client != nil {
@@ -258,7 +264,7 @@ func getKubernetesDelegate(client *ClientInfo, net *types.NetworkSelectionElemen
logging.Debugf("getKubernetesDelegate: found resourceName annotation : %s", resourceName)
if resourceMap == nil {
ck, err := kubeletclient.GetResourceClient()
ck, err := kubeletclient.GetResourceClient("")
if err != nil {
return nil, resourceMap, logging.Errorf("getKubernetesDelegate: failed to get a ResourceClient instance: %v", err)
}
@@ -284,7 +290,7 @@ func getKubernetesDelegate(client *ClientInfo, net *types.NetworkSelectionElemen
return nil, resourceMap, err
}
delegate, err := types.LoadDelegateNetConf(configBytes, net, deviceID)
delegate, err := types.LoadDelegateNetConf(configBytes, net, deviceID, resourceName)
if err != nil {
return nil, resourceMap, err
}
@@ -336,7 +342,7 @@ func TryLoadPodDelegates(pod *v1.Pod, conf *types.NetConf, clientInfo *ClientInf
networks, err := GetPodNetwork(pod)
if networks != nil {
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, conf.ConfDir, conf.NamespaceIsolation, resourceMap)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, conf, resourceMap)
if err != nil {
if _, ok := err.(*NoK8sNetworkError); ok {
@@ -360,13 +366,19 @@ func TryLoadPodDelegates(pod *v1.Pod, conf *types.NetConf, clientInfo *ClientInf
}
if isGatewayConfigured == true {
types.CheckGatewayConfig(conf.Delegates)
err = types.CheckGatewayConfig(conf.Delegates)
if err != nil {
return 0, nil, err
}
}
return len(delegates), clientInfo, nil
return len(delegates), clientInfo, err
}
return 0, clientInfo, nil
if _, ok := err.(*NoK8sNetworkError); ok {
return 0, clientInfo, nil
}
return 0, clientInfo, err
}
// GetK8sClient gets client info from kubeconfig
@@ -448,8 +460,9 @@ func GetPodNetwork(pod *v1.Pod) ([]*types.NetworkSelectionElement, error) {
}
// GetNetworkDelegates returns delegatenetconf from net-attach-def annotation in pod
func GetNetworkDelegates(k8sclient *ClientInfo, pod *v1.Pod, networks []*types.NetworkSelectionElement, confdir string, confnamespaceIsolation bool, resourceMap map[string]*types.ResourceInfo) ([]*types.DelegateNetConf, error) {
logging.Debugf("GetNetworkDelegates: %v, %v, %v, %v, %v, %v", k8sclient, pod, networks, confdir, confnamespaceIsolation, resourceMap)
func GetNetworkDelegates(k8sclient *ClientInfo, pod *v1.Pod, networks []*types.NetworkSelectionElement, conf *types.NetConf, resourceMap map[string]*types.ResourceInfo) ([]*types.DelegateNetConf, error) {
logging.Debugf("GetNetworkDelegates: %v, %v, %v, %v, %v", k8sclient, pod, networks, conf, resourceMap)
// Read all network objects referenced by 'networks'
var delegates []*types.DelegateNetConf
defaultNamespace := pod.ObjectMeta.Namespace
@@ -458,16 +471,16 @@ func GetNetworkDelegates(k8sclient *ClientInfo, pod *v1.Pod, networks []*types.N
// The pods namespace (stored as defaultNamespace, does not equal the annotation's target namespace in net.Namespace)
// In the case that this is a mismatch when namespaceisolation is enabled, this should be an error.
if confnamespaceIsolation {
if conf.NamespaceIsolation {
if defaultNamespace != net.Namespace {
// There is an exception however, we always allow a reference to the default namespace.
if net.Namespace != "default" {
// We allow exceptions based on the specified list of non-isolated namespaces (and/or "default" namespace, by default)
if !isValidNamespaceReference(net.Namespace, conf.NonIsolatedNamespaces) {
return nil, logging.Errorf("GetNetworkDelegates: namespace isolation enabled, annotation violates permission, pod is in namespace %v but refers to target namespace %v", defaultNamespace, net.Namespace)
}
}
}
delegate, updatedResourceMap, err := getKubernetesDelegate(k8sclient, net, confdir, pod, resourceMap)
delegate, updatedResourceMap, err := getKubernetesDelegate(k8sclient, net, conf.ConfDir, pod, resourceMap)
if err != nil {
return nil, logging.Errorf("GetNetworkDelegates: failed getting the delegate: %v", err)
}
@@ -478,6 +491,15 @@ func GetNetworkDelegates(k8sclient *ClientInfo, pod *v1.Pod, networks []*types.N
return delegates, nil
}
func isValidNamespaceReference(targetns string, allowednamespaces []string) bool {
for _, eachns := range allowednamespaces {
if eachns == targetns {
return true
}
}
return false
}
func getNetDelegate(client *ClientInfo, pod *v1.Pod, netname, confdir, namespace string, resourceMap map[string]*types.ResourceInfo) (*types.DelegateNetConf, map[string]*types.ResourceInfo, error) {
logging.Debugf("getNetDelegate: %v, %v, %v, %s", client, netname, confdir, namespace)
// option1) search CRD object for the network
@@ -494,14 +516,14 @@ func getNetDelegate(client *ClientInfo, pod *v1.Pod, netname, confdir, namespace
var configBytes []byte
configBytes, err = netutils.GetCNIConfigFromFile(netname, confdir)
if err == nil {
delegate, err := types.LoadDelegateNetConf(configBytes, nil, "")
delegate, err := types.LoadDelegateNetConf(configBytes, nil, "", "")
if err != nil {
return nil, resourceMap, err
}
return delegate, resourceMap, nil
}
// option3) search directry
// option3) search directory
fInfo, err := os.Stat(netname)
if err == nil {
if fInfo.IsDir() {
@@ -513,7 +535,7 @@ func getNetDelegate(client *ClientInfo, pod *v1.Pod, netname, confdir, namespace
var configBytes []byte
configBytes, err = netutils.GetCNIConfigFromFile("", netname)
if err == nil {
delegate, err := types.LoadDelegateNetConf(configBytes, nil, "")
delegate, err := types.LoadDelegateNetConf(configBytes, nil, "", "")
if err != nil {
return nil, resourceMap, err
}
@@ -597,3 +619,16 @@ func tryLoadK8sPodDefaultNetwork(kubeClient *ClientInfo, pod *v1.Pod, conf *type
return delegate, nil
}
// ConfigSourceAnnotationKey specifies kubernetes annotation, defined in k8s.io/kubernetes/pkg/kubelet/types
const ConfigSourceAnnotationKey = "kubernetes.io/config.source"
// IsStaticPod returns true if the pod is static pod.
func IsStaticPod(pod *v1.Pod) bool {
if pod.Annotations != nil {
if source, ok := pod.Annotations[ConfigSourceAnnotationKey]; ok == true {
return source != "api"
}
}
return false
}

View File

@@ -23,10 +23,10 @@ import (
"testing"
types020 "github.com/containernetworking/cni/pkg/types/020"
testutils "gopkg.in/intel/multus-cni.v3/testing"
testutils "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/testing"
"github.com/containernetworking/cni/pkg/skel"
"gopkg.in/intel/multus-cni.v3/types"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
nettypes "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
netfake "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/clientset/versioned/fake"
@@ -54,10 +54,29 @@ func NewFakeClientInfo() *ClientInfo {
var _ = Describe("k8sclient operations", func() {
var tmpDir string
var err error
var genericConf string
var args *skel.CmdArgs
const fakePodName string = "testPod"
BeforeEach(func() {
tmpDir, err = ioutil.TempDir("", "multus_tmp")
Expect(err).NotTo(HaveOccurred())
genericConf = `{
"name":"node-cni-network",
"type":"multus",
"delegates": [{
"name": "weave1",
"cniVersion": "0.2.0",
"type": "weave-net"
}],
"kubeconfig":"/etc/kubernetes/node-kubeconfig.yaml"
}`
args = &skel.CmdArgs{
// Values come from NewFakePod()
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s;K8S_POD_UID=%s", fakePodName, "test", "testUID"),
}
})
AfterEach(func() {
@@ -66,7 +85,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves delegates from kubernetes using simple format annotation", func() {
fakePod := testutils.NewFakePod("testpod", "net1,net2", "")
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
net1 := `{
"name": "net1",
"type": "mynet",
@@ -83,10 +102,6 @@ var _ = Describe("k8sclient operations", func() {
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -104,7 +119,9 @@ var _ = Describe("k8sclient operations", func() {
Expect(err).NotTo(HaveOccurred())
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(2))
@@ -117,15 +134,12 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails when the network does not exist", func() {
fakePod := testutils.NewFakePod("testpod", "net1,net2", "")
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
net3 := `{
"name": "net3",
"type": "mynet3",
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -139,13 +153,15 @@ var _ = Describe("k8sclient operations", func() {
Expect(err).NotTo(HaveOccurred())
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(len(delegates)).To(Equal(0))
Expect(err).To(MatchError("GetNetworkDelegates: failed getting the delegate: getKubernetesDelegate: cannot find a network-attachment-definition (net1) in namespace (test): network-attachment-definitions.k8s.cni.cncf.io \"net1\" not found"))
})
It("retrieves delegates from kubernetes using JSON format annotation", func() {
fakePod := testutils.NewFakePod("testpod", `[
fakePod := testutils.NewFakePod(fakePodName, `[
{"name":"net1"},
{
"name":"net2",
@@ -157,9 +173,6 @@ var _ = Describe("k8sclient operations", func() {
"namespace":"other-ns"
}
]`, "")
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -188,7 +201,9 @@ var _ = Describe("k8sclient operations", func() {
pod, err := clientInfo.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(3))
@@ -201,10 +216,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails when the JSON format annotation is invalid", func() {
fakePod := testutils.NewFakePod("testpod", "[adsfasdfasdfasf]", "")
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
fakePod := testutils.NewFakePod(fakePodName, "[adsfasdfasdfasf]", "")
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -219,7 +231,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("can set the default-gateway on an additional interface", func() {
fakePod := testutils.NewFakePod("testpod", `[
fakePod := testutils.NewFakePod(fakePodName, `[
{"name":"net1"},
{
"name":"net2",
@@ -230,9 +242,6 @@ var _ = Describe("k8sclient operations", func() {
"namespace":"other-ns"
}
]`, "")
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -262,7 +271,9 @@ var _ = Describe("k8sclient operations", func() {
pod, err := clientInfo.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(3))
@@ -275,10 +286,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves delegates from kubernetes using on-disk config files", func() {
fakePod := testutils.NewFakePod("testpod", "net1,net2", "")
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -306,7 +314,9 @@ var _ = Describe("k8sclient operations", func() {
pod, err := clientInfo.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(2))
@@ -317,10 +327,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("injects network name into minimal thick plugin CNI config", func() {
fakePod := testutils.NewFakePod("testpod", "net1", "")
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
fakePod := testutils.NewFakePod(fakePodName, "net1", "")
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -333,7 +340,9 @@ var _ = Describe("k8sclient operations", func() {
pod, err := clientInfo.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(1))
@@ -342,10 +351,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails when on-disk config file is not valid", func() {
fakePod := testutils.NewFakePod("testpod", "net1,net2", "")
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -368,13 +374,15 @@ var _ = Describe("k8sclient operations", func() {
pod, err := clientInfo.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(len(delegates)).To(Equal(0))
Expect(err).To(MatchError(fmt.Sprintf("GetNetworkDelegates: failed getting the delegate: GetCNIConfig: err in GetCNIConfigFromFile: Error loading CNI config file %s: error parsing configuration: invalid character 'a' looking for beginning of value", net2Name)))
})
It("retrieves cluster network from CRD", func() {
fakePod := testutils.NewFakePod("testpod", "", "")
fakePod := testutils.NewFakePod(fakePodName, "", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -384,10 +392,6 @@ var _ = Describe("k8sclient operations", func() {
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -406,7 +410,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves default networks from CRD", func() {
fakePod := testutils.NewFakePod("testpod", "", "")
fakePod := testutils.NewFakePod(fakePodName, "", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -417,10 +421,6 @@ var _ = Describe("k8sclient operations", func() {
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -444,7 +444,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("ignore default networks from CRD in case of kube-system namespace", func() {
fakePod := testutils.NewFakePod("testpod", "", "")
fakePod := testutils.NewFakePod(fakePodName, "", "")
// overwrite namespace
fakePod.ObjectMeta.Namespace = "kube-system"
conf := `{
@@ -457,10 +457,6 @@ var _ = Describe("k8sclient operations", func() {
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -482,7 +478,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves cluster network from file", func() {
fakePod := testutils.NewFakePod("testpod", "", "")
fakePod := testutils.NewFakePod(fakePodName, "", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -493,10 +489,6 @@ var _ = Describe("k8sclient operations", func() {
netConf.ConfDir = tmpDir
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -521,7 +513,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves cluster network from path", func() {
fakePod := testutils.NewFakePod("testpod", "", "")
fakePod := testutils.NewFakePod(fakePodName, "", "")
conf := fmt.Sprintf(`{
"name":"node-cni-network",
"type":"multus",
@@ -531,10 +523,6 @@ var _ = Describe("k8sclient operations", func() {
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -557,7 +545,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("Error in case of CRD not found", func() {
fakePod := testutils.NewFakePod("testpod", "", "")
fakePod := testutils.NewFakePod(fakePodName, "", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -567,10 +555,6 @@ var _ = Describe("k8sclient operations", func() {
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -583,7 +567,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("overwrite cluster network when Pod annotation is set", func() {
fakePod := testutils.NewFakePod("testpod", "", "net1")
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -594,10 +578,6 @@ var _ = Describe("k8sclient operations", func() {
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -625,7 +605,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails with bad confdir", func() {
fakePod := testutils.NewFakePod("testpod", "", "net1")
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -636,10 +616,6 @@ var _ = Describe("k8sclient operations", func() {
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -661,7 +637,7 @@ var _ = Describe("k8sclient operations", func() {
It("overwrite multus config when Pod annotation is set", func() {
fakePod := testutils.NewFakePod("testpod", "", "net1")
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -676,10 +652,6 @@ var _ = Describe("k8sclient operations", func() {
Expect(netConf.Delegates[0].Conf.Type).To(Equal("mynet2"))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -698,7 +670,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails with no kubeclient and invalid kubeconfig", func() {
fakePod := testutils.NewFakePod("testpod", "", "net1")
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -713,10 +685,6 @@ var _ = Describe("k8sclient operations", func() {
Expect(netConf.Delegates[0].Conf.Type).To(Equal("mynet2"))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -732,7 +700,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails with no kubeclient and no kubeconfig", func() {
fakePod := testutils.NewFakePod("testpod", "", "net1")
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -747,10 +715,6 @@ var _ = Describe("k8sclient operations", func() {
Expect(netConf.Delegates[0].Conf.Type).To(Equal("mynet2"))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -796,7 +760,7 @@ users:
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBMUxyZmUxaXNQaTNDYng4eHRDWG1QTlE2L0xXYVVEa1ZRNVBkdGtQK3dVYmdJUFJ6CmNtRzNEcnUya0kvamsxMGQzaUgzOVZlZDlHVjczbnZyVm5Id0pzZEF5QzVmYlRTQVBUVVNJOW90MVFGbENRUGcKQ2JLbjBRbmxUeUhXMmNnTis2Q3BWK0U4dFpWajJ2cDZoTDlmR0s2bUJCL2I4VFN3RmYxRmtnWC9hTGN1emZDaApmVFNDOWlRTk15cEFjamZadEkzWiszbHVzSVB6TVpBNTZPZXNodzlnRkJHUkw3RzZHWmZKcG9OaGVxTDlmandUClRkNURlbVZXcUxUR05ZNWhhV3hicy9VbThuM0QvdlV1QkVPYTYxQ2cvcGljUkU3Um14SmFJRWJiQlNTV1dkSDMKWDlrem5RdHJrUXloL28xZnpSV1pxeWFCY3hxR1FWN0JSSzFtSndJREFRQUJBb0lCQUJ0bjA4QzFSTU5oNjhtYgpFREV3TE1BcmEwb0JMMWNrYzN2WVFkam9XNXFVd2UwYzhQNk1YaVAweE9sTTBEbTg1a3NtdnlZSldwMFFzZXVRCnRWbldwZVNwQ015QlJPUHh2bytrRmFrdXczYk1qaktpSUN1L3EyVC96RjNzY3h4dGJIZTlVL094WGJ2YStobE0KNlpuT2ViYlpVU1A0NHNIcFVzSVNkZk1BK00ySmg1UFJibGZWaUFEY1hxNFR5RU1JaStzRkhOcFIrdmdWZzRFawp4RmFVaS83V0E2YUxWVzBUTzREdjMwbTJ0TVczWXN1bk1LTU0xOTNyUEZrU0dEdFpheWV2Z0JDeURXaFhOTEo2Clh1cTNxSUg4bFE2bzRBUjMvcDc1ZW9hOCtrVzVmT3o2UWF3WnpPYlBENkRCQlVOYVM1YklXaVV1dmx5L0JlM20KZnlxK3NRRUNnWUVBMW84R3l6ODk2bFhwdU1yVXVsb2orbGp5U0FaNkpLOCsyaFMvQnpyREx6NlpvY3FzKzg3awpVUkwzKy9LL1pja2pIMVVDeXROVVZ6Q3RKaG4zZmdLS2dpQWhsU0pNRzhqc05sbEkydFZSazNZZ1RCcUg2bXZxCit3citsTUxoUDZxbWFObUx3QXljY2lEanpMdXlRdjhVOFhKazJOdVFsQlFwbkt2eWJIRGdxSUVDZ1lFQS9kRnMKazNlYmRNNFAxV2psYXJoRTV5blpuRmdQbDg1L2Vudk4rQ1oxcStlMGxYendaUGswdWdJUWozYyt2UEpLWlh0OApLWk1HQjM0N2VLNlFIL3J1a2xRWXlLOStHeUV1YnRJQUZ2NWFrYXZxV1haR1p5ZC9QdDR1V09adXMrd3BnSG00CkxFY0lzZElsYkpFY2RJTzJyb3FaY0VNY3FEbGtXcTdwQWxqU2VxY0NnWUJYdUQ0RTFxUlByRFJVRXNrS0wxUksKUkJjNkR6dmN4N0VncEI2OXErNms0Q2tibHF0R2YvMmtqK2JISVNYVFRYcUlrczhEY1ljbjVvVEQ4UlhZZE4xLworZmNBNi9iRjNVMkZvdGRBY0xwYldZNDJ6eG9HWTN5OGluQXZEY1hkcTcxQlhML2dFc2ZiZVVycEowdm9URFdaCnlUVWwzQTZ1RzlndmI3VTdWS0xsQVFLQmdBTmNscmVOU2YzT0ROK2l1QWNsMGFQT0poZXdBdVRiMDB4bi8xNWUKQkFqMjFLbDJNaWprTkJLU25HMktBc2ExM3M1aFNFKzBwc3ZLbkRjSStOZXpseDFSQjlNQW9BYno5WTE2TW80YgphRSt0bXpqOEhBcVp0MUc1MTV0TjBnR0lDelNzYUFnT0dNdGlJU1RDOTBHRHpST2F1bFdHVGdiY1c3dm52U1pPCnp0clpBb0dBWmtIRWV5em16Z2cxR3dtTzN3bmljSHRMR1BQRFhiSW53NTdsdkIrY3lyd0FrVEs1MlFScTM0VkMKRDhnQWFwMTU2OWlWUER3YlgrNkpBQk1WQ2tNUmdxMjdHanUzN0pVY2Fib2g1YzJQeTBYNUlhUG8rek1hWHgvQwpqbjUvUW5YandjU1MrRU5hL1lXVWcxWEVjQjJYdEM0UExCdGUycitrUTVLbFNOREcxSTQ9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==`
kubeletconf.Write([]byte(kubeletconfDef))
fakePod := testutils.NewFakePod("testpod", "", "net1")
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
conf := fmt.Sprintf(`{
"name":"node-cni-network",
"type":"multus",
@@ -811,10 +775,6 @@ users:
Expect(netConf.Delegates[0].Conf.Type).To(Equal("mynet2"))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -829,7 +789,7 @@ users:
})
It("Errors when namespace isolation is violated", func() {
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -842,7 +802,6 @@ users:
"namespaceIsolation": true
}`
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
net1 := `{
@@ -851,10 +810,6 @@ users:
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -867,12 +822,60 @@ users:
pod, err := clientInfo.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
_, err = GetNetworkDelegates(clientInfo, pod, networks, tmpDir, netConf.NamespaceIsolation, nil)
netConf, err := types.LoadNetConf([]byte(conf))
netConf.ConfDir = tmpDir
_, err = GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError("GetNetworkDelegates: namespace isolation enabled, annotation violates permission, pod is in namespace test but refers to target namespace kube-system"))
})
It("Properly allows a specified namespace reference when namespace isolation is enabled", func() {
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
"delegates": [{
"name": "weave1",
"cniVersion": "0.2.0",
"type": "weave-net"
}],
"kubeconfig":"/etc/kubernetes/node-kubeconfig.yaml",
"namespaceIsolation": true,
"globalNamespaces": "kube-system,donkey-kong"
}`
Expect(err).NotTo(HaveOccurred())
net1 := `{
"name": "net1",
"type": "mynet",
"cniVersion": "0.2.0"
}`
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
_, err = clientInfo.AddNetAttachDef(testutils.NewFakeNetAttachDef("kube-system", "net1", net1))
Expect(err).NotTo(HaveOccurred())
k8sArgs, err := GetK8sArgs(args)
Expect(err).NotTo(HaveOccurred())
pod, err := clientInfo.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
netConf, err := types.LoadNetConf([]byte(conf))
netConf.ConfDir = tmpDir
_, err = GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
Expect(err).NotTo(HaveOccurred())
})
Context("Error function", func() {
It("Returns proper error message", func() {
err := &NoK8sNetworkError{"no kubernetes network found"}
@@ -882,7 +885,7 @@ users:
Context("getDefaultNetDelegateCRD", func() {
It("fails when netConf contains bad confDir", func() {
fakePod := testutils.NewFakePod("testpod", "", "net1")
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -893,10 +896,6 @@ users:
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -916,9 +915,9 @@ users:
Context("GetK8sArgs", func() {
It("fails when provided with bad format", func() {
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME:%s;K8S_POD_NAMESPACE:%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
args = &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME:%s;K8S_POD_NAMESPACE:%s;K8S_POD_UID:%s", fakePod.Name, fakePod.Namespace, fakePod.UID),
}
// using colon instead of equals sign makes an invalid CmdArgs
@@ -929,7 +928,7 @@ users:
Context("getKubernetesDelegate", func() {
It("failed to get a ResourceClient instance", func() {
fakePod := testutils.NewFakePod("testpod", "net1,net2", "")
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
net1 := `{
"name": "net1",
"type": "mynet",
@@ -945,9 +944,6 @@ users:
"type": "mynet3",
"cniVersion": "0.2.0"
}`
// args := &skel.CmdArgs{
// Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
// }
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -966,17 +962,16 @@ users:
networks, err := GetPodNetwork(fakePod)
Expect(err).NotTo(HaveOccurred())
_, err = GetNetworkDelegates(clientInfo, fakePod, networks, tmpDir, false, nil)
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
_, err = GetNetworkDelegates(clientInfo, fakePod, networks, netConf, nil)
Expect(err).To(HaveOccurred())
})
})
Context("parsePodNetworkObjectName", func() {
It("fails to get podnetwork given bad annotation values", func() {
fakePod := testutils.NewFakePod("testpod", "net1", "")
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
fakePod := testutils.NewFakePod(fakePodName, "net1", "")
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -1009,7 +1004,7 @@ users:
Context("setPodNetworkAnnotation", func() {
It("Sets pod network annotations without error", func() {
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
net1 := `{
"name": "net1",
@@ -1017,10 +1012,6 @@ users:
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -1094,10 +1085,6 @@ users:
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -1105,6 +1092,9 @@ users:
_, err = clientInfo.AddNetAttachDef(testutils.NewFakeNetAttachDef("kube-system", "net1", net1))
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s;K8S_POD_UID=%s", fakePod.Name, fakePod.Namespace, "blahblah"),
}
k8sArgs, err := GetK8sArgs(args)
Expect(err).NotTo(HaveOccurred())
@@ -1131,7 +1121,7 @@ users:
// TODO Still figuring this next one out. deals with exponentialBackoff
// It("Fails to set pod network annotations without error", func() {
// fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
// fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
// net1 := `{
// "name": "net1",
@@ -1139,10 +1129,6 @@ users:
// "cniVersion": "0.2.0"
// }`
// args := &skel.CmdArgs{
// Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
// }
// clientInfo := NewFakeClientInfo()
// _, err := clientInfo.AddPod(fakePod)
// Expect(err).NotTo(HaveOccurred())
@@ -1162,7 +1148,7 @@ users:
})
Context("SetNetworkStatus", func() {
It("Sets network status without error", func() {
It("Sets network status without error when pod UIDs match", func() {
result := &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
@@ -1184,16 +1170,16 @@ users:
}
}`
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0")
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0", "")
Expect(err).NotTo(HaveOccurred())
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin, nil)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
@@ -1204,10 +1190,6 @@ users:
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -1221,6 +1203,123 @@ users:
Expect(err).NotTo(HaveOccurred())
})
It("Sets pod network annotations without error when runtime does not provide a pod UID", func() {
result := &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
IP: *testutils.EnsureCIDR("1.1.1.2/24"),
},
}
conf := `{
"name": "node-cni-network",
"type": "multus",
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
"delegates": [{
"type": "weave-net"
}],
"runtimeConfig": {
"portMappings": [
{"hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
}
}`
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0", "")
Expect(err).NotTo(HaveOccurred())
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin, nil)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
net1 := `{
"name": "net1",
"type": "mynet",
"cniVersion": "0.2.0"
}`
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
_, err = clientInfo.AddNetAttachDef(testutils.NewFakeNetAttachDef("kube-system", "net1", net1))
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.Name, fakePod.Namespace),
}
k8sArgs, err := GetK8sArgs(args)
Expect(err).NotTo(HaveOccurred())
err = SetNetworkStatus(clientInfo, k8sArgs, netstatus, netConf)
Expect(err).NotTo(HaveOccurred())
})
It("Fails to set pod network annotations when pod UIDs don't match", func() {
result := &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
IP: *testutils.EnsureCIDR("1.1.1.2/24"),
},
}
conf := `{
"name": "node-cni-network",
"type": "multus",
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
"delegates": [{
"type": "weave-net"
}],
"runtimeConfig": {
"portMappings": [
{"hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
}
}`
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0", "")
Expect(err).NotTo(HaveOccurred())
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin, nil)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
net1 := `{
"name": "net1",
"type": "mynet",
"cniVersion": "0.2.0"
}`
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
_, err = clientInfo.AddNetAttachDef(testutils.NewFakeNetAttachDef("kube-system", "net1", net1))
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s;K8S_POD_UID=%s", fakePod.Name, fakePod.Namespace, "foobar"),
}
k8sArgs, err := GetK8sArgs(args)
Expect(err).NotTo(HaveOccurred())
err = SetNetworkStatus(clientInfo, k8sArgs, netstatus, netConf)
Expect(err.Error()).To(ContainSubstring(fmt.Sprintf("expected pod %s/%s UID %q but got %q from Kube API",
fakePod.Namespace, fakePod.Name, string(k8sArgs.K8S_POD_UID), fakePod.UID)))
})
It("Sets network status with kubeclient built from kubeconfig and attempts to connect", func() {
kubeletconf, err := os.Create("/etc/kubernetes/kubelet.conf")
kubeletconfDef := `apiVersion: v1
@@ -1266,16 +1365,16 @@ users:
}
}`
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0")
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0", "")
Expect(err).NotTo(HaveOccurred())
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin, nil)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
@@ -1286,10 +1385,6 @@ users:
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -1326,16 +1421,16 @@ users:
}`
// note that the provided kubeconfig is invalid
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "")
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "", "")
Expect(err).NotTo(HaveOccurred())
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin, nil)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
@@ -1346,10 +1441,6 @@ users:
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -1385,16 +1476,16 @@ users:
}
}`
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0")
delegate, err := types.LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0", "")
Expect(err).NotTo(HaveOccurred())
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin, nil)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
@@ -1405,10 +1496,6 @@ users:
"cniVersion": "0.2.0"
}`
args := &skel.CmdArgs{
Args: fmt.Sprintf("K8S_POD_NAME=%s;K8S_POD_NAMESPACE=%s", fakePod.ObjectMeta.Name, fakePod.ObjectMeta.Namespace),
}
clientInfo := NewFakeClientInfo()
_, err = clientInfo.AddPod(fakePod)
Expect(err).NotTo(HaveOccurred())

17
pkg/kubeletclient/doc.go Normal file
View File

@@ -0,0 +1,17 @@
// 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 kubeletclient is the package that contains the kubelet's libraries that
// controls podresource API in kubelet
package kubeletclient

View File

@@ -1,57 +1,56 @@
package kubeletclient
import (
"net/url"
"os"
"path/filepath"
"time"
"golang.org/x/net/context"
"gopkg.in/intel/multus-cni.v3/checkpoint"
"gopkg.in/intel/multus-cni.v3/logging"
"gopkg.in/intel/multus-cni.v3/types"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/checkpoint"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
v1 "k8s.io/api/core/v1"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
"k8s.io/kubernetes/pkg/kubelet/apis/podresources"
podresourcesapi "k8s.io/kubernetes/pkg/kubelet/apis/podresources/v1alpha1"
"k8s.io/kubernetes/pkg/kubelet/util"
)
const (
defaultKubeletSocketFile = "kubelet.sock"
defaultPodResourcesMaxSize = 1024 * 1024 * 16 // 16 Mb
)
var (
kubeletSocket string
defaultPodResourcesPath = "/var/lib/kubelet/pod-resources"
defaultPodResourcesPath = "/var/lib/kubelet/pod-resources"
)
// GetResourceClient returns an instance of ResourceClient interface initialized with Pod resource information
func GetResourceClient() (types.ResourceClient, error) {
func GetResourceClient(kubeletSocket string) (types.ResourceClient, error) {
if kubeletSocket == "" {
kubeletSocket, _ = util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
}
// If Kubelet resource API endpoint exist use that by default
// Or else fallback with checkpoint file
if hasKubeletAPIEndpoint() {
if hasKubeletAPIEndpoint(kubeletSocket) {
logging.Debugf("GetResourceClient: using Kubelet resource API endpoint")
return getKubeletClient()
return getKubeletClient(kubeletSocket)
}
logging.Debugf("GetResourceClient: using Kubelet device plugin checkpoint")
return checkpoint.GetCheckpoint()
}
func getKubeletClient() (types.ResourceClient, error) {
func getKubeletClient(kubeletSocket string) (types.ResourceClient, error) {
newClient := &kubeletClient{}
if kubeletSocket == "" {
kubeletSocket = util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
kubeletSocket, _ = util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
}
client, conn, err := podresources.GetClient(kubeletSocket, 10*time.Second, defaultPodResourcesMaxSize)
client, conn, err := podresources.GetV1Client(kubeletSocket, 10*time.Second, defaultPodResourcesMaxSize)
if err != nil {
return nil, logging.Errorf("getKubeletClient: error getting grpc client: %v\n", err)
}
defer conn.Close()
if err := newClient.getPodResources(client); err != nil {
return nil, logging.Errorf("getKubeletClient: error ge tting pod resources from client: %v\n", err)
return nil, logging.Errorf("getKubeletClient: error getting pod resources from client: %v\n", err)
}
return newClient, nil
@@ -102,10 +101,13 @@ func (rc *kubeletClient) GetPodResourceMap(pod *v1.Pod) (map[string]*types.Resou
return resourceMap, nil
}
func hasKubeletAPIEndpoint() bool {
func hasKubeletAPIEndpoint(endpoint string) bool {
u, err := url.Parse(endpoint)
if err != nil {
return false
}
// Check for kubelet resource API socket file
kubeletAPISocket := filepath.Join(defaultPodResourcesPath, defaultKubeletSocketFile)
if _, err := os.Stat(kubeletAPISocket); err != nil {
if _, err := os.Stat(u.Path); err != nil {
logging.Debugf("hasKubeletAPIEndpoint: error looking up kubelet resource api socket file: %q", err)
return false
}

View File

@@ -15,8 +15,8 @@ import (
k8sTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/kubelet/util"
mtypes "gopkg.in/intel/multus-cni.v3/types"
podresourcesapi "k8s.io/kubernetes/pkg/kubelet/apis/podresources/v1alpha1"
mtypes "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
)
var (
@@ -29,6 +29,13 @@ type fakeResourceServer struct {
server *grpc.Server
}
/* This is for 1.21.x or later. Uncomment it once we update vendor here!
//TODO: This is stub code for test, but we may need to change for the testing we use this API in the future...
func (m *fakeResourceServer) GetAllocatableResources(ctx context.Context, req *podresourcesapi.AllocatableResourcesRequest) (*podresourcesapi.AllocatableResourcesResponse, error) {
return &podresourcesapi.AllocatableResourcesResponse{}, nil
}
*/
func (m *fakeResourceServer) List(ctx context.Context, req *podresourcesapi.ListPodResourcesRequest) (*podresourcesapi.ListPodResourcesResponse, error) {
podName := "pod-name"
podNamespace := "pod-namespace"
@@ -63,25 +70,28 @@ func TestKubeletclient(t *testing.T) {
RunSpecs(t, "Kubeletclient Suite")
}
var testKubeletSocket string
func setUp() error {
tempSocketDir, err := ioutil.TempDir("", "kubelet-resource-client")
if err != nil {
return err
}
defaultPodResourcesPath = filepath.Join(tempSocketDir, defaultPodResourcesPath)
testingPodResourcesPath := filepath.Join(tempSocketDir, defaultPodResourcesPath)
if err := os.MkdirAll(defaultPodResourcesPath, os.ModeDir); err != nil {
if err := os.MkdirAll(testingPodResourcesPath, os.ModeDir); err != nil {
return err
}
socketDir = defaultPodResourcesPath
socketDir = testingPodResourcesPath
socketName = filepath.Join(socketDir, "kubelet.sock")
testKubeletSocket = socketName
fakeServer = &fakeResourceServer{server: grpc.NewServer()}
podresourcesapi.RegisterPodResourcesListerServer(fakeServer.server, fakeServer)
lis, err := util.CreateListener(socketName)
if err != nil {
return nil
return err
}
go fakeServer.server.Serve(lis)
return nil
@@ -91,10 +101,8 @@ func tearDown(path string) error {
if fakeServer != nil {
fakeServer.server.Stop()
}
if err := os.RemoveAll(path); err != nil {
return err
}
return nil
err := os.RemoveAll(path)
return err
}
var _ = BeforeSuite(func() {
@@ -111,14 +119,12 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
Context("GetResourceClient()", func() {
It("should return no error", func() {
kubeletSocket = socketName
_, err := GetResourceClient()
_, err := GetResourceClient(testKubeletSocket)
Expect(err).NotTo(HaveOccurred())
})
It("should fail with missing file", func() {
kubeletSocket = "sampleSocketString"
_, err := GetResourceClient()
_, err := GetResourceClient("sampleSocketString")
Expect(err).To(HaveOccurred())
})
})
@@ -140,41 +146,11 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
},
},
}
kubeletSocket = socketName
client, err := getKubeletClient()
client, err := getKubeletClient(testKubeletSocket)
Expect(err).NotTo(HaveOccurred())
outputRMap := map[string]*mtypes.ResourceInfo{
"resource": &mtypes.ResourceInfo{DeviceIDs: []string{"dev0", "dev1"}},
}
resourceMap, err := client.GetPodResourceMap(fakePod)
Expect(err).NotTo(HaveOccurred())
Expect(resourceMap).ShouldNot(BeNil())
Expect(resourceMap).To(Equal(outputRMap))
})
It("should return no error with empty socket", func() {
podUID := k8sTypes.UID("970a395d-bb3b-11e8-89df-408d5c537d23")
fakePod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-name",
Namespace: "pod-namespace",
UID: podUID,
},
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Name: "container-name",
},
},
},
}
kubeletSocket = ""
client, err := getKubeletClient()
Expect(err).NotTo(HaveOccurred())
outputRMap := map[string]*mtypes.ResourceInfo{
"resource": &mtypes.ResourceInfo{DeviceIDs: []string{"dev0", "dev1"}},
"resource": {DeviceIDs: []string{"dev0", "dev1"}},
}
resourceMap, err := client.GetPodResourceMap(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -183,8 +159,7 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
})
It("should return an error with garbage socket value", func() {
kubeletSocket = "/badfilepath!?//"
_, err := getKubeletClient()
_, err := getKubeletClient("/badfilepath!?//")
Expect(err).To(HaveOccurred())
})
})
@@ -199,8 +174,7 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
UID: podUID,
},
}
kubeletSocket = socketName
client, err := getKubeletClient()
client, err := getKubeletClient(testKubeletSocket)
Expect(err).NotTo(HaveOccurred())
_, err = client.GetPodResourceMap(fakePod)
Expect(err).To(HaveOccurred())
@@ -217,8 +191,7 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
UID: podUID,
},
}
kubeletSocket = socketName
client, err := getKubeletClient()
client, err := getKubeletClient(testKubeletSocket)
Expect(err).NotTo(HaveOccurred())
_, err = client.GetPodResourceMap(fakePod)
Expect(err).To(HaveOccurred())
@@ -236,8 +209,7 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
},
}
kubeletSocket = socketName
client, err := getKubeletClient()
client, err := getKubeletClient(testKubeletSocket)
Expect(err).NotTo(HaveOccurred())
emptyRMap := map[string]*mtypes.ResourceInfo{}

16
pkg/logging/doc.go Normal file
View File

@@ -0,0 +1,16 @@
// 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 logging is the package that contains logging library.
package logging

View File

@@ -16,10 +16,10 @@ package logging
import (
"fmt"
"io"
"os"
"strings"
"time"
"io"
"github.com/pkg/errors"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
@@ -41,9 +41,42 @@ const (
var loggingStderr bool
var loggingW io.Writer
var loggingLevel Level
var logger *lumberjack.Logger
const defaultTimestampFormat = time.RFC3339
// LogOptions specifies the configuration of the log
type LogOptions struct {
MaxAge *int `json:"maxAge,omitempty"`
MaxSize *int `json:"maxSize,omitempty"`
MaxBackups *int `json:"maxBackups,omitempty"`
Compress *bool `json:"compress,omitempty"`
}
// SetLogOptions set the LoggingOptions of NetConf
func SetLogOptions(options *LogOptions) {
// give some default value
logger.MaxSize = 100
logger.MaxAge = 5
logger.MaxBackups = 5
logger.Compress = true
if options != nil {
if options.MaxAge != nil {
logger.MaxAge = *options.MaxAge
}
if options.MaxSize != nil {
logger.MaxSize = *options.MaxSize
}
if options.MaxBackups != nil {
logger.MaxBackups = *options.MaxBackups
}
if options.Compress != nil {
logger.Compress = *options.Compress
}
}
loggingW = logger
}
func (l Level) String() string {
switch l {
case PanicLevel:
@@ -94,7 +127,7 @@ func Errorf(format string, a ...interface{}) error {
return fmt.Errorf(format, a...)
}
// Panicf prints logging plus stack trace. This should be used only for unrecoverble error
// Panicf prints logging plus stack trace. This should be used only for unrecoverable error
func Panicf(format string, a ...interface{}) {
printf(PanicLevel, format, a...)
printf(PanicLevel, "========= Stack trace output ========")
@@ -141,13 +174,8 @@ func SetLogFile(filename string) {
return
}
loggingW = &lumberjack.Logger{
Filename: filename,
MaxSize: 100, // megabytes
MaxBackups: 5,
MaxAge: 5, // days
Compress: true,
}
logger.Filename = filename
loggingW = logger
}
@@ -155,4 +183,5 @@ func init() {
loggingStderr = true
loggingW = nil
loggingLevel = PanicLevel
logger = &lumberjack.Logger{}
}

View File

@@ -15,6 +15,8 @@
package logging
import (
testutils "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/testing"
"gopkg.in/natefinch/lumberjack.v2"
"testing"
. "github.com/onsi/ginkgo"
@@ -42,13 +44,13 @@ var _ = Describe("logging operations", func() {
It("Check file setter with empty", func() {
SetLogFile("/tmp/foobar.logging")
Expect(loggingW).NotTo(Equal(nil))
// check file existance
// check file existence
})
It("Check file setter with bad filepath", func() {
SetLogFile("/invalid/filepath")
Expect(loggingW).NotTo(Equal(nil))
// check file existance
// check file existence
})
It("Check loglevel setter", func() {
@@ -79,4 +81,54 @@ var _ = Describe("logging operations", func() {
currentLevel := loggingLevel
Expect(currentLevel).To(Equal(GetLoggingLevel()))
})
It("Check user settings logOptions for logging", func() {
SetLogFile("/var/log/multus.log")
expectLogger := &lumberjack.Logger{
Filename: "/var/log/multus.log",
MaxAge: 1,
MaxSize: 10,
MaxBackups: 1,
Compress: true,
}
logOptions := &LogOptions{
MaxAge: testutils.Int(1),
MaxSize: testutils.Int(10),
MaxBackups: testutils.Int(1),
Compress: testutils.Bool(true),
}
SetLogOptions(logOptions)
Expect(expectLogger).To(Equal(logger))
})
It("Check user settings logOptions and missing some options", func() {
SetLogFile("/var/log/multus.log")
expectLogger := &lumberjack.Logger{
Filename: "/var/log/multus.log",
MaxAge: 5,
MaxSize: 100,
MaxBackups: 1,
Compress: true,
}
logOptions := &LogOptions{
MaxBackups: testutils.Int(1),
Compress: testutils.Bool(true),
}
SetLogOptions(logOptions)
Expect(expectLogger).To(Equal(logger))
})
It("Check user don't settings logOptions for logging", func() {
SetLogFile("/var/log/multus.log")
logger1 := &lumberjack.Logger{
Filename: "/var/log/multus.log",
MaxAge: 5,
MaxSize: 100,
MaxBackups: 5,
Compress: true,
}
SetLogOptions(nil)
Expect(logger1).To(Equal(logger))
})
})

17
pkg/multus/doc.go Normal file
View File

@@ -0,0 +1,17 @@
// 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 multus is the package that contains main multus function, which
// manipulates CNI request for delegate plugins.
package multus

View File

@@ -12,16 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// This is a "Multi-plugin".The delegate concept refered from CNI project
// It reads other plugin netconf, and then invoke them, e.g.
// flannel or sriov plugin.
package main
package multus
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net"
@@ -35,28 +30,39 @@ import (
"github.com/containernetworking/cni/pkg/skel"
cnitypes "github.com/containernetworking/cni/pkg/types"
cnicurrent "github.com/containernetworking/cni/pkg/types/current"
cniversion "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns"
nettypes "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
"github.com/vishvananda/netlink"
k8s "gopkg.in/intel/multus-cni.v3/k8sclient"
"gopkg.in/intel/multus-cni.v3/logging"
"gopkg.in/intel/multus-cni.v3/netutils"
"gopkg.in/intel/multus-cni.v3/types"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
k8snet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/wait"
k8s "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/k8sclient"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/netutils"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
)
var version = "master@git"
var commit = "unknown commit"
var date = "unknown date"
const (
shortPollDuration = 250 * time.Millisecond
shortPollTimeout = 2500 * time.Millisecond
)
var pollDuration = 1000 * time.Millisecond
var pollTimeout = 45 * time.Second
var (
version = "master@git"
commit = "unknown commit"
date = "unknown date"
)
func printVersionString() string {
var (
pollDuration = 1000 * time.Millisecond
pollTimeout = 45 * time.Second
)
// PrintVersionString ...
func PrintVersionString() string {
return fmt.Sprintf("multus-cni version:%s, commit:%s, date:%s",
version, commit, date)
}
@@ -100,6 +106,20 @@ func getIfname(delegate *types.DelegateNetConf, argif string, idx int) string {
return fmt.Sprintf("net%d", idx)
}
func getDelegateDeviceInfo(delegate *types.DelegateNetConf, runtimeConf *libcni.RuntimeConf) (*nettypes.DeviceInfo, error) {
// If the DPDeviceInfoFile was created, it was copied to the CNIDeviceInfoFile.
// If the DPDeviceInfoFile was not created, CNI might have created it. So
// either way, load CNIDeviceInfoFile.
if info, ok := runtimeConf.CapabilityArgs["CNIDeviceInfoFile"]; ok {
if infostr, ok := info.(string); ok {
return nadutils.LoadDeviceInfoFromCNI(infostr)
}
} else {
logging.Debugf("getDelegateDeviceInfo(): No CapArgs - info=%v ok=%v", info, ok)
}
return nil, nil
}
func saveDelegates(containerID, dataDir string, delegates []*types.DelegateNetConf) error {
logging.Debugf("saveDelegates: %s, %s, %v", containerID, dataDir, delegates)
delegatesBytes, err := json.Marshal(delegates)
@@ -146,12 +166,12 @@ func validateIfName(nsname string, ifname string) error {
return err
}
func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) (cnitypes.Result, error) {
logging.Debugf("confAdd: %v, %s, %s", rt, string(rawNetconf), binDir)
func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.NetConf, exec invoke.Exec) (cnitypes.Result, error) {
logging.Debugf("confAdd: %v, %s", rt, string(rawNetconf))
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
conf, err := libcni.ConfFromBytes(rawNetconf)
if err != nil {
@@ -166,12 +186,12 @@ func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invo
return result, nil
}
func confCheck(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("confCheck: %v, %s, %s", rt, string(rawNetconf), binDir)
func confCheck(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.NetConf, exec invoke.Exec) error {
logging.Debugf("confCheck: %v, %s", rt, string(rawNetconf))
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
conf, err := libcni.ConfFromBytes(rawNetconf)
if err != nil {
@@ -180,18 +200,18 @@ func confCheck(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec in
err = cniNet.CheckNetwork(context.Background(), conf, rt)
if err != nil {
return logging.Errorf("error in getting result from DelNetwork: %v", err)
return logging.Errorf("error in getting result from CheckNetwork: %v", err)
}
return err
}
func confDel(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistDel: %v, %s, %s", rt, string(rawNetconf), binDir)
func confDel(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.NetConf, exec invoke.Exec) error {
logging.Debugf("confDel: %v, %s", rt, string(rawNetconf))
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
conf, err := libcni.ConfFromBytes(rawNetconf)
if err != nil {
@@ -206,12 +226,12 @@ func confDel(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invo
return err
}
func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) (cnitypes.Result, error) {
logging.Debugf("conflistAdd: %v, %s, %s", rt, string(rawnetconflist), binDir)
func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *types.NetConf, exec invoke.Exec) (cnitypes.Result, error) {
logging.Debugf("conflistAdd: %v, %s", rt, string(rawnetconflist))
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
@@ -226,12 +246,12 @@ func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, e
return result, nil
}
func conflistCheck(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistCheck: %v, %s, %s", rt, string(rawnetconflist), binDir)
func conflistCheck(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *types.NetConf, exec invoke.Exec) error {
logging.Debugf("conflistCheck: %v, %s", rt, string(rawnetconflist))
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
@@ -246,12 +266,12 @@ func conflistCheck(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string,
return err
}
func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistDel: %v, %s, %s", rt, string(rawnetconflist), binDir)
func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *types.NetConf, exec invoke.Exec) error {
logging.Debugf("conflistDel: %v, %s", rt, string(rawnetconflist))
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
@@ -266,23 +286,15 @@ func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, e
return err
}
func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, ifName string, delegate *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string, cniArgs string) (cnitypes.Result, error) {
logging.Debugf("delegateAdd: %v, %s, %v, %v, %s", exec, ifName, delegate, rt, binDir)
if os.Setenv("CNI_IFNAME", ifName) != nil {
return nil, logging.Errorf("delegateAdd: error setting envionment variable CNI_IFNAME")
}
func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, delegate *types.DelegateNetConf, rt *libcni.RuntimeConf, multusNetconf *types.NetConf) (cnitypes.Result, error) {
logging.Debugf("delegateAdd: %v, %v, %v", exec, delegate, rt)
if err := validateIfName(os.Getenv("CNI_NETNS"), ifName); err != nil {
return nil, logging.Errorf("delegateAdd: cannot set %q interface name to %q: %v", delegate.Conf.Type, ifName, err)
if err := validateIfName(rt.NetNS, rt.IfName); err != nil {
return nil, logging.Errorf("delegateAdd: cannot set %q interface name to %q: %v", delegate.Conf.Type, rt.IfName, err)
}
// Deprecated in ver 3.5.
if delegate.MacRequest != "" || delegate.IPRequest != nil {
if cniArgs != "" {
cniArgs = fmt.Sprintf("%s;IgnoreUnknown=true", cniArgs)
} else {
cniArgs = "IgnoreUnknown=true"
}
if delegate.MacRequest != "" {
// validate Mac address
_, err := net.ParseMAC(delegate.MacRequest)
@@ -290,8 +302,7 @@ func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, ifNa
return nil, logging.Errorf("delegateAdd: failed to parse mac address %q", delegate.MacRequest)
}
cniArgs = fmt.Sprintf("%s;MAC=%s", cniArgs, delegate.MacRequest)
logging.Debugf("delegateAdd: set MAC address %q to %q", delegate.MacRequest, ifName)
logging.Debugf("delegateAdd: set MAC address %q to %q", delegate.MacRequest, rt.IfName)
rt.Args = append(rt.Args, [2]string{"MAC", delegate.MacRequest})
}
@@ -309,8 +320,7 @@ func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, ifNa
}
ips := strings.Join(delegate.IPRequest, ",")
cniArgs = fmt.Sprintf("%s;IP=%s", cniArgs, ips)
logging.Debugf("delegateAdd: set IP address %q to %q", ips, ifName)
logging.Debugf("delegateAdd: set IP address %q to %q", ips, rt.IfName)
rt.Args = append(rt.Args, [2]string{"IP", ips})
}
}
@@ -318,12 +328,12 @@ func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, ifNa
var result cnitypes.Result
var err error
if delegate.ConfListPlugin {
result, err = conflistAdd(rt, delegate.Bytes, binDir, exec)
result, err = conflistAdd(rt, delegate.Bytes, multusNetconf, exec)
if err != nil {
return nil, err
}
} else {
result, err = confAdd(rt, delegate.Bytes, binDir, exec)
result, err = confAdd(rt, delegate.Bytes, multusNetconf, exec)
if err != nil {
return nil, err
}
@@ -364,18 +374,15 @@ func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, ifNa
kubeClient.Eventf(pod, v1.EventTypeNormal, "AddedInterface", "Add %s %v", rt.IfName, ips)
}
} else {
// for further debug https://github.com/intel/multus-cni/issues/481
// for further debug https://github.com/k8snetworkplumbingwg/multus-cni/issues/481
logging.Errorf("delegateAdd: pod nil pointer: namespace: %s, name: %s, container id: %s, pod: %v", rt.Args[1][1], rt.Args[2][1], rt.Args[3][1], pod)
}
return result, nil
}
func delegateCheck(exec invoke.Exec, ifName string, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string) error {
logging.Debugf("delegateCheck: %v, %s, %v, %v, %s", exec, ifName, delegateConf, rt, binDir)
if os.Setenv("CNI_IFNAME", ifName) != nil {
return logging.Errorf("delegateCheck: error setting envionment variable CNI_IFNAME")
}
func delegateCheck(exec invoke.Exec, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, multusNetconf *types.NetConf) error {
logging.Debugf("delegateCheck: %v, %v, %v", exec, delegateConf, rt)
if logging.GetLoggingLevel() >= logging.VerboseLevel {
var cniConfName string
@@ -389,12 +396,12 @@ func delegateCheck(exec invoke.Exec, ifName string, delegateConf *types.Delegate
var err error
if delegateConf.ConfListPlugin {
err = conflistCheck(rt, delegateConf.Bytes, binDir, exec)
err = conflistCheck(rt, delegateConf.Bytes, multusNetconf, exec)
if err != nil {
return logging.Errorf("delegateCheck: error invoking ConflistCheck - %q: %v", delegateConf.ConfList.Name, err)
}
} else {
err = confCheck(rt, delegateConf.Bytes, binDir, exec)
err = confCheck(rt, delegateConf.Bytes, multusNetconf, exec)
if err != nil {
return logging.Errorf("delegateCheck: error invoking DelegateCheck - %q: %v", delegateConf.Conf.Type, err)
}
@@ -403,11 +410,8 @@ func delegateCheck(exec invoke.Exec, ifName string, delegateConf *types.Delegate
return err
}
func delegateDel(exec invoke.Exec, pod *v1.Pod, ifName string, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string) error {
logging.Debugf("delegateDel: %v, %v, %s, %v, %v, %s", exec, pod, ifName, delegateConf, rt, binDir)
if os.Setenv("CNI_IFNAME", ifName) != nil {
return logging.Errorf("delegateDel: error setting envionment variable CNI_IFNAME")
}
func delegateDel(exec invoke.Exec, pod *v1.Pod, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, multusNetconf *types.NetConf) error {
logging.Debugf("delegateDel: %v, %v, %v, %v", exec, pod, delegateConf, rt)
if logging.GetLoggingLevel() >= logging.VerboseLevel {
var confName string
@@ -425,12 +429,12 @@ func delegateDel(exec invoke.Exec, pod *v1.Pod, ifName string, delegateConf *typ
var err error
if delegateConf.ConfListPlugin {
err = conflistDel(rt, delegateConf.Bytes, binDir, exec)
err = conflistDel(rt, delegateConf.Bytes, multusNetconf, exec)
if err != nil {
return logging.Errorf("delegateDel: error invoking ConflistDel - %q: %v", delegateConf.ConfList.Name, err)
}
} else {
err = confDel(rt, delegateConf.Bytes, binDir, exec)
err = confDel(rt, delegateConf.Bytes, multusNetconf, exec)
if err != nil {
return logging.Errorf("delegateDel: error invoking DelegateDel - %q: %v", delegateConf.Conf.Type, err)
}
@@ -439,20 +443,28 @@ func delegateDel(exec invoke.Exec, pod *v1.Pod, ifName string, delegateConf *typ
return err
}
func delPlugins(exec invoke.Exec, pod *v1.Pod, argIfname string, delegates []*types.DelegateNetConf, lastIdx int, rt *libcni.RuntimeConf, binDir string) error {
logging.Debugf("delPlugins: %v, %v, %s, %v, %d, %v, %s", exec, pod, argIfname, delegates, lastIdx, rt, binDir)
if os.Setenv("CNI_COMMAND", "DEL") != nil {
return logging.Errorf("delPlugins: error setting envionment variable CNI_COMMAND to a value of DEL")
}
// delPlugins deletes plugins in reverse order from lastdIdx
// Uses netRt as base RuntimeConf (coming from NetConf) but merges it
// with each of the delegates' configuration
func delPlugins(exec invoke.Exec, pod *v1.Pod, args *skel.CmdArgs, k8sArgs *types.K8sArgs, delegates []*types.DelegateNetConf, lastIdx int, netRt *types.RuntimeConfig, multusNetconf *types.NetConf) error {
logging.Debugf("delPlugins: %v, %v, %v, %v, %v, %d, %v", exec, pod, args, k8sArgs, delegates, lastIdx, netRt)
var errorstrings []string
for idx := lastIdx; idx >= 0; idx-- {
ifName := getIfname(delegates[idx], argIfname, idx)
rt.IfName = ifName
ifName := getIfname(delegates[idx], args.IfName, idx)
rt, cniDeviceInfoPath := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, netRt, delegates[idx])
// Attempt to delete all but do not error out, instead, collect all errors.
if err := delegateDel(exec, pod, ifName, delegates[idx], rt, binDir); err != nil {
if err := delegateDel(exec, pod, delegates[idx], rt, multusNetconf); err != nil {
errorstrings = append(errorstrings, err.Error())
}
if cniDeviceInfoPath != "" {
err := nadutils.CleanDeviceInfoForCNI(cniDeviceInfoPath)
// Even if the filename is set, file may not be present. Ignore error,
// but log and in the future may need to filter on specific errors.
if err != nil {
logging.Debugf("delPlugins: CleanDeviceInfoForCNI returned an error - err=%v", err)
}
}
}
// Check if we had any errors, and send them all back.
@@ -466,7 +478,7 @@ func delPlugins(exec invoke.Exec, pod *v1.Pod, argIfname string, delegates []*ty
func cmdErr(k8sArgs *types.K8sArgs, format string, args ...interface{}) error {
prefix := "Multus: "
if k8sArgs != nil {
prefix += fmt.Sprintf("[%s/%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME)
prefix += fmt.Sprintf("[%s/%s/%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME, k8sArgs.K8S_POD_UID)
}
return logging.Errorf(prefix+format, args...)
}
@@ -474,14 +486,72 @@ func cmdErr(k8sArgs *types.K8sArgs, format string, args ...interface{}) error {
func cmdPluginErr(k8sArgs *types.K8sArgs, confName string, format string, args ...interface{}) error {
msg := ""
if k8sArgs != nil {
msg += fmt.Sprintf("[%s/%s:%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME, confName)
msg += fmt.Sprintf("[%s/%s/%s:%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME, k8sArgs.K8S_POD_UID, confName)
}
return logging.Errorf(msg+format, args...)
}
func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (cnitypes.Result, error) {
func isCriticalRequestRetriable(err error) bool {
logging.Debugf("isCriticalRequestRetriable: %v", err)
errorTypesAllowingRetry := []func(error) bool{
errors.IsServiceUnavailable, errors.IsInternalError, k8snet.IsConnectionReset, k8snet.IsConnectionRefused}
for _, f := range errorTypesAllowingRetry {
if f(err) {
return true
}
}
return false
}
func getPod(kubeClient *k8s.ClientInfo, k8sArgs *types.K8sArgs, warnOnly bool) (*v1.Pod, error) {
if kubeClient == nil {
return nil, nil
}
podNamespace := string(k8sArgs.K8S_POD_NAMESPACE)
podName := string(k8sArgs.K8S_POD_NAME)
podUID := string(k8sArgs.K8S_POD_UID)
pod, err := kubeClient.GetPod(podNamespace, podName)
if err != nil {
// in case of a retriable error, retry 10 times with 0.25 sec interval
if isCriticalRequestRetriable(err) {
waitErr := wait.PollImmediate(shortPollDuration, shortPollTimeout, func() (bool, error) {
pod, err = kubeClient.GetPod(podNamespace, podName)
return pod != nil, err
})
// retry failed, then return error with retry out
if waitErr != nil {
return nil, cmdErr(k8sArgs, "error waiting for pod: %v", err)
}
} else if warnOnly && errors.IsNotFound(err) {
// If not found, proceed to remove interface with cache
return nil, nil
} else {
// Other case, return error
return nil, cmdErr(k8sArgs, "error getting pod: %v", err)
}
}
// In case of static pod, UID through kube api is different because of mirror pod, hence it is expected.
if podUID != "" && string(pod.UID) != podUID && !k8s.IsStaticPod(pod) {
msg := fmt.Sprintf("expected pod UID %q but got %q from Kube API", podUID, pod.UID)
if warnOnly {
// On CNI DEL we just operate on the cache when these mismatch, we don't error out.
// For example: stateful sets namespace/name can remain the same while podUID changes.
logging.Verbosef("warning: %s", msg)
return nil, nil
}
return nil, cmdErr(k8sArgs, msg)
}
return pod, nil
}
// CmdAdd ...
func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (cnitypes.Result, error) {
n, err := types.LoadNetConf(args.StdinData)
logging.Debugf("cmdAdd: %v, %v, %v", args, exec, kubeClient)
logging.Debugf("CmdAdd: %v, %v, %v", args, exec, kubeClient)
if err != nil {
return nil, cmdErr(nil, "error loading netconf: %v", err)
}
@@ -502,32 +572,13 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c
return err == nil, nil
})
if err != nil {
return nil, cmdErr(k8sArgs, "PollImmediate error waiting for ReadinessIndicatorFile: %v", err)
return nil, cmdErr(k8sArgs, "have you checked that your default network is ready? still waiting for readinessindicatorfile @ %v. pollimmediate error: %v", n.ReadinessIndicatorFile, err)
}
}
pod := (*v1.Pod)(nil)
if kubeClient != nil {
pod, err = kubeClient.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
if err != nil {
var waitErr error
// in case of ServiceUnavailable, retry 10 times with 0.5 sec interval
if errors.IsServiceUnavailable(err) {
pollDuration := 500 * time.Millisecond
pollTimeout := 5 * time.Second
waitErr = wait.PollImmediate(pollDuration, pollTimeout, func() (bool, error) {
pod, err = kubeClient.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
return pod != nil, err
})
// retry failed, then return error with retry out
if waitErr != nil {
return nil, cmdErr(k8sArgs, "error getting pod by service unavailable: %v", err)
}
} else {
// Other case, return error
return nil, cmdErr(k8sArgs, "error getting pod: %v", err)
}
}
pod, err := getPod(kubeClient, k8sArgs, false)
if err != nil {
return nil, err
}
// resourceMap holds Pod device allocation information; only initizized if CRD contains 'resourceName' annotation.
@@ -555,52 +606,89 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c
var result, tmpResult cnitypes.Result
var netStatus []nettypes.NetworkStatus
cniArgs := os.Getenv("CNI_ARGS")
for idx, delegate := range n.Delegates {
ifName := getIfname(delegate, args.IfName, idx)
rt, cniDeviceInfoPath := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, n.RuntimeConfig, delegate)
if cniDeviceInfoPath != "" && delegate.ResourceName != "" && delegate.DeviceID != "" {
err = nadutils.CopyDeviceInfoForCNIFromDP(cniDeviceInfoPath, delegate.ResourceName, delegate.DeviceID)
// Even if the filename is set, file may not be present. Ignore error,
// but log and in the future may need to filter on specific errors.
if err != nil {
logging.Debugf("cmdAdd: CopyDeviceInfoForCNIFromDP returned an error - err=%v", err)
}
}
runtimeConfig := types.MergeCNIRuntimeConfig(n.RuntimeConfig, delegate)
rt := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, runtimeConfig)
tmpResult, err = delegateAdd(exec, kubeClient, pod, ifName, delegate, rt, n.BinDir, cniArgs)
// We collect the delegate netName for the cachefile name as well as following errors
netName := delegate.Conf.Name
if netName == "" {
netName = delegate.ConfList.Name
}
tmpResult, err = delegateAdd(exec, kubeClient, pod, delegate, rt, n)
if err != nil {
// If the add failed, tear down all networks we already added
netName := delegate.Conf.Name
if netName == "" {
netName = delegate.ConfList.Name
}
// Ignore errors; DEL must be idempotent anyway
_ = delPlugins(exec, nil, args.IfName, n.Delegates, idx, rt, n.BinDir)
_ = delPlugins(exec, nil, args, k8sArgs, n.Delegates, idx, n.RuntimeConfig, n)
return nil, cmdPluginErr(k8sArgs, netName, "error adding container to network %q: %v", netName, err)
}
// Remove gateway from routing table if the gateway is not used
deletegateway := false
deleteV4gateway := false
deleteV6gateway := false
adddefaultgateway := false
if delegate.IsFilterGateway {
deletegateway = true
logging.Debugf("Marked interface %v for gateway deletion", ifName)
if delegate.IsFilterV4Gateway {
deleteV4gateway = true
logging.Debugf("Marked interface %v for v4 gateway deletion", ifName)
} else {
// Otherwise, determine if this interface now gets our default route.
if delegate.GatewayRequest != nil {
deletegateway = true
// According to
// https://docs.google.com/document/d/1Ny03h6IDVy_e_vmElOqR7UdTPAG_RNydhVE1Kx54kFQ (4.1.2.1.9)
// the list can be empty; if it is, we'll assume the CNI's config for the default gateway holds,
// else we'll update the defaultgateway to the one specified.
if delegate.GatewayRequest != nil && len(*delegate.GatewayRequest) != 0 {
deleteV4gateway = true
adddefaultgateway = true
logging.Debugf("Detected gateway override on interface %v to %v", ifName, delegate.GatewayRequest)
}
}
if deletegateway {
tmpResult, err = netutils.DeleteDefaultGW(args, ifName, &tmpResult)
if err != nil {
return nil, cmdErr(k8sArgs, "error deleting default gateway: %v", err)
if delegate.IsFilterV6Gateway {
deleteV6gateway = true
logging.Debugf("Marked interface %v for v6 gateway deletion", ifName)
} else {
// Otherwise, determine if this interface now gets our default route.
// According to
// https://docs.google.com/document/d/1Ny03h6IDVy_e_vmElOqR7UdTPAG_RNydhVE1Kx54kFQ (4.1.2.1.9)
// the list can be empty; if it is, we'll assume the CNI's config for the default gateway holds,
// else we'll update the defaultgateway to the one specified.
if delegate.GatewayRequest != nil && len(*delegate.GatewayRequest) != 0 {
deleteV6gateway = true
adddefaultgateway = true
logging.Debugf("Detected gateway override on interface %v to %v", ifName, delegate.GatewayRequest)
}
}
// Here we'll set the default gateway
// Remove gateway if `default-route` network selection is specified
if deleteV4gateway || deleteV6gateway {
err = netutils.DeleteDefaultGW(args, ifName)
if err != nil {
return nil, cmdErr(k8sArgs, "error deleting default gateway: %v", err)
}
err = netutils.DeleteDefaultGWCache(n.CNIDir, rt, netName, ifName, deleteV4gateway, deleteV6gateway)
if err != nil {
return nil, cmdErr(k8sArgs, "error deleting default gateway in cache: %v", err)
}
}
// Here we'll set the default gateway which specified in `default-route` network selection
if adddefaultgateway {
tmpResult, err = netutils.SetDefaultGW(args, ifName, delegate.GatewayRequest, &tmpResult)
err = netutils.SetDefaultGW(args, ifName, *delegate.GatewayRequest)
if err != nil {
return nil, cmdErr(k8sArgs, "error setting default gateway: %v", err)
}
err = netutils.AddDefaultGWCache(n.CNIDir, rt, netName, ifName, *delegate.GatewayRequest)
if err != nil {
return nil, cmdErr(k8sArgs, "error setting default gateway in cache: %v", err)
}
}
// Master plugin result is always used if present
@@ -608,24 +696,39 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c
result = tmpResult
}
//create the network status, only in case Multus as kubeconfig
// Read devInfo from CNIDeviceInfoFile if it exists so
// it can be copied to the NetworkStatus.
devinfo, err := getDelegateDeviceInfo(delegate, rt)
if err != nil {
// Even if the filename is set, file may not be present. Ignore error,
// but log and in the future may need to filter on specific errors.
logging.Debugf("cmdAdd: getDelegateDeviceInfo returned an error - err=%v", err)
}
// create the network status, only in case Multus as kubeconfig
if n.Kubeconfig != "" && kc != nil {
if !types.CheckSystemNamespaces(string(k8sArgs.K8S_POD_NAME), n.SystemNamespaces) {
delegateNetStatus, err := nadutils.CreateNetworkStatus(tmpResult, delegate.Name, delegate.MasterPlugin)
delegateNetStatus, err := nadutils.CreateNetworkStatus(tmpResult, delegate.Name, delegate.MasterPlugin, devinfo)
if err != nil {
return nil, cmdErr(k8sArgs, "error setting network status: %v", err)
}
netStatus = append(netStatus, *delegateNetStatus)
}
} else if devinfo != nil {
// Warn that devinfo exists but could not add it to downwards API
logging.Errorf("devinfo available, but no kubeConfig so NetworkStatus not modified.")
}
}
//set the network status annotation in apiserver, only in case Multus as kubeconfig
// set the network status annotation in apiserver, only in case Multus as kubeconfig
if n.Kubeconfig != "" && kc != nil {
if !types.CheckSystemNamespaces(string(k8sArgs.K8S_POD_NAME), n.SystemNamespaces) {
err = k8s.SetNetworkStatus(kubeClient, k8sArgs, netStatus, n)
if err != nil {
if strings.Contains(err.Error(), "failed to query the pod") {
return nil, cmdErr(k8sArgs, "error setting the networks status, pod was already deleted: %v", err)
}
return nil, cmdErr(k8sArgs, "error setting the networks status: %v", err)
}
}
@@ -634,9 +737,10 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c
return result, nil
}
func cmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error {
// CmdCheck ...
func CmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error {
in, err := types.LoadNetConf(args.StdinData)
logging.Debugf("cmdCheck: %v, %v, %v", args, exec, kubeClient)
logging.Debugf("CmdCheck: %v, %v, %v", args, exec, kubeClient)
if err != nil {
return err
}
@@ -649,9 +753,8 @@ func cmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo)
for idx, delegate := range in.Delegates {
ifName := getIfname(delegate, args.IfName, idx)
runtimeConfig := types.MergeCNIRuntimeConfig(in.RuntimeConfig, delegate)
rt := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, runtimeConfig)
err = delegateCheck(exec, ifName, delegate, rt, in.BinDir)
rt, _ := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, in.RuntimeConfig, delegate)
err = delegateCheck(exec, delegate, rt, in)
if err != nil {
return err
}
@@ -660,25 +763,26 @@ func cmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo)
return nil
}
func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error {
// CmdDel ...
func CmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error {
in, err := types.LoadNetConf(args.StdinData)
logging.Debugf("cmdDel: %v, %v, %v", args, exec, kubeClient)
logging.Debugf("CmdDel: %v, %v, %v", args, exec, kubeClient)
if err != nil {
return err
}
netnsfound := true
skipStatusUpdate := false
netns, err := ns.GetNS(args.Netns)
if err != nil {
// if NetNs is passed down by the Cloud Orchestration Engine, or if it called multiple times
// so don't return an error if the device is already removed.
// https://github.com/kubernetes/kubernetes/issues/43014#issuecomment-287164444
_, ok := err.(ns.NSPathNotExistErr)
skipStatusUpdate = true
if ok {
netnsfound = false
logging.Debugf("cmdDel: WARNING netns may not exist, netns: %s, err: %s", args.Netns, err)
logging.Debugf("CmdDel: WARNING netns may not exist, netns: %s, err: %s", args.Netns, err)
} else {
return cmdErr(nil, "failed to open netns %q: %v", netns, err)
logging.Debugf("CmdDel: WARNING failed to open netns %q: %v", netns, err)
}
}
@@ -706,31 +810,12 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er
return cmdErr(nil, "error getting k8s client: %v", err)
}
pod := (*v1.Pod)(nil)
if kubeClient != nil {
pod, err = kubeClient.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
if err != nil {
var waitErr error
// in case of ServiceUnavailable, retry 10 times with 0.5 sec interval
if errors.IsServiceUnavailable(err) {
pollDuration := 500 * time.Millisecond
pollTimeout := 5 * time.Second
waitErr = wait.PollImmediate(pollDuration, pollTimeout, func() (bool, error) {
pod, err = kubeClient.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
return pod != nil, err
})
// retry failed, then return error with retry out
if waitErr != nil {
return cmdErr(k8sArgs, "error getting pod by service unavailable: %v", err)
}
} else if errors.IsNotFound(err) {
// If not found, proceed to remove interface with cache
pod = nil
} else {
// Other case, return error
return cmdErr(k8sArgs, "error getting pod: %v", err)
}
}
pod, err := getPod(kubeClient, k8sArgs, true)
if err != nil {
// getPod may be failed but just do print error in its log and continue to delete
logging.Errorf("Multus: getPod failed: %v, but continue to delete", err)
// skip status update because k8s api seems to be stucked
skipStatusUpdate = true
}
// Read the cache to get delegates json for the pod
@@ -758,10 +843,14 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er
logging.Errorf("Multus: failed to get delegates: %v, but continue to delete clusterNetwork", err)
}
} else {
return cmdErr(k8sArgs, "error reading the delegates: %v", err)
// The options to continue with a delete have been exhausted (cachefile + API query didn't work)
// We cannot exit with an error as this may cause a sandbox to never get deleted.
logging.Errorf("Multus: failed to get the cached delegates file: %v, cannot properly delete", err)
return nil
}
} else {
defer os.Remove(path)
in.Delegates = []*types.DelegateNetConf{}
if err := json.Unmarshal(netconfBytes, &in.Delegates); err != nil {
return cmdErr(k8sArgs, "failed to load netconf: %v", err)
}
@@ -780,12 +869,16 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er
if v.ConfListPlugin == true && v.ConfList.CNIVersion == "" && in.CNIVersion != "" {
v.ConfList.CNIVersion = in.CNIVersion
v.Bytes, err = json.Marshal(v.ConfList)
if err != nil {
// error happen but continue to delete
logging.Errorf("Multus: failed to marshal delegate %q config: %v", v.Name, err)
}
}
}
// unset the network status annotation in apiserver, only in case Multus as kubeconfig
if in.Kubeconfig != "" {
if netnsfound {
if !skipStatusUpdate {
if !types.CheckSystemNamespaces(string(k8sArgs.K8S_POD_NAMESPACE), in.SystemNamespaces) {
err := k8s.SetNetworkStatus(kubeClient, k8sArgs, nil, in)
if err != nil {
@@ -794,40 +887,9 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er
}
}
} else {
logging.Debugf("WARNING: Unset SetNetworkStatus skipped due to netns not found.")
logging.Debugf("WARNING: Unset SetNetworkStatus skipped")
}
}
rt := types.CreateCNIRuntimeConf(args, k8sArgs, "", in.RuntimeConfig)
return delPlugins(exec, pod, args.IfName, in.Delegates, len(in.Delegates)-1, rt, in.BinDir)
}
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 == true {
fmt.Printf("%s\n", printVersionString())
return
}
skel.PluginMain(
func(args *skel.CmdArgs) error {
result, err := cmdAdd(args, nil, nil)
if err != nil {
return err
}
return result.Print()
},
func(args *skel.CmdArgs) error {
return cmdCheck(args, nil, nil)
},
func(args *skel.CmdArgs) error { return cmdDel(args, nil, nil) },
cniversion.All, "meta-plugin that delegates to other CNI plugins")
return delPlugins(exec, pod, args, k8sArgs, in.Delegates, len(in.Delegates)-1, in.RuntimeConfig, in)
}

File diff suppressed because it is too large Load Diff

16
pkg/netutils/doc.go Normal file
View File

@@ -0,0 +1,16 @@
// 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 netutils is the package that contains network related utilities.
package netutils

405
pkg/netutils/netutils.go Normal file
View File

@@ -0,0 +1,405 @@
// Copyright (c) 2019 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 netutils
import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"path/filepath"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/vishvananda/netlink"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
)
// DeleteDefaultGW removes the default gateway from marked interfaces.
func DeleteDefaultGW(args *skel.CmdArgs, ifName string) error {
netns, err := ns.GetNS(args.Netns)
if err != nil {
return logging.Errorf("DeleteDefaultGW: Error getting namespace %v", err)
}
defer netns.Close()
err = netns.Do(func(_ ns.NetNS) error {
var err error
link, _ := netlink.LinkByName(ifName)
routes, _ := netlink.RouteList(link, netlink.FAMILY_ALL)
for _, nlroute := range routes {
if nlroute.Dst == nil {
err = netlink.RouteDel(&nlroute)
}
}
return err
})
return err
}
// SetDefaultGW adds a default gateway on a specific interface
func SetDefaultGW(args *skel.CmdArgs, ifName string, gateways []net.IP) error {
// This ensures we're acting within the net namespace for the pod.
netns, err := ns.GetNS(args.Netns)
if err != nil {
return logging.Errorf("SetDefaultGW: Error getting namespace %v", err)
}
defer netns.Close()
// Do this within the net namespace.
err = netns.Do(func(_ ns.NetNS) error {
var err error
// Pick up the link info as we need the index.
link, err := netlink.LinkByName(ifName)
if err != nil {
return logging.Errorf("SetDefaultGW: Error getting link %v", err)
}
// Cycle through all the desired gateways.
for _, gw := range gateways {
// Create a new route (note: dst is nil by default)
logging.Debugf("SetDefaultGW: Adding default route on %v (index: %v) to %v", ifName, link.Attrs().Index, gw)
newDefaultRoute := netlink.Route{
LinkIndex: link.Attrs().Index,
Gw: gw,
}
// Perform the creation of the default route....
err = netlink.RouteAdd(&newDefaultRoute)
if err != nil {
logging.Errorf("SetDefaultGW: Error adding route: %v", err)
}
}
return err
})
return err
}
// DeleteDefaultGWCache updates libcni cache to remove default gateway routes in result
func DeleteDefaultGWCache(cacheDir string, rt *libcni.RuntimeConf, netName string, ifName string, ipv4, ipv6 bool) error {
cacheFile := filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName))
cache, err := ioutil.ReadFile(cacheFile)
if err != nil {
return err
}
logging.Debugf("DeleteDefaultGWCache: update cache to delete GW from: %s", string(cache))
newCache, err := deleteDefaultGWCacheBytes(cache, ipv4, ipv6)
if err != nil {
return err
}
logging.Debugf("DeleteDefaultGWCache: update cache to delete GW: %s", string(newCache))
return ioutil.WriteFile(cacheFile, newCache, 0600)
}
func deleteDefaultGWCacheBytes(cacheFile []byte, ipv4, ipv6 bool) ([]byte, error) {
var cachedInfo map[string]interface{}
if err := json.Unmarshal(cacheFile, &cachedInfo); err != nil {
return nil, err
}
// try to get result
_, ok := cachedInfo["result"]
if !ok {
return nil, fmt.Errorf("cannot get result from cache")
}
resultJSON, ok := cachedInfo["result"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("wrong result type: %v", cachedInfo["result"])
}
newResult, err := deleteDefaultGWResult(resultJSON, ipv4, ipv6)
if err != nil {
return nil, err
}
cachedInfo["result"] = newResult
newCache, err := json.Marshal(cachedInfo)
if err != nil {
return nil, fmt.Errorf("failed to encode json: %v", err)
}
return newCache, nil
}
func deleteDefaultGWResultRoutes(routes []interface{}, dstGW string) ([]interface{}, error) {
for i, r := range routes {
route, ok := r.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("wrong route format: %v", r)
}
_, ok = route["dst"]
if ok {
dst, ok := route["dst"].(string)
if !ok {
return nil, fmt.Errorf("wrong dst format: %v", route["dst"])
}
if dst == dstGW {
routes = append(routes[:i], routes[i+1:]...)
}
}
}
return routes, nil
}
func deleteDefaultGWResult(result map[string]interface{}, ipv4, ipv6 bool) (map[string]interface{}, error) {
// try to get cniVersion from result
_, ok := result["cniVersion"]
if !ok {
// fallback to processing result for old cni version(0.1.0/0.2.0)
return deleteDefaultGWResult020(result, ipv4, ipv6)
}
cniVersion, ok := result["cniVersion"].(string)
if !ok {
return nil, fmt.Errorf("wrong cniVersion format: %v", result["cniVersion"])
}
if cniVersion == "0.1.0" || cniVersion == "0.2.0" {
// fallback to processing result for old cni version(0.1.0/0.2.0)
return deleteDefaultGWResult020(result, ipv4, ipv6)
}
if cniVersion != "0.3.0" && cniVersion != "0.3.1" && cniVersion != "0.4.0" && cniVersion != "1.0.0" {
return nil, fmt.Errorf("not supported version: %s", cniVersion)
}
_, ok = result["routes"]
if !ok {
// No route in result, hence we do nothing
return result, nil
}
routes, ok := result["routes"].([]interface{})
if !ok {
return nil, fmt.Errorf("wrong routes format: %v", result["routes"])
}
var err error
// delete IPv4 default routes
if ipv4 {
routes, err = deleteDefaultGWResultRoutes(routes, "0.0.0.0/0")
if err != nil {
return nil, err
}
}
if ipv6 {
routes, err = deleteDefaultGWResultRoutes(routes, "::0/0")
if err != nil {
return nil, err
}
}
result["routes"] = routes
return result, nil
}
func deleteDefaultGWResult020(result map[string]interface{}, ipv4, ipv6 bool) (map[string]interface{}, error) {
var err error
if ipv4 {
_, ok := result["ip4"]
if ok {
ip4, ok := result["ip4"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("wrong ip4 format: %v", result["ip4"])
}
_, ok = ip4["routes"]
if ok {
routes, ok := ip4["routes"].([]interface{})
if !ok {
return nil, fmt.Errorf("wrong ip4 routes format: %v", ip4["routes"])
}
routes, err = deleteDefaultGWResultRoutes(routes, "0.0.0.0/0")
if err != nil {
return nil, err
}
ip4["routes"] = routes
}
}
}
if ipv6 {
_, ok := result["ip6"]
if ok {
ip6, ok := result["ip6"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("wrong ip6 format: %v", result["ip6"])
}
_, ok = ip6["routes"]
if ok {
routes, ok := ip6["routes"].([]interface{})
if !ok {
return nil, fmt.Errorf("wrong ip6 routes format: %v", ip6["routes"])
}
routes, err = deleteDefaultGWResultRoutes(routes, "::0/0")
if err != nil {
return nil, err
}
ip6["routes"] = routes
}
}
}
return result, nil
}
// AddDefaultGWCache updates libcni cache to add default gateway result
func AddDefaultGWCache(cacheDir string, rt *libcni.RuntimeConf, netName string, ifName string, gw []net.IP) error {
cacheFile := filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName))
cache, err := ioutil.ReadFile(cacheFile)
if err != nil {
return err
}
logging.Debugf("AddDefaultGWCache: update cache to add GW from: %s", string(cache))
newCache, err := addDefaultGWCacheBytes(cache, gw)
if err != nil {
return err
}
logging.Debugf("AddDefaultGWCache: update cache to add GW: %s", string(newCache))
return ioutil.WriteFile(cacheFile, newCache, 0600)
}
func addDefaultGWCacheBytes(cacheFile []byte, gw []net.IP) ([]byte, error) {
var cachedInfo map[string]interface{}
if err := json.Unmarshal(cacheFile, &cachedInfo); err != nil {
return nil, err
}
// try to get result
_, ok := cachedInfo["result"]
if !ok {
return nil, fmt.Errorf("cannot get result from cache")
}
resultJSON, ok := cachedInfo["result"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("wrong result type: %v", cachedInfo["result"])
}
newResult, err := addDefaultGWResult(resultJSON, gw)
if err != nil {
return nil, err
}
cachedInfo["result"] = newResult
newCache, err := json.Marshal(cachedInfo)
if err != nil {
return nil, fmt.Errorf("failed to encode json: %v", err)
}
return newCache, nil
}
func addDefaultGWResult(result map[string]interface{}, gw []net.IP) (map[string]interface{}, error) {
// try to get cniVersion from result
_, ok := result["cniVersion"]
if !ok {
// fallback to processing result for old cni version(0.1.0/0.2.0)
return addDefaultGWResult020(result, gw)
}
cniVersion, ok := result["cniVersion"].(string)
if !ok {
return nil, fmt.Errorf("wrong cniVersion format: %v", result["cniVersion"])
}
if cniVersion == "0.1.0" || cniVersion == "0.2.0" {
// fallback to processing result for old cni version(0.1.0/0.2.0)
return addDefaultGWResult020(result, gw)
}
if cniVersion != "0.3.0" && cniVersion != "0.3.1" && cniVersion != "0.4.0" && cniVersion != "1.0.0" {
return nil, fmt.Errorf("not supported version: %s", cniVersion)
}
routes := []interface{}{}
_, ok = result["routes"]
if ok {
routes, ok = result["routes"].([]interface{})
if !ok {
return nil, fmt.Errorf("wrong routes format: %v", result["routes"])
}
}
for _, g := range gw {
dst := "0.0.0.0/0"
if g.To4() == nil {
dst = "::0/0"
}
routes = append(routes, map[string]string{
"dst": dst,
"gw": g.String(),
})
}
result["routes"] = routes
return result, nil
}
func addDefaultGWResult020(result map[string]interface{}, gw []net.IP) (map[string]interface{}, error) {
for _, g := range gw {
if g.To4() != nil {
_, ok := result["ip4"]
if ok {
ip4, ok := result["ip4"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("wrong ip4 format: %v", result["ip4"])
}
routes := []interface{}{}
_, ok = ip4["routes"]
if ok {
routes, ok = ip4["routes"].([]interface{})
if !ok {
return nil, fmt.Errorf("wrong ip4 routes format: %v", ip4["routes"])
}
}
ip4["routes"] = append(routes, map[string]string{
"dst": "0.0.0.0/0",
"gw": g.String(),
})
}
} else {
_, ok := result["ip6"]
if ok {
ip6, ok := result["ip6"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("wrong ip6 format: %v", result["ip4"])
}
routes := []interface{}{}
_, ok = ip6["routes"]
if ok {
routes, ok = ip6["routes"].([]interface{})
if !ok {
return nil, fmt.Errorf("wrong ip6 routes format: %v", ip6["routes"])
}
}
ip6["routes"] = append(routes, map[string]string{
"dst": "::/0",
"gw": g.String(),
})
}
}
}
return result, nil
}

File diff suppressed because it is too large Load Diff

16
pkg/testing/doc.go Normal file
View File

@@ -0,0 +1,16 @@
// 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 testing is the package that contains unit-test libraries.
package testing

View File

@@ -166,3 +166,13 @@ func (r *Result) String() string {
}
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
}
// Int returns a pointer to an int
func Int(i int) *int {
return &i
}
// Bool returns a pointer to a bool.
func Bool(b bool) *bool {
return &b
}

View File

@@ -19,12 +19,15 @@ import (
"encoding/json"
"fmt"
"net"
"os"
"strings"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
"gopkg.in/intel/multus-cni.v3/logging"
nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
)
const (
@@ -33,6 +36,7 @@ const (
defaultBinDir = "/opt/cni/bin"
defaultReadinessIndicatorFile = ""
defaultMultusNamespace = "kube-system"
defaultNonIsolatedNamespace = "default"
)
// LoadDelegateNetConfList reads DelegateNetConf from bytes
@@ -51,18 +55,20 @@ func LoadDelegateNetConfList(bytes []byte, delegateConf *DelegateNetConf) error
return logging.Errorf("LoadDelegateNetConfList: a plugin delegate must have the 'type' field")
}
delegateConf.ConfListPlugin = true
delegateConf.Name = delegateConf.ConfList.Name
return nil
}
// LoadDelegateNetConf converts raw CNI JSON into a DelegateNetConf structure
func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID string) (*DelegateNetConf, error) {
func LoadDelegateNetConf(bytes []byte, netElement *NetworkSelectionElement, deviceID string, resourceName string) (*DelegateNetConf, error) {
var err error
logging.Debugf("LoadDelegateNetConf: %s, %v, %s", string(bytes), net, deviceID)
logging.Debugf("LoadDelegateNetConf: %s, %v, %s", string(bytes), netElement, deviceID)
delegateConf := &DelegateNetConf{}
if err := json.Unmarshal(bytes, &delegateConf.Conf); err != nil {
return nil, logging.Errorf("LoadDelegateNetConf: error unmarshalling delegate config: %v", err)
}
delegateConf.Name = delegateConf.Conf.Name
// Do some minimal validation
if delegateConf.Conf.Type == "" {
@@ -74,9 +80,11 @@ func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID st
if err != nil {
return nil, logging.Errorf("LoadDelegateNetConf: failed to add deviceID in NetConfList bytes: %v", err)
}
delegateConf.ResourceName = resourceName
delegateConf.DeviceID = deviceID
}
if net != nil && net.CNIArgs != nil {
bytes, err = addCNIArgsInConfList(bytes, net.CNIArgs)
if netElement != nil && netElement.CNIArgs != nil {
bytes, err = addCNIArgsInConfList(bytes, netElement.CNIArgs)
if err != nil {
return nil, logging.Errorf("LoadDelegateNetConf(): failed to add cni-args in NetConfList bytes: %v", err)
}
@@ -87,40 +95,56 @@ func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID st
if err != nil {
return nil, logging.Errorf("LoadDelegateNetConf: failed to add deviceID in NetConf bytes: %v", err)
}
// Save them for housekeeping
delegateConf.ResourceName = resourceName
delegateConf.DeviceID = deviceID
}
if net != nil && net.CNIArgs != nil {
bytes, err = addCNIArgsInConfig(bytes, net.CNIArgs)
if netElement != nil && netElement.CNIArgs != nil {
bytes, err = addCNIArgsInConfig(bytes, netElement.CNIArgs)
if err != nil {
return nil, logging.Errorf("LoadDelegateNetConf(): failed to add cni-args in NetConfList bytes: %v", err)
}
}
}
if net != nil {
if net.Name != "" {
if netElement != nil {
if netElement.Name != "" {
// Overwrite CNI config name with net-attach-def name
delegateConf.Name = fmt.Sprintf("%s/%s", net.Namespace, net.Name)
delegateConf.Name = fmt.Sprintf("%s/%s", netElement.Namespace, netElement.Name)
}
if net.InterfaceRequest != "" {
delegateConf.IfnameRequest = net.InterfaceRequest
if netElement.InterfaceRequest != "" {
delegateConf.IfnameRequest = netElement.InterfaceRequest
}
if net.MacRequest != "" {
delegateConf.MacRequest = net.MacRequest
if netElement.MacRequest != "" {
delegateConf.MacRequest = netElement.MacRequest
}
if net.IPRequest != nil {
delegateConf.IPRequest = net.IPRequest
if netElement.IPRequest != nil {
delegateConf.IPRequest = netElement.IPRequest
}
if net.BandwidthRequest != nil {
delegateConf.BandwidthRequest = net.BandwidthRequest
if netElement.BandwidthRequest != nil {
delegateConf.BandwidthRequest = netElement.BandwidthRequest
}
if net.PortMappingsRequest != nil {
delegateConf.PortMappingsRequest = net.PortMappingsRequest
if netElement.PortMappingsRequest != nil {
delegateConf.PortMappingsRequest = netElement.PortMappingsRequest
}
if net.GatewayRequest != nil {
delegateConf.GatewayRequest = append(delegateConf.GatewayRequest, net.GatewayRequest...)
if netElement.GatewayRequest != nil {
var list []net.IP
if delegateConf.GatewayRequest != nil {
list = append(*delegateConf.GatewayRequest, *netElement.GatewayRequest...)
} else {
list = *netElement.GatewayRequest
}
delegateConf.GatewayRequest = &list
}
if net.InfinibandGUIDRequest != "" {
delegateConf.InfinibandGUIDRequest = net.InfinibandGUIDRequest
if netElement.InfinibandGUIDRequest != "" {
delegateConf.InfinibandGUIDRequest = netElement.InfinibandGUIDRequest
}
if netElement.DeviceID != "" {
if deviceID != "" {
logging.Debugf("Warning: Both RuntimeConfig and ResourceMap provide deviceID. Ignoring RuntimeConfig")
} else {
delegateConf.DeviceID = netElement.DeviceID
}
}
}
@@ -129,76 +153,136 @@ func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID st
return delegateConf, nil
}
// MergeCNIRuntimeConfig creates CNI runtimeconfig from delegate
func MergeCNIRuntimeConfig(runtimeConfig *RuntimeConfig, delegate *DelegateNetConf) *RuntimeConfig {
logging.Debugf("MergeCNIRuntimeConfig: %v %v", runtimeConfig, delegate)
// mergeCNIRuntimeConfig creates CNI runtimeconfig from delegate
func mergeCNIRuntimeConfig(runtimeConfig *RuntimeConfig, delegate *DelegateNetConf) *RuntimeConfig {
logging.Debugf("mergeCNIRuntimeConfig: %v %v", runtimeConfig, delegate)
var mergedRuntimeConfig RuntimeConfig
if runtimeConfig == nil {
runtimeConfig = &RuntimeConfig{}
mergedRuntimeConfig = RuntimeConfig{}
} else {
mergedRuntimeConfig = *runtimeConfig
}
// multus inject RuntimeConfig only in case of non MasterPlugin.
if delegate.MasterPlugin != true {
logging.Debugf("MergeCNIRuntimeConfig: add runtimeConfig for net-attach-def: %v", runtimeConfig)
logging.Debugf("mergeCNIRuntimeConfig: add runtimeConfig for net-attach-def: %v", mergedRuntimeConfig)
if delegate.PortMappingsRequest != nil {
runtimeConfig.PortMaps = delegate.PortMappingsRequest
mergedRuntimeConfig.PortMaps = delegate.PortMappingsRequest
}
if delegate.BandwidthRequest != nil {
runtimeConfig.Bandwidth = delegate.BandwidthRequest
mergedRuntimeConfig.Bandwidth = delegate.BandwidthRequest
}
if delegate.IPRequest != nil {
runtimeConfig.IPs = delegate.IPRequest
mergedRuntimeConfig.IPs = delegate.IPRequest
}
if delegate.MacRequest != "" {
runtimeConfig.Mac = delegate.MacRequest
mergedRuntimeConfig.Mac = delegate.MacRequest
}
if delegate.InfinibandGUIDRequest != "" {
runtimeConfig.InfinibandGUID = delegate.InfinibandGUIDRequest
mergedRuntimeConfig.InfinibandGUID = delegate.InfinibandGUIDRequest
}
if delegate.DeviceID != "" {
mergedRuntimeConfig.DeviceID = delegate.DeviceID
}
logging.Debugf("mergeCNIRuntimeConfig: add runtimeConfig for net-attach-def: %v", mergedRuntimeConfig)
}
return runtimeConfig
return &mergedRuntimeConfig
}
// CreateCNIRuntimeConf create CNI RuntimeConf
func CreateCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string, rc *RuntimeConfig) *libcni.RuntimeConf {
logging.Debugf("LoadCNIRuntimeConf: %v, %v, %s, %v", args, k8sArgs, ifName, rc)
// CreateCNIRuntimeConf create CNI RuntimeConf for a delegate. If delegate configuration
// exists, merge data with the runtime config.
func CreateCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string, rc *RuntimeConfig, delegate *DelegateNetConf) (*libcni.RuntimeConf, string) {
logging.Debugf("CreateCNIRuntimeConf: %v, %v, %s, %v %v", args, k8sArgs, ifName, rc, delegate)
var cniDeviceInfoFile string
var delegateRc *RuntimeConfig
if delegate != nil {
delegateRc = mergeCNIRuntimeConfig(rc, delegate)
if delegateRc.DeviceID != "" {
if delegateRc.CNIDeviceInfoFile != "" {
logging.Debugf("Warning: Existing value of CNIDeviceInfoFile will be overwritten %s", delegateRc.CNIDeviceInfoFile)
}
autoDeviceInfo := fmt.Sprintf("%s-%s_%s", delegate.Name, args.ContainerID, ifName)
delegateRc.CNIDeviceInfoFile = nadutils.GetCNIDeviceInfoPath(autoDeviceInfo)
cniDeviceInfoFile = delegateRc.CNIDeviceInfoFile
logging.Debugf("Adding auto-generated CNIDeviceInfoFile: %s", delegateRc.CNIDeviceInfoFile)
}
} else {
delegateRc = rc
}
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#buildCNIRuntimeConf
// Todo
// ingress, egress and bandwidth capability features as same as kubelet.
rt := &libcni.RuntimeConf{
ContainerID: args.ContainerID,
NetNS: args.Netns,
IfName: ifName,
// NOTE: Verbose logging depends on this order, so please keep Args order.
// NOTE: Verbose logging (pod namespace/pod name)depends on this order, so please keep Args order.
Args: [][2]string{
{"IgnoreUnknown", string("true")},
{"K8S_POD_NAMESPACE", string(k8sArgs.K8S_POD_NAMESPACE)},
{"K8S_POD_NAME", string(k8sArgs.K8S_POD_NAME)},
{"K8S_POD_INFRA_CONTAINER_ID", string(k8sArgs.K8S_POD_INFRA_CONTAINER_ID)},
{"K8S_POD_UID", string(k8sArgs.K8S_POD_UID)},
},
}
if rc != nil {
// Populate rt.Args with CNI_ARGS if the rt.Args value is not set
cniArgs := os.Getenv("CNI_ARGS")
if cniArgs != "" {
for _, arg := range strings.Split(cniArgs, ";") {
// SplitN to handle = within values, like BLAH=foo=bar
keyval := strings.SplitN(arg, "=", 2)
if len(keyval) != 2 {
logging.Errorf("CreateCNIRuntimeConf: CNI_ARGS %s %s %d is not recognized as CNI arg, skipped", arg, keyval, len(keyval))
continue
}
envKey := string(keyval[0])
envVal := string(keyval[1])
found := false
for i := range rt.Args {
// Update existing key if its value is empty
if rt.Args[i][0] == envKey && rt.Args[i][1] == "" && envVal != "" {
logging.Debugf("CreateCNIRuntimeConf: add new val: %s", arg)
rt.Args[i][1] = envVal
found = true
break
}
}
if !found {
// Add the new key if it didn't exist yet
rt.Args = append(rt.Args, [2]string{envKey, envVal})
}
}
}
if delegateRc != nil {
capabilityArgs := map[string]interface{}{}
if len(rc.PortMaps) != 0 {
capabilityArgs["portMappings"] = rc.PortMaps
if len(delegateRc.PortMaps) != 0 {
capabilityArgs["portMappings"] = delegateRc.PortMaps
}
if rc.Bandwidth != nil {
capabilityArgs["bandwidth"] = rc.Bandwidth
if delegateRc.Bandwidth != nil {
capabilityArgs["bandwidth"] = delegateRc.Bandwidth
}
if len(rc.IPs) != 0 {
capabilityArgs["ips"] = rc.IPs
if len(delegateRc.IPs) != 0 {
capabilityArgs["ips"] = delegateRc.IPs
}
if len(rc.Mac) != 0 {
capabilityArgs["mac"] = rc.Mac
if len(delegateRc.Mac) != 0 {
capabilityArgs["mac"] = delegateRc.Mac
}
if len(rc.InfinibandGUID) != 0 {
capabilityArgs["infinibandGUID"] = rc.InfinibandGUID
if len(delegateRc.InfinibandGUID) != 0 {
capabilityArgs["infinibandGUID"] = delegateRc.InfinibandGUID
}
if delegateRc.DeviceID != "" {
capabilityArgs["deviceID"] = delegateRc.DeviceID
}
if delegateRc.CNIDeviceInfoFile != "" {
capabilityArgs["CNIDeviceInfoFile"] = delegateRc.CNIDeviceInfoFile
}
rt.CapabilityArgs = capabilityArgs
}
return rt
return rt, cniDeviceInfoFile
}
// GetGatewayFromResult retrieves gateway IP addresses from CNI result
@@ -215,7 +299,8 @@ func GetGatewayFromResult(result *current.Result) []net.IP {
// LoadNetConf converts inputs (i.e. stdin) to NetConf
func LoadNetConf(bytes []byte) (*NetConf, error) {
netconf := &NetConf{}
// LogToStderr's default value set to true
netconf := &NetConf{LogToStderr: true}
logging.Debugf("LoadNetConf: %s", string(bytes))
if err := json.Unmarshal(bytes, netconf); err != nil {
@@ -223,6 +308,8 @@ func LoadNetConf(bytes []byte) (*NetConf, error) {
}
// Logging
logging.SetLogStderr(netconf.LogToStderr)
logging.SetLogOptions(netconf.LogOptions)
if netconf.LogFile != "" {
logging.SetLogFile(netconf.LogFile)
}
@@ -281,6 +368,19 @@ func LoadNetConf(bytes []byte) (*NetConf, error) {
netconf.MultusNamespace = defaultMultusNamespace
}
// setup namespace isolation
if netconf.RawNonIsolatedNamespaces == "" {
netconf.NonIsolatedNamespaces = []string{defaultNonIsolatedNamespace}
} else {
// Parse the comma separated list
nonisolated := strings.Split(netconf.RawNonIsolatedNamespaces, ",")
// Cleanup the whitespace
for i, nonv := range nonisolated {
nonisolated[i] = strings.TrimSpace(nonv)
}
netconf.NonIsolatedNamespaces = nonisolated
}
// get RawDelegates and put delegates field
if netconf.ClusterNetwork == "" {
// for Delegates
@@ -292,7 +392,7 @@ func LoadNetConf(bytes []byte) (*NetConf, error) {
if err != nil {
return nil, logging.Errorf("LoadNetConf: error marshalling delegate %d config: %v", idx, err)
}
delegateConf, err := LoadDelegateNetConf(bytes, nil, "")
delegateConf, err := LoadDelegateNetConf(bytes, nil, "", "")
if err != nil {
return nil, logging.Errorf("LoadNetConf: failed to load delegate %d config: %v", idx, err)
}
@@ -444,15 +544,47 @@ func addCNIArgsInConfList(inBytes []byte, cniArgs *map[string]interface{}) ([]by
return configBytes, nil
}
// CheckGatewayConfig check gatewayRequest and mark IsFilterGateway flag if
// CheckGatewayConfig check gatewayRequest and mark IsFilter{V4,V6}Gateway flag if
// gw filtering is required
func CheckGatewayConfig(delegates []*DelegateNetConf) {
// Check the Gateway
for i, delegate := range delegates {
if delegate.GatewayRequest == nil {
delegates[i].IsFilterGateway = true
func CheckGatewayConfig(delegates []*DelegateNetConf) error {
v4Gateways := 0
v6Gateways := 0
// Check the gateway
for _, delegate := range delegates {
if delegate.GatewayRequest != nil {
for _, gw := range *delegate.GatewayRequest {
if gw.To4() != nil {
v4Gateways++
} else {
v6Gateways++
}
}
}
}
if v4Gateways > 1 || v6Gateways > 1 {
return fmt.Errorf("multus does not support ECMP for default-route")
}
// set filter flag for each delegate
for i, delegate := range delegates {
// no GatewayRequest
if delegate.GatewayRequest == nil {
delegates[i].IsFilterV4Gateway = true
delegates[i].IsFilterV6Gateway = true
} else {
for _, gw := range *delegate.GatewayRequest {
if gw.To4() != nil {
delegates[i].IsFilterV6Gateway = true
} else {
delegates[i].IsFilterV4Gateway = true
}
}
}
}
return nil
}
// CheckSystemNamespaces checks whether given namespace is in systemNamespaces or not.

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