136 Commits

Author SHA1 Message Date
Yun Zhou
dc9315f125 Provide the option to not to log via STDERR
Today, Multus will always log via STDERR, and these logs will then
logged by the Kubelet. If we also choose to have Multus log to a file by
setting the LogFile option in the CNI configuration, the same logs will
be logged twice.

This commit provide the option to disable logging to STDERR.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Call it from delPlugins for each delegate

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

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

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

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

Fixes #554

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

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

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

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

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

Fixes intel#544

Signed-off-by: <Clement.Nussbaumer@Swisscom.com>
2020-08-05 10:29:05 +02:00
Doug Smith
e15f97860f Merge pull request #541 from s1061123/fix/crd
Fix CRD yaml file for right explan information
2020-07-28 10:39:13 -04:00
Tomofumi Hayashi
78669323d2 Fix CRD yaml file for right explan information 2020-07-28 11:56:08 +09:00
Doug Smith
c85b79f5ff Merge pull request #536 from s1061123/fix/update-vendor
Update vendors
2020-07-21 20:17:50 -04:00
Tomofumi Hayashi
801e1e8940 Update vendors 2020-07-22 09:15:24 +09:00
Doug Smith
41fefbb001 Merge pull request #522 from s1061123/fix/del-error-fix
Fix/del error fix
2020-07-21 17:13:33 -04:00
Tomofumi Hayashi
4eb6ae1553 Fix error handling on cmdDel
Fix #519
2020-07-21 14:52:07 +09:00
Doug Smith
b5211cb0d2 Merge pull request #534 from bmwiedemann/date
Allow to override build date with SOURCE_DATE_EPOCH
2020-07-09 09:52:28 -04:00
Bernhard M. Wiedemann
29df24fa6c Allow to override build date with SOURCE_DATE_EPOCH
in order to make builds reproducible.
See https://reproducible-builds.org/ for why this is good
and https://reproducible-builds.org/specs/source-date-epoch/
for the definition of this variable.

This date call only works with GNU date.

Also use UTC to be independent of timezone.
2020-07-08 05:08:11 +02:00
Doug Smith
e4a8c9f639 Merge pull request #530 from rktidwell/master
build: Enable -mod build flag to be toggled via environment variable
2020-07-06 08:49:58 -04:00
Adrian Chiris
2796202d8c Add infinibandGUID runtime config to delegate netconf
This commit extend the various network conf methods
to translate `infiniband-guid` network attachment configuration
to `infinibandGUID` CNI runtime config.

- Update relevant methods in types package
- Update unit tests
2020-06-26 14:55:21 +09:00
Adrian Chiris
46be700cb5 Struct updates
- Add `infinibandGUID` CNI runtime config
- Add `infiniband-guid` Network attachment attribute
- Reflect infiniband guid request in Delegate network config
2020-06-26 14:55:21 +09:00
Ryan Tidwell
a167d54608 build: Enable -mod build flag to be toggled via environment variable
This change will enable builds to be performed with the option of
passing the -mod flag to 'go build'. This can be done by optionally
setting the MODMODE environment variable prior to building.

For example, toggling the -mod setting for the build can be
accomplished as follows:

```
$ MODMODE=vendor ./build
```

Signed-off-by: Ryan Tidwell <rtidwell@suse.com>
2020-06-24 16:11:19 -05:00
Shahar Klein
ecb79330c0 Add support for log rotation
When using debug level the log files tends to fill up quickly.
Add support for log rotation using lumberjack, simple and easy to use
log rotation module.

Signed-off-by: Shahar Klein <sklein@nvidia.com>
2020-06-17 15:31:19 +09:00
Doug Smith
3882e405ef Merge pull request #515 from s1061123/fix/namespace-in-status
Fix network status name/namespace to compliant with multi-net-spec
2020-06-09 15:08:23 -04:00
Doug Smith
5e15ff18f8 Merge pull request #518 from dougbtv/readme-typo
README typo for roll-YOUR-own
2020-06-09 15:07:49 -04:00
dougbtv
28bb98f78b README typo for roll-YOUR-own 2020-06-09 15:07:19 -04:00
Tomofumi Hayashi
e7c8977423 Fix network status name/namespace to compliant with multi-net-spec
This fix changes network status name to net-attach-def name with
namespace, to compliant with k8s npwg's multi-net-spec.
2020-06-09 12:43:49 +09:00
Doug Smith
fff8e490b9 Merge pull request #511 from s1061123/fix/error-on-fail
Change the error handling for kubernetes client
2020-06-08 09:14:02 -04:00
dougbtv
809a16fbed Adds code of conduct 2020-06-05 09:18:10 +09:00
Tomofumi Hayashi
f5dcb9d8be Change the error handling for kubernetes client
This change adds retry to wait a bit if kubernetes client
returns service unavailable.

This fixes #492
2020-06-01 23:17:42 +09:00
Doug Smith
fdd7d30095 Merge pull request #507 from s1061123/dev/update-example
Simplify examples directory
2020-05-28 09:59:10 -04:00
Tomofumi Hayashi
c976f6bceb Add deviceid in clusterNetwork 2020-05-28 10:00:29 +09:00
Tomofumi Hayashi
8131923b14 Simplify examples directory 2020-05-19 21:29:19 +09:00
Tomofumi Hayashi
4eac660359 Introduce gopkg.in for go module
This change introduces gopkg.in to allow everyone to use multus
code as library. Currently multus-cni uses version 'v3', hence
it hits go modules limitation, as #504 describes.

This change fix #504 with gopkg.in.
2020-05-15 21:33:26 +09:00
dougbtv
ec5fd6c923 Move pre-1.16 Kubernetes assets to a deprecated folder (to later remove) 2020-05-08 09:50:33 +09:00
Doug Smith
b8540e190d Merge pull request #455 from cucosion/feature/gkeexample
Daemonset for Google Kubernetes Engine for kubernetes cluster version…
2020-04-30 09:44:45 -04:00
Doug Smith
a4c2b67784 Merge pull request #497 from s1061123/fix/improve-cni-logs
[WIP] Fix/improve cni logs
2020-04-30 08:43:09 -04:00
Tomofumi Hayashi
b2439f8279 Simplify error message in case of delegating CNI error
This fix removes redundant error messages and try to simplify
if the error comes from delegating CNI plugins.
2020-04-30 18:02:05 +09:00
dougbtv
c8fc357f05 Adds development docs note regarding issue policy 2020-04-30 10:59:26 +09:00
dougbtv
92beb4bcdc Sets the Kubernetes API calls timeout to 60 seconds 2020-04-23 10:47:11 +09:00
dougbtv
931c12531d Allows namespaceIsolation to allow pods in any namespace refer to the default namespace 2020-04-22 09:08:11 +09:00
Doug Smith
8dba1f4a37 Merge pull request #487 from redhat-nfvpe/dev/test
Skip docker push action if REPOSITORY_PASS is not set
2020-04-21 08:56:08 -04:00
Tomofumi Hayashi
aeaf78a310 Skip docker push action if REPOSITORY_PASS is not set 2020-04-21 21:54:44 +09:00
Tomofumi Hayashi
ab073f88cd Add error message in case of unexpected situation
To troubleshooting #481, adds error message for further analysis.
2020-04-21 21:42:57 +09:00
Ted Yu
1df1e4f530 Check Pod parameter against nil before calling Eventf
Signed-off-by: Ted Yu <yuzhihong@gmail.com>
2020-04-21 00:12:36 +09:00
dougbtv
81297f5b74 Updates Dockerfile to golang 1.13 (specifying version) 2020-04-20 21:44:17 +09:00
dougbtv
3ddc6250de Fix pre 1.16 api version for CRDs 2020-04-17 17:10:20 +09:00
dougbtv
8fff5aac60 Add description fields to NetworkAttachmentDefinition CRD 2020-04-16 02:01:40 +09:00
Doug Smith
6ccf03e8b3 Merge pull request #473 from ashish-billore/master
Error log for network-attachment-definition
2020-04-02 09:46:00 -04:00
dougbtv
c0891354ff Adds readinessindicatorfile check on CNI DEL 2020-04-02 22:40:09 +09:00
ashish-billore
ea95133ef4 Error log for network-attachment-definition
Corrected the error for missing network-attachment-definition
updated test for this error.
2020-04-02 17:40:32 +09:00
Doug Smith
5931d24639 Merge pull request #469 from ashish-billore/patch-2
Update quickstart.md
2020-03-30 08:40:33 -04:00
Ashish Billore
055f74fc61 Update quickstart.md 2020-03-30 14:02:35 +09:00
Mamduh Alassi
14fb6be109 Use latest image version 3.4.1 in daemonset file 2020-03-27 00:21:21 +09:00
ICucos
b6bfb75af0 Daemonset for Google Kubernetes Engine for kubernetes cluster version pre 1.16. To be able to start cluster with version 1.15 i used "gcloud container clusters create cl-name --zone europe-west1-b --num-nodes=2 --machine-type=n1-standard-8 --image-type ubuntu --enable-network-policy --cluster-version=1.15.9-gke.24". !!!!important to start the cluster with --enable-network-policy that will enable CNI in kublet 2020-03-25 13:03:44 +02:00
Tomofumi Hayashi
7f50f5f175 Fix CI workflow 2020-03-23 23:49:05 +09:00
Doug Smith
ea72e56fab Merge pull request #462 from s1061123/dev/add-arm64v8-ci
Add arm64 build in CI workflow
2020-03-23 09:52:25 -04:00
Tomofumi Hayashi
055a7568ad Logging improvement (UID, net-attach-def)
This change adds pod UID and net-attach-def name in verbose log
and sends kubernetes event when net-attach-def is not found.
2020-03-23 21:32:33 +09:00
Tomofumi Hayashi
c7cd0ef822 Add arm64 build in CI workflow 2020-03-23 13:00:50 +09:00
Trevor Tao
f885b38332 Add Dockerfile for building image on arm64
Add Dockerfile.arm64 for building Multus image on arm64.

Actually it's multi-arch capable and works for
amd64 platform too if used.

Signed-off-by: Trevor Tao <trevor.tao@arm.com>
2020-03-19 22:41:27 +09:00
Trevor Tao
cd1a76f919 Add Dockerfile for building image on arm64
Add Dockerfile.arm64 for building Multus image on arm64.

Actually it's multi-arch capable and works for
amd64 platform too if used.

Signed-off-by: Trevor Tao <trevor.tao@arm.com>
2020-03-19 22:41:27 +09:00
Tomofumi Hayashi
079c853eba Add Kubernetes event log when the pod is launched 2020-03-16 22:08:03 +09:00
Tomofumi Hayashi
bfaf22964b Fix goreleaser file for build-release error 2020-03-16 21:58:41 +09:00
ICucos
1bc8064a5b Daemonset for Google Kubernetes Engine for kubernetes cluster version 1.16 +. To be able to start cluster with version 1.16 i used rapid channel "gcloud beta container clusters create cluster --zone europe-west1-b --num-nodes=1 --machine-type=n1-standard-8 --image-type ubuntu --enable-network-policy --release-channel=rapid --enable-autorepair" 2020-03-16 13:19:36 +02:00
Tomofumi Hayashi
eaf6ff6e20 Update vendor file to fix #426 2020-03-13 12:16:49 +09:00
Adrian Chiris
5577822b36 Pass DeviceID to each plugin in configuration list
Until today, it was hardcoded that DeviceID will only be
injected for the first CNI in the chain.

This commit modifies multus to pass DeviceID to each network
configuration element in a network configuration list.
This will  allow multiple CNI's to act on DeviceID when CNI
plugins are being chained for a specific network.

The change is required to allow CNI's to ensure network
isolation (introduced in kernel >= 5.2.0 see [1]) for RDMA devices
when exist.

e.g for SR-IOV network:
sriov-cni moves network device associated with the provided DeviceID
to to the container's network namespace.
An "RDMA cni" would do the same for the corresponding RDMA device when
RDMA traffic is desired on the network.

[1] https://patchwork.kernel.org/cover/10810451/
2020-03-12 23:48:03 +09:00
Periyasamy Palanisamy
32fe803221 Log message after logger initialization
In cmdCheck and cmdDelete debug messages are logged even before logger
is initialized with its log level and file (done in types.LoadNetConf).
Because of this those debug messages are not logged into the file.

