197 Commits

Author SHA1 Message Date
OpenShift Merge Robot
30668ec22b Merge pull request #85 from openshift-bot/art-consistency-openshift-4.6-multus-cni
Updating multus-cni builder & base images to be consistent with ART
2021-01-14 18:51:37 -05:00
AOS Automation Release Team
dc18392c36 Updating multus-cni builder & base images to be consistent with ART
Reconciling with 4a98c8d67f/images/multus-cni.yml
2021-01-14 05:01:47 +00:00
OpenShift Merge Robot
6fb56fdcbe Merge pull request #84 from dougbtv/backport-exit-zero-on-del-4.6
Bug 1905230: Multus should exit zero on DEL when cache file is missing [backport 4.6]
2020-12-12 09:52:19 -05:00
dougbtv
4459a257d5 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-12-07 14:17:43 -05:00
Justin Pierce
dffa09195b Merge pull request #79 from jupierce/art-consistency-openshift-4.6-multus-cni
Bug 1878163: Updating multus-cni builder & base images to be consistent with ART
2020-10-01 14:02:49 -04:00
Justin Pierce
c99db40b96 Updating multus-cni builder & base images to be consistent with ART
Reconciling with 990044f295/images/multus-cni.yml
2020-09-30 14:55:27 -04:00
OpenShift Merge Robot
d20a45d15d Merge pull request #78 from openshift-bot/updating-dockerfile.openshift-baseimages-to-mach-ocp-build-data-config
Bug 1878163: Updating Dockerfile.openshift baseimages to mach ocp-build-data config
2020-09-19 06:42:34 -04:00
openshift-bot
9c3188d430 Updating Dockerfile.openshift baseimages to mach ocp-build-data config
This PR is autogenerated by the [ocp-build-data-enforcer][1].
It updates the base images in the Dockerfile used for promotion in order to ensure it
matches the configuration in the [ocp-build-data repository][2] used
for producing release artifacts.

Instead of merging this PR you can also create an alternate PR that includes the changes found here.

If you believe the content of this PR is incorrect, please contact the dptp team in
#aos-art.

[1]: https://github.com/openshift/ci-tools/tree/master/cmd/ocp-build-data-enforcer
[2]: https://github.com/openshift/ocp-build-data/tree/openshift-4.6/images
2020-09-16 20:14:32 -04:00
OpenShift Merge Robot
956afbd557 Merge pull request #77 from openshift-bot/updating-dockerfile.openshift-baseimages-to-mach-ocp-build-data-config
Bug 1872080: Updating Dockerfile.openshift baseimages to mach ocp-build-data config
2020-08-25 18:15:31 -04:00
openshift-bot
5aa55766f1 Updating Dockerfile.openshift baseimages to mach ocp-build-data config
This PR is autogenerated by the [ocp-build-data-enforcer][1].
It updates the baseimages in the Dockerfile used for promotion in order to ensure it
matches the configuration in the [ocp-build-data repository][2] used
for producing release artifacts.

If you believe the content of this PR is incorrect, please contact the dptp team in
#forum-testplatform.

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

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

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

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

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

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

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

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

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

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

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

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

* Fix typo

* Update error message

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

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

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

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

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

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

Fixes #179

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

Fixes #177

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

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

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

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

Change-Id: I6630f56adc0a8307f575fc09ce9090c1ffca0337
2018-11-01 22:41:56 +09:00
Louis Woods
93237e6161 Improve grep in entrypoint.sh to only find .conf and .conflist files 2018-11-01 22:41:56 +09:00
Louis Woods
bae3adf158 Add the option to auto generate 00-multus.conf
When `--multus-conf-file=auto` is used, 00-multus.conf will be
automatically generated from the CNI configuration file of the master
plugin (the first file in lexicographical order in cni-conf-dir).
2018-11-01 22:41:56 +09:00
Tomofumi Hayashi
e9aa18319d Add bracket [] in Dockerfile's entrypoint to parse argument correctly. 2018-11-01 22:41:56 +09:00
1995 changed files with 182180 additions and 312237 deletions

View File

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

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

@@ -0,0 +1,25 @@
name: Go-build-ppc64le
on: [push, pull_request]
jobs:
build:
name: Build and test
runs-on: ubuntu-latest
if: >
(( github.event.pull_request.head.repo.owner.login != github.event.pull_request.base.repo.owner.login ) &&
github.event_name == 'pull_request' ) || (github.event_name == 'push' && github.event.commits != '[]' )
env:
GO111MODULE: on
TARGET: arm64
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build
run: GOARCH="${TARGET}" ./build

View File

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

View File

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

View File

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

View File

@@ -1,285 +0,0 @@
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,7 +1,9 @@
name: e2e-kind
on: [push, pull_request]
jobs:
e2e-kind:
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 ) &&
@@ -14,7 +16,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 -f images/Dockerfile.thick .
run: docker build -t localhost:5000/multus:e2e .
- name: Push to local registry
run: docker push localhost:5000/multus:e2e
@@ -27,22 +29,10 @@ 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

View File

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

View File

@@ -1,15 +0,0 @@
name: 'Close stale issues and PRs'
on:
schedule:
- cron: '30 1 * * *'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@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

View File

@@ -1,48 +0,0 @@
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,6 +1,5 @@
# 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: ./cmd/
main: ./multus/
goos:
- linux
goarch:
@@ -17,9 +17,6 @@ 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}" ./hack/build-go.sh
- GOARCH="${TARGET}" ./build
- |
if [ "${TARGET}" == "amd64" ]; then
sudo env PATH=${PATH} ./scripts/test.sh
sudo env PATH=${PATH} ./test.sh
goveralls -coverprofile=coverage.out -service=travis-ci
docker build -t ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 .
docker build -t ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le -f Dockerfile.ppc64le .

20
Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
# This Dockerfile is used to build the image available on DockerHub
FROM centos:centos7 as build
# Add everything
ADD . /usr/src/multus-cni
ENV INSTALL_PKGS "git golang-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"]

20
Dockerfile.arm64 Normal file
View File

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

41
Dockerfile.openshift Normal file
View File

@@ -0,0 +1,41 @@
# This dockerfile is specific to building Multus for OpenShift
FROM registry.ci.openshift.org/ocp/builder:rhel-8-golang-1.15-openshift-4.6 AS rhel8
ADD . /usr/src/multus-cni
WORKDIR /usr/src/multus-cni
ENV CGO_ENABLED=1
ENV GO111MODULE=off
ENV VERSION=rhel8 COMMIT=unset
RUN ./build && \
cd /usr/src/multus-cni/bin
WORKDIR /
FROM registry.ci.openshift.org/ocp/builder:rhel-7-golang-1.15-openshift-4.6 AS rhel7
ADD . /usr/src/multus-cni
WORKDIR /usr/src/multus-cni
ENV CGO_ENABLED=1
ENV GO111MODULE=off
RUN ./build && \
cd /usr/src/multus-cni/bin
WORKDIR /usr/src/multus-cni
ENV GO111MODULE=off
RUN ./build && \
cd /usr/src/multus-cni/bin
WORKDIR /
FROM registry.ci.openshift.org/ocp/4.6:base
RUN mkdir -p /usr/src/multus-cni/images && \
mkdir -p /usr/src/multus-cni/bin && \
mkdir -p /usr/src/multus-cni/rhel7/bin && \
mkdir -p /usr/src/multus-cni/rhel8/bin
COPY --from=rhel7 /usr/src/multus-cni/bin/multus /usr/src/multus-cni/rhel7/bin
COPY --from=rhel8 /usr/src/multus-cni/bin/multus /usr/src/multus-cni/bin
COPY --from=rhel8 /usr/src/multus-cni/bin/multus /usr/src/multus-cni/rhel8/bin
ADD ./images/entrypoint.sh /
LABEL io.k8s.display-name="Multus CNI" \
io.k8s.description="This is a component of OpenShift Container Platform and provides a meta CNI plugin." \
io.openshift.tags="openshift" \
maintainer="Doug Smith <dosmith@redhat.com>"
ENTRYPOINT ["/entrypoint.sh"]

25
Dockerfile.ppc64le Normal file
View File

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

16
OWNERS Normal file
View File

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

View File