Signed-off-by: Periyasamy Palanisamy <periyasamy.palanisamy@est.tech>
2020-03-05 23:48:15 +09:00
Doug Smith
1412caafbd Merge pull request #446 from s1061123/no-config-invalidation
Removes configuration invalidation
2020-03-02 10:08:45 -05:00
Tomofumi Hayashi
bdece11192 Removes configuration invalidation 2020-02-28 12:43:49 +09:00
Doug Smith
32952b929c Merge pull request #442 from s1061123/dev/kind-e2e
[WIP]Add e2e test with kind in CI pipeline
2020-02-20 09:35:25 -05:00
dougbtv
1042a5e25f Changes wait.ExponentialBackoff to wait.PollImmediate and exposes readinessindicatorfile via entrypoint parameter 2020-02-19 10:03:43 +09:00
dougbtv
9774ddade1 [docs] Updates quickstart to use alpine image 2020-02-13 17:09:37 +09:00
Tomofumi Hayashi
76c458228e Add e2e test with kind in CI pipeline 2020-02-13 17:02:22 +09:00
dougbtv
4fdaf3f427 [bugfix] Fixes unnecessary wait when readinessindicatorfile is not present 2020-02-13 01:04:28 +09:00
Tomofumi Hayashi
8334b73296 Add Github Actions for CI pipeline, to be replace with travis 2020-02-12 22:57:04 +09:00
groclawski
3e601c6ffe double word fix 2020-02-11 00:04:04 +09:00
Tomofumi Hayashi
9c35b5ccf2 Fix travis CI 2020-02-10 16:01:18 +09:00
Tomofumi Hayashi
da9da81be9 Fix travis ci error 2020-01-25 01:18:32 +09:00
Doug Smith
36f630fd39 Merge pull request #431 from s1061123/dev/container-march-images
Support container multi architecture image with manifest
2020-01-23 09:40:07 -05:00
Tomofumi Hayashi
dfd19e5b10 Support container multi architecture image with manifest 2020-01-21 02:48:10 +09:00
Doug Smith
c534b7d364 Uses atomic operation for writing config file (#430) 2020-01-17 16:10:46 -05:00
Michael C. Cambria
c35c4b7e97 Add CNI Check support (#427)
Add tests for CNI version 0.4.0
2020-01-13 09:57:21 -05:00
Tomofumi Hayashi
b2bf154328 Fix travis ci (#421)
This change update travis pipeline as following:
 - cleanup unused parameters/stages
 - add condition based on parameter (now user can use travis CI
   in cloned repo)
 - suppress ppc64 deployment because amd64 also build ppc images
   and other arch (fixes #418)
2019-12-11 15:41:36 -05:00
Tomofumi Hayashi
9874c14e23 Replace multus code with NPWG client library code 2019-12-10 14:38:09 +09:00
Tomofumi Hayashi
f4f2f65d1d Use NPWG client library to manipulate net-attach-defs
This change introduce NPWG client library functions to manipulate
net-attach-defs. This also requires to change k8sclient and unit
test code as well.
2019-12-10 14:38:09 +09:00
Shravan Vallala
cb3f59e7e7 Server URL in multus kubeconfig at /etc/cni/net.d/multus.d/multus.kubeconfig
is not properly setup for IPv6 installs

Fix:

If the API server host is IPv6 address then the server url should
include square braces to separate the IP address and port correctly
2019-12-10 09:09:37 +09:00
Shravan Vallala
a3712815fd Server URL in multus kubeconfig at
/etc/cni/net.d/multus.d/multus.kubeconfig is not properly setup for IPv6
installs

Fix:

If the API server host is IPv6 address then the server url should
include square braces to separate the IP address and port correctly
2019-12-10 09:09:37 +09:00
Doug Smith
230f314877 Bump daemonset tag to v3.4 (#417) 2019-12-03 12:38:10 -08:00
Tomofumi Hayashi
a314f90dda Fix unit test to track on updated error message 2019-11-15 08:31:49 -05:00
dougbtv
ecc474a264 Skips binary copy in entrypoint with new parameter 2019-11-07 23:24:04 +08:00
Dan Williams
c7f957194b multus: print pod namespace/name in cmdAdd/cmdDel error messages
Signed-off-by: Dan Williams <dcbw@redhat.com>
2019-11-06 10:04:47 -05:00
1295 changed files with 211598 additions and 160735 deletions

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

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

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

@@ -0,0 +1,92 @@
name: Image build
on: [pull_request]
jobs:
build-amd64:
name: Image build/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: Build container image
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: ghcr.io/${{ github.repository }}:latest-amd64
file: deployments/Dockerfile
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: deployments/Dockerfile.arm64
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: deployments/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: deployments/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: deployments/Dockerfile.openshift

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

@@ -0,0 +1,187 @@
name: Image push for master
on:
push:
branches:
- master
env:
REPOSITORY: docker.io/nfvpe/multus
REPOSITORY_USER: nfvperobot
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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:latest-amd64
${{ env.REPOSITORY }}:snapshot-amd64
file: deployments/Dockerfile
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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:latest-arm64
${{ env.REPOSITORY }}:snapshot-arm64
file: deployments/Dockerfile.arm64
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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:latest-ppc64le
${{ env.REPOSITORY }}:snapshot-ppc64le
file: deployments/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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:latest-s390x
${{ env.REPOSITORY }}:snapshot-s390x
file: deployments/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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:latest-origin
${{ env.REPOSITORY }}:snapshot-origin
file: deployments/Dockerfile.openshift
push-manifest:
needs: [push-amd64, push-arm64, push-ppc64le, push-s390x]
runs-on: ubuntu-latest
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Create manifest for multi-arch images
if: github.repository_owner == 'intel'
run: |
# get artifacts from previous steps
docker pull ${{ env.REPOSITORY }}:snapshot-amd64
docker pull ${{ env.REPOSITORY }}:snapshot-arm64
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-ppc64le
docker pull ${{ env.REPOSITORY }}:latest-s390x
docker manifest create ${{ env.REPOSITORY }}:snapshot ${{ env.REPOSITORY }}:snapshot-amd64 ${{ env.REPOSITORY }}:snapshot-arm64 ${{ 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-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-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-ppc64le --arch ppc64le
docker manifest annotate ${{ env.REPOSITORY }}:latest ${{ env.REPOSITORY }}:latest-s390x --arch s390x
docker manifest push ${{ env.REPOSITORY }}:latest

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

@@ -0,0 +1,229 @@
name: Image push release
on:
push:
tags:
- v*
env:
REPOSITORY: docker.io/nfvpe/multus
REPOSITORY_USER: nfvperobot
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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: ${{ env.REPOSITORY }}
tag-latest: false
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:stable-amd64
${{ steps.docker_meta.outputs.tags }}-amd64
file: deployments/Dockerfile
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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: ${{ env.REPOSITORY }}
tag-latest: false
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:stable-arm64
${{ steps.docker_meta.outputs.tags }}-arm64
file: deployments/Dockerfile.arm64
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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: ${{ env.REPOSITORY }}
tag-latest: false
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:stable-ppc64le
${{ steps.docker_meta.outputs.tags }}-ppc64le
file: deployments/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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: ${{ env.REPOSITORY }}
tag-latest: false
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:stable-s390x
${{ steps.docker_meta.outputs.tags }}-s390x
file: deployments/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 Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Docker meta
id: docker_meta
uses: crazy-max/ghaction-docker-meta@v1
with:
images: ${{ env.REPOSITORY }}
tag-latest: false
- name: Push container image
if: github.repository_owner == 'intel'
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
${{ env.REPOSITORY }}:stable-origin
${{ steps.docker_meta.outputs.tags }}-origin
file: deployments/Dockerfile.openshift
push-manifest:
needs: [push-amd64, push-arm64, push-ppc64le, push-s390x]
runs-on: ubuntu-latest
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: ${{ env.REPOSITORY }}
tag-latest: false
- name: Login to Container Registry
if: github.repository_owner == 'intel'
uses: docker/login-action@v1
with:
username: ${{ env.REPOSITORY_USER }}
password: ${{ secrets.REPOSITORY_PASS }}
- name: Create manifest for multi-arch images
if: github.repository_owner == 'intel'
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 }}-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 }}-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 }}-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-ppc64le
docker pull ${{ env.REPOSITORY }}:stable-s390x
docker manifest create ${{ env.REPOSITORY }}:stable ${{ env.REPOSITORY }}:stable-amd64 ${{ env.REPOSITORY }}:stable-arm64 ${{ 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-ppc64le --arch ppc64le
docker manifest annotate ${{ env.REPOSITORY }}:stable ${{ env.REPOSITORY }}:stable-s390x --arch s390x
docker manifest push ${{ env.REPOSITORY }}:stable

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

@@ -0,0 +1,42 @@
name: e2e-kind
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 deployments/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: ./setup_cluster.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

26
.github/workflows/release.yml vendored Normal file
View File

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

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

@@ -0,0 +1,49 @@
on: [push, pull_request]
name: Test
jobs:
test:
strategy:
matrix:
go-version: [1.15.x, 1.16.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
run: |
GO111MODULE=off go get github.com/mgechev/revive
$(go env GOPATH)/bin/revive -exclude ./vendor/... ./... # this is ouput for user
$(go env GOPATH)/bin/revive -exclude ./vendor/... ./...| xargs -0 -r false # this is for github actions
- name: Run go fmt
run: go fmt ./...
#run: diff -u <(echo -n) <(gofmt -d -s .)
- name: Run go vet
run: go vet ./...
- name: Test
run: sudo ./hack/test-go.sh
- name: Send coverage
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: coverage.out
flag-name: Go-${{ matrix.go }}
parallel: true
# notifies that all test jobs are finished.
finish:
needs: test
runs-on: ubuntu-latest
steps:
- uses: shogo82148/actions-goveralls@v1
with:
parallel-finished: true

1
.gitignore vendored
View File

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

View File

@@ -9,7 +9,7 @@ builds:
-
env:
- CGO_ENABLED=0
main: ./multus/
main: ./cmd/
goos:
- linux
goarch:
@@ -17,8 +17,9 @@ builds:
- amd64
- arm
- arm64
archive:
wrap_in_directory: true
- s390x
archives:
- wrap_in_directory: true
checksum:
name_template: 'checksums.txt'
snapshot:

View File

@@ -1,23 +1,31 @@
os: linux
language: go
# see https://docs.travis-ci.com/user/reference/overview/#Virtualization-environments
# for the detail
# sudo: requried
dist: trusty
dist: bionic
services:
- docker
go:
- 1.12.x
- 1.13.x
env:
global:
- GO111MODULE=on
- REGISTRY_USER=${REGISTRY_USER}
- REGISTRY_USER=${REGISTRY_USER:-nfvpe}
- REGISTRY_PASS=${REGISTRY_PASS}
- REPOSITORY_NAME=${REPOSITORY_NAME}
- REPOSITORY_USER=${REPOSITORY_USER}
- DOCKER_CLI_EXPERIMENTAL="enabled"
- secure: "${REGISTRY_SECURE}"
matrix:
jobs:
- TARGET=amd64
- TARGET=ppc64le
before_install:
- if [ "${REPOSITORY_NAME}" = "" ]; then export REPOSITORY_NAME=multus; fi
- sudo apt-get update -qq
- go get github.com/mattn/goveralls
@@ -33,73 +41,76 @@ before_script:
# - gocyclo -over 15 ./multus
script:
- GOARCH="${TARGET}" ./build
- GOARCH="${TARGET}" ./hack/build-go.sh
- |
if [ "${TARGET}" == "amd64" ]; then
sudo env PATH=${PATH} ./test.sh
sudo env PATH=${PATH} ./scripts/test.sh
goveralls -coverprofile=coverage.out -service=travis-ci
mkdir -p ${TRAVIS_BUILD_DIR}/dist
tar cvfz ${TRAVIS_BUILD_DIR}/dist/multus-cni_amd64.tar.gz --warning=no-file-changed --exclude="dist" .
docker build -t nfvpe/multus:latest-amd64 .
docker build -t nfvpe/multus:latest-ppc64le -f Dockerfile.ppc64le .
docker build -t nfvpe/multus-origin:latest -f Dockerfile.openshift .
docker build -t ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 .
docker build -t ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le -f Dockerfile.ppc64le .
docker build -t ${REPOSITORY_USER}/${REPOSITORY_NAME}-origin:latest -f Dockerfile.openshift .
fi
deploy:
# Release on versioned tag (e.g. v1.0)
- provider: script
#skip_cleanup: true
script: curl -sL https://git.io/goreleaser | bash
#cleanup: false
script: curl -sL https://git.io/goreleaser
on:
tags: true
all_branches: true
condition: "$TRAVIS_TAG =~ ^v[0-9].*$"
condition: "$TARGET = amd64 && $TRAVIS_TAG =~ ^v[0-9].*$ && ! -z $GITHUB_TOKEN && $TRAVIS_OS_NAME = linux"
# Push images to Dockerhub on tag
- provider: script
skip_cleanup: true
cleanup: false
script: >
bash -c '
docker tag nfvpe/multus:latest-amd64 nfvpe/multus:latest;
docker tag nfvpe/multus:latest-amd64 nfvpe/multus:stable;
docker tag nfvpe/multus:latest-amd64 nfvpe/multus:stable-amd64;
docker tag nfvpe/multus:latest-amd64 nfvpe/multus:$TRAVIS_TAG;
docker tag nfvpe/multus:latest-ppc64le nfvpe/multus:stable-ppc64le;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-amd64;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:$TRAVIS_TAG;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-ppc64le;
docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASS";
docker push nfvpe/multus:latest;
docker push nfvpe/multus:latest-amd64;
docker push nfvpe/multus:latest-ppc64le;
docker push nfvpe/multus:stable;
docker push nfvpe/multus:stable-amd64;
docker push nfvpe/multus:stable-ppc64le;
docker push nfvpe/multus:$TRAVIS_TAG;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-amd64;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-ppc64le;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:$TRAVIS_TAG;
export DOCKER_CLI_EXPERIMENTAL="enabled";
docker manifest create ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 --arch amd64;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le --arch ppc64le;
docker manifest push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest;
docker manifest create ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-ppc64le;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-amd64 --arch amd64;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable-ppc64le --arch ppc64le;
docker manifest push ${REPOSITORY_USER}/${REPOSITORY_NAME}:stable;
echo done'
on:
tags: true
all_branches: true
condition: "$TRAVIS_TAG =~ ^v[0-9].*$"
condition: "$TRAVIS_TAG =~ ^v[0-9].*$ && -n $REGISTRY_USER && -n $REGISTRY_PASS && -n $REPOSITORY_NAME && -n $REPOSITORY_USER"
# Push images to Dockerhub on merge to master
- provider: script
on:
branch: master
condition: "-n $REGISTRY_USER && -n $REGISTRY_PASS && -n $REPOSITORY_NAME && -n $REPOSITORY_USER"
script: >
bash -c '
docker tag nfvpe/multus:latest-amd64 nfvpe/multus:snapshot;
docker tag nfvpe/multus:latest-amd64 nfvpe/multus:snapshot-amd64;
docker tag nfvpe/multus:latest-ppc64le nfvpe/multus:snapshot-ppc64le;
docker tag ${REPOSITORY_USER}/:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-amd64;
docker tag ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-ppc64le;
docker login -u "$REGISTRY_USER" -p "$REGISTRY_PASS";
docker push nfvpe/multus:snapshot;
docker push nfvpe/multus:snapshot-amd64;
docker push nfvpe/multus:snapshot-ppc64le;
docker push nfvpe/multus:latest;
docker push nfvpe/multus:latest-amd64;
docker push nfvpe/multus:latest-ppc64le;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-amd64;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-ppc64le;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64;
docker push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le;
docker manifest create ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-ppc64le;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-amd64 --arch amd64;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot-ppc64le --arch ppc64le;
docker manifest push ${REPOSITORY_USER}/${REPOSITORY_NAME}:snapshot;
docker manifest create ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-amd64 --arch amd64;
docker manifest annotate ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest-ppc64le --arch ppc64le;
docker manifest push ${REPOSITORY_USER}/${REPOSITORY_NAME}:latest;
echo done'
after_success:
# put build tgz to bintray
- curl -T ${TRAVIS_BUILD_DIR}/dist/multus-cni_amd64.tar.gz -u${BINTRAY_USER}:${BINTRAY_APIKEY} https://api.bintray.com/content/redhat-nfvpe/multus-cni-crd-snapshots/snapshot/snapshot-${TRAVIS_COMMIT}/multus-cni_amd64-${TRAVIS_COMMIT}.tar.gz
# publish uploaded file
- curl -X POST -u${BINTRAY_USER}:${BINTRAY_APIKEY} https://api.bintray.com/content/redhat-nfvpe/multus-cni-crd-snapshots/snapshot/snapshot-${TRAVIS_COMMIT}/publish
# put it in bintray download list
- sleep 20
- "curl -X PUT -H 'Accept: application/json' -H 'Content-type: application/json' -u${BINTRAY_USER}:${BINTRAY_APIKEY} https://api.bintray.com/file_metadata/redhat-nfvpe/multus-cni-crd-snapshots/multus-cni_amd64-${TRAVIS_COMMIT}.tar.gz -d '{\"list_in_downloads\":true}'"

130
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,130 @@
# Multus CNI Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[The Multus Slack Page](https://intel-corp.herokuapp.com/).
All complaints will be reviewed and investigated promptly and fairly. Or you
may specifically contact Doug Smith (dosmith@redhat.com) via email.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

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

15
OWNERS
View File

@@ -1,15 +0,0 @@
reviewers:
- dougbtv
- dcbw
- squeed
- zshi
- fepan
- s1061123
approvers:
- dougbtv
- dcbw
- squeed
- zshi
- fepan
- s1061123

View File

@@ -1,8 +1,8 @@
# Multus-CNI
![multus-cni Logo](https://github.com/intel/multus-cni/blob/master/doc/images/Multus.png)
![multus-cni Logo](https://github.com/intel/multus-cni/blob/master/docs/images/Multus.png)
[![Travis CI](https://travis-ci.org/intel/multus-cni.svg?branch=master)](https://travis-ci.org/intel/multus-cni/builds)[![Go Report Card](https://goreportcard.com/badge/github.com/intel/multus-cni)](https://goreportcard.com/report/github.com/intel/multus-cni)[![Coverage Status](https://coveralls.io/repos/github/intel/multus-cni/badge.svg)](https://coveralls.io/github/intel/multus-cni)
[![Build](https://github.com/intel/multus-cni/actions/workflows/build.yml/badge.svg)](https://github.com/intel/multus-cni/actions/workflows/build.yml)[![Test](https://github.com/intel/multus-cni/actions/workflows/test.yml/badge.svg)](https://github.com/intel/multus-cni/actions/workflows/test.yml)[![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,11 +18,11 @@ Multus is one of the projects in the [Baremetal Container Experience kit](https:
Here's an illustration of the network interfaces attached to a pod, as provisioned by Multus CNI. The diagram shows the pod with three interfaces: `eth0`, `net0` and `net1`. `eth0` connects kubernetes cluster network to connect with kubernetes server/services (e.g. kubernetes api-server, kubelet and so on). `net0` and `net1` are additional network attachments and connect to other networks by using [other CNI plugins](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) (e.g. vlan/vxlan/ptp).
![multus-pod-image](doc/images/multus-pod-image.svg)
![multus-pod-image](docs/images/multus-pod-image.svg)
## Quickstart Installation Guide
The quickstart installation method for Multus requires that you have first installed a Kubernetes CNI plugin to serve as your pod-to-pod network, which we refer to as your "default network" (a network interface that every pod will be creatd with). Each network attachment created by Multus will be in addition to this default network interface. For more detail on installing a default network CNI plugins, refer to our [quick-start guide](doc/quickstart.md).
The quickstart installation method for Multus requires that you have first installed a Kubernetes CNI plugin to serve as your pod-to-pod network, which we refer to as your "default network" (a network interface that every pod will be 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](docs/quickstart.md).
Clone this GitHub repository, we'll apply a daemonset which installs Multus using to `kubectl` from this repo. From the root directory of the clone, apply the daemonset YAML file:
@@ -30,22 +30,22 @@ Clone this GitHub repository, we'll apply a daemonset which installs Multus usin
$ 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](doc/quickstart.md)
This will configure your systems to be ready to use Multus CNI, but, to get started with adding additional interfaces to your pods, refer to our complete [quick-start guide](docs/quickstart.md)
## Additional installation Options
- Install via daemonset using the quick-start guide, above.
- Download binaries from [release page](https://github.com/intel/multus-cni/releases)
- By Docker image from [Docker Hub](https://hub.docker.com/r/nfvpe/multus/tags/)
- Or, roll-you-own and build from source
- See [Development](doc/development.md)
- Or, roll-your-own and build from source
- See [Development](docs/development.md)
## Comprehensive Documentation
- [How to use](doc/how-to-use.md)
- [Configuration](doc/configuration.md)
- [Development](doc/development.md)
- [How to use](docs/how-to-use.md)
- [Configuration](docs/configuration.md)
- [Development](docs/development.md)
## Contact Us
For any questions about Multus CNI, feel free to ask a question in #general in the [Intel-Corp Slack](https://intel-corp.herokuapp.com/), or open up a GitHub issue.
For any questions about Multus CNI, feel free to ask a question in #general in the [NPWG Slack](https://npwg-team.slack.com/), or open up a GitHub issue. Request an invite to NPWG slack [here](https://intel-corp.herokuapp.com/).

60
cmd/main.go Normal file
View File

@@ -0,0 +1,60 @@
// 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 refered from CNI project
// It reads other plugin netconf, and then invoke them, e.g.
// flannel or sriov plugin.
package main
import (
"flag"
"fmt"
"os"
"gopkg.in/intel/multus-cni.v3/pkg/multus"
"github.com/containernetworking/cni/pkg/skel"
cniversion "github.com/containernetworking/cni/pkg/version"
)
func main() {
// Init command line flags to clear vendored packages' one, especially in init()
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
// add version flag
versionOpt := false
flag.BoolVar(&versionOpt, "version", false, "Show application version")
flag.BoolVar(&versionOpt, "v", false, "Show application version")
flag.Parse()
if versionOpt == true {
fmt.Printf("%s\n", multus.PrintVersionString())
return
}
skel.PluginMain(
func(args *skel.CmdArgs) error {
result, err := multus.CmdAdd(args, nil, nil)
if err != nil {
return err
}
return result.Print()
},
func(args *skel.CmdArgs) error {
return multus.CmdCheck(args, nil, nil)
},
func(args *skel.CmdArgs) error { return multus.CmdDel(args, nil, nil) },
cniversion.All, "meta-plugin that delegates to other CNI plugins")
}

View File

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

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

@@ -1,50 +0,0 @@
# 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,27 +0,0 @@
# Copyright (c) 2018 Intel Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
apiVersion: v1
kind: Service
metadata:
name: multus-webhook-service
labels:
app: multus-webhook
namespace: default
spec:
ports:
- port: 443
targetPort: 443
selector:
app: multus-webhook

View File

@@ -4,13 +4,13 @@ FROM centos:centos7 as build
# Add everything
ADD . /usr/src/multus-cni
ENV INSTALL_PKGS "git golang"
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
./hack/build-go.sh
FROM centos:centos7
COPY --from=build /usr/src/multus-cni /usr/src/multus-cni

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 && \
./hack/build-go.sh
# build arm64 container
FROM arm64v8/centos:7
COPY --from=build /usr/src/multus-cni /usr/src/multus-cni
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -0,0 +1,20 @@
# This dockerfile is specific to building Multus for OpenShift
FROM openshift/origin-release:golang-1.15 as builder
ADD . /usr/src/multus-cni
WORKDIR /usr/src/multus-cni
ENV GO111MODULE=off
RUN ./hack/build-go.sh
FROM openshift/origin-base
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

@@ -13,7 +13,7 @@ RUN rpm --import https://mirror.go-repo.io/centos/RPM-GPG-KEY-GO-REPO && \
yum install -y $INSTALL_PKGS && \
rpm -V $INSTALL_PKGS && \
cd /usr/src/multus-cni && \
./build
./hack/build-go.sh
# build ppc container
FROM ppc64le/centos:latest

View File

@@ -0,0 +1,19 @@
# This Dockerfile is used to build the image available on DockerHub
FROM golang:1.13 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:3-slim
COPY --from=build /usr/src/multus-cni /usr/src/multus-cni
WORKDIR /
ADD ./images/entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -1,30 +0,0 @@
## Development Information
## How to build the multus-cni?
```
git clone https://github.com/intel/multus-cni.git
cd multus-cni
./build
```
## How to run CI tests?
Multus has go unit tests (based on ginkgo framework).The following commands drive CI tests manually in your environment:
```
sudo ./test.sh
```
## Logging Best Practices
Following are multus logging best practices:
* Add `logging.Debugf()` at the begining of functions
* In case of error handling, use `logging.Errorf()` with given error info
* `logging.Panicf()` only be used for critical errors (it should NOT normally be used)
## CI Introduction
TBD

View File

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

@@ -40,6 +40,7 @@ Following is the example of multus config file, in `/etc/cni/net.d/`.
* `cniDir` (string, optional): Multus CNI data directory, default `/var/lib/cni/multus`
* `binDir` (string, optional): additional directory for CNI plugins which multus calls, in addition to the default (the default is typically set to `/opt/cni/bin`)
* `kubeconfig` (string, optional): kubeconfig file for the out of cluster communication with kube-apiserver. See the example [kubeconfig](https://github.com/intel/multus-cni/blob/master/doc/node-kubeconfig.yaml). If you would like to use CRD (i.e. network attachment definition), this is required
* `logToStderr` (bool, optional): Enable or disable logging to `STDERR`. Defaults to true.
* `logFile` (string, optional): file path for log file. multus puts log in given file
* `logLevel` (string, optional): logging level ("debug", "error", "verbose", or "panic")
* `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.
@@ -84,7 +85,15 @@ Only one option is necessary to configure this functionality:
You may wish to enable some enhanced logging for Multus, especially during the process where you're configuring Multus and need to understand what is or isn't working with your particular configuration.
Multus will always log via `STDERR`, which is the standard method by which CNI plugins communicate errors, and these errors are logged by the Kubelet. This method is always enabled.
#### Logging via STDERR
By default, Multus will log via `STDERR`, which is the standard method by which CNI plugins communicate errors, and these errors are logged by the Kubelet.
Optionally, you may disable this method by setting the `logToStderr` option in your CNI configuration:
```
"logToStderr": false,
```
#### Writing to a Log File
@@ -117,6 +126,10 @@ You may configure the logging level by using the `LogLevel` option in your CNI c
The functionality provided by the `namespaceIsolation` configuration option enables a mode where Multus only allows pods to access custom resources (the `NetworkAttachmentDefinitions`) within the namespace where that pod resides. In other words, the `NetworkAttachmentDefinitions` are isolated to usage within the namespace in which they're created.
**NOTE**: The default namespace is special in this scenario. Even with namespace isolation enabled, any pod, in any namespace is allowed to refer to `NetworkAttachmentDefinitions` in the default namespace. This allows you to create commonly used unprivileged `NetworkAttachmentDefinitions` without having to put them in all namespaces. For example, if you had a `NetworkAttachmentDefinition` named `foo` the default namespace, you may reference it in an annotation with: `default/foo`.
**NOTE**: You can also add additional namespaces which can be referred to globally using the `global-namespaces` option (see next section).
For example, if a pod is created in the namespace called `development`, Multus will not allow networks to be attached when defined by custom resources created in a different namespace, say in the `default` network.
Consider the situation where you have a system that has users of different privilege levels -- as an example, a platform which has two administrators: a Senior Administrator and a Junior Administrator. The Senior Administrator may have access to all namespaces, and some network configurations as used by Multus are considered to be privileged in that they allow access to some protected resources available on the network. However, the Junior Administrator has access to only a subset of namespaces, and therefore it should be assumed that the Junior Administrator cannot create pods in their limited subset of namespaces. The `namespaceIsolation` feature provides for this isolation, allowing pods created in given namespaces to only access custom resources in the same namespace as the pod.
@@ -213,7 +226,7 @@ pod/samplepod created
You'll note that pod fails to spawn successfully. If you check the Multus logs, you'll see an entry such as:
```
2018-12-18T21:41:32Z [error] GetPodNetwork: namespace isolation violation: podnamespace: development / target namespace: privileged
2018-12-18T21:41:32Z [error] GetNetworkDelegates: namespace isolation enabled, annotation violates permission, pod is in namespace development but refers to target namespace privileged
```
This error expresses that the pod resides in the namespace named `development` but refers to a `NetworkAttachmentDefinition` outside of that namespace, in this case, the namespace named `privileged`.
@@ -251,6 +264,16 @@ NAME READY STATUS RESTARTS AGE
samplepod 1/1 Running 0 31s
```
### Allow specific namespaces to be used across namespaces when using namespace isolation
The `globalNamespaces` configuration option is only used when `namespaceIsolation` is set to true. `globalNamespaces` specifies a comma-delimited list of namespaces which can be referred to from outside of any given namespace in which a pod resides.
```
"globalNamespaces": "default,namespace-a,namespace-b",
```
Note that when using `globalNamespaces` the `default` namespace must be specified in the list if you wish to use that namespace, when `globalNamespaces` is not set, the `default` namespace is implied to be used across namespaces.
### Specify default cluster network in Pod annotations
Users may also specify the default network for any given pod (via annotation), for cases where there are multiple cluster networks available within a Kubernetes cluster.

52
docs/development.md Normal file
View File

@@ -0,0 +1,52 @@
## Development Information
## How to utilize multus-cni code as library?
Multus now uses [gopkg.in](http://gopkg.in/) to expose its code as library.
You can use following command to import our code into your go code.
```
go get gopkg.in/intel/multus-cni.v3
```
## How do I submit an issue?
Use GitHub as normally, you'll be presented with an option to submit a issue or enhancement request.
Issues are considered stale after 90 days. After which, the maintainers reserve the right to close an issue.
Typically, we'll tag the submitter and ask for more information if necessary before closing.
If an issue is closed that you don't feel is sufficiently resolved, please feel free to re-open the issue and provide any necessary information.
## How do I build multus-cni?
You can use the built in `./hack/build-go.sh` script!
```
git clone https://github.com/intel/multus-cni.git
cd multus-cni
./hack/build-go.sh
```
## How do I run CI tests?
Multus has go unit tests (based on ginkgo framework).The following commands drive CI tests manually in your environment:
```
sudo ./scripts/test.sh
```
## What are the best practices for logging?
The following are the best practices for multus logging:
* Add `logging.Debugf()` at the begining of functions
* In case of error handling, use `logging.Errorf()` with given error info
* `logging.Panicf()` only be used for critical errors (it should NOT normally be used)
## CI Introduction
TBD

View File

@@ -567,7 +567,11 @@ This the directory in which the Multus binary will be installed.
--namespace-isolation=false
Setting this option to true enables the Namespace isolation feature, which insists that custom resources must be created in the same namespace as the pods, otherwise it will refuse to attach those definitions as additional interfaces.
Setting this option to true enables the Namespace isolation feature, which insists that custom resources must be created in the same namespace as the pods, otherwise it will refuse to attach those definitions as additional interfaces. See (the configuration guide for more information)[configuration.md].
--global-namespaces=default,foo,bar
The `--global-namespaces` works only when `--namespace-isolation=true`. This takes a comma-separated list of namespaces which can be referred to globally when namespace isolation is enabled. See (the configuration guide for more information)[configuration.md].
--multus-bin-file=/usr/src/multus-cni/bin/multus

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

@@ -1,6 +1,6 @@
# Quickstart Guide
This guide is intended as a way to get you off the ground, and using Multus CNI to create Kubernetes pods with multiple interfaces. If you're already using Multus and need more detail, see the [comprehensive usage guide](how-to-use.md). This document a quickstart and a getting started guide in one, intended for your first run-through of Multus CNI.
This guide is intended as a way to get you off the ground, using Multus CNI to create Kubernetes pods with multiple interfaces. If you're already using Multus and need more detail, see the [comprehensive usage guide](how-to-use.md). This document is a quickstart and a getting started guide in one, intended for your first run-through of Multus CNI.
We'll first install Multus CNI, and then we'll setup some configurations so that you can see how multiple interfaces are created for pods.
@@ -15,6 +15,8 @@ Two things we'll refer to a number of times through this document are:
Our installation method requires that you first have installed Kubernetes and have configured a default network -- that is, a CNI plugin that's used for your pod-to-pod connectivity.
We recommend Kubernetes 1.16 or later.
To install Kubernetes, you may decide to use [kubeadm](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/), or potentially [kubespray](https://github.com/kubernetes-sigs/kubespray).
After installing Kubernetes, you must install a default network CNI plugin. If you're using kubeadm, refer to the "[Installing a pod network add-on](https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#pod-network)" section in the kubeadm documentation. If it's your first time, we generally recommend using Flannel for the sake of simplicity.
@@ -27,7 +29,14 @@ To verify that you default network is ready, you may list your Kubernetes nodes
kubectl get nodes
```
In the case that your default network is ready
In the case that your default network is ready you will see the `STATUS` column also switch to `Ready` for each node.
```
NAME STATUS ROLES AGE VERSION
master-0 Ready master 1h v1.17.1
master-1 Ready master 1h v1.17.1
master-2 Ready master 1h v1.17.1
```
## Installation
@@ -39,23 +48,16 @@ Firstly, clone this GitHub repository.
git clone https://github.com/intel/multus-cni.git && cd multus-cni
```
If you're using Kubernetes 1.16+, we'll apply a YAML file with `kubectl` from this repo.
We'll apply a YAML file with `kubectl` from this repo.
```
$ cat ./images/multus-daemonset.yml | kubectl apply -f -
```
Or, for Kubernetes versions < 1.16, use the prior version yaml:
```
$ cat ./images/multus-daemonset-pre-1.16.yml | kubectl apply -f -
```
### What the Multus daemonset does
* Starts a Multus daemonset, this runs a pod on each node which places a Multus binary on each node in `/opt/cni/bin`
* Reads the lexigraphically (alphabetically) first configuration file in `/etc/cni/net.d`, and creates a new configuration file for Multus as `/etc/cni/net.d/00-multus.conf`, this configuration is auto-generated and is based on the default network configuration (which is assumed to be the alphabetically first configuration)
* Reads the lexicographically (alphabetically) first configuration file in `/etc/cni/net.d`, and creates a new configuration file for Multus 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.
@@ -67,7 +69,7 @@ Generally, the first step in validating your installation is to ensure that the
$ 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 alphabetically first
You may further validate that it has ran by looking at the `/etc/cni/net.d/` directory and ensure that the auto-generated `/etc/cni/net.d/00-multus.conf` exists corresponding to the alphabetically first configuration file.
## Creating additional interfaces
@@ -91,11 +93,11 @@ CNI configurations are JSON, and we have a structure here that has a few things
2. `type`: This tells CNI which binary to call on disk. Each CNI plugin is a binary that's called. Typically, these binaries are stored in `/opt/cni/bin` on each node, and CNI executes this binary. In this case we've specified the `loopback` binary (which create a loopback-type network interface). If this is your first time installing Multus, you might want to verify that the plugins that are in the "type" field are actually on disk in the `/opt/cni/bin` directory.
3. `additional`: This field is put here as an example, each CNI plugin can specify whatever configuration parameters they'd like in JSON. These are specific to the binary you're calling in the `type` field.
For an even further example -- take a look at the [bridge CNI plugin README](https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge) which shows additional
For an even further example -- take a look at the [bridge CNI plugin README](https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge) which shows additional details.
If you'd like more information about CNI configuration, you can read [the entire CNI specification](https://github.com/containernetworking/cni/blob/master/SPEC.md). It might also be useful to look at the [CNI reference plugins](https://github.com/containernetworking/plugins) and see how they're configured.
You do not need to reload or refresh the Kubelets when CNI configurations change. These are read on each creation & deletion of pods. So if you change a configuration, it'll apply the next time a pod is created. Existing pods may need to be restarted if they need the new configuration.
You do not need to reload or refresh the Kubelets when CNI configurations change. These are read on each creation & deletion of pods. So if you change a configuration, it'll apply the next time a pod is created. Existing pods may need to be restarted if they need the new configuration.
### Storing a configuration as a Custom Resource
@@ -151,7 +153,7 @@ kubectl describe network-attachment-definitions macvlan-conf
### Creating a pod that attaches an additional interface
We're going to create a pod. This will look familiar as any pod you might have created before, but, we'll have a special `annotations` field -- in this case we'll have an annotation called `k8s.v1.cni.cncf.io/networks`. This field takes a comma delimited list of the names of your `NetworkAttachmentDefinition`s as we created above. Note in the comand below that we have the annotation of `k8s.v1.cni.cncf.io/networks: macvlan-conf` where `macvlan-conf` is the name we used above when we created our configuration.
We're going to create a pod. This will look familiar as any pod you might have created before, but, we'll have a special `annotations` field -- in this case we'll have an annotation called `k8s.v1.cni.cncf.io/networks`. This field takes a comma delimited list of the names of your `NetworkAttachmentDefinition`s as we created above. Note in the command below that we have the annotation of `k8s.v1.cni.cncf.io/networks: macvlan-conf` where `macvlan-conf` is the name we used above when we created our configuration.
Let's go ahead and create a pod (that just sleeps for a really long time) with this command:
@@ -166,18 +168,18 @@ metadata:
spec:
containers:
- name: samplepod
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
image: dougbtv/centos-network
command: ["/bin/ash", "-c", "trap : TERM INT; sleep infinity & wait"]
image: alpine
EOF
```
You may now inspect the pod and see what interfaces interfaces are attached, like so:
You may now inspect the pod and see what interfaces are attached, like so:
```
$ kubectl exec -it samplepod -- ip a
```
You should note that there's 3 interfaces:
You should note that there are 3 interfaces:
* `lo` a loopback interface
* `eth0` our default network
@@ -225,8 +227,8 @@ metadata:
spec:
containers:
- name: samplepod
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
image: dougbtv/centos-network
command: ["/bin/ash", "-c", "trap : TERM INT; sleep infinity & wait"]
image: alpine
EOF
```

18
e2e/README.md Normal file
View File

@@ -0,0 +1,18 @@
## Multus e2e test with kind
### How to test e2e
```
$ 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
```

64
e2e/cni-install.yml Normal file
View File

@@ -0,0 +1,64 @@
---
kind: ConfigMap
apiVersion: v1
metadata:
name: cni-install-sh
namespace: kube-system
data:
install_cni.sh: |
cd /tmp
wget https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-amd64-v0.8.5.tgz
cd /host/opt/cni/bin
tar xvfzp /tmp/cni-plugins-linux-amd64-v0.8.5.tgz
sleep infinite
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: install-cni-plugins
namespace: kube-system
labels:
name: cni-plugins
spec:
selector:
matchLabels:
name: cni-plugins
template:
metadata:
labels:
name: cni-plugins
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/arch: amd64
tolerations:
- operator: Exists
effect: NoSchedule
containers:
- name: install-cni-plugins
image: alpine
command: ["/bin/sh", "/scripts/install_cni.sh"]
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: true
volumeMounts:
- name: cni-bin
mountPath: /host/opt/cni/bin
- name: scripts
mountPath: /scripts
volumes:
- name: cni-bin
hostPath:
path: /opt/cni/bin
- name: scripts
configMap:
name: cni-install-sh
items:
- key: install_cni.sh
path: install_cni.sh

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

@@ -0,0 +1,57 @@
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: default-route-config
spec:
config: '{
"cniVersion": "0.3.1",
"plugins": [
{
"type": "macvlan",
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "static"
}
} ]
}'
---
apiVersion: v1
kind: Pod
metadata:
name: default-route-worker1
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "default-route-config",
"ips": [ "10.1.1.21/24" ] ,
"default-route": [ "10.1.1.254" ] }
]'
labels:
app: default-route1
spec:
containers:
- name: default-route-worker1
image: centos:8
command: ["/bin/sleep", "10000"]
securityContext:
privileged: true
---
apiVersion: v1
kind: Pod
metadata:
name: default-route-worker2
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "default-route-config",
"ips": [ "10.1.1.22/24" ] }
]'
labels:
app: default-route1
spec:
containers:
- name: default-route-worker2
image: centos:8
command: ["/bin/sleep", "10000"]
securityContext:
privileged: true

15
e2e/get_tools.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/sh
set -o errexit
if [ ! -d bin ]; then
mkdir bin
fi
curl -Lo ./bin/kind "https://github.com/kubernetes-sigs/kind/releases/download/v0.8.1/kind-$(uname)-amd64"
chmod +x ./bin/kind
curl -Lo ./bin/kubectl https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
chmod +x ./bin/kubectl
curl -Lo ./bin/koko https://github.com/redhat-nfvpe/koko/releases/download/v0.82/koko_0.82_linux_amd64
chmod +x ./bin/koko
curl -Lo ./bin/jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
chmod +x ./bin/jq

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

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

68
e2e/setup_cluster.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/bin/sh
set -o errexit
export PATH=${PATH}:./bin
kind_network='kind'
reg_name='kind-registry'
reg_port='5000'
running="$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)"
if [ "${running}" != 'true' ]; then
# run registry and push the multus image
docker run -d --restart=always -p "${reg_port}:5000" --name "${reg_name}" registry:2
docker build -t localhost:5000/multus:e2e ..
docker push localhost:5000/multus:e2e
fi
reg_host="${reg_name}"
if [ "${kind_network}" = "bridge" ]; then
reg_host="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' "${reg_name}")"
fi
echo "Registry Host: ${reg_host}"
# deploy cluster with kind
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
endpoint = ["http://${reg_host}:${reg_port}"]
nodes:
- role: control-plane
- role: worker
- 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=$(docker 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
docker network connect "${kind_network}" "${reg_name}" || true
fi
kind export kubeconfig
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-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

63
e2e/simple-macvlan1.yml Normal file
View File

@@ -0,0 +1,63 @@
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan1-config
spec:
config: '{
"cniVersion": "0.3.1",
"plugins": [
{
"type": "macvlan",
"capabilities": { "ips": true },
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "static"
}
}, {
"type": "tuning"
} ]
}'
---
apiVersion: v1
kind: Pod
metadata:
name: macvlan1-worker1
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "macvlan1-config",
"ips": [ "10.1.1.11/24" ] }
]'
labels:
app: macvlan
spec:
containers:
- name: macvlan-worker1
image: centos:8
command: ["/bin/sleep", "10000"]
securityContext:
privileged: true
nodeSelector:
kubernetes.io/hostname: kind-worker
---
apiVersion: v1
kind: Pod
metadata:
name: macvlan1-worker2
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "macvlan1-config",
"ips": [ "10.1.1.12/24" ] }
]'
labels:
app: macvlan
spec:
containers:
- name: macvlan-worker2
image: centos:8
command: ["/bin/sleep", "10000"]
securityContext:
privileged: true
nodeSelector:
kubernetes.io/hostname: kind-worker2

10
e2e/teardown.sh Executable file
View File

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

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

@@ -0,0 +1,44 @@
#!/bin/sh
set -o errexit
export PATH=${PATH}:./bin
kubectl create -f default-route1.yml
kubectl wait --for=condition=ready -l app=default-route1 --timeout=300s pod
echo "check default-route-worker1 interface: net1"
kubectl exec default-route-worker1 -- ip a show dev net1
echo "check default-route-worker1 interface address: net1"
ipaddr=$(kubectl exec default-route-worker1 -- ip -j a show | jq -r \
'.[]|select(.ifname =="net1")|.addr_info[]|select(.family=="inet").local')
if [ $ipaddr != "10.1.1.21" ]; then
echo "default-route-worker1 IP address is different: ${ipaddr}"
fi
echo "check default-route-worker1 default route"
ipaddr=$(kubectl exec default-route-worker1 -- ip -j route | jq -r \
'.[]|select(.dst=="default")|.gateway')
if [ $ipaddr != "10.1.1.254" ]; then
echo "default-route-worker1 default route is different: ${ipaddr}"
fi
echo "check default-route-worker2 interface: net1"
kubectl exec default-route-worker2 -- ip a show dev net1
echo "check default-route-worker2 interface address: net1"
ipaddr=$(kubectl exec default-route-worker2 -- ip -j a show | jq -r \
'.[]|select(.ifname =="net1")|.addr_info[]|select(.family=="inet").local')
if [ $ipaddr != "10.1.1.22" ]; then
echo "default-route-worker2 IP address is different: ${ipaddr}"
fi
echo "check default-route-worker2 default route"
ipaddr=$(kubectl exec default-route-worker2 -- ip -j route | jq -r \
'.[]|select(.dst=="default")|.gateway')
if [ $ipaddr != "10.244.1.1" ]; then
echo "default-route-worker2 default route is different: ${ipaddr}"
fi
echo "cleanup resources"
kubectl delete -f default-route1.yml

30
e2e/test-simple-macvlan1.sh Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/sh
set -o errexit
export PATH=${PATH}:./bin
kubectl create -f simple-macvlan1.yml
kubectl wait --for=condition=ready -l app=macvlan --timeout=300s pod
echo "check macvlan1-worker1 interface: net1"
kubectl exec macvlan1-worker1 -- ip a show dev net1
echo "check macvlan1-worker1 interface address: net1"
ipaddr=$(kubectl exec macvlan1-worker1 -- ip -j a show | jq -r \
'.[]|select(.ifname =="net1")|.addr_info[]|select(.family=="inet").local')
if [ $ipaddr != "10.1.1.11" ]; then
echo "macvlan1-worker1 IP address is different: ${ipaddr}"
fi
echo "check macvlan1-worker2 interface: net1"
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 \
'.[]|select(.ifname =="net1")|.addr_info[]|select(.family=="inet").local')
if [ $ipaddr != "10.1.1.12" ]; then
echo "macvlan1-worker2 IP address is different: ${ipaddr}"
fi
echo "cleanup resources"
kubectl delete -f simple-macvlan1.yml

View File

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

View File

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

View File

@@ -1,21 +0,0 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: network-attachment-definitions.k8s.cni.cncf.io
spec:
group: k8s.cni.cncf.io
version: v1
scope: Namespaced
names:
plural: network-attachment-definitions
singular: network-attachment-definition
kind: NetworkAttachmentDefinition
shortNames:
- net-attach-def
validation:
openAPIV3Schema:
properties:
spec:
properties:
config:
type: string

View File

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

View File

@@ -1,21 +0,0 @@
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-conf
spec:
config: '{
"cniVersion": "0.3.0",
"type": "macvlan",
"master": "eth0",
"mode": "bridge",
"ipam": {
"type": "host-local",
"subnet": "192.168.1.0/24",
"rangeStart": "192.168.1.200",
"rangeEnd": "192.168.1.216",
"routes": [
{ "dst": "0.0.0.0/0" }
],
"gateway": "192.168.1.1"
}
}'

56
examples/macvlan-pod.yml Normal file
View File

@@ -0,0 +1,56 @@
---
# This net-attach-def defines macvlan-conf with
# + ips capabilities to specify ip in pod annotation and
# + mac capabilities to specify mac address in pod annotation
# default gateway is defined as well
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-conf
spec:
config: '{
"cniVersion": "0.3.1",
"plugins": [
{
"type": "macvlan",
"capabilities": { "ips": true },
"master": "eth0",
"mode": "bridge",
"ipam": {
"type": "static",
"routes": [
{
"dst": "0.0.0.0/0",
"gw": "10.1.1.1"
}
]
}
}, {
"capabilities": { "mac": true },
"type": "tuning"
}
]
}'
---
# Define a pod with macvlan-conf, defined above, with ip address and mac, and
# "gateway" overrides default gateway to use macvlan-conf's one.
# without "gateway" in k8s.v1.cni.cncf.io/networks, default route will be cluster
# network interface, eth0, even tough macvlan-conf has default gateway config.
apiVersion: v1
kind: Pod
metadata:
name: samplepod
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "macvlan-conf",
"ips": [ "10.1.1.101/24" ],
"mac": "c2:b0:57:49:47:f1",
"gateway": [ "10.1.1.1" ]
}]'
spec:
containers:
- name: samplepod
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
image: dougbtv/centos-network
ports:
- containerPort: 80

View File

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

View File

@@ -1,163 +0,0 @@
# -----------------------------------------------
# - Example Configuration Deployment
# -----------------------------------------------
# - Deploys a .conf file on each node
# - Configured for Multus + Flannel.
# - As well as assets for Flannel
# - Based on https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml
# -----------------------------------------------
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: flannel
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-multus-cfg
namespace: kube-system
labels:
tier: node
app: multus
data:
cni-conf.json: |
{
"name": "multus-cni-network",
"type": "multus",
"delegates": [
{
"type": "flannel",
"delegate": {
"isDefaultGateway": true
}
}
],
"kubeconfig": "/etc/kubernetes/kubelet.conf"
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: kube-multus-ds
namespace: kube-system
labels:
tier: node
app: multus
spec:
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: multus
spec:
hostNetwork: true
nodeSelector:
beta.kubernetes.io/arch: amd64
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: flannel
initContainers:
- name: install-cni
image: quay.io/coreos/flannel:v0.10.0-amd64
command:
- cp
args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-multus-with-flannel.conf
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: multus-cfg
mountPath: /etc/kube-flannel/
containers:
- name: kube-flannel
image: quay.io/coreos/flannel:v0.10.0-amd64
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: run
mountPath: /run
- name: multus-cfg
mountPath: /etc/kube-flannel/
volumes:
- name: run
hostPath:
path: /run
- name: cni
hostPath:
path: /etc/cni/net.d
- name: multus-cfg
configMap:
name: kube-multus-cfg

View File

@@ -1,21 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: testpod1
labels:
env: test
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net-a
spec:
containers:
- name: appcntr1
image: centos/tools
imagePullPolicy: IfNotPresent
command: [ "/bin/bash", "-c", "--" ]
args: [ "while true; do sleep 300000; done;" ]
resources:
requests:
intel.com/sriov: '1'
limits:
intel.com/sriov: '1'
restartPolicy: "Never"

View File

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

View File

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

View File

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

View File

@@ -1,72 +0,0 @@
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-conf-1
spec:
config: '{
"cniVersion": "0.3.0",
"type": "macvlan",
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "static",
"addresses": [
{ "address": "10.1.1.101/24" }
]
}
}'
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-conf-2
spec:
config: '{
"cniVersion": "0.3.0",
"type": "macvlan",
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "static",
"addresses": [
{ "address": "10.1.1.102/24" }
]
}
}'
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-conf-3
spec:
config: '{
"cniVersion": "0.3.0",
"type": "macvlan",
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "static",
"addresses": [
{ "address": "10.1.1.103/24" }
]
}
}'
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-conf-4
spec:
config: '{
"cniVersion": "0.3.0",
"type": "macvlan",
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "static",
"addresses": [
{ "address": "10.1.1.104/24" }
]
}
}'

View File

@@ -1,19 +0,0 @@
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: vlan-conf-1-1
namespace: testns1
spec:
config: '{
"cniVersion": "0.3.0",
"type": "vlan",
"master": "eth1",
"vlanid": 1,
"ipam": {
"type": "static",
"addresses": [
{ "address": "172.16.1.101/24"
} ]
}
}'

View File

@@ -1,196 +0,0 @@
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: flannel2
namespace: kube-system
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: flannel2
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel2
subjects:
- kind: ServiceAccount
name: flannel2
namespace: kube-system
---
kind: ServiceAccount
apiVersion: v1
metadata:
name: flannel2
namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-flannel2-cfg
namespace: kube-system
labels:
tier: node
app: flannel2
data:
flannel2-conf.json: |
{
"type": "flannel",
"name": "flannel-2",
"subnetFile": "/run/flannel/flannel2.env",
"dataDir": "/var/lib/cni/flannel2",
"delegate": {
"bridge": "kbr1"
}
}
net-conf.json: |
{
"Network": "10.144.0.0/16",
"SubnetLen": 24,
"SubnetMin": "10.144.0.0",
"Backend": {
"Type": "vxlan"
}
}
---
apiVersion: v1
kind: Pod
metadata:
name: flannel-etcd
namespace: kube-system
spec:
containers:
- command:
- etcd
- --advertise-client-urls=http://10.1.1.1:12379
- --listen-client-urls=http://0.0.0.0:12379
- --listen-peer-urls=http://localhost:12380
image: quay.io/coreos/etcd:latest
name: etcd
hostNetwork: true
nodeName: kube-master
---
apiVersion: batch/v1
kind: Job
metadata:
name: flannel-etcdctl
namespace: kube-system
spec:
template:
spec:
containers:
- name: flannel-etcdctl
image: quay.io/coreos/etcd:latest
command: ["etcdctl"]
args: ["--endpoints=http://10.1.1.1:12379", "set", "/flannel2/network/config", '{ "Network": "10.5.0.0/16", "Backend": {"Type": "vxlan", "VNI": 2}}']
hostNetwork: true
nodeName: kube-master
restartPolicy: Never
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: kube-flannel2-ds
namespace: kube-system
labels:
tier: node
app: flannel2
spec:
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: flannel2
spec:
hostNetwork: true
nodeSelector:
beta.kubernetes.io/arch: amd64
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
serviceAccountName: flannel2
initContainers:
- name: install-cni
image: quay.io/coreos/flannel:v0.10.0-amd64
command:
- cp
args:
- -f
- /etc/kube-flannel/flannel2-conf.json
- /etc/cni/multus/net.d/10-flannel.conf
volumeMounts:
- name: cni
mountPath: /etc/cni/multus/net.d
- name: flannel2-cfg
mountPath: /etc/kube-flannel/
containers:
- name: kube-flannel2
image: quay.io/coreos/flannel:v0.10.0-amd64
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --etcd-endpoints=http://10.1.1.1:12379
- -iface=eth1
- -subnet-file=/run/flannel/flannel2.env
- -etcd-prefix=/flannel2/network
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: run
mountPath: /run
volumes:
- name: run
hostPath:
path: /run
- name: cni
hostPath:
path: /etc/cni/multus/net.d
- name: flannel2-cfg
configMap:
name: kube-flannel2-cfg
---
apiVersion: "kubernetes.cni.cncf.io/v1"
kind: Network
metadata:
name: flannel-2

View File

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

View File

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

View File

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

View File

@@ -1,17 +0,0 @@
---
apiVersion: v1
kind: Pod
metadata:
name: pod-case-03
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "macvlan-conf-3" },
{ "name": "macvlan-conf-4" },
{ "name": "flannel-2" }
]'
spec:
containers:
- name: pod-case-03
image: docker.io/centos/tools:latest
command:
- /sbin/init

View File

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

View File

@@ -1,15 +0,0 @@
---
apiVersion: v1
kind: Pod
metadata:
name: pod-case-05
annotations:
k8s.v1.cni.cncf.io/networks: '[
{ "name": "ptp-tuning-conflist" }
]'
spec:
containers:
- name: pod-case-05
image: docker.io/centos/tools:latest
command:
- /sbin/init

View File

@@ -1,14 +0,0 @@
---
apiVersion: v1
kind: Pod
metadata:
name: samplepod
annotations:
k8s.v1.cni.cncf.io/networks: macvlan-conf
spec:
containers:
- name: samplepod
command: ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
image: dougbtv/centos-network
ports:
- containerPort: 80

View File

@@ -1,21 +0,0 @@
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: sriov-net-a
annotations:
k8s.v1.cni.cncf.io/resourceName: intel.com/sriov
spec:
config: '{
"type": "sriov",
"vlan": 1000,
"ipam": {
"type": "host-local",
"subnet": "10.56.217.0/24",
"rangeStart": "10.56.217.171",
"rangeEnd": "10.56.217.181",
"routes": [{
"dst": "0.0.0.0/0"
}],
"gateway": "10.56.217.1"
}
}'

47
examples/sriov-pod.yml Normal file
View File

@@ -0,0 +1,47 @@
# This net-attach-def defines SR-IOV CNI config
# Please see https://github.com/intel/sriov-cni and https://github.com/intel/sriov-network-device-plugin
# for its detail.
---
apiVersion: "k8s.cni.cncf.io/v1"
kind: NetworkAttachmentDefinition
metadata:
name: sriov-net-a
annotations:
k8s.v1.cni.cncf.io/resourceName: intel.com/sriov
spec:
config: '{
"type": "sriov",
"vlan": 1000,
"ipam": {
"type": "host-local",
"subnet": "10.56.217.0/24",
"rangeStart": "10.56.217.171",
"rangeEnd": "10.56.217.181",
"routes": [{
"dst": "0.0.0.0/0"
}],
"gateway": "10.56.217.1"
}
}'
---
apiVersion: v1
kind: Pod
metadata:
name: testpod1
labels:
env: test
annotations:
k8s.v1.cni.cncf.io/networks: sriov-net-a
spec:
containers:
- name: appcntr1
image: centos/tools
imagePullPolicy: IfNotPresent
command: [ "/bin/bash", "-c", "--" ]
args: [ "while true; do sleep 300000; done;" ]
resources:
requests:
intel.com/sriov: '1'
limits:
intel.com/sriov: '1'
restartPolicy: "Never"

20
go.mod
View File

@@ -1,4 +1,4 @@
module github.com/intel/multus-cni
module gopkg.in/intel/multus-cni.v3
go 1.12
@@ -6,17 +6,21 @@ require (
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/containernetworking/cni v0.7.1
github.com/containernetworking/plugins v0.8.2
github.com/golang/protobuf v1.3.2 // indirect
github.com/k8snetworkplumbingwg/network-attachment-definition-client v0.0.0-20191025120722-4c57cd5732f3
github.com/onsi/ginkgo v1.10.1
github.com/json-iterator/go v1.1.9 // indirect
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.1.1-0.20201119153432-9d213757d22d
github.com/onsi/ginkgo v1.11.0
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/net v0.0.0-20190930134127-c5a3c61f89f3
golang.org/x/net v0.0.0-20201021035429-f5854403a974
google.golang.org/grpc v1.23.0
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
gopkg.in/natefinch/lumberjack.v2 v2.0.0
k8s.io/api v0.18.3
k8s.io/apimachinery v0.18.3
k8s.io/client-go v0.18.3
k8s.io/klog v1.0.0
k8s.io/kubernetes v1.13.0
)
replace github.com/gogo/protobuf => github.com/gogo/protobuf v1.3.2

236
go.sum
View File