@@ -1,8 +1,8 @@
# Multus-CNI
![multus-cni Logo](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/images/Multus.png)
![multus-cni Logo](https://github.com/intel/multus-cni/blob/master/doc/images/Multus.png)
[![Build](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/build.yml/badge.svg)](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/build.yml)[![Test](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/test.yml/badge.svg)](https://github.com/k8snetworkplumbingwg/multus-cni/actions/workflows/test.yml)[![Go Report Card](https://goreportcard.com/badge/github.com/k8snetworkplumbingwg/multus-cni)](https://goreportcard.com/report/github.com/k8snetworkplumbingwg/multus-cni)[![Coverage Status](https://coveralls.io/repos/github/k8snetworkplumbingwg/multus-cni/badge.svg)](https://coveralls.io/github/k8snetworkplumbingwg/multus-cni)
[![Travis CI](https://travis-ci.org/intel/multus-cni.svg?branch=master)](https://travis-ci.org/intel/multus-cni/builds)[![Go Report Card](https://goreportcard.com/badge/github.com/intel/multus-cni)](https://goreportcard.com/report/github.com/intel/multus-cni)[![Coverage Status](https://coveralls.io/repos/github/intel/multus-cni/badge.svg)](https://coveralls.io/github/intel/multus-cni)
Multus CNI enables attaching multiple network interfaces to pods in Kubernetes.
@@ -18,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](docs/images/multus-pod-image.svg)
![multus-pod-image](doc/images/multus-pod-image.svg)
## Quickstart Installation Guide
The quickstart installation method for Multus requires that you have first installed a Kubernetes CNI plugin to serve as your pod-to-pod network, which we refer to as your "default network" (a network interface that every pod will be created with). Each network attachment created by Multus will be in addition to this default network interface. For more detail on installing a default network CNI plugins, refer to our [quick-start guide](docs/quickstart.md).
The quickstart installation method for Multus requires that you have first installed a Kubernetes CNI plugin to serve as your pod-to-pod network, which we refer to as your "default network" (a network interface that every pod will be creatd with). Each network attachment created by Multus will be in addition to this default network interface. For more detail on installing a default network CNI plugins, refer to our [quick-start guide](doc/quickstart.md).
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 ./deployments/multus-daemonset-thick-plugin.yml | kubectl apply -f -
$ cat ./images/multus-daemonset.yml | kubectl apply -f -
```
This will configure your systems to be ready to use Multus CNI, but, to get started with adding additional interfaces to your pods, refer to our complete [quick-start guide](docs/quickstart.md)
This will configure your systems to be ready to use Multus CNI, but, to get started with adding additional interfaces to your pods, refer to our complete [quick-start guide](doc/quickstart.md)
## Additional installation Options
- Install via daemonset using the quick-start guide, above.
- Download binaries from [release page](https://github.com/k8snetworkplumbingwg/multus-cni/releases)
- Download binaries from [release page](https://github.com/intel/multus-cni/releases)
- By Docker image from [Docker Hub](https://hub.docker.com/r/nfvpe/multus/tags/)
- Or, roll-your-own and build from source
- See [Development](docs/development.md)
- See [Development](doc/development.md)
## Comprehensive Documentation
- [How to use](docs/how-to-use.md)
- [Configuration](docs/configuration.md)
- [Development](docs/development.md)
- [How to use](doc/how-to-use.md)
- [Configuration](doc/configuration.md)
- [Development](doc/development.md)
## Contact Us
For any questions about Multus CNI, 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/).
For any questions about Multus CNI, feel free to ask a question in #general in the [Intel-Corp Slack](https://intel-corp.herokuapp.com/), or open up a GitHub issue.

50
build Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

@@ -0,0 +1,95 @@
#!/bin/bash
# Copyright (c) 2018 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# create temp dir to store intermediate files
tmp=$(mktemp -d)
# generate private key
echo "Generating private RSA key..."
openssl genrsa -out ${tmp}/webhook-key.pem 2048 >/dev/null 2>&1
# generate CSR
echo "Generating CSR configuration file..."
cat <<EOF >> ${tmp}/webhook.conf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = multus-webhook-service
DNS.2 = multus-webhook-service.default
DNS.3 = multus-webhook-service.default.svc
EOF
openssl req -new -key ${tmp}/webhook-key.pem -subj "/CN=multus-webhook-service.default.svc" -out ${tmp}/server.csr -config ${tmp}/webhook.conf
# push CSR to Kubernetes API server
echo "Sending CSR to Kubernetes..."
csr_name="multus-webhook-service.default"
kubectl delete csr ${csr_name} >/dev/null 2>&1
cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: ${csr_name}
spec:
request: $(cat ${tmp}/server.csr | base64 -w0)
groups:
- system:authenticated
usages:
- digital signature
- key encipherment
- server auth
EOF
# approve certificate
echo "Approving CSR..."
kubectl certificate approve ${csr_name}
# wait for the cert to be issued
echo -n "Waiting for the certificate to be issued..."
cert=""
for sec in $(seq 15); do
cert=$(kubectl get csr ${csr_name} -o jsonpath='{.status.certificate}')
if [[ $cert != "" ]]; then
echo -e "\nCertificate issued succesfully."
echo $cert | base64 --decode > ${tmp}/webhook-cert.pem
break
fi
echo -n "."; sleep 1
done
if [[ $cert == "" ]]; then
echo -e "\nError: certificate not issued. Verify that the API for signing certificates is enabled."
exit
fi
# create secret
echo "Creating secret..."
kubectl delete secret "multus-webhook-secret"
kubectl create secret generic --from-file=key.pem=${tmp}/webhook-key.pem --from-file=cert.pem=${tmp}/webhook-cert.pem "multus-webhook-secret"
# set cert in webhook configuration
echo "Patching configuration file with certificate..."
if [[ -f configuration-template.yaml ]]; then
sed "s/__CERT__/${cert}/" configuration-template.yaml > configuration.yaml
echo "File configuration.yaml patched."
else
echo -e "Error: validating configuration template file 'configuration-template.yaml' is missing. Please update it with cert.pem value from the secret manually."
fi

View File

@@ -0,0 +1,38 @@
# Copyright (c) 2018 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
labels:
app: multus-webhook
name: multus-webhook-config
webhooks:
- clientConfig:
caBundle: __CERT__
service:
name: multus-webhook-service
namespace: default
path: /validate
failurePolicy: Fail
name: multus-webhook.k8s.cni.cncf.io
rules:
- apiGroups:
- k8s.cni.cncf.io
apiVersions:
- v1
resources:
- network-attachment-definitions
operations:
- CREATE

View File

@@ -0,0 +1,50 @@
# Copyright (c) 2018 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: multus-webhook
name: multus-webhook-deployment
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: multus-webhook
template:
metadata:
labels:
app: multus-webhook
spec:
containers:
- name: multus-webhook
image: multus-webhook
command:
- /webhook/webhook
args:
- --bind-address=0.0.0.0
- --port=443
- --tls-private-key-file=/webhook/tls/key.pem
- --tls-cert-file=/webhook/tls/cert.pem
volumeMounts:
- mountPath: /webhook/tls
name: multus-webhook-secret
readOnly: True
imagePullPolicy: IfNotPresent
volumes:
- name: multus-webhook-secret
secret:
secretName: multus-webhook-secret

View File

@@ -1,9 +1,10 @@
# Copyright 2018 The Prometheus Authors
# Copyright (c) 2018 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
# 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,
@@ -11,19 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
include Makefile.common
%/.unpacked: %.ttar
@echo ">> extracting fixtures"
./ttar -C $(dir $*) -x -f $*.ttar
touch $@
update_fixtures:
rm -vf fixtures/.unpacked
./ttar -c -f fixtures.ttar fixtures/
.PHONY: build
build:
.PHONY: test
test: fixtures/.unpacked common-test
apiVersion: v1
kind: Service
metadata:
name: multus-webhook-service
labels:
app: multus-webhook
namespace: default
spec:
ports:
- port: 443
targetPort: 443
selector:
app: multus-webhook

View File

@@ -1,189 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: network-attachment-definitions.k8s.cni.cncf.io
spec:
group: k8s.cni.cncf.io
scope: Namespaced
names:
plural: network-attachment-definitions
singular: network-attachment-definition
kind: NetworkAttachmentDefinition
shortNames:
- net-attach-def
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
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

@@ -5,7 +5,6 @@ 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",
@@ -14,12 +13,6 @@ 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
},
@@ -46,19 +39,17 @@ 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/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.
* `kubeconfig` (string, optional): kubeconfig file for the out of cluster communication with kube-apiserver. See the example [kubeconfig](https://github.com/intel/multus-cni/blob/master/doc/node-kubeconfig.yaml). If you would like to use CRD (i.e. network attachment definition), this is required
* `logFile` (string, optional): file path for log file. multus puts log in given file
* `logLevel` (string, optional): logging level ("debug", "error", "verbose", or "panic")
* `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/Bandwidth capability for cluster networks).
* `readinessindicatorfile`: The path to a file whose existence 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 capability for now). See the [example](https://github.com/intel/multus-cni/blob/master/examples/multus-ptp-portmap.conf).
* `readinessindicatorfile`: The path to a file whose existance denotes that the default network is ready
User should chose following parameters combination (`clusterNetwork`+`defaultNetworks` or `delegates`):
* `clusterNetwork` (string, required): default CNI network for pods, used in kubernetes cluster (Pod IP and so on): name of network-attachment-definition, CNI json file name (without 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
* `clusterNetwork` (string, required): default CNI network for pods, used in kubernetes cluster (Pod IP and so on): name of network-attachment-definition, CNI json file name (without extention, .conf/.conflist) or directory for CNI config file
* `defaultNetworks` ([]string, required): default CNI network attachment: name of network-attachment-definition, CNI json file name (without extention, .conf/.conflist) or directory for CNI config file
* `systemNamespaces` ([]string, optional): list of namespaces for Kubernetes system (namespaces listed here will not have `defaultNetworks` added)
* `multusNamespace` (string, optional): namespace for `clusterNetwork`/`defaultNetworks`
* `delegates` ([]map,required): number of delegate details in the Multus
@@ -68,7 +59,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 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. CNI json config file in `confDir`. Given name should be without extention, like .conf/.conflist. (e.g. "test" for "test.conf")
1. Directory for CNI json config file. Multus will find alphabetically first file for the network
1. Multus failed to find network. Multus raise error message
@@ -84,7 +75,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 existence denotes that the default network is ready.
* `readinessindicatorfile`: The path to a file whose existance denotes that the default network is ready.
*NOTE*: If `readinessindicatorfile` is unset, or is an empty string, this functionality will be disabled, and is disabled by default.
@@ -93,15 +84,7 @@ 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.
#### 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,
```
Multus will always log via `STDERR`, which is the standard method by which CNI plugins communicate errors, and these errors are logged by the Kubelet. This method is always enabled.
#### Writing to a Log File
@@ -110,7 +93,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
@@ -127,27 +110,7 @@ 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",
```
#### 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
}
"LogLevel": "debug",
```
### Namespace Isolation
@@ -156,8 +119,6 @@ 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.
@@ -254,7 +215,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] GetNetworkDelegates: namespace isolation enabled, annotation violates permission, pod is in namespace development but refers to target namespace privileged
2018-12-18T21:41:32Z [error] GetPodNetwork: namespace isolation violation: podnamespace: development / target namespace: privileged
```
This error expresses that the pod resides in the namespace named `development` but refers to a `NetworkAttachmentDefinition` outside of that namespace, in this case, the namespace named `privileged`.
@@ -292,16 +253,6 @@ 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/k8snetworkplumbingwg/multus-cni.v3
go get gopkg.in/intel/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 `./hack/build-go.sh` script!
You can use the built in `./build` script!
```
git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
git clone https://github.com/intel/multus-cni.git
cd multus-cni
./hack/build-go.sh
./build
```
## 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 ./hack/test-go.sh
sudo ./test.sh
```
## What are the best practices for logging?
The following are the best practices for multus logging:
* Add `logging.Debugf()` at the beginning of functions
* Add `logging.Debugf()` at the begining of functions
* In case of error handling, use `logging.Errorf()` with given error info
* `logging.Panicf()` only be used for critical errors (it should NOT normally be used)
## Multus release schedule
## CI Introduction
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.
TBD

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/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).
You may acquire the Multus binary via compilation (see the [developer guide](development.md)) or download the a binary from the [GitHub releases](https://github.com/intel/multus-cni/releases) page. Copy multus binary into CNI binary directory, usually `/opt/cni/bin`. Perform this on all nodes in your cluster (master and nodes).
cp multus /opt/cni/bin
$ 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 ./deployments/multus-daemonset.yml | kubectl apply -f -
$ cat ./images/{multus-daemonset.yml,flannel-daemonset.yml} | kubectl apply -f -
If you need more comprehensive detail, continue along with this guide, otherwise, you may wish to either [follow the quickstart guide]() or skip to the ['Create network attachment definition'](#create-network-attachment-definition) section.
@@ -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/00-multus.conf <<EOF
$ mkdir -p /etc/cni/net.d
$ cat >/etc/cni/net.d/30-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
#### Launch pod with text annotation
#### Lauch 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
```
#### Launch pod with text annotation for NetworkAttachmentDefinition in different namespace
#### Lauch pod with text annotation for NetworkAttachmentDefinition in different namespace
You can also specify NetworkAttachmentDefinition with its namespace as adding `<namespace>/`
```
# Execute following command at Kubernetes master
cat <<EOF | kubectl create -f -
$ 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
```
#### Launch pod with text annotation with interface name
#### Lauch pod with text annotation with interface name
You can also specify interface name as adding `@<ifname>`.
```
# Execute following command at Kubernetes master
cat <<EOF | kubectl create -f -
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -352,11 +352,11 @@ spec:
EOF
```
#### Launch pod with json annotation
#### Lauch 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
```
#### Launch pod with json annotation for NetworkAttachmentDefinition in different namespace
#### Lauch pod with json annotation for NetworkAttachmentDefinition in different namespace
You can also specify NetworkAttachmentDefinition with its namespace as adding `"namespace": "<namespace>"`.
```
# Execute following command at Kubernetes master
cat <<EOF | kubectl create -f -
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -400,13 +400,13 @@ spec:
EOF
```
#### Launch pod with json annotation with interface
#### Lauch pod with json annotation with interface
You can also specify interface name as adding `"interface": "<ifname>"`.
```
# Execute following command at Kubernetes master
cat <<EOF | kubectl create -f -
$ cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
@@ -432,8 +432,7 @@ 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
@@ -530,8 +529,7 @@ 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
@@ -569,11 +567,7 @@ This the directory in which the Multus binary will be installed.
--namespace-isolation=false
Setting this option to true enables the Namespace isolation feature, which insists that custom resources must be created in the same namespace as the pods, otherwise it will refuse to attach those definitions as additional interfaces. See (the configuration guide for more information)[configuration.md].
--global-namespaces=default,foo,bar
The `--global-namespaces` works only when `--namespace-isolation=true`. This takes a comma-separated list of namespaces which can be referred to globally when namespace isolation is enabled. See (the configuration guide for more information)[configuration.md].
Setting this option to true enables the Namespace isolation feature, which insists that custom resources must be created in the same namespace as the pods, otherwise it will refuse to attach those definitions as additional interfaces.
--multus-bin-file=/usr/src/multus-cni/bin/multus
@@ -597,20 +591,16 @@ 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](https://github.com/k8snetworkplumbingwg/multus-cni/blob/master/docs/configuration.md#logging) for which values are permitted.
Used only with `--multus-conf-file=auto`. See the documentation for logging for which values are permitted.
Used only with `--multus-conf-file=auto`. Allows you to specify CNI spec version. Please set if you need to specify CNI spec version.
Used only with `--multus-conf-file=auto`. Allows you to specify CNI spec version. Please set if you need to speicfy CNI spec version.
--cni-version=
In some cases, the original CNI configuration that the Multus configuration was generated from (using `--multus-conf-file=auto`) may be used as a sort of semaphor for network readiness -- as this model is used by the Kubelet itself. If you need to disable Multus' 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:
In some cases, the original CNI configuration that the Multus configuration was generated from (using `--multus-conf-file=auto`) may be used as a sort of semaphor for network readiness -- as this model is used by the Kubelet itself. If you need to disable Multus' availablity, you may wish to clean out the generated configuration file when the source file for autogeneration of the config file is no longer present. You can use this functionality by setting:
--cleanup-config-on-exit=true

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 support Kubernetes versions that Kubernetes community supports. Please see [Supported versions](https://kubernetes.io/releases/version-skew-policy/#supported-versions) in Kubernetes document.
We recommend Kubernetes 1.16 or later.
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,14 +29,7 @@ To verify that you default network is ready, you may list your Kubernetes nodes
kubectl get nodes
```
In the case that your default network is ready you will see the `STATUS` column also switch to `Ready` for each node.
```
NAME STATUS ROLES AGE VERSION
master-0 Ready master 1h v1.17.1
master-1 Ready master 1h v1.17.1
master-2 Ready master 1h v1.17.1
```
In the case that your default network is ready
## Installation
@@ -45,19 +38,19 @@ Our recommended quickstart method to deploy Multus is to deploy using a Daemonse
Firstly, clone this GitHub repository.
```
git clone https://github.com/k8snetworkplumbingwg/multus-cni.git && cd multus-cni
git clone https://github.com/intel/multus-cni.git && cd multus-cni
```
We'll apply a YAML file with `kubectl` from this repo.
```
cat ./deployments/multus-daemonset-thick-plugin.yml | kubectl apply -f -
$ cat ./images/multus-daemonset.yml | kubectl apply -f -
```
### What the Multus daemonset does
* Starts a Multus daemonset, this runs a pod on each node which places a Multus binary on each node in `/opt/cni/bin`
* Reads the lexicographically (alphabetically) first configuration file in `/etc/cni/net.d`, and creates a new configuration file for Multus on each node as `/etc/cni/net.d/00-multus.conf`, this configuration is auto-generated and is based on the default network configuration (which is assumed to be the alphabetically first configuration)
* Reads the lexicographically (alphabetically) first configuration file in `/etc/cni/net.d`, and creates a new configuration file for Multus as `/etc/cni/net.d/00-multus.conf`, this configuration is auto-generated and is based on the default network configuration (which is assumed to be the alphabetically first configuration)
* Creates a `/etc/cni/net.d/multus.d` directory on each node with authentication information for Multus to access the Kubernetes API.
@@ -66,7 +59,7 @@ cat ./deployments/multus-daemonset-thick-plugin.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.
@@ -176,7 +169,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:
@@ -191,7 +184,7 @@ For additional confirmation, use `kubectl describe pod samplepod` and there will
```
Annotations: k8s.v1.cni.cncf.io/networks: macvlan-conf
k8s.v1.cni.cncf.io/network-status:
k8s.v1.cni.cncf.io/networks-status:
[{
"name": "cbr0",
"ips": [

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

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

View File

@@ -4,15 +4,9 @@
```
$ git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
$ git clone https://github.com/intel/multus-cni.git
$ cd multus-cni/e2e
$ ./get_tools.sh
$ ./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/v1.1.1/cni-plugins-linux-amd64-v1.1.1.tgz
wget https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-amd64-v0.8.5.tgz
cd /host/opt/cni/bin
tar xvfzp /tmp/cni-plugins-linux-amd64-v1.1.1.tgz
tar xvfzp /tmp/cni-plugins-linux-amd64-v0.8.5.tgz
sleep infinite
---
apiVersion: apps/v1

View File

@@ -1,57 +0,0 @@
---
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.12.0/kind-$(uname)-amd64"
curl -Lo ./bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-$(uname)-amd64"
chmod +x ./bin/kind
curl -Lo ./bin/kubectl https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
chmod +x ./bin/kubectl
curl -Lo ./bin/koko https://github.com/redhat-nfvpe/koko/releases/download/v0.83/koko_0.83_linux_amd64
curl -Lo ./bin/koko https://github.com/redhat-nfvpe/koko/releases/download/v0.82/koko_0.82_linux_amd64
chmod +x ./bin/koko
curl -Lo ./bin/jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
chmod +x ./bin/jq

View File

@@ -1,264 +0,0 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: network-attachment-definitions.k8s.cni.cncf.io
spec:
group: k8s.cni.cncf.io
scope: Namespaced
names:
plural: network-attachment-definitions
singular: network-attachment-definition
kind: NetworkAttachmentDefinition
shortNames:
- net-attach-def
versions:
- name: v1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
config:
type: string
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: multus
rules:
- apiGroups: ["k8s.cni.cncf.io"]
resources:
- '*'
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods
- pods/status
verbs:
- get
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: multus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: multus
subjects:
- kind: ServiceAccount
name: multus
namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: multus
namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
name: multus-cni-config
namespace: kube-system
labels:
tier: node
app: multus
data:
# NOTE: If you'd prefer to manually apply a configuration file, you may create one here.
# In the case you'd like to customize the Multus installation, you should change the arguments to the Multus pod
# change the "args" line below from
# - "--multus-conf-file=auto"
# to:
# "--multus-conf-file=/tmp/multus-conf/70-multus.conf"
# Additionally -- you should ensure that the name "70-multus.conf" is the alphabetically first name in the
# /etc/cni/net.d/ directory on each node, otherwise, it will not be used by the Kubelet.
cni-conf.json: |
{
"name": "multus-cni-network",
"type": "multus",
"capabilities": {
"portMappings": true
},
"delegates": [
{
"cniVersion": "0.3.1",
"name": "default-cni-network",
"plugins": [
{
"type": "flannel",
"name": "flannel.1",
"delegate": {
"isDefaultGateway": true,
"hairpinMode": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
],
"kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig"
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds-amd64
namespace: kube-system
labels:
tier: node
app: multus
name: multus
spec:
selector:
matchLabels:
name: multus
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: multus
name: multus
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/arch: amd64
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
image: localhost:5000/multus:e2e
command: ["/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: 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:
path: /etc/cni/net.d
- name: cnibin
hostPath:
path: /opt/cni/bin
- name: multus-cfg
configMap:
name: multus-cni-config
items:
- key: cni-conf.json
path: 70-multus.conf
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds-ppc64le
namespace: kube-system
labels:
tier: node
app: multus
name: multus
spec:
selector:
matchLabels:
name: multus
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: multus
name: multus
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/arch: ppc64le
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
# ppc64le support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest-ppc64le
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
- "--cni-version=0.3.1"
resources:
requests:
cpu: "100m"
memory: "90Mi"
limits:
cpu: "100m"
memory: "90Mi"
securityContext:
privileged: true
volumeMounts:
- name: cni
mountPath: /host/etc/cni/net.d
- name: cnibin
mountPath: /host/opt/cni/bin
- name: multus-cfg
mountPath: /tmp/multus-conf
volumes:
- name: cni
hostPath:
path: /etc/cni/net.d
- name: cnibin
hostPath:
path: /opt/cni/bin
- name: multus-cfg
configMap:
name: multus-cni-config
items:
- key: cni-conf.json
path: 70-multus.conf

View File

@@ -44,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
@@ -154,16 +145,10 @@ spec:
containers:
- name: kube-multus
image: localhost:5000/multus:e2e
imagePullPolicy: Always
command: [ "/usr/src/multus-cni/bin/multus-daemon" ]
command: ["/entrypoint.sh"]
args:
- "-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"
- "--multus-conf-file=auto"
- "--cni-version=0.3.1"
resources:
requests:
cpu: "100m"
@@ -180,40 +165,6 @@ 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,82 +3,32 @@ 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="$($OCI_BIN inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)"
running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)"
if [ "${running}" != 'true' ]; then
# 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
docker run -d --restart=always -p "${reg_port}:5000" --name "${reg_name}" registry:2
fi
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}"
reg_ip="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' "${reg_name}")"
# deploy cluster with kind
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
endpoint = ["http://${reg_host}:${reg_port}"]
endpoint = ["http://${reg_ip}:${reg_port}"]
nodes:
- role: control-plane
- role: worker
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
pod-manifest-path: "/etc/kubernetes/manifests/"
- 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 -p "$worker1_pid,eth1" -p "$worker2_pid,eth1"
sudo env PATH=${PATH} koko -d kind-worker,eth1 -d kind-worker2,eth1
sleep 1
kubectl -n kube-system wait --for=condition=available deploy/coredns --timeout=300s
kubectl create -f "$MULTUS_MANIFEST"
kubectl create -f https://raw.githubusercontent.com/intel/multus-cni/master/images/multus-daemonset.yml
sleep 1
kubectl -n kube-system wait --for=condition=ready -l name=multus pod --timeout=300s
kubectl create -f cni-install.yml

15
e2e/setup_multus.sh Executable file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,44 +0,0 @@
#!/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 simple-macvlan1.yml
kubectl create -f macvlan1.yml
kubectl wait --for=condition=ready -l app=macvlan --timeout=300s pod
echo "check macvlan1-worker1 interface: net1"
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,6 +25,3 @@ ipaddr=$(kubectl exec macvlan1-worker2 -- ip -j a show | jq -r \
if [ $ipaddr != "10.1.1.12" ]; then
echo "macvlan1-worker2 IP address is different: ${ipaddr}"
fi
echo "cleanup resources"
kubectl delete -f simple-macvlan1.yml

View File

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

View File

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

View File

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

70
go.mod
View File

@@ -1,53 +1,29 @@
module gopkg.in/k8snetworkplumbingwg/multus-cni.v3
module gopkg.in/intel/multus-cni.v3
go 1.16
go 1.12
require (
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
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
gopkg.in/natefinch/lumberjack.v2 v2.0.0
k8s.io/api v0.20.10
k8s.io/apimachinery v0.20.10
k8s.io/client-go v0.20.10
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/klog v1.0.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
k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c // indirect
k8s.io/kubernetes v1.13.0
)

926
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -1,60 +0,0 @@
#!/usr/bin/env bash
set -e
DEST_DIR="bin"
if [ ! -d ${DEST_DIR} ]; then
mkdir ${DEST_DIR}
fi
# Add version/commit/date into binary
# In case of TravisCI, need to check error code of 'git describe'.
if [ -z "$VERSION" ]; then
set +e
git describe --tags --abbrev=0 > /dev/null 2>&1
if [ "$?" != "0" ]; then
VERSION="master"
else
VERSION=$(git describe --tags --abbrev=0)
fi
set -e
fi
DATE=$(date -u -d "@${SOURCE_DATE_EPOCH:-$(date +%s)}" --iso-8601=seconds)
COMMIT=${COMMIT:-$(git rev-parse --verify HEAD)}
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
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/k8snetworkplumbingwg"
REPO_PATH="${ORG_PATH}/multus-cni.v3"
if [ ! -h gopath/src/${REPO_PATH} ]; then
mkdir -p gopath/src/${ORG_PATH}
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
fi
export GO15VENDOREXPERIMENT=1
export GOBIN=${PWD}/bin
export GOPATH=${PWD}/gopath
go 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
BUILD_ARGS=(-o ${DEST_DIR}/multus -tags no_openssl)
if [ -n "$MODMODE" ]; then
BUILD_ARGS+=(-mod "$MODMODE")
fi
echo "Building plugins"
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

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

View File

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

View File

@@ -1,22 +0,0 @@
# 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,21 +0,0 @@
# This dockerfile is specific to building Multus for OpenShift
FROM openshift/origin-release:golang-1.16 as builder
ADD . /usr/src/multus-cni
WORKDIR /usr/src/multus-cni
ENV GO111MODULE=off
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 /
LABEL io.k8s.display-name="Multus CNI" \
io.k8s.description="This is a component of OpenShift Container Platform and provides a meta CNI plugin." \
io.openshift.tags="openshift" \
maintainer="Doug Smith <dosmith@redhat.com>"
ENTRYPOINT ["/entrypoint.sh"]

View File

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

View File

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

View File

@@ -1,16 +0,0 @@
# 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 -f images/Dockerfile .
$ docker build -t dougbtv/multus .
```
---
@@ -15,7 +15,7 @@ $ docker build -t dougbtv/multus -f images/Dockerfile .
You may wish to deploy Multus as a daemonset, you can do so by starting with the example Daemonset shown here:
```
$ kubectl create -f ./deployments/multus-daemonset.yml
$ kubectl create -f ./images/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,7 +41,9 @@ 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
```
@@ -63,4 +65,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://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).
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).

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:v3.6
image: nfvpe/multus:latest
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.6
image: nfvpe/multus:v3.4
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.6
image: nfvpe/multus:v3.4.1
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"

View File

@@ -3,13 +3,6 @@
# 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"
@@ -18,11 +11,7 @@ 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=""
@@ -36,28 +25,24 @@ SKIP_BINARY_COPY=false
# Give help text for parameters.
function usage()
{
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 "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 ""
echo -e "./entrypoint.sh"
echo -e "\t-h --help"
echo -e "\t--cni-bin-dir=$CNI_BIN_DIR"
echo -e "\t--cni-conf-dir=$CNI_CONF_DIR"
echo -e "\t--cni-bin-dir=$CNI_BIN_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)"
@@ -83,25 +68,6 @@ 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}'`
@@ -114,9 +80,6 @@ while [ "$1" != "" ]; do
--cni-version)
CNI_VERSION=$VALUE
;;
--cni-bin-dir)
CNI_BIN_DIR=$VALUE
;;
--cni-conf-dir)
CNI_CONF_DIR=$VALUE
;;
@@ -126,21 +89,15 @@ 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
;;
@@ -240,10 +197,9 @@ 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_TEMP_KUBECONFIG
chmod ${KUBECONFIG_MODE:-600} $MULTUS_TEMP_KUBECONFIG
# Write the kubeconfig to a temp file first.
cat > $MULTUS_TEMP_KUBECONFIG <<EOF
touch $MULTUS_KUBECONFIG
chmod ${KUBECONFIG_MODE:-600} $MULTUS_KUBECONFIG
cat > $MULTUS_KUBECONFIG <<EOF
# Kubeconfig file for Multus CNI plugin.
apiVersion: v1
kind: Config
@@ -264,9 +220,6 @@ 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
@@ -281,15 +234,11 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
found_master=false
tries=0
while [ $found_master == false ]; do
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
MASTER_PLUGIN="$(ls $MULTUS_AUTOCONF_DIR | grep -E '\.conf(list)?$' | grep -Ev '00-multus\.conf' | head -1)"
if [ "$MASTER_PLUGIN" == "" ]; then
if [ $tries -lt 600 ]; then
if ! (($tries % 5)); then
log "Attempting to find master plugin configuration, attempt $tries"
log "Attemping to find master plugin configuration, attempt $tries"
fi
let "tries+=1"
sleep 1;
@@ -298,7 +247,6 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
exit 1;
fi
else
log "Using MASTER_PLUGIN: $MASTER_PLUGIN"
found_master=true
@@ -307,17 +255,6 @@ 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
@@ -332,7 +269,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
@@ -360,50 +297,20 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
if [ "$OVERRIDE_NETWORK_NAME" == "true" ]; then
MASTER_PLUGIN_NET_NAME="$(cat $MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN | \
python3 -c 'import json,sys;print(json.load(sys.stdin)["name"])')"
python -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
@@ -420,7 +327,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
@@ -467,9 +374,5 @@ if [ "$MULTUS_CLEANUP_CONFIG_ON_EXIT" == true ]; then
done
else
log "Entering sleep (success)..."
if tty -s; then
read
else
sleep infinity
fi
sleep infinity
fi

View File

@@ -143,7 +143,7 @@ data:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds
name: kube-multus-ds-amd64
namespace: kube-system
labels:
tier: node
@@ -163,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: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
image: nfvpe/multus:latest
command: ["/entrypoint.sh"]
args:
- "--cni-version=0.3.1"
@@ -193,14 +193,12 @@ 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:
@@ -217,3 +215,72 @@ 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
name: kube-multus-ds-amd64
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: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
image: nfvpe/multus:v3.4
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
@@ -179,3 +179,71 @@ 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

@@ -143,7 +143,7 @@ data:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds
name: kube-multus-ds-amd64
namespace: kube-system
labels:
tier: node
@@ -163,15 +163,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: ghcr.io/k8snetworkplumbingwg/multus-cni:stable
image: nfvpe/multus:v3.4.1
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
@@ -192,24 +192,73 @@ spec:
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
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds-ppc64le
namespace: kube-system
labels:
tier: node
app: multus
name: multus
spec:
selector:
matchLabels:
name: multus
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: multus
name: multus
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/arch: ppc64le
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
# ppc64le support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest-ppc64le
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
- "--cni-version=0.3.1"
resources:
requests:
cpu: "100m"
memory: "90Mi"
limits:
cpu: "100m"
memory: "90Mi"
securityContext:
privileged: true
volumeMounts:
- name: cni
mountPath: /host/etc/cni/net.d
- name: cnibin
mountPath: /host/opt/cni/bin
- name: multus-cfg
mountPath: /tmp/multus-conf
volumes:
- name: cni
hostPath:

View File

@@ -15,7 +15,6 @@
package k8sclient
import (
"context"
"encoding/json"
"fmt"
"net"
@@ -41,9 +40,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/k8snetworkplumbingwg/multus-cni.v3/pkg/kubeletclient"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
"gopkg.in/intel/multus-cni.v3/kubeletclient"
"gopkg.in/intel/multus-cni.v3/logging"
"gopkg.in/intel/multus-cni.v3/types"
)
const (
@@ -67,22 +66,22 @@ type ClientInfo struct {
// AddPod adds pod into kubernetes
func (c *ClientInfo) AddPod(pod *v1.Pod) (*v1.Pod, error) {
return c.Client.CoreV1().Pods(pod.ObjectMeta.Namespace).Create(context.TODO(), pod, metav1.CreateOptions{})
return c.Client.Core().Pods(pod.ObjectMeta.Namespace).Create(pod)
}
// GetPod gets pod from kubernetes
func (c *ClientInfo) GetPod(namespace, name string) (*v1.Pod, error) {
return c.Client.CoreV1().Pods(namespace).Get(context.TODO(), name, metav1.GetOptions{})
return c.Client.Core().Pods(namespace).Get(name, metav1.GetOptions{})
}
// DeletePod deletes a pod from kubernetes
func (c *ClientInfo) DeletePod(namespace, name string) error {
return c.Client.CoreV1().Pods(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{})
return c.Client.Core().Pods(namespace).Delete(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(context.TODO(), netattach, metav1.CreateOptions{})
return c.NetClient.NetworkAttachmentDefinitions(netattach.ObjectMeta.Namespace).Create(netattach)
}
// Eventf puts event into kubernetes events
@@ -92,7 +91,7 @@ func (c *ClientInfo) Eventf(object runtime.Object, eventtype, reason, messageFmt
}
}
func (e *NoK8sNetworkError) Error() string { return e.message }
func (e *NoK8sNetworkError) Error() string { return string(e.message) }
// SetNetworkStatus sets network status into Pod annotation
func SetNetworkStatus(client *ClientInfo, k8sArgs *types.K8sArgs, netStatus []nettypes.NetworkStatus, conf *types.NetConf) error {
@@ -114,16 +113,11 @@ 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 {
@@ -247,7 +241,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(context.TODO(), net.Name, metav1.GetOptions{})
customResource, err := client.NetClient.NetworkAttachmentDefinitions(net.Namespace).Get(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 {
@@ -264,7 +258,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)
}
@@ -290,7 +284,7 @@ func getKubernetesDelegate(client *ClientInfo, net *types.NetworkSelectionElemen
return nil, resourceMap, err
}
delegate, err := types.LoadDelegateNetConf(configBytes, net, deviceID, resourceName)
delegate, err := types.LoadDelegateNetConf(configBytes, net, deviceID)
if err != nil {
return nil, resourceMap, err
}
@@ -342,7 +336,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, resourceMap)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, conf.ConfDir, conf.NamespaceIsolation, resourceMap)
if err != nil {
if _, ok := err.(*NoK8sNetworkError); ok {
@@ -366,19 +360,13 @@ func TryLoadPodDelegates(pod *v1.Pod, conf *types.NetConf, clientInfo *ClientInf
}
if isGatewayConfigured == true {
err = types.CheckGatewayConfig(conf.Delegates)
if err != nil {
return 0, nil, err
}
types.CheckGatewayConfig(conf.Delegates)
}
return len(delegates), clientInfo, err
return len(delegates), clientInfo, nil
}
if _, ok := err.(*NoK8sNetworkError); ok {
return 0, clientInfo, nil
}
return 0, clientInfo, err
return 0, clientInfo, nil
}
// GetK8sClient gets client info from kubeconfig
@@ -460,9 +448,8 @@ 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, conf *types.NetConf, resourceMap map[string]*types.ResourceInfo) ([]*types.DelegateNetConf, error) {
logging.Debugf("GetNetworkDelegates: %v, %v, %v, %v, %v", k8sclient, pod, networks, conf, resourceMap)
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)
// Read all network objects referenced by 'networks'
var delegates []*types.DelegateNetConf
defaultNamespace := pod.ObjectMeta.Namespace
@@ -471,16 +458,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 conf.NamespaceIsolation {
if confnamespaceIsolation {
if defaultNamespace != net.Namespace {
// We allow exceptions based on the specified list of non-isolated namespaces (and/or "default" namespace, by default)
if !isValidNamespaceReference(net.Namespace, conf.NonIsolatedNamespaces) {
// There is an exception however, we always allow a reference to the default namespace.
if net.Namespace != "default" {
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, conf.ConfDir, pod, resourceMap)
delegate, updatedResourceMap, err := getKubernetesDelegate(k8sclient, net, confdir, pod, resourceMap)
if err != nil {
return nil, logging.Errorf("GetNetworkDelegates: failed getting the delegate: %v", err)
}
@@ -491,15 +478,6 @@ 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
@@ -516,14 +494,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 directory
// option3) search directry
fInfo, err := os.Stat(netname)
if err == nil {
if fInfo.IsDir() {
@@ -535,7 +513,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
}
@@ -619,16 +597,3 @@ 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/k8snetworkplumbingwg/multus-cni.v3/pkg/testing"
testutils "gopkg.in/intel/multus-cni.v3/testing"
"github.com/containernetworking/cni/pkg/skel"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
"gopkg.in/intel/multus-cni.v3/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,29 +54,10 @@ 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() {
@@ -85,7 +66,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves delegates from kubernetes using simple format annotation", func() {
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
fakePod := testutils.NewFakePod("testpod", "net1,net2", "")
net1 := `{
"name": "net1",
"type": "mynet",
@@ -102,6 +83,10 @@ 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())
@@ -119,9 +104,7 @@ var _ = Describe("k8sclient operations", func() {
Expect(err).NotTo(HaveOccurred())
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(2))
@@ -134,12 +117,15 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails when the network does not exist", func() {
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
fakePod := testutils.NewFakePod("testpod", "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)
@@ -153,15 +139,13 @@ var _ = Describe("k8sclient operations", func() {
Expect(err).NotTo(HaveOccurred())
networks, err := GetPodNetwork(pod)
Expect(err).NotTo(HaveOccurred())
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, 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(fakePodName, `[
fakePod := testutils.NewFakePod("testpod", `[
{"name":"net1"},
{
"name":"net2",
@@ -173,6 +157,9 @@ 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)
@@ -201,9 +188,7 @@ 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())
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(3))
@@ -216,7 +201,10 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails when the JSON format annotation is invalid", func() {
fakePod := testutils.NewFakePod(fakePodName, "[adsfasdfasdfasf]", "")
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),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -231,7 +219,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("can set the default-gateway on an additional interface", func() {
fakePod := testutils.NewFakePod(fakePodName, `[
fakePod := testutils.NewFakePod("testpod", `[
{"name":"net1"},
{
"name":"net2",
@@ -242,6 +230,9 @@ 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)
@@ -271,9 +262,7 @@ 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())
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(3))
@@ -286,7 +275,10 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves delegates from kubernetes using on-disk config files", func() {
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
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),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -314,9 +306,7 @@ 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())
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(2))
@@ -327,7 +317,10 @@ var _ = Describe("k8sclient operations", func() {
})
It("injects network name into minimal thick plugin CNI config", func() {
fakePod := testutils.NewFakePod(fakePodName, "net1", "")
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),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -340,9 +333,7 @@ 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())
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, nil)
Expect(err).NotTo(HaveOccurred())
Expect(len(delegates)).To(Equal(1))
@@ -351,7 +342,10 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails when on-disk config file is not valid", func() {
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
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),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -374,15 +368,13 @@ 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())
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, netConf, nil)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, tmpDir, false, 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(fakePodName, "", "")
fakePod := testutils.NewFakePod("testpod", "", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -392,6 +384,10 @@ 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())
@@ -410,7 +406,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves default networks from CRD", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "")
fakePod := testutils.NewFakePod("testpod", "", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -421,6 +417,10 @@ 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(fakePodName, "", "")
fakePod := testutils.NewFakePod("testpod", "", "")
// overwrite namespace
fakePod.ObjectMeta.Namespace = "kube-system"
conf := `{
@@ -457,6 +457,10 @@ 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())
@@ -478,7 +482,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves cluster network from file", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "")
fakePod := testutils.NewFakePod("testpod", "", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -489,6 +493,10 @@ 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())
@@ -513,7 +521,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("retrieves cluster network from path", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "")
fakePod := testutils.NewFakePod("testpod", "", "")
conf := fmt.Sprintf(`{
"name":"node-cni-network",
"type":"multus",
@@ -523,6 +531,10 @@ 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())
@@ -545,7 +557,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("Error in case of CRD not found", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "")
fakePod := testutils.NewFakePod("testpod", "", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -555,6 +567,10 @@ 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())
@@ -567,7 +583,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("overwrite cluster network when Pod annotation is set", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
fakePod := testutils.NewFakePod("testpod", "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -578,6 +594,10 @@ 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())
@@ -605,7 +625,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails with bad confdir", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
fakePod := testutils.NewFakePod("testpod", "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -616,6 +636,10 @@ 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())
@@ -637,7 +661,7 @@ var _ = Describe("k8sclient operations", func() {
It("overwrite multus config when Pod annotation is set", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
fakePod := testutils.NewFakePod("testpod", "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -652,6 +676,10 @@ 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())
@@ -670,7 +698,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails with no kubeclient and invalid kubeconfig", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
fakePod := testutils.NewFakePod("testpod", "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -685,6 +713,10 @@ 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())
@@ -700,7 +732,7 @@ var _ = Describe("k8sclient operations", func() {
})
It("fails with no kubeclient and no kubeconfig", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
fakePod := testutils.NewFakePod("testpod", "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -715,6 +747,10 @@ 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())
@@ -760,7 +796,7 @@ users:
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb2dJQkFBS0NBUUVBMUxyZmUxaXNQaTNDYng4eHRDWG1QTlE2L0xXYVVEa1ZRNVBkdGtQK3dVYmdJUFJ6CmNtRzNEcnUya0kvamsxMGQzaUgzOVZlZDlHVjczbnZyVm5Id0pzZEF5QzVmYlRTQVBUVVNJOW90MVFGbENRUGcKQ2JLbjBRbmxUeUhXMmNnTis2Q3BWK0U4dFpWajJ2cDZoTDlmR0s2bUJCL2I4VFN3RmYxRmtnWC9hTGN1emZDaApmVFNDOWlRTk15cEFjamZadEkzWiszbHVzSVB6TVpBNTZPZXNodzlnRkJHUkw3RzZHWmZKcG9OaGVxTDlmandUClRkNURlbVZXcUxUR05ZNWhhV3hicy9VbThuM0QvdlV1QkVPYTYxQ2cvcGljUkU3Um14SmFJRWJiQlNTV1dkSDMKWDlrem5RdHJrUXloL28xZnpSV1pxeWFCY3hxR1FWN0JSSzFtSndJREFRQUJBb0lCQUJ0bjA4QzFSTU5oNjhtYgpFREV3TE1BcmEwb0JMMWNrYzN2WVFkam9XNXFVd2UwYzhQNk1YaVAweE9sTTBEbTg1a3NtdnlZSldwMFFzZXVRCnRWbldwZVNwQ015QlJPUHh2bytrRmFrdXczYk1qaktpSUN1L3EyVC96RjNzY3h4dGJIZTlVL094WGJ2YStobE0KNlpuT2ViYlpVU1A0NHNIcFVzSVNkZk1BK00ySmg1UFJibGZWaUFEY1hxNFR5RU1JaStzRkhOcFIrdmdWZzRFawp4RmFVaS83V0E2YUxWVzBUTzREdjMwbTJ0TVczWXN1bk1LTU0xOTNyUEZrU0dEdFpheWV2Z0JDeURXaFhOTEo2Clh1cTNxSUg4bFE2bzRBUjMvcDc1ZW9hOCtrVzVmT3o2UWF3WnpPYlBENkRCQlVOYVM1YklXaVV1dmx5L0JlM20KZnlxK3NRRUNnWUVBMW84R3l6ODk2bFhwdU1yVXVsb2orbGp5U0FaNkpLOCsyaFMvQnpyREx6NlpvY3FzKzg3awpVUkwzKy9LL1pja2pIMVVDeXROVVZ6Q3RKaG4zZmdLS2dpQWhsU0pNRzhqc05sbEkydFZSazNZZ1RCcUg2bXZxCit3citsTUxoUDZxbWFObUx3QXljY2lEanpMdXlRdjhVOFhKazJOdVFsQlFwbkt2eWJIRGdxSUVDZ1lFQS9kRnMKazNlYmRNNFAxV2psYXJoRTV5blpuRmdQbDg1L2Vudk4rQ1oxcStlMGxYendaUGswdWdJUWozYyt2UEpLWlh0OApLWk1HQjM0N2VLNlFIL3J1a2xRWXlLOStHeUV1YnRJQUZ2NWFrYXZxV1haR1p5ZC9QdDR1V09adXMrd3BnSG00CkxFY0lzZElsYkpFY2RJTzJyb3FaY0VNY3FEbGtXcTdwQWxqU2VxY0NnWUJYdUQ0RTFxUlByRFJVRXNrS0wxUksKUkJjNkR6dmN4N0VncEI2OXErNms0Q2tibHF0R2YvMmtqK2JISVNYVFRYcUlrczhEY1ljbjVvVEQ4UlhZZE4xLworZmNBNi9iRjNVMkZvdGRBY0xwYldZNDJ6eG9HWTN5OGluQXZEY1hkcTcxQlhML2dFc2ZiZVVycEowdm9URFdaCnlUVWwzQTZ1RzlndmI3VTdWS0xsQVFLQmdBTmNscmVOU2YzT0ROK2l1QWNsMGFQT0poZXdBdVRiMDB4bi8xNWUKQkFqMjFLbDJNaWprTkJLU25HMktBc2ExM3M1aFNFKzBwc3ZLbkRjSStOZXpseDFSQjlNQW9BYno5WTE2TW80YgphRSt0bXpqOEhBcVp0MUc1MTV0TjBnR0lDelNzYUFnT0dNdGlJU1RDOTBHRHpST2F1bFdHVGdiY1c3dm52U1pPCnp0clpBb0dBWmtIRWV5em16Z2cxR3dtTzN3bmljSHRMR1BQRFhiSW53NTdsdkIrY3lyd0FrVEs1MlFScTM0VkMKRDhnQWFwMTU2OWlWUER3YlgrNkpBQk1WQ2tNUmdxMjdHanUzN0pVY2Fib2g1YzJQeTBYNUlhUG8rek1hWHgvQwpqbjUvUW5YandjU1MrRU5hL1lXVWcxWEVjQjJYdEM0UExCdGUycitrUTVLbFNOREcxSTQ9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==`
kubeletconf.Write([]byte(kubeletconfDef))
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
fakePod := testutils.NewFakePod("testpod", "", "net1")
conf := fmt.Sprintf(`{
"name":"node-cni-network",
"type":"multus",
@@ -775,6 +811,10 @@ 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())
@@ -789,7 +829,7 @@ users:
})
It("Errors when namespace isolation is violated", func() {
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -802,6 +842,7 @@ users:
"namespaceIsolation": true
}`
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
net1 := `{
@@ -810,6 +851,10 @@ 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())
@@ -822,60 +867,12 @@ users:
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)
_, err = GetNetworkDelegates(clientInfo, pod, networks, tmpDir, netConf.NamespaceIsolation, 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"}
@@ -885,7 +882,7 @@ users:
Context("getDefaultNetDelegateCRD", func() {
It("fails when netConf contains bad confDir", func() {
fakePod := testutils.NewFakePod(fakePodName, "", "net1")
fakePod := testutils.NewFakePod("testpod", "", "net1")
conf := `{
"name":"node-cni-network",
"type":"multus",
@@ -896,6 +893,10 @@ 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())
@@ -915,9 +916,9 @@ users:
Context("GetK8sArgs", func() {
It("fails when provided with bad format", func() {
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),
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),
}
// using colon instead of equals sign makes an invalid CmdArgs
@@ -928,7 +929,7 @@ users:
Context("getKubernetesDelegate", func() {
It("failed to get a ResourceClient instance", func() {
fakePod := testutils.NewFakePod(fakePodName, "net1,net2", "")
fakePod := testutils.NewFakePod("testpod", "net1,net2", "")
net1 := `{
"name": "net1",
"type": "mynet",
@@ -944,6 +945,9 @@ 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)
@@ -962,16 +966,17 @@ users:
networks, err := GetPodNetwork(fakePod)
Expect(err).NotTo(HaveOccurred())
netConf, err := types.LoadNetConf([]byte(genericConf))
netConf.ConfDir = tmpDir
_, err = GetNetworkDelegates(clientInfo, fakePod, networks, netConf, nil)
_, err = GetNetworkDelegates(clientInfo, fakePod, networks, tmpDir, false, nil)
Expect(err).To(HaveOccurred())
})
})
Context("parsePodNetworkObjectName", func() {
It("fails to get podnetwork given bad annotation values", func() {
fakePod := testutils.NewFakePod(fakePodName, "net1", "")
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),
}
clientInfo := NewFakeClientInfo()
_, err := clientInfo.AddPod(fakePod)
@@ -1004,7 +1009,7 @@ users:
Context("setPodNetworkAnnotation", func() {
It("Sets pod network annotations without error", func() {
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
net1 := `{
"name": "net1",
@@ -1012,6 +1017,10 @@ 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())
@@ -1085,6 +1094,10 @@ 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())
@@ -1092,9 +1105,6 @@ 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())
@@ -1121,7 +1131,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(fakePodName, "kube-system/net1", "")
// fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
// net1 := `{
// "name": "net1",
@@ -1129,6 +1139,10 @@ 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())
@@ -1148,7 +1162,7 @@ users:
})
Context("SetNetworkStatus", func() {
It("Sets network status without error when pod UIDs match", func() {
It("Sets network status without error", func() {
result := &types020.Result{
CNIVersion: "0.2.0",
IP4: &types020.IPConfig{
@@ -1170,16 +1184,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, nil)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
@@ -1190,6 +1204,10 @@ 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())
@@ -1203,123 +1221,6 @@ 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
@@ -1365,16 +1266,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, nil)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
@@ -1385,6 +1286,10 @@ 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())
@@ -1421,16 +1326,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, nil)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
@@ -1441,6 +1346,10 @@ 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())
@@ -1476,16 +1385,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, nil)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
Expect(err).NotTo(HaveOccurred())
netstatus := []nettypes.NetworkStatus{*delegateNetStatus}
fakePod := testutils.NewFakePod(fakePodName, "kube-system/net1", "")
fakePod := testutils.NewFakePod("testpod", "kube-system/net1", "")
netConf, err := types.LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
@@ -1496,6 +1405,10 @@ 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())

View File

@@ -1,56 +1,57 @@
package kubeletclient
import (
"net/url"
"os"
"path/filepath"
"time"
"golang.org/x/net/context"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/checkpoint"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
"gopkg.in/intel/multus-cni.v3/checkpoint"
"gopkg.in/intel/multus-cni.v3/logging"
"gopkg.in/intel/multus-cni.v3/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
defaultPodResourcesPath = "/var/lib/kubelet/pod-resources"
)
var (
kubeletSocket string
defaultPodResourcesPath = "/var/lib/kubelet/pod-resources"
)
// GetResourceClient returns an instance of ResourceClient interface initialized with Pod resource information
func GetResourceClient(kubeletSocket string) (types.ResourceClient, error) {
if kubeletSocket == "" {
kubeletSocket, _ = util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
}
func GetResourceClient() (types.ResourceClient, error) {
// If Kubelet resource API endpoint exist use that by default
// Or else fallback with checkpoint file
if hasKubeletAPIEndpoint(kubeletSocket) {
if hasKubeletAPIEndpoint() {
logging.Debugf("GetResourceClient: using Kubelet resource API endpoint")
return getKubeletClient(kubeletSocket)
return getKubeletClient()
}
logging.Debugf("GetResourceClient: using Kubelet device plugin checkpoint")
return checkpoint.GetCheckpoint()
}
func getKubeletClient(kubeletSocket string) (types.ResourceClient, error) {
func getKubeletClient() (types.ResourceClient, error) {
newClient := &kubeletClient{}
if kubeletSocket == "" {
kubeletSocket, _ = util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
kubeletSocket = util.LocalEndpoint(defaultPodResourcesPath, podresources.Socket)
}
client, conn, err := podresources.GetV1Client(kubeletSocket, 10*time.Second, defaultPodResourcesMaxSize)
client, conn, err := podresources.GetClient(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 getting pod resources from client: %v\n", err)
return nil, logging.Errorf("getKubeletClient: error ge tting pod resources from client: %v\n", err)
}
return newClient, nil
@@ -101,13 +102,10 @@ func (rc *kubeletClient) GetPodResourceMap(pod *v1.Pod) (map[string]*types.Resou
return resourceMap, nil
}
func hasKubeletAPIEndpoint(endpoint string) bool {
u, err := url.Parse(endpoint)
if err != nil {
return false
}
func hasKubeletAPIEndpoint() bool {
// Check for kubelet resource API socket file
if _, err := os.Stat(u.Path); err != nil {
kubeletAPISocket := filepath.Join(defaultPodResourcesPath, defaultKubeletSocketFile)
if _, err := os.Stat(kubeletAPISocket); 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/k8snetworkplumbingwg/multus-cni.v3/pkg/types"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
mtypes "gopkg.in/intel/multus-cni.v3/types"
podresourcesapi "k8s.io/kubernetes/pkg/kubelet/apis/podresources/v1alpha1"
)
var (
@@ -29,13 +29,6 @@ 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"
@@ -70,28 +63,25 @@ 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
}
testingPodResourcesPath := filepath.Join(tempSocketDir, defaultPodResourcesPath)
defaultPodResourcesPath = filepath.Join(tempSocketDir, defaultPodResourcesPath)
if err := os.MkdirAll(testingPodResourcesPath, os.ModeDir); err != nil {
if err := os.MkdirAll(defaultPodResourcesPath, os.ModeDir); err != nil {
return err
}
socketDir = testingPodResourcesPath
socketDir = defaultPodResourcesPath
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 err
return nil
}
go fakeServer.server.Serve(lis)
return nil
@@ -101,8 +91,10 @@ func tearDown(path string) error {
if fakeServer != nil {
fakeServer.server.Stop()
}
err := os.RemoveAll(path)
return err
if err := os.RemoveAll(path); err != nil {
return err
}
return nil
}
var _ = BeforeSuite(func() {
@@ -119,12 +111,14 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
Context("GetResourceClient()", func() {
It("should return no error", func() {
_, err := GetResourceClient(testKubeletSocket)
kubeletSocket = socketName
_, err := GetResourceClient()
Expect(err).NotTo(HaveOccurred())
})
It("should fail with missing file", func() {
_, err := GetResourceClient("sampleSocketString")
kubeletSocket = "sampleSocketString"
_, err := GetResourceClient()
Expect(err).To(HaveOccurred())
})
})
@@ -146,11 +140,41 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
},
},
}
client, err := getKubeletClient(testKubeletSocket)
kubeletSocket = socketName
client, err := getKubeletClient()
Expect(err).NotTo(HaveOccurred())
outputRMap := map[string]*mtypes.ResourceInfo{
"resource": {DeviceIDs: []string{"dev0", "dev1"}},
"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"}},
}
resourceMap, err := client.GetPodResourceMap(fakePod)
Expect(err).NotTo(HaveOccurred())
@@ -159,7 +183,8 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
})
It("should return an error with garbage socket value", func() {
_, err := getKubeletClient("/badfilepath!?//")
kubeletSocket = "/badfilepath!?//"
_, err := getKubeletClient()
Expect(err).To(HaveOccurred())
})
})
@@ -174,7 +199,8 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
UID: podUID,
},
}
client, err := getKubeletClient(testKubeletSocket)
kubeletSocket = socketName
client, err := getKubeletClient()
Expect(err).NotTo(HaveOccurred())
_, err = client.GetPodResourceMap(fakePod)
Expect(err).To(HaveOccurred())
@@ -191,7 +217,8 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
UID: podUID,
},
}
client, err := getKubeletClient(testKubeletSocket)
kubeletSocket = socketName
client, err := getKubeletClient()
Expect(err).NotTo(HaveOccurred())
_, err = client.GetPodResourceMap(fakePod)
Expect(err).To(HaveOccurred())
@@ -209,7 +236,8 @@ var _ = Describe("Kubelet resource endpoint data read operations", func() {
},
}
client, err := getKubeletClient(testKubeletSocket)
kubeletSocket = socketName
client, err := getKubeletClient()
Expect(err).NotTo(HaveOccurred())
emptyRMap := map[string]*mtypes.ResourceInfo{}

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,42 +41,9 @@ 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:
@@ -127,7 +94,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 unrecoverable error
// Panicf prints logging plus stack trace. This should be used only for unrecoverble error
func Panicf(format string, a ...interface{}) {
printf(PanicLevel, format, a...)
printf(PanicLevel, "========= Stack trace output ========")
@@ -174,8 +141,13 @@ func SetLogFile(filename string) {
return
}
logger.Filename = filename
loggingW = logger
loggingW = &lumberjack.Logger{
Filename: filename,
MaxSize: 100, // megabytes
MaxBackups: 5,
MaxAge: 5, // days
Compress: true,
}
}
@@ -183,5 +155,4 @@ func init() {
loggingStderr = true
loggingW = nil
loggingLevel = PanicLevel
logger = &lumberjack.Logger{}
}

View File

@@ -15,8 +15,6 @@
package logging
import (
testutils "gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/testing"
"gopkg.in/natefinch/lumberjack.v2"
"testing"
. "github.com/onsi/ginkgo"
@@ -44,13 +42,13 @@ var _ = Describe("logging operations", func() {
It("Check file setter with empty", func() {
SetLogFile("/tmp/foobar.logging")
Expect(loggingW).NotTo(Equal(nil))
// check file existence
// check file existance
})
It("Check file setter with bad filepath", func() {
SetLogFile("/invalid/filepath")
Expect(loggingW).NotTo(Equal(nil))
// check file existence
// check file existance
})
It("Check loglevel setter", func() {
@@ -81,54 +79,4 @@ 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))
})
})

View File

@@ -12,11 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package multus
// 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
import (
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"net"
@@ -30,39 +35,28 @@ 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"
)
const (
shortPollDuration = 250 * time.Millisecond
shortPollTimeout = 2500 * time.Millisecond
)
var version = "master@git"
var commit = "unknown commit"
var date = "unknown date"
var (
version = "master@git"
commit = "unknown commit"
date = "unknown date"
)
var pollDuration = 1000 * time.Millisecond
var pollTimeout = 45 * time.Second
var (
pollDuration = 1000 * time.Millisecond
pollTimeout = 45 * time.Second
)
// PrintVersionString ...
func PrintVersionString() string {
func printVersionString() string {
return fmt.Sprintf("multus-cni version:%s, commit:%s, date:%s",
version, commit, date)
}
@@ -106,20 +100,6 @@ 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)
@@ -166,12 +146,12 @@ func validateIfName(nsname string, ifname string) error {
return err
}
func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.NetConf, exec invoke.Exec) (cnitypes.Result, error) {
logging.Debugf("confAdd: %v, %s", rt, string(rawNetconf))
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)
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
conf, err := libcni.ConfFromBytes(rawNetconf)
if err != nil {
@@ -186,12 +166,12 @@ func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.Net
return result, nil
}
func confCheck(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.NetConf, exec invoke.Exec) error {
logging.Debugf("confCheck: %v, %s", rt, string(rawNetconf))
func confCheck(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("confCheck: %v, %s, %s", rt, string(rawNetconf), binDir)
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
conf, err := libcni.ConfFromBytes(rawNetconf)
if err != nil {
@@ -200,18 +180,18 @@ func confCheck(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.N
err = cniNet.CheckNetwork(context.Background(), conf, rt)
if err != nil {
return logging.Errorf("error in getting result from CheckNetwork: %v", err)
return logging.Errorf("error in getting result from DelNetwork: %v", err)
}
return err
}
func confDel(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.NetConf, exec invoke.Exec) error {
logging.Debugf("confDel: %v, %s", rt, string(rawNetconf))
func confDel(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistDel: %v, %s, %s", rt, string(rawNetconf), binDir)
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
conf, err := libcni.ConfFromBytes(rawNetconf)
if err != nil {
@@ -226,12 +206,12 @@ func confDel(rt *libcni.RuntimeConf, rawNetconf []byte, multusNetconf *types.Net
return err
}
func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *types.NetConf, exec invoke.Exec) (cnitypes.Result, error) {
logging.Debugf("conflistAdd: %v, %s", rt, string(rawnetconflist))
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)
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
@@ -246,12 +226,12 @@ func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *t
return result, nil
}
func conflistCheck(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *types.NetConf, exec invoke.Exec) error {
logging.Debugf("conflistCheck: %v, %s", rt, string(rawnetconflist))
func conflistCheck(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistCheck: %v, %s, %s", rt, string(rawnetconflist), binDir)
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
@@ -266,12 +246,12 @@ func conflistCheck(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf
return err
}
func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *types.NetConf, exec invoke.Exec) error {
logging.Debugf("conflistDel: %v, %s", rt, string(rawnetconflist))
func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistDel: %v, %s, %s", rt, string(rawnetconflist), binDir)
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{multusNetconf.BinDir}, binDirs...)
cniNet := libcni.NewCNIConfigWithCacheDir(binDirs, multusNetconf.CNIDir, exec)
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
@@ -286,15 +266,23 @@ func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, multusNetconf *t
return err
}
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)
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")
}
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)
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)
}
// 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)
@@ -302,7 +290,8 @@ func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, dele
return nil, logging.Errorf("delegateAdd: failed to parse mac address %q", delegate.MacRequest)
}
logging.Debugf("delegateAdd: set MAC address %q to %q", delegate.MacRequest, rt.IfName)
cniArgs = fmt.Sprintf("%s;MAC=%s", cniArgs, delegate.MacRequest)
logging.Debugf("delegateAdd: set MAC address %q to %q", delegate.MacRequest, ifName)
rt.Args = append(rt.Args, [2]string{"MAC", delegate.MacRequest})
}
@@ -320,7 +309,8 @@ func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, dele
}
ips := strings.Join(delegate.IPRequest, ",")
logging.Debugf("delegateAdd: set IP address %q to %q", ips, rt.IfName)
cniArgs = fmt.Sprintf("%s;IP=%s", cniArgs, ips)
logging.Debugf("delegateAdd: set IP address %q to %q", ips, ifName)
rt.Args = append(rt.Args, [2]string{"IP", ips})
}
}
@@ -328,12 +318,12 @@ func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, dele
var result cnitypes.Result
var err error
if delegate.ConfListPlugin {
result, err = conflistAdd(rt, delegate.Bytes, multusNetconf, exec)
result, err = conflistAdd(rt, delegate.Bytes, binDir, exec)
if err != nil {
return nil, err
}
} else {
result, err = confAdd(rt, delegate.Bytes, multusNetconf, exec)
result, err = confAdd(rt, delegate.Bytes, binDir, exec)
if err != nil {
return nil, err
}
@@ -374,15 +364,18 @@ func delegateAdd(exec invoke.Exec, kubeClient *k8s.ClientInfo, pod *v1.Pod, dele
kubeClient.Eventf(pod, v1.EventTypeNormal, "AddedInterface", "Add %s %v", rt.IfName, ips)
}
} else {
// for further debug https://github.com/k8snetworkplumbingwg/multus-cni/issues/481
// for further debug https://github.com/intel/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, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, multusNetconf *types.NetConf) error {
logging.Debugf("delegateCheck: %v, %v, %v", exec, delegateConf, rt)
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")
}
if logging.GetLoggingLevel() >= logging.VerboseLevel {
var cniConfName string
@@ -396,12 +389,12 @@ func delegateCheck(exec invoke.Exec, delegateConf *types.DelegateNetConf, rt *li
var err error
if delegateConf.ConfListPlugin {
err = conflistCheck(rt, delegateConf.Bytes, multusNetconf, exec)
err = conflistCheck(rt, delegateConf.Bytes, binDir, exec)
if err != nil {
return logging.Errorf("delegateCheck: error invoking ConflistCheck - %q: %v", delegateConf.ConfList.Name, err)
}
} else {
err = confCheck(rt, delegateConf.Bytes, multusNetconf, exec)
err = confCheck(rt, delegateConf.Bytes, binDir, exec)
if err != nil {
return logging.Errorf("delegateCheck: error invoking DelegateCheck - %q: %v", delegateConf.Conf.Type, err)
}
@@ -410,8 +403,11 @@ func delegateCheck(exec invoke.Exec, delegateConf *types.DelegateNetConf, rt *li
return err
}
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)
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")
}
if logging.GetLoggingLevel() >= logging.VerboseLevel {
var confName string
@@ -429,12 +425,12 @@ func delegateDel(exec invoke.Exec, pod *v1.Pod, delegateConf *types.DelegateNetC
var err error
if delegateConf.ConfListPlugin {
err = conflistDel(rt, delegateConf.Bytes, multusNetconf, exec)
err = conflistDel(rt, delegateConf.Bytes, binDir, exec)
if err != nil {
return logging.Errorf("delegateDel: error invoking ConflistDel - %q: %v", delegateConf.ConfList.Name, err)
}
} else {
err = confDel(rt, delegateConf.Bytes, multusNetconf, exec)
err = confDel(rt, delegateConf.Bytes, binDir, exec)
if err != nil {
return logging.Errorf("delegateDel: error invoking DelegateDel - %q: %v", delegateConf.Conf.Type, err)
}
@@ -443,28 +439,20 @@ func delegateDel(exec invoke.Exec, pod *v1.Pod, delegateConf *types.DelegateNetC
return err
}
// 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)
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")
}
var errorstrings []string
for idx := lastIdx; idx >= 0; idx-- {
ifName := getIfname(delegates[idx], args.IfName, idx)
rt, cniDeviceInfoPath := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, netRt, delegates[idx])
ifName := getIfname(delegates[idx], argIfname, idx)
rt.IfName = ifName
// Attempt to delete all but do not error out, instead, collect all errors.
if err := delegateDel(exec, pod, delegates[idx], rt, multusNetconf); err != nil {
if err := delegateDel(exec, pod, ifName, delegates[idx], rt, binDir); 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.
@@ -478,7 +466,7 @@ func delPlugins(exec invoke.Exec, pod *v1.Pod, args *skel.CmdArgs, k8sArgs *type
func cmdErr(k8sArgs *types.K8sArgs, format string, args ...interface{}) error {
prefix := "Multus: "
if k8sArgs != nil {
prefix += fmt.Sprintf("[%s/%s/%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME, k8sArgs.K8S_POD_UID)
prefix += fmt.Sprintf("[%s/%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME)
}
return logging.Errorf(prefix+format, args...)
}
@@ -486,72 +474,14 @@ 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:%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME, k8sArgs.K8S_POD_UID, confName)
msg += fmt.Sprintf("[%s/%s:%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME, confName)
}
return logging.Errorf(msg+format, args...)
}
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) {
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)
}
@@ -572,13 +502,32 @@ func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c
return err == nil, nil
})
if err != nil {
return nil, cmdErr(k8sArgs, "have you checked that your default network is ready? still waiting for readinessindicatorfile @ %v. pollimmediate error: %v", n.ReadinessIndicatorFile, err)
return nil, cmdErr(k8sArgs, "PollImmediate error waiting for ReadinessIndicatorFile: %v", err)
}
}
pod, err := getPod(kubeClient, k8sArgs, false)
if err != nil {
return nil, 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)
}
}
}
// resourceMap holds Pod device allocation information; only initizized if CRD contains 'resourceName' annotation.
@@ -606,89 +555,52 @@ 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)
}
}
// 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)
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)
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, k8sArgs, n.Delegates, idx, n.RuntimeConfig, n)
_ = delPlugins(exec, nil, args.IfName, n.Delegates, idx, rt, n.BinDir)
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
deleteV4gateway := false
deleteV6gateway := false
deletegateway := false
adddefaultgateway := false
if delegate.IsFilterV4Gateway {
deleteV4gateway = true
logging.Debugf("Marked interface %v for v4 gateway deletion", ifName)
if delegate.IsFilterGateway {
deletegateway = true
logging.Debugf("Marked interface %v for 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 {
deleteV4gateway = true
if delegate.GatewayRequest != nil {
deletegateway = true
adddefaultgateway = true
logging.Debugf("Detected gateway override on interface %v to %v", ifName, delegate.GatewayRequest)
}
}
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)
}
}
// Remove gateway if `default-route` network selection is specified
if deleteV4gateway || deleteV6gateway {
err = netutils.DeleteDefaultGW(args, ifName)
if deletegateway {
tmpResult, err = netutils.DeleteDefaultGW(args, ifName, &tmpResult)
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
// Here we'll set the default gateway
if adddefaultgateway {
err = netutils.SetDefaultGW(args, ifName, *delegate.GatewayRequest)
tmpResult, err = netutils.SetDefaultGW(args, ifName, delegate.GatewayRequest, &tmpResult)
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
@@ -696,39 +608,24 @@ func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c
result = tmpResult
}
// 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
//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, devinfo)
delegateNetStatus, err := nadutils.CreateNetworkStatus(tmpResult, delegate.Name, delegate.MasterPlugin)
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)
}
}
@@ -737,10 +634,9 @@ func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (c
return result, nil
}
// CmdCheck ...
func CmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error {
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
}
@@ -753,8 +649,9 @@ func CmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo)
for idx, delegate := range in.Delegates {
ifName := getIfname(delegate, args.IfName, idx)
rt, _ := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, in.RuntimeConfig, delegate)
err = delegateCheck(exec, delegate, rt, in)
runtimeConfig := types.MergeCNIRuntimeConfig(in.RuntimeConfig, delegate)
rt := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, runtimeConfig)
err = delegateCheck(exec, ifName, delegate, rt, in.BinDir)
if err != nil {
return err
}
@@ -763,26 +660,25 @@ func CmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo)
return nil
}
// CmdDel ...
func CmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error {
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
}
skipStatusUpdate := false
netnsfound := true
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 {
logging.Debugf("CmdDel: WARNING netns may not exist, netns: %s, err: %s", args.Netns, err)
netnsfound = false
logging.Debugf("cmdDel: WARNING netns may not exist, netns: %s, err: %s", args.Netns, err)
} else {
logging.Debugf("CmdDel: WARNING failed to open netns %q: %v", netns, err)
return cmdErr(nil, "failed to open netns %q: %v", netns, err)
}
}
@@ -810,12 +706,31 @@ func CmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er
return cmdErr(nil, "error getting k8s client: %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
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)
}
}
}
// Read the cache to get delegates json for the pod
@@ -850,7 +765,6 @@ func CmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er
}
} 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)
}
@@ -869,16 +783,12 @@ 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 !skipStatusUpdate {
if netnsfound {
if !types.CheckSystemNamespaces(string(k8sArgs.K8S_POD_NAMESPACE), in.SystemNamespaces) {
err := k8s.SetNetworkStatus(kubeClient, k8sArgs, nil, in)
if err != nil {
@@ -887,9 +797,40 @@ func CmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) er
}
}
} else {
logging.Debugf("WARNING: Unset SetNetworkStatus skipped")
logging.Debugf("WARNING: Unset SetNetworkStatus skipped due to netns not found.")
}
}
return delPlugins(exec, pod, args, k8sArgs, in.Delegates, len(in.Delegates)-1, in.RuntimeConfig, in)
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")
}