@@ -1,12 +1,24 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
@@ -27,57 +39,90 @@ github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjI
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.10.0+incompatible h1:l6Soi8WCOOVAeCo4W98iBFC6Og7/X8bpRt51oNLZ2C8=
github.com/emicklei/go-restful v2.10.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.1.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE=
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf h1:+RRA9JqSOZFfKrOeqr2z77+8R2RKyh8PG66dcu1V0ck=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.1.0 h1:rVsPeBmXbYv4If/cumu1AzZPwV58q433hvONV1UEZoI=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f h1:ShTPMJQes6tubcjzGMODIVG5hlrCeImaBnZzKF2N8SM=
github.com/gregjones/httpcache v0.0.0-20181110185634-c63ab54fda8f/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.0.0-20171009183408-7fe0c75c13ab h1:k/Biv+LJL35wkk0Hveko1nj7as4tSHkHdZaNlzn/gcQ=
github.com/imdario/mergo v0.0.0-20171009183408-7fe0c75c13ab/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3 h1:/UewZcckqhvnnS0C6r3Sher2hSEbVmM6Ogpcjen08+Y=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/juju/errors v0.0.0-20180806074554-22422dad46e1/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
github.com/juju/testing v0.0.0-20190613124551-e81189438503/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
github.com/k8snetworkplumbingwg/network-attachment-definition-client v0.0.0-20191025120722-4c57cd5732f3 h1:rGn6h+Qy9GUP4D0MKqzCCmEzuyFbm0JRoui+FzqKKzE=
github.com/k8snetworkplumbingwg/network-attachment-definition-client v0.0.0-20191025120722-4c57cd5732f3/go.mod h1:nIUH9A0XLvWfHgOUq1MX3SuDsZ7qUf6xUdshzdKt0pU=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.1.1-0.20201119153432-9d213757d22d h1:9QbZltQGRFe7temwcTDjj8rIbow48Gv6mIKOxuks+OI=
github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.1.1-0.20201119153432-9d213757d22d/go.mod h1:+1DpV8uIwteAhxNO0lgRox8gHkTG6w3OeDfAlg+qqjA=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@@ -86,21 +131,31 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b h1:Ey6yH0acn50T/v6CB75bGP4EMJqnv9WvnjN7oZaj+xE=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a h1:KfNOeFvoAssuZLT7IntKZElKwi/5LRuxY71k+t6rfaM=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
@@ -109,16 +164,16 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -132,68 +187,110 @@ github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I=
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3 h1:6KET3Sqa7fkVfD63QnAM81ZeYg5n4HwApOJkufONnHA=
golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 h1:JIqe8uIcRBHXDQVvZtHwp80ai3Lw3IJAeJEs55Dc1W0=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190927073244-c990c680b611 h1:q9u40nxWT5zRClI/uU9dHCiYGottAg6Nzz4YUQyHxdA=
golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0 h1:7+F62GGWUowoiJOUDivedlBECd/fTeUDJnCu0JetQO0=
golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
@@ -205,39 +302,46 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.0.0-20181115043458-b799cb063522 h1:JGWVL/WnKVncCXESoe8wCBZJbXbUvYi+AuSHJ/IuFN8=
k8s.io/api v0.0.0-20181115043458-b799cb063522/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apimachinery v0.0.0-20181110190943-2a7c93004028 h1:5i4gnQYlGFIWZp34j0qnt3eeCEFs2ymFzRhzFOPG5/U=
k8s.io/apimachinery v0.0.0-20181110190943-2a7c93004028/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v0.0.0-20181115111358-9bea17718df8 h1:Q1pEgTyAmkwHY+lScBj4VZaGP3ykzGUoP9qC0/JxbEk=
k8s.io/client-go v0.0.0-20181115111358-9bea17718df8/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/code-generator v0.0.0-20181114232248-ae218e241252 h1:bURaCtXd+lhw5buMNdeUig98d3Eq97Muux1hUFvkqb0=
k8s.io/code-generator v0.0.0-20181114232248-ae218e241252/go.mod h1:IPqxl/YHk05nodzupwjke6ctMjyNRdV2zZ5/j3/F204=
k8s.io/gengo v0.0.0-20181106084056-51747d6e00da/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190907103519-ebc107f98eab h1:j4L8spMe0tFfBvvW6lrc0c+Ql8+nnkcV3RYfi3eSwGY=
k8s.io/gengo v0.0.0-20190907103519-ebc107f98eab/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20190306015804-8e90cee79f82/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/api v0.18.3 h1:2AJaUQdgUZLoDZHrun21PW2Nx9+ll6cUzvn3IKhSIn0=
k8s.io/api v0.18.3/go.mod h1:UOaMwERbqJMfeeeHc8XJKawj4P9TgDRnViIqqBeH2QA=
k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk=
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
k8s.io/client-go v0.18.3 h1:QaJzz92tsN67oorwzmoB0a9r9ZVHuD5ryjbCKP0U22k=
k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
k8s.io/code-generator v0.18.3 h1:5H57pYEbkMMXCLKD16YQH3yDPAbVLweUsB1M3m70D1c=
k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120 h1:RPscN6KhmG54S33L+lr3GS+oD1jmchIU0ll519K6FA4=
k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be h1:aWEq4nbj7HRJ0mtKYjNSk/7X28Tl6TI6FeG8gKF+r7Q=
k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6 h1:Oh3Mzx5pJ+yIumsAD0MOECPVeXsVot0UkiaCGVyfGQY=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8=
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -19,18 +19,18 @@ if [ -z "$VERSION" ]; then
fi
set -e
fi
DATE=$(date --iso-8601=seconds)
DATE=$(date -u -d "@${SOURCE_DATE_EPOCH:-$(date +%s)}" --iso-8601=seconds)
COMMIT=${COMMIT:-$(git rev-parse --verify HEAD)}
LDFLAGS="-X main.version=${VERSION:-master} -X main.commit=${COMMIT} -X main.date=${DATE}"
export CGO_ENABLED=1
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="github.com/intel"
REPO_PATH="${ORG_PATH}/multus-cni"
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}
@@ -40,11 +40,15 @@ if [ "$GO111MODULE" == "off" ]; then
export GO15VENDOREXPERIMENT=1
export GOBIN=${PWD}/bin
export GOPATH=${PWD}/gopath
go install -ldflags "${LDFLAGS}" "$@" ${REPO_PATH}/multus
go build -o ${PWD}/bin/multus -tags no_openssl -ldflags "${LDFLAGS}" "$@" ${REPO_PATH}/cmd
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 -o ${DEST_DIR}/multus -ldflags "${LDFLAGS}" "$@" ./multus
go build ${BUILD_ARGS[*]} -ldflags "${LDFLAGS}" "$@" ./cmd
fi

View File

@@ -5,7 +5,6 @@ metadata:
name: network-attachment-definitions.k8s.cni.cncf.io
spec:
group: k8s.cni.cncf.io
version: v1
scope: Namespaced
names:
plural: network-attachment-definitions
@@ -13,13 +12,24 @@ spec:
kind: NetworkAttachmentDefinition
shortNames:
- net-attach-def
validation:
openAPIV3Schema:
properties:
spec:
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:
config:
type: string
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/v1beta1
@@ -39,6 +49,15 @@ rules:
verbs:
- get
- update
- apiGroups:
- ""
- events.k8s.io
resources:
- events
verbs:
- create
- patch
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
@@ -135,7 +154,7 @@ spec:
containers:
- name: kube-multus
# crio support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest
image: nfvpe/multus:v3.6
command: ["/entrypoint.sh"]
args:
- "--cni-bin-dir=/host/usr/libexec/cni"

View File

@@ -0,0 +1,232 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: network-attachment-definitions.k8s.cni.cncf.io
spec:
group: k8s.cni.cncf.io
version: v1
scope: Namespaced
names:
plural: network-attachment-definitions
singular: network-attachment-definition
kind: NetworkAttachmentDefinition
shortNames:
- net-attach-def
validation:
openAPIV3Schema:
properties:
spec:
properties:
config:
type: string
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: multus
rules:
- apiGroups: ["k8s.cni.cncf.io"]
resources:
- '*'
verbs:
- '*'
- apiGroups:
- ""
resources:
- pods
- pods/status
verbs:
- get
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: multus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: multus
subjects:
- kind: ServiceAccount
name: multus
namespace: kube-system
---
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: extensions/v1beta1
kind: DaemonSet
metadata:
name: kube-multus-ds-amd64
namespace: kube-system
labels:
tier: node
app: multus
spec:
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: multus
spec:
hostNetwork: true
nodeSelector:
beta.kubernetes.io/arch: amd64
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
image: nfvpe/multus:v3.6
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
- "--cni-bin-dir=/host/home/kubernetes/bin"
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/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
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: kube-multus-ds-ppc64le
namespace: kube-system
labels:
tier: node
app: multus
spec:
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
tier: node
app: multus
spec:
hostNetwork: true
nodeSelector:
beta.kubernetes.io/arch: 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-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

@@ -5,7 +5,6 @@ metadata:
name: network-attachment-definitions.k8s.cni.cncf.io
spec:
group: k8s.cni.cncf.io
version: v1
scope: Namespaced
names:
plural: network-attachment-definitions
@@ -13,13 +12,24 @@ spec:
kind: NetworkAttachmentDefinition
shortNames:
- net-attach-def
validation:
openAPIV3Schema:
properties:
spec:
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:
config:
type: string
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/v1beta1
@@ -39,6 +49,15 @@ rules:
verbs:
- get
- update
- apiGroups:
- ""
- events.k8s.io
resources:
- events
verbs:
- create
- patch
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
@@ -134,7 +153,7 @@ spec:
serviceAccountName: multus
containers:
- name: kube-multus
image: nfvpe/multus:v3.2
image: nfvpe/multus:v3.6
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"

View File

@@ -3,6 +3,13 @@
# Always exit on errors.
set -e
# Trap sigterm
function exitonsigterm() {
echo "Trapped sigterm, exiting."
exit 0
}
trap exitonsigterm SIGTERM
# Set our known directories.
CNI_CONF_DIR="/host/etc/cni/net.d"
CNI_BIN_DIR="/host/opt/cni/bin"
@@ -12,6 +19,8 @@ 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_NAMESPACE_ISOLATION=false
MULTUS_GLOBAL_NAMESPACES=""
MULTUS_LOG_TO_STDERR=true
MULTUS_LOG_LEVEL=""
MULTUS_LOG_FILE=""
MULTUS_READINESS_INDICATOR_FILE=""
@@ -42,7 +51,9 @@ function usage()
echo -e "\t--skip-multus-binary-copy=$SKIP_BINARY_COPY"
echo -e "\t--multus-kubeconfig-file-host=$MULTUS_KUBECONFIG_FILE_HOST"
echo -e "\t--namespace-isolation=$MULTUS_NAMESPACE_ISOLATION"
echo -e "\t--global-namespaces=$MULTUS_GLOBAL_NAMESPACES (used only with --namespace-isolation=true)"
echo -e "\t--multus-autoconfig-dir=$MULTUS_AUTOCONF_DIR (used only with --multus-conf-file=auto)"
echo -e "\t--multus-log-to-stderr=$MULTUS_LOG_TO_STDERR (empty by default, used only with --multus-conf-file=auto)"
echo -e "\t--multus-log-level=$MULTUS_LOG_LEVEL (empty by default, used only with --multus-conf-file=auto)"
echo -e "\t--multus-log-file=$MULTUS_LOG_FILE (empty by default, used only with --multus-conf-file=auto)"
echo -e "\t--override-network-name=false (used only with --multus-conf-file=auto)"
@@ -68,6 +79,10 @@ function warn()
log "WARN: {$1}"
}
if ! type python3 &> /dev/null; then
alias python=python3
fi
# Parse parameters given as arguments to this script.
while [ "$1" != "" ]; do
PARAM=`echo $1 | awk -F= '{print $1}'`
@@ -98,6 +113,12 @@ while [ "$1" != "" ]; do
--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
;;
@@ -206,7 +227,7 @@ kind: Config
clusters:
- name: local
cluster:
server: ${KUBERNETES_SERVICE_PROTOCOL:-https}://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}
server: ${KUBERNETES_SERVICE_PROTOCOL:-https}://[${KUBERNETES_SERVICE_HOST}]:${KUBERNETES_SERVICE_PORT}
$TLS_CFG
users:
- name: multus
@@ -255,6 +276,17 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
ISOLATION_STRING="\"namespaceIsolation\": true,"
fi
GLOBAL_NAMESPACES_STRING=""
if [ ! -z "${MULTUS_GLOBAL_NAMESPACES// }" ]; then
GLOBAL_NAMESPACES_STRING="\"globalNamespaces\": \"$MULTUS_GLOBAL_NAMESPACES\","
fi
LOG_TO_STDERR_STRING=""
if [ "$MULTUS_LOG_TO_STDERR" == false ]; then
LOG_TO_STDERR_STRING="\"logToStderr\": false,"
fi
LOG_LEVEL_STRING=""
if [ ! -z "${MULTUS_LOG_LEVEL// }" ]; then
case "$MULTUS_LOG_LEVEL" in
@@ -297,11 +329,32 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
if [ "$OVERRIDE_NETWORK_NAME" == "true" ]; then
MASTER_PLUGIN_NET_NAME="$(cat $MULTUS_AUTOCONF_DIR/$MASTER_PLUGIN | \
python -c 'import json,sys;print json.load(sys.stdin)["name"]')"
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 | \
python $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"
@@ -310,7 +363,10 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
$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
@@ -322,7 +378,9 @@ if [ "$MULTUS_CONF_FILE" == "auto" ]; then
}
EOF
)
echo $CONF > $CNI_CONF_DIR/00-multus.conf
tmpfile=$(mktemp)
echo $CONF > $tmpfile
mv $tmpfile $CNI_CONF_DIR/00-multus.conf
log "Config file created @ $CNI_CONF_DIR/00-multus.conf"
echo $CONF
@@ -372,5 +430,9 @@ if [ "$MULTUS_CLEANUP_CONFIG_ON_EXIT" == true ]; then
done
else
log "Entering sleep (success)..."
sleep infinity
fi
if tty -s; then
read
else
sleep infinity
fi
fi

View File

@@ -18,12 +18,30 @@ spec:
storage: true
schema:
openAPIV3Schema:
description: 'NetworkAttachmentDefinition is a CRD schema specified by the Network Plumbing
Working Group to express the intent for attaching pods to one or more logical or physical
networks. More information available at: https://github.com/k8snetworkplumbingwg/multi-net-spec'
type: object
properties:
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
@@ -44,6 +62,15 @@ rules:
verbs:
- get
- update
- apiGroups:
- ""
- events.k8s.io
resources:
- events
verbs:
- create
- patch
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -145,7 +172,7 @@ spec:
containers:
- name: kube-multus
# crio support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest
image: docker.io/nfvpe/multus:stable
command: ["/entrypoint.sh"]
args:
- "--cni-version=0.3.1"
@@ -172,6 +199,7 @@ spec:
mountPath: /host/usr/libexec/cni
- name: multus-cfg
mountPath: /tmp/multus-conf
terminationGracePeriodSeconds: 10
volumes:
- name: run
hostPath:
@@ -221,7 +249,7 @@ spec:
containers:
- name: kube-multus
# crio support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest-ppc64le
image: docker.io/nfvpe/multus:stable-ppc64le
command: ["/entrypoint.sh"]
args:
- "--cni-version=0.3.1"
@@ -244,6 +272,147 @@ spec:
mountPath: /host/usr/libexec/cni
- name: multus-cfg
mountPath: /tmp/multus-conf
terminationGracePeriodSeconds: 10
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
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds-arm64v8
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: arm64
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
# crio support requires multus:latest for now. support 3.3 or later.
image: docker.io/nfvpe/multus:stable-arm64v8
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
terminationGracePeriodSeconds: 10
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
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-multus-ds-s390x
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: s390x
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
# crio support requires multus:latest for now. support 3.3 or later.
image: docker.io/nfvpe/multus:stable-s390x
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
terminationGracePeriodSeconds: 10
volumes:
- name: cni
hostPath:

View File

@@ -0,0 +1,249 @@
---
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: docker.io/nfvpe/multus:stable
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: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
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
---
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: docker.io/nfvpe/multus:stable-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

@@ -18,12 +18,30 @@ spec:
storage: true
schema:
openAPIV3Schema:
description: 'NetworkAttachmentDefinition is a CRD schema specified by the Network Plumbing
Working Group to express the intent for attaching pods to one or more logical or physical
networks. More information available at: https://github.com/k8snetworkplumbingwg/multi-net-spec'
type: object
properties:
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
@@ -44,6 +62,15 @@ rules:
verbs:
- get
- update
- apiGroups:
- ""
- events.k8s.io
resources:
- events
verbs:
- create
- patch
- update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
@@ -144,7 +171,7 @@ spec:
serviceAccountName: multus
containers:
- name: kube-multus
image: nfvpe/multus:v3.3
image: docker.io/nfvpe/multus:stable
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
@@ -165,6 +192,7 @@ spec:
mountPath: /host/opt/cni/bin
- name: multus-cfg
mountPath: /tmp/multus-conf
terminationGracePeriodSeconds: 10
volumes:
- name: cni
hostPath:
@@ -211,7 +239,7 @@ spec:
containers:
- name: kube-multus
# ppc64le support requires multus:latest for now. support 3.3 or later.
image: nfvpe/multus:latest-ppc64le
image: docker.io/nfvpe/multus:stable-ppc64le
command: ["/entrypoint.sh"]
args:
- "--multus-conf-file=auto"
@@ -232,6 +260,141 @@ spec:
mountPath: /host/opt/cni/bin
- name: multus-cfg
mountPath: /tmp/multus-conf
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-arm64v8
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: arm64
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
image: docker.io/nfvpe/multus:stable-arm64v8
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
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-s390x
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: s390x
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: multus
containers:
- name: kube-multus
image: docker.io/nfvpe/multus:stable-s390x
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
terminationGracePeriodSeconds: 10
volumes:
- name: cni
hostPath:

View File

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

View File

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

View File

@@ -15,27 +15,35 @@
package k8sclient
import (
"context"
"encoding/json"
"fmt"
"net"
"os"
"regexp"
"strings"
"time"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/retry"
"k8s.io/client-go/tools/record"
"k8s.io/klog"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/skel"
cnitypes "github.com/containernetworking/cni/pkg/types"
"github.com/intel/multus-cni/kubeletclient"
"github.com/intel/multus-cni/logging"
"github.com/intel/multus-cni/types"
"gopkg.in/intel/multus-cni.v3/pkg/kubeletclient"
"gopkg.in/intel/multus-cni.v3/pkg/logging"
"gopkg.in/intel/multus-cni.v3/pkg/types"
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"
)
const (
@@ -51,48 +59,51 @@ type NoK8sNetworkError struct {
// ClientInfo contains information given from k8s client
type ClientInfo struct {
Client KubeClient
Podnamespace string
Podname string
Client kubernetes.Interface
NetClient netclient.K8sCniCncfIoV1Interface
EventBroadcaster record.EventBroadcaster
EventRecorder record.EventRecorder
}
// 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{})
}
// 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{})
}
// 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{})
}
// 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{})
}
// Eventf puts event into kubernetes events
func (c *ClientInfo) Eventf(object runtime.Object, eventtype, reason, messageFmt string, args ...interface{}) {
if c != nil && c.EventRecorder != nil {
c.EventRecorder.Eventf(object, eventtype, reason, messageFmt, args...)
}
}
func (e *NoK8sNetworkError) Error() string { return string(e.message) }
type defaultKubeClient struct {
client kubernetes.Interface
}
// defaultKubeClient implements KubeClient
var _ KubeClient = &defaultKubeClient{}
func (d *defaultKubeClient) GetRawWithPath(path string) ([]byte, error) {
return d.client.ExtensionsV1beta1().RESTClient().Get().AbsPath(path).DoRaw()
}
func (d *defaultKubeClient) GetPod(namespace, name string) (*v1.Pod, error) {
return d.client.Core().Pods(namespace).Get(name, metav1.GetOptions{})
}
func (d *defaultKubeClient) UpdatePodStatus(pod *v1.Pod) (*v1.Pod, error) {
return d.client.Core().Pods(pod.Namespace).UpdateStatus(pod)
}
func setKubeClientInfo(c *ClientInfo, client KubeClient, k8sArgs *types.K8sArgs) {
logging.Debugf("setKubeClientInfo: %v, %v, %v", c, client, k8sArgs)
c.Client = client
c.Podnamespace = string(k8sArgs.K8S_POD_NAMESPACE)
c.Podname = string(k8sArgs.K8S_POD_NAME)
}
// SetNetworkStatus sets network status into Pod annotation
func SetNetworkStatus(client KubeClient, k8sArgs *types.K8sArgs, netStatus []*types.NetworkStatus, conf *types.NetConf) error {
func SetNetworkStatus(client *ClientInfo, k8sArgs *types.K8sArgs, netStatus []nettypes.NetworkStatus, conf *types.NetConf) error {
var err error
logging.Debugf("SetNetworkStatus: %v, %v, %v, %v", client, k8sArgs, netStatus, conf)
client, err := GetK8sClient(conf.Kubeconfig, client)
client, err = GetK8sClient(conf.Kubeconfig, client)
if err != nil {
return logging.Errorf("SetNetworkStatus: %v", err)
}
if client == nil {
if client == nil || client.Client == nil {
if len(conf.Delegates) == 0 {
// No available kube client and no delegates, we can't do anything
return logging.Errorf("SetNetworkStatus: must have either Kubernetes config or delegates")
@@ -108,69 +119,16 @@ func SetNetworkStatus(client KubeClient, k8sArgs *types.K8sArgs, netStatus []*ty
return logging.Errorf("SetNetworkStatus: failed to query the pod %v in out of cluster comm: %v", podName, err)
}
var networkStatuses string
if netStatus != nil {
var networkStatus []string
for _, status := range netStatus {
// Clean each empty gateway
// Otherwise we wind up with JSON that's a "default-route": [""]
cleargateway := true
for _, eachgateway := range status.Gateway {
if eachgateway != nil {
cleargateway = false
}
}
if cleargateway {
status.Gateway = nil
}
// Now we can sanely marshal the JSON output
data, err := json.MarshalIndent(status, "", " ")
if err != nil {
return logging.Errorf("SetNetworkStatus: error with Marshal Indent: %v", err)
}
networkStatus = append(networkStatus, string(data))
err = netutils.SetNetworkStatus(client.Client, pod, netStatus)
if err != nil {
return logging.Errorf("SetNetworkStatus: failed to update the pod %v in out of cluster comm: %v", podName, err)
}
networkStatuses = fmt.Sprintf("[%s]", strings.Join(networkStatus, ","))
}
_, err = setPodNetworkAnnotation(client, podNamespace, pod, networkStatuses)
if err != nil {
return logging.Errorf("SetNetworkStatus: failed to update the pod %v in out of cluster comm: %v", podName, err)
}
return nil
}
func setPodNetworkAnnotation(client KubeClient, namespace string, pod *v1.Pod, networkstatus string) (*v1.Pod, error) {
logging.Debugf("setPodNetworkAnnotation: %v, %s, %v, %s", client, namespace, pod, networkstatus)
//if pod annotations is empty, make sure it allocatable
if len(pod.Annotations) == 0 {
pod.Annotations = make(map[string]string)
}
pod.Annotations["k8s.v1.cni.cncf.io/networks-status"] = networkstatus
pod = pod.DeepCopy()
var err error
if resultErr := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
if err != nil {
// Re-get the pod unless it's the first attempt to update
pod, err = client.GetPod(pod.Namespace, pod.Name)
if err != nil {
return err
}
}
pod, err = client.UpdatePodStatus(pod)
return err
}); resultErr != nil {
return nil, logging.Errorf("setPodNetworkAnnotation: status update failed for pod %s/%s: %v", pod.Namespace, pod.Name, resultErr)
}
return pod, nil
}
func parsePodNetworkObjectName(podnetwork string) (string, string, string, error) {
var netNsName string
var netIfName string
@@ -254,6 +212,12 @@ func parsePodNetworkAnnotation(podNetworks, defaultNamespace string) ([]*types.N
return nil, logging.Errorf("parsePodNetworkAnnotation: failed to mac: %v", err)
}
}
if n.InfinibandGUIDRequest != "" {
// validate GUID address
if _, err := net.ParseMAC(n.InfinibandGUIDRequest); err != nil {
return nil, logging.Errorf("parsePodNetworkAnnotation: failed to validate infiniband GUID: %v", err)
}
}
if n.IPRequest != nil {
for _, ip := range n.IPRequest {
// validate IP address
@@ -275,119 +239,16 @@ func parsePodNetworkAnnotation(podNetworks, defaultNamespace string) ([]*types.N
return networks, nil
}
func getCNIConfigFromFile(name string, confdir string) ([]byte, error) {
logging.Debugf("getCNIConfigFromFile: %s, %s", name, confdir)
func getKubernetesDelegate(client *ClientInfo, net *types.NetworkSelectionElement, confdir string, pod *v1.Pod, resourceMap map[string]*types.ResourceInfo) (*types.DelegateNetConf, map[string]*types.ResourceInfo, error) {
// In the absence of valid keys in a Spec, the runtime (or
// meta-plugin) should load and execute a CNI .configlist
// or .config (in that order) file on-disk whose JSON
// “name” key matches this Network objects name.
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#getDefaultCNINetwork
files, err := libcni.ConfFiles(confdir, []string{".conf", ".json", ".conflist"})
switch {
case err != nil:
return nil, logging.Errorf("getCNIConfigFromFile: no networks found in %s", confdir)
case len(files) == 0:
return nil, logging.Errorf("getCNIConfigFromFile: no networks found in %s", confdir)
}
for _, confFile := range files {
var confList *libcni.NetworkConfigList
if strings.HasSuffix(confFile, ".conflist") {
confList, err = libcni.ConfListFromFile(confFile)
if err != nil {
return nil, logging.Errorf("getCNIConfigFromFile: error loading CNI conflist file %s: %v", confFile, err)
}
if confList.Name == name || name == "" {
return confList.Bytes, nil
}
} else {
conf, err := libcni.ConfFromFile(confFile)
if err != nil {
return nil, logging.Errorf("getCNIConfigFromFile: error loading CNI config file %s: %v", confFile, err)
}
if conf.Network.Name == name || name == "" {
// Ensure the config has a "type" so we know what plugin to run.
// Also catches the case where somebody put a conflist into a conf file.
if conf.Network.Type == "" {
return nil, logging.Errorf("getCNIConfigFromFile: error loading CNI config file %s: does not have a 'type' key; perhaps this is a .conflist?", confFile)
}
return conf.Bytes, nil
}
}
}
return nil, logging.Errorf("getCNIConfigFromFile: no network available with the name %s in cni dir %s", name, confdir)
}
// getCNIConfigFromSpec reads a CNI JSON configuration from the NetworkAttachmentDefinition
// object's Spec.Config field and fills in any missing details like the network name
func getCNIConfigFromSpec(configData, netName string) ([]byte, error) {
var rawConfig map[string]interface{}
var err error
logging.Debugf("getCNIConfigFromSpec: %s, %s", configData, netName)
configBytes := []byte(configData)
err = json.Unmarshal(configBytes, &rawConfig)
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{})
if err != nil {
return nil, logging.Errorf("getCNIConfigFromSpec: failed to unmarshal Spec.Config: %v", err)
}
// Inject network name if missing from Config for the thick plugin case
if n, ok := rawConfig["name"]; !ok || n == "" {
rawConfig["name"] = netName
configBytes, err = json.Marshal(rawConfig)
if err != nil {
return nil, logging.Errorf("getCNIConfigFromSpec: failed to re-marshal Spec.Config: %v", err)
errMsg := fmt.Sprintf("cannot find a network-attachment-definition (%s) in namespace (%s): %v", net.Name, net.Namespace, err)
if client != nil {
client.Eventf(pod, v1.EventTypeWarning, "NoNetworkFound", errMsg)
}
}
return configBytes, nil
}
func cniConfigFromNetworkResource(customResource *nettypes.NetworkAttachmentDefinition, confdir string) ([]byte, error) {
var config []byte
var err error
logging.Debugf("cniConfigFromNetworkResource: %v, %s", customResource, confdir)
emptySpec := nettypes.NetworkAttachmentDefinitionSpec{}
if customResource.Spec == emptySpec {
// Network Spec empty; generate delegate from CNI JSON config
// from the configuration directory that has the same network
// name as the custom resource
config, err = getCNIConfigFromFile(customResource.GetName(), confdir)
if err != nil {
return nil, logging.Errorf("cniConfigFromNetworkResource: %v", err)
}
} else {
// Config contains a standard JSON-encoded CNI configuration
// or configuration list which defines the plugin chain to
// execute.
config, err = getCNIConfigFromSpec(customResource.Spec.Config, customResource.GetName())
if err != nil {
return nil, logging.Errorf("cniConfigFromNetworkResource: %v", err)
}
}
return config, nil
}
func getKubernetesDelegate(client KubeClient, 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", client, net, confdir)
rawPath := fmt.Sprintf("/apis/k8s.cni.cncf.io/v1/namespaces/%s/network-attachment-definitions/%s", net.Namespace, net.Name)
netData, err := client.GetRawWithPath(rawPath)
if err != nil {
return nil, resourceMap, logging.Errorf("getKubernetesDelegate: cannot find get a network-attachment-definition (%s) in namespace (%s): %v", net.Name, net.Namespace, err)
}
customResource := &nettypes.NetworkAttachmentDefinition{}
if err := json.Unmarshal(netData, customResource); err != nil {
return nil, resourceMap, logging.Errorf("getKubernetesDelegate: failed to parse the network-attachment-definition: %v", err)
return nil, resourceMap, logging.Errorf("getKubernetesDelegate: " + errMsg)
}
// Get resourceName annotation from NetworkAttachmentDefinition
@@ -419,12 +280,12 @@ func getKubernetesDelegate(client KubeClient, net *types.NetworkSelectionElement
}
}
configBytes, err := cniConfigFromNetworkResource(customResource, confdir)
configBytes, err := netutils.GetCNIConfig(customResource, confdir)
if err != nil {
return nil, resourceMap, err
}
delegate, err := types.LoadDelegateNetConf(configBytes, net, deviceID)
delegate, err := types.LoadDelegateNetConf(configBytes, net, deviceID, resourceName)
if err != nil {
return nil, resourceMap, err
}
@@ -432,13 +293,6 @@ func getKubernetesDelegate(client KubeClient, net *types.NetworkSelectionElement
return delegate, resourceMap, nil
}
// KubeClient is abstraction layer for k8s client (used testing package)
type KubeClient interface {
GetRawWithPath(path string) ([]byte, error)
GetPod(namespace, name string) (*v1.Pod, error)
UpdatePodStatus(pod *v1.Pod) (*v1.Pod, error)
}
// GetK8sArgs gets k8s related args from CNI args
func GetK8sArgs(args *skel.CmdArgs) (*types.K8sArgs, error) {
k8sArgs := &types.K8sArgs{}
@@ -454,17 +308,16 @@ func GetK8sArgs(args *skel.CmdArgs) (*types.K8sArgs, error) {
// TryLoadPodDelegates attempts to load Kubernetes-defined delegates and add them to the Multus config.
// Returns the number of Kubernetes-defined delegates added or an error.
func TryLoadPodDelegates(k8sArgs *types.K8sArgs, conf *types.NetConf, kubeClient KubeClient) (int, *ClientInfo, error) {
func TryLoadPodDelegates(pod *v1.Pod, conf *types.NetConf, clientInfo *ClientInfo, resourceMap map[string]*types.ResourceInfo) (int, *ClientInfo, error) {
var err error
clientInfo := &ClientInfo{}
logging.Debugf("TryLoadPodDelegates: %v, %v, %v", k8sArgs, conf, kubeClient)
kubeClient, err = GetK8sClient(conf.Kubeconfig, kubeClient)
logging.Debugf("TryLoadPodDelegates: %v, %v, %v", pod, conf, clientInfo)
clientInfo, err = GetK8sClient(conf.Kubeconfig, clientInfo)
if err != nil {
return 0, nil, err
}
if kubeClient == nil {
if clientInfo == nil {
if len(conf.Delegates) == 0 {
// No available kube client and no delegates, we can't do anything
return 0, nil, logging.Errorf("TryLoadPodDelegates: must have either Kubernetes config or delegates")
@@ -472,15 +325,7 @@ func TryLoadPodDelegates(k8sArgs *types.K8sArgs, conf *types.NetConf, kubeClient
return 0, nil, nil
}
setKubeClientInfo(clientInfo, kubeClient, k8sArgs)
// Get the pod info. If cannot get it, we use cached delegates
pod, err := kubeClient.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
if err != nil {
logging.Debugf("TryLoadPodDelegates: Err in loading K8s cluster default network from pod annotation: %v, use cached delegates", err)
return 0, nil, nil
}
delegate, err := tryLoadK8sPodDefaultNetwork(kubeClient, pod, conf)
delegate, err := tryLoadK8sPodDefaultNetwork(clientInfo, pod, conf)
if err != nil {
return 0, nil, logging.Errorf("TryLoadPodDelegates: error in loading K8s cluster default network from pod annotation: %v", err)
}
@@ -492,13 +337,13 @@ func TryLoadPodDelegates(k8sArgs *types.K8sArgs, conf *types.NetConf, kubeClient
networks, err := GetPodNetwork(pod)
if networks != nil {
delegates, err := GetNetworkDelegates(kubeClient, pod, networks, conf.ConfDir, conf.NamespaceIsolation)
delegates, err := GetNetworkDelegates(clientInfo, pod, networks, conf, resourceMap)
if err != nil {
if _, ok := err.(*NoK8sNetworkError); ok {
return 0, clientInfo, nil
}
return 0, nil, logging.Errorf("TryLoadPodDelegates: error in getting k8s network from pod: %v", err)
return 0, nil, logging.Errorf("TryLoadPodDelegates: error in getting k8s network for pod: %v", err)
}
if err = conf.AddDelegates(delegates); err != nil {
@@ -526,7 +371,7 @@ func TryLoadPodDelegates(k8sArgs *types.K8sArgs, conf *types.NetConf, kubeClient
}
// GetK8sClient gets client info from kubeconfig
func GetK8sClient(kubeconfig string, kubeClient KubeClient) (KubeClient, error) {
func GetK8sClient(kubeconfig string, kubeClient *ClientInfo) (*ClientInfo, error) {
logging.Debugf("GetK8sClient: %s, %v", kubeconfig, kubeClient)
// If we get a valid kubeClient (eg from testcases) just return that
// one.
@@ -558,6 +403,8 @@ func GetK8sClient(kubeconfig string, kubeClient KubeClient) (KubeClient, error)
// Specify that we use gRPC
config.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
config.ContentType = "application/vnd.kubernetes.protobuf"
// Set the config timeout to one minute.
config.Timeout = time.Minute
// creates the clientset
client, err := kubernetes.NewForConfig(config)
@@ -565,7 +412,22 @@ func GetK8sClient(kubeconfig string, kubeClient KubeClient) (KubeClient, error)
return nil, err
}
return &defaultKubeClient{client: client}, nil
netclient, err := netclient.NewForConfig(config)
if err != nil {
return nil, err
}
broadcaster := record.NewBroadcaster()
broadcaster.StartLogging(klog.Infof)
broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: client.CoreV1().Events("")})
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "multus"})
return &ClientInfo{
Client: client,
NetClient: netclient,
EventBroadcaster: broadcaster,
EventRecorder: recorder,
}, nil
}
// GetPodNetwork gets net-attach-def annotation from pod
@@ -587,11 +449,8 @@ func GetPodNetwork(pod *v1.Pod) ([]*types.NetworkSelectionElement, error) {
}
// GetNetworkDelegates returns delegatenetconf from net-attach-def annotation in pod
func GetNetworkDelegates(k8sclient KubeClient, pod *v1.Pod, networks []*types.NetworkSelectionElement, confdir string, confnamespaceIsolation bool) ([]*types.DelegateNetConf, error) {
logging.Debugf("GetNetworkDelegates: %v, %v, %v, %v, %v", k8sclient, pod, networks, confdir, confnamespaceIsolation)
// resourceMap holds Pod device allocation information; only initizized if CRD contains 'resourceName' annotation.
// This will only be initialized once and all delegate objects can reference this to look up device info.
var resourceMap map[string]*types.ResourceInfo
func GetNetworkDelegates(k8sclient *ClientInfo, pod *v1.Pod, networks []*types.NetworkSelectionElement, conf *types.NetConf, resourceMap map[string]*types.ResourceInfo) ([]*types.DelegateNetConf, error) {
logging.Debugf("GetNetworkDelegates: %v, %v, %v, %v, %v", k8sclient, pod, networks, conf, resourceMap)
// Read all network objects referenced by 'networks'
var delegates []*types.DelegateNetConf
@@ -601,13 +460,16 @@ func GetNetworkDelegates(k8sclient KubeClient, pod *v1.Pod, networks []*types.Ne
// The pods namespace (stored as defaultNamespace, does not equal the annotation's target namespace in net.Namespace)
// In the case that this is a mismatch when namespaceisolation is enabled, this should be an error.
if confnamespaceIsolation {
if conf.NamespaceIsolation {
if defaultNamespace != net.Namespace {
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)
// We allow exceptions based on the specified list of non-isolated namespaces (and/or "default" namespace, by default)
if !isValidNamespaceReference(net.Namespace, conf.NonIsolatedNamespaces) {
return nil, logging.Errorf("GetNetworkDelegates: namespace isolation enabled, annotation violates permission, pod is in namespace %v but refers to target namespace %v", defaultNamespace, net.Namespace)
}
}
}
delegate, updatedResourceMap, err := getKubernetesDelegate(k8sclient, net, confdir, pod, resourceMap)
delegate, updatedResourceMap, err := getKubernetesDelegate(k8sclient, net, conf.ConfDir, pod, resourceMap)
if err != nil {
return nil, logging.Errorf("GetNetworkDelegates: failed getting the delegate: %v", err)
}
@@ -618,49 +480,36 @@ func GetNetworkDelegates(k8sclient KubeClient, pod *v1.Pod, networks []*types.Ne
return delegates, nil
}
func getDefaultNetDelegateCRD(client KubeClient, net, confdir, namespace string) (*types.DelegateNetConf, error) {
logging.Debugf("getDefaultNetDelegateCRD: %v, %v, %s, %s", client, net, confdir, namespace)
rawPath := fmt.Sprintf("/apis/k8s.cni.cncf.io/v1/namespaces/%s/network-attachment-definitions/%s", namespace, net)
netData, err := client.GetRawWithPath(rawPath)
if err != nil {
return nil, logging.Errorf("getDefaultNetDelegateCRD: failed to get network resource: %v", err)
func isValidNamespaceReference(targetns string, allowednamespaces []string) bool {
for _, eachns := range allowednamespaces {
if eachns == targetns {
return true
}
}
customResource := &nettypes.NetworkAttachmentDefinition{}
if err := json.Unmarshal(netData, customResource); err != nil {
return nil, logging.Errorf("getDefaultNetDelegateCRD: failed to get the netplugin data: %v", err)
}
configBytes, err := cniConfigFromNetworkResource(customResource, confdir)
if err != nil {
return nil, err
}
delegate, err := types.LoadDelegateNetConf(configBytes, nil, "")
if err != nil {
return nil, err
}
return delegate, nil
return false
}
func getNetDelegate(client KubeClient, netname, confdir, namespace string) (*types.DelegateNetConf, error) {
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
delegate, err := getDefaultNetDelegateCRD(client, netname, confdir, namespace)
net := &types.NetworkSelectionElement{
Name: netname,
Namespace: namespace,
}
delegate, resourceMap, err := getKubernetesDelegate(client, net, confdir, pod, resourceMap)
if err == nil {
return delegate, nil
return delegate, resourceMap, nil
}
// option2) search CNI json config file
var configBytes []byte
configBytes, err = getCNIConfigFromFile(netname, confdir)
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, err
return nil, resourceMap, err
}
return delegate, nil
return delegate, resourceMap, nil
}
// option3) search directry
@@ -669,69 +518,70 @@ func getNetDelegate(client KubeClient, netname, confdir, namespace string) (*typ
if fInfo.IsDir() {
files, err := libcni.ConfFiles(netname, []string{".conf", ".conflist"})
if err != nil {
return nil, err
return nil, resourceMap, err
}
if len(files) > 0 {
var configBytes []byte
configBytes, err = getCNIConfigFromFile("", netname)
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, err
return nil, resourceMap, err
}
return delegate, nil
return delegate, resourceMap, nil
}
return nil, err
return nil, resourceMap, err
}
}
}
return nil, logging.Errorf("getNetDelegate: cannot find network: %v", netname)
return nil, resourceMap, logging.Errorf("getNetDelegate: cannot find network: %v", netname)
}
// GetDefaultNetworks parses 'defaultNetwork' config, gets network json and put it into netconf.Delegates.
func GetDefaultNetworks(k8sArgs *types.K8sArgs, conf *types.NetConf, kubeClient KubeClient) error {
logging.Debugf("GetDefaultNetworks: %v, %v, %v", k8sArgs, conf, kubeClient)
func GetDefaultNetworks(pod *v1.Pod, conf *types.NetConf, kubeClient *ClientInfo, resourceMap map[string]*types.ResourceInfo) (map[string]*types.ResourceInfo, error) {
logging.Debugf("GetDefaultNetworks: %v, %v, %v, %v", pod, conf, kubeClient, resourceMap)
var delegates []*types.DelegateNetConf
kubeClient, err := GetK8sClient(conf.Kubeconfig, kubeClient)
if err != nil {
return err
return resourceMap, err
}
if kubeClient == nil {
if len(conf.Delegates) == 0 {
// No available kube client and no delegates, we can't do anything
return logging.Errorf("GetDefaultNetworks: must have either Kubernetes config or delegates")
return resourceMap, logging.Errorf("GetDefaultNetworks: must have either Kubernetes config or delegates")
}
return nil
return resourceMap, nil
}
delegate, err := getNetDelegate(kubeClient, conf.ClusterNetwork, conf.ConfDir, conf.MultusNamespace)
delegate, resourceMap, err := getNetDelegate(kubeClient, pod, conf.ClusterNetwork, conf.ConfDir, conf.MultusNamespace, resourceMap)
if err != nil {
return err
return resourceMap, logging.Errorf("GetDefaultNetworks: failed to get clusterNetwork %s in namespace %s", conf.ClusterNetwork, conf.MultusNamespace)
}
delegate.MasterPlugin = true
delegates = append(delegates, delegate)
// Pod in kube-system namespace does not have default network for now.
if !types.CheckSystemNamespaces(string(k8sArgs.K8S_POD_NAMESPACE), conf.SystemNamespaces) {
if !types.CheckSystemNamespaces(pod.ObjectMeta.Namespace, conf.SystemNamespaces) {
for _, netname := range conf.DefaultNetworks {
delegate, err := getNetDelegate(kubeClient, netname, conf.ConfDir, conf.MultusNamespace)
delegate, resourceMap, err := getNetDelegate(kubeClient, pod, netname, conf.ConfDir, conf.MultusNamespace, resourceMap)
if err != nil {
return err
return resourceMap, err
}
delegates = append(delegates, delegate)
}
}
if err = conf.AddDelegates(delegates); err != nil {
return err
return resourceMap, err
}
return nil
return resourceMap, nil
}
// tryLoadK8sPodDefaultNetwork get pod default network from annotations
func tryLoadK8sPodDefaultNetwork(kubeClient KubeClient, pod *v1.Pod, conf *types.NetConf) (*types.DelegateNetConf, error) {
func tryLoadK8sPodDefaultNetwork(kubeClient *ClientInfo, pod *v1.Pod, conf *types.NetConf) (*types.DelegateNetConf, error) {
var netAnnot string
logging.Debugf("tryLoadK8sPodDefaultNetwork: %v, %v, %v", kubeClient, pod, conf)

View File

@@ -5,9 +5,9 @@ import (
"path/filepath"
"time"
"github.com/intel/multus-cni/checkpoint"
"github.com/intel/multus-cni/logging"
"github.com/intel/multus-cni/types"
"gopkg.in/intel/multus-cni.v3/pkg/checkpoint"
"gopkg.in/intel/multus-cni.v3/pkg/logging"
"gopkg.in/intel/multus-cni.v3/pkg/types"
"golang.org/x/net/context"
v1 "k8s.io/api/core/v1"
"k8s.io/kubernetes/pkg/kubelet/apis/podresources"

View File

@@ -15,7 +15,7 @@ import (
k8sTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/kubernetes/pkg/kubelet/util"
mtypes "github.com/intel/multus-cni/types"
mtypes "gopkg.in/intel/multus-cni.v3/pkg/types"
podresourcesapi "k8s.io/kubernetes/pkg/kubelet/apis/podresources/v1alpha1"
)
@@ -91,10 +91,8 @@ func tearDown(path string) error {
if fakeServer != nil {
fakeServer.server.Stop()
}
if err := os.RemoveAll(path); err != nil {
return err
}
return nil
err := os.RemoveAll(path)
return err
}
var _ = BeforeSuite(func() {

View File

@@ -16,11 +16,13 @@ package logging
import (
"fmt"
"io"
"os"
"strings"
"time"
"github.com/pkg/errors"
lumberjack "gopkg.in/natefinch/lumberjack.v2"
)
// Level type
@@ -37,7 +39,7 @@ const (
)
var loggingStderr bool
var loggingFp *os.File
var loggingW io.Writer
var loggingLevel Level
const defaultTimestampFormat = time.RFC3339
@@ -69,10 +71,10 @@ func printf(level Level, format string, a ...interface{}) {
fmt.Fprintf(os.Stderr, "\n")
}
if loggingFp != nil {
fmt.Fprintf(loggingFp, header, t.Format(defaultTimestampFormat), level)
fmt.Fprintf(loggingFp, format, a...)
fmt.Fprintf(loggingFp, "\n")
if loggingW != nil {
fmt.Fprintf(loggingW, header, t.Format(defaultTimestampFormat), level)
fmt.Fprintf(loggingW, format, a...)
fmt.Fprintf(loggingW, "\n")
}
}
@@ -139,16 +141,18 @@ func SetLogFile(filename string) {
return
}
fp, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
loggingFp = nil
fmt.Fprintf(os.Stderr, "multus logging: cannot open %s", filename)
loggingW = &lumberjack.Logger{
Filename: filename,
MaxSize: 100, // megabytes
MaxBackups: 5,
MaxAge: 5, // days
Compress: true,
}
loggingFp = fp
}
func init() {
loggingStderr = true
loggingFp = nil
loggingW = nil
loggingLevel = PanicLevel
}

View File

@@ -30,24 +30,24 @@ var _ = Describe("logging operations", func() {
BeforeEach(func() {
loggingStderr = false
loggingFp = nil
loggingW = nil
loggingLevel = PanicLevel
})
It("Check file setter with empty", func() {
SetLogFile("")
Expect(loggingFp).To(BeNil())
Expect(loggingW).To(BeNil())
})
It("Check file setter with empty", func() {
SetLogFile("/tmp/foobar.logging")
Expect(loggingFp).NotTo(Equal(nil))
Expect(loggingW).NotTo(Equal(nil))
// check file existance
})
It("Check file setter with bad filepath", func() {
SetLogFile("/invalid/filepath")
Expect(loggingFp).NotTo(Equal(nil))
Expect(loggingW).NotTo(Equal(nil))
// check file existance
})

View File

@@ -12,11 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// This is a "Multi-plugin".The delegate concept refered from CNI project
// It reads other plugin netconf, and then invoke them, e.g.
// flannel or sriov plugin.
package main
package multus
import (
"context"
@@ -34,24 +30,34 @@ import (
"github.com/containernetworking/cni/pkg/invoke"
"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"
k8s "github.com/intel/multus-cni/k8sclient"
"github.com/intel/multus-cni/logging"
"github.com/intel/multus-cni/netutils"
"github.com/intel/multus-cni/types"
k8s "gopkg.in/intel/multus-cni.v3/pkg/k8sclient"
"gopkg.in/intel/multus-cni.v3/pkg/logging"
"gopkg.in/intel/multus-cni.v3/pkg/netutils"
"gopkg.in/intel/multus-cni.v3/pkg/types"
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"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/wait"
)
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
)
func printVersionString() string {
//PrintVersionString ...
func PrintVersionString() string {
return fmt.Sprintf("multus-cni version:%s, commit:%s, date:%s",
version, commit, date)
}
@@ -95,6 +101,20 @@ func getIfname(delegate *types.DelegateNetConf, argif string, idx int) string {
return fmt.Sprintf("net%d", idx)
}
func getDelegateDeviceInfo(delegate *types.DelegateNetConf, runtimeConf *libcni.RuntimeConf) (*nettypes.DeviceInfo, error) {
// If the DPDeviceInfoFile was created, it was copied to the CNIDeviceInfoFile.
// If the DPDeviceInfoFile was not created, CNI might have created it. So
// either way, load CNIDeviceInfoFile.
if info, ok := runtimeConf.CapabilityArgs["CNIDeviceInfoFile"]; ok {
if infostr, ok := info.(string); ok {
return nadutils.LoadDeviceInfoFromCNI(infostr)
}
} else {
logging.Debugf("getDelegateDeviceInfo(): No CapArgs - info=%v ok=%v", info, ok)
}
return nil, nil
}
func saveDelegates(containerID, dataDir string, delegates []*types.DelegateNetConf) error {
logging.Debugf("saveDelegates: %s, %s, %v", containerID, dataDir, delegates)
delegatesBytes, err := json.Marshal(delegates)
@@ -142,7 +162,7 @@ func validateIfName(nsname string, ifname string) error {
}
func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) (cnitypes.Result, error) {
logging.Debugf("conflistAdd: %v, %s, %s", rt, string(rawNetconf), binDir)
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{binDir}, binDirs...)
@@ -155,12 +175,32 @@ func confAdd(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invo
result, err := cniNet.AddNetwork(context.Background(), conf, rt)
if err != nil {
return nil, logging.Errorf("error in getting result from AddNetwork: %v", err)
return nil, err
}
return result, nil
}
func confCheck(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("confCheck: %v, %s, %s", rt, string(rawNetconf), binDir)
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
conf, err := libcni.ConfFromBytes(rawNetconf)
if err != nil {
return logging.Errorf("error in converting the raw bytes to conf: %v", err)
}
err = cniNet.CheckNetwork(context.Background(), conf, rt)
if err != nil {
return logging.Errorf("error in getting result from DelNetwork: %v", err)
}
return err
}
func confDel(rt *libcni.RuntimeConf, rawNetconf []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistDel: %v, %s, %s", rt, string(rawNetconf), binDir)
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
@@ -195,12 +235,32 @@ func conflistAdd(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, e
result, err := cniNet.AddNetworkList(context.Background(), confList, rt)
if err != nil {
return nil, logging.Errorf("conflistAdd: error in getting result from AddNetworkList: %v", err)
return nil, err
}
return result, nil
}
func conflistCheck(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistCheck: %v, %s, %s", rt, string(rawnetconflist), binDir)
binDirs := filepath.SplitList(os.Getenv("CNI_PATH"))
binDirs = append([]string{binDir}, binDirs...)
cniNet := libcni.NewCNIConfig(binDirs, exec)
confList, err := libcni.ConfListFromBytes(rawnetconflist)
if err != nil {
return logging.Errorf("conflistCheck: error converting the raw bytes into a conflist: %v", err)
}
err = cniNet.CheckNetworkList(context.Background(), confList, rt)
if err != nil {
return logging.Errorf("conflistCheck: error in getting result from CheckNetworkList: %v", err)
}
return err
}
func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, exec invoke.Exec) error {
logging.Debugf("conflistDel: %v, %s, %s", rt, string(rawnetconflist), binDir)
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go
@@ -221,7 +281,7 @@ func conflistDel(rt *libcni.RuntimeConf, rawnetconflist []byte, binDir string, e
return err
}
func delegateAdd(exec invoke.Exec, ifName string, delegate *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string, cniArgs string) (cnitypes.Result, error) {
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")
@@ -275,32 +335,91 @@ func delegateAdd(exec invoke.Exec, ifName string, delegate *types.DelegateNetCon
if delegate.ConfListPlugin {
result, err = conflistAdd(rt, delegate.Bytes, binDir, exec)
if err != nil {
return nil, logging.Errorf("delegateAdd: error invoking conflistAdd - %q: %v", delegate.ConfList.Name, err)
return nil, err
}
} else {
result, err = confAdd(rt, delegate.Bytes, binDir, exec)
if err != nil {
return nil, logging.Errorf("delegateAdd: error invoking DelegateAdd - %q: %v", delegate.Conf.Type, err)
return nil, err
}
}
if logging.GetLoggingLevel() >= logging.VerboseLevel {
data, _ := json.Marshal(result)
var confName string
var cniConfName string
if delegate.ConfListPlugin {
confName = delegate.ConfList.Name
cniConfName = delegate.ConfList.Name
} else {
confName = delegate.Conf.Name
cniConfName = delegate.Conf.Name
}
logging.Verbosef("Add: %s:%s:%s:%s %s", rt.Args[1][1], rt.Args[2][1], confName, rt.IfName, string(data))
podUID := "unknownUID"
if pod != nil {
podUID = string(pod.ObjectMeta.UID)
}
logging.Verbosef("Add: %s:%s:%s:%s(%s):%s %s", rt.Args[1][1], rt.Args[2][1], podUID, delegate.Name, cniConfName, rt.IfName, string(data))
}
// get IP addresses from result
ips := []string{}
res, err := cnicurrent.NewResultFromResult(result)
if err != nil {
logging.Errorf("delegateAdd: error converting result: %v", err)
return result, nil
}
for _, ip := range res.IPs {
ips = append(ips, ip.Address.String())
}
if pod != nil {
// send kubernetes events
if delegate.Name != "" {
kubeClient.Eventf(pod, v1.EventTypeNormal, "AddedInterface", "Add %s %v from %s", rt.IfName, ips, delegate.Name)
} else {
kubeClient.Eventf(pod, v1.EventTypeNormal, "AddedInterface", "Add %s %v", rt.IfName, ips)
}
} else {
// 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 delegateDel(exec invoke.Exec, ifName string, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string) error {
logging.Debugf("delegateDel: %v, %s, %v, %v, %s", exec, ifName, delegateConf, rt, binDir)
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
if delegateConf.ConfListPlugin {
cniConfName = delegateConf.ConfList.Name
} else {
cniConfName = delegateConf.Conf.Name
}
logging.Verbosef("Check: %s:%s:%s(%s):%s %s", rt.Args[1][1], rt.Args[2][1], delegateConf.Name, cniConfName, rt.IfName, string(delegateConf.Bytes))
}
var err error
if delegateConf.ConfListPlugin {
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, binDir, exec)
if err != nil {
return logging.Errorf("delegateCheck: error invoking DelegateCheck - %q: %v", delegateConf.Conf.Type, err)
}
}
return err
}
func delegateDel(exec invoke.Exec, pod *v1.Pod, ifName string, delegateConf *types.DelegateNetConf, rt *libcni.RuntimeConf, binDir string) error {
logging.Debugf("delegateDel: %v, %v, %s, %v, %v, %s", exec, pod, ifName, delegateConf, rt, binDir)
if os.Setenv("CNI_IFNAME", ifName) != nil {
return logging.Errorf("delegateDel: error setting envionment variable CNI_IFNAME")
}
@@ -312,7 +431,11 @@ func delegateDel(exec invoke.Exec, ifName string, delegateConf *types.DelegateNe
} else {
confName = delegateConf.Conf.Name
}
logging.Verbosef("Del: %s:%s:%s:%s %s", rt.Args[1][1], rt.Args[2][1], confName, rt.IfName, string(delegateConf.Bytes))
podUID := "unknownUID"
if pod != nil {
podUID = string(pod.ObjectMeta.UID)
}
logging.Verbosef("Del: %s:%s:%s:%s:%s %s", rt.Args[1][1], rt.Args[2][1], podUID, confName, rt.IfName, string(delegateConf.Bytes))
}
var err error
@@ -331,20 +454,31 @@ func delegateDel(exec invoke.Exec, ifName string, delegateConf *types.DelegateNe
return err
}
func delPlugins(exec invoke.Exec, argIfname string, delegates []*types.DelegateNetConf, lastIdx int, rt *libcni.RuntimeConf, binDir string) error {
logging.Debugf("delPlugins: %v, %s, %v, %d, %v, %s", exec, argIfname, delegates, lastIdx, rt, binDir)
// 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, binDir string) error {
logging.Debugf("delPlugins: %v, %v, %v, %v, %v, %d, %v, %s", exec, pod, args, k8sArgs, delegates, lastIdx, netRt, binDir)
if os.Setenv("CNI_COMMAND", "DEL") != nil {
return logging.Errorf("delPlugins: error setting envionment variable CNI_COMMAND to a value of DEL")
return logging.Errorf("delPlugins: error setting environment variable CNI_COMMAND to a value of DEL")
}
var errorstrings []string
for idx := lastIdx; idx >= 0; idx-- {
ifName := getIfname(delegates[idx], argIfname, idx)
rt.IfName = ifName
ifName := getIfname(delegates[idx], args.IfName, idx)
rt, cniDeviceInfoPath := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, netRt, delegates[idx])
// Attempt to delete all but do not error out, instead, collect all errors.
if err := delegateDel(exec, ifName, delegates[idx], rt, binDir); 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.
@@ -363,13 +497,27 @@ func cmdErr(k8sArgs *types.K8sArgs, format string, args ...interface{}) error {
return logging.Errorf(prefix+format, args...)
}
func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cnitypes.Result, error) {
func cmdPluginErr(k8sArgs *types.K8sArgs, confName string, format string, args ...interface{}) error {
msg := ""
if k8sArgs != nil {
msg += fmt.Sprintf("[%s/%s:%s]: ", k8sArgs.K8S_POD_NAMESPACE, k8sArgs.K8S_POD_NAME, confName)
}
return logging.Errorf(msg+format, args...)
}
//CmdAdd ...
func CmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) (cnitypes.Result, error) {
n, err := types.LoadNetConf(args.StdinData)
logging.Debugf("cmdAdd: %v, %v, %v", args, exec, kubeClient)
logging.Debugf("CmdAdd: %v, %v, %v", args, exec, kubeClient)
if err != nil {
return nil, cmdErr(nil, "error loading netconf: %v", err)
}
kubeClient, err = k8s.GetK8sClient(n.Kubeconfig, kubeClient)
if err != nil {
return nil, cmdErr(nil, "error getting k8s client: %v", err)
}
k8sArgs, err := k8s.GetK8sArgs(args)
if err != nil {
return nil, cmdErr(nil, "error getting k8s args: %v", err)
@@ -385,8 +533,36 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
}
}
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.
// This will only be initialized once and all delegate objects can reference this to look up device info.
var resourceMap map[string]*types.ResourceInfo
if n.ClusterNetwork != "" {
err = k8s.GetDefaultNetworks(k8sArgs, n, kubeClient)
resourceMap, err = k8s.GetDefaultNetworks(pod, n, kubeClient, resourceMap)
if err != nil {
return nil, cmdErr(k8sArgs, "failed to get clusterNetwork/defaultNetworks: %v", err)
}
@@ -394,7 +570,7 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
n.Delegates[0].MasterPlugin = true
}
_, kc, err := k8s.TryLoadPodDelegates(k8sArgs, n, kubeClient)
_, kc, err := k8s.TryLoadPodDelegates(pod, n, kubeClient, resourceMap)
if err != nil {
return nil, cmdErr(k8sArgs, "error loading k8s delegates k8s args: %v", err)
}
@@ -405,14 +581,21 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
}
var result, tmpResult cnitypes.Result
var netStatus []*types.NetworkStatus
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 != "" {
err = nadutils.CopyDeviceInfoForCNIFromDP(cniDeviceInfoPath, delegate.ResourceName, delegate.DeviceID)
// Even if the filename is set, file may not be present. Ignore error,
// but log and in the future may need to filter on specific errors.
if err != nil {
logging.Debugf("cmdAdd: CopyDeviceInfoForCNIFromDP returned an error - err=%v", err)
}
}
runtimeConfig := types.MergeCNIRuntimeConfig(n.RuntimeConfig, delegate)
rt := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, runtimeConfig)
tmpResult, err = delegateAdd(exec, ifName, delegate, rt, n.BinDir, cniArgs)
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
@@ -420,8 +603,8 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
netName = delegate.ConfList.Name
}
// Ignore errors; DEL must be idempotent anyway
_ = delPlugins(exec, args.IfName, n.Delegates, idx, rt, n.BinDir)
return nil, cmdErr(k8sArgs, "error adding container to network %q: %v", netName, err)
_ = delPlugins(exec, nil, args, k8sArgs, n.Delegates, idx, n.RuntimeConfig, 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
@@ -432,7 +615,11 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
logging.Debugf("Marked interface %v for gateway deletion", ifName)
} else {
// Otherwise, determine if this interface now gets our default route.
if delegate.GatewayRequest != nil {
// 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 && delegate.GatewayRequest[0] != nil {
deletegateway = true
adddefaultgateway = true
logging.Debugf("Detected gateway override on interface %v to %v", ifName, delegate.GatewayRequest)
@@ -459,24 +646,39 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
result = tmpResult
}
//create the network status, only in case Multus as kubeconfig
// Read devInfo from CNIDeviceInfoFile if it exists so
// it can be copied to the NetworkStatus.
devinfo, err := getDelegateDeviceInfo(delegate, rt)
if err != nil {
// Even if the filename is set, file may not be present. Ignore error,
// but log and in the future may need to filter on specific errors.
logging.Debugf("cmdAdd: getDelegateDeviceInfo returned an error - err=%v", err)
}
// create the network status, only in case Multus as kubeconfig
if n.Kubeconfig != "" && kc != nil {
if !types.CheckSystemNamespaces(kc.Podnamespace, n.SystemNamespaces) {
delegateNetStatus, err := types.LoadNetworkStatus(tmpResult, delegate.Conf.Name, delegate.MasterPlugin)
if !types.CheckSystemNamespaces(string(k8sArgs.K8S_POD_NAME), n.SystemNamespaces) {
delegateNetStatus, err := nadutils.CreateNetworkStatus(tmpResult, delegate.Name, delegate.MasterPlugin, devinfo)
if err != nil {
return nil, cmdErr(k8sArgs, "error setting network status: %v", err)
}
netStatus = append(netStatus, delegateNetStatus)
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(kc.Podnamespace, n.SystemNamespaces) {
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)
}
}
@@ -485,21 +687,36 @@ func cmdAdd(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cn
return result, nil
}
func cmdGet(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) (cnitypes.Result, error) {
logging.Debugf("cmdGet: %v, %v, %v", args, exec, kubeClient)
//CmdCheck ...
func CmdCheck(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error {
in, err := types.LoadNetConf(args.StdinData)
logging.Debugf("CmdCheck: %v, %v, %v", args, exec, kubeClient)
if err != nil {
return nil, err
return err
}
// FIXME: call all delegates
k8sArgs, err := k8s.GetK8sArgs(args)
if err != nil {
return cmdErr(nil, "error getting k8s args: %v", err)
}
return in.PrevResult, nil
for idx, delegate := range in.Delegates {
ifName := getIfname(delegate, args.IfName, idx)
rt, _ := types.CreateCNIRuntimeConf(args, k8sArgs, ifName, in.RuntimeConfig, delegate)
err = delegateCheck(exec, ifName, delegate, rt, in.BinDir)
if err != nil {
return err
}
}
return nil
}
func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) error {
logging.Debugf("cmdDel: %v, %v, %v", args, exec, kubeClient)
//CmdDel ...
func CmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient *k8s.ClientInfo) error {
in, err := types.LoadNetConf(args.StdinData)
logging.Debugf("CmdDel: %v, %v, %v", args, exec, kubeClient)
if err != nil {
return err
}
@@ -513,7 +730,7 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) err
_, ok := err.(ns.NSPathNotExistErr)
if ok {
netnsfound = false
logging.Debugf("cmdDel: WARNING netns may not exist, netns: %s, err: %s", args.Netns, err)
logging.Debugf("CmdDel: WARNING netns may not exist, netns: %s, err: %s", args.Netns, err)
} else {
return cmdErr(nil, "failed to open netns %q: %v", netns, err)
}
@@ -538,13 +755,45 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) err
}
}
kubeClient, err = k8s.GetK8sClient(in.Kubeconfig, kubeClient)
if err != nil {
return cmdErr(nil, "error getting k8s client: %v", err)
}
pod := (*v1.Pod)(nil)
if kubeClient != nil {
pod, err = kubeClient.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
if err != nil {
var waitErr error
// in case of ServiceUnavailable, retry 10 times with 0.5 sec interval
if errors.IsServiceUnavailable(err) {
pollDuration := 500 * time.Millisecond
pollTimeout := 5 * time.Second
waitErr = wait.PollImmediate(pollDuration, pollTimeout, func() (bool, error) {
pod, err = kubeClient.GetPod(string(k8sArgs.K8S_POD_NAMESPACE), string(k8sArgs.K8S_POD_NAME))
return pod != nil, err
})
// retry failed, then return error with retry out
if waitErr != nil {
return cmdErr(k8sArgs, "error getting pod by service unavailable: %v", err)
}
} else if errors.IsNotFound(err) {
// If not found, proceed to remove interface with cache
pod = nil
} else {
// Other case, return error
return cmdErr(k8sArgs, "error getting pod: %v", err)
}
}
}
// Read the cache to get delegates json for the pod
netconfBytes, path, err := consumeScratchNetConf(args.ContainerID, in.CNIDir)
if err != nil {
// Fetch delegates again if cache is not exist
if os.IsNotExist(err) {
// Fetch delegates again if cache is not exist and pod info can be read
if os.IsNotExist(err) && pod != nil {
if in.ClusterNetwork != "" {
err = k8s.GetDefaultNetworks(k8sArgs, in, kubeClient)
_, err = k8s.GetDefaultNetworks(pod, in, kubeClient, nil)
if err != nil {
return cmdErr(k8sArgs, "failed to get clusterNetwork/defaultNetworks: %v", err)
}
@@ -553,7 +802,7 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) err
}
// Get pod annotation and so on
_, _, err := k8s.TryLoadPodDelegates(k8sArgs, in, kubeClient)
_, _, err := k8s.TryLoadPodDelegates(pod, in, kubeClient, nil)
if err != nil {
if len(in.Delegates) == 0 {
// No delegate available so send error
@@ -563,7 +812,10 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) err
logging.Errorf("Multus: failed to get delegates: %v, but continue to delete clusterNetwork", err)
}
} else {
return cmdErr(k8sArgs, "error reading the delegates: %v", err)
// The options to continue with a delete have been exhausted (cachefile + API query didn't work)
// We cannot exit with an error as this may cause a sandbox to never get deleted.
logging.Errorf("Multus: failed to get the cached delegates file: %v, cannot properly delete", err)
return nil
}
} else {
defer os.Remove(path)
@@ -603,12 +855,10 @@ func cmdDel(args *skel.CmdArgs, exec invoke.Exec, kubeClient k8s.KubeClient) err
}
}
rt := types.CreateCNIRuntimeConf(args, k8sArgs, "", in.RuntimeConfig)
return delPlugins(exec, args.IfName, in.Delegates, len(in.Delegates)-1, rt, in.BinDir)
return delPlugins(exec, pod, args, k8sArgs, in.Delegates, len(in.Delegates)-1, in.RuntimeConfig, 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)
@@ -618,25 +868,21 @@ func main() {
flag.BoolVar(&versionOpt, "v", false, "Show application version")
flag.Parse()
if versionOpt == true {
fmt.Printf("%s\n", printVersionString())
fmt.Printf("%s\n", PrintVersionString())
return
}
skel.PluginMain(
func(args *skel.CmdArgs) error {
result, err := cmdAdd(args, nil, nil)
result, err := CmdAdd(args, nil, nil)
if err != nil {
return err
}
return result.Print()
},
func(args *skel.CmdArgs) error {
result, err := cmdGet(args, nil, nil)
if err != nil {
return err
}
return result.Print()
return CmdCheck(args, nil, nil)
},
func(args *skel.CmdArgs) error { return cmdDel(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

View File

@@ -20,7 +20,7 @@ import (
cnitypes "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/intel/multus-cni/logging"
"gopkg.in/intel/multus-cni.v3/pkg/logging"
"github.com/vishvananda/netlink"
"net"
"strings"

View File

@@ -24,123 +24,56 @@ import (
"os"
"strings"
netv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/containernetworking/cni/pkg/types"
types020 "github.com/containernetworking/cni/pkg/types/020"
"github.com/onsi/gomega"
)
// FakeKubeClient is stub KubeClient for testing
type FakeKubeClient struct {
pods map[string]*v1.Pod
PodCount int
nets map[string]string
NetCount int
}
// NewFakeKubeClient creates FakeKubeClient for testing
func NewFakeKubeClient() *FakeKubeClient {
return &FakeKubeClient{
pods: make(map[string]*v1.Pod),
nets: make(map[string]string),
// NewFakeNetAttachDef returns net-attach-def for testing
func NewFakeNetAttachDef(namespace, name, config string) *netv1.NetworkAttachmentDefinition {
return &netv1.NetworkAttachmentDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: netv1.NetworkAttachmentDefinitionSpec{
Config: config,
},
}
}
// GetRawWithPath returns k8s raw data from its path
func (f *FakeKubeClient) GetRawWithPath(path string) ([]byte, error) {
obj, ok := f.nets[path]
if !ok {
return nil, fmt.Errorf("resource not found")
// NewFakeNetAttachDefFile returns net-attach-def for testing with conf file
func NewFakeNetAttachDefFile(namespace, name, filePath, fileData string) *netv1.NetworkAttachmentDefinition {
netAttach := &netv1.NetworkAttachmentDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
}
f.NetCount++
return []byte(obj), nil
}
// AddNetConfig adds net-attach-def into its client
func (f *FakeKubeClient) AddNetConfig(namespace, name, data string) {
cr := fmt.Sprintf(`{
"apiVersion": "k8s.cni.cncf.io/v1",
"kind": "Network",
"metadata": {
"namespace": "%s",
"name": "%s"
},
"spec": {
"config": "%s"
}
}`, namespace, name, strings.Replace(data, "\"", "\\\"", -1))
cr = strings.Replace(cr, "\n", "", -1)
cr = strings.Replace(cr, "\t", "", -1)
f.nets[fmt.Sprintf("/apis/k8s.cni.cncf.io/v1/namespaces/%s/network-attachment-definitions/%s", namespace, name)] = cr
}
// AddNetConfigAnnotation adds net-attach-def into its client with an annotation
func (f *FakeKubeClient) AddNetConfigAnnotation(namespace, name, data string) {
cr := fmt.Sprintf(`{
"apiVersion": "k8s.cni.cncf.io/v1",
"kind": "Network",
"metadata": {
"namespace": "%s",
"name": "%s",
"annotations": {
"k8s.v1.cni.cncf.io/resourceName": "intel.com/sriov"
}
},
"spec": {
"config": "%s"
}
}`, namespace, name, strings.Replace(data, "\"", "\\\"", -1))
cr = strings.Replace(cr, "\n", "", -1)
cr = strings.Replace(cr, "\t", "", -1)
f.nets[fmt.Sprintf("/apis/k8s.cni.cncf.io/v1/namespaces/%s/network-attachment-definitions/%s", namespace, name)] = cr
}
// AddNetFile puts config file as net-attach-def
func (f *FakeKubeClient) AddNetFile(namespace, name, filePath, fileData string) {
cr := fmt.Sprintf(`{
"apiVersion": "k8s.cni.cncf.io/v1",
"kind": "Network",
"metadata": {
"namespace": "%s",
"name": "%s"
}
}`, namespace, name)
f.nets[fmt.Sprintf("/apis/k8s.cni.cncf.io/v1/namespaces/%s/network-attachment-definitions/%s", namespace, name)] = cr
err := ioutil.WriteFile(filePath, []byte(fileData), 0600)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
return netAttach
}
// GetPod query pod by namespace/pod and return it if exists
func (f *FakeKubeClient) GetPod(namespace, name string) (*v1.Pod, error) {
key := fmt.Sprintf("%s/%s", namespace, name)
pod, ok := f.pods[key]
if !ok {
return nil, fmt.Errorf("pod not found")
// NewFakeNetAttachDefAnnotation returns net-attach-def with resource annotation
func NewFakeNetAttachDefAnnotation(namespace, name, config string) *netv1.NetworkAttachmentDefinition {
return &netv1.NetworkAttachmentDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Annotations: map[string]string{
"k8s.v1.cni.cncf.io/resourceName": "intel.com/sriov",
},
},
Spec: netv1.NetworkAttachmentDefinitionSpec{
Config: config,
},
}
f.PodCount++
return pod, nil
}
// UpdatePodStatus update pod status
func (f *FakeKubeClient) UpdatePodStatus(pod *v1.Pod) (*v1.Pod, error) {
key := fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)
f.pods[key] = pod
return f.pods[key], nil
}
// AddPod adds pod into fake client
func (f *FakeKubeClient) AddPod(pod *v1.Pod) {
key := fmt.Sprintf("%s/%s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
f.pods[key] = pod
}
// DeletePod remove pod from fake client
func (f *FakeKubeClient) DeletePod(pod *v1.Pod) {
key := fmt.Sprintf("%s/%s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name)
delete(f.pods, key)
}
// NewFakePod creates fake Pod object
@@ -149,6 +82,7 @@ func NewFakePod(name string, netAnnotation string, defaultNetAnnotation string)
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "test",
UID: "testUID",
},
Spec: v1.PodSpec{
Containers: []v1.Container{

View File

@@ -17,14 +17,16 @@ package types
import (
"encoding/json"
"fmt"
"net"
"strings"
"github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
"github.com/intel/multus-cni/logging"
nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
"gopkg.in/intel/multus-cni.v3/pkg/logging"
)
const (
@@ -33,6 +35,7 @@ const (
defaultBinDir = "/opt/cni/bin"
defaultReadinessIndicatorFile = ""
defaultMultusNamespace = "kube-system"
defaultNonIsolatedNamespace = "default"
)
// LoadDelegateNetConfList reads DelegateNetConf from bytes
@@ -55,7 +58,7 @@ func LoadDelegateNetConfList(bytes []byte, delegateConf *DelegateNetConf) error
}
// LoadDelegateNetConf converts raw CNI JSON into a DelegateNetConf structure
func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID string) (*DelegateNetConf, error) {
func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID string, resourceName string) (*DelegateNetConf, error) {
var err error
logging.Debugf("LoadDelegateNetConf: %s, %v, %s", string(bytes), net, deviceID)
@@ -87,6 +90,9 @@ func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID st
if err != nil {
return nil, logging.Errorf("LoadDelegateNetConf: failed to add deviceID in NetConf bytes: %v", err)
}
// Save them for housekeeping
delegateConf.ResourceName = resourceName
delegateConf.DeviceID = deviceID
}
if net != nil && net.CNIArgs != nil {
bytes, err = addCNIArgsInConfig(bytes, net.CNIArgs)
@@ -97,6 +103,10 @@ func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID st
}
if net != nil {
if net.Name != "" {
// Overwrite CNI config name with net-attach-def name
delegateConf.Name = fmt.Sprintf("%s/%s", net.Namespace, net.Name)
}
if net.InterfaceRequest != "" {
delegateConf.IfnameRequest = net.InterfaceRequest
}
@@ -115,6 +125,16 @@ func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID st
if net.GatewayRequest != nil {
delegateConf.GatewayRequest = append(delegateConf.GatewayRequest, net.GatewayRequest...)
}
if net.InfinibandGUIDRequest != "" {
delegateConf.InfinibandGUIDRequest = net.InfinibandGUIDRequest
}
if net.DeviceID != "" {
if deviceID != "" {
logging.Debugf("Warning: Both RuntimeConfig and ResourceMap provide deviceID. Ignoring RuntimeConfig")
} else {
delegateConf.DeviceID = net.DeviceID
}
}
}
delegateConf.Bytes = bytes
@@ -122,36 +142,62 @@ func LoadDelegateNetConf(bytes []byte, net *NetworkSelectionElement, deviceID st
return delegateConf, nil
}
// MergeCNIRuntimeConfig creates CNI runtimeconfig from delegate
func MergeCNIRuntimeConfig(runtimeConfig *RuntimeConfig, delegate *DelegateNetConf) *RuntimeConfig {
logging.Debugf("MergeCNIRuntimeConfig: %v %v", runtimeConfig, delegate)
// mergeCNIRuntimeConfig creates CNI runtimeconfig from delegate
func mergeCNIRuntimeConfig(runtimeConfig *RuntimeConfig, delegate *DelegateNetConf) *RuntimeConfig {
logging.Debugf("mergeCNIRuntimeConfig: %v %v", runtimeConfig, delegate)
var mergedRuntimeConfig RuntimeConfig
if runtimeConfig == nil {
runtimeConfig = &RuntimeConfig{}
mergedRuntimeConfig = RuntimeConfig{}
} else {
mergedRuntimeConfig = *runtimeConfig
}
// multus inject RuntimeConfig only in case of non MasterPlugin.
if delegate.MasterPlugin != true {
logging.Debugf("MergeCNIRuntimeConfig: add runtimeConfig for net-attach-def: %v", runtimeConfig)
logging.Debugf("mergeCNIRuntimeConfig: add runtimeConfig for net-attach-def: %v", mergedRuntimeConfig)
if delegate.PortMappingsRequest != nil {
runtimeConfig.PortMaps = delegate.PortMappingsRequest
mergedRuntimeConfig.PortMaps = delegate.PortMappingsRequest
}
if delegate.BandwidthRequest != nil {
runtimeConfig.Bandwidth = delegate.BandwidthRequest
mergedRuntimeConfig.Bandwidth = delegate.BandwidthRequest
}
if delegate.IPRequest != nil {
runtimeConfig.IPs = delegate.IPRequest
mergedRuntimeConfig.IPs = delegate.IPRequest
}
if delegate.MacRequest != "" {
runtimeConfig.Mac = delegate.MacRequest
mergedRuntimeConfig.Mac = delegate.MacRequest
}
if delegate.InfinibandGUIDRequest != "" {
mergedRuntimeConfig.InfinibandGUID = delegate.InfinibandGUIDRequest
}
if delegate.DeviceID != "" {
mergedRuntimeConfig.DeviceID = delegate.DeviceID
}
logging.Debugf("mergeCNIRuntimeConfig: add runtimeConfig for net-attach-def: %v", mergedRuntimeConfig)
}
return runtimeConfig
return &mergedRuntimeConfig
}
// CreateCNIRuntimeConf create CNI RuntimeConf
func CreateCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string, rc *RuntimeConfig) *libcni.RuntimeConf {
logging.Debugf("LoadCNIRuntimeConf: %v, %v, %s, %v", args, k8sArgs, ifName, rc)
// CreateCNIRuntimeConf create CNI RuntimeConf for a delegate. If delegate configuration
// exists, merge data with the runtime config.
func CreateCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string, rc *RuntimeConfig, delegate *DelegateNetConf) (*libcni.RuntimeConf, string) {
logging.Debugf("LoadCNIRuntimeConf: %v, %v, %s, %v %v", args, k8sArgs, ifName, rc, delegate)
var cniDeviceInfoFile string
var delegateRc *RuntimeConfig
if delegate != nil {
delegateRc = mergeCNIRuntimeConfig(rc, delegate)
if delegateRc.CNIDeviceInfoFile != "" {
logging.Debugf("Warning: Existing value of CNIDeviceInfoFile will be overwritten %s", delegateRc.CNIDeviceInfoFile)
}
autoDeviceInfo := fmt.Sprintf("%s-%s_%s", delegate.Name, args.ContainerID, ifName)
delegateRc.CNIDeviceInfoFile = nadutils.GetCNIDeviceInfoPath(autoDeviceInfo)
cniDeviceInfoFile = delegateRc.CNIDeviceInfoFile
logging.Debugf("Adding auto-generated CNIDeviceInfoFile: %s", delegateRc.CNIDeviceInfoFile)
} else {
delegateRc = rc
}
// In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#buildCNIRuntimeConf
// Todo
@@ -160,6 +206,7 @@ func CreateCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string, r
ContainerID: args.ContainerID,
NetNS: args.Netns,
IfName: ifName,
// NOTE: Verbose logging depends on this order, so please keep Args order.
Args: [][2]string{
{"IgnoreUnknown", string("true")},
{"K8S_POD_NAMESPACE", string(k8sArgs.K8S_POD_NAMESPACE)},
@@ -168,23 +215,32 @@ func CreateCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string, r
},
}
if rc != nil {
if delegateRc != nil {
capabilityArgs := map[string]interface{}{}
if len(rc.PortMaps) != 0 {
capabilityArgs["portMappings"] = rc.PortMaps
if len(delegateRc.PortMaps) != 0 {
capabilityArgs["portMappings"] = delegateRc.PortMaps
}
if rc.Bandwidth != nil {
capabilityArgs["bandwidth"] = rc.Bandwidth
if delegateRc.Bandwidth != nil {
capabilityArgs["bandwidth"] = delegateRc.Bandwidth
}
if len(rc.IPs) != 0 {
capabilityArgs["ips"] = rc.IPs
if len(delegateRc.IPs) != 0 {
capabilityArgs["ips"] = delegateRc.IPs
}
if len(rc.Mac) != 0 {
capabilityArgs["mac"] = rc.Mac
if len(delegateRc.Mac) != 0 {
capabilityArgs["mac"] = delegateRc.Mac
}
if len(delegateRc.InfinibandGUID) != 0 {
capabilityArgs["infinibandGUID"] = delegateRc.InfinibandGUID
}
if delegateRc.DeviceID != "" {
capabilityArgs["deviceID"] = delegateRc.DeviceID
}
if delegateRc.CNIDeviceInfoFile != "" {
capabilityArgs["CNIDeviceInfoFile"] = delegateRc.CNIDeviceInfoFile
}
rt.CapabilityArgs = capabilityArgs
}
return rt
return rt, cniDeviceInfoFile
}
// GetGatewayFromResult retrieves gateway IP addresses from CNI result
@@ -199,46 +255,10 @@ func GetGatewayFromResult(result *current.Result) []net.IP {
return gateways
}
// LoadNetworkStatus create network status from CNI result
func LoadNetworkStatus(r types.Result, netName string, defaultNet bool) (*NetworkStatus, error) {
logging.Debugf("LoadNetworkStatus: %v, %s, %t", r, netName, defaultNet)
netstatus := &NetworkStatus{}
netstatus.Name = netName
// Convert whatever the IPAM result was into the current Result type
result, err := current.NewResultFromResult(r)
if err != nil {
logging.Errorf("LoadNetworkStatus: error converting the type.Result to current.Result: %v", err)
return netstatus, err
}
for _, ifs := range result.Interfaces {
//Only pod interfaces can have sandbox information
if ifs.Sandbox != "" {
netstatus.Interface = ifs.Name
netstatus.Mac = ifs.Mac
}
}
for _, ipconfig := range result.IPs {
if ipconfig.Version == "4" && ipconfig.Address.IP.To4() != nil {
netstatus.IPs = append(netstatus.IPs, ipconfig.Address.IP.String())
}
if ipconfig.Version == "6" && ipconfig.Address.IP.To16() != nil {
netstatus.IPs = append(netstatus.IPs, ipconfig.Address.IP.String())
}
}
netstatus.DNS = result.DNS
netstatus.Gateway = GetGatewayFromResult(result)
return netstatus, nil
}
// LoadNetConf converts inputs (i.e. stdin) to NetConf
func LoadNetConf(bytes []byte) (*NetConf, error) {
netconf := &NetConf{}
// LogToStderr's default value set to true
netconf := &NetConf{LogToStderr: true}
logging.Debugf("LoadNetConf: %s", string(bytes))
if err := json.Unmarshal(bytes, netconf); err != nil {
@@ -246,6 +266,7 @@ func LoadNetConf(bytes []byte) (*NetConf, error) {
}
// Logging
logging.SetLogStderr(netconf.LogToStderr)
if netconf.LogFile != "" {
logging.SetLogFile(netconf.LogFile)
}
@@ -304,6 +325,19 @@ func LoadNetConf(bytes []byte) (*NetConf, error) {
netconf.MultusNamespace = defaultMultusNamespace
}
// setup namespace isolation
if netconf.RawNonIsolatedNamespaces == "" {
netconf.NonIsolatedNamespaces = []string{defaultNonIsolatedNamespace}
} else {
// Parse the comma separated list
nonisolated := strings.Split(netconf.RawNonIsolatedNamespaces, ",")
// Cleanup the whitespace
for i, nonv := range nonisolated {
nonisolated[i] = strings.TrimSpace(nonv)
}
netconf.NonIsolatedNamespaces = nonisolated
}
// get RawDelegates and put delegates field
if netconf.ClusterNetwork == "" {
// for Delegates
@@ -315,7 +349,7 @@ func LoadNetConf(bytes []byte) (*NetConf, error) {
if err != nil {
return nil, logging.Errorf("LoadNetConf: error marshalling delegate %d config: %v", idx, err)
}
delegateConf, err := LoadDelegateNetConf(bytes, nil, "")
delegateConf, err := LoadDelegateNetConf(bytes, nil, "", "")
if err != nil {
return nil, logging.Errorf("LoadNetConf: failed to load delegate %d config: %v", idx, err)
}
@@ -377,13 +411,15 @@ func addDeviceIDInConfList(inBytes []byte, deviceID string) ([]byte, error) {
return nil, logging.Errorf("addDeviceIDInConfList: unable to typecast plugin list")
}
firstPlugin, ok := pMap[0].(map[string]interface{})
if !ok {
return nil, logging.Errorf("addDeviceIDInConfList: unable to typecast pMap")
for idx, plugin := range pMap {
currentPlugin, ok := plugin.(map[string]interface{})
if !ok {
return nil, logging.Errorf("addDeviceIDInConfList: unable to typecast plugin #%d", idx)
}
// Inject deviceID
currentPlugin["deviceID"] = deviceID
currentPlugin["pciBusID"] = deviceID
}
// Inject deviceID
firstPlugin["deviceID"] = deviceID
firstPlugin["pciBusID"] = deviceID
configBytes, err := json.Marshal(rawConfig)
if err != nil {

View File

@@ -26,7 +26,8 @@ import (
types020 "github.com/containernetworking/cni/pkg/types/020"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
testhelpers "github.com/intel/multus-cni/testing"
netutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils"
testhelpers "gopkg.in/intel/multus-cni.v3/pkg/testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -101,7 +102,7 @@ var _ = Describe("config operations", func() {
_, err := LoadNetConf([]byte(conf))
Expect(err).To(HaveOccurred())
_, err = LoadDelegateNetConf([]byte(conf), nil, "")
_, err = LoadDelegateNetConf([]byte(conf), nil, "", "")
Expect(err).To(HaveOccurred())
err = LoadDelegateNetConfList([]byte(conf), &DelegateNetConf{})
Expect(err).To(HaveOccurred())
@@ -134,6 +135,47 @@ var _ = Describe("config operations", func() {
Expect(netConf.LogFile).To(Equal("/var/log/multus.log"))
})
It("properly sets namespace isolation using the default namespace", func() {
conf := `{
"name": "node-cni-network",
"type": "multus",
"logLevel": "debug",
"logFile": "/var/log/multus.log",
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
"namespaceIsolation": true,
"delegates": [{
"type": "weave-net"
}]
}`
netConf, err := LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
Expect(netConf.NamespaceIsolation).To(Equal(true))
Expect(len(netConf.NonIsolatedNamespaces)).To(Equal(1))
Expect(netConf.NonIsolatedNamespaces[0]).To(Equal("default"))
})
It("properly sets namespace isolation using custom namespaces", func() {
conf := `{
"name": "node-cni-network",
"type": "multus",
"logLevel": "debug",
"logFile": "/var/log/multus.log",
"kubeconfig": "/etc/kubernetes/node-kubeconfig.yaml",
"namespaceIsolation": true,
"globalNamespaces": " foo,bar ,default",
"delegates": [{
"type": "weave-net"
}]
}`
netConf, err := LoadNetConf([]byte(conf))
Expect(err).NotTo(HaveOccurred())
Expect(netConf.NamespaceIsolation).To(Equal(true))
Expect(len(netConf.NonIsolatedNamespaces)).To(Equal(3))
Expect(netConf.NonIsolatedNamespaces[0]).To(Equal("foo"))
Expect(netConf.NonIsolatedNamespaces[1]).To(Equal("bar"))
Expect(netConf.NonIsolatedNamespaces[2]).To(Equal("default"))
})
It("prevResult with no errors", func() {
conf := `{
"name": "node-cni-network",
@@ -279,7 +321,7 @@ var _ = Describe("config operations", func() {
DeviceID string `json:"deviceID"`
}
sriovConf := &sriovNetConf{}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0")
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0", "")
Expect(err).NotTo(HaveOccurred())
err = json.Unmarshal(delegateNetConf.Bytes, &sriovConf)
@@ -305,7 +347,7 @@ var _ = Describe("config operations", func() {
Plugins []*sriovNetConf `json:"plugins"`
}
sriovConfList := &sriovNetConfList{}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.1")
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.1", "")
Expect(err).NotTo(HaveOccurred())
err = json.Unmarshal(delegateNetConf.Bytes, &sriovConfList)
@@ -313,6 +355,37 @@ var _ = Describe("config operations", func() {
Expect(sriovConfList.Plugins[0].DeviceID).To(Equal("0000:00:00.1"))
})
It("assigns deviceID in delegated conf list multiple plugins", func() {
conf := `{
"name": "second-network",
"plugins": [
{
"type": "sriov"
},
{
"type": "other-cni"
}
]
}`
type sriovNetConf struct {
Name string `json:"name"`
Type string `json:"type"`
DeviceID string `json:"deviceID"`
}
type sriovNetConfList struct {
Plugins []*sriovNetConf `json:"plugins"`
}
sriovConfList := &sriovNetConfList{}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.1", "")
Expect(err).NotTo(HaveOccurred())
err = json.Unmarshal(delegateNetConf.Bytes, &sriovConfList)
Expect(err).NotTo(HaveOccurred())
for _, plugin := range sriovConfList.Plugins {
Expect(plugin.DeviceID).To(Equal("0000:00:00.1"))
}
})
It("assigns pciBusID in delegated conf", func() {
conf := `{
"name": "second-network",
@@ -324,7 +397,7 @@ var _ = Describe("config operations", func() {
PCIBusID string `json:"pciBusID"`
}
hostDeviceConf := &hostDeviceNetConf{}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.2")
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.2", "")
Expect(err).NotTo(HaveOccurred())
err = json.Unmarshal(delegateNetConf.Bytes, &hostDeviceConf)
@@ -350,7 +423,7 @@ var _ = Describe("config operations", func() {
Plugins []*hostDeviceNetConf `json:"plugins"`
}
hostDeviceConfList := &hostDeviceNetConfList{}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.3")
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.3", "")
Expect(err).NotTo(HaveOccurred())
err = json.Unmarshal(delegateNetConf.Bytes, &hostDeviceConfList)
@@ -358,6 +431,37 @@ var _ = Describe("config operations", func() {
Expect(hostDeviceConfList.Plugins[0].PCIBusID).To(Equal("0000:00:00.3"))
})
It("assigns pciBusID in delegated conf list multiple plugins", func() {
conf := `{
"name": "second-network",
"plugins": [
{
"type": "host-device"
},
{
"type": "other-cni"
}
]
}`
type hostDeviceNetConf struct {
Name string `json:"name"`
Type string `json:"type"`
PCIBusID string `json:"pciBusID"`
}
type hostDeviceNetConfList struct {
Plugins []*hostDeviceNetConf `json:"plugins"`
}
hostDeviceConfList := &hostDeviceNetConfList{}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.3", "")
Expect(err).NotTo(HaveOccurred())
err = json.Unmarshal(delegateNetConf.Bytes, &hostDeviceConfList)
Expect(err).NotTo(HaveOccurred())
for _, plugin := range hostDeviceConfList.Plugins {
Expect(plugin.PCIBusID).To(Equal("0000:00:00.3"))
}
})
It("add cni-args in config", func() {
var args map[string]interface{}
conf := `{
@@ -368,20 +472,20 @@ var _ = Describe("config operations", func() {
"args1": "val1"
}`
type bridgeNetConf struct {
Name string `json:"name"`
Type string `json:"type"`
Args struct {
Name string `json:"name"`
Type string `json:"type"`
Args struct {
CNI map[string]string `json:"cni"`
} `json:"args"`
}
err := json.Unmarshal([]byte(cniArgs), &args)
err := json.Unmarshal([]byte(cniArgs), &args)
Expect(err).NotTo(HaveOccurred())
net := &NetworkSelectionElement{
Name: "test-elem",
net := &NetworkSelectionElement{
Name: "test-elem",
CNIArgs: &args,
}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), net, "")
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), net, "", "")
Expect(err).NotTo(HaveOccurred())
bridgeConf := &bridgeNetConf{}
err = json.Unmarshal(delegateNetConf.Bytes, bridgeConf)
@@ -404,20 +508,20 @@ var _ = Describe("config operations", func() {
"args1": "val1a"
}`
type bridgeNetConf struct {
Name string `json:"name"`
Type string `json:"type"`
Args struct {
Name string `json:"name"`
Type string `json:"type"`
Args struct {
CNI map[string]string `json:"cni"`
} `json:"args"`
}
err := json.Unmarshal([]byte(cniArgs), &args)
err := json.Unmarshal([]byte(cniArgs), &args)
Expect(err).NotTo(HaveOccurred())
net := &NetworkSelectionElement{
Name: "test-elem",
net := &NetworkSelectionElement{
Name: "test-elem",
CNIArgs: &args,
}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), net, "")
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), net, "", "")
Expect(err).NotTo(HaveOccurred())
bridgeConf := &bridgeNetConf{}
err = json.Unmarshal(delegateNetConf.Bytes, bridgeConf)
@@ -439,23 +543,23 @@ var _ = Describe("config operations", func() {
"args1": "val1"
}`
type bridgeNetConf struct {
Type string `json:"type"`
Args struct {
Type string `json:"type"`
Args struct {
CNI map[string]string `json:"cni"`
} `json:"args"`
}
type bridgeNetConfList struct {
Name string `json:"name"`
Name string `json:"name"`
Plugins []*bridgeNetConf `json:"plugins"`
}
err := json.Unmarshal([]byte(cniArgs), &args)
err := json.Unmarshal([]byte(cniArgs), &args)
Expect(err).NotTo(HaveOccurred())
net := &NetworkSelectionElement{
Name: "test-elem",
net := &NetworkSelectionElement{
Name: "test-elem",
CNIArgs: &args,
}
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), net, "")
delegateNetConf, err := LoadDelegateNetConf([]byte(conf), net, "", "")
Expect(err).NotTo(HaveOccurred())
bridgeConflist := &bridgeNetConfList{}
err = json.Unmarshal(delegateNetConf.Bytes, bridgeConflist)
@@ -502,7 +606,7 @@ var _ = Describe("config operations", func() {
HostIP: "anotherSampleHostIP",
}
rt := CreateCNIRuntimeConf(args, k8sArgs, "", rc)
rt, _ := CreateCNIRuntimeConf(args, k8sArgs, "", rc, nil)
fmt.Println("rt.ContainerID: ", rt.ContainerID)
Expect(rt.ContainerID).To(Equal("123456789"))
Expect(rt.NetNS).To(Equal(args.Netns))
@@ -532,10 +636,10 @@ var _ = Describe("config operations", func() {
}
}`
delegate, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0")
delegate, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0", "")
Expect(err).NotTo(HaveOccurred())
delegateNetStatus, err := LoadNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin, nil)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
@@ -565,10 +669,10 @@ var _ = Describe("config operations", func() {
}
}`
delegate, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0")
delegate, err := LoadDelegateNetConf([]byte(conf), nil, "0000:00:00.0", "")
Expect(err).NotTo(HaveOccurred())
fmt.Println("result.Version: ", result.Version())
delegateNetStatus, err := LoadNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin)
delegateNetStatus, err := netutils.CreateNetworkStatus(result, delegate.Conf.Name, delegate.MasterPlugin, nil)
GinkgoT().Logf("delegateNetStatus %+v\n", delegateNetStatus)
@@ -596,24 +700,26 @@ var _ = Describe("config operations", func() {
}
networkSelection := &NetworkSelectionElement{
Name: "testname",
InterfaceRequest: "testIF1",
MacRequest: "c2:11:22:33:44:66",
IPRequest: []string{"10.0.0.1/24"},
BandwidthRequest: bandwidthEntry1,
PortMappingsRequest: []*PortMapEntry{portMapEntry1},
Name: "testname",
InterfaceRequest: "testIF1",
MacRequest: "c2:11:22:33:44:66",
InfinibandGUIDRequest: "24:8a:07:03:00:8d:ae:2e",
IPRequest: []string{"10.0.0.1/24"},
BandwidthRequest: bandwidthEntry1,
PortMappingsRequest: []*PortMapEntry{portMapEntry1},
}
delegateConf, err := LoadDelegateNetConf([]byte(cniConfig), networkSelection, "")
delegateConf, err := LoadDelegateNetConf([]byte(cniConfig), networkSelection, "", "")
Expect(err).NotTo(HaveOccurred())
Expect(delegateConf.IfnameRequest).To(Equal(networkSelection.InterfaceRequest))
Expect(delegateConf.MacRequest).To(Equal(networkSelection.MacRequest))
Expect(delegateConf.InfinibandGUIDRequest).To(Equal(networkSelection.InfinibandGUIDRequest))
Expect(delegateConf.IPRequest).To(Equal(networkSelection.IPRequest))
Expect(delegateConf.BandwidthRequest).To(Equal(networkSelection.BandwidthRequest))
Expect(delegateConf.PortMappingsRequest).To(Equal(networkSelection.PortMappingsRequest))
})
It("test MergeCNIRuntimeConfig with masterPlugin", func() {
It("test mergeCNIRuntimeConfig with masterPlugin", func() {
conf := `{
"name": "node-cni-network",
"type": "multus",
@@ -641,22 +747,27 @@ var _ = Describe("config operations", func() {
}
networkSelection := &NetworkSelectionElement{
Name: "testname",
InterfaceRequest: "testIF1",
MacRequest: "c2:11:22:33:44:66",
IPRequest: []string{"10.0.0.1/24"},
BandwidthRequest: bandwidthEntry1,
PortMappingsRequest: []*PortMapEntry{portMapEntry1},
Name: "testname",
InterfaceRequest: "testIF1",
MacRequest: "c2:11:22:33:44:66",
InfinibandGUIDRequest: "24:8a:07:03:00:8d:ae:2e",
IPRequest: []string{"10.0.0.1/24"},
BandwidthRequest: bandwidthEntry1,
PortMappingsRequest: []*PortMapEntry{portMapEntry1},
}
delegate, err := LoadDelegateNetConf([]byte(conf), networkSelection, "")
delegate, err := LoadDelegateNetConf([]byte(conf), networkSelection, "", "")
delegate.MasterPlugin = true
origRuntimeConfig := RuntimeConfig{}
Expect(err).NotTo(HaveOccurred())
runtimeConf := MergeCNIRuntimeConfig(&RuntimeConfig{}, delegate)
runtimeConf := mergeCNIRuntimeConfig(&origRuntimeConfig, delegate)
Expect(runtimeConf.PortMaps).To(BeNil())
Expect(runtimeConf.Bandwidth).To(BeNil())
Expect(runtimeConf.InfinibandGUID).To(Equal(""))
// The original RuntimeConfig must have not been overwritten
Expect(origRuntimeConfig).To(Equal(RuntimeConfig{}))
})
It("test MergeCNIRuntimeConfig with delegate plugin", func() {
It("test mergeCNIRuntimeConfig with delegate plugin", func() {
conf := `{
"name": "weave1",
"cniVersion": "0.2.0",
@@ -676,21 +787,26 @@ var _ = Describe("config operations", func() {
}
networkSelection := &NetworkSelectionElement{
Name: "testname",
InterfaceRequest: "testIF1",
MacRequest: "c2:11:22:33:44:66",
IPRequest: []string{"10.0.0.1/24"},
BandwidthRequest: bandwidthEntry1,
PortMappingsRequest: []*PortMapEntry{portMapEntry1},
Name: "testname",
InterfaceRequest: "testIF1",
MacRequest: "c2:11:22:33:44:66",
InfinibandGUIDRequest: "24:8a:07:03:00:8d:ae:2e",
IPRequest: []string{"10.0.0.1/24"},
BandwidthRequest: bandwidthEntry1,
PortMappingsRequest: []*PortMapEntry{portMapEntry1},
}
delegate, err := LoadDelegateNetConf([]byte(conf), networkSelection, "")
origRuntimeConfig := RuntimeConfig{}
delegate, err := LoadDelegateNetConf([]byte(conf), networkSelection, "", "")
Expect(err).NotTo(HaveOccurred())
runtimeConf := MergeCNIRuntimeConfig(&RuntimeConfig{}, delegate)
runtimeConf := mergeCNIRuntimeConfig(&origRuntimeConfig, delegate)
Expect(runtimeConf.PortMaps).NotTo(BeNil())
Expect(len(runtimeConf.PortMaps)).To(BeEquivalentTo(1))
Expect(runtimeConf.PortMaps[0]).To(Equal(portMapEntry1))
Expect(runtimeConf.Bandwidth).To(Equal(bandwidthEntry1))
Expect(len(runtimeConf.IPs)).To(BeEquivalentTo(1))
Expect(runtimeConf.Mac).To(Equal("c2:11:22:33:44:66"))
Expect(runtimeConf.InfinibandGUID).To(Equal("24:8a:07:03:00:8d:ae:2e"))
// The original RuntimeConfig must have not been overwritten
Expect(origRuntimeConfig).To(Equal(RuntimeConfig{}))
})
})

View File

@@ -38,17 +38,20 @@ type NetConf struct {
// RawDelegates is private to the NetConf class; use Delegates instead
RawDelegates []map[string]interface{} `json:"delegates"`
Delegates []*DelegateNetConf `json:"-"`
NetStatus []*NetworkStatus `json:"-"`
Kubeconfig string `json:"kubeconfig"`
ClusterNetwork string `json:"clusterNetwork"`
DefaultNetworks []string `json:"defaultNetworks"`
LogFile string `json:"logFile"`
LogLevel string `json:"logLevel"`
LogToStderr bool `json:"logToStderr,omitempty"`
RuntimeConfig *RuntimeConfig `json:"runtimeConfig,omitempty"`
// Default network readiness options
ReadinessIndicatorFile string `json:"readinessindicatorfile"`
// Option to isolate the usage of CR's to the namespace in which a pod resides.
NamespaceIsolation bool `json:"namespaceIsolation"`
NamespaceIsolation bool `json:"namespaceIsolation"`
RawNonIsolatedNamespaces string `json:"globalNamespaces"`
NonIsolatedNamespaces []string `json:"-"`
// Option to set system namespaces (to avoid to add defaultNetworks)
SystemNamespaces []string `json:"systemNamespaces"`
// Option to set the namespace that multus-cni uses (clusterNetwork/defaultNetworks)
@@ -57,10 +60,13 @@ type NetConf struct {
// RuntimeConfig specifies CNI RuntimeConfig
type RuntimeConfig struct {
PortMaps []*PortMapEntry `json:"portMappings,omitempty"`
Bandwidth *BandwidthEntry `json:"bandwidth,omitempty"`
IPs []string `json:"ips,omitempty"`
Mac string `json:"mac,omitempty"`
PortMaps []*PortMapEntry `json:"portMappings,omitempty"`
Bandwidth *BandwidthEntry `json:"bandwidth,omitempty"`
IPs []string `json:"ips,omitempty"`
Mac string `json:"mac,omitempty"`
InfinibandGUID string `json:"infinibandGUID,omitempty"`
DeviceID string `json:"deviceID,omitempty"`
CNIDeviceInfoFile string `json:"CNIDeviceInfoFile,omitempty"`
}
// PortMapEntry for CNI PortMapEntry
@@ -80,31 +86,27 @@ type BandwidthEntry struct {
EgressBurst int `json:"egressBurst"`
}
// NetworkStatus is for network status annotation for pod
type NetworkStatus struct {
Name string `json:"name"`
Interface string `json:"interface,omitempty"`
IPs []string `json:"ips,omitempty"`
Mac string `json:"mac,omitempty"`
DNS types.DNS `json:"dns,omitempty"`
Gateway []net.IP `json:"default-route,omitempty"`
}
// DelegateNetConf for net-attach-def for pod
type DelegateNetConf struct {
Conf types.NetConf
ConfList types.NetConfList
IfnameRequest string `json:"ifnameRequest,omitempty"`
MacRequest string `json:"macRequest,omitempty"`
IPRequest []string `json:"ipRequest,omitempty"`
PortMappingsRequest []*PortMapEntry `json:"-"`
BandwidthRequest *BandwidthEntry `json:"-"`
GatewayRequest []net.IP `json:"default-route,omitempty"`
IsFilterGateway bool
Conf types.NetConf
ConfList types.NetConfList
Name string
IfnameRequest string `json:"ifnameRequest,omitempty"`
MacRequest string `json:"macRequest,omitempty"`
InfinibandGUIDRequest string `json:"infinibandGUIDRequest,omitempty"`
IPRequest []string `json:"ipRequest,omitempty"`
PortMappingsRequest []*PortMapEntry `json:"-"`
BandwidthRequest *BandwidthEntry `json:"-"`
GatewayRequest []net.IP `json:"default-route,omitempty"`
IsFilterGateway bool
// MasterPlugin is only used internal housekeeping
MasterPlugin bool `json:"-"`
// Conflist plugin is only used internal housekeeping
ConfListPlugin bool `json:"-"`
// DeviceID is only used internal housekeeping
DeviceID string `json:"deviceID,omitempty"`
// ResourceName is only used internal housekeeping
ResourceName string `json:"resourceName,omitempty"`
// Raw JSON
Bytes []byte
@@ -125,6 +127,9 @@ type NetworkSelectionElement struct {
// MacRequest contains an optional requested MAC address for this
// network attachment
MacRequest string `json:"mac,omitempty"`
// InfinibandGUID request contains an optional requested Infiniband GUID address
// for this network attachment
InfinibandGUIDRequest string `json:"infiniband-guid,omitempty"`
// InterfaceRequest contains an optional requested name for the
// network interface this attachment will create in the container
InterfaceRequest string `json:"interface,omitempty"`
@@ -137,6 +142,8 @@ type NetworkSelectionElement struct {
// BandwidthRequest contains an optional requested bandwidth for
// the network
BandwidthRequest *BandwidthEntry `json:"bandwidth,omitempty"`
// DeviceID contains an optional requested deviceID the network
DeviceID string `json:"deviceID,omitempty"`
// CNIArgs contains additional CNI arguments for the network interface
CNIArgs *map[string]interface{} `json:"cni-args"`
// GatewayRequest contains default route IP address for the pod
@@ -147,9 +154,9 @@ type NetworkSelectionElement struct {
type K8sArgs struct {
types.CommonArgs
IP net.IP
K8S_POD_NAME types.UnmarshallableString
K8S_POD_NAMESPACE types.UnmarshallableString
K8S_POD_INFRA_CONTAINER_ID types.UnmarshallableString
K8S_POD_NAME types.UnmarshallableString //revive:disable-line
K8S_POD_NAMESPACE types.UnmarshallableString //revive:disable-line
K8S_POD_INFRA_CONTAINER_ID types.UnmarshallableString //revive:disable-line
}
// ResourceInfo is struct to hold Pod device allocation information

15
vendor/github.com/davecgh/go-spew/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,15 @@
ISC License
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

145
vendor/github.com/davecgh/go-spew/spew/bypass.go generated vendored Normal file
View File

@@ -0,0 +1,145 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// Go versions prior to 1.4 are disabled because they use a different layout
// for interfaces which make the implementation of unsafeReflectValue more complex.
// +build !js,!appengine,!safe,!disableunsafe,go1.4
package spew
import (
"reflect"
"unsafe"
)
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = false
// ptrSize is the size of a pointer on the current arch.
ptrSize = unsafe.Sizeof((*byte)(nil))
)
type flag uintptr
var (
// flagRO indicates whether the value field of a reflect.Value
// is read-only.
flagRO flag
// flagAddr indicates whether the address of the reflect.Value's
// value may be taken.
flagAddr flag
)
// flagKindMask holds the bits that make up the kind
// part of the flags field. In all the supported versions,
// it is in the lower 5 bits.
const flagKindMask = flag(0x1f)
// Different versions of Go have used different
// bit layouts for the flags type. This table
// records the known combinations.
var okFlags = []struct {
ro, addr flag
}{{
// From Go 1.4 to 1.5
ro: 1 << 5,
addr: 1 << 7,
}, {
// Up to Go tip.
ro: 1<<5 | 1<<6,
addr: 1 << 8,
}}
var flagValOffset = func() uintptr {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
return field.Offset
}()
// flagField returns a pointer to the flag field of a reflect.Value.
func flagField(v *reflect.Value) *flag {
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
}
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
// the typical safety restrictions preventing access to unaddressable and
// unexported data. It works by digging the raw pointer to the underlying
// value out of the protected value and generating a new unprotected (unsafe)
// reflect.Value to it.
//
// This allows us to check for implementations of the Stringer and error
// interfaces to be used for pretty printing ordinarily unaddressable and
// inaccessible values such as unexported struct fields.
func unsafeReflectValue(v reflect.Value) reflect.Value {
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
return v
}
flagFieldPtr := flagField(&v)
*flagFieldPtr &^= flagRO
*flagFieldPtr |= flagAddr
return v
}
// Sanity checks against future reflect package changes
// to the type or semantics of the Value.flag field.
func init() {
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
if !ok {
panic("reflect.Value has no flag field")
}
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
panic("reflect.Value flag field has changed kind")
}
type t0 int
var t struct {
A t0
// t0 will have flagEmbedRO set.
t0
// a will have flagStickyRO set
a t0
}
vA := reflect.ValueOf(t).FieldByName("A")
va := reflect.ValueOf(t).FieldByName("a")
vt0 := reflect.ValueOf(t).FieldByName("t0")
// Infer flagRO from the difference between the flags
// for the (otherwise identical) fields in t.
flagPublic := *flagField(&vA)
flagWithRO := *flagField(&va) | *flagField(&vt0)
flagRO = flagPublic ^ flagWithRO
// Infer flagAddr from the difference between a value
// taken from a pointer and not.
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
flagNoPtr := *flagField(&vA)
flagPtr := *flagField(&vPtrA)
flagAddr = flagNoPtr ^ flagPtr
// Check that the inferred flags tally with one of the known versions.
for _, f := range okFlags {
if flagRO == f.ro && flagAddr == f.addr {
return
}
}
panic("reflect.Value read-only flag has changed semantics")
}

38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is running on Google App Engine, compiled by GopherJS, or
// "-tags safe" is added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build js appengine safe disableunsafe !go1.4
package spew
import "reflect"
const (
// UnsafeDisabled is a build-time constant which specifies whether or
// not access to the unsafe package is available.
UnsafeDisabled = true
)
// unsafeReflectValue typically converts the passed reflect.Value into a one
// that bypasses the typical safety restrictions preventing access to
// unaddressable and unexported data. However, doing this relies on access to
// the unsafe package. This is a stub version which simply returns the passed
// reflect.Value when the unsafe package is not available.
func unsafeReflectValue(v reflect.Value) reflect.Value {
return v
}

341
vendor/github.com/davecgh/go-spew/spew/common.go generated vendored Normal file
View File

@@ -0,0 +1,341 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"strconv"
)
// Some constants in the form of bytes to avoid string overhead. This mirrors
// the technique used in the fmt package.
var (
panicBytes = []byte("(PANIC=")
plusBytes = []byte("+")
iBytes = []byte("i")
trueBytes = []byte("true")
falseBytes = []byte("false")
interfaceBytes = []byte("(interface {})")
commaNewlineBytes = []byte(",\n")
newlineBytes = []byte("\n")
openBraceBytes = []byte("{")
openBraceNewlineBytes = []byte("{\n")
closeBraceBytes = []byte("}")
asteriskBytes = []byte("*")
colonBytes = []byte(":")
colonSpaceBytes = []byte(": ")
openParenBytes = []byte("(")
closeParenBytes = []byte(")")
spaceBytes = []byte(" ")
pointerChainBytes = []byte("->")
nilAngleBytes = []byte("<nil>")
maxNewlineBytes = []byte("<max depth reached>\n")
maxShortBytes = []byte("<max>")
circularBytes = []byte("<already shown>")
circularShortBytes = []byte("<shown>")
invalidAngleBytes = []byte("<invalid>")
openBracketBytes = []byte("[")
closeBracketBytes = []byte("]")
percentBytes = []byte("%")
precisionBytes = []byte(".")
openAngleBytes = []byte("<")
closeAngleBytes = []byte(">")
openMapBytes = []byte("map[")
closeMapBytes = []byte("]")
lenEqualsBytes = []byte("len=")
capEqualsBytes = []byte("cap=")
)
// hexDigits is used to map a decimal value to a hex digit.
var hexDigits = "0123456789abcdef"
// catchPanic handles any panics that might occur during the handleMethods
// calls.
func catchPanic(w io.Writer, v reflect.Value) {
if err := recover(); err != nil {
w.Write(panicBytes)
fmt.Fprintf(w, "%v", err)
w.Write(closeParenBytes)
}
}
// handleMethods attempts to call the Error and String methods on the underlying
// type the passed reflect.Value represents and outputes the result to Writer w.
//
// It handles panics in any called methods by catching and displaying the error
// as the formatted value.
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
// We need an interface to check if the type implements the error or
// Stringer interface. However, the reflect package won't give us an
// interface on certain things like unexported struct fields in order
// to enforce visibility rules. We use unsafe, when it's available,
// to bypass these restrictions since this package does not mutate the
// values.
if !v.CanInterface() {
if UnsafeDisabled {
return false
}
v = unsafeReflectValue(v)
}
// Choose whether or not to do error and Stringer interface lookups against
// the base type or a pointer to the base type depending on settings.
// Technically calling one of these methods with a pointer receiver can
// mutate the value, however, types which choose to satisify an error or
// Stringer interface with a pointer receiver should not be mutating their
// state inside these interface methods.
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
v = unsafeReflectValue(v)
}
if v.CanAddr() {
v = v.Addr()
}
// Is it an error or Stringer?
switch iface := v.Interface().(type) {
case error:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.Error()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.Error()))
return true
case fmt.Stringer:
defer catchPanic(w, v)
if cs.ContinueOnMethod {
w.Write(openParenBytes)
w.Write([]byte(iface.String()))
w.Write(closeParenBytes)
w.Write(spaceBytes)
return false
}
w.Write([]byte(iface.String()))
return true
}
return false
}
// printBool outputs a boolean value as true or false to Writer w.
func printBool(w io.Writer, val bool) {
if val {
w.Write(trueBytes)
} else {
w.Write(falseBytes)
}
}
// printInt outputs a signed integer value to Writer w.
func printInt(w io.Writer, val int64, base int) {
w.Write([]byte(strconv.FormatInt(val, base)))
}
// printUint outputs an unsigned integer value to Writer w.
func printUint(w io.Writer, val uint64, base int) {
w.Write([]byte(strconv.FormatUint(val, base)))
}
// printFloat outputs a floating point value using the specified precision,
// which is expected to be 32 or 64bit, to Writer w.
func printFloat(w io.Writer, val float64, precision int) {
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
}
// printComplex outputs a complex value using the specified float precision
// for the real and imaginary parts to Writer w.
func printComplex(w io.Writer, c complex128, floatPrecision int) {
r := real(c)
w.Write(openParenBytes)
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
i := imag(c)
if i >= 0 {
w.Write(plusBytes)
}
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
w.Write(iBytes)
w.Write(closeParenBytes)
}
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
// prefix to Writer w.
func printHexPtr(w io.Writer, p uintptr) {
// Null pointer.
num := uint64(p)
if num == 0 {
w.Write(nilAngleBytes)
return
}
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
buf := make([]byte, 18)
// It's simpler to construct the hex string right to left.
base := uint64(16)
i := len(buf) - 1
for num >= base {
buf[i] = hexDigits[num%base]
num /= base
i--
}
buf[i] = hexDigits[num]
// Add '0x' prefix.
i--
buf[i] = 'x'
i--
buf[i] = '0'
// Strip unused leading bytes.
buf = buf[i:]
w.Write(buf)
}
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
// elements to be sorted.
type valuesSorter struct {
values []reflect.Value
strings []string // either nil or same len and values
cs *ConfigState
}
// newValuesSorter initializes a valuesSorter instance, which holds a set of
// surrogate keys on which the data should be sorted. It uses flags in
// ConfigState to decide if and how to populate those surrogate keys.
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
vs := &valuesSorter{values: values, cs: cs}
if canSortSimply(vs.values[0].Kind()) {
return vs
}
if !cs.DisableMethods {
vs.strings = make([]string, len(values))
for i := range vs.values {
b := bytes.Buffer{}
if !handleMethods(cs, &b, vs.values[i]) {
vs.strings = nil
break
}
vs.strings[i] = b.String()
}
}
if vs.strings == nil && cs.SpewKeys {
vs.strings = make([]string, len(values))
for i := range vs.values {
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
}
}
return vs
}
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
// directly, or whether it should be considered for sorting by surrogate keys
// (if the ConfigState allows it).
func canSortSimply(kind reflect.Kind) bool {
// This switch parallels valueSortLess, except for the default case.
switch kind {
case reflect.Bool:
return true
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return true
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return true
case reflect.Float32, reflect.Float64:
return true
case reflect.String:
return true
case reflect.Uintptr:
return true
case reflect.Array:
return true
}
return false
}
// Len returns the number of values in the slice. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Len() int {
return len(s.values)
}
// Swap swaps the values at the passed indices. It is part of the
// sort.Interface implementation.
func (s *valuesSorter) Swap(i, j int) {
s.values[i], s.values[j] = s.values[j], s.values[i]
if s.strings != nil {
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
}
}
// valueSortLess returns whether the first value should sort before the second
// value. It is used by valueSorter.Less as part of the sort.Interface
// implementation.
func valueSortLess(a, b reflect.Value) bool {
switch a.Kind() {
case reflect.Bool:
return !a.Bool() && b.Bool()
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return a.Int() < b.Int()
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return a.Uint() < b.Uint()
case reflect.Float32, reflect.Float64:
return a.Float() < b.Float()
case reflect.String:
return a.String() < b.String()
case reflect.Uintptr:
return a.Uint() < b.Uint()
case reflect.Array:
// Compare the contents of both arrays.
l := a.Len()
for i := 0; i < l; i++ {
av := a.Index(i)
bv := b.Index(i)
if av.Interface() == bv.Interface() {
continue
}
return valueSortLess(av, bv)
}
}
return a.String() < b.String()
}
// Less returns whether the value at index i should sort before the
// value at index j. It is part of the sort.Interface implementation.
func (s *valuesSorter) Less(i, j int) bool {
if s.strings == nil {
return valueSortLess(s.values[i], s.values[j])
}
return s.strings[i] < s.strings[j]
}
// sortValues is a sort function that handles both native types and any type that
// can be converted to error or Stringer. Other inputs are sorted according to
// their Value.String() value to ensure display stability.
func sortValues(values []reflect.Value, cs *ConfigState) {
if len(values) == 0 {
return
}
sort.Sort(newValuesSorter(values, cs))
}

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