File diff suppressed because it is too large Load Diff

119
netutils/netutils.go Normal file
View File

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

@@ -1,17 +0,0 @@
// Copyright (c) 2021 Multus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package checkpoint is the package that contains the libraries that manipulates kubelet's
// checkpoint API
package checkpoint

View File

@@ -1,17 +0,0 @@
// Copyright (c) 2021 Multus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package config is the package that contains multus cni config related
// utilities.
package config

View File

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

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

View File

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

View File

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

View File

@@ -1,16 +0,0 @@
// Copyright (c) 2021 Multus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package k8sclient is the package that contains the Kubernetes client libraries.
package k8sclient

View File

@@ -1,17 +0,0 @@
// Copyright (c) 2021 Multus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package kubeletclient is the package that contains the kubelet's libraries that
// controls podresource API in kubelet
package kubeletclient

View File

@@ -1,16 +0,0 @@
// Copyright (c) 2021 Multus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package logging is the package that contains logging library.
package logging

View File

@@ -1,17 +0,0 @@
// Copyright (c) 2021 Multus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package multus is the package that contains main multus function, which
// manipulates CNI request for delegate plugins.
package multus

View File

@@ -1,16 +0,0 @@
// Copyright (c) 2021 Multus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package netutils is the package that contains network related utilities.
package netutils

View File

@@ -1,405 +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 (
"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

